/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.pivotal.strepsirrhini.chaoslemur;
import io.pivotal.strepsirrhini.chaoslemur.infrastructure.DestructionException;
import io.pivotal.strepsirrhini.chaoslemur.infrastructure.Infrastructure;
import io.pivotal.strepsirrhini.chaoslemur.reporter.Reporter;
import io.pivotal.strepsirrhini.chaoslemur.state.State;
import io.pivotal.strepsirrhini.chaoslemur.state.StateProvider;
import io.pivotal.strepsirrhini.chaoslemur.task.Task;
import io.pivotal.strepsirrhini.chaoslemur.task.TaskRepository;
import io.pivotal.strepsirrhini.chaoslemur.task.TaskUriBuilder;
import io.pivotal.strepsirrhini.chaoslemur.task.Trigger;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.net.URI;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
public final class DestroyerTest {
private final ExecutorService executorService = mock(ExecutorService.class);
private final FateEngine fateEngine = mock(FateEngine.class);
@SuppressWarnings("unchecked")
private final Future<Void> future = mock(Future.class);
private final Infrastructure infrastructure = mock(Infrastructure.class);
private final Member member1 = new Member("test-id-1", "test-deployment", "test-job", "test-name-1");
private final Member member2 = new Member("test-id-2", "test-deployment", "test-job", "test-name-2");
private final Set<Member> members = Stream.of(this.member1, this.member2).collect(Collectors.toSet());
private final Reporter reporter = mock(Reporter.class);
private final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
private final StateProvider stateProvider = mock(StateProvider.class);
private final TaskRepository taskRepository = mock(TaskRepository.class);
private final TaskUriBuilder taskUriBuilder = mock(TaskUriBuilder.class);
private final Destroyer destroyer = new Destroyer(false, this.executorService, this.fateEngine, this.infrastructure, this.reporter, this.stateProvider, "", this.taskRepository,
this.taskUriBuilder);
private final MockMvc mockMvc = standaloneSetup(this.destroyer).build();
@Test
public void destroy() throws DestructionException {
this.destroyer.destroy();
runRunnables();
verify(this.infrastructure).destroy(this.member1);
verify(this.infrastructure, never()).destroy(this.member2);
}
@Test
public void destroyDryRun() throws DestructionException {
Destroyer destroyer = new Destroyer(true, this.executorService, this.fateEngine, this.infrastructure,
this.reporter, this.stateProvider, "", this.taskRepository, this.taskUriBuilder);
destroyer.destroy();
runRunnables();
verify(this.infrastructure, never()).destroy(this.member1);
verify(this.infrastructure, never()).destroy(this.member2);
}
@Test
public void destroyError() throws DestructionException, IllegalStateException {
doThrow(new IllegalStateException()).when(this.infrastructure).destroy(this.member1);
this.destroyer.destroy();
verify(this.infrastructure, never()).destroy(this.member1);
}
@Test
@SuppressWarnings("unchecked")
public void destroyFail() throws DestructionException {
doThrow(new DestructionException()).when(this.infrastructure).destroy(this.member1);
this.destroyer.destroy();
verify(this.infrastructure, never()).destroy(this.member1);
}
@Test
public void destroyWhenStopped() throws DestructionException {
when(this.stateProvider.get()).thenReturn(State.STOPPED);
this.destroyer.destroy();
verify(this.infrastructure, never()).getMembers();
}
@Before
@SuppressWarnings("unchecked")
public void executor() throws ExecutionException, InterruptedException {
when((Future<Void>) this.executorService.submit(any(Runnable.class))).thenReturn(this.future);
when(this.future.get()).thenReturn(null);
}
@Test
public void invalidKey() throws Exception {
this.mockMvc.perform(post("/chaos")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"foo\":\"destroy\"}"))
.andExpect(status().isBadRequest());
verify(this.infrastructure, never()).destroy(this.member1);
}
@Test
public void invalidValue() throws Exception {
this.mockMvc.perform(post("/chaos")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"event\":\"foo\"}"))
.andExpect(status().isBadRequest());
verify(this.infrastructure, never()).destroy(this.member1);
}
@Test
public void manualDestroy() throws Exception {
Task task = new Task(2L, Trigger.MANUAL);
when(this.taskRepository.create(Trigger.MANUAL)).thenReturn(task);
when(this.taskUriBuilder.getUri(task)).thenReturn(URI.create("http://foo.com"));
this.mockMvc.perform(post("/chaos")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"event\":\"destroy\"}"))
.andExpect(status().isAccepted())
.andExpect(header().string("Location", "http://foo.com"));
runRunnables();
verify(this.infrastructure).destroy(this.member1);
verify(this.infrastructure, never()).destroy(this.member2);
}
@Before
public void members() {
when(this.infrastructure.getMembers()).thenReturn(this.members);
when(this.fateEngine.shouldDie(this.member1)).thenReturn(true);
when(this.fateEngine.shouldDie(this.member2)).thenReturn(false);
when(this.taskRepository.create(Trigger.SCHEDULED)).thenReturn(new Task(1L, Trigger.SCHEDULED));
}
private void runRunnables() {
verify(this.executorService, atMost(1)).execute(this.runnableCaptor.capture());
this.runnableCaptor.getAllValues().forEach(Runnable::run);
verify(this.executorService, times(2)).submit(this.runnableCaptor.capture());
this.runnableCaptor.getAllValues().forEach(Runnable::run);
}
}