package uk.ac.ox.zoo.seeg.abraid.mp.common.service.workflow.support; import ch.lambdaj.function.convert.Converter; import org.apache.commons.mail.EmailException; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import uk.ac.ox.zoo.seeg.abraid.mp.common.AbstractCommonSpringIntegrationTests; import uk.ac.ox.zoo.seeg.abraid.mp.common.dao.*; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.*; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.DiseaseService; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.EmailService; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.GeometryService; import java.util.HashSet; import java.util.List; import java.util.Set; import static ch.lambdaj.Lambda.*; import static com.googlecode.catchexception.CatchException.catchException; import static com.googlecode.catchexception.CatchException.caughtException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; /** * Tests that the ModelRunOccurrencesSelectorHelper returns expected list of occurrences under each condition. * Copyright (c) 2014 University of Oxford */ @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ModelRunOccurrencesSelectorHelperIntegrationTest extends AbstractCommonSpringIntegrationTests { @Autowired private DiseaseService diseaseService; @Autowired private GeometryService geometryService; @Autowired private AlertDao alertDao; @Autowired private DiseaseOccurrenceDao diseaseOccurrenceDao; @Autowired private FeedDao feedDao; @Autowired private LocationDao locationDao; @Autowired private ProvenanceDao provenanceDao; private EmailService emailService; @Before public void setUp() { emailService = mock(EmailService.class); } @Test public void selectModelRunDiseaseOccurrencesThrowsExceptionWhenFewerOccurrencesThanMDV() { // Arrange int diseaseGroupId = 87; ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act catchException(selector).selectModelRunDiseaseOccurrences(); // Assert assertThat(diseaseService.getDiseaseOccurrencesForModelRunRequest(diseaseGroupId, false)).hasSize(27); assertThat(diseaseService.getDiseaseGroupById(diseaseGroupId).getMinDataVolume()).isEqualTo(500); assertThat(caughtException()).isInstanceOf(ModelRunWorkflowException.class); verifySendEmail(); } @Test public void selectModelRunDiseaseOccurrencesThrowsExceptionWhenFewerOccurrencesThanMDVAndAutomaticModelRunsAreDisabled() { // Arrange - NB. Automatic model runs are disabled by default int diseaseGroupId = 87; ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act catchException(selector).selectModelRunDiseaseOccurrences(); // Assert assertThat(diseaseService.getDiseaseOccurrencesForModelRunRequest(diseaseGroupId, false)).hasSize(27); assertThat(diseaseService.getDiseaseGroupById(diseaseGroupId).getMinDataVolume()).isEqualTo(500); assertThat(caughtException()).isInstanceOf(ModelRunWorkflowException.class); verifySendEmail(); } @Test public void selectModelRunDiseaseOccurrencesThrowsExceptionWhenFewerOccurrencesThanMDVAndGoldStandardDataUsed() { // Arrange - NB. Automatic model runs are disabled by default int diseaseGroupId = 87; addManuallyUploadedGoldStandardOccurrences(); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, true); // Act catchException(selector).selectModelRunDiseaseOccurrences(); // Assert assertThat(diseaseService.getDiseaseOccurrencesForModelRunRequest(diseaseGroupId, true)).hasSize(2); assertThat(diseaseService.getDiseaseGroupById(diseaseGroupId).getMinDataVolume()).isEqualTo(500); assertThat(caughtException()).isInstanceOf(ModelRunWorkflowException.class); verifySendEmail(); } @Test public void selectModelRunDiseaseOccurrencesReturnsFirstSubsetWhenOccursInAfricaIsNull() throws Exception { // Arrange int diseaseGroupId = 87; int minDataVolume = 20; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs diseaseGroup.setMinDataVolume(minDataVolume); // Ensure MDVSatisfied check will pass diseaseGroup.setOccursInAfrica(null); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act List<DiseaseOccurrence> occurrences = selector.selectModelRunDiseaseOccurrences(); // Assert assertThat(occurrences).hasSize(minDataVolume); } @Test public void selectModelRunDiseaseOccurrencesReturnsAllOccurrencesWhenAutomaticModelRunsAreDisabled() throws Exception { // Arrange - NB. Automatic model runs are disabled by default int diseaseGroupId = 87; int minDataVolume = 20; int expectedAllOccurrencesSize = 27; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setMinDataVolume(minDataVolume); // Ensure MDVSatisfied check will pass diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act List<DiseaseOccurrence> occurrences = selector.selectModelRunDiseaseOccurrences(); // Assert assertThat(occurrences).hasSize(expectedAllOccurrencesSize); } @Test public void selectModelRunDiseaseOccurrencesReturnsFirstSubsetWhenOccursInAfricaIsTrueButParametersNotDefined() { // Arrange int diseaseGroupId = 87; int minDataVolume = 20; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs diseaseGroup.setMinDataVolume(minDataVolume); // Ensure MDVSatisfied check will pass diseaseGroup.setOccursInAfrica(true); diseaseGroup.setHighFrequencyThreshold(null); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act List<DiseaseOccurrence> occurrences = selector.selectModelRunDiseaseOccurrences(); // Assert assertThat(occurrences).hasSize(minDataVolume); } @Test public void selectModelRunDiseaseOccurrencesReturnsFirstSubsetWhenOccursInAfricaIsFalseButParametersNotDefined() { // Arrange int diseaseGroupId = 87; int minDataVolume = 20; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs diseaseGroup.setMinDataVolume(minDataVolume); // Ensure MDVSatisfied check will pass diseaseGroup.setOccursInAfrica(false); diseaseGroup.setMinDistinctCountries(null); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act List<DiseaseOccurrence> occurrences = selector.selectModelRunDiseaseOccurrences(); // Assert assertThat(occurrences).hasSize(minDataVolume); } @Test public void selectorThrowsExceptionWhenMDSNotMetBeforeRunningOutOfOccurrencesForAfricanDiseaseGroup() { // Arrange int diseaseGroupId = 87; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs diseaseGroup.setMinDataVolume(20); // Ensure MDVSatisfied check will pass diseaseGroup.setOccursInAfrica(true); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act catchException(selector).selectModelRunDiseaseOccurrences(); // Assert assertThat(caughtException()).isInstanceOf(ModelRunWorkflowException.class); verifySendEmail(); } @Test public void selectorReturnsNullWhenMDSNotMetBeforeRunningOutOfOccurrencesForOtherDiseaseGroup() { // Arrange int diseaseGroupId = 87; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs diseaseGroup.setMinDataVolume(20); // Ensure MDVSatisfied check will pass diseaseGroup.setOccursInAfrica(false); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act catchException(selector).selectModelRunDiseaseOccurrences(); // Assert assertThat(caughtException()).isInstanceOf(ModelRunWorkflowException.class); verifySendEmail(); } @Test public void selectorAddsOccurrencesUntilMinDataSpreadIsSatisfiedForAfricanDiseaseGroup() { // Arrange int diseaseGroupId = 87; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs addOccurrences(diseaseGroup); diseaseGroup.setMinDataVolume(2); // Selector will initially get the first 2 occurrences diseaseGroup.setOccursInAfrica(true); diseaseGroup.setMinDistinctCountries(2); diseaseGroup.setHighFrequencyThreshold(2); // Then add the 3rd occurrence to satisfy high frequency check diseaseGroup.setMinHighFrequencyCountries(1); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act List<DiseaseOccurrence> occurrences = selector.selectModelRunDiseaseOccurrences(); // Assert assertThat(occurrences).hasSize(3); assertThat(extractDistinctGaulCodes(occurrences)).hasSize(2); } // Set up so that there is at least 1 occurrence in (minDistinctCountries = 2) countries, // and at least (highFrequencyThreshold = 2) occurrences in (minHighFrequencyCountries = 1) country. // NB. Locations must not have LocationPrecision.COUNTRY because they are not included in model run request. private void addOccurrences(DiseaseGroup diseaseGroup) { Location location1 = locationDao.getById(24204); // Precise location in Somalia Location location2 = locationDao.getById(1574); // Admin 1 location in Zimbabwe Alert alert = alertDao.getById(212855); DiseaseOccurrence o1 = new DiseaseOccurrence(1, diseaseGroup, location1, alert, DiseaseOccurrenceStatus.READY, 0.1, DateTime.now()); DiseaseOccurrence o2 = new DiseaseOccurrence(2, diseaseGroup, location2, alert, DiseaseOccurrenceStatus.READY, 0.1, DateTime.now()); DiseaseOccurrence o3 = new DiseaseOccurrence(3, diseaseGroup, location2, alert, DiseaseOccurrenceStatus.READY, 0.1, DateTime.now()); diseaseOccurrenceDao.save(o1); diseaseOccurrenceDao.save(o2); diseaseOccurrenceDao.save(o3); } @Test public void selectorAddsOccurrencesUntilMinDataSpreadIsSatisfiedForOtherDiseaseGroup() { // Arrange int diseaseGroupId = 87; int minDistinctCountries = 2; DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(diseaseGroupId); diseaseGroup.setAutomaticModelRunsStartDate(DateTime.now()); // Enable automatic model runs diseaseGroup.setMinDataVolume(1); diseaseGroup.setOccursInAfrica(false); diseaseGroup.setMinDistinctCountries(minDistinctCountries); diseaseService.saveDiseaseGroup(diseaseGroup); ModelRunOccurrencesSelectorHelper selector = new ModelRunOccurrencesSelectorHelper(diseaseService, geometryService, emailService, diseaseGroupId, false); // Act List<DiseaseOccurrence> occurrences = selector.selectModelRunDiseaseOccurrences(); // Assert assertThat(occurrences).hasSize(2); assertThat(extractDistinctGaulCodes(occurrences)).hasSize(minDistinctCountries); } private Set<Integer> extractDistinctGaulCodes(List<DiseaseOccurrence> occurrences) { Set<Location> locations = new HashSet<>(extract(occurrences, on(DiseaseOccurrence.class).getLocation())); return new HashSet<>(convert(locations, new Converter<Location, Integer>() { public Integer convert(Location location) { return location.getCountryGaulCode(); } })); } private void addManuallyUploadedGoldStandardOccurrences() { Feed feed = new Feed("Test feed", provenanceDao.getByName(ProvenanceNames.MANUAL_GOLD_STANDARD)); feedDao.save(feed); createManuallyUploadedDiseaseOccurrenceForDengue(feed); createManuallyUploadedDiseaseOccurrenceForDengue(feed); } private void createManuallyUploadedDiseaseOccurrenceForDengue(Feed feed) { DiseaseGroup diseaseGroup = diseaseService.getDiseaseGroupById(87); Location location = locationDao.getById(80); Alert alert = new Alert(); alert.setFeed(feed); DiseaseOccurrence occurrence = new DiseaseOccurrence(); occurrence.setDiseaseGroup(diseaseGroup); occurrence.setLocation(location); occurrence.setAlert(alert); occurrence.setFinalWeighting(1.0); occurrence.setFinalWeightingExcludingSpatial(1.0); occurrence.setStatus(DiseaseOccurrenceStatus.READY); occurrence.setOccurrenceDate(DateTime.now()); diseaseOccurrenceDao.save(occurrence); } private void verifySendEmail() { try { verify(emailService).sendEmail(eq("Minimum Data Volume/Spread Not Satisfied"), anyString()); } catch (EmailException e) { throw new RuntimeException(e); } } }