package ca.intelliware.ihtsdo.mlds.web.rest; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.util.NestedServletException; import ca.intelliware.ihtsdo.mlds.domain.Affiliate; import ca.intelliware.ihtsdo.mlds.domain.AffiliateDetails; import ca.intelliware.ihtsdo.mlds.domain.ApprovalState; import ca.intelliware.ihtsdo.mlds.domain.Country; import ca.intelliware.ihtsdo.mlds.domain.ExtensionApplication; import ca.intelliware.ihtsdo.mlds.domain.MailingAddress; import ca.intelliware.ihtsdo.mlds.domain.PrimaryApplication; import ca.intelliware.ihtsdo.mlds.domain.StandingState; import ca.intelliware.ihtsdo.mlds.domain.User; import ca.intelliware.ihtsdo.mlds.repository.AffiliateDetailsRepository; import ca.intelliware.ihtsdo.mlds.repository.AffiliateRepository; import ca.intelliware.ihtsdo.mlds.repository.AffiliateSearchRepository; import ca.intelliware.ihtsdo.mlds.repository.UserRepository; import ca.intelliware.ihtsdo.mlds.security.ihtsdo.CurrentSecurityContext; import ca.intelliware.ihtsdo.mlds.security.ihtsdo.SecurityContextSetup; import ca.intelliware.ihtsdo.mlds.service.AffiliateAuditEvents; import ca.intelliware.ihtsdo.mlds.service.AffiliateDeleter; import ca.intelliware.ihtsdo.mlds.service.affiliatesimport.AffiliateImportAuditEvents; import ca.intelliware.ihtsdo.mlds.service.affiliatesimport.AffiliatesExporterService; import ca.intelliware.ihtsdo.mlds.service.affiliatesimport.AffiliatesImportGenerator; import ca.intelliware.ihtsdo.mlds.service.affiliatesimport.AffiliatesImportSpec; import ca.intelliware.ihtsdo.mlds.web.SessionService; public class AffiliateResourceTest { @Mock private ApplicationAuthorizationChecker applicationAuthorizationChecker; @Mock private AffiliateRepository affiliateRepository; @Mock private AffiliateSearchRepository affiliateSearchRepository; @Mock private AffiliateDetailsRepository affiliateDetailsRepository; @Mock private AffiliateAuditEvents affiliateAuditEvents; @Mock private AffiliateImportAuditEvents affiliateImportAuditEvents; @Mock private AffiliatesExporterService affiliatesExporterService; @Mock private AffiliatesImportGenerator affiliatesImportGenerator; @Mock private AffiliateDeleter affiliateDeleter; @Mock private SessionService sessionService; @Mock private UserRepository userRepository; private MockMvc restUserMockMvc; SecurityContextSetup securityContextSetup = new SecurityContextSetup(); @Before public void setup() { MockitoAnnotations.initMocks(this); AffiliateResource affiliateResource = new AffiliateResource(); affiliateResource.affiliateDetailsRepository = affiliateDetailsRepository; affiliateResource.affiliateRepository = affiliateRepository; affiliateResource.affiliateSearchRepository = affiliateSearchRepository; affiliateResource.applicationAuthorizationChecker = applicationAuthorizationChecker; affiliateResource.affiliateAuditEvents = affiliateAuditEvents; affiliateResource.affiliatesExporterService = affiliatesExporterService; affiliateResource.affiliatesImportGenerator = affiliatesImportGenerator; affiliateResource.userRepository = userRepository; affiliateResource.sessionService = sessionService; affiliateResource.affiliateImportAuditEvents = affiliateImportAuditEvents; affiliateResource.affiliateDeleter = affiliateDeleter; affiliateResource.currentSecurityContext = new CurrentSecurityContext(); securityContextSetup.asAdmin(); this.restUserMockMvc = MockMvcBuilders .standaloneSetup(affiliateResource) .setMessageConverters(new MockMvcJacksonTestSupport().getConfiguredMessageConverters()) .build(); } @Test public void getAffiliateShouldFailForUnknownAffiliate() throws Exception { when(affiliateRepository.findOne(999L)).thenReturn(null); restUserMockMvc.perform(get(Routes.AFFILIATE, 999L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } @Test public void getAffiliate() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.getAffiliateDetails().setFirstName("Test FirstName"); affiliate.getAffiliateDetails().getAddress().setStreet("Test Street"); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(get(Routes.AFFILIATE, 1L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.affiliateDetails.firstName").value("Test FirstName")) .andExpect(jsonPath("$.affiliateDetails.address.street").value("Test Street")); } @Test public void updateAffiliateDetailShouldFailForUnknownAffiliate() throws Exception { when(affiliateRepository.findOne(999L)).thenReturn(null); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 999L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"firstName\": \"Updated FirstName\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } @Test public void updateAffiliateDetailShouldFailWhenNoDetailsSetOnAffiliateYet() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setAffiliateDetails(null); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"firstName\": \"Updated FirstName\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isConflict()); } @Test public void updateAffiliateDetailShouldFailWhenApplicationNotApproved() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.getApplication().setApprovalState(ApprovalState.REJECTED); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"firstName\": \"Updated FirstName\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isConflict()); } @Test public void updateAffiliateDetailShouldUpdateSaveWithSafeFields() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.getAffiliateDetails().setFirstName("Original FirstName"); affiliate.getAffiliateDetails().getAddress().setStreet("Original Street"); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"firstName\": \"Updated FirstName\", \"address\": { \"street\":\"Updated Street\" }, \"billingAddress\": {} }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.firstName").value("Updated FirstName")) .andExpect(jsonPath("$.address.street").value("Updated Street")) ; } @Test public void updateAffiliateDetailShouldUpdateUserName() throws Exception { User user = new User(); user.setLogin("Original Login"); user.setFirstName("Original FirstName"); user.setLastName("Original LastName"); when(userRepository.findByLoginIgnoreCase("user@email.com")).thenReturn(user); Affiliate affiliate = createBlankAffiliate(); affiliate.getAffiliateDetails().setEmail("user@email.com"); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"email\":\"new@email.com\", \"firstName\": \"Updated FirstName\", \"lastName\": \"Updated LastName\", \"address\": { \"street\":\"Updated Street\" }, \"billingAddress\": {} }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) ; Assert.assertEquals("Updated FirstName", user.getFirstName()); Assert.assertEquals("Updated LastName", user.getLastName()); // FIXME MLDS-540 who can change the login? What notifications do we need? Assert.assertEquals("new@email.com", user.getLogin()); } @Test public void updateAffiliateDetailShouldIgnoreOrganizationFieldUpdates() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.getAffiliateDetails().setOrganizationName("Original OrganizationName"); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"organizationName\": \"Updated OrganizationName\", \"address\": {}, \"billingAddress\": {} }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.organizationName").value("Original OrganizationName")) ; } @Test public void updateAffiliateDetailShouldIgnoreAddressCountryFieldUpdates() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.getAffiliateDetails().getAddress().setCountry(createCountry("CA")); affiliate.getAffiliateDetails().getBillingAddress().setCountry(createCountry("CA")); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE_DETAIL, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"address\": { \"country\": {\"isoCode2\":\"DK\"}}, \"billingAddress\": {\"country\": {\"isoCode2\":\"DK\"}} }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.address.country.isoCode2").value("CA")) /* Original address country preserved */ .andExpect(jsonPath("$.billingAddress.country.isoCode2").value("DK")) /* Updated billing country */ ; } @Test public void getAffiliatesImportSpec() throws Exception { AffiliatesImportSpec spec = new AffiliatesImportSpec(); spec.setExample("Example File Content"); when(affiliatesExporterService.exportSpec()).thenReturn(spec); restUserMockMvc.perform(get(Routes.AFFILIATES_CSV_SPEC) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.example").value("Example File Content")) ; } @Test public void exportAffiliatesShouldExportAllAffiliatesInCSVFormat() throws Exception { when(affiliatesExporterService.exportToCSV()).thenReturn("affiliates file content"); restUserMockMvc.perform(get(Routes.AFFILIATES_CSV) .accept("application/csv")) .andExpect(status().isOk()) .andExpect(content().contentType("application/csv;charset=UTF-8")) .andExpect(content().string(Matchers.equalTo("affiliates file content"))) ; } @Test public void exportAffiliatesShouldGenerateSpecifiedNumberOfAffiliatesInCSVFormat() throws Exception { when(affiliatesImportGenerator.generateFile(Mockito.eq(10))).thenReturn("generated affiliates file content"); restUserMockMvc.perform(get(Routes.AFFILIATES_CSV) .param("generate", "10") .accept("application/csv")) .andExpect(status().isOk()) .andExpect(content().contentType("application/csv;charset=UTF-8")) .andExpect(content().string(Matchers.equalTo("generated affiliates file content"))) ; } private Country createCountry(String code) { return new Country(code, code, code); } private Affiliate createBlankAffiliate() { Affiliate affiliate = new Affiliate(); AffiliateDetails affiliateDetails = new AffiliateDetails(); affiliateDetails.setAddress(new MailingAddress()); affiliateDetails.setBillingAddress(new MailingAddress()); affiliate.setAffiliateDetails(affiliateDetails); PrimaryApplication application = new PrimaryApplication(1L); application.setApprovalState(ApprovalState.APPROVED); affiliate.setApplication(application); return affiliate; } @Test public void updateAffiliateShouldFailForUnknownAffiliate() throws Exception { when(affiliateRepository.findOne(999L)).thenReturn(null); restUserMockMvc.perform(put(Routes.AFFILIATE, 999L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"notesInternal\": \"Updated notes\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } @Test public void updateAffiliateShouldFailIfCurrentUserCannotManage() throws Exception { when(affiliateRepository.findOne(999L)).thenReturn(createBlankAffiliate()); Mockito.doThrow(new IllegalStateException("not allowed")).when(applicationAuthorizationChecker).checkCanManageAffiliate(Mockito.any(Affiliate.class)); try { restUserMockMvc.perform(put(Routes.AFFILIATE, 999L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"notesInternal\": \"Updated notes\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is5xxServerError()); Assert.fail(); } catch (NestedServletException e) { Assert.assertThat(e.getRootCause().getMessage(), Matchers.containsString("not allowed")); } } @Test public void updateAffiliateShouldUpdateSaveWithNotesField() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setNotesInternal("Original Notes"); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"notesInternal\": \"Updated Notes\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.notesInternal").value("Updated Notes")) ; Mockito.verify(affiliateRepository).save(Mockito.any(Affiliate.class)); } @Test public void updateAffiliateShouldUpdateSaveWithStandingStateField() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.IN_GOOD_STANDING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"standingState\": \"DEACTIVATED\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.standingState").value("DEACTIVATED")) ; Mockito.verify(affiliateRepository).save(Mockito.any(Affiliate.class)); } @Test public void updateAffiliateShouldIgnoreBlankStandingState() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.IN_GOOD_STANDING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"standingState\": null }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.standingState").value("IN_GOOD_STANDING")) ; Mockito.verify(affiliateRepository).save(Mockito.any(Affiliate.class)); } @Test public void updateAffiliateShouldFailWhenChangingStandingStateFromApplying() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.APPLYING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"standingState\": \"IN_GOOD_STANDING\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isConflict()) ; } @Test public void updateAffiliateShouldAuditLogWithAffiliateUpdate() throws Exception { Affiliate affiliate = createBlankAffiliate(); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"notesInternal\": \"Updated Notes\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) ; Mockito.verify(affiliateAuditEvents).logUpdateOfAffiliate(Mockito.any(Affiliate.class)); } @Test public void updateAffiliateShouldAuditLogWithStandingStateChangeWhenChanged() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.IN_GOOD_STANDING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"standingState\": \"DEACTIVATED\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) ; Mockito.verify(affiliateAuditEvents).logStandingStateChange(Mockito.any(Affiliate.class)); } @Test public void updateAffiliateShouldSkipStandingStateAuditLogWhenUnchanged() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.APPLYING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"standingState\": \"APPLYING\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) ; Mockito.verify(affiliateAuditEvents, Mockito.never()).logStandingStateChange(Mockito.any(Affiliate.class)); } @Test public void updateAffiliateShouldIgnoreNonSafeFields() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setCreator("original@email.com"); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(put(Routes.AFFILIATE, 1L) .contentType(MediaType.APPLICATION_JSON) .content("{ \"creator\": \"updated@email.com\" }") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.creator").value("original@email.com")) ; Mockito.verify(affiliateRepository).save(Mockito.any(Affiliate.class)); } @Test public void deleteAffiliateShouldFailForUnknownAffiliate() throws Exception { when(affiliateRepository.findOne(999L)).thenReturn(null); restUserMockMvc.perform(delete(Routes.AFFILIATE, 999L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } @Test public void deleteAffiliateShouldFailIfCurrentUserCannotManage() throws Exception { when(affiliateRepository.findOne(999L)).thenReturn(createBlankAffiliate()); Mockito.doThrow(new IllegalStateException("not allowed")).when(applicationAuthorizationChecker).checkCanManageAffiliate(Mockito.any(Affiliate.class)); try { restUserMockMvc.perform(delete(Routes.AFFILIATE, 999L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is5xxServerError()); Assert.fail(); } catch (NestedServletException e) { Assert.assertThat(e.getRootCause().getMessage(), Matchers.containsString("not allowed")); } } @Test public void deleteAffiliateShouldFailWhenAffiliateStandingStateNotApplying() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.IN_GOOD_STANDING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(delete(Routes.AFFILIATE, 1L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isConflict()) ; } @Test public void deleteAffiliateShouldDeleteAffiliateAndDependentApplicationsAndUser() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.APPLYING); affiliate.addApplication(new PrimaryApplication(1L)); affiliate.addApplication(new ExtensionApplication(1L)); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(delete(Routes.AFFILIATE, 1L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNoContent()); Mockito.verify(affiliateDeleter).deleteAffiliate(Mockito.any(Affiliate.class)); } @Test public void deleteAffiliateShouldAuditLogWithAffiliateUpdate() throws Exception { Affiliate affiliate = createBlankAffiliate(); affiliate.setStandingState(StandingState.APPLYING); when(affiliateRepository.findOne(1L)).thenReturn(affiliate); restUserMockMvc.perform(delete(Routes.AFFILIATE, 1L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNoContent()); Mockito.verify(affiliateAuditEvents).logDeleteOfAffiliate(Mockito.any(Affiliate.class)); } }