package rocks.inspectit.server.alerting; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.slf4j.Logger; import org.testng.annotations.Test; import rocks.inspectit.server.alerting.state.AlertingState; import rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent; import rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent.AlertingDefinitionCreatedEvent; import rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent.AlertingDefinitionDeletedEvent; import rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent.AlertingDefinitionLoadedEvent; import rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent.AlertingDefinitionUpdateEvent; import rocks.inspectit.shared.all.testbase.TestBase; import rocks.inspectit.shared.cs.ci.AlertingDefinition; import rocks.inspectit.shared.cs.communication.data.cmr.Alert; import rocks.inspectit.shared.cs.communication.data.cmr.AlertClosingReason; /** * Tests for the {@link AlertingScheduler}. * * @author Marius Oehler * */ @SuppressWarnings("PMD") public class AlertingSchedulerTest extends TestBase { @InjectMocks AlertingScheduler alertingScheduler; @Mock Logger logger; @Mock ThresholdChecker thresholdChecker; @Mock ScheduledExecutorService executorService; /** * Test the {@link AlertingScheduler#updateState()} method. */ public static class UpdateState extends AlertingSchedulerTest { @Test public void deactivate() { alertingScheduler.active = false; alertingScheduler.updateState(); verifyZeroInteractions(thresholdChecker); verifyZeroInteractions(executorService); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void activate() { ScheduledFuture future = Mockito.mock(ScheduledFuture.class); when(executorService.scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES)).thenReturn(future); alertingScheduler.active = true; alertingScheduler.updateState(); verify(executorService).scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES); verifyNoMoreInteractions(executorService); verifyZeroInteractions(thresholdChecker); verifyZeroInteractions(future); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void activateWhenActive() { ScheduledFuture future = Mockito.mock(ScheduledFuture.class); when(future.isDone()).thenReturn(false); when(executorService.scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES)).thenReturn(future); alertingScheduler.active = true; alertingScheduler.updateState(); alertingScheduler.updateState(); verify(executorService).scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES); verifyNoMoreInteractions(executorService); verify(future).isDone(); verifyNoMoreInteractions(future); verifyZeroInteractions(thresholdChecker); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void activateWhenInactive() { ScheduledFuture future = Mockito.mock(ScheduledFuture.class); when(future.isDone()).thenReturn(true); when(executorService.scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES)).thenReturn(future); alertingScheduler.active = true; alertingScheduler.updateState(); alertingScheduler.updateState(); verify(executorService, times(2)).scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES); verifyNoMoreInteractions(executorService); verify(future).isDone(); verifyNoMoreInteractions(future); verifyZeroInteractions(thresholdChecker); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void deactivateWhenActive() { ScheduledFuture future = Mockito.mock(ScheduledFuture.class); when(future.isDone()).thenReturn(false); when(executorService.scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES)).thenReturn(future); alertingScheduler.active = true; alertingScheduler.updateState(); alertingScheduler.active = false; alertingScheduler.updateState(); verify(executorService).scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES); verifyNoMoreInteractions(executorService); verify(future).isDone(); verify(future).cancel(false); verifyNoMoreInteractions(future); verifyZeroInteractions(thresholdChecker); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void deactivateWhenInactive() { ScheduledFuture future = Mockito.mock(ScheduledFuture.class); when(future.isDone()).thenReturn(false, true); when(executorService.scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES)).thenReturn(future); alertingScheduler.active = true; alertingScheduler.updateState(); alertingScheduler.active = false; alertingScheduler.updateState(); alertingScheduler.updateState(); verify(executorService).scheduleAtFixedRate(alertingScheduler, 0L, AlertingScheduler.CHECK_INTERVAL, TimeUnit.MINUTES); verifyNoMoreInteractions(executorService); verify(future, times(2)).isDone(); verify(future).cancel(false); verifyNoMoreInteractions(future); verifyZeroInteractions(thresholdChecker); } } /** * Test the {@link AlertingScheduler#run()} method. */ public static class Run extends AlertingSchedulerTest { @Test public void checkExistingAlertingStates() throws Exception { AlertingDefinition definitionOne = mock(AlertingDefinition.class); AlertingDefinition definitionTwo = mock(AlertingDefinition.class); when(definitionOne.getTimeRange(any(TimeUnit.class))).thenReturn(1L); when(definitionTwo.getTimeRange(any(TimeUnit.class))).thenReturn(3600000L); alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionTwo)); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { ((AlertingState) invocation.getArguments()[0]).setLastCheckTime(System.currentTimeMillis()); return null; } }).when(thresholdChecker).checkThreshold(any(AlertingState.class)); alertingScheduler.run(); // both are checked Thread.sleep(10); alertingScheduler.run(); // only first is checked ArgumentCaptor<AlertingState> stateCaptor = ArgumentCaptor.forClass(AlertingState.class); verify(thresholdChecker, times(3)).checkThreshold(stateCaptor.capture()); verifyNoMoreInteractions(thresholdChecker); verifyZeroInteractions(executorService); assertThat(stateCaptor.getAllValues().get(0).getAlertingDefinition(), equalTo(definitionOne)); assertThat(stateCaptor.getAllValues().get(1).getAlertingDefinition(), equalTo(definitionTwo)); assertThat(stateCaptor.getAllValues().get(2).getAlertingDefinition(), equalTo(definitionOne)); } @Test public void noAlertingStates() throws Exception { alertingScheduler.run(); verifyZeroInteractions(thresholdChecker); verifyZeroInteractions(executorService); } @Test public void thresholdCheckerThrowsException() throws Exception { AlertingDefinition definitionOne = mock(AlertingDefinition.class); alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); doThrow(RuntimeException.class).when(thresholdChecker).checkThreshold(any(AlertingState.class)); alertingScheduler.run(); verify(thresholdChecker).checkThreshold(any(AlertingState.class)); verifyNoMoreInteractions(thresholdChecker); verifyZeroInteractions(executorService); } } /** * Test the * {@link AlertingScheduler#onApplicationEvent(rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent)} * method. * */ public static class OnApplicationEvent extends AlertingSchedulerTest { @Mock AlertingDefinition definitionOne; @Mock AlertingDefinition definitionTwo; @SuppressWarnings("unchecked") private List<AlertingState> getAlertingStates() { try { Field field = AlertingScheduler.class.getDeclaredField("alertingStates"); field.setAccessible(true); return (List<AlertingState>) field.get(alertingScheduler); } catch (Exception e) { throw new RuntimeException(e); } } @Test public void nullEvent() { alertingScheduler.onApplicationEvent(null); verifyZeroInteractions(thresholdChecker, executorService); } @Test public void loadingEvent() { AbstractAlertingDefinitionEvent event = new AlertingDefinitionLoadedEvent(this, Arrays.asList(definitionOne, definitionTwo)); alertingScheduler.onApplicationEvent(event); verifyZeroInteractions(thresholdChecker, executorService, definitionOne, definitionTwo); assertThat(getAlertingStates(), hasSize(2)); assertThat(getAlertingStates().get(0).getAlertingDefinition(), equalTo(definitionOne)); assertThat(getAlertingStates().get(1).getAlertingDefinition(), equalTo(definitionTwo)); } @Test public void createEvent() { AbstractAlertingDefinitionEvent event = new AlertingDefinitionCreatedEvent(this, definitionOne); alertingScheduler.onApplicationEvent(event); verifyZeroInteractions(thresholdChecker, executorService, definitionOne); assertThat(getAlertingStates(), hasSize(1)); assertThat(getAlertingStates().get(0).getAlertingDefinition(), equalTo(definitionOne)); } @Test public void deletedEvent() { alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); assertThat(getAlertingStates(), hasSize(1)); AbstractAlertingDefinitionEvent event = new AlertingDefinitionDeletedEvent(this, definitionOne); when(definitionOne.getId()).thenReturn("id"); alertingScheduler.onApplicationEvent(event); verify(definitionOne, times(2)).getId(); verifyNoMoreInteractions(definitionOne); verifyZeroInteractions(thresholdChecker, executorService); assertThat(getAlertingStates(), hasSize(0)); } @Test public void deletedEventAlertActive() { alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); assertThat(getAlertingStates(), hasSize(1)); AbstractAlertingDefinitionEvent event = new AlertingDefinitionDeletedEvent(this, definitionOne); when(definitionOne.getId()).thenReturn("id"); Alert alertMock = mock(Alert.class); // set manually because it would done by the threshold checker which is also mocked getAlertingStates().get(0).setAlert(alertMock); alertingScheduler.onApplicationEvent(event); ArgumentCaptor<AlertClosingReason> reasonCapture = ArgumentCaptor.forClass(AlertClosingReason.class); verify(alertMock, times(1)).close(any(Long.class), reasonCapture.capture()); verify(definitionOne, times(2)).getId(); verifyNoMoreInteractions(definitionOne); verifyZeroInteractions(thresholdChecker, executorService); assertThat(getAlertingStates(), hasSize(0)); assertThat(reasonCapture.getValue(), equalTo(AlertClosingReason.ALERTING_DEFINITION_DELETED)); } @Test public void deletedUnknownEvent() { alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); assertThat(getAlertingStates(), hasSize(1)); AbstractAlertingDefinitionEvent event = new AlertingDefinitionDeletedEvent(this, definitionTwo); when(definitionOne.getId()).thenReturn("id"); alertingScheduler.onApplicationEvent(event); verify(definitionOne).getId(); verify(definitionTwo).getId(); verifyNoMoreInteractions(definitionOne, definitionTwo); verifyZeroInteractions(thresholdChecker, executorService); assertThat(getAlertingStates(), hasSize(1)); } @Test public void updateEvent() { alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); assertThat(getAlertingStates().get(0).getAlertingDefinition(), equalTo(definitionOne)); AbstractAlertingDefinitionEvent event = new AlertingDefinitionUpdateEvent(this, definitionTwo); when(definitionOne.getId()).thenReturn("id"); when(definitionTwo.getId()).thenReturn("id"); alertingScheduler.onApplicationEvent(event); verify(definitionOne).getId(); verify(definitionTwo).getId(); verifyNoMoreInteractions(definitionOne, definitionTwo); verifyZeroInteractions(thresholdChecker, executorService); assertThat(getAlertingStates(), hasSize(1)); assertThat(getAlertingStates().get(0).getAlertingDefinition(), equalTo(definitionTwo)); } @Test public void updateEventAlertActive() { alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); AbstractAlertingDefinitionEvent event = new AlertingDefinitionUpdateEvent(this, definitionOne); when(definitionOne.getId()).thenReturn("id"); Alert alertMock = mock(Alert.class); // set manually because it would done by the threshold checker which is also mocked getAlertingStates().get(0).setAlert(alertMock); alertingScheduler.onApplicationEvent(event); verify(alertMock, times(1)).setAlertingDefinition(definitionOne); verify(definitionOne, times(2)).getId(); verifyNoMoreInteractions(definitionOne); verifyZeroInteractions(thresholdChecker, executorService); assertThat(getAlertingStates(), hasSize(1)); } @Test public void updateUnknownEvent() { alertingScheduler.onApplicationEvent(new AlertingDefinitionCreatedEvent(this, definitionOne)); AbstractAlertingDefinitionEvent event = new AlertingDefinitionUpdateEvent(this, definitionTwo); when(definitionOne.getId()).thenReturn("id"); when(definitionTwo.getId()).thenReturn("id_2"); alertingScheduler.onApplicationEvent(event); assertThat(getAlertingStates(), hasSize(1)); assertThat(getAlertingStates().get(0).getAlertingDefinition(), equalTo(definitionOne)); verify(definitionOne).getId(); verify(definitionTwo).getId(); verifyNoMoreInteractions(definitionOne, definitionTwo); verifyZeroInteractions(thresholdChecker, executorService); assertThat(getAlertingStates(), hasSize(1)); } } }