package uk.ac.ox.zoo.seeg.abraid.mp.datamanager.process; import org.joda.time.DateTime; import org.joda.time.DateTimeUtils; import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.DiseaseGroup; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.DiseaseService; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Tests the DiseaseProcessGatekeeper. * Copyright (c) 2014 University of Oxford */ public class DiseaseProcessGatekeeperTest { private DiseaseService diseaseService; private DiseaseProcessGatekeeper diseaseProcessGatekeeper; private DiseaseGroup diseaseGroup; private static final int DISEASE_GROUP_ID = 87; @Before public void setUp() { diseaseService = mock(DiseaseService.class); diseaseProcessGatekeeper = new DiseaseProcessGatekeeper(diseaseService); when(diseaseService.subtractMaxDaysOnValidator(any(DateTime.class))).thenAnswer(new Answer<LocalDate>() { @Override public LocalDate answer(InvocationOnMock invocationOnMock) throws Throwable { return ((DateTime) invocationOnMock.getArguments()[0]).toLocalDate().minusDays(7); } }); // The default disease group for dengue has automatic model runs set to true diseaseGroup = mock(DiseaseGroup.class); when(diseaseGroup.getId()).thenReturn(DISEASE_GROUP_ID); when(diseaseGroup.getName()).thenReturn("Dengue"); when(diseaseGroup.getMaxDaysBetweenModelRuns()).thenReturn(7); when(diseaseGroup.getAutomaticModelRunsStartDate()).thenReturn(DateTime.now()); when(diseaseGroup.getMinDistanceFromDiseaseExtentForTriggering()).thenReturn(0.0); when(diseaseGroup.getMaxEnvironmentalSuitabilityForTriggering()).thenReturn(0.0); when(diseaseService.getDiseaseGroupById(DISEASE_GROUP_ID)).thenReturn(diseaseGroup); DateTimeUtils.setCurrentMillisFixed(1429543707000L); } @Test public void modelShouldRunWhenLastModelRunPrepDateIsNull() throws Exception { expectModelShouldRun(null, false, true); expectModelShouldRun(null, false, false); expectModelShouldRun(null, false, null); expectModelShouldRun(null, true, true); expectModelShouldRun(null, true, false); expectModelShouldRun(null, true, null); expectModelShouldRun(null, null, true); expectModelShouldRun(null, null, false); expectModelShouldRun(null, null, null); } @Test public void modelShouldRunWhenEnoughDaysHaveElapsedSinceLastModelRunPrepDate() throws Exception { expectModelShouldRun(true, false, true); expectModelShouldRun(true, false, false); expectModelShouldRun(true, false, null); expectModelShouldRun(true, true, true); expectModelShouldRun(true, true, false); expectModelShouldRun(true, true, null); expectModelShouldRun(true, null, true); expectModelShouldRun(true, null, false); expectModelShouldRun(true, null, null); } @Test public void modelShouldRunWhenNewLocationsIsOverThreshold() throws Exception { expectModelShouldRun(true, false, true); expectModelShouldRun(false, false, true); expectModelShouldRun(null, false, true); expectModelShouldRun(true, true, true); expectModelShouldRun(false, true, true); expectModelShouldRun(null, true, true); expectModelShouldRun(true, null, true); expectModelShouldRun(false, null, true); expectModelShouldRun(null, null, true); } @Test public void modelShouldRunWhenExtentHasChanged() throws Exception { expectModelShouldRun(false, true, true); expectModelShouldRun(true, true, false); expectModelShouldRun(false, true, false); expectModelShouldRun(null, true, false); expectModelShouldRun(true, true, true); expectModelShouldRun(false, true, true); expectModelShouldRun(null, true, true); expectModelShouldRun(true, true, null); expectModelShouldRun(false, true, null); expectModelShouldRun(null, true, null); } @Test public void modelShouldNotRunWhenNoConditionsMet() throws Exception { expectModelShouldNotToRun(false, false, false); expectModelShouldNotToRun(false, null, false); expectModelShouldNotToRun(false, false, null); expectModelShouldNotToRun(false, null, null); } @Test public void modelShouldNotRunWhenMinDistanceFromDiseaseExtentNotDefined() throws Exception { when(diseaseGroup.getMinDistanceFromDiseaseExtentForTriggering()).thenReturn(null); expectModelShouldNotToRun(false, false, true); } @Test public void modelShouldNotRunWhenMaxEnvironmentalSuitabilityNotDefined() throws Exception { when(diseaseGroup.getMaxEnvironmentalSuitabilityForTriggering()).thenReturn(null); expectModelShouldNotToRun(false, false, true); } @Test public void extentShouldRunWhenLastExtentGenerationDateIsNull() throws Exception { expectExtentShouldRun(null, false, true); expectExtentShouldRun(null, false, false); expectExtentShouldRun(null, false, null); expectExtentShouldRun(null, true, true); expectExtentShouldRun(null, true, false); expectExtentShouldRun(null, true, null); expectExtentShouldRun(null, null, true); expectExtentShouldRun(null, null, false); expectExtentShouldRun(null, null, null); } @Test public void extentShouldRunWhenEnoughDaysHaveElapsedSinceLastExtentGenerationDate() throws Exception { expectExtentShouldRun(true, false, true); expectExtentShouldRun(true, false, false); expectExtentShouldRun(true, false, null); expectExtentShouldRun(true, true, true); expectExtentShouldRun(true, true, false); expectExtentShouldRun(true, true, null); expectExtentShouldRun(true, null, true); expectExtentShouldRun(true, null, false); expectExtentShouldRun(true, null, null); } @Test public void extentShouldRunWhenNewLocationsIsOverThreshold() throws Exception { expectExtentShouldRun(true, false, true); expectExtentShouldRun(false, false, true); expectExtentShouldRun(null, false, true); expectExtentShouldRun(true, true, true); expectExtentShouldRun(false, true, true); expectExtentShouldRun(null, true, true); expectExtentShouldRun(true, null, true); expectExtentShouldRun(false, null, true); expectExtentShouldRun(null, null, true); } @Test public void extentShouldNotRunWhenNoConditionsMet() throws Exception { expectExtentShouldNotToRun(false, false, false); expectExtentShouldNotToRun(false, null, false); expectExtentShouldNotToRun(false, false, null); expectExtentShouldNotToRun(false, null, null); // Extent class changes is not a condition for extent generation expectExtentShouldNotToRun(false, true, false); expectExtentShouldNotToRun(false, true, null); } @Test public void extentShouldNotRunWhenMinDistanceFromDiseaseExtentNotDefined() throws Exception { when(diseaseGroup.getMinDistanceFromDiseaseExtentForTriggering()).thenReturn(null); expectExtentShouldNotToRun(false, false, true); } @Test public void extentShouldNotRunWhenMaxEnvironmentalSuitabilityNotDefined() throws Exception { when(diseaseGroup.getMaxEnvironmentalSuitabilityForTriggering()).thenReturn(null); expectExtentShouldNotToRun(false, false, true); } private void expectModelShouldRun(Boolean weekHasElapsed, Boolean hasExtentChanged, Boolean newLocationCountOverThreshold) { // Arrange and Act boolean result = arrangeAndAct(weekHasElapsed, hasExtentChanged, newLocationCountOverThreshold); // Assert assertThat(result).isTrue(); } private void expectModelShouldNotToRun(Boolean weekHasElapsed, Boolean hasExtentChanged, Boolean newLocationCountOverThreshold) { // Arrange and Act boolean result = arrangeAndAct(weekHasElapsed, hasExtentChanged, newLocationCountOverThreshold); // Assert assertThat(result).isFalse(); } private void expectExtentShouldRun(Boolean weekHasElapsed, Boolean hasExtentChanged, Boolean newLocationCountOverThreshold) { // Arrange and Act boolean result = arrangeAndActExtent(weekHasElapsed, hasExtentChanged, newLocationCountOverThreshold); // Assert assertThat(result).isTrue(); } private void expectExtentShouldNotToRun(Boolean weekHasElapsed, Boolean hasExtentChanged, Boolean newLocationCountOverThreshold) { // Arrange and Act boolean result = arrangeAndActExtent(weekHasElapsed, hasExtentChanged, newLocationCountOverThreshold); // Assert assertThat(result).isFalse(); } private boolean arrangeAndAct(Boolean weekHasElapsed, Boolean hasExtentChanged, Boolean newLocationCountOverThreshold) { // Arrange long newLocationsCount = 10; setLastModelRunPrepDate(weekHasElapsed); setLastClassChangeDate(weekHasElapsed, hasExtentChanged); when(diseaseService.getDistinctLocationsCountForTriggeringModelRun(diseaseGroup, diseaseGroup.getLastModelRunPrepDate())) .thenReturn(newLocationsCount); setMinNewLocations(newLocationsCount, newLocationCountOverThreshold); // Act return diseaseProcessGatekeeper.modelShouldRun(DISEASE_GROUP_ID); } private boolean arrangeAndActExtent(Boolean weekHasElapsed, Boolean hasExtentChanged, Boolean newLocationCountOverThreshold) { // Arrange long newLocationsCount = 10; setLastExtentGenerationDate(weekHasElapsed); setLastClassChangeDate(weekHasElapsed, hasExtentChanged); when(diseaseService.getDistinctLocationsCountForTriggeringModelRun(diseaseGroup, diseaseGroup.getLastModelRunPrepDate())) .thenReturn(newLocationsCount); setMinNewLocations(newLocationsCount, newLocationCountOverThreshold); // Act return diseaseProcessGatekeeper.extentShouldRun(DISEASE_GROUP_ID); } private void setLastClassChangeDate(Boolean weekHasElapsed, Boolean hasChanged) { DateTime changeDate = null; if (hasChanged != null && weekHasElapsed != null) { int runDays = weekHasElapsed ? 7 : 1; int changeDays = hasChanged ? runDays - 1 : runDays + 1; changeDate = DateTime.now().minusDays(changeDays); } when(diseaseService.getLatestDiseaseExtentClassChangeDateByDiseaseGroupId(DISEASE_GROUP_ID)).thenReturn(changeDate); } private void setLastModelRunPrepDate(Boolean weekHasElapsed) { DateTime lastModelRunPrepDate = null; if (weekHasElapsed != null) { int days = weekHasElapsed ? 7 : 1; lastModelRunPrepDate = DateTime.now().minusDays(days); } when(diseaseGroup.getLastModelRunPrepDate()).thenReturn(lastModelRunPrepDate); } private void setLastExtentGenerationDate(Boolean weekHasElapsed) { DateTime lastExtentGenerationDate = null; if (weekHasElapsed != null) { int days = weekHasElapsed ? 7 : 1; lastExtentGenerationDate = DateTime.now().minusDays(days); } when(diseaseGroup.getLastExtentGenerationDate()).thenReturn(lastExtentGenerationDate); } private void setMinNewLocations(long newLocationsCount, Boolean newLocationCountOverThreshold) { Integer minNewLocations = null; if (newLocationCountOverThreshold != null) { long thresholdAdjustment = newLocationCountOverThreshold ? -1 : +1; minNewLocations = (int) (newLocationsCount + thresholdAdjustment); } when(diseaseGroup.getMinNewLocationsTrigger()).thenReturn(minNewLocations); } }