diff --git a/pom.xml b/pom.xml index 062df58..4bc3e13 100644 --- a/pom.xml +++ b/pom.xml @@ -280,6 +280,17 @@ h2 runtime + + org.springframework.boot + spring-boot-starter-test + test + + + jakarta.xml.bind + jakarta.xml.bind-api + + + diff --git a/src/test/java/com/iemr/admin/controller/blocking/BlockingControllerTest.java b/src/test/java/com/iemr/admin/controller/blocking/BlockingControllerTest.java new file mode 100644 index 0000000..b93dab4 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/blocking/BlockingControllerTest.java @@ -0,0 +1,577 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.blocking; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.blocking.M_Providerservicemapping_Blocking; +import com.iemr.admin.data.blocking.M_Serviceprovider_Blocking; +import com.iemr.admin.data.blocking.M_Status1; +import com.iemr.admin.data.blocking.T_Providerservicemappingdetail; +import com.iemr.admin.data.blocking.T_Serviceproviderdetail; +import com.iemr.admin.data.blocking.T_Userdetail; +import com.iemr.admin.data.blocking.UserForBlocking; +import com.iemr.admin.data.blocking.V_Showproviderservicemapping; +import com.iemr.admin.service.blocking.BlockingInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The blocking endpoints suspend and reinstate a provider - whole, per service + * line, per state, or one user at a time - and write an audit row for each + * change of status. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("BlockingController Test Suite") +class BlockingControllerTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer SERVICE_ID = 3; + private static final Integer STATE_ID = 29; + + @Mock + private BlockingInter blockingInter; + + @InjectMocks + private BlockingController controller; + + private static M_Providerservicemapping_Blocking mapping(Integer mapId) { + M_Providerservicemapping_Blocking mapping = new M_Providerservicemapping_Blocking(); + mapping.setProviderServiceMapID(mapId); + mapping.setServiceProviderID(PROVIDER_ID); + mapping.setServiceID(SERVICE_ID); + mapping.setStateID(STATE_ID); + mapping.setStatusID(1); + return mapping; + } + + private static V_Showproviderservicemapping view(Integer mapId) { + V_Showproviderservicemapping view = new V_Showproviderservicemapping(); + view.setProviderServiceMapID(mapId); + view.setServiceProviderID(PROVIDER_ID); + return view; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("blockProvider1 should record the previous status before the provider is blocked") + void blockProvider1_shouldRecordPreviousStatus() { + M_Serviceprovider_Blocking stored = new M_Serviceprovider_Blocking(); + stored.setServiceProviderID(PROVIDER_ID); + stored.setServiceProviderName("Piramal Swasthya"); + stored.setStatusID(1); + T_Serviceproviderdetail saved = new T_Serviceproviderdetail(); + saved.setServiceProviderDetailID(9001); + when(blockingInter.getProviderDetailsById(PROVIDER_ID)).thenReturn(stored); + when(blockingInter.saveData(any())).thenReturn(saved); + + String response = controller.blockProvider1( + "{\"serviceProviderID\":77,\"statusID\":2,\"reason\":\"contract ended\"}"); + + assertSuccessContaining(response, "9001"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(T_Serviceproviderdetail.class); + verify(blockingInter).saveData(captor.capture()); + assertEquals(1, captor.getValue().getPreviousStatusID()); + assertEquals(2, captor.getValue().getUpdatedStatusID()); + assertEquals("contract ended", captor.getValue().getReason()); + assertEquals(2, stored.getStatusID(), "the provider must carry the new status when it is saved"); + verify(blockingInter).blockServiceProvider(stored); + } + + @Test + @DisplayName("blockProvider1 should answer an error envelope for a provider that does not exist") + void blockProvider1_shouldAnswerErrorEnvelopeForUnknownProvider() { + when(blockingInter.getProviderDetailsById(PROVIDER_ID)).thenReturn(null); + + assertCodeException(controller.blockProvider1("{\"serviceProviderID\":77,\"statusID\":2}")); + } + + @Test + @DisplayName("blockProvider should write one audit row per service mapping it blocks") + void blockProvider_shouldWriteOneAuditRowPerMapping() { + when(blockingInter.getProviderStatus(PROVIDER_ID)) + .thenReturn(new ArrayList<>(List.of(mapping(4001), mapping(4002)))); + T_Providerservicemappingdetail saved = new T_Providerservicemappingdetail(); + saved.setProviderServiceMapID(4001); + when(blockingInter.savetpsmd(anyList())).thenReturn(new ArrayList<>(List.of(saved))); + + String response = controller.blockProvider( + "{\"serviceProviderID\":77,\"statusID\":2,\"reason\":\"contract ended\"}"); + + assertSuccessContaining(response, "4001"); + verify(blockingInter).blockProvider(PROVIDER_ID, 2); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(blockingInter).savetpsmd(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals("contract ended", captor.getValue().get(0).getReason()); + } + + @Test + @DisplayName("blockProvider should answer an error envelope when the mappings cannot be read") + void blockProvider_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStatus(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.blockProvider("{\"serviceProviderID\":77,\"statusID\":2}")); + } + + @Test + @DisplayName("blockProviderByServiceId should block only the service line the caller names") + void blockProviderByServiceId_shouldBlockOnlyNamedServiceLine() { + when(blockingInter.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)) + .thenReturn(new ArrayList<>(List.of(mapping(4001)))); + T_Providerservicemappingdetail saved = new T_Providerservicemappingdetail(); + saved.setProviderServiceMapID(4001); + when(blockingInter.savetpsmd(anyList())).thenReturn(new ArrayList<>(List.of(saved))); + + assertSuccessContaining(controller.blockProviderByServiceId( + "{\"serviceProviderID\":77,\"serviceID\":3,\"statusID\":2}"), "4001"); + verify(blockingInter).blockProviderByProviderIdAndServiceId(PROVIDER_ID, SERVICE_ID, 2); + } + + @Test + @DisplayName("blockProviderByServiceId should answer an error envelope when the mappings cannot be read") + void blockProviderByServiceId_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStatusByProviderAndServiceId(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure( + controller.blockProviderByServiceId("{\"serviceProviderID\":77,\"serviceID\":3,\"statusID\":2}")); + } + + @Test + @DisplayName("getProviderStatus should answer the mapping view for the provider") + void getProviderStatus_shouldAnswerMappingView() { + when(blockingInter.getProviderStatus1(PROVIDER_ID)).thenReturn(new ArrayList<>(List.of(view(4001)))); + + assertSuccessContaining(controller.getProviderStatus("{\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("getProviderStatus should answer an error envelope when the view cannot be read") + void getProviderStatus_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStatus1(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getProviderStatus("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getProviderStatus1 should answer the second mapping view for the provider") + void getProviderStatus1_shouldAnswerSecondView() { + when(blockingInter.getProviderStatus2(PROVIDER_ID)).thenReturn(new ArrayList<>(List.of(view(4001)))); + + assertSuccessContaining(controller.getProviderStatus1("{\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("getProviderStatus1 should answer an error envelope when the view cannot be read") + void getProviderStatus1_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStatus2(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getProviderStatus1("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getServiceLiensUsingProvider should answer the service lines the provider runs") + void getServiceLiensUsingProvider_shouldAnswerServiceLines() { + when(blockingInter.getServiceLiensUsingProvider(PROVIDER_ID)) + .thenReturn(new ArrayList<>(List.of(mapping(4001)))); + + assertSuccessContaining(controller.getServiceLiensUsingProvider("{\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("getServiceLiensUsingProvider should answer an error envelope when the lookup fails") + void getServiceLiensUsingProvider_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getServiceLiensUsingProvider(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getServiceLiensUsingProvider("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getProviderStatusByProviderAndServiceId should narrow the view to the service line") + void getProviderStatusByProviderAndServiceId_shouldNarrowToServiceLine() { + when(blockingInter.getProviderStatusByProviderAndServiceId2(PROVIDER_ID, SERVICE_ID)) + .thenReturn(new ArrayList<>(List.of(view(4001)))); + + assertSuccessContaining(controller.getProviderStatusByProviderAndServiceId( + "{\"serviceProviderID\":77,\"serviceID\":3}"), "4001"); + } + + @Test + @DisplayName("getProviderStatusByProviderAndServiceId should answer an error envelope when the lookup fails") + void getProviderStatusByProviderAndServiceId_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStatusByProviderAndServiceId2(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller + .getProviderStatusByProviderAndServiceId("{\"serviceProviderID\":77,\"serviceID\":3}")); + } + + @Test + @DisplayName("blockProviderByService should record the previous status of the one mapping it blocks") + void blockProviderByService_shouldRecordPreviousStatus() { + M_Providerservicemapping_Blocking stored = mapping(4001); + T_Providerservicemappingdetail saved = new T_Providerservicemappingdetail(); + saved.setProviderServiceMapID(4001); + when(blockingInter.getProviderServiceMappingDetails(PROVIDER_ID, STATE_ID, SERVICE_ID)).thenReturn(stored); + when(blockingInter.savetpsdData(any())).thenReturn(saved); + + assertSuccessContaining(controller.blockProviderByService("{\"serviceProviderID\":77,\"stateID\":29," + + "\"serviceID\":3,\"statusID\":2,\"reason\":\"suspended\"}"), "4001"); + verify(blockingInter).blockProviderByService(PROVIDER_ID, STATE_ID, SERVICE_ID, 2); + } + + @Test + @DisplayName("blockProviderByService should answer an error envelope for a mapping that does not exist") + void blockProviderByService_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(blockingInter.getProviderServiceMappingDetails(any(), any(), any())).thenReturn(null); + + assertCodeException(controller.blockProviderByService( + "{\"serviceProviderID\":77,\"stateID\":29,\"serviceID\":3,\"statusID\":2}")); + } + + @Test + @DisplayName("getProviderStatusByService should answer the view for the service line in the state") + void getProviderStatusByService_shouldAnswerViewForServiceInState() { + when(blockingInter.getProviderServiceMappingDetails2(PROVIDER_ID, STATE_ID, SERVICE_ID)) + .thenReturn(new ArrayList<>(List.of(view(4001)))); + + assertSuccessContaining(controller.getProviderStatusByService( + "{\"serviceProviderID\":77,\"stateID\":29,\"serviceID\":3}"), "4001"); + } + + @Test + @DisplayName("getProviderStatusByService should answer an error envelope when the lookup fails") + void getProviderStatusByService_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderServiceMappingDetails2(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getProviderStatusByService("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("blockProviderByState should write one audit row per mapping in the state") + void blockProviderByState_shouldWriteOneAuditRowPerMapping() { + when(blockingInter.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)) + .thenReturn(List.of(mapping(4001), mapping(4002))); + T_Providerservicemappingdetail saved = new T_Providerservicemappingdetail(); + saved.setProviderServiceMapID(4001); + when(blockingInter.savetpsmd(anyList())).thenReturn(new ArrayList<>(List.of(saved))); + + assertSuccessContaining(controller.blockProviderByState( + "{\"serviceProviderID\":77,\"stateID\":29,\"statusID\":2,\"reason\":\"suspended\"}"), "4001"); + verify(blockingInter).blockProviderByState(PROVIDER_ID, STATE_ID, 2); + } + + @Test + @DisplayName("blockProviderByState should answer an error envelope when the mappings cannot be read") + void blockProviderByState_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStateMappingDetails(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure( + controller.blockProviderByState("{\"serviceProviderID\":77,\"stateID\":29,\"statusID\":2}")); + } + + @Test + @DisplayName("getProviderStatusByState should answer the view for the state") + void getProviderStatusByState_shouldAnswerViewForState() { + when(blockingInter.getProviderStateMappingDetails1(PROVIDER_ID, STATE_ID)) + .thenReturn(new ArrayList<>(List.of(view(4001)))); + + assertSuccessContaining( + controller.getProviderStatusByState("{\"serviceProviderID\":77,\"stateID\":29}"), "4001"); + } + + @Test + @DisplayName("getProviderStatusByState should answer an error envelope when the lookup fails") + void getProviderStatusByState_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getProviderStateMappingDetails1(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getProviderStatusByState("{\"serviceProviderID\":77,\"stateID\":29}")); + } + + @Test + @DisplayName("blockUser should record the user's previous credentials before the block takes effect") + void blockUser_shouldRecordPreviousCredentials() { + UserForBlocking stored = new UserForBlocking(); + stored.setUserID(3117); + stored.setUserName("asha.rao"); + stored.setPassword("old-secret"); + stored.setStatusID(1); + when(blockingInter.getUserDetailByUserId(3117)).thenReturn(stored); + + String response = controller.blockUser("{\"userID\":3117,\"statusID\":2," + + "\"updatedPassword\":\"new-secret\",\"updatedStatusID\":2}"); + + assertSuccessContaining(response, "jai"); + verify(blockingInter).blockUser(3117, 2); + + ArgumentCaptor captor = ArgumentCaptor.forClass(T_Userdetail.class); + verify(blockingInter).saveUserDetails(captor.capture()); + assertEquals("old-secret", captor.getValue().getPreviousPassword()); + assertEquals("new-secret", captor.getValue().getUpdatedPassword()); + assertEquals(1, captor.getValue().getPreviousStatusID()); + } + + @Test + @DisplayName("blockUser should answer an error envelope for a user that does not exist") + void blockUser_shouldAnswerErrorEnvelopeForUnknownUser() { + when(blockingInter.getUserDetailByUserId(3117)).thenReturn(null); + + assertCodeException(controller.blockUser("{\"userID\":3117,\"statusID\":2}")); + } + + @Test + @DisplayName("getStatus should answer every status on record") + void getStatus_shouldAnswerEveryStatus() { + M_Status1 status = new M_Status1(); + status.setStatusID(1); + status.setStatus("Active"); + when(blockingInter.getStatusData()).thenReturn(new ArrayList<>(List.of(status))); + + assertSuccessContaining(controller.getStatus("{}"), "Active"); + } + + @Test + @DisplayName("getStatus should answer an error envelope when the lookup fails") + void getStatus_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getStatusData()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getStatus("{}")); + } + + @Test + @DisplayName("ProviderStateAndServiceLines should create one mapping per state the request names") + void providerStateAndServiceLines_shouldCreateOneMappingPerState() { + when(blockingInter.AddServiceProvider(anyList())) + .thenReturn(new ArrayList<>(List.of(mapping(4001)))); + + String response = controller.ProviderStateAndServiceLines("[{\"serviceProviderID\":77,\"serviceID\":3," + + "\"createdBy\":\"admin\",\"statusID\":1,\"stateID1\":[29,30]}]"); + + assertSuccessContaining(response, "4001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(blockingInter).AddServiceProvider(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals(29, captor.getValue().get(0).getStateID()); + } + + @Test + @DisplayName("ProviderStateAndServiceLines should fall back to the state on the record when none is listed") + void providerStateAndServiceLines_shouldFallBackToRecordState() { + when(blockingInter.AddServiceProvider(anyList())).thenReturn(new ArrayList<>()); + + controller.ProviderStateAndServiceLines("[{\"serviceProviderID\":77,\"serviceID\":3," + + "\"stateID\":29,\"createdBy\":\"admin\",\"statusID\":1,\"stateID1\":[]}]"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(blockingInter).AddServiceProvider(captor.capture()); + assertEquals(1, captor.getValue().size()); + assertEquals(29, captor.getValue().get(0).getStateID()); + } + + @Test + @DisplayName("ProviderStateAndServiceLines should answer an error envelope when no states are named") + void providerStateAndServiceLines_shouldAnswerErrorEnvelopeWithoutStates() { + assertCodeException(controller.ProviderStateAndServiceLines("[{\"serviceProviderID\":77}]")); + } + + @Test + @DisplayName("deleteProviderStateAndServiceLines should build one record per service line named") + void deleteProviderStateAndServiceLines_shouldBuildOneRecordPerService() { + when(blockingInter.AddServiceProvider(anyList())).thenReturn(new ArrayList<>(List.of(mapping(4001)))); + + String response = controller.deleteProviderStateAndServiceLines("{\"serviceProviderID\":77," + + "\"stateID\":29,\"createdBy\":\"admin\",\"statusID\":1,\"serviceID1\":[3,4]}"); + + assertSuccessContaining(response, "4001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(blockingInter).AddServiceProvider(captor.capture()); + assertEquals(2, captor.getValue().size()); + } + + @Test + @DisplayName("deleteProviderStateAndServiceLines should answer an error envelope when no services are named") + void deleteProviderStateAndServiceLines_shouldAnswerErrorEnvelopeWithoutServices() { + assertCodeException(controller.deleteProviderStateAndServiceLines("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("createCitMappingwithServiceLines should answer the summary the service reports") + void createCitMappingwithServiceLines_shouldAnswerSummary() { + when(blockingInter.mapctidata(anyList())).thenReturn("2 campaigns mapped"); + + assertSuccessContaining(controller.createCitMappingwithServiceLines( + "[{\"providerServiceMapID\":4001,\"cTI_CampaignName\":\"104\"}]"), "2 campaigns mapped"); + } + + @Test + @DisplayName("createCitMappingwithServiceLines should answer an error envelope when the mapping fails") + void createCitMappingwithServiceLines_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.mapctidata(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createCitMappingwithServiceLines("[{\"providerServiceMapID\":4001}]")); + } + + @Test + @DisplayName("getMappedServiceLinesAndStatetoProvider should answer the mappings the service resolves") + void getMappedServiceLines_shouldAnswerResolvedMappings() { + when(blockingInter.getServiceLiensUsingProvider1(any())) + .thenReturn(new ArrayList<>(List.of(mapping(4001)))); + + assertSuccessContaining( + controller.getMappedServiceLinesAndStatetoProvider("{\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("getMappedServiceLinesAndStatetoProvider should answer an error envelope when the lookup fails") + void getMappedServiceLines_shouldAnswerErrorEnvelopeOnFailure() { + when(blockingInter.getServiceLiensUsingProvider1(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getMappedServiceLinesAndStatetoProvider("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("mapProviderAndServiceLines should mark every new mapping active") + void mapProviderAndServiceLines_shouldMarkNewMappingsActive() { + when(blockingInter.AddServiceProvider(anyList())).thenReturn(new ArrayList<>(List.of(mapping(4001)))); + + String response = controller.mapProviderAndServiceLines("[{\"serviceProviderID\":77,\"serviceID\":3," + + "\"createdBy\":\"admin\",\"stateID1\":[29,30]}]"); + + assertSuccessContaining(response, "4001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(blockingInter).AddServiceProvider(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals(1, captor.getValue().get(0).getStatusID(), "a new mapping starts active"); + } + + @Test + @DisplayName("mapProviderAndServiceLines should fall back to the state on the record when none is listed") + void mapProviderAndServiceLines_shouldFallBackToRecordState() { + when(blockingInter.AddServiceProvider(anyList())).thenReturn(new ArrayList<>()); + + controller.mapProviderAndServiceLines("[{\"serviceProviderID\":77,\"serviceID\":3," + + "\"stateID\":29,\"createdBy\":\"admin\",\"stateID1\":[]}]"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(blockingInter).AddServiceProvider(captor.capture()); + assertEquals(1, captor.getValue().size()); + } + + @Test + @DisplayName("mapProviderAndServiceLines should answer an error envelope when no states are named") + void mapProviderAndServiceLines_shouldAnswerErrorEnvelopeWithoutStates() { + assertCodeException(controller.mapProviderAndServiceLines("[{\"serviceProviderID\":77}]")); + } + + @Test + @DisplayName("editMappedServiceLinesAndStatetoProvider should copy the edits onto the stored mapping") + void editMappedServiceLines_shouldCopyEdits() { + M_Providerservicemapping_Blocking stored = mapping(4001); + when(blockingInter.getDataByProviderServiceMapId(4001)).thenReturn(stored); + when(blockingInter.updateProviderData(stored)).thenReturn(stored); + + String response = controller.editMappedServiceLinesAndStatetoProvider("{\"providerServiceMapID\":4001," + + "\"serviceProviderID\":78,\"serviceID\":4,\"stateID\":30,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "4001"); + assertEquals(78, stored.getServiceProviderID()); + assertEquals(30, stored.getStateID()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editMappedServiceLinesAndStatetoProvider should answer an error envelope for an unknown mapping") + void editMappedServiceLines_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(blockingInter.getDataByProviderServiceMapId(4001)).thenReturn(null); + + assertCodeException( + controller.editMappedServiceLinesAndStatetoProvider("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("deleteMappedServiceLinesAndStatetoProvider should mark the mapping deleted") + void deleteMappedServiceLines_shouldMarkMappingDeleted() { + M_Providerservicemapping_Blocking stored = mapping(4001); + when(blockingInter.getDataByProviderServiceMapId(4001)).thenReturn(stored); + when(blockingInter.updateProviderData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteMappedServiceLinesAndStatetoProvider( + "{\"providerServiceMapID\":4001,\"deleted\":true}"), "4001"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteMappedServiceLinesAndStatetoProvider should answer an error envelope for an unknown mapping") + void deleteMappedServiceLines_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(blockingInter.getDataByProviderServiceMapId(4001)).thenReturn(null); + + assertCodeException(controller.deleteMappedServiceLinesAndStatetoProvider( + "{\"providerServiceMapID\":4001,\"deleted\":true}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/bulkRegistration/BulkRegistrationControllerTest.java b/src/test/java/com/iemr/admin/controller/bulkRegistration/BulkRegistrationControllerTest.java new file mode 100644 index 0000000..ab12766 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/bulkRegistration/BulkRegistrationControllerTest.java @@ -0,0 +1,185 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.bulkRegistration; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.iemr.admin.data.bulkuser.BulkRegistrationError; +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepoo; +import com.iemr.admin.service.bulkRegistration.BulkRegistrationService; +import com.iemr.admin.service.bulkRegistration.BulkRegistrationServiceImpl; +import com.iemr.admin.service.bulkRegistration.EmployeeXmlService; +import com.iemr.admin.service.locationmaster.LocationMasterServiceInter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; + +/** + * The bulk registration endpoint reports how much of an uploaded sheet was + * accepted, and hands the rejected rows back as a spreadsheet the uploader can + * correct and resubmit. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("BulkRegistrationController Test Suite") +class BulkRegistrationControllerTest { + + @Mock + private EmployeeXmlService employeeXmlService; + + @Spy + private BulkRegistrationServiceImpl bulkRegistrationServiceimpl; + + @Mock + private BulkRegistrationService bulkRegistrationService; + + @Mock + private EmployeeMasterRepoo employeeMasterRepoo; + + @Mock + private LocationMasterServiceInter locationMasterServiceInter; + + @InjectMocks + private BulkRegistrationController controller; + + @Test + @DisplayName("registerBulkUser should report how many rows were accepted and what was rejected") + void registerBulkUser_shouldReportAcceptedAndRejectedRows() throws Exception { + doAnswer(call -> { + bulkRegistrationServiceimpl.totalEmployeeListSize = 3; + bulkRegistrationServiceimpl.m_bulkUser.add(new M_User1()); + bulkRegistrationServiceimpl.m_bulkUser.add(new M_User1()); + bulkRegistrationServiceimpl.errorLogs.add("Row 3: Title is missing."); + return null; + }).when(bulkRegistrationService).registerBulkUser(anyString(), anyString(), anyString(), anyInt()); + + ResponseEntity> response = controller.registerBulkUser( + "", "auth", "admin", new MockHttpServletRequest(), 77); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("Success", response.getBody().get("status")); + assertEquals(200, response.getBody().get("statusCode")); + assertEquals(3, response.getBody().get("totalUser")); + assertEquals(2, response.getBody().get("registeredUser")); + assertTrue(response.getBody().get("error").toString().contains("Title is missing.")); + } + + @Test + @DisplayName("registerBulkUser should clear the accumulated state so the next upload starts clean") + void registerBulkUser_shouldClearAccumulatedState() throws Exception { + doAnswer(call -> { + bulkRegistrationServiceimpl.totalEmployeeListSize = 1; + bulkRegistrationServiceimpl.m_bulkUser.add(new M_User1()); + bulkRegistrationServiceimpl.errorLogs.add("Row 1: Title is missing."); + return null; + }).when(bulkRegistrationService).registerBulkUser(anyString(), anyString(), anyString(), anyInt()); + + controller.registerBulkUser("", "auth", "admin", new MockHttpServletRequest(), 77); + + assertTrue(bulkRegistrationServiceimpl.m_bulkUser.isEmpty()); + assertTrue(bulkRegistrationServiceimpl.errorLogs.isEmpty()); + assertEquals(0, bulkRegistrationServiceimpl.totalEmployeeListSize); + } + + @Test + @DisplayName("registerBulkUser should discard the rejected rows of a previous upload before it starts") + void registerBulkUser_shouldDiscardPreviousRejectedRows() throws Exception { + bulkRegistrationServiceimpl.bulkRegistrationErrors.add(new BulkRegistrationError()); + + controller.registerBulkUser("", "auth", "admin", new MockHttpServletRequest(), 77); + + verify(bulkRegistrationService).registerBulkUser("", "auth", "admin", 77); + } + + @Test + @DisplayName("registerBulkUser should report the failure rather than a partial success") + void registerBulkUser_shouldReportFailure() throws Exception { + doThrow(new IllegalStateException("the upload could not be read")) + .when(bulkRegistrationService).registerBulkUser(anyString(), anyString(), anyString(), anyInt()); + + ResponseEntity> response = controller.registerBulkUser( + "", "auth", "admin", new MockHttpServletRequest(), 77); + + assertEquals(500, response.getBody().get("statusCode")); + assertEquals("the upload could not be read", response.getBody().get("message")); + } + + @Test + @DisplayName("downloadErrorSheet should answer the rejected rows as a spreadsheet attachment") + void downloadErrorSheet_shouldAnswerSpreadsheetAttachment() { + BulkRegistrationError error = new BulkRegistrationError(); + error.setUserName("EMP-1"); + error.setError(java.util.List.of("Title is missing.")); + bulkRegistrationServiceimpl.bulkRegistrationErrors.add(error); + + ResponseEntity response = controller.downloadErrorSheet(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertTrue(response.getBody().length > 0); + assertTrue(response.getHeaders().getFirst("Content-Disposition").contains("error_log.xlsx")); + assertTrue(bulkRegistrationServiceimpl.bulkRegistrationErrors.isEmpty(), + "the rejected rows are handed over once, then cleared"); + } + + @Test + @DisplayName("downloadErrorSheet should still answer a sheet when nothing was rejected") + void downloadErrorSheet_shouldAnswerSheetWithoutRejectedRows() { + ResponseEntity response = controller.downloadErrorSheet(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody().length > 0); + } + + @Test + @DisplayName("downloadErrorSheet should answer a failure rather than an empty file when the sheet cannot be built") + void downloadErrorSheet_shouldAnswerFailureWhenSheetCannotBeBuilt() { + doThrow(new IllegalStateException("out of memory")).when(bulkRegistrationServiceimpl).insertErrorLog(); + + ResponseEntity response = controller.downloadErrorSheet(); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertNull(response.getBody()); + } +} diff --git a/src/test/java/com/iemr/admin/controller/calibration/CalibrationControllerTest.java b/src/test/java/com/iemr/admin/controller/calibration/CalibrationControllerTest.java new file mode 100644 index 0000000..1f3a60e --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/calibration/CalibrationControllerTest.java @@ -0,0 +1,177 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.calibration; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.calibration.CalibrationStrip; +import com.iemr.admin.service.calibration.CalibrationService; +import com.iemr.admin.utils.exception.IEMRException; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * The calibration screen keeps the test strip codes a provider calibrates + * against. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CalibrationController Test Suite") +class CalibrationControllerTest { + + private static final String REQUEST = "{\"calibrationStripID\":6601,\"stripCode\":\"STRIP-77\"," + + "\"providerServiceMapID\":4001,\"deleted\":false,\"createdBy\":\"admin\"}"; + + @Mock + private CalibrationService calibrationService; + + @InjectMocks + private CalibrationController controller; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static String errorMessageOf(String response) { + return new JSONObject(response).getString("errorMessage"); + } + + @Test + @DisplayName("createCalibrationStrip should confirm the strip it recorded") + void create_shouldConfirmRecordedStrip() throws Exception { + when(calibrationService.saveData(any(CalibrationStrip.class))).thenReturn(1); + + String response = controller.createCalibrationStrip(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data saved successfully"), response); + } + + @Test + @DisplayName("createCalibrationStrip should report the failure when no strip was recorded") + void create_shouldReportNothingRecorded() throws Exception { + when(calibrationService.saveData(any(CalibrationStrip.class))).thenReturn(0); + + assertEquals("Error while saving Calibration master data", + errorMessageOf(controller.createCalibrationStrip(REQUEST))); + } + + @Test + @DisplayName("createCalibrationStrip should pass on the refusal in the service's own words") + void create_shouldPassOnRefusal() throws Exception { + when(calibrationService.saveData(any(CalibrationStrip.class))) + .thenThrow(new IEMRException("Strip code already exists")); + + assertEquals("Strip code already exists", errorMessageOf(controller.createCalibrationStrip(REQUEST))); + } + + @Test + @DisplayName("fetchCalibrationStrips should answer the strips the provider holds") + void fetch_shouldAnswerHeldStrips() throws Exception { + when(calibrationService.fetchData(any(CalibrationStrip.class))) + .thenReturn("{\"calibrationData\":[{\"stripCode\":\"STRIP-77\"}]}"); + + String response = controller.fetchCalibrationStrips(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("STRIP-77"), response); + } + + @Test + @DisplayName("fetchCalibrationStrips should report the failure when the strips cannot be answered") + void fetch_shouldReportLookupFailure() throws Exception { + when(calibrationService.fetchData(any(CalibrationStrip.class))) + .thenThrow(new IEMRException("Error while fetching Calibration data")); + + assertEquals(OutputResponse.USERID_FAILURE, statusCodeOf(controller.fetchCalibrationStrips(REQUEST))); + } + + @Test + @DisplayName("deleteCalibrationStrip should confirm the retirement it recorded") + void delete_shouldConfirmRetirement() throws Exception { + when(calibrationService.deleteData(any(CalibrationStrip.class))).thenReturn(1); + + String response = controller.deleteCalibrationStrip(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data deleted successfully"), response); + } + + @Test + @DisplayName("deleteCalibrationStrip should report the failure when no strip was retired") + void delete_shouldReportNothingRetired() throws Exception { + when(calibrationService.deleteData(any(CalibrationStrip.class))).thenReturn(0); + + assertEquals("Error while updating Calibration master data", + errorMessageOf(controller.deleteCalibrationStrip(REQUEST))); + } + + @Test + @DisplayName("deleteCalibrationStrip should report the failure when the request is refused") + void delete_shouldReportRefusal() throws Exception { + when(calibrationService.deleteData(any(CalibrationStrip.class))) + .thenThrow(new IEMRException("Invalid request")); + + assertEquals(OutputResponse.USERID_FAILURE, statusCodeOf(controller.deleteCalibrationStrip(REQUEST))); + } + + @Test + @DisplayName("updateCalibrationStrip should confirm the change it recorded") + void update_shouldConfirmRecordedChange() throws Exception { + when(calibrationService.updateData(any(CalibrationStrip.class))).thenReturn(1); + + String response = controller.updateCalibrationStrip(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data updated successfully"), response); + } + + @Test + @DisplayName("updateCalibrationStrip should report the failure when no strip was changed") + void update_shouldReportNothingChanged() throws Exception { + when(calibrationService.updateData(any(CalibrationStrip.class))).thenReturn(0); + + assertEquals("Error while updating Calibration master data", + errorMessageOf(controller.updateCalibrationStrip(REQUEST))); + } + + @Test + @DisplayName("updateCalibrationStrip should pass on the refusal in the service's own words") + void update_shouldPassOnRefusal() throws Exception { + when(calibrationService.updateData(any(CalibrationStrip.class))) + .thenThrow(new IEMRException("Error while updating data")); + + assertEquals("Error while updating data", errorMessageOf(controller.updateCalibrationStrip(REQUEST))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/createorder/CareStreamCreateOrderControllerTest.java b/src/test/java/com/iemr/admin/controller/createorder/CareStreamCreateOrderControllerTest.java new file mode 100644 index 0000000..8c209fc --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/createorder/CareStreamCreateOrderControllerTest.java @@ -0,0 +1,66 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.createorder; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * The imaging order screens talk to a radiology server over a hard-coded socket + * address, so only the request reading that happens before the connection is + * opened can be checked here; a request the screen cannot read is refused + * without any connection being attempted at all. + */ +@DisplayName("CareStreamCreateOrderController Test Suite") +class CareStreamCreateOrderControllerTest { + + private static final String UNREADABLE_REQUEST = "{not json"; + + private final CareStreamCreateOrderController controller = new CareStreamCreateOrderController(); + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("createOrder should refuse a request it cannot read rather than reach the radiology server") + void createOrder_shouldRefuseUnreadableRequest() throws Exception { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.createOrder(UNREADABLE_REQUEST))); + } + + @Test + @DisplayName("UpdateOrder should refuse a request it cannot read rather than reach the radiology server") + void updateOrder_shouldRefuseUnreadableRequest() throws Exception { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.UpdateOrder(UNREADABLE_REQUEST))); + } + + @Test + @DisplayName("deleteOrder should refuse a request it cannot read rather than reach the radiology server") + void deleteOrder_shouldRefuseUnreadableRequest() throws Exception { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.deleteOrder(UNREADABLE_REQUEST))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/drugstrength/DrugStrengthTest.java b/src/test/java/com/iemr/admin/controller/drugstrength/DrugStrengthTest.java new file mode 100644 index 0000000..def2c57 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/drugstrength/DrugStrengthTest.java @@ -0,0 +1,157 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.drugstrength; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.drugstrangth.M_104DrugStrength; +import com.iemr.admin.service.drugstrangth.DrugStrangthInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The drug strength screen keeps the strengths a drug can be dispensed in. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DrugStrength controller Test Suite") +class DrugStrengthTest { + + private static final Integer STRENGTH_ID = 33; + + @Mock + private DrugStrangthInter durgStrangthInter; + + @InjectMocks + private DrugStrength controller; + + private static M_104DrugStrength strength() { + M_104DrugStrength strength = new M_104DrugStrength(); + strength.setDrugStrengthID(STRENGTH_ID); + strength.setDrugStrength("500 mg"); + strength.setDrugStrengthDesc("Standard adult dose"); + return strength; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("createDrugStrangth should answer the strengths it added") + void create_shouldAnswerAddedStrengths() { + when(durgStrangthInter.createDrugStrangth(anyList())).thenReturn(new ArrayList<>(List.of(strength()))); + + assertSuccessContaining(controller.createDrugStrangth("[{\"drugStrength\":\"500 mg\"}]"), "500 mg"); + } + + @Test + @DisplayName("createDrugStrangth should report the failure when the strength cannot be added") + void create_shouldReportStorageFailure() { + when(durgStrangthInter.createDrugStrangth(anyList())).thenThrow(new RuntimeException("already on file")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.createDrugStrangth("[{\"drugStrength\":\"500 mg\"}]"))); + } + + @Test + @DisplayName("getDrugStrangth should answer the strengths on file") + void get_shouldAnswerStrengthsOnFile() { + when(durgStrangthInter.getDrugStrangth()).thenReturn(new ArrayList<>(List.of(strength()))); + + assertSuccessContaining(controller.getDrugStrangth("{}"), "500 mg"); + } + + @Test + @DisplayName("getDrugStrangth should report the failure when the list cannot be answered") + void get_shouldReportLookupFailure() { + when(durgStrangthInter.getDrugStrangth()).thenThrow(new RuntimeException("connection reset")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getDrugStrangth("{}"))); + } + + @Test + @DisplayName("updateDrugStrangth should answer the strength whose details it changed") + void update_shouldAnswerChangedStrength() { + M_104DrugStrength stored = strength(); + when(durgStrangthInter.updateDrugStrangth(STRENGTH_ID)).thenReturn(stored); + when(durgStrangthInter.saveupdatedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.updateDrugStrangth( + "{\"drugStrengthID\":33,\"drugStrength\":\"650 mg\",\"drugStrengthDesc\":\"Higher dose\"," + + "\"modifiedBy\":\"admin\"}"), + "650 mg"); + assertEquals("Higher dose", stored.getDrugStrengthDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("updateDrugStrangth should report the failure when the strength is unknown") + void update_shouldReportUnknownStrength() { + when(durgStrangthInter.updateDrugStrangth(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.updateDrugStrangth("{\"drugStrengthID\":-1}"))); + } + + @Test + @DisplayName("deleteDrugStrangth should answer the strength it retired") + void delete_shouldAnswerRetiredStrength() { + M_104DrugStrength stored = strength(); + when(durgStrangthInter.updateDrugStrangth(STRENGTH_ID)).thenReturn(stored); + when(durgStrangthInter.saveupdatedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteDrugStrangth("{\"drugStrengthID\":33,\"deleted\":true}"), "33"); + assertEquals(Boolean.TRUE, stored.getDeleted()); + } + + @Test + @DisplayName("deleteDrugStrangth should report the failure when the strength is unknown") + void delete_shouldReportUnknownStrength() { + when(durgStrangthInter.updateDrugStrangth(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.deleteDrugStrangth("{\"drugStrengthID\":-1,\"deleted\":true}"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/drugtype/DrugtypeControllerTest.java b/src/test/java/com/iemr/admin/controller/drugtype/DrugtypeControllerTest.java new file mode 100644 index 0000000..91d9d01 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/drugtype/DrugtypeControllerTest.java @@ -0,0 +1,160 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.drugtype; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.drugtype.M_Drugtype; +import com.iemr.admin.service.drugtype.DrugtypeInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The drug type screen keeps the dosage forms a provider stocks. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DrugtypeController Test Suite") +class DrugtypeControllerTest { + + private static final Integer DRUG_TYPE_ID = 21; + + @Mock + private DrugtypeInter drugtypeInter; + + @InjectMocks + private DrugtypeController controller; + + private static M_Drugtype drugType() { + M_Drugtype drugType = new M_Drugtype(); + drugType.setDrugTypeID(DRUG_TYPE_ID); + drugType.setDrugTypeName("Tablet"); + drugType.setDrugTypeCode("TAB"); + drugType.setProviderServiceMapID(4001); + return drugType; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("createManufacturer should answer the drug types it added") + void create_shouldAnswerAddedDrugTypes() { + when(drugtypeInter.createDrugtypeData(anyList())).thenReturn(new ArrayList<>(List.of(drugType()))); + + assertSuccessContaining( + controller.createManufacturer("[{\"drugTypeName\":\"Tablet\",\"drugTypeCode\":\"TAB\"}]"), "Tablet"); + } + + @Test + @DisplayName("createManufacturer should report the failure when the drug type cannot be added") + void create_shouldReportStorageFailure() { + when(drugtypeInter.createDrugtypeData(anyList())).thenThrow(new RuntimeException("already on file")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.createManufacturer("[{\"drugTypeName\":\"Tablet\"}]"))); + } + + @Test + @DisplayName("getManufacturer should answer the drug types the provider stocks") + void get_shouldAnswerStockedDrugTypes() { + when(drugtypeInter.getDrugtypeData(4001)).thenReturn(new ArrayList<>(List.of(drugType()))); + + assertSuccessContaining(controller.getManufacturer("{\"providerServiceMapID\":4001}"), "Tablet"); + } + + @Test + @DisplayName("getManufacturer should report the failure when the list cannot be answered") + void get_shouldReportLookupFailure() { + when(drugtypeInter.getDrugtypeData(any())).thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getManufacturer("{\"providerServiceMapID\":4001}"))); + } + + @Test + @DisplayName("editManufacturer should answer the drug type whose details it changed") + void edit_shouldAnswerChangedDrugType() { + M_Drugtype stored = drugType(); + when(drugtypeInter.editDrugtypeData(DRUG_TYPE_ID)).thenReturn(stored); + when(drugtypeInter.saveeditDrugtype(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editManufacturer( + "{\"drugTypeID\":21,\"drugTypeName\":\"Capsule\",\"drugTypeCode\":\"CAP\",\"status\":\"A\"," + + "\"modifiedBy\":\"admin\"}"), + "Capsule"); + assertEquals("CAP", stored.getDrugTypeCode()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editManufacturer should report the failure when the drug type is unknown") + void edit_shouldReportUnknownDrugType() { + when(drugtypeInter.editDrugtypeData(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.editManufacturer("{\"drugTypeID\":-1}"))); + } + + @Test + @DisplayName("deleteManufacturer should answer the drug type it retired") + void delete_shouldAnswerRetiredDrugType() { + M_Drugtype stored = drugType(); + when(drugtypeInter.editDrugtypeData(DRUG_TYPE_ID)).thenReturn(stored); + when(drugtypeInter.saveeditDrugtype(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteManufacturer("{\"drugTypeID\":21,\"deleted\":true}"), "21"); + assertEquals(Boolean.TRUE, stored.getDeleted()); + } + + @Test + @DisplayName("deleteManufacturer should report the failure when the drug type is unknown") + void delete_shouldReportUnknownDrugType() { + when(drugtypeInter.editDrugtypeData(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.deleteManufacturer("{\"drugTypeID\":-1,\"deleted\":true}"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/emailconfig/EmailConfigControllerTest.java b/src/test/java/com/iemr/admin/controller/emailconfig/EmailConfigControllerTest.java new file mode 100644 index 0000000..de89ef1 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/emailconfig/EmailConfigControllerTest.java @@ -0,0 +1,147 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.emailconfig; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.iemr.admin.model.emailconfig.AuthEmailRequest; +import com.iemr.admin.model.emailconfig.AuthEmailResponse; +import com.iemr.admin.model.emailconfig.CreateAuthEmailRequestModel; +import com.iemr.admin.model.emailconfig.UpdateAuthEmailRequest; +import com.iemr.admin.service.emailconfig.EmailConfigService; +import com.iemr.admin.utils.mapper.OutputMapper; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The email config screen keeps the authority mailboxes a complaint is copied + * to. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("EmailConfigController Test Suite") +class EmailConfigControllerTest { + + @Mock + private EmailConfigService emailConfigService; + + @InjectMocks + private EmailConfigController controller; + + private final MockHttpServletRequest servletRequest = new MockHttpServletRequest(); + + @BeforeEach + @DisplayName("Prime the shared output builder the screens publish through") + void setUp() { + new OutputMapper(); + } + + private static AuthEmailResponse mailbox() { + AuthEmailResponse mailbox = new AuthEmailResponse(); + mailbox.setEmailID("dho.bengaluru@example.org"); + return mailbox; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("saveConfig should answer the mailboxes it recorded") + void save_shouldAnswerRecordedMailboxes() { + when(emailConfigService.saveEmailConfigs(anyList())).thenReturn(List.of(mailbox())); + + String response = controller.saveConfig(List.of(new CreateAuthEmailRequestModel()), servletRequest); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("dho.bengaluru@example.org"), response); + } + + @Test + @DisplayName("saveConfig should report the failure when the mailbox cannot be recorded") + void save_shouldReportStorageFailure() { + when(emailConfigService.saveEmailConfigs(anyList())).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.saveConfig(new ArrayList<>(), servletRequest))); + } + + @Test + @DisplayName("getEmailConfigs should answer the mailboxes matching what the caller narrowed by") + void get_shouldAnswerMatchingMailboxes() { + when(emailConfigService.getAllEmailConfigs(any(AuthEmailRequest.class))).thenReturn(List.of(mailbox())); + + String response = controller.getEmailConfigs(new AuthEmailRequest(), servletRequest); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("dho.bengaluru@example.org"), response); + } + + @Test + @DisplayName("getEmailConfigs should report the failure when the mailboxes cannot be answered") + void get_shouldReportLookupFailure() { + when(emailConfigService.getAllEmailConfigs(any(AuthEmailRequest.class))) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getEmailConfigs(new AuthEmailRequest(), servletRequest))); + } + + @Test + @DisplayName("updateEmailConfig should answer the mailbox as it stands after the change") + void update_shouldAnswerChangedMailbox() { + when(emailConfigService.updateEmailConfigs(any(UpdateAuthEmailRequest.class))).thenReturn(mailbox()); + + String response = controller.updateEmailConfig(new UpdateAuthEmailRequest(), servletRequest); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("dho.bengaluru@example.org"), response); + } + + @Test + @DisplayName("updateEmailConfig should report the failure when the change cannot be recorded") + void update_shouldReportStorageFailure() { + when(emailConfigService.updateEmailConfigs(any(UpdateAuthEmailRequest.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.updateEmailConfig(new UpdateAuthEmailRequest(), servletRequest))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/employeemaster/AshaSupervisorMappingControllerTest.java b/src/test/java/com/iemr/admin/controller/employeemaster/AshaSupervisorMappingControllerTest.java new file mode 100644 index 0000000..108c41f --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/employeemaster/AshaSupervisorMappingControllerTest.java @@ -0,0 +1,356 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.employeemaster; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.employeemaster.AshaSupervisorMapping; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepo; +import com.iemr.admin.repository.store.MainStoreRepo; +import com.iemr.admin.service.employeemaster.AshaSupervisorMappingService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The ASHA supervisor endpoints decide which ASHAs each supervisor oversees at + * each facility, so a wrong mapping puts a health worker under the wrong + * supervisor. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("AshaSupervisorMappingController Test Suite") +class AshaSupervisorMappingControllerTest { + + @Mock + private AshaSupervisorMappingService ashaSupervisorMappingService; + + @Mock + private EmployeeMasterRepo employeeMasterRepo; + + @Mock + private MainStoreRepo mainStoreRepo; + + @InjectMocks + private AshaSupervisorMappingController controller; + + private static AshaSupervisorMapping mapping(Long id, Integer supervisorId, Integer ashaId) { + AshaSupervisorMapping mapping = new AshaSupervisorMapping(); + mapping.setId(id); + mapping.setSupervisorUserID(supervisorId); + mapping.setAshaUserID(ashaId); + return mapping; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + @Test + @DisplayName("getAshasByFacility should answer the ASHAs attached to the facilities the caller lists") + void getAshasByFacility_shouldAnswerAshasForListedFacilities() { + M_UserServiceRoleMapping2 asha = new M_UserServiceRoleMapping2(); + asha.setuSRMappingID(9001); + asha.setUserID(3117); + when(ashaSupervisorMappingService.getAshasByFacility(List.of(501, 502))) + .thenReturn(new ArrayList<>(List.of(asha))); + + assertSuccessContaining(controller.getAshasByFacility("{\"facilityIDs\":[501,502]}"), "9001"); + } + + @Test + @DisplayName("getAshasByFacility should fall back to the single facility id when no list is sent") + void getAshasByFacility_shouldFallBackToSingleFacilityId() { + when(ashaSupervisorMappingService.getAshasByFacility(List.of(501))).thenReturn(new ArrayList<>()); + + controller.getAshasByFacility("{\"facilityID\":501}"); + + verify(ashaSupervisorMappingService).getAshasByFacility(List.of(501)); + } + + @Test + @DisplayName("getAshasByFacility should ask for nothing when the request names no facility at all") + void getAshasByFacility_shouldAskForNothingWithoutAFacility() { + when(ashaSupervisorMappingService.getAshasByFacility(null)).thenReturn(new ArrayList<>()); + + controller.getAshasByFacility("{}"); + + verify(ashaSupervisorMappingService).getAshasByFacility(null); + } + + @Test + @DisplayName("getAshasByFacility should answer an error envelope when the lookup fails") + void getAshasByFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(ashaSupervisorMappingService.getAshasByFacility(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getAshasByFacility("{\"facilityIDs\":[501]}"))); + } + + @Test + @DisplayName("saveAshaSupervisorMapping should answer the mappings the service stored") + void saveAshaSupervisorMapping_shouldAnswerStoredMappings() { + when(ashaSupervisorMappingService.saveAshaSupervisorMappings(anyList())) + .thenReturn(new ArrayList<>(List.of(mapping(1L, 3117, 4001)))); + + assertSuccessContaining( + controller.saveAshaSupervisorMapping("[{\"supervisorUserID\":3117,\"ashaUserID\":4001}]"), "3117"); + } + + @Test + @DisplayName("saveAshaSupervisorMapping should answer an error envelope when the store fails") + void saveAshaSupervisorMapping_shouldAnswerErrorEnvelopeOnFailure() { + when(ashaSupervisorMappingService.saveAshaSupervisorMappings(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.saveAshaSupervisorMapping("[{\"supervisorUserID\":3117}]"))); + } + + @Test + @DisplayName("getSupervisorMappingByFacility should answer the mappings at the facility") + void getSupervisorMappingByFacility_shouldAnswerFacilityMappings() { + when(ashaSupervisorMappingService.getSupervisorMappingByFacility(501)) + .thenReturn(new ArrayList<>(List.of(mapping(1L, 3117, 4001)))); + + assertSuccessContaining(controller.getSupervisorMappingByFacility("{\"facilityID\":501}"), "3117"); + } + + @Test + @DisplayName("getSupervisorMappingByFacility should answer an error envelope when the lookup fails") + void getSupervisorMappingByFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(ashaSupervisorMappingService.getSupervisorMappingByFacility(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getSupervisorMappingByFacility("{\"facilityID\":501}"))); + } + + @Test + @DisplayName("deleteAshaSupervisorMapping should retire the mappings the caller names") + void deleteAshaSupervisorMapping_shouldRetireNamedMappings() { + String response = controller + .deleteAshaSupervisorMapping("{\"supervisorUserID\":3117,\"facilityIDs\":[501,502]}"); + + assertSuccessContaining(response, "Deleted successfully"); + verify(ashaSupervisorMappingService).deleteBySupervisorAndFacilities(3117, List.of(501, 502), "Admin"); + } + + @Test + @DisplayName("deleteAshaSupervisorMapping should refuse a request that names no supervisor") + void deleteAshaSupervisorMapping_shouldRefuseRequestWithoutSupervisor() { + String response = controller.deleteAshaSupervisorMapping("{\"facilityIDs\":[501]}"); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response)); + assertTrue(response.contains("supervisorUserID and facilityIDs are required"), response); + verify(ashaSupervisorMappingService, never()).deleteBySupervisorAndFacilities(anyInt(), anyList(), anyString()); + } + + @Test + @DisplayName("deleteAshaSupervisorMapping should refuse a request that names no facilities") + void deleteAshaSupervisorMapping_shouldRefuseRequestWithoutFacilities() { + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.deleteAshaSupervisorMapping("{\"supervisorUserID\":3117}"))); + } + + @Test + @DisplayName("deleteAshaSupervisorMapping should answer an error envelope when the retirement fails") + void deleteAshaSupervisorMapping_shouldAnswerErrorEnvelopeOnFailure() { + org.mockito.Mockito.doThrow(new IllegalStateException("no connection")) + .when(ashaSupervisorMappingService) + .deleteBySupervisorAndFacilities(anyInt(), anyList(), anyString()); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller + .deleteAshaSupervisorMapping("{\"supervisorUserID\":3117,\"facilityIDs\":[501]}"))); + } + + @Test + @DisplayName("updateAshaSupervisorMappingAtomically should replace the old mappings in one step") + void updateAtomically_shouldReplaceMappingsInOneStep() { + when(ashaSupervisorMappingService.updateAshaMappingsAtomically(anyInt(), anyList(), anyList(), anyString())) + .thenReturn(new ArrayList<>(List.of(mapping(1L, 3117, 4001)))); + + String response = controller.updateAshaSupervisorMappingAtomically("{\"supervisorUserID\":3117," + + "\"modifiedBy\":\"admin\",\"facilityIDs\":[501,502]," + + "\"newMappings\":[{\"supervisorUserID\":3117,\"ashaUserID\":4001,\"facilityID\":501}]}"); + + assertSuccessContaining(response, "3117"); + verify(ashaSupervisorMappingService).updateAshaMappingsAtomically( + org.mockito.ArgumentMatchers.eq(3117), org.mockito.ArgumentMatchers.eq(List.of(501, 502)), + anyList(), org.mockito.ArgumentMatchers.eq("admin")); + } + + @Test + @DisplayName("updateAshaSupervisorMappingAtomically should attribute the change to Admin when none is named") + void updateAtomically_shouldAttributeToAdminByDefault() { + when(ashaSupervisorMappingService.updateAshaMappingsAtomically(anyInt(), anyList(), anyList(), anyString())) + .thenReturn(new ArrayList<>()); + + controller.updateAshaSupervisorMappingAtomically("{\"supervisorUserID\":3117,\"facilityIDs\":[501]}"); + + verify(ashaSupervisorMappingService).updateAshaMappingsAtomically( + org.mockito.ArgumentMatchers.eq(3117), anyList(), anyList(), + org.mockito.ArgumentMatchers.eq("Admin")); + } + + @Test + @DisplayName("updateAshaSupervisorMappingAtomically should answer an error envelope without a supervisor") + void updateAtomically_shouldAnswerErrorEnvelopeWithoutSupervisor() { + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.updateAshaSupervisorMappingAtomically("{\"facilityIDs\":[501]}"))); + } + + @Test + @DisplayName("restoreAshaSupervisorMapping should reinstate the mappings the caller names") + void restoreAshaSupervisorMapping_shouldReinstateNamedMappings() { + String response = controller.restoreAshaSupervisorMapping("{\"ids\":[1,2]}"); + + assertSuccessContaining(response, "Restored successfully"); + verify(ashaSupervisorMappingService).restoreMappings(List.of(1L, 2L), "Admin"); + } + + @Test + @DisplayName("restoreAshaSupervisorMapping should refuse a request that names no mappings") + void restoreAshaSupervisorMapping_shouldRefuseRequestWithoutIds() { + String response = controller.restoreAshaSupervisorMapping("{}"); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response)); + assertTrue(response.contains("ids are required"), response); + } + + @Test + @DisplayName("restoreAshaSupervisorMapping should answer an error envelope when the reinstatement fails") + void restoreAshaSupervisorMapping_shouldAnswerErrorEnvelopeOnFailure() { + org.mockito.Mockito.doThrow(new IllegalStateException("no connection")) + .when(ashaSupervisorMappingService).restoreMappings(anyList(), anyString()); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.restoreAshaSupervisorMapping("{\"ids\":[1]}"))); + } + + @Test + @DisplayName("getFacilityByMappingID should answer the facility the mapping points at") + void getFacilityByMappingID_shouldAnswerMappedFacility() { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(9001); + mapping.setFacilityID(501); + M_Facility facility = new M_Facility(); + facility.setFacilityID(501); + facility.setFacilityName("PHC North"); + facility.setFacilityTypeID(3); + facility.setRuralUrban("Rural"); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(mapping)); + when(mainStoreRepo.findByFacilityIDAndDeleted(501, false)).thenReturn(facility); + + String response = controller.getFacilityByMappingID("{\"uSRMappingID\":9001}"); + + assertSuccessContaining(response, "PHC North"); + assertTrue(response.contains("Rural"), response); + } + + @Test + @DisplayName("getFacilityByMappingID should report a facility that has since been deleted") + void getFacilityByMappingID_shouldReportDeletedFacility() { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(9001); + mapping.setFacilityID(501); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(mapping)); + when(mainStoreRepo.findByFacilityIDAndDeleted(501, false)).thenReturn(null); + + assertSuccessContaining(controller.getFacilityByMappingID("{\"uSRMappingID\":9001}"), "facilityDeleted"); + } + + @Test + @DisplayName("getFacilityByMappingID should answer no facility for a mapping that was retired") + void getFacilityByMappingID_shouldAnswerNoFacilityForRetiredMapping() { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(9001); + mapping.setFacilityID(501); + mapping.setDeleted(Boolean.TRUE); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(mapping)); + + assertSuccessContaining(controller.getFacilityByMappingID("{\"uSRMappingID\":9001}"), "\"data\":{}"); + verify(mainStoreRepo, never()).findByFacilityIDAndDeleted(anyInt(), org.mockito.ArgumentMatchers.anyBoolean()); + } + + @Test + @DisplayName("getFacilityByMappingID should answer no facility for a mapping that names none") + void getFacilityByMappingID_shouldAnswerNoFacilityWhenMappingNamesNone() { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(9001); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(mapping)); + + assertSuccessContaining(controller.getFacilityByMappingID("{\"uSRMappingID\":9001}"), "\"data\":{}"); + } + + @Test + @DisplayName("getFacilityByMappingID should answer no facility for a mapping that does not exist") + void getFacilityByMappingID_shouldAnswerNoFacilityForUnknownMapping() { + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.empty()); + + assertSuccessContaining(controller.getFacilityByMappingID("{\"uSRMappingID\":9001}"), "\"data\":{}"); + } + + @Test + @DisplayName("getFacilityByMappingID should answer no facility when the request names no mapping") + void getFacilityByMappingID_shouldAnswerNoFacilityWithoutMappingId() { + assertSuccessContaining(controller.getFacilityByMappingID("{}"), "\"data\":{}"); + verify(employeeMasterRepo, never()).findById(anyInt()); + } + + @Test + @DisplayName("getFacilityByMappingID should answer an error envelope when the lookup fails") + void getFacilityByMappingID_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterRepo.findById(9001)).thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getFacilityByMappingID("{\"uSRMappingID\":9001}"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterAgentControllerTest.java b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterAgentControllerTest.java new file mode 100644 index 0000000..f3e07c2 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterAgentControllerTest.java @@ -0,0 +1,155 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.employeemaster; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.employeemaster.USRAgentMapping; +import com.iemr.admin.utils.exception.IEMRException; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The agent endpoints hand out and reclaim the CTI agent ids an employee logs + * into the call centre with. + */ +@DisplayName("EmployeeMasterController agent Test Suite") +class EmployeeMasterAgentControllerTest extends EmployeeMasterFixture { + + private static final String AGENT_REQUEST = "{\"providerServiceMapID\":4001,\"cti_CampaignName\":\"104\"}"; + + private static USRAgentMapping agent(Integer id, String agentId) { + return USRAgentMapping.initializeAllUSRAgentMapping(id, 9001, null, 4001, null, agentId, + "agent-secret", "104", Boolean.TRUE); + } + + @Test + @DisplayName("getAvailableAgentIds should answer the agent ids still free on the campaign") + void getAvailableAgentIds_shouldAnswerFreeAgentIds() throws Exception { + when(usrAgentMappingService.getAvailableAgentIds(AGENT_REQUEST)).thenReturn(List.of(agent(1, "A-1"))); + + assertSuccessContaining(controller.getAvailableAgentIds(AGENT_REQUEST), "A-1"); + } + + @Test + @DisplayName("getAvailableAgentIds should answer an error envelope when the lookup fails") + void getAvailableAgentIds_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(usrAgentMappingService.getAvailableAgentIds(anyString())) + .thenThrow(new IEMRException("providerServiceMapID is required")); + + assertIemrFailure(controller.getAvailableAgentIds("{}")); + } + + @Test + @DisplayName("createUSRAgentMapping should answer the mappings the service stored") + void createUSRAgentMapping_shouldAnswerStoredMappings() throws Exception { + when(usrAgentMappingService.createUSRAgentMapping(anyString())).thenReturn(List.of(agent(1, "A-1"))); + + assertSuccessContaining( + controller.createUSRAgentMapping("[{\"agentID\":\"A-1\",\"providerServiceMapID\":4001}]"), "A-1"); + } + + @Test + @DisplayName("createUSRAgentMapping should answer an error envelope when the store fails") + void createUSRAgentMapping_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(usrAgentMappingService.createUSRAgentMapping(anyString())) + .thenThrow(new IEMRException("agentID is required")); + + assertIemrFailure(controller.createUSRAgentMapping("[{}]")); + } + + @Test + @DisplayName("getAvailableCampaigns should answer the campaigns configured for the mapping") + void getAvailableCampaigns_shouldAnswerConfiguredCampaigns() throws Exception { + when(usrAgentMappingService.getAvailableCampaigns(anyString())).thenReturn(List.of("104", "1097")); + + assertSuccessContaining(controller.getAvailableCampaigns(AGENT_REQUEST), "104"); + } + + @Test + @DisplayName("getAvailableCampaigns should answer an error envelope when the lookup fails") + void getAvailableCampaigns_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(usrAgentMappingService.getAvailableCampaigns(anyString())) + .thenThrow(new IEMRException("providerServiceMapID is required")); + + assertIemrFailure(controller.getAvailableCampaigns("{}")); + } + + @Test + @DisplayName("updateAgentIds should answer how many agent ids the service changed") + void updateAgentIds_shouldAnswerChangedCount() throws Exception { + when(usrAgentMappingService.updateAgentIds(anyString())).thenReturn(1); + + assertSuccessContaining(controller.updateAgentIds("{\"isAvailable\":false,\"usrMappingID\":9001}"), "1"); + verify(usrAgentMappingService).updateAgentIds("{\"isAvailable\":false,\"usrMappingID\":9001}"); + } + + @Test + @DisplayName("updateAgentIds should answer an error envelope when the change fails") + void updateAgentIds_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(usrAgentMappingService.updateAgentIds(anyString())) + .thenThrow(new IEMRException("usrAgentMappingID is required")); + + assertIemrFailure(controller.updateAgentIds("{}")); + } + + @Test + @DisplayName("getAllAgentIds should answer every agent id under the mapping") + void getAllAgentIds_shouldAnswerEveryAgentId() throws Exception { + when(usrAgentMappingService.getAllAgentIds(anyString())).thenReturn(List.of(agent(1, "A-1"))); + + assertSuccessContaining(controller.getAllAgentIds(AGENT_REQUEST), "A-1"); + } + + @Test + @DisplayName("getAllAgentIds should answer an error envelope when the lookup fails") + void getAllAgentIds_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(usrAgentMappingService.getAllAgentIds(anyString())) + .thenThrow(new IEMRException("providerServiceMapID is required")); + + assertIemrFailure(controller.getAllAgentIds("{}")); + } + + @Test + @DisplayName("updateCTICampaignNameMapping should answer how many mappings moved campaign") + void updateCTICampaignNameMapping_shouldAnswerChangedCount() throws Exception { + when(usrAgentMappingService.updateCTICampaignNameMapping(anyString())).thenReturn(1); + + assertSuccessContaining( + controller.updateCTICampaignNameMapping("{\"cti_CampaignName\":\"1097\",\"usrAgentMappingID\":1}"), + "1"); + } + + @Test + @DisplayName("updateCTICampaignNameMapping should answer an error envelope when the change fails") + void updateCTICampaignNameMapping_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(usrAgentMappingService.updateCTICampaignNameMapping(anyString())) + .thenThrow(new IEMRException("usrAgentMappingID is required")); + + assertIemrFailure(controller.updateCTICampaignNameMapping("{}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterFixture.java b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterFixture.java new file mode 100644 index 0000000..f446f3c --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterFixture.java @@ -0,0 +1,117 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.employeemaster; + +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_UserDemographics; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.service.employeemaster.EmployeeMasterInter; +import com.iemr.admin.service.employeemaster.M_DesignationInter; +import com.iemr.admin.service.employeemaster.USRAgentMappingService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** Shared mocks and helpers for the suites over the employee master controller. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +abstract class EmployeeMasterFixture { + + protected static final String AUTH_HEADER = "sess-0d5f3a7c"; + + @Mock + protected M_DesignationInter m_DesignationInter; + + @Mock + protected EmployeeMasterInter employeeMasterInter; + + @Mock + protected USRAgentMappingService usrAgentMappingService; + + @InjectMocks + protected EmployeeMasterController controller; + + protected MockHttpServletRequest request; + + @BeforeEach + void prepareRequest() { + request = new MockHttpServletRequest(); + request.addHeader("Authorization", AUTH_HEADER); + } + + protected static M_User1 user(Integer id, String userName) { + M_User1 user = new M_User1(); + user.setUserID(id); + user.setUserName(userName); + return user; + } + + protected static M_UserDemographics demographics(Integer userId, String fathersName) { + M_UserDemographics demographics = new M_UserDemographics(); + demographics.setUserID(userId); + demographics.setFathersName(fathersName); + return demographics; + } + + protected static M_UserServiceRoleMapping2 roleMapping(Integer id, Integer userId) { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(id); + mapping.setUserID(userId); + return mapping; + } + + protected static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + protected static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + protected static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + /** + * Asserts the envelope reports the failure the response builder maps an + * {@link com.iemr.admin.utils.exception.IEMRException} onto. + */ + protected static void assertIemrFailure(String response) { + assertEquals(OutputResponse.USERID_FAILURE, statusCodeOf(response), response); + } + + protected static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } +} diff --git a/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterLookupControllerTest.java b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterLookupControllerTest.java new file mode 100644 index 0000000..2676ee6 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterLookupControllerTest.java @@ -0,0 +1,555 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.employeemaster; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.employeemaster.M_Community; +import com.iemr.admin.data.employeemaster.M_Designation; +import com.iemr.admin.data.employeemaster.M_Gender; +import com.iemr.admin.data.employeemaster.M_ProviderServiceMap1; +import com.iemr.admin.data.employeemaster.M_Religion; +import com.iemr.admin.data.employeemaster.M_Role; +import com.iemr.admin.data.employeemaster.M_Title; +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.employeemaster.M_Userqualification; +import com.iemr.admin.data.employeemaster.Showofficedetails1; +import com.iemr.admin.data.employeemaster.Showuserdetailsfromuserservicerolemapping; +import com.iemr.admin.data.employeemaster.V_Showuser; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The read-only endpoints back the employee search screens and the drop-downs + * that feed the employee forms. + */ +@DisplayName("EmployeeMasterController lookup Test Suite") +class EmployeeMasterLookupControllerTest extends EmployeeMasterFixture { + + private static Showuserdetailsfromuserservicerolemapping userDetail(Integer userId, String name) { + Showuserdetailsfromuserservicerolemapping detail = new Showuserdetailsfromuserservicerolemapping(); + detail.setUserID(userId); + detail.setUserName(name); + return detail; + } + + private static V_Showuser showUser(Integer userId, String name) { + V_Showuser user = new V_Showuser(); + user.setUserID(userId); + user.setUserName(name); + return user; + } + + @Test + @DisplayName("getAllRole should answer every role on record") + void getAllRole_shouldAnswerEveryRole() { + M_Role role = new M_Role(); + role.setRoleID(11); + role.setRoleName("Provider Admin"); + when(employeeMasterInter.getAllRole()).thenReturn(new ArrayList<>(List.of(role))); + + assertSuccessContaining(controller.getAllRole("{}"), "Provider Admin"); + } + + @Test + @DisplayName("getAllRole should answer an error envelope when the lookup fails") + void getAllRole_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getAllRole()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRole("{}")); + } + + @Test + @DisplayName("searchEmployee should answer the role mappings on record") + void searchEmployee_shouldAnswerRoleMappings() { + when(employeeMasterInter.getEmployeeDetails()) + .thenReturn(new ArrayList<>(List.of(roleMapping(9001, 3117)))); + + assertSuccessContaining(controller.searchEmployee("{}"), "9001"); + } + + @Test + @DisplayName("searchEmployee should answer an error envelope when the search fails") + void searchEmployee_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee("{}")); + } + + @Test + @DisplayName("searchEmployee1 should answer the second view of the role mappings") + void searchEmployee1_shouldAnswerRoleMappings() { + when(employeeMasterInter.getEmployeeDetails1()) + .thenReturn(new ArrayList<>(List.of(roleMapping(9001, 3117)))); + + assertSuccessContaining(controller.searchEmployee1("{}"), "9001"); + } + + @Test + @DisplayName("searchEmployee1 should answer an error envelope when the search fails") + void searchEmployee1_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails1()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee1("{}")); + } + + @Test + @DisplayName("searchEmployee2 should narrow the search to the provider and its state") + void searchEmployee2_shouldNarrowByProviderAndState() { + when(employeeMasterInter.getEmployeeDetails2(77, 29)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining( + controller.searchEmployee2("{\"serviceProviderID\":77,\"pSMStateID\":29}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee2 should answer an error envelope when the search fails") + void searchEmployee2_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails2(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee2("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee3 should narrow the search to the provider and role") + void searchEmployee3_shouldNarrowByProviderAndRole() { + when(employeeMasterInter.getEmployeeDetails3(77, 11)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining( + controller.searchEmployee3("{\"serviceProviderID\":77,\"roleID\":11}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee3 should answer an error envelope when the search fails") + void searchEmployee3_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails3(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee3("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee4 should narrow the search to the provider") + void searchEmployee4_shouldNarrowByProvider() { + when(employeeMasterInter.getEmployeeDetails4(77)) + .thenReturn(new ArrayList<>(List.of(showUser(3117, "dr.mehta")))); + + assertSuccessContaining(controller.searchEmployee4("{\"serviceProviderID\":77}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee4 should answer an error envelope when the search fails") + void searchEmployee4_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails4(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee4("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee5 should answer every user on record") + void searchEmployee5_shouldAnswerEveryUser() { + when(employeeMasterInter.getEmployeeDetails5()) + .thenReturn(new ArrayList<>(List.of(showUser(3117, "dr.mehta")))); + + assertSuccessContaining(controller.searchEmployee5("{}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee5 should answer an error envelope when the search fails") + void searchEmployee5_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails5()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee5("{}")); + } + + @Test + @DisplayName("searchEmployee6 should narrow the search to one user under the provider") + void searchEmployee6_shouldNarrowByProviderAndUser() { + when(employeeMasterInter.getEmployeeDetails6(77, 3117)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining( + controller.searchEmployee6("{\"serviceProviderID\":77,\"userID\":3117}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee6 should answer an error envelope when the search fails") + void searchEmployee6_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails6(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee6("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee7 should narrow the search down to the working district") + void searchEmployee7_shouldNarrowByDistrict() { + when(employeeMasterInter.getEmployeeDetails7(77, 29, 301)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining(controller.searchEmployee7( + "{\"serviceProviderID\":77,\"pSMStateID\":29,\"workingDistrictID\":301}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee7 should answer an error envelope when the search fails") + void searchEmployee7_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails7(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee7("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee8 should narrow the search down to the working location") + void searchEmployee8_shouldNarrowByLocation() { + when(employeeMasterInter.getEmployeeDetails8(77, 29, 301, 401)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining(controller.searchEmployee8("{\"serviceProviderID\":77,\"pSMStateID\":29," + + "\"workingDistrictID\":301,\"workingLocationID\":401}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee8 should answer an error envelope when the search fails") + void searchEmployee8_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails8(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee8("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee9 should narrow the search to the role within the state") + void searchEmployee9_shouldNarrowByStateAndRole() { + when(employeeMasterInter.getEmployeeDetails9(77, 29, 11)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining(controller.searchEmployee9( + "{\"serviceProviderID\":77,\"pSMStateID\":29,\"roleID\":11}"), "dr.mehta"); + } + + @Test + @DisplayName("searchEmployee9 should answer an error envelope when the search fails") + void searchEmployee9_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails9(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee9("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchEmployee10 should pass every filter the screen sends through") + void searchEmployee10_shouldPassEveryFilterThrough() { + when(employeeMasterInter.getEmployeeDetails11(77, 29, 3, 11, "dr.mehta", 3117)) + .thenReturn(new ArrayList<>(List.of(userDetail(3117, "dr.mehta")))); + + assertSuccessContaining(controller.searchEmployee10("{\"serviceProviderID\":77,\"pSMStateID\":29," + + "\"serviceID\":3,\"roleID\":11,\"userName\":\"dr.mehta\",\"userID\":3117}"), "dr.mehta"); + verify(employeeMasterInter).getEmployeeDetails11(77, 29, 3, 11, "dr.mehta", 3117); + } + + @Test + @DisplayName("searchEmployee10 should answer an error envelope when the search fails") + void searchEmployee10_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeDetails11(any(), any(), any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchEmployee10("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getAgentID should answer a success envelope without reaching a service") + void getAgentID_shouldAnswerSuccessEnvelope() { + assertGenericFailure(controller.getAgentID("{}")); + } + + @Test + @DisplayName("getAllTitle should answer every title on record") + void getAllTitle_shouldAnswerEveryTitle() { + M_Title title = new M_Title(); + title.setTitleID(1); + title.setTitleName("Dr"); + when(employeeMasterInter.getAllTitle()).thenReturn(new ArrayList<>(List.of(title))); + + assertSuccessContaining(controller.getAllTitle("{}"), "Dr"); + } + + @Test + @DisplayName("getAllTitle should answer an error envelope when the lookup fails") + void getAllTitle_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getAllTitle()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllTitle("{}")); + } + + @Test + @DisplayName("getAllGender should answer every gender on record") + void getAllGender_shouldAnswerEveryGender() { + M_Gender gender = new M_Gender(); + gender.setGenderID(1); + gender.setGenderName("Female"); + when(employeeMasterInter.getAllGender()).thenReturn(new ArrayList<>(List.of(gender))); + + assertSuccessContaining(controller.getAllGender("{}"), "Female"); + } + + @Test + @DisplayName("getAllGender should answer an error envelope when the lookup fails") + void getAllGender_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getAllGender()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllGender("{}")); + } + + @Test + @DisplayName("getAlllocation should resolve the mapping before reading the office details") + void getAlllocation_shouldResolveMappingFirst() { + M_ProviderServiceMap1 mapping = new M_ProviderServiceMap1(); + mapping.setProviderServiceMapID(4001); + Showofficedetails1 office = new Showofficedetails1(); + office.setLocationName("Bengaluru Office"); + when(employeeMasterInter.getAllByMapId2(77, 29, 3)).thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterInter.getlocationByMapid2(4001, 301)).thenReturn(new ArrayList<>(List.of(office))); + + assertSuccessContaining(controller.getAlllocation("{\"serviceProviderID\":77,\"stateID\":29," + + "\"serviceID\":3,\"districtID\":301}"), "Bengaluru Office"); + } + + @Test + @DisplayName("getAlllocation should fall back to no mapping when the provider has none") + void getAlllocation_shouldFallBackWithoutMapping() { + when(employeeMasterInter.getAllByMapId2(any(), any(), any())).thenReturn(new ArrayList<>()); + when(employeeMasterInter.getlocationByMapid2(0, 301)).thenReturn(new ArrayList<>()); + + assertSuccessContaining(controller.getAlllocation("{\"serviceProviderID\":77,\"districtID\":301}"), + "statusCode"); + verify(employeeMasterInter).getlocationByMapid2(0, 301); + } + + @Test + @DisplayName("getAlllocation should answer an error envelope when the lookup fails") + void getAlllocation_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getAllByMapId2(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAlllocation("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("FindEmployeeName should answer whether the user name is taken") + void findEmployeeName_shouldAnswerWhetherNameIsTaken() { + when(employeeMasterInter.FindEmployeeName("dr.mehta")).thenReturn("dr.mehta"); + + assertSuccessContaining(controller.FindEmployeeName("{\"userName\":\"dr.mehta\"}"), "dr.mehta"); + } + + @Test + @DisplayName("FindEmployeeName should answer an error envelope when the check fails") + void findEmployeeName_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.FindEmployeeName(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.FindEmployeeName("{\"userName\":\"dr.mehta\"}")); + } + + @Test + @DisplayName("Qualification should answer every qualification on record") + void qualification_shouldAnswerEveryQualification() { + M_Userqualification qualification = new M_Userqualification(); + qualification.setQualificationID(5); + qualification.setName("MBBS"); + when(employeeMasterInter.getQualification()).thenReturn(new ArrayList<>(List.of(qualification))); + + assertSuccessContaining(controller.Qualification("{}"), "MBBS"); + } + + @Test + @DisplayName("Qualification should answer an error envelope when the lookup fails") + void qualification_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getQualification()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.Qualification("{}")); + } + + @Test + @DisplayName("checkingEmpDetails should report that the identifiers are already in use") + void checkingEmpDetails_shouldReportIdentifiersInUse() { + when(employeeMasterInter.checkingEmpDetails("dr.mehta", "111122223333", "ABCDE1234F", "EMP-1", "HP-1")) + .thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkingEmpDetails("{\"userName\":\"dr.mehta\"," + + "\"aadhaarNo\":\"111122223333\",\"pAN\":\"ABCDE1234F\",\"employeeID\":\"EMP-1\"," + + "\"healthProfessionalID\":\"HP-1\"}"), "true"); + } + + @Test + @DisplayName("checkingEmpDetails should answer an error envelope when the check fails") + void checkingEmpDetails_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.checkingEmpDetails(any(), any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkingEmpDetails("{\"userName\":\"dr.mehta\"}")); + } + + @Test + @DisplayName("getDesignation should answer every designation on record") + void getDesignation_shouldAnswerEveryDesignation() { + M_Designation designation = new M_Designation(); + designation.setDesignationID(7); + designation.setDesignationName("ASHA"); + when(m_DesignationInter.getDesinationlist()).thenReturn(new ArrayList<>(List.of(designation))); + + assertSuccessContaining(controller.getDesignation("{}"), "ASHA"); + } + + @Test + @DisplayName("getDesignation should answer an error envelope when the lookup fails") + void getDesignation_shouldAnswerErrorEnvelopeOnFailure() { + when(m_DesignationInter.getDesinationlist()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getDesignation("{}")); + } + + @Test + @DisplayName("getEmployeeByDesignation should answer the employees holding the designation") + void getEmployeeByDesignation_shouldAnswerMatchingEmployees() { + when(employeeMasterInter.getEmployeeByDesiganationID(7, 77)) + .thenReturn(new ArrayList<>(List.of(user(3117, "dr.mehta")))); + + assertSuccessContaining( + controller.getEmployeeByDesignation("{\"designationID\":7,\"serviceProviderID\":77}"), "dr.mehta"); + } + + @Test + @DisplayName("getEmployeeByDesignation should answer an error envelope when the lookup fails") + void getEmployeeByDesignation_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getEmployeeByDesiganationID(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getEmployeeByDesignation("{\"designationID\":7}")); + } + + @Test + @DisplayName("completeUserDetails should answer the full user view") + void completeUserDetails_shouldAnswerFullUserView() { + when(employeeMasterInter.getcompleteUserDetails()) + .thenReturn(new ArrayList<>(List.of(showUser(3117, "dr.mehta")))); + + assertSuccessContaining(controller.completeUserDetails("{}"), "dr.mehta"); + } + + @Test + @DisplayName("completeUserDetails should answer an error envelope when the lookup fails") + void completeUserDetails_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getcompleteUserDetails()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.completeUserDetails("{}")); + } + + @Test + @DisplayName("getReligion should answer every religion on record") + void getReligion_shouldAnswerEveryReligion() { + M_Religion religion = new M_Religion(); + religion.setReligionID(1); + religion.setReligionType("Hindu"); + when(employeeMasterInter.getAllReligion()).thenReturn(new ArrayList<>(List.of(religion))); + + assertSuccessContaining(controller.getReligion("{}"), "Hindu"); + } + + @Test + @DisplayName("getReligion should answer an error envelope when the lookup fails") + void getReligion_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getAllReligion()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getReligion("{}")); + } + + @Test + @DisplayName("getCommunity should answer every community on record") + void getCommunity_shouldAnswerEveryCommunity() { + M_Community community = new M_Community(); + community.setCommunityID(1); + community.setCommunityType("General"); + when(employeeMasterInter.getAllCommunity()).thenReturn(new ArrayList<>(List.of(community))); + + assertSuccessContaining(controller.getCommunity("{}"), "General"); + } + + @Test + @DisplayName("getCommunity should answer an error envelope when the lookup fails") + void getCommunity_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getAllCommunity()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getCommunity("{}")); + } + + @Test + @DisplayName("FindEmployeeDetailsByUserName should answer the user the service resolves") + void findEmployeeDetailsByUserName_shouldAnswerResolvedUser() { + when(employeeMasterInter.FindEmployeeName1("dr.mehta")).thenReturn(user(3117, "dr.mehta")); + + assertSuccessContaining( + controller.FindEmployeeDetailsByUserName("{\"userName\":\"dr.mehta\"}"), "dr.mehta"); + } + + @Test + @DisplayName("FindEmployeeDetailsByUserName should answer an error envelope for an unknown user") + void findEmployeeDetailsByUserName_shouldAnswerErrorEnvelopeForUnknownUser() { + when(employeeMasterInter.FindEmployeeName1("dr.mehta")).thenReturn(null); + + assertCodeException(controller.FindEmployeeDetailsByUserName("{\"userName\":\"dr.mehta\"}")); + } + + @Test + @DisplayName("searchEmployee should answer a plain envelope for an empty result") + void searchEmployee_shouldAnswerPlainEnvelopeForEmptyResult() { + when(employeeMasterInter.getEmployeeDetails()).thenReturn(new ArrayList()); + + assertSuccessContaining(controller.searchEmployee("{}"), "\"statusCode\":200"); + } + + @Test + @DisplayName("getEmployeeByDesignation should read the provider from the user carrier, not the designation") + void getEmployeeByDesignation_shouldReadProviderFromUserCarrier() { + ArrayList employees = new ArrayList<>(List.of(user(3117, "dr.mehta"))); + when(employeeMasterInter.getEmployeeByDesiganationID(7, 77)).thenReturn(employees); + + controller.getEmployeeByDesignation("{\"designationID\":7,\"serviceProviderID\":77}"); + + verify(employeeMasterInter).getEmployeeByDesiganationID(7, 77); + } +} diff --git a/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterWriteControllerTest.java b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterWriteControllerTest.java new file mode 100644 index 0000000..ccdb99e --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeMasterWriteControllerTest.java @@ -0,0 +1,760 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.employeemaster; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_UserDemographics; +import com.iemr.admin.data.employeemaster.M_UserLangMapping; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.employeemaster.V_Userservicerolemapping; +import com.iemr.admin.data.rolemaster.UserRole; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The write endpoints create and amend employees, their demographics, and the + * language and role mappings that decide what each of them may do. + */ +@DisplayName("EmployeeMasterController write Test Suite") +class EmployeeMasterWriteControllerTest extends EmployeeMasterFixture { + + private static final String ADD_EMPLOYEE_REQUEST = "{\"firstName\":\"Asha\",\"lastName\":\"Rao\"," + + "\"userName\":\"asha.rao\",\"createdBy\":\"admin\",\"titleID\":1,\"genderID\":1," + + "\"fathersName\":\"Ravi\",\"languageID\":[1,2],\"weightage\":[5,3]," + + "\"canRead\":[true,false],\"canWrite\":[true,false],\"canSpeak\":[true,true]," + + "\"previleges\":[{\"providerServiceMapID\":4001,\"workingLocationID\":401,\"roleID\":[11,12]}]}"; + + private static M_UserLangMapping langMapping(Integer id, Integer languageId) { + M_UserLangMapping mapping = new M_UserLangMapping(); + mapping.setUserLangID(id); + mapping.setLanguageID(languageId); + return mapping; + } + + @Test + @DisplayName("addEmployee should store the user, the demographics, the languages and the roles") + void addEmployee_shouldStoreEveryPartOfTheEmployee() throws Exception { + when(employeeMasterInter.saveEmployee(any())).thenReturn(3117); + when(employeeMasterInter.saveDemography(any())).thenReturn(5001); + when(employeeMasterInter.mapLanguage(anyList())).thenReturn(new ArrayList<>(List.of(langMapping(7001, 1)))); + when(employeeMasterInter.mapRole(anyList(), anyString())) + .thenReturn(new ArrayList<>(List.of(roleMapping(9001, 3117)))); + + assertSuccessContaining(controller.addEmployee(ADD_EMPLOYEE_REQUEST, request), "9001"); + + ArgumentCaptor> languages = ArgumentCaptor.forClass(List.class); + verify(employeeMasterInter).mapLanguage(languages.capture()); + assertEquals(2, languages.getValue().size(), "each language in the request must be mapped"); + assertEquals(3117, languages.getValue().get(0).getUserID()); + + ArgumentCaptor> roles = ArgumentCaptor.forClass(List.class); + verify(employeeMasterInter).mapRole(roles.capture(), anyString()); + assertEquals(4001, roles.getValue().get(0).getProviderServiceMapID()); + } + + @Test + @DisplayName("addEmployee should pass the caller's authorization on to the role mapping") + void addEmployee_shouldPassAuthorizationOn() throws Exception { + when(employeeMasterInter.saveEmployee(any())).thenReturn(3117); + when(employeeMasterInter.mapLanguage(anyList())).thenReturn(new ArrayList<>()); + when(employeeMasterInter.mapRole(anyList(), anyString())).thenReturn(new ArrayList<>()); + + controller.addEmployee(ADD_EMPLOYEE_REQUEST, request); + + verify(employeeMasterInter).mapRole(anyList(), org.mockito.ArgumentMatchers.eq(AUTH_HEADER)); + } + + @Test + @DisplayName("addEmployee should answer an error envelope for a request that names no languages") + void addEmployee_shouldAnswerErrorEnvelopeWithoutLanguages() { + when(employeeMasterInter.saveEmployee(any())).thenReturn(3117); + + assertCodeException(controller.addEmployee("{\"firstName\":\"Asha\"}", request)); + } + + @Test + @DisplayName("addEmployee should answer an error envelope when the user cannot be stored") + void addEmployee_shouldAnswerErrorEnvelopeOnStoreFailure() { + when(employeeMasterInter.saveEmployee(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.addEmployee(ADD_EMPLOYEE_REQUEST, request)); + } + + @Test + @DisplayName("editEmployee should copy the edits onto both the user and the demographics") + void editEmployee_shouldCopyEditsOntoUserAndDemographics() { + M_User1 stored = user(3117, "old.name"); + M_UserDemographics storedDemographics = demographics(3117, "old father"); + when(employeeMasterInter.editEmployee(3117)).thenReturn(stored); + when(employeeMasterInter.saveEditData(stored)).thenReturn(stored); + when(employeeMasterInter.mdedit(3117)).thenReturn(storedDemographics); + when(employeeMasterInter.saveeditDemo(storedDemographics)).thenReturn(5001); + + String response = controller.editEmployee("{\"userID\":3117,\"firstName\":\"Asha\"," + + "\"userName\":\"asha.rao\",\"fathersName\":\"Ravi\",\"pinCode\":\"560001\"}"); + + assertSuccessContaining(response, "5001"); + assertEquals("Asha", stored.getFirstName()); + assertEquals("Ravi", storedDemographics.getFathersName()); + assertEquals("560001", storedDemographics.getPinCode()); + } + + @Test + @DisplayName("editEmployee should answer an error envelope for a user that does not exist") + void editEmployee_shouldAnswerErrorEnvelopeForUnknownUser() { + when(employeeMasterInter.editEmployee(3117)).thenReturn(null); + + assertCodeException(controller.editEmployee("{\"userID\":3117}")); + } + + @Test + @DisplayName("deleteEmployee should mark the employee deleted and answer what was saved") + void deleteEmployee_shouldMarkEmployeeDeleted() { + M_User1 stored = user(3117, "asha.rao"); + when(employeeMasterInter.editEmployee(3117)).thenReturn(stored); + when(employeeMasterInter.saveEditData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteEmployee("{\"userID\":3117}"), "asha.rao"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteEmployee should answer an error envelope for a user that does not exist") + void deleteEmployee_shouldAnswerErrorEnvelopeForUnknownUser() { + when(employeeMasterInter.editEmployee(3117)).thenReturn(null); + + assertCodeException(controller.deleteEmployee("{\"userID\":3117}")); + } + + @Test + @DisplayName("updateEmployee should build one role mapping per role the request names") + void updateEmployee_shouldBuildOneMappingPerRole() { + when(employeeMasterInter.mapRoleUpdation(anyList())) + .thenReturn(new ArrayList<>(List.of(roleMapping(9001, 3117)))); + + String response = controller.updateEmployee("{\"userID\":3117,\"roleID1\":[11,12]," + + "\"providerServiceMapID\":4001,\"workingLocationID\":401,\"createdBy\":\"admin\"}"); + + assertSuccessContaining(response, "9001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(employeeMasterInter).mapRoleUpdation(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals(11, captor.getValue().get(0).getRoleID()); + assertEquals(4001, captor.getValue().get(0).getProviderServiceMapID()); + } + + @Test + @DisplayName("updateEmployee should answer an error envelope when the request names no roles") + void updateEmployee_shouldAnswerErrorEnvelopeWithoutRoles() { + assertCodeException(controller.updateEmployee("{\"userID\":3117}")); + } + + @Test + @DisplayName("deleteEmployeeRole should mark the role mapping deleted") + void deleteEmployeeRole_shouldMarkRoleMappingDeleted() { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + when(employeeMasterInter.uRoledelte(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleEdit(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteEmployeeRole("{\"uSRMappingID\":9001}"), "9001"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteEmployeeRole should answer an error envelope for a mapping that does not exist") + void deleteEmployeeRole_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(employeeMasterInter.uRoledelte(9001)).thenReturn(null); + + assertCodeException(controller.deleteEmployeeRole("{\"uSRMappingID\":9001}")); + } + + @Test + @DisplayName("usrRoleAndCtiMapping should answer the summary the service reports") + void usrRoleAndCtiMapping_shouldAnswerSummary() throws Exception { + when(employeeMasterInter.mapctiAgent(anyList())).thenReturn("2 agents mapped"); + + assertSuccessContaining(controller.usrRoleAndCtiMapping("[{\"uSRMappingID\":9001,\"agentID\":\"A-1\"}]"), + "2 agents mapped"); + } + + @Test + @DisplayName("usrRoleAndCtiMapping should answer an error envelope when the mapping fails") + void usrRoleAndCtiMapping_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(employeeMasterInter.mapctiAgent(anyList())).thenThrow(new IllegalStateException("cti is unreachable")); + + assertGenericFailure(controller.usrRoleAndCtiMapping("[{\"uSRMappingID\":9001}]")); + } + + @Test + @DisplayName("ResetUserPassword should answer the outcome the service reports") + void resetUserPassword_shouldAnswerOutcome() { + when(employeeMasterInter.ResetPassword(any())).thenReturn("password reset"); + + assertSuccessContaining(controller.ResetUserPassword("{\"userID\":3117}"), "password reset"); + } + + @Test + @DisplayName("ResetUserPassword should answer an error envelope when the reset fails") + void resetUserPassword_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.ResetPassword(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.ResetUserPassword("{\"userID\":3117}")); + } + + @Test + @DisplayName("createProviderAdmin should answer the admins the service stored") + void createProviderAdmin_shouldAnswerStoredAdmins() throws Exception { + when(employeeMasterInter.createProviderAdmin(anyList())) + .thenReturn(new ArrayList<>(List.of(user(3117, "pa.user")))); + + assertSuccessContaining(controller.createProviderAdmin("[{\"userName\":\"pa.user\"}]"), "pa.user"); + } + + @Test + @DisplayName("createProviderAdmin should answer an error envelope when the store fails") + void createProviderAdmin_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(employeeMasterInter.createProviderAdmin(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createProviderAdmin("[{\"userName\":\"pa.user\"}]")); + } + + @Test + @DisplayName("getProviderAdmin should answer every provider admin on record") + void getProviderAdmin_shouldAnswerEveryAdmin() { + when(employeeMasterInter.getProviderAdmin()).thenReturn(new ArrayList<>(List.of(user(3117, "pa.user")))); + + assertSuccessContaining(controller.getProviderAdmin("{}"), "pa.user"); + } + + @Test + @DisplayName("getProviderAdmin should answer an error envelope when the lookup fails") + void getProviderAdmin_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getProviderAdmin()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getProviderAdmin("{}")); + } + + @Test + @DisplayName("editProviderAdmin should copy the edited contact details onto the stored admin") + void editProviderAdmin_shouldCopyContactDetails() { + M_User1 stored = user(3117, "pa.user"); + when(employeeMasterInter.getProviderAdminForEdit(3117)).thenReturn(stored); + when(employeeMasterInter.saveeditedData(stored)).thenReturn(stored); + + String response = controller.editProviderAdmin("{\"userID\":3117,\"firstName\":\"Asha\"," + + "\"emailID\":\"asha@example.org\",\"contactNo\":\"9000000001\",\"remarks\":\"promoted\"," + + "\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "Asha"); + assertEquals("asha@example.org", stored.getEmailID()); + assertEquals("promoted", stored.getRemarks()); + } + + @Test + @DisplayName("editProviderAdmin should answer an error envelope for an admin that does not exist") + void editProviderAdmin_shouldAnswerErrorEnvelopeForUnknownAdmin() { + when(employeeMasterInter.getProviderAdminForEdit(3117)).thenReturn(null); + + assertCodeException(controller.editProviderAdmin("{\"userID\":3117}")); + } + + @Test + @DisplayName("deleteProviderAdmin should mark the admin deleted") + void deleteProviderAdmin_shouldMarkAdminDeleted() { + M_User1 stored = user(3117, "pa.user"); + when(employeeMasterInter.getProviderAdminForEdit(3117)).thenReturn(stored); + when(employeeMasterInter.saveeditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteProviderAdmin("{\"userID\":3117,\"deleted\":true}"), "pa.user"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteProviderAdmin should answer an error envelope for an admin that does not exist") + void deleteProviderAdmin_shouldAnswerErrorEnvelopeForUnknownAdmin() { + when(employeeMasterInter.getProviderAdminForEdit(3117)).thenReturn(null); + + assertCodeException(controller.deleteProviderAdmin("{\"userID\":3117,\"deleted\":true}")); + } + + @Test + @DisplayName("createNewUser should store the demographics and register the user with the call centre") + void createNewUser_shouldStoreDemographicsAndRegisterUser() throws Exception { + M_User1 created = user(3117, "asha.rao"); + created.setCreatedBy("admin"); + when(employeeMasterInter.createNewUser(anyList())).thenReturn(new ArrayList<>(List.of(created))); + when(employeeMasterInter.SaveDemographics(any())).thenReturn(new ArrayList<>()); + + String response = controller.createNewUser( + "[{\"userName\":\"asha.rao\",\"createdBy\":\"admin\",\"fathersName\":\"Ravi\"}]", request); + + assertSuccessContaining(response, "asha.rao"); + verify(employeeMasterInter).createUserInCallCentre(created, AUTH_HEADER); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class); + verify(employeeMasterInter).SaveDemographics(captor.capture()); + assertEquals("Ravi", captor.getValue().get(0).getFathersName()); + assertEquals(3117, captor.getValue().get(0).getUserID()); + } + + @Test + @DisplayName("createNewUser should answer an error envelope when the store fails") + void createNewUser_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(employeeMasterInter.createNewUser(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createNewUser("[{\"userName\":\"asha.rao\"}]", request)); + } + + @Test + @DisplayName("editUserDetails should copy the edits onto both the user and the demographics") + void editUserDetails_shouldCopyEditsOntoBoth() { + M_User1 stored = user(3117, "asha.rao"); + M_UserDemographics storedDemographics = demographics(3117, "old father"); + when(employeeMasterInter.editData(3117)).thenReturn(stored); + when(employeeMasterInter.saveeditedData(stored)).thenReturn(stored); + when(employeeMasterInter.DataByUserID(3117)).thenReturn(storedDemographics); + when(employeeMasterInter.saveeditedDemoData(storedDemographics)).thenReturn(storedDemographics); + + String response = controller.editUserDetails("{\"userID\":3117,\"firstName\":\"Asha\"," + + "\"emailID\":\"asha@example.org\",\"fathersName\":\"Ravi\",\"permPinCode\":\"560002\"," + + "\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "Ravi"); + assertEquals("Asha", stored.getFirstName()); + assertEquals(560002, storedDemographics.getPermPinCode()); + } + + @Test + @DisplayName("editUserDetails should answer an empty envelope when the user has no demographics on record") + void editUserDetails_shouldAnswerEmptyEnvelopeWithoutDemographics() { + M_User1 stored = user(3117, "asha.rao"); + when(employeeMasterInter.editData(3117)).thenReturn(stored); + when(employeeMasterInter.saveeditedData(stored)).thenReturn(stored); + when(employeeMasterInter.DataByUserID(3117)).thenReturn(null); + + assertGenericFailure(controller.editUserDetails("{\"userID\":3117,\"firstName\":\"Asha\"}")); + verify(employeeMasterInter, never()).saveeditedDemoData(any()); + } + + @Test + @DisplayName("editUserDetails should answer an error envelope for a user that does not exist") + void editUserDetails_shouldAnswerErrorEnvelopeForUnknownUser() { + when(employeeMasterInter.editData(3117)).thenReturn(null); + + assertCodeException(controller.editUserDetails("{\"userID\":3117}")); + } + + @Test + @DisplayName("deletedUserDetails should release the agent id and expire the session for a deleted user") + void deletedUserDetails_shouldReleaseAgentIdAndExpireSession() { + M_User1 stored = user(3117, "asha.rao"); + stored.setAgentID("A-1"); + M_UserDemographics storedDemographics = demographics(3117, "Ravi"); + when(employeeMasterInter.editData(3117)).thenReturn(stored); + when(employeeMasterInter.saveeditedData(stored)).thenReturn(stored); + when(employeeMasterInter.DataByUserID(3117)).thenReturn(storedDemographics); + when(employeeMasterInter.saveeditedDemoData(storedDemographics)).thenReturn(storedDemographics); + + assertSuccessContaining( + controller.deletedUserDetails("{\"userID\":3117,\"deleted\":true}", request), "Ravi"); + verify(usrAgentMappingService).updateDeletedAgentIDStatus("A-1"); + verify(employeeMasterInter).expireAuth(stored, AUTH_HEADER); + } + + @Test + @DisplayName("deletedUserDetails should leave the agent id alone when the user is only reinstated") + void deletedUserDetails_shouldLeaveAgentIdAloneWhenReinstating() { + M_User1 stored = user(3117, "asha.rao"); + M_UserDemographics storedDemographics = demographics(3117, "Ravi"); + when(employeeMasterInter.editData(3117)).thenReturn(stored); + when(employeeMasterInter.saveeditedData(stored)).thenReturn(stored); + when(employeeMasterInter.DataByUserID(3117)).thenReturn(storedDemographics); + when(employeeMasterInter.saveeditedDemoData(storedDemographics)).thenReturn(storedDemographics); + + controller.deletedUserDetails("{\"userID\":3117,\"deleted\":false}", request); + + verify(usrAgentMappingService, never()).updateDeletedAgentIDStatus(anyString()); + } + + @Test + @DisplayName("deletedUserDetails should answer an error envelope for a user that does not exist") + void deletedUserDetails_shouldAnswerErrorEnvelopeForUnknownUser() { + when(employeeMasterInter.editData(3117)).thenReturn(null); + + assertCodeException(controller.deletedUserDetails("{\"userID\":3117,\"deleted\":true}", request)); + } + + @Test + @DisplayName("searchMappedLanguageByUserId should answer the languages mapped to the user") + void searchMappedLanguageByUserId_shouldAnswerMappedLanguages() { + when(employeeMasterInter.searchMappedLangugeByUserId(3117)) + .thenReturn(new ArrayList<>(List.of(langMapping(7001, 1)))); + + assertSuccessContaining(controller.searchMappedLanguageByUserId("{\"userID\":3117}"), "7001"); + } + + @Test + @DisplayName("searchMappedLanguageByUserId should answer an error envelope when the lookup fails") + void searchMappedLanguageByUserId_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.searchMappedLangugeByUserId(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchMappedLanguageByUserId("{\"userID\":3117}")); + } + + @Test + @DisplayName("getUserMappedLanguage should answer the languages mapped under the provider") + void getUserMappedLanguage_shouldAnswerProviderLanguages() { + when(employeeMasterInter.getMappedLanguge(77)) + .thenReturn(new ArrayList<>(List.of(langMapping(7001, 1)))); + + assertSuccessContaining(controller.getUserMappedLanguage("{\"serviceProviderID\":77}"), "7001"); + } + + @Test + @DisplayName("getUserMappedLanguage should answer an error envelope when the lookup fails") + void getUserMappedLanguage_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getMappedLanguge(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getUserMappedLanguage("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("UserLangMapping should build one mapping per language in the request") + void userLangMapping_shouldBuildOneMappingPerLanguage() { + when(employeeMasterInter.mapLanguage(anyList())) + .thenReturn(new ArrayList<>(List.of(langMapping(7001, 1)))); + + String response = controller.UserLangMapping("[{\"userID\":3117,\"createdBy\":\"admin\"," + + "\"serviceProviderID\":77,\"languageID\":[1,2],\"weightage\":[5,3]," + + "\"canRead\":[true,false],\"canWrite\":[true,false],\"canSpeak\":[true,true]," + + "\"weightage_Read\":[5,3],\"weightage_Write\":[5,3],\"weightage_Speak\":[5,3]}]"); + + assertSuccessContaining(response, "7001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(employeeMasterInter).mapLanguage(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals(77, captor.getValue().get(0).getServiceProviderID()); + } + + @Test + @DisplayName("UserLangMapping should answer an error envelope when the request names no languages") + void userLangMapping_shouldAnswerErrorEnvelopeWithoutLanguages() { + assertCodeException(controller.UserLangMapping("[{\"userID\":3117}]")); + } + + @Test + @DisplayName("updateUserLanguageMapping should copy the edits onto the stored mapping") + void updateUserLanguageMapping_shouldCopyEdits() { + M_UserLangMapping stored = langMapping(7001, 1); + when(employeeMasterInter.updateLangMapping(7001)).thenReturn(stored); + when(employeeMasterInter.saveUserLangEditedData(stored)).thenReturn(stored); + + String response = controller.updateUserLanguageMapping("{\"userLangID\":7001,\"userID\":3117," + + "\"languageID\":2,\"weightage\":9,\"canRead\":true,\"canWrite\":false,\"canSpeak\":true," + + "\"weightage_Read\":9,\"weightage_Write\":1,\"weightage_Speak\":9,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "7001"); + assertEquals(2, stored.getLanguageID()); + assertEquals(9, stored.getWeightage()); + } + + @Test + @DisplayName("updateUserLanguageMapping should answer an error envelope for a mapping that does not exist") + void updateUserLanguageMapping_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(employeeMasterInter.updateLangMapping(7001)).thenReturn(null); + + assertCodeException(controller.updateUserLanguageMapping("{\"userLangID\":7001}")); + } + + @Test + @DisplayName("UserLanguageMapping should mark the language mapping deleted") + void userLanguageMapping_shouldMarkMappingDeleted() { + M_UserLangMapping stored = langMapping(7001, 1); + when(employeeMasterInter.updateLangMapping(7001)).thenReturn(stored); + when(employeeMasterInter.saveUserLangEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.UserLanguageMapping("{\"userLangID\":7001,\"deleted\":true}"), "7001"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("UserLanguageMapping should answer an error envelope for a mapping that does not exist") + void userLanguageMapping_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(employeeMasterInter.updateLangMapping(7001)).thenReturn(null); + + assertCodeException(controller.UserLanguageMapping("{\"userLangID\":7001,\"deleted\":true}")); + } + + @Test + @DisplayName("UserRoleMapping should build one mapping per role under each privilege") + void userRoleMapping_shouldBuildOneMappingPerRole() throws Exception { + when(employeeMasterInter.mapRole(anyList(), anyString())) + .thenReturn(new ArrayList<>(List.of(roleMapping(9001, 3117)))); + + String response = controller.UserRoleMapping("[{\"userID\":3117,\"createdBy\":\"admin\"," + + "\"serviceProviderID\":77,\"previleges\":[{\"roleID\":[11,12]," + + "\"providerServiceMapID\":4001,\"workingLocationID\":401}]}]", request); + + assertSuccessContaining(response, "9001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(employeeMasterInter).mapRole(captor.capture(), anyString()); + assertEquals(2, captor.getValue().size()); + assertEquals(11, captor.getValue().get(0).getRoleID()); + } + + @Test + @DisplayName("UserRoleMapping should answer an error envelope when the request names no privileges") + void userRoleMapping_shouldAnswerErrorEnvelopeWithoutPrivileges() { + assertCodeException(controller.UserRoleMapping("[{\"userID\":3117}]", request)); + } + + @Test + @DisplayName("UserRoleMappings should carry the 1097 helpline flags onto the mapping") + void userRoleMappings_shouldCarryHelplineFlags() throws Exception { + when(employeeMasterInter.mapRole(anyList(), anyString())) + .thenReturn(new ArrayList<>(List.of(roleMapping(9001, 3117)))); + + String response = controller.UserRoleMappings("[{\"userID\":3117,\"createdBy\":\"admin\"," + + "\"serviceProviderID\":77,\"previleges\":[{\"providerServiceMapID\":4001," + + "\"workingLocationID\":401,\"stateID\":29,\"districtID\":301,\"blockID\":401," + + "\"blockName\":\"North\",\"facilityID\":501," + + "\"ID\":[{\"roleID\":11,\"inbound\":true,\"outbound\":false," + + "\"teleConsultation\":\"Y\"}]}]}]", request); + + assertSuccessContaining(response, "9001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(employeeMasterInter).mapRole(captor.capture(), anyString()); + M_UserServiceRoleMapping2 built = captor.getValue().get(0); + assertEquals(11, built.getRoleID()); + assertTrue(built.getInbound()); + assertEquals("Y", built.getTeleConsultation()); + assertEquals("North", built.getBlockName()); + } + + @Test + @DisplayName("UserRoleMappings should answer an error envelope when the request names no privileges") + void userRoleMappings_shouldAnswerErrorEnvelopeWithoutPrivileges() throws Exception { + assertCodeException(controller.UserRoleMappings("[{\"userID\":3117}]", request)); + } + + @Test + @DisplayName("updateUserRoleMapping should cascade the supervisor mappings when the role changes") + void updateUserRoleMapping_shouldCascadeWhenRoleChanges() throws Exception { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + stored.setRoleID(11); + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleMappingeditedData(any(), anyString())).thenReturn(stored); + + String response = controller.updateUserRoleMapping("{\"uSRMappingID\":9001,\"userID\":3117," + + "\"roleID\":12,\"modifiedBy\":\"admin\"}", request); + + assertSuccessContaining(response, "9001"); + verify(employeeMasterInter).cascadeDeleteAshaMappingsForUser(3117); + assertEquals(12, stored.getRoleID()); + } + + @Test + @DisplayName("updateUserRoleMapping should cascade the supervisor mappings when the facility changes") + void updateUserRoleMapping_shouldCascadeWhenFacilityChanges() throws Exception { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + stored.setRoleID(11); + stored.setFacilityID(501); + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleMappingeditedData(any(), anyString())).thenReturn(stored); + + controller.updateUserRoleMapping("{\"uSRMappingID\":9001,\"userID\":3117,\"roleID\":11," + + "\"facilityID\":502}", request); + + verify(employeeMasterInter).cascadeDeleteAshaMappingsForUser(3117); + } + + @Test + @DisplayName("updateUserRoleMapping should leave the supervisor mappings alone when nothing changed") + void updateUserRoleMapping_shouldLeaveSupervisorMappingsAlone() throws Exception { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + stored.setRoleID(11); + stored.setFacilityID(501); + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleMappingeditedData(any(), anyString())).thenReturn(stored); + + controller.updateUserRoleMapping("{\"uSRMappingID\":9001,\"userID\":3117,\"roleID\":11," + + "\"facilityID\":501}", request); + + verify(employeeMasterInter, never()).cascadeDeleteAshaMappingsForUser(anyInt()); + } + + @Test + @DisplayName("updateUserRoleMapping should carry the optional call flags only when the request sets them") + void updateUserRoleMapping_shouldCarryOptionalFlagsOnlyWhenSet() throws Exception { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + stored.setRoleID(11); + stored.setInbound(Boolean.TRUE); + stored.setTeleConsultation("Y"); + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleMappingeditedData(any(), anyString())).thenReturn(stored); + + controller.updateUserRoleMapping("{\"uSRMappingID\":9001,\"userID\":3117,\"roleID\":11}", request); + + assertTrue(stored.getInbound(), "an unset flag must keep the value already on record"); + assertEquals("Y", stored.getTeleConsultation()); + } + + @Test + @DisplayName("updateUserRoleMapping should answer an error envelope for a mapping that does not exist") + void updateUserRoleMapping_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(null); + + assertCodeException(controller.updateUserRoleMapping("{\"uSRMappingID\":9001}", request)); + } + + @Test + @DisplayName("deleteUserRoleMapping should cascade the supervisor mappings before marking it deleted") + void deleteUserRoleMapping_shouldCascadeBeforeDeleting() throws Exception { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleMappingeditedData(any(), anyString())).thenReturn(stored); + + assertSuccessContaining( + controller.deleteUserRoleMapping("{\"uSRMappingID\":9001,\"deleted\":true}", request), "9001"); + verify(employeeMasterInter).cascadeDeleteAshaMappingsForDeactivation(stored); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteUserRoleMapping should not cascade when the mapping is being reinstated") + void deleteUserRoleMapping_shouldNotCascadeWhenReinstating() throws Exception { + M_UserServiceRoleMapping2 stored = roleMapping(9001, 3117); + stored.setDeleted(Boolean.TRUE); + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(stored); + when(employeeMasterInter.saveRoleMappingeditedData(any(), anyString())).thenReturn(stored); + + controller.deleteUserRoleMapping("{\"uSRMappingID\":9001,\"deleted\":false}", request); + + verify(employeeMasterInter, never()).cascadeDeleteAshaMappingsForDeactivation(any()); + } + + @Test + @DisplayName("deleteUserRoleMapping should answer an error envelope for a mapping that does not exist") + void deleteUserRoleMapping_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(employeeMasterInter.getDataUsrId(9001)).thenReturn(null); + + assertCodeException(controller.deleteUserRoleMapping("{\"uSRMappingID\":9001,\"deleted\":true}", request)); + } + + @Test + @DisplayName("getUserRoleMapped should answer the mapped roles for the provider") + void getUserRoleMapped_shouldAnswerMappedRoles() { + V_Userservicerolemapping mapped = new V_Userservicerolemapping(); + mapped.setuSRMappingID(9001); + mapped.setName("Asha Rao"); + when(employeeMasterInter.getMappedRole(77)).thenReturn(new ArrayList<>(List.of(mapped))); + + assertSuccessContaining(controller.getUserRoleMapped("{\"serviceProviderID\":77}"), "Asha Rao"); + } + + @Test + @DisplayName("getUserRoleMapped should answer an error envelope when the lookup fails") + void getUserRoleMapped_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getMappedRole(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getUserRoleMapped("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("searchMappedRoleByNameorUserId should answer the roles matching the name or id") + void searchMappedRoleByNameorUserId_shouldAnswerMatchingRoles() { + V_Userservicerolemapping mapped = new V_Userservicerolemapping(); + mapped.setuSRMappingID(9001); + mapped.setName("Asha Rao"); + when(employeeMasterInter.getMappedRole("Asha Rao", 3117)).thenReturn(new ArrayList<>(List.of(mapped))); + + assertSuccessContaining( + controller.searchMappedRoleByNameorUserId("{\"name\":\"Asha Rao\",\"userID\":3117}"), "Asha Rao"); + } + + @Test + @DisplayName("searchMappedRoleByNameorUserId should answer an error envelope when the search fails") + void searchMappedRoleByNameorUserId_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getMappedRole(anyString(), anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchMappedRoleByNameorUserId("{\"name\":\"Asha Rao\",\"userID\":3117}")); + } + + @Test + @DisplayName("getUserRoleTM should answer the telemedicine roles the service resolves") + void getUserRoleTM_shouldAnswerTelemedicineRoles() { + UserRole role = new UserRole(); + role.setRoleID(11); + role.setRolename("Specialist"); + when(employeeMasterInter.getUserRoleTM(any())).thenReturn(new ArrayList<>(List.of(role))); + + assertSuccessContaining(controller.getUserRoleTM("{\"userID\":3117}"), "Specialist"); + } + + @Test + @DisplayName("getUserRoleTM should answer an error envelope when the lookup fails") + void getUserRoleTM_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeMasterInter.getUserRoleTM(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getUserRoleTM("{\"userID\":3117}")); + } + + @Test + @DisplayName("deleteUserRoleMappingTM should answer the mapping the service retired") + void deleteUserRoleMappingTM_shouldAnswerRetiredMapping() throws Exception { + when(employeeMasterInter.deleteuserrolemapTM(any())).thenReturn(roleMapping(9001, 3117)); + + assertSuccessContaining( + controller.deleteUserRoleMappingTM("{\"uSRMappingID\":9001,\"deleted\":true}", request), "9001"); + } + + @Test + @DisplayName("deleteUserRoleMappingTM should answer an error envelope when the retirement fails") + void deleteUserRoleMappingTM_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(employeeMasterInter.deleteuserrolemapTM(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.deleteUserRoleMappingTM("{\"uSRMappingID\":9001}", request)); + } +} diff --git a/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeSignatureControllerTest.java b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeSignatureControllerTest.java new file mode 100644 index 0000000..87a59d5 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/employeemaster/EmployeeSignatureControllerTest.java @@ -0,0 +1,238 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.employeemaster; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.iemr.admin.data.employeemaster.EmployeeSignature; +import com.iemr.admin.service.employeemaster.EmployeeSignatureServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * A user's signature is stamped onto the prescriptions they sign, so the upload, + * download and activation endpoints all guard a clinically meaningful artefact. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("EmployeeSignatureController Test Suite") +class EmployeeSignatureControllerTest { + + private static final byte[] SIGNATURE = "a-png-body".getBytes(StandardCharsets.UTF_8); + + @Mock + private EmployeeSignatureServiceImpl employeeSignatureServiceImpl; + + @InjectMocks + private EmployeeSignatureController controller; + + private static EmployeeSignature signature(String fileName, String fileType) { + EmployeeSignature signature = new EmployeeSignature(); + signature.setUserID(3117L); + signature.setFileName(fileName); + signature.setFileType(fileType); + signature.setSignature(SIGNATURE); + return signature; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("uploadFile should decode the uploaded body and answer the stored signature id") + void uploadFile_shouldDecodeAndStore() { + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setUserID(3117L); + uploaded.setFileContent(Base64.getEncoder().encodeToString(SIGNATURE)); + when(employeeSignatureServiceImpl.uploadSignature(any())).thenReturn(9001L); + + String response = controller.uploadFile(uploaded); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("9001"), response); + assertArrayEquals(SIGNATURE, uploaded.getSignature(), + "the base64 body must be decoded before it is stored"); + } + + @Test + @DisplayName("uploadFile should answer an error envelope for a body that is not valid base64") + void uploadFile_shouldAnswerErrorEnvelopeForInvalidBody() { + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setFileContent("not base 64 !!"); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.uploadFile(uploaded))); + } + + @Test + @DisplayName("uploadFile should answer an error envelope when the store fails") + void uploadFile_shouldAnswerErrorEnvelopeOnStoreFailure() { + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setFileContent(Base64.getEncoder().encodeToString(SIGNATURE)); + when(employeeSignatureServiceImpl.uploadSignature(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.uploadFile(uploaded))); + } + + @Test + @DisplayName("fetchFile should answer the signature as an attachment of its own type") + void fetchFile_shouldAnswerSignatureAsAttachment() throws Exception { + when(employeeSignatureServiceImpl.fetchSignature(3117L)) + .thenReturn(signature("asha-signature.png", MediaType.IMAGE_PNG_VALUE)); + + ResponseEntity response = controller.fetchFile(3117L); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(MediaType.IMAGE_PNG, response.getHeaders().getContentType()); + assertArrayEquals(SIGNATURE, response.getBody()); + assertTrue(response.getHeaders().getContentDisposition().toString().contains("asha-signature.png")); + } + + @Test + @DisplayName("fetchFile should fall back to a plain download for a file type it cannot parse") + void fetchFile_shouldFallBackForUnparseableType() throws Exception { + when(employeeSignatureServiceImpl.fetchSignature(3117L)) + .thenReturn(signature("asha-signature.png", "not/a/media/type")); + + ResponseEntity response = controller.fetchFile(3117L); + + assertEquals(MediaType.APPLICATION_OCTET_STREAM, response.getHeaders().getContentType()); + } + + @Test + @DisplayName("fetchFile should fall back to a plain download when no file type is on record") + void fetchFile_shouldFallBackWithoutAType() throws Exception { + when(employeeSignatureServiceImpl.fetchSignature(3117L)) + .thenReturn(signature("asha-signature.png", null)); + + assertEquals(MediaType.APPLICATION_OCTET_STREAM, controller.fetchFile(3117L).getHeaders().getContentType()); + } + + @Test + @DisplayName("fetchFile should raise rather than answer an empty download for a missing signature") + void fetchFile_shouldRaiseForMissingSignature() { + when(employeeSignatureServiceImpl.fetchSignature(3117L)).thenReturn(null); + + Exception thrown = assertThrows(Exception.class, () -> controller.fetchFile(3117L)); + assertTrue(thrown.getMessage().contains("Error while downloading file"), thrown.getMessage()); + } + + @Test + @DisplayName("existFile should report both that a signature exists and whether it is active") + void existFile_shouldReportExistenceAndActivation() throws Exception { + when(employeeSignatureServiceImpl.existSignature(3117L)).thenReturn(Boolean.TRUE); + when(employeeSignatureServiceImpl.isSignatureActive(3117L)).thenReturn(Boolean.TRUE); + + String response = controller.existFile(3117L); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("\"signStatus\":\"true\""), response); + assertTrue(response.contains("\"response\":\"true\""), response); + } + + @Test + @DisplayName("existFile should report a signature that exists but has been deactivated") + void existFile_shouldReportDeactivatedSignature() throws Exception { + when(employeeSignatureServiceImpl.existSignature(3117L)).thenReturn(Boolean.TRUE); + when(employeeSignatureServiceImpl.isSignatureActive(3117L)).thenReturn(Boolean.FALSE); + + assertTrue(controller.existFile(3117L).contains("\"signStatus\":\"false\"")); + } + + @Test + @DisplayName("existFile should answer an error envelope when the check fails") + void existFile_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(employeeSignatureServiceImpl.existSignature(anyLong())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.existFile(3117L))); + } + + @Test + @DisplayName("ActivateUser should report the signature as active once it is reinstated") + void activateUser_shouldReportSignatureActive() { + EmployeeSignature updated = signature("asha-signature.png", MediaType.IMAGE_PNG_VALUE); + updated.setDeleted(Boolean.FALSE); + when(employeeSignatureServiceImpl.updateUserSignatureStatus(anyString())).thenReturn(updated); + + String response = controller.ActivateUser("{\"userID\":3117,\"deleted\":false}", + new MockHttpServletRequest()); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("\"active\":true"), response); + } + + @Test + @DisplayName("ActivateUser should report the signature as inactive once it is deactivated") + void activateUser_shouldReportSignatureInactive() { + EmployeeSignature updated = signature("asha-signature.png", MediaType.IMAGE_PNG_VALUE); + updated.setDeleted(Boolean.TRUE); + when(employeeSignatureServiceImpl.updateUserSignatureStatus(anyString())).thenReturn(updated); + + assertTrue(controller.ActivateUser("{\"userID\":3117,\"deleted\":true}", new MockHttpServletRequest()) + .contains("\"active\":false")); + } + + @Test + @DisplayName("ActivateUser should treat a signature with no flag on record as inactive") + void activateUser_shouldTreatMissingFlagAsInactive() { + EmployeeSignature updated = signature("asha-signature.png", MediaType.IMAGE_PNG_VALUE); + when(employeeSignatureServiceImpl.updateUserSignatureStatus(anyString())).thenReturn(updated); + + assertTrue(controller.ActivateUser("{\"userID\":3117}", new MockHttpServletRequest()) + .contains("\"active\":false")); + } + + @Test + @DisplayName("ActivateUser should answer an error envelope when the change fails") + void activateUser_shouldAnswerErrorEnvelopeOnFailure() { + when(employeeSignatureServiceImpl.updateUserSignatureStatus(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.ActivateUser("{\"userID\":3117}", new MockHttpServletRequest()))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/facilitytype/FacilitytypeControllerTest.java b/src/test/java/com/iemr/admin/controller/facilitytype/FacilitytypeControllerTest.java new file mode 100644 index 0000000..0dce546 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/facilitytype/FacilitytypeControllerTest.java @@ -0,0 +1,314 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.facilitytype; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.facilitytype.M_facilitytype; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.data.store.M_FacilityLevel; +import com.iemr.admin.repository.store.MainStoreRepo; +import com.iemr.admin.service.facilitytype.M_facilitytypeInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The facility type endpoints define the levels of the health facility + * hierarchy, so a type still in use may not be retired out from under the + * facilities that carry it. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("FacilitytypeController Test Suite") +class FacilitytypeControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer TYPE_ID = 3; + + @Mock + private M_facilitytypeInter m_facilitytypeInter; + + @Mock + private MainStoreRepo mainStoreRepo; + + @InjectMocks + private FacilitytypeController controller; + + private static M_facilitytype type(Integer id, String name) { + M_facilitytype type = new M_facilitytype(); + type.setFacilityTypeID(id); + type.setFacilityTypeName(name); + return type; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("getFacility should answer the facility types of the provider") + void getFacility_shouldAnswerProviderTypes() { + when(m_facilitytypeInter.getAllFicilityData(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(type(TYPE_ID, "PHC")))); + + assertSuccessContaining(controller.getFacility("{\"providerServiceMapID\":4001}"), "PHC"); + } + + @Test + @DisplayName("getFacility should answer an error envelope when the lookup fails") + void getFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.getAllFicilityData(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacility("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("addFacility should answer the facility types the service stored") + void addFacility_shouldAnswerStoredTypes() { + when(m_facilitytypeInter.addAllFicilityData(anyList())) + .thenReturn(new ArrayList<>(List.of(type(TYPE_ID, "PHC")))); + + assertSuccessContaining(controller.addFacility("[{\"facilityTypeName\":\"PHC\"}]"), "PHC"); + } + + @Test + @DisplayName("addFacility should answer an error envelope when the store fails") + void addFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.addAllFicilityData(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.addFacility("[{\"facilityTypeName\":\"PHC\"}]")); + } + + @Test + @DisplayName("editFacility should copy only the fields the request actually sets") + void editFacility_shouldCopyOnlySuppliedFields() { + M_facilitytype stored = type(TYPE_ID, "old name"); + stored.setRuralUrban("Rural"); + stored.setFacilityTypeDesc("old description"); + when(m_facilitytypeInter.editAllFicilityData(TYPE_ID)).thenReturn(stored); + when(m_facilitytypeInter.updateFacilityData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editFacility( + "{\"facilityTypeID\":3,\"facilityTypeName\":\"PHC\",\"modifiedBy\":\"admin\"}"), "PHC"); + assertEquals("Rural", stored.getRuralUrban(), "an unset field keeps the value on record"); + assertEquals("old description", stored.getFacilityTypeDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editFacility should copy the rural-urban split and description when the request sets them") + void editFacility_shouldCopySuppliedSplitAndDescription() { + M_facilitytype stored = type(TYPE_ID, "old name"); + when(m_facilitytypeInter.editAllFicilityData(TYPE_ID)).thenReturn(stored); + when(m_facilitytypeInter.updateFacilityData(stored)).thenReturn(stored); + + controller.editFacility("{\"facilityTypeID\":3,\"ruralUrban\":\"Urban\"," + + "\"facilityTypeDesc\":\"Urban primary centre\",\"modifiedBy\":\"admin\"}"); + + assertEquals("Urban", stored.getRuralUrban()); + assertEquals("Urban primary centre", stored.getFacilityTypeDesc()); + } + + @Test + @DisplayName("editFacility should answer an error envelope for a facility type that does not exist") + void editFacility_shouldAnswerErrorEnvelopeForUnknownType() { + when(m_facilitytypeInter.editAllFicilityData(TYPE_ID)).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.editFacility("{\"facilityTypeID\":3}"))); + } + + @Test + @DisplayName("deleteFacility should refuse to retire a facility type facilities still carry") + void deleteFacility_shouldRefuseTypeInUse() { + when(m_facilitytypeInter.editAllFicilityData(TYPE_ID)).thenReturn(type(TYPE_ID, "PHC")); + when(mainStoreRepo.findByFacilityTypeIDAndDeletedFalse(TYPE_ID)) + .thenReturn(List.of(new M_Facility(), new M_Facility())); + + String response = controller.deleteFacility("{\"facilityTypeID\":3,\"deleted\":true}"); + + assertGenericFailure(response); + assertTrue(response.contains("in use by 2 active facilities"), response); + } + + @Test + @DisplayName("deleteFacility should retire a facility type nothing carries") + void deleteFacility_shouldRetireUnusedType() { + M_facilitytype stored = type(TYPE_ID, "PHC"); + when(m_facilitytypeInter.editAllFicilityData(TYPE_ID)).thenReturn(stored); + when(mainStoreRepo.findByFacilityTypeIDAndDeletedFalse(TYPE_ID)).thenReturn(new ArrayList<>()); + when(m_facilitytypeInter.updateFacilityData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteFacility("{\"facilityTypeID\":3,\"deleted\":true}"), "PHC"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteFacility should reinstate a facility type without checking what carries it") + void deleteFacility_shouldReinstateWithoutChecking() { + M_facilitytype stored = type(TYPE_ID, "PHC"); + stored.setDeleted(Boolean.TRUE); + when(m_facilitytypeInter.editAllFicilityData(TYPE_ID)).thenReturn(stored); + when(m_facilitytypeInter.updateFacilityData(stored)).thenReturn(stored); + + controller.deleteFacility("{\"facilityTypeID\":3,\"deleted\":false}"); + + verify(mainStoreRepo, never()).findByFacilityTypeIDAndDeletedFalse(anyInt()); + } + + @Test + @DisplayName("checkFacilityTypeCode should report whether the code is already taken") + void checkFacilityTypeCode_shouldReportWhetherCodeIsTaken() { + when(m_facilitytypeInter.checkFacilityTypeCode(any())).thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkFacilityTypeCode("{\"facilityTypeCode\":\"PHC-1\"}"), "true"); + } + + @Test + @DisplayName("checkFacilityTypeCode should answer an error envelope when the check fails") + void checkFacilityTypeCode_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.checkFacilityTypeCode(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkFacilityTypeCode("{\"facilityTypeCode\":\"PHC-1\"}")); + } + + @Test + @DisplayName("getFacilityTypesByRuralUrban should narrow the types to the split the caller names") + void getByRuralUrban_shouldNarrowToSplit() { + when(m_facilitytypeInter.getFacilityTypesByRuralUrban(PSM_ID, "Rural")) + .thenReturn(new ArrayList<>(List.of(type(TYPE_ID, "PHC")))); + + assertSuccessContaining(controller.getFacilityTypesByRuralUrban( + "{\"providerServiceMapID\":4001,\"ruralUrban\":\"Rural\"}"), "PHC"); + } + + @Test + @DisplayName("getFacilityTypesByRuralUrban should answer an error envelope when the lookup fails") + void getByRuralUrban_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.getFacilityTypesByRuralUrban(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacilityTypesByRuralUrban("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getFacilityLevels should answer the hierarchy levels on record") + void getFacilityLevels_shouldAnswerHierarchyLevels() { + M_FacilityLevel level = new M_FacilityLevel(); + when(m_facilitytypeInter.getFacilityLevels()).thenReturn(new ArrayList<>(List.of(level))); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(controller.getFacilityLevels())); + } + + @Test + @DisplayName("getFacilityLevels should answer an error envelope when the lookup fails") + void getFacilityLevels_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.getFacilityLevels()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacilityLevels()); + } + + @Test + @DisplayName("getFacilityTypesByBlock should answer the types used in the block") + void getByBlock_shouldAnswerBlockTypes() { + when(m_facilitytypeInter.getFacilityTypesByBlock(401)) + .thenReturn(new ArrayList<>(List.of(type(TYPE_ID, "PHC")))); + + assertSuccessContaining(controller.getFacilityTypesByBlock("{\"blockID\":401}"), "PHC"); + } + + @Test + @DisplayName("getFacilityTypesByBlock should answer an error envelope when the lookup fails") + void getByBlock_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.getFacilityTypesByBlock(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacilityTypesByBlock("{\"blockID\":401}")); + } + + @Test + @DisplayName("getFacilityTypesByState should answer the types used in the state") + void getByState_shouldAnswerStateTypes() { + when(m_facilitytypeInter.getFacilityTypesByState(29)) + .thenReturn(new ArrayList<>(List.of(type(TYPE_ID, "PHC")))); + + assertSuccessContaining(controller.getFacilityTypesByState("{\"stateID\":29}"), "PHC"); + } + + @Test + @DisplayName("getFacilityTypesByState should answer an error envelope when the lookup fails") + void getByState_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.getFacilityTypesByState(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacilityTypesByState("{\"stateID\":29}")); + } + + @Test + @DisplayName("checkFacilityTypeName should report whether the name is already used in the state") + void checkFacilityTypeName_shouldReportWhetherNameIsUsed() { + when(m_facilitytypeInter.checkFacilityTypeNameExists("PHC", 29)).thenReturn(true); + + assertSuccessContaining( + controller.checkFacilityTypeName("{\"facilityTypeName\":\"PHC\",\"stateID\":29}"), "true"); + } + + @Test + @DisplayName("checkFacilityTypeName should answer an error envelope when the check fails") + void checkFacilityTypeName_shouldAnswerErrorEnvelopeOnFailure() { + when(m_facilitytypeInter.checkFacilityTypeNameExists(anyString(), anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkFacilityTypeName("{\"facilityTypeName\":\"PHC\",\"stateID\":29}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/foetalmonitormaster/FoetalMonitorControllerTest.java b/src/test/java/com/iemr/admin/controller/foetalmonitormaster/FoetalMonitorControllerTest.java new file mode 100644 index 0000000..caf6063 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/foetalmonitormaster/FoetalMonitorControllerTest.java @@ -0,0 +1,355 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.foetalmonitormaster; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.foetalmonitormaster.FoetalMonitorDeviceID; +import com.iemr.admin.service.foetalmonitormaster.FoetalMonitorService; +import com.iemr.admin.utils.exception.IEMRException; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The fetosense endpoints keep the device catalogue and the pairing between a + * device and the van it travels in. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("FoetalMonitorController Test Suite") +class FoetalMonitorControllerTest { + + private static final Integer PSM_ID = 4001; + private static final String AUTH = "sess-0d5f3a7c"; + private static final String DEVICE_JSON = "{\"vfdID\":9001,\"deviceID\":\"FS-1\",\"vanID\":71," + + "\"providerServiceMapID\":4001,\"deactivated\":false}"; + + @Mock + private FoetalMonitorService foetalMonitorService; + + @InjectMocks + private FoetalMonitorController controller; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertInvalidRequest(String response) { + assertEquals(OutputResponse.USERID_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should answer the tests the service stored") + void createTestMaster_shouldAnswerStoredTests() throws Exception { + when(foetalMonitorService.createFoetalMonitorTestMaster(anyString())) + .thenReturn("[{\"testName\":\"Non stress test\"}]"); + + assertSuccessContaining(controller.createFoetalMonitorTestMaster("[{}]"), "Non stress test"); + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should stay at its default when the service stored nothing") + void createTestMaster_shouldStayAtDefaultWhenNothingStored() throws Exception { + when(foetalMonitorService.createFoetalMonitorTestMaster(anyString())).thenReturn(null); + + assertGenericFailure(controller.createFoetalMonitorTestMaster("[{}]")); + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should answer an error envelope when the store fails") + void createTestMaster_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(foetalMonitorService.createFoetalMonitorTestMaster(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createFoetalMonitorTestMaster("[{}]")); + } + + @Test + @DisplayName("fetchFoetalMonitorTestMaster should answer the tests of the provider") + void fetchTestMaster_shouldAnswerProviderTests() { + when(foetalMonitorService.getFoetalMonitorTestMaster(PSM_ID)) + .thenReturn("[{\"testName\":\"Non stress test\"}]"); + + assertSuccessContaining(controller.fetchFoetalMonitorTestMaster(PSM_ID), "Non stress test"); + } + + @Test + @DisplayName("fetchFoetalMonitorTestMaster should refuse a request that names no provider") + void fetchTestMaster_shouldRefuseRequestWithoutProvider() { + assertInvalidRequest(controller.fetchFoetalMonitorTestMaster(0)); + verify(foetalMonitorService, never()).getFoetalMonitorTestMaster(anyInt()); + } + + @Test + @DisplayName("fetchFoetalMonitorTestMaster should answer an error envelope when the lookup fails") + void fetchTestMaster_shouldAnswerErrorEnvelopeOnFailure() { + when(foetalMonitorService.getFoetalMonitorTestMaster(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.fetchFoetalMonitorTestMaster(PSM_ID)); + } + + @Test + @DisplayName("updateProcedureMaster should answer the test once the edit lands") + void updateTestMaster_shouldAnswerEditedTest() { + when(foetalMonitorService.updateFoetalMonitorTestMaster(anyString())) + .thenReturn("{\"testName\":\"Non stress test\"}"); + + assertSuccessContaining(controller.updateProcedureMaster("{}"), "Non stress test"); + } + + @Test + @DisplayName("updateProcedureMaster should report an edit the service could not apply") + void updateTestMaster_shouldReportUnappliedEdit() { + when(foetalMonitorService.updateFoetalMonitorTestMaster(anyString())).thenReturn(null); + + String response = controller.updateProcedureMaster("{}"); + + assertInvalidRequest(response); + assertTrue(response.contains("Failed to update procedure details"), response); + } + + @Test + @DisplayName("updateProcedureStatus should answer the test once its status has changed") + void updateTestStatus_shouldAnswerChangedTest() throws Exception { + when(foetalMonitorService.updateFoetalMonitorTestMasterStatus(11, true)) + .thenReturn("{\"testName\":\"Non stress test\"}"); + + assertSuccessContaining( + controller.updateProcedureStatus("{\"foetalMonitorTestID\":11,\"deleted\":true}"), + "Non stress test"); + } + + @Test + @DisplayName("updateProcedureStatus should report a status the service could not change") + void updateTestStatus_shouldReportUnchangedStatus() throws Exception { + when(foetalMonitorService.updateFoetalMonitorTestMasterStatus(anyInt(), anyBoolean())).thenReturn(null); + + assertInvalidRequest(controller.updateProcedureStatus("{\"foetalMonitorTestID\":11,\"deleted\":true}")); + } + + @Test + @DisplayName("updateProcedureStatus should refuse a request that names no test") + void updateTestStatus_shouldRefuseRequestWithoutTest() { + assertInvalidRequest(controller.updateProcedureStatus("{\"deleted\":true}")); + } + + @Test + @DisplayName("saveFoetalMonitorDeviceID should report the devices it stored") + void saveDeviceID_shouldReportStoredDevices() throws Exception { + when(foetalMonitorService.saveFoetalMonitorDeviceID(any())).thenReturn(1); + + assertSuccessContaining( + controller.saveFoetalMonitorDeviceID(new ArrayList<>(List.of(new FoetalMonitorDeviceID())), AUTH), + "Device ID saved successfully"); + } + + @Test + @DisplayName("saveFoetalMonitorDeviceID should report the reason the service refused the device") + void saveDeviceID_shouldReportRefusal() throws Exception { + when(foetalMonitorService.saveFoetalMonitorDeviceID(any())) + .thenThrow(new IEMRException("Error in saving foetal monitor device ID")); + + String response = controller.saveFoetalMonitorDeviceID(new ArrayList<>(), AUTH); + + assertGenericFailure(response); + assertTrue(response.contains("Error in saving foetal monitor device ID"), response); + } + + @Test + @DisplayName("saveVanIDandDeviceIDMapping should report a pairing the service accepted") + void saveMapping_shouldReportAcceptedPairing() throws Exception { + when(foetalMonitorService.vanIDAndDeviceIDMapping(any())).thenReturn(1); + + assertSuccessContaining(controller.saveVanIDandDeviceIDMapping(DEVICE_JSON, AUTH), + "Mapping Done successfully"); + } + + @Test + @DisplayName("saveVanIDandDeviceIDMapping should report the reason the service refused the pairing") + void saveMapping_shouldReportRefusal() throws Exception { + when(foetalMonitorService.vanIDAndDeviceIDMapping(any())) + .thenThrow(new IEMRException("Error in updating the VanID")); + + assertGenericFailure(controller.saveVanIDandDeviceIDMapping(DEVICE_JSON, AUTH)); + } + + @Test + @DisplayName("getFoetalMonitorDeviceID should answer the devices the service publishes") + void getDeviceID_shouldAnswerPublishedDevices() throws Exception { + when(foetalMonitorService.getFoetalMonitorDeviceID(any())) + .thenReturn("{\"fetosenseDeviceIDs\":[{\"deviceID\":\"FS-1\"}]}"); + + assertSuccessContaining(controller.getFoetalMonitorDeviceID(DEVICE_JSON), "FS-1"); + } + + @Test + @DisplayName("getFoetalMonitorDeviceID should report the reason the lookup failed") + void getDeviceID_shouldReportFailure() throws Exception { + when(foetalMonitorService.getFoetalMonitorDeviceID(any())) + .thenThrow(new IEMRException("Error in getting foetal monitor DeviceID")); + + assertGenericFailure(controller.getFoetalMonitorDeviceID(DEVICE_JSON)); + } + + @Test + @DisplayName("getVanIDAndDeviceID should answer the vans and devices still free to pair") + void getVanAndDevice_shouldAnswerFreePairs() throws Exception { + when(foetalMonitorService.getvanIDAndFoetalMonitorDeviceID(any())) + .thenReturn("{\"VanIDs\":[],\"deviceIDs\":[]}"); + + assertSuccessContaining(controller.getVanIDAndDeviceID(DEVICE_JSON), "VanIDs"); + } + + @Test + @DisplayName("getVanIDAndDeviceID should report the reason the lookup failed") + void getVanAndDevice_shouldReportFailure() throws Exception { + when(foetalMonitorService.getvanIDAndFoetalMonitorDeviceID(any())) + .thenThrow(new IEMRException("Error in getting vanID and foetalMonitorID")); + + assertGenericFailure(controller.getVanIDAndDeviceID(DEVICE_JSON)); + } + + @Test + @DisplayName("getMappedWorklist should answer the pairings on record") + void getWorklist_shouldAnswerPairings() throws Exception { + when(foetalMonitorService.getVanIDMappingWorklist(any())) + .thenReturn("[{\"deviceID\":\"FS-1\"}]"); + + assertSuccessContaining(controller.getMappedWorklist(DEVICE_JSON), "FS-1"); + } + + @Test + @DisplayName("getMappedWorklist should report the reason the lookup failed") + void getWorklist_shouldReportFailure() throws Exception { + when(foetalMonitorService.getVanIDMappingWorklist(any())) + .thenThrow(new IEMRException("Error in getting vanID mapping worklist")); + + assertGenericFailure(controller.getMappedWorklist(DEVICE_JSON)); + } + + @Test + @DisplayName("updateFoetalMonitorDeviceID should report a device the service saved") + void updateDeviceID_shouldReportSavedDevice() throws Exception { + when(foetalMonitorService.updateFoetalMonitorDeviceID(any())).thenReturn(1); + + assertSuccessContaining(controller.updateFoetalMonitorDeviceID(DEVICE_JSON, AUTH), + "DeviceID updated successfully"); + } + + @Test + @DisplayName("updateFoetalMonitorDeviceID should report the reason the service refused the edit") + void updateDeviceID_shouldReportRefusal() throws Exception { + when(foetalMonitorService.updateFoetalMonitorDeviceID(any())) + .thenThrow(new IEMRException("Error in updating the Device ID")); + + String response = controller.updateFoetalMonitorDeviceID(DEVICE_JSON, AUTH); + + assertGenericFailure(response); + assertTrue(response.contains("Unable to update deviceID"), response); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should report a device the service retired") + void deleteDeviceID_shouldReportRetiredDevice() throws Exception { + when(foetalMonitorService.deleteFoetalMonitorDeviceID(any())).thenReturn(1); + + assertSuccessContaining(controller.deleteFoetalMonitorDeviceID(DEVICE_JSON, AUTH), + "Device ID de-activated successfully"); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should report the reason the service refused the retirement") + void deleteDeviceID_shouldReportRefusal() throws Exception { + when(foetalMonitorService.deleteFoetalMonitorDeviceID(any())) + .thenThrow(new IEMRException("Error in de-activating the device ID")); + + assertGenericFailure(controller.deleteFoetalMonitorDeviceID(DEVICE_JSON, AUTH)); + } + + @Test + @DisplayName("updateMapping should report a pairing the service moved") + void updateMapping_shouldReportMovedPairing() throws Exception { + when(foetalMonitorService.updatingvanIDAndDeviceIDMapping(any())).thenReturn(1); + + assertSuccessContaining(controller.updateMapping(DEVICE_JSON, AUTH), "Mapping updated successfully"); + } + + @Test + @DisplayName("updateMapping should report the reason the service refused to move the pairing") + void updateMapping_shouldReportRefusal() throws Exception { + when(foetalMonitorService.updatingvanIDAndDeviceIDMapping(any())) + .thenThrow(new IEMRException("Error in updating van details")); + + assertGenericFailure(controller.updateMapping(DEVICE_JSON, AUTH)); + } + + @Test + @DisplayName("deleteVanIDAndFoetalMonitorDeviceID should report a pairing the service released") + void deleteMapping_shouldReportReleasedPairing() throws Exception { + when(foetalMonitorService.deleteVanIDAndDeviceIDMapping(any())).thenReturn(1); + + assertSuccessContaining(controller.deleteVanIDAndFoetalMonitorDeviceID(DEVICE_JSON, AUTH), + "Mapped deactivated successfully"); + } + + @Test + @DisplayName("deleteVanIDAndFoetalMonitorDeviceID should report the reason the service refused the release") + void deleteMapping_shouldReportRefusal() throws Exception { + when(foetalMonitorService.deleteVanIDAndDeviceIDMapping(any())) + .thenThrow(new IEMRException("The Van is already mapped with a device")); + + String response = controller.deleteVanIDAndFoetalMonitorDeviceID(DEVICE_JSON, AUTH); + + assertGenericFailure(response); + assertTrue(response.contains("already mapped with a device"), response); + } +} diff --git a/src/test/java/com/iemr/admin/controller/health/HealthControllerTest.java b/src/test/java/com/iemr/admin/controller/health/HealthControllerTest.java new file mode 100644 index 0000000..a8fcd38 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/health/HealthControllerTest.java @@ -0,0 +1,102 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.health; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.iemr.admin.service.health.HealthService; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.when; + +/** + * Monitoring reads the HTTP status of this endpoint, so a degraded deployment + * has to stay a 200 while an unreachable one becomes a 503. + */ +@ExtendWith(MockitoExtension.class) +@DisplayName("HealthController Test Suite") +class HealthControllerTest { + + @Mock + private HealthService healthService; + + private static Map health(String status) { + Map health = new LinkedHashMap<>(); + health.put("status", status); + health.put("checkedAt", "2026-02-17T09:30:00Z"); + return health; + } + + @Test + @DisplayName("checkHealth should answer 200 for a deployment that is up") + void checkHealth_shouldAnswerOkWhenUp() { + when(healthService.checkHealth()).thenReturn(health("UP")); + + ResponseEntity> response = new HealthController(healthService).checkHealth(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("UP", response.getBody().get("status")); + } + + @Test + @DisplayName("checkHealth should answer 200 for a deployment that is degraded but serving") + void checkHealth_shouldAnswerOkWhenDegraded() { + when(healthService.checkHealth()).thenReturn(health("DEGRADED")); + + ResponseEntity> response = new HealthController(healthService).checkHealth(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("DEGRADED", response.getBody().get("status")); + } + + @Test + @DisplayName("checkHealth should answer 503 for a deployment that is down") + void checkHealth_shouldAnswerUnavailableWhenDown() { + when(healthService.checkHealth()).thenReturn(health("DOWN")); + + ResponseEntity> response = new HealthController(healthService).checkHealth(); + + assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode()); + } + + @Test + @DisplayName("checkHealth should answer 503 rather than propagate a failure of the check itself") + void checkHealth_shouldAnswerUnavailableWhenCheckFails() { + when(healthService.checkHealth()).thenThrow(new IllegalStateException("diagnostics unavailable")); + + ResponseEntity> response = new HealthController(healthService).checkHealth(); + + assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode()); + assertEquals("DOWN", response.getBody().get("status")); + assertNotNull(response.getBody().get("timestamp")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/item/ItemControllerTest.java b/src/test/java/com/iemr/admin/controller/item/ItemControllerTest.java new file mode 100644 index 0000000..e4fb8b1 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/item/ItemControllerTest.java @@ -0,0 +1,545 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.item; + +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.items.CodeChecker; +import com.iemr.admin.data.items.ItemMaster; +import com.iemr.admin.data.items.M_ItemCategory; +import com.iemr.admin.data.items.M_ItemForm; +import com.iemr.admin.data.items.M_Route; +import com.iemr.admin.service.item.ItemService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The item endpoints keep the inventory catalogue: the drugs and consumables a + * facility can issue, and the categories, forms and routes that classify them. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ItemController Test Suite") +class ItemControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer ITEM_ID = 101; + + @Mock + private ItemService itemService; + + @InjectMocks + private ItemController controller; + + private static ItemMaster item(Integer id, String name) { + ItemMaster item = new ItemMaster(); + item.setItemID(id); + item.setItemName(name); + return item; + } + + private static M_ItemCategory category(Integer id, String name) { + M_ItemCategory category = new M_ItemCategory(); + category.setItemCategoryID(id); + category.setItemCategoryName(name); + return category; + } + + private static M_ItemForm form(Integer id, String name) { + M_ItemForm form = new M_ItemForm(); + form.setItemFormID(id); + form.setItemForm(name); + return form; + } + + private static M_Route route(Integer id, String name) { + M_Route route = new M_Route(); + route.setRouteID(id); + route.setRouteName(name); + return route; + } + + private static CodeChecker codeChecker(String name, String code) { + CodeChecker checker = new CodeChecker(); + checker.setName(name); + checker.setCode(code); + checker.setProviderServiceMapID(PSM_ID); + return checker; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("getItemForm should answer the item forms configured for the provider") + void getItemForm_shouldAnswerConfiguredForms() { + when(itemService.getItemFormProviderServiceMapID(PSM_ID)).thenReturn(List.of(form(11, "Tablet"))); + + assertSuccessContaining(controller.getItemForm(PSM_ID), "Tablet"); + } + + @Test + @DisplayName("getItemForm should answer an error envelope when the lookup fails") + void getItemForm_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.getItemFormProviderServiceMapID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItemForm(PSM_ID)); + } + + @Test + @DisplayName("getItemRoute should answer the routes configured for the provider") + void getItemRoute_shouldAnswerConfiguredRoutes() { + when(itemService.getItemRouteProviderServiceMapID(PSM_ID)).thenReturn(List.of(route(21, "Oral"))); + + assertSuccessContaining(controller.getItemRoute(PSM_ID), "Oral"); + } + + @Test + @DisplayName("getItemRoute should answer an error envelope when the lookup fails") + void getItemRoute_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.getItemRouteProviderServiceMapID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItemRoute(PSM_ID)); + } + + @Test + @DisplayName("getItemCategory should ask for every category when the caller sends zero") + void getItemCategory_shouldAskForEveryCategoryOnZero() { + when(itemService.getItemCategory(true, PSM_ID)).thenReturn(List.of(category(31, "Drugs"))); + + assertSuccessContaining(controller.getItemCategory(PSM_ID, 0), "Drugs"); + verify(itemService).getItemCategory(true, PSM_ID); + } + + @Test + @DisplayName("getItemCategory should ask for the live categories only for any other flag") + void getItemCategory_shouldAskForLiveCategoriesOtherwise() { + when(itemService.getItemCategory(false, PSM_ID)).thenReturn(List.of(category(31, "Drugs"))); + + assertSuccessContaining(controller.getItemCategory(PSM_ID, 1), "Drugs"); + verify(itemService).getItemCategory(false, PSM_ID); + } + + @Test + @DisplayName("getItemCategory should answer an error envelope when the lookup fails") + void getItemCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.getItemCategory(anyBoolean(), anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItemCategory(PSM_ID, 0)); + } + + @Test + @DisplayName("createItemMaster should answer the items the service stored") + void createItemMaster_shouldAnswerStoredItems() { + when(itemService.addAllItemMaster(anyList())).thenReturn(List.of(item(ITEM_ID, "Paracetamol"))); + + assertSuccessContaining(controller.createItemMaster(new ItemMaster[] { item(null, "Paracetamol") }), + "Paracetamol"); + } + + @Test + @DisplayName("createItemMaster should answer an error envelope when the store fails") + void createItemMaster_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.addAllItemMaster(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createItemMaster(new ItemMaster[] { item(null, "Paracetamol") })); + } + + @Test + @DisplayName("getItemMaster should answer the catalogue of the provider") + void getItemMaster_shouldAnswerProviderCatalogue() { + when(itemService.getItemMaster(PSM_ID)).thenReturn(List.of(item(ITEM_ID, "Paracetamol"))); + + assertSuccessContaining(controller.getItemMaster(PSM_ID), "Paracetamol"); + } + + @Test + @DisplayName("getItemMaster should answer an error envelope when the lookup fails") + void getItemMaster_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.getItemMaster(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItemMaster(PSM_ID)); + } + + @Test + @DisplayName("blockItemMaster should answer how many items the service blocked") + void blockItemMaster_shouldAnswerBlockedCount() { + when(itemService.blockItemMaster(ITEM_ID, true)).thenReturn(1); + + assertSuccessContaining(controller.blockItemMaster(ITEM_ID, true), "1"); + } + + @Test + @DisplayName("blockItemMaster should answer an error envelope when the block fails") + void blockItemMaster_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.blockItemMaster(anyInt(), anyBoolean())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.blockItemMaster(ITEM_ID, true)); + } + + @Test + @DisplayName("discontinueItemMaster should answer how many items the service discontinued") + void discontinueItemMaster_shouldAnswerDiscontinuedCount() { + when(itemService.discontinueItemMaster(ITEM_ID, true)).thenReturn(1); + + assertSuccessContaining(controller.discontinueItemMaster(ITEM_ID, true), "1"); + } + + @Test + @DisplayName("discontinueItemMaster should answer an error envelope when the change fails") + void discontinueItemMaster_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.discontinueItemMaster(anyInt(), anyBoolean())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.discontinueItemMaster(ITEM_ID, true)); + } + + @Test + @DisplayName("editItemMaster should copy the edited fields onto the stored item") + void editItemMaster_shouldCopyEditedFields() { + ItemMaster stored = item(ITEM_ID, "Paracetamol"); + ItemMaster request = item(ITEM_ID, "Paracetamol"); + request.setIsMedical(Boolean.TRUE); + request.setItemCategoryID(31); + request.setPharmacologyCategoryID(41); + request.setManufacturerID(51); + request.setIsScheduledDrug(Boolean.FALSE); + request.setItemDesc("Antipyretic"); + request.setSctCode("SCT-1"); + request.setSctTerm("Paracetamol 500mg"); + request.setModifiedBy("admin"); + when(itemService.getItemMasterByID(ITEM_ID)).thenReturn(stored); + when(itemService.createItemMaster(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editItemMaster(request), "Paracetamol"); + assertEquals("Antipyretic", stored.getItemDesc()); + assertEquals("SCT-1", stored.getSctCode()); + assertEquals(31, stored.getItemCategoryID()); + } + + @Test + @DisplayName("editItemMaster should answer an error envelope for an item that does not exist") + void editItemMaster_shouldAnswerErrorEnvelopeForUnknownItem() { + when(itemService.getItemMasterByID(ITEM_ID)).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.editItemMaster(item(ITEM_ID, "x")))); + } + + @Test + @DisplayName("configItemIssue should answer how many categories the service reconfigured") + void configItemIssue_shouldAnswerReconfiguredCount() { + when(itemService.updateItemIssueConfig(anyList())).thenReturn(2); + + assertSuccessContaining(controller.configItemIssue(new M_ItemCategory[] { category(31, "Drugs") }), "2"); + } + + @Test + @DisplayName("configItemIssue should answer an error envelope when the change fails") + void configItemIssue_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.updateItemIssueConfig(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.configItemIssue(new M_ItemCategory[] { category(31, "Drugs") })); + } + + @Test + @DisplayName("configexpiryalert should answer how many categories had their alert window changed") + void configexpiryalert_shouldAnswerChangedCount() { + when(itemService.updateExpiryAlert(anyList())).thenReturn(1); + + assertSuccessContaining(controller.configexpiryalert(new M_ItemCategory[] { category(31, "Drugs") }), "1"); + } + + @Test + @DisplayName("configexpiryalert should answer an error envelope when the change fails") + void configexpiryalert_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.updateExpiryAlert(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.configexpiryalert(new M_ItemCategory[] { category(31, "Drugs") })); + } + + @Test + @DisplayName("getItem should answer the items in the category") + void getItem_shouldAnswerItemsInCategory() { + when(itemService.getItemMasters(PSM_ID, 31)).thenReturn(List.of(item(ITEM_ID, "Paracetamol"))); + + assertSuccessContaining( + controller.getItem("{\"providerServiceMapID\":4001,\"itemCategoryID\":31}"), "Paracetamol"); + } + + @Test + @DisplayName("getItem should answer an error envelope when the lookup fails") + void getItem_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.getItemMasters(any(), any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItem("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("createItemCategories should report success only when the service stored something") + void createItemCategories_shouldReportOutcome() { + when(itemService.createItemCategories(anyList())).thenReturn(1); + assertSuccessContaining(controller.createItemCategories(new M_ItemCategory[] { category(null, "Drugs") }), + "Item Categories saved successfully"); + + when(itemService.createItemCategories(anyList())).thenReturn(0); + assertSuccessContaining(controller.createItemCategories(new M_ItemCategory[] { category(null, "Drugs") }), + "Failed to store Item Categories"); + } + + @Test + @DisplayName("createItemCategories should answer an error envelope when the store fails") + void createItemCategories_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.createItemCategories(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createItemCategories(new M_ItemCategory[] { category(null, "Drugs") })); + } + + @Test + @DisplayName("editItemCategory should report success only when the service changed something") + void editItemCategory_shouldReportOutcome() { + when(itemService.editItemCategory(any())).thenReturn(1); + assertSuccessContaining(controller.editItemCategory(category(31, "Drugs")), + "Item Category updated successfully"); + + when(itemService.editItemCategory(any())).thenReturn(0); + assertSuccessContaining(controller.editItemCategory(category(31, "Drugs")), + "Failed to update Item Category"); + } + + @Test + @DisplayName("editItemCategory should answer an error envelope when the change fails") + void editItemCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.editItemCategory(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.editItemCategory(category(31, "Drugs"))); + } + + @Test + @DisplayName("blockItemCategory should report success only when the service blocked something") + void blockItemCategory_shouldReportOutcome() { + when(itemService.blockItemCategory(any())).thenReturn(1); + assertSuccessContaining(controller.blockItemCategory(category(31, "Drugs")), + "Item Category blocked successfully"); + + when(itemService.blockItemCategory(any())).thenReturn(0); + assertSuccessContaining(controller.blockItemCategory(category(31, "Drugs")), + "Failed to block Item Category"); + } + + @Test + @DisplayName("blockItemCategory should answer an error envelope when the block fails") + void blockItemCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.blockItemCategory(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.blockItemCategory(category(31, "Drugs"))); + } + + @Test + @DisplayName("createItemForms should report success only when the service stored something") + void createItemForms_shouldReportOutcome() { + when(itemService.createItemForms(anyList())).thenReturn(1); + assertSuccessContaining(controller.createItemForms(new M_ItemForm[] { form(null, "Tablet") }), + "Item Forms saved successfully"); + + when(itemService.createItemForms(anyList())).thenReturn(0); + assertSuccessContaining(controller.createItemForms(new M_ItemForm[] { form(null, "Tablet") }), + "Failed to store Item Forms"); + } + + @Test + @DisplayName("createItemForms should answer an error envelope when the store fails") + void createItemForms_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.createItemForms(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createItemForms(new M_ItemForm[] { form(null, "Tablet") })); + } + + @Test + @DisplayName("editItemForm should report success only when the service changed something") + void editItemForm_shouldReportOutcome() { + when(itemService.editItemForm(any())).thenReturn(1); + assertSuccessContaining(controller.editItemForm(form(11, "Tablet")), "Item Form updated successfully"); + + when(itemService.editItemForm(any())).thenReturn(0); + assertSuccessContaining(controller.editItemForm(form(11, "Tablet")), "Failed to update Item Form"); + } + + @Test + @DisplayName("editItemForm should answer an error envelope when the change fails") + void editItemForm_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.editItemForm(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.editItemForm(form(11, "Tablet"))); + } + + @Test + @DisplayName("blockItemForm should report success only when the service blocked something") + void blockItemForm_shouldReportOutcome() { + when(itemService.blockItemForm(any())).thenReturn(1); + assertSuccessContaining(controller.blockItemForm(form(11, "Tablet")), "Item Form blocked successfully"); + + when(itemService.blockItemForm(any())).thenReturn(0); + assertSuccessContaining(controller.blockItemForm(form(11, "Tablet")), "Failed to block Item Form"); + } + + @Test + @DisplayName("blockItemForm should answer an error envelope when the block fails") + void blockItemForm_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.blockItemForm(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.blockItemForm(form(11, "Tablet"))); + } + + @Test + @DisplayName("createRoutes should report success only when the service stored something") + void createRoutes_shouldReportOutcome() { + when(itemService.createRoutes(anyList())).thenReturn(1); + assertSuccessContaining(controller.createRoutes(new M_Route[] { route(null, "Oral") }), + "Routes saved successfully"); + + when(itemService.createRoutes(anyList())).thenReturn(0); + assertSuccessContaining(controller.createRoutes(new M_Route[] { route(null, "Oral") }), + "Failed to store Routes"); + } + + @Test + @DisplayName("createRoutes should answer an error envelope when the store fails") + void createRoutes_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.createRoutes(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createRoutes(new M_Route[] { route(null, "Oral") })); + } + + @Test + @DisplayName("editRoute should report success only when the service changed something") + void editRoute_shouldReportOutcome() { + when(itemService.editRoute(any())).thenReturn(1); + assertSuccessContaining(controller.editRoute(route(21, "Oral")), "Route data updated successfully"); + + when(itemService.editRoute(any())).thenReturn(0); + assertSuccessContaining(controller.editRoute(route(21, "Oral")), "Failed to update Route data"); + } + + @Test + @DisplayName("editRoute should answer an error envelope when the change fails") + void editRoute_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.editRoute(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.editRoute(route(21, "Oral"))); + } + + @Test + @DisplayName("blockRoute should report success only when the service blocked something") + void blockRoute_shouldReportOutcome() { + when(itemService.blockRoute(any())).thenReturn(1); + assertSuccessContaining(controller.blockRoute(route(21, "Oral")), "Route blocked successfully"); + + when(itemService.blockRoute(any())).thenReturn(0); + assertSuccessContaining(controller.blockRoute(route(21, "Oral")), "Failed to block Route"); + } + + @Test + @DisplayName("blockRoute should answer an error envelope when the block fails") + void blockRoute_shouldAnswerErrorEnvelopeOnFailure() { + when(itemService.blockRoute(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.blockRoute(route(21, "Oral"))); + } + + @Test + @DisplayName("checkCode should route the check to the master the caller names") + void checkCode_shouldRouteToNamedMaster() { + when(itemService.checkCodeCategory(any())).thenReturn(Boolean.TRUE); + when(itemService.checkCodeForm(any())).thenReturn(Boolean.FALSE); + when(itemService.checkCodeItem(any())).thenReturn(Boolean.TRUE); + when(itemService.checkCodeRoute(any())).thenReturn(Boolean.FALSE); + + assertSuccessContaining(controller.blockRoute(codeChecker("itemCategory", "CAT-1")), "true"); + assertSuccessContaining(controller.blockRoute(codeChecker("itemForm", "FRM-1")), "false"); + assertSuccessContaining(controller.blockRoute(codeChecker("itemMaster", "ITM-1")), "true"); + assertSuccessContaining(controller.blockRoute(codeChecker("route", "RTE-1")), "false"); + } + + @Test + @DisplayName("checkCode should refuse a master it does not know how to check") + void checkCode_shouldRefuseUnknownMaster() { + String response = controller.blockRoute(codeChecker("something-else", "X-1")); + + assertGenericFailure(response); + assertTrue(response.contains("Failed to check code for something-else"), response); + verify(itemService, never()).checkCodeCategory(any()); + } + + @Test + @DisplayName("checkCode should refuse a request that leaves out what to check") + void checkCode_shouldRefuseIncompleteRequest() { + CodeChecker incomplete = new CodeChecker(); + incomplete.setName("itemCategory"); + + String response = controller.blockRoute(incomplete); + + assertGenericFailure(response); + assertTrue(response.contains("Name, Code and ProviderServiceMapID is mandatory"), response); + } + + @Test + @DisplayName("checkCode should refuse a request that names no provider mapping") + void checkCode_shouldRefuseRequestWithoutProviderMapping() { + CodeChecker incomplete = codeChecker("itemCategory", "CAT-1"); + incomplete.setProviderServiceMapID(0); + + assertGenericFailure(controller.blockRoute(incomplete)); + } +} diff --git a/src/test/java/com/iemr/admin/controller/itemfacilitymapping/MItemFacilityMappingControllerTest.java b/src/test/java/com/iemr/admin/controller/itemfacilitymapping/MItemFacilityMappingControllerTest.java new file mode 100644 index 0000000..85bbc0a --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/itemfacilitymapping/MItemFacilityMappingControllerTest.java @@ -0,0 +1,259 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.itemfacilitymapping; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.items.ItemInStore; +import com.iemr.admin.data.itemfacilitymapping.M_itemfacilitymapping; +import com.iemr.admin.data.itemfacilitymapping.V_fetchItemFacilityMap; +import com.iemr.admin.service.itemfacilitymapping.M_itemfacilitymappingInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The item-facility endpoints decide which items each store may issue, so an + * unmapped item is one the store cannot dispense. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("MItemFacilityMappingController Test Suite") +class MItemFacilityMappingControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer FACILITY_ID = 501; + + @Mock + private M_itemfacilitymappingInter M_itemfacilitymappingInter; + + @InjectMocks + private MItemFacilityMappingController controller; + + private static M_itemfacilitymapping mapping(Integer id, Integer itemId) { + M_itemfacilitymapping mapping = new M_itemfacilitymapping(); + mapping.setItemStoreMapID(id); + mapping.setItemID(itemId); + mapping.setFacilityID(FACILITY_ID); + return mapping; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + @Test + @DisplayName("mapItemtoStrore should create one mapping per item the request lists") + void mapItemtoStrore_shouldCreateOneMappingPerItem() { + when(M_itemfacilitymappingInter.mapItemtoStore(anyList())) + .thenReturn(new ArrayList<>(List.of(mapping(9001, 101)))); + + String response = controller.mapItemtoStrore("[{\"facilityID\":501,\"mappingType\":\"Store\"," + + "\"providerServiceMapID\":4001,\"status\":\"Active\",\"createdBy\":\"admin\"," + + "\"itemID1\":[101,102]}]"); + + assertSuccessContaining(response, "9001"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(M_itemfacilitymappingInter).mapItemtoStore(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals(FACILITY_ID, captor.getValue().get(0).getFacilityID()); + } + + @Test + @DisplayName("mapItemtoStrore should answer an error envelope when no items are listed") + void mapItemtoStrore_shouldAnswerErrorEnvelopeWithoutItems() { + assertCodeException(controller.mapItemtoStrore("[{\"facilityID\":501}]")); + } + + @Test + @DisplayName("editItemtoStrore should copy the edits onto the stored mapping") + void editItemtoStrore_shouldCopyEdits() { + M_itemfacilitymapping stored = mapping(9001, 101); + when(M_itemfacilitymappingInter.editdata(9001)).thenReturn(stored); + when(M_itemfacilitymappingInter.saveEditedItem(stored)).thenReturn(stored); + + String response = controller.editItemtoStrore("{\"itemFacilityMapID\":9001,\"facilityID\":502," + + "\"itemID\":102,\"mappingType\":\"Sub Store\",\"providerServiceMapID\":4001," + + "\"status\":\"Inactive\"}"); + + assertSuccessContaining(response, "9001"); + assertEquals(502, stored.getFacilityID()); + assertEquals("Sub Store", stored.getMappingType()); + assertEquals("Inactive", stored.getStatus()); + } + + @Test + @DisplayName("editItemtoStrore should answer an error envelope for a mapping that does not exist") + void editItemtoStrore_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(M_itemfacilitymappingInter.editdata(9001)).thenReturn(null); + + assertCodeException(controller.editItemtoStrore("{\"itemFacilityMapID\":9001}")); + } + + @Test + @DisplayName("deleteItemtoStrore should mark the mapping deleted") + void deleteItemtoStrore_shouldMarkMappingDeleted() { + M_itemfacilitymapping stored = mapping(9001, 101); + when(M_itemfacilitymappingInter.editdata(9001)).thenReturn(stored); + when(M_itemfacilitymappingInter.saveEditedItem(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.deleteItemtoStrore("{\"itemFacilityMapID\":9001,\"deleted\":true}"), "9001"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteItemtoStrore should answer an error envelope for a mapping that does not exist") + void deleteItemtoStrore_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(M_itemfacilitymappingInter.editdata(9001)).thenReturn(null); + + assertCodeException(controller.deleteItemtoStrore("{\"itemFacilityMapID\":9001,\"deleted\":true}")); + } + + @Test + @DisplayName("getSubStroreitem should answer the items mapped to the sub store") + void getSubStroreitem_shouldAnswerMappedItems() { + when(M_itemfacilitymappingInter.getsubitemforsubStote(PSM_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(mapping(9001, 101)))); + + assertSuccessContaining( + controller.getSubStroreitem("{\"providerServiceMapID\":4001,\"facilityID\":501}"), "9001"); + } + + @Test + @DisplayName("getSubStroreitem should answer an error envelope when the lookup fails") + void getSubStroreitem_shouldAnswerErrorEnvelopeOnFailure() { + when(M_itemfacilitymappingInter.getsubitemforsubStote(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getSubStroreitem("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getAllFacilityMappedData should answer the mappings of the provider") + void getAllFacilityMappedData_shouldAnswerProviderMappings() { + V_fetchItemFacilityMap view = new V_fetchItemFacilityMap(); + view.setItemFacilityMapID(9001); + when(M_itemfacilitymappingInter.getAllFacilityMappedData(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(view))); + + assertSuccessContaining( + controller.getAllFacilityMappedData("{\"providerServiceMapID\":4001}"), "9001"); + } + + @Test + @DisplayName("getAllFacilityMappedData should answer an error envelope when the lookup fails") + void getAllFacilityMappedData_shouldAnswerErrorEnvelopeOnFailure() { + when(M_itemfacilitymappingInter.getAllFacilityMappedData(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllFacilityMappedData("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getItemMappingsByFacility should answer the mappings of the facility") + void getItemMappingsByFacility_shouldAnswerFacilityMappings() { + V_fetchItemFacilityMap view = new V_fetchItemFacilityMap(); + view.setItemFacilityMapID(9001); + when(M_itemfacilitymappingInter.getItemMappingsByFacilityID(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(view))); + + assertSuccessContaining(controller.getItemMappingsByFacility("{\"facilityID\":501}"), "9001"); + } + + @Test + @DisplayName("getItemMappingsByFacility should answer an error envelope when the lookup fails") + void getItemMappingsByFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(M_itemfacilitymappingInter.getItemMappingsByFacilityID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItemMappingsByFacility("{\"facilityID\":501}")); + } + + @Test + @DisplayName("getItemFromStoreID should answer the items the store holds") + void getItemFromStoreID_shouldAnswerStoreItems() { + when(M_itemfacilitymappingInter.getItemMastersFromStoreID(FACILITY_ID)) + .thenReturn(List.of(new ItemInStore(FACILITY_ID, 101, "Paracetamol", 25L))); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(controller.getItemFromStoreID(FACILITY_ID))); + } + + @Test + @DisplayName("getItemFromStoreID should answer an error envelope when the lookup fails") + void getItemFromStoreID_shouldAnswerErrorEnvelopeOnFailure() { + when(M_itemfacilitymappingInter.getItemMastersFromStoreID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getItemFromStoreID(FACILITY_ID)); + } + + @Test + @DisplayName("deleteItemStoreMapping should answer how many mappings the service released") + void deleteItemStoreMapping_shouldAnswerReleasedCount() { + M_itemfacilitymapping request = mapping(9001, 101); + when(M_itemfacilitymappingInter.deleteItemStoreMapping(request)).thenReturn(1); + + assertSuccessContaining(controller.deleteItemStoreMapping(request), "1"); + } + + @Test + @DisplayName("deleteItemStoreMapping should answer an error envelope when the release fails") + void deleteItemStoreMapping_shouldAnswerErrorEnvelopeOnFailure() { + when(M_itemfacilitymappingInter.deleteItemStoreMapping(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.deleteItemStoreMapping(mapping(9001, 101))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/labmodule/LabModuleControllerTest.java b/src/test/java/com/iemr/admin/controller/labmodule/LabModuleControllerTest.java new file mode 100644 index 0000000..0db8a08 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/labmodule/LabModuleControllerTest.java @@ -0,0 +1,356 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.labmodule; + +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.sevice.labmodule.MastersCreationServiceImpl; +import com.iemr.admin.sevice.labmodule.MastersFetchingServiceImpl; +import com.iemr.admin.sevice.labmodule.MastersMappingServiceImpl; +import com.iemr.admin.sevice.labmodule.MastersStatusUpdateImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The lab module endpoints let a provider admin define the diagnostic tests and + * their result components, and refuse a request that does not name what to act + * on. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("LabModuleController Test Suite") +class LabModuleControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PROCEDURE_ID = 71; + private static final Integer COMPONENT_ID = 81; + private static final String PROCEDURE_JSON = "{\"procedureID\":71,\"procedureName\":\"Haemoglobin\"}"; + + @Mock + private MastersCreationServiceImpl mastersCreationServiceImpl; + + @Mock + private MastersMappingServiceImpl mastersMappingServiceImpl; + + @Mock + private MastersFetchingServiceImpl mastersFetchingServiceImpl; + + @Mock + private MastersStatusUpdateImpl mastersStatusUpdateImpl; + + private LabModuleController controller; + + @BeforeEach + void setUp() { + controller = new LabModuleController(); + controller.setMastersCreationServiceImpl(mastersCreationServiceImpl); + controller.setMastersMappingServiceImpl(mastersMappingServiceImpl); + controller.setMastersFetchingServiceImpl(mastersFetchingServiceImpl); + controller.setMastersStatusUpdateImpl(mastersStatusUpdateImpl); + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertInvalidRequest(String response) { + assertEquals(OutputResponse.USERID_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("createProcedureMaster should answer the procedure the service stored") + void createProcedureMaster_shouldAnswerStoredProcedure() throws Exception { + when(mastersCreationServiceImpl.createProcedureMaster(anyString())).thenReturn(PROCEDURE_JSON); + + assertSuccessContaining(controller.createProcedureMaster(PROCEDURE_JSON), "Haemoglobin"); + } + + @Test + @DisplayName("createProcedureMaster should stay at its default when the service stored nothing") + void createProcedureMaster_shouldStayAtDefaultWhenNothingStored() throws Exception { + when(mastersCreationServiceImpl.createProcedureMaster(anyString())).thenReturn(null); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.createProcedureMaster("{}"))); + } + + @Test + @DisplayName("createProcedureMaster should answer an error envelope when the store fails") + void createProcedureMaster_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(mastersCreationServiceImpl.createProcedureMaster(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.createProcedureMaster("{}"))); + } + + @Test + @DisplayName("createComponentMaster should answer the component the service stored") + void createComponentMaster_shouldAnswerStoredComponent() throws Exception { + when(mastersCreationServiceImpl.createComponentMaster(anyString())) + .thenReturn("{\"testComponentName\":\"Haemoglobin count\"}"); + + assertSuccessContaining(controller.createComponentMaster("{}"), "Haemoglobin count"); + } + + @Test + @DisplayName("createComponentMaster should answer an error envelope when the store fails") + void createComponentMaster_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(mastersCreationServiceImpl.createComponentMaster(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.createComponentMaster("{}"))); + } + + @Test + @DisplayName("createProcedureComponentMapping should answer the mapping the service stored") + void createMapping_shouldAnswerStoredMapping() throws Exception { + when(mastersMappingServiceImpl.createProcedureComponentMapping(anyString())) + .thenReturn("[{\"procedureName\":\"Haemoglobin\"}]"); + + assertSuccessContaining(controller.createProcedureComponentMapping("{}"), "Haemoglobin"); + } + + @Test + @DisplayName("createProcedureComponentMapping should refuse a request that maps no components") + void createMapping_shouldRefuseEmptyRequest() throws Exception { + when(mastersMappingServiceImpl.createProcedureComponentMapping(anyString())).thenReturn("1"); + + String response = controller.createProcedureComponentMapping("{}"); + + assertInvalidRequest(response); + assertTrue(response.contains("Invalid request."), response); + } + + @Test + @DisplayName("createProcedureComponentMapping should report a mapping the service could not store") + void createMapping_shouldReportUnstoredMapping() throws Exception { + when(mastersMappingServiceImpl.createProcedureComponentMapping(anyString())).thenReturn(null); + + String response = controller.createProcedureComponentMapping("{}"); + + assertInvalidRequest(response); + assertTrue(response.contains("Error while saving the data"), response); + } + + @Test + @DisplayName("createProcedureComponentMapping should answer an error envelope when the store fails") + void createMapping_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(mastersMappingServiceImpl.createProcedureComponentMapping(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.createProcedureComponentMapping("{}"))); + } + + @Test + @DisplayName("the fetch endpoints should each answer what their own service publishes") + void fetchEndpoints_shouldAnswerTheirOwnService() throws Exception { + when(mastersFetchingServiceImpl.getProcedureMaster(PSM_ID)).thenReturn("[{\"a\":1}]"); + when(mastersFetchingServiceImpl.getComponentMaster(PSM_ID)).thenReturn("[{\"b\":2}]"); + when(mastersFetchingServiceImpl.getProcedureMasterDelFalse(PSM_ID)).thenReturn("[{\"c\":3}]"); + when(mastersFetchingServiceImpl.getComponentMasterDelFalse(PSM_ID)).thenReturn("[{\"d\":4}]"); + when(mastersFetchingServiceImpl.getProcCompMappingDelFalse(PSM_ID)).thenReturn("[{\"e\":5}]"); + when(mastersFetchingServiceImpl.getProcCompMappingForProcedureID(PROCEDURE_ID)).thenReturn("[{\"f\":6}]"); + + assertSuccessContaining(controller.fetchProcedureMaster(PSM_ID), "\"a\":1"); + assertSuccessContaining(controller.fetchComponentMaster(PSM_ID), "\"b\":2"); + assertSuccessContaining(controller.fetchProcedureMasterDelFalse(PSM_ID), "\"c\":3"); + assertSuccessContaining(controller.fetchComponentMasterDelFalse(PSM_ID), "\"d\":4"); + assertSuccessContaining(controller.fetchProcCompMappingDelFalse(PSM_ID), "\"e\":5"); + assertSuccessContaining(controller.fetchProcCompMappingForSingleProcedure(PROCEDURE_ID), "\"f\":6"); + } + + @Test + @DisplayName("the fetch endpoints should refuse a request that names no record to read") + void fetchEndpoints_shouldRefuseRequestWithoutRecord() throws Exception { + assertInvalidRequest(controller.fetchProcedureMaster(0)); + assertInvalidRequest(controller.fetchComponentMaster(0)); + assertInvalidRequest(controller.fetchProcedureMasterDelFalse(0)); + assertInvalidRequest(controller.fetchComponentMasterDelFalse(0)); + assertInvalidRequest(controller.fetchProcCompMappingDelFalse(0)); + assertInvalidRequest(controller.fetchProcCompMappingForSingleProcedure(0)); + assertInvalidRequest(controller.fetchComponentDetailsForComponentID(0)); + verify(mastersFetchingServiceImpl, never()).getProcedureMaster(anyInt()); + } + + @Test + @DisplayName("fetchProcedureMaster should answer an error envelope when the lookup fails") + void fetchProcedureMaster_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(mastersFetchingServiceImpl.getProcedureMaster(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.fetchProcedureMaster(PSM_ID))); + } + + @Test + @DisplayName("fetchComponentDetailsForComponentID should answer the component the service publishes") + void fetchComponentDetails_shouldAnswerPublishedComponent() throws Exception { + when(mastersFetchingServiceImpl.getComponentDetailsForComponentID(COMPONENT_ID)) + .thenReturn("{\"testComponentName\":\"Haemoglobin count\"}"); + + assertSuccessContaining(controller.fetchComponentDetailsForComponentID(COMPONENT_ID), + "Haemoglobin count"); + } + + @Test + @DisplayName("fetchComponentDetailsForComponentID should say so when the component is not on record") + void fetchComponentDetails_shouldSaySoForUnknownComponent() throws Exception { + when(mastersFetchingServiceImpl.getComponentDetailsForComponentID(COMPONENT_ID)).thenReturn(null); + + assertSuccessContaining(controller.fetchComponentDetailsForComponentID(COMPONENT_ID), + "Component Details not found in Database."); + } + + @Test + @DisplayName("updateProcedureStatus should answer the procedure once its status has changed") + void updateProcedureStatus_shouldAnswerChangedProcedure() throws Exception { + when(mastersStatusUpdateImpl.updateProcedureStatus(PROCEDURE_ID, true)).thenReturn(PROCEDURE_JSON); + + assertSuccessContaining( + controller.updateProcedureStatus("{\"procedureID\":71,\"deleted\":true}"), "Haemoglobin"); + } + + @Test + @DisplayName("updateProcedureStatus should report a status the service could not change") + void updateProcedureStatus_shouldReportUnchangedStatus() throws Exception { + when(mastersStatusUpdateImpl.updateProcedureStatus(anyInt(), anyBoolean())).thenReturn(null); + + String response = controller.updateProcedureStatus("{\"procedureID\":71,\"deleted\":true}"); + + assertInvalidRequest(response); + assertTrue(response.contains("Failed to update the status"), response); + } + + @Test + @DisplayName("updateProcedureStatus should refuse a request that names no procedure") + void updateProcedureStatus_shouldRefuseRequestWithoutProcedure() { + assertInvalidRequest(controller.updateProcedureStatus("{\"deleted\":true}")); + assertInvalidRequest(controller.updateProcedureStatus("{\"procedureID\":0,\"deleted\":true}")); + } + + @Test + @DisplayName("updateProcedureStatus should answer an error envelope for a body it cannot read") + void updateProcedureStatus_shouldAnswerErrorEnvelopeForMalformedBody() { + assertEquals(OutputResponse.OBJECT_FAILURE, statusCodeOf(controller.updateProcedureStatus("not json"))); + } + + @Test + @DisplayName("updateComponentStatus should answer the component once its status has changed") + void updateComponentStatus_shouldAnswerChangedComponent() throws Exception { + when(mastersStatusUpdateImpl.updateComponentStatus(COMPONENT_ID, true)) + .thenReturn("{\"testComponentName\":\"Haemoglobin count\"}"); + + assertSuccessContaining( + controller.updateComponentStatus("{\"componentID\":81,\"deleted\":true}"), "Haemoglobin count"); + } + + @Test + @DisplayName("updateComponentStatus should report a status the service could not change") + void updateComponentStatus_shouldReportUnchangedStatus() throws Exception { + when(mastersStatusUpdateImpl.updateComponentStatus(anyInt(), anyBoolean())).thenReturn(null); + + assertInvalidRequest(controller.updateComponentStatus("{\"componentID\":81,\"deleted\":true}")); + } + + @Test + @DisplayName("updateComponentStatus should refuse a request that names no component") + void updateComponentStatus_shouldRefuseRequestWithoutComponent() { + assertInvalidRequest(controller.updateComponentStatus("{\"deleted\":true}")); + } + + @Test + @DisplayName("updateProcedureMaster should answer the procedure once the edit lands") + void updateProcedureMaster_shouldAnswerEditedProcedure() throws Exception { + when(mastersStatusUpdateImpl.updateProcedureMaster(anyString())).thenReturn(PROCEDURE_JSON); + + assertSuccessContaining(controller.updateProcedureMaster(PROCEDURE_JSON), "Haemoglobin"); + } + + @Test + @DisplayName("updateProcedureMaster should report an edit the service could not apply") + void updateProcedureMaster_shouldReportUnappliedEdit() throws Exception { + when(mastersStatusUpdateImpl.updateProcedureMaster(anyString())).thenReturn(null); + + String response = controller.updateProcedureMaster("{}"); + + assertInvalidRequest(response); + assertTrue(response.contains("Failed to update procedure details"), response); + } + + @Test + @DisplayName("updateProcedureMaster should answer an error envelope when the edit fails") + void updateProcedureMaster_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(mastersStatusUpdateImpl.updateProcedureMaster(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.updateProcedureMaster("{}"))); + } + + @Test + @DisplayName("updateComponentMaster should answer the component once the edit lands") + void updateComponentMaster_shouldAnswerEditedComponent() throws Exception { + when(mastersStatusUpdateImpl.updateComponentMaster(anyString())) + .thenReturn("{\"testComponentName\":\"Haemoglobin count\"}"); + + assertSuccessContaining(controller.updateComponentMaster("{}"), "Haemoglobin count"); + } + + @Test + @DisplayName("updateComponentMaster should report an edit the service could not apply") + void updateComponentMaster_shouldReportUnappliedEdit() throws Exception { + when(mastersStatusUpdateImpl.updateComponentMaster(anyString())).thenReturn(null); + + String response = controller.updateComponentMaster("{}"); + + assertInvalidRequest(response); + assertTrue(response.contains("Failed to update component details"), response); + } + + @Test + @DisplayName("updateComponentMaster should answer an error envelope when the edit fails") + void updateComponentMaster_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(mastersStatusUpdateImpl.updateComponentMaster(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.updateComponentMaster("{}"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/labmodule/SmartDiagnosticsControllerTest.java b/src/test/java/com/iemr/admin/controller/labmodule/SmartDiagnosticsControllerTest.java new file mode 100644 index 0000000..2e181c9 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/labmodule/SmartDiagnosticsControllerTest.java @@ -0,0 +1,103 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.labmodule; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.admin.sevice.labmodule.IOTService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +/** + * The smart diagnostics endpoints tell the client which device tests exist and + * where the rapid screening device is reachable. + */ +@ExtendWith(MockitoExtension.class) +@DisplayName("SmartDiagnosticsController Test Suite") +class SmartDiagnosticsControllerTest { + + @Mock + private IOTService iotService; + + @InjectMocks + private SmartDiagnosticsController controller; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("getIOTProcedure should answer the device tests the service publishes") + void getIOTProcedure_shouldAnswerDeviceTests() { + when(iotService.getIOTProcedure()).thenReturn("[{\"calibrationCode\":\"HB\"}]"); + + String response = controller.getIOTProcedure(); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("HB"), response); + } + + @Test + @DisplayName("getIOTProcedure should answer an error envelope when the lookup fails") + void getIOTProcedure_shouldAnswerErrorEnvelopeOnFailure() { + when(iotService.getIOTProcedure()).thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getIOTProcedure())); + } + + @Test + @DisplayName("getIOTComponent should answer the device components the service publishes") + void getIOTComponent_shouldAnswerDeviceComponents() { + when(iotService.getIOTComponent()).thenReturn("[{\"iotComponentID\":1}]"); + + String response = controller.getIOTComponent(); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("iotComponentID"), response); + } + + @Test + @DisplayName("getIOTComponent should answer an error envelope when the lookup fails") + void getIOTComponent_shouldAnswerErrorEnvelopeOnFailure() { + when(iotService.getIOTComponent()).thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getIOTComponent())); + } + + @Test + @DisplayName("getBiologicalScreeningDeviceUrl should answer the configured device address") + void getBiologicalScreeningDeviceUrl_shouldAnswerConfiguredAddress() { + ReflectionTestUtils.setField(controller, "biologicalScreeningDeviceUrl", "http://device.local:9000"); + + assertEquals("http://device.local:9000", controller.getBiologicalScreeningDeviceUrl()); + } +} diff --git a/src/test/java/com/iemr/admin/controller/locationmaster/LocationMasterControllerTest.java b/src/test/java/com/iemr/admin/controller/locationmaster/LocationMasterControllerTest.java new file mode 100644 index 0000000..fd267e5 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/locationmaster/LocationMasterControllerTest.java @@ -0,0 +1,425 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.locationmaster; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.locationmaster.M_ProviderServiceAddMapping; +import com.iemr.admin.data.locationmaster.Showofficedetails; +import com.iemr.admin.data.locationmaster.StateServiceMapping1; +import com.iemr.admin.service.locationmaster.LocationMasterServiceInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The location endpoints keep the office addresses a provider works out of, and + * choose between a national and a state-scoped lookup depending on the service + * line. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("LocationMasterController Test Suite") +class LocationMasterControllerTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer PSM_ID = 4001; + + @Mock + private LocationMasterServiceInter locationMasterServiceInter; + + @InjectMocks + private LocationMasterController controller; + + private static StateServiceMapping1 mapping(Integer psmId) { + return new StateServiceMapping1(psmId); + } + + private static Showofficedetails office(String name) { + Showofficedetails office = new Showofficedetails(); + office.setLocationName(name); + office.setProviderServiceMapID(PSM_ID); + return office; + } + + private static M_ProviderServiceAddMapping address(Integer id, String name) { + M_ProviderServiceAddMapping address = new M_ProviderServiceAddMapping(); + address.setpSAddMapID(id); + address.setLocationName(name); + return address; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + @Test + @DisplayName("getAllRole2 should resolve the mapping before reading its addresses") + void getAllRole2_shouldResolveMappingFirst() { + when(locationMasterServiceInter.getAllByMapId2(PROVIDER_ID, 29, 3)) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + when(locationMasterServiceInter.getlocationByMapid(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(address(51, "Bengaluru Office")))); + + assertSuccessContaining(controller.getAllRole2( + "{\"serviceProviderID\":77,\"stateID\":29,\"serviceID\":3}"), "Bengaluru Office"); + } + + @Test + @DisplayName("getAllRole2 should fall back to no mapping when the provider has none") + void getAllRole2_shouldFallBackWithoutMapping() { + when(locationMasterServiceInter.getAllByMapId2(any(), any(), any())).thenReturn(new ArrayList<>()); + when(locationMasterServiceInter.getlocationByMapid(0)).thenReturn(new ArrayList<>()); + + controller.getAllRole2("{\"serviceProviderID\":77}"); + + verify(locationMasterServiceInter).getlocationByMapid(0); + } + + @Test + @DisplayName("getAllRole2 should answer an error envelope when the lookup fails") + void getAllRole2_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getAllByMapId2(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRole2("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getAlllocation should use the national lookup for a national service line") + void getAlllocation_shouldUseNationalLookup() { + when(locationMasterServiceInter.getAllByMapId3(PROVIDER_ID, 3)) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + when(locationMasterServiceInter.getlocationByMapid2(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining(controller.getAlllocation( + "{\"serviceProviderID\":77,\"serviceID\":3,\"isNational\":true}"), "Bengaluru Office"); + verify(locationMasterServiceInter).getAllByMapId3(PROVIDER_ID, 3); + } + + @Test + @DisplayName("getAlllocation should use the state lookup for a state-scoped service line") + void getAlllocation_shouldUseStateLookup() { + when(locationMasterServiceInter.getAllByMapId2(PROVIDER_ID, 29, 3)) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + when(locationMasterServiceInter.getlocationByMapid2(PSM_ID)).thenReturn(new ArrayList<>()); + + controller.getAlllocation("{\"serviceProviderID\":77,\"stateID\":29,\"serviceID\":3,\"isNational\":false}"); + + verify(locationMasterServiceInter).getAllByMapId2(PROVIDER_ID, 29, 3); + } + + @Test + @DisplayName("getAlllocation should narrow to the district when the caller names one") + void getAlllocation_shouldNarrowToDistrict() { + when(locationMasterServiceInter.getAllByMapId2(any(), any(), any())) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + when(locationMasterServiceInter.getlocationByMapid4(PSM_ID, 301)) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining(controller.getAlllocation("{\"serviceProviderID\":77,\"stateID\":29," + + "\"serviceID\":3,\"districtID\":301,\"isNational\":false}"), "Bengaluru Office"); + verify(locationMasterServiceInter, never()).getlocationByMapid2(anyInt()); + } + + @Test + @DisplayName("getAlllocation should answer an error envelope when the national flag is missing") + void getAlllocation_shouldAnswerErrorEnvelopeWithoutNationalFlag() { + assertCodeException(controller.getAlllocation("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getAlllocationNew should read the addresses straight off the mapping the caller names") + void getAlllocationNew_shouldReadFromNamedMapping() { + when(locationMasterServiceInter.getlocationByMapid2(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining(controller.getAlllocationNew("{\"providerServiceMapID\":4001}"), + "Bengaluru Office"); + } + + @Test + @DisplayName("getAlllocationNew should narrow to the district when the caller names one") + void getAlllocationNew_shouldNarrowToDistrict() { + when(locationMasterServiceInter.getlocationByMapid4(PSM_ID, 301)) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining( + controller.getAlllocationNew("{\"providerServiceMapID\":4001,\"districtID\":301}"), + "Bengaluru Office"); + } + + @Test + @DisplayName("getAlllocationNew should answer an error envelope when the lookup fails") + void getAlllocationNew_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getlocationByMapid2(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAlllocationNew("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("searchRole should answer the states the provider serves") + void searchRole_shouldAnswerServedStates() { + when(locationMasterServiceInter.getStateByServiceProviderId(PROVIDER_ID)) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + + assertSuccessContaining(controller.searchRole("{\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("searchRole should answer an error envelope when the lookup fails") + void searchRole_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getStateByServiceProviderId(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchRole("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getService should answer the service lines the provider runs in the state") + void getService_shouldAnswerServiceLinesInState() { + when(locationMasterServiceInter.getServiceByServiceProviderIdAndStateId(PROVIDER_ID, 29)) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + + assertSuccessContaining(controller.getService("{\"serviceProviderID\":77,\"stateID\":29}"), "4001"); + } + + @Test + @DisplayName("getService should answer an error envelope when the lookup fails") + void getService_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getServiceByServiceProviderIdAndStateId(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getService("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getAllDistrict should answer the districts of the state") + void getAllDistrict_shouldAnswerStateDistricts() { + M_District district = new M_District(); + district.setDistrictID(301); + district.setDistrictName("Bengaluru Urban"); + when(locationMasterServiceInter.getAllDistrictByStateId(29)) + .thenReturn(new ArrayList<>(List.of(district))); + + assertSuccessContaining(controller.getAllDistrict("{\"stateID\":29}"), "Bengaluru Urban"); + } + + @Test + @DisplayName("getAllDistrict should answer an error envelope when the lookup fails") + void getAllDistrict_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getAllDistrictByStateId(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllDistrict("{\"stateID\":29}")); + } + + @Test + @DisplayName("getAllRole should create one address per mapping the request names") + void getAllRole_shouldCreateOneAddressPerMapping() { + when(locationMasterServiceInter.addlocation(anyList())) + .thenReturn(new ArrayList<>(List.of(address(51, "Bengaluru Office")))); + + String response = controller.getAllRole("{\"providerServiceMapID\":[4001,4002]," + + "\"address\":\"Main Road\",\"locationName\":\"Bengaluru Office\",\"districtID\":301," + + "\"createdBy\":\"admin\",\"abdmFacilityId\":\"ABDM-1\"," + + "\"abdmFacilityName\":\"Bengaluru PHC\"}"); + + assertSuccessContaining(response, "Bengaluru Office"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(locationMasterServiceInter).addlocation(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals("ABDM-1", captor.getValue().get(0).getAbdmFacilityId()); + } + + @Test + @DisplayName("getAllRole should answer an error envelope when no mappings are named") + void getAllRole_shouldAnswerErrorEnvelopeWithoutMappings() { + assertCodeException(controller.getAllRole("{\"address\":\"Main Road\"}")); + } + + @Test + @DisplayName("geteditLocation should copy the edits onto the stored address") + void geteditLocation_shouldCopyEdits() { + M_ProviderServiceAddMapping stored = address(51, "old name"); + when(locationMasterServiceInter.editData(51)).thenReturn(stored); + when(locationMasterServiceInter.saveEditData(stored)).thenReturn(stored); + + String response = controller.geteditLocation("{\"pSAddMapID\":51,\"providerServiceMapID\":4001," + + "\"districtID\":301,\"address\":\"Main Road\",\"locationName\":\"Bengaluru Office\"," + + "\"abdmFacilityId\":\"ABDM-1\",\"abdmFacilityName\":\"Bengaluru PHC\"}"); + + assertSuccessContaining(response, "Bengaluru Office"); + assertEquals("Main Road", stored.getAddress()); + assertEquals("ABDM-1", stored.getAbdmFacilityId()); + } + + @Test + @DisplayName("geteditLocation should answer an error envelope for an address that does not exist") + void geteditLocation_shouldAnswerErrorEnvelopeForUnknownAddress() { + when(locationMasterServiceInter.editData(51)).thenReturn(null); + + assertCodeException(controller.geteditLocation("{\"pSAddMapID\":51}")); + } + + @Test + @DisplayName("deleteLocation should mark the address deleted") + void deleteLocation_shouldMarkAddressDeleted() { + M_ProviderServiceAddMapping stored = address(51, "Bengaluru Office"); + when(locationMasterServiceInter.editData(51)).thenReturn(stored); + when(locationMasterServiceInter.saveEditData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteLocation("{\"pSAddMapID\":51,\"deleted\":true}"), + "Bengaluru Office"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteLocation should answer an error envelope for an address that does not exist") + void deleteLocation_shouldAnswerErrorEnvelopeForUnknownAddress() { + when(locationMasterServiceInter.editData(51)).thenReturn(null); + + assertCodeException(controller.deleteLocation("{\"pSAddMapID\":51,\"deleted\":true}")); + } + + @Test + @DisplayName("getLocationByServiceID should gather the addresses of every mapping on the service line") + void getLocationByServiceID_shouldGatherAddressesAcrossMappings() { + when(locationMasterServiceInter.getLocationByServiceId(PROVIDER_ID, 3)) + .thenReturn(new ArrayList<>(List.of(mapping(4001), mapping(4002)))); + when(locationMasterServiceInter.getlocationByMapid1(any())) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining( + controller.getLocationByServiceID("{\"serviceProviderID\":77,\"serviceID\":3}"), + "Bengaluru Office"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class); + verify(locationMasterServiceInter).getlocationByMapid1(captor.capture()); + assertEquals(List.of(4001, 4002), captor.getValue()); + } + + @Test + @DisplayName("getLocationByServiceID should answer an error envelope when the lookup fails") + void getLocationByServiceID_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getLocationByServiceId(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getLocationByServiceID("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getLocationByStateId should gather the addresses of every mapping in the state") + void getLocationByStateId_shouldGatherAddressesAcrossMappings() { + when(locationMasterServiceInter.getLocationBySateID(PROVIDER_ID, 29)) + .thenReturn(new ArrayList<>(List.of(mapping(4001)))); + when(locationMasterServiceInter.getlocationByMapid1(any())) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining( + controller.getLocationByStateId("{\"serviceProviderID\":77,\"stateID\":29}"), "Bengaluru Office"); + } + + @Test + @DisplayName("getLocationByStateId should answer an error envelope when the lookup fails") + void getLocationByStateId_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getLocationBySateID(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getLocationByStateId("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getOfficeNameByMapId should ask for one office per mapping the request lists") + void getOfficeNameByMapId_shouldAskForOneOfficePerMapping() { + when(locationMasterServiceInter.getOfficeName(any())) + .thenReturn(new ArrayList<>(List.of(office("Bengaluru Office")))); + + assertSuccessContaining( + controller.getOfficeNameByMapId("{\"providerServiceMapID\":[4001,4002]}"), "Bengaluru Office"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class); + verify(locationMasterServiceInter).getOfficeName(captor.capture()); + assertEquals(2, captor.getValue().size()); + } + + @Test + @DisplayName("getOfficeNameByMapId should answer an error envelope when no mappings are listed") + void getOfficeNameByMapId_shouldAnswerErrorEnvelopeWithoutMappings() { + assertCodeException(controller.getOfficeNameByMapId("{}")); + } + + @Test + @DisplayName("getStatesByServiceID should answer the states the service line runs in") + void getStatesByServiceID_shouldAnswerServedStates() { + when(locationMasterServiceInter.getStatesByServiceId(3, PROVIDER_ID)) + .thenReturn(new ArrayList<>(List.of(mapping(PSM_ID)))); + + assertSuccessContaining( + controller.getStatesByServiceID("{\"serviceID\":3,\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("getStatesByServiceID should answer an error envelope when the lookup fails") + void getStatesByServiceID_shouldAnswerErrorEnvelopeOnFailure() { + when(locationMasterServiceInter.getStatesByServiceId(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getStatesByServiceID("{\"serviceID\":3}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/manufacturer/ManufacturerControllerTest.java b/src/test/java/com/iemr/admin/controller/manufacturer/ManufacturerControllerTest.java new file mode 100644 index 0000000..f0f3477 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/manufacturer/ManufacturerControllerTest.java @@ -0,0 +1,169 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.manufacturer; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.manufacturer.M_Manufacturer; +import com.iemr.admin.service.manufacturer.ManufacturerInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The manufacturer master endpoints keep the manufacturer catalogue an inventory operator picks from. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ManufacturerController Test Suite") +class ManufacturerControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private ManufacturerInter manufacturerInter; + + @InjectMocks + private ManufacturerController controller; + + private static M_Manufacturer record(Integer id, String name) { + M_Manufacturer record = new M_Manufacturer(); + record.setManufacturerID(id); + record.setManufacturerName(name); + return record; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("createManufacturer should answer the records the service stored") + void create_shouldAnswerStoredRecords() { + when(manufacturerInter.createManufacturer(anyList())).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "Cipla")))); + + assertSuccessContaining(controller.createManufacturer("[{\"manufacturerCode\":\"C-1\"}]"), "Cipla"); + } + + @Test + @DisplayName("createManufacturer should answer an error envelope when the store fails") + void create_shouldAnswerErrorEnvelopeOnFailure() { + when(manufacturerInter.createManufacturer(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createManufacturer("[{}]")); + } + + @Test + @DisplayName("getManufacturer should answer the records of the provider") + void get_shouldAnswerProviderRecords() { + when(manufacturerInter.createManufacturer(PSM_ID)).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "Cipla")))); + + assertSuccessContaining(controller.getManufacturer("{\"providerServiceMapID\":4001}"), "Cipla"); + } + + @Test + @DisplayName("getManufacturer should answer an error envelope when the lookup fails") + void get_shouldAnswerErrorEnvelopeOnFailure() { + when(manufacturerInter.createManufacturer(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getManufacturer("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("editManufacturer should copy the edits onto the stored record") + void edit_shouldCopyEdits() { + M_Manufacturer stored = record(RECORD_ID, "Cipla"); + when(manufacturerInter.editManufacturer(RECORD_ID)).thenReturn(stored); + when(manufacturerInter.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editManufacturer("{\"manufacturerID\":11,\"manufacturerDesc\":\"Generic maker\",\"contactPerson\":\"Asha\",\"modifiedBy\":\"admin\"}"), "Cipla"); + assertEquals("Generic maker", stored.getManufacturerDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editManufacturer should answer an error envelope for a record that does not exist") + void edit_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(manufacturerInter.editManufacturer(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.editManufacturer("{\"manufacturerID\":11,\"manufacturerDesc\":\"Generic maker\",\"contactPerson\":\"Asha\",\"modifiedBy\":\"admin\"}"))); + } + + @Test + @DisplayName("deleteManufacturer should mark the record deleted") + void delete_shouldMarkRecordDeleted() { + M_Manufacturer stored = record(RECORD_ID, "Cipla"); + when(manufacturerInter.editManufacturer(RECORD_ID)).thenReturn(stored); + when(manufacturerInter.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteManufacturer("{\"manufacturerID\":11,\"deleted\":true}"), "Cipla"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteManufacturer should answer an error envelope for a record that does not exist") + void delete_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(manufacturerInter.editManufacturer(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.deleteManufacturer("{\"manufacturerID\":11,\"deleted\":true}"))); + } + + @Test + @DisplayName("checkManufacturerCode should report whether the code is already taken") + void check_shouldReportWhetherCodeIsTaken() { + when(manufacturerInter.checkManufacturerCode(any())).thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkManufacturerCode("{\"manufacturerCode\":\"C-1\"}"), "true"); + } + + @Test + @DisplayName("checkManufacturerCode should answer an error envelope when the check fails") + void check_shouldAnswerErrorEnvelopeOnFailure() { + when(manufacturerInter.checkManufacturerCode(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkManufacturerCode("{\"manufacturerCode\":\"C-1\"}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/nodalConfig/NodalConfigControllerTest.java b/src/test/java/com/iemr/admin/controller/nodalConfig/NodalConfigControllerTest.java new file mode 100644 index 0000000..e5129d3 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/nodalConfig/NodalConfigControllerTest.java @@ -0,0 +1,147 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.nodalConfig; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.iemr.admin.model.emailconfig.NodalEmailRequest; +import com.iemr.admin.model.emailconfig.NodalEmailResponse; +import com.iemr.admin.model.emailconfig.CreateNodalEmailRequestModel; +import com.iemr.admin.model.emailconfig.UpdateNodalEmailRequest; +import com.iemr.admin.service.nodalemailconfig.NodalConfigService; +import com.iemr.admin.utils.mapper.OutputMapper; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The nodal config screen keeps the nodal officer mailboxes a complaint is + * escalated to. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NodalConfigController Test Suite") +class NodalConfigControllerTest { + + @Mock + private NodalConfigService nodalConfigService; + + @InjectMocks + private NodalConfigController controller; + + private final MockHttpServletRequest servletRequest = new MockHttpServletRequest(); + + @BeforeEach + @DisplayName("Prime the shared output builder the screens publish through") + void setUp() { + new OutputMapper(); + } + + private static NodalEmailResponse mailbox() { + NodalEmailResponse mailbox = new NodalEmailResponse(); + mailbox.setEmailID("nodal.bengaluru@example.org"); + return mailbox; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("saveConfig should answer the mailboxes it recorded") + void save_shouldAnswerRecordedMailboxes() { + when(nodalConfigService.saveNodalEmailConfigs(anyList())).thenReturn(List.of(mailbox())); + + String response = controller.saveConfig(List.of(new CreateNodalEmailRequestModel()), servletRequest); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("nodal.bengaluru@example.org"), response); + } + + @Test + @DisplayName("saveConfig should report the failure when the mailbox cannot be recorded") + void save_shouldReportStorageFailure() { + when(nodalConfigService.saveNodalEmailConfigs(anyList())).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.saveConfig(new ArrayList<>(), servletRequest))); + } + + @Test + @DisplayName("getEmailConfigs should answer the mailboxes matching what the caller narrowed by") + void get_shouldAnswerMatchingMailboxes() { + when(nodalConfigService.getAllNodalEmailConfigs(any(NodalEmailRequest.class))).thenReturn(List.of(mailbox())); + + String response = controller.getNodalEmailConfigs(new NodalEmailRequest(), servletRequest); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("nodal.bengaluru@example.org"), response); + } + + @Test + @DisplayName("getEmailConfigs should report the failure when the mailboxes cannot be answered") + void get_shouldReportLookupFailure() { + when(nodalConfigService.getAllNodalEmailConfigs(any(NodalEmailRequest.class))) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getNodalEmailConfigs(new NodalEmailRequest(), servletRequest))); + } + + @Test + @DisplayName("updateEmailConfig should answer the mailbox as it stands after the change") + void update_shouldAnswerChangedMailbox() { + when(nodalConfigService.updateNodalEmailConfigs(any(UpdateNodalEmailRequest.class))).thenReturn(mailbox()); + + String response = controller.updateNodalEmailConfig(new UpdateNodalEmailRequest(), servletRequest); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("nodal.bengaluru@example.org"), response); + } + + @Test + @DisplayName("updateEmailConfig should report the failure when the change cannot be recorded") + void update_shouldReportStorageFailure() { + when(nodalConfigService.updateNodalEmailConfigs(any(UpdateNodalEmailRequest.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.updateNodalEmailConfig(new UpdateNodalEmailRequest(), servletRequest))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/parkingPlace/ParkingPlaceControllerTest.java b/src/test/java/com/iemr/admin/controller/parkingPlace/ParkingPlaceControllerTest.java new file mode 100644 index 0000000..bfa0d24 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/parkingPlace/ParkingPlaceControllerTest.java @@ -0,0 +1,260 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.parkingPlace; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.parkingPlace.M_Parkingplace; +import com.iemr.admin.service.parkingPlace.ParkingPlaceServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The parking place screens create, retire and re-address the places a van is + * stationed at, and answer them back filtered by state, provider or zone. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ParkingPlaceController Test Suite") +class ParkingPlaceControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PARKING_PLACE_ID = 31; + + @Mock + private ParkingPlaceServiceImpl parkingPlaceServiceImpl; + + @InjectMocks + private ParkingPlaceController controller; + + private static M_Parkingplace place() { + M_Parkingplace place = new M_Parkingplace(); + place.setParkingPlaceID(PARKING_PLACE_ID); + place.setParkingPlaceName("Hosur parking"); + place.setParkingPlaceDesc("Near the bus stand"); + place.setProviderServiceMapID(PSM_ID); + return place; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + private static void assertFailure(String response, int expectedCode) { + assertEquals(expectedCode, statusCodeOf(response), response); + } + + @Test + @DisplayName("saveParkingPlace should answer the parking places it created") + void saveParkingPlace_shouldAnswerCreatedPlaces() throws Exception { + when(parkingPlaceServiceImpl.saveParkingPlace(anyList())) + .thenReturn(new ArrayList<>(List.of(place()))); + + String response = controller.saveParkingPlace( + "{\"parkingPlaces\":[{\"parkingPlaceName\":\"Hosur parking\",\"providerServiceMapID\":4001}]}"); + + assertSuccessContaining(response, "Hosur parking"); + } + + @Test + @DisplayName("saveParkingPlace should report the failure when the parking place cannot be stored") + void saveParkingPlace_shouldReportStorageFailure() throws Exception { + when(parkingPlaceServiceImpl.saveParkingPlace(anyList())) + .thenThrow(new RuntimeException("duplicate parking place")); + + String response = controller.saveParkingPlace("{\"parkingPlaces\":[{\"parkingPlaceName\":\"Hosur\"}]}"); + + assertFailure(response, OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("getParkingPlaces should answer the parking places of the state and district asked for") + void getParkingPlaces_shouldAnswerPlacesOfStateAndDistrict() throws Exception { + when(parkingPlaceServiceImpl.getAvailableParkingPlaces(29, 301, 5)) + .thenReturn(new ArrayList<>(List.of(place()))); + + String response = controller + .getParkingPlaces("{\"stateID\":29,\"districtID\":301,\"serviceProviderID\":5}"); + + assertSuccessContaining(response, "Hosur parking"); + } + + @Test + @DisplayName("getParkingPlaces should report the failure when the lookup cannot be answered") + void getParkingPlaces_shouldReportLookupFailure() throws Exception { + when(parkingPlaceServiceImpl.getAvailableParkingPlaces(any(), any(), any())) + .thenThrow(new RuntimeException("query timed out")); + + assertFailure(controller.getParkingPlaces("{\"stateID\":29}"), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("deleteParkingPlace should confirm the retirement it recorded") + void deleteParkingPlace_shouldConfirmRetirement() throws Exception { + when(parkingPlaceServiceImpl.updateParkingPlaceStatus(any(M_Parkingplace.class))).thenReturn(1); + + String response = controller + .deleteParkingPlace("{\"parkingPlaceID\":31,\"deleted\":true,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "status updated successfully"); + } + + @Test + @DisplayName("deleteParkingPlace should say so when no parking place was retired") + void deleteParkingPlace_shouldSaySoWhenNothingRetired() throws Exception { + when(parkingPlaceServiceImpl.updateParkingPlaceStatus(any(M_Parkingplace.class))).thenReturn(0); + + String response = controller.deleteParkingPlace("{\"parkingPlaceID\":-1,\"deleted\":true}"); + + assertSuccessContaining(response, "Failed to update the status"); + } + + @Test + @DisplayName("deleteParkingPlace should report the failure when the retirement cannot be recorded") + void deleteParkingPlace_shouldReportRetirementFailure() throws Exception { + when(parkingPlaceServiceImpl.updateParkingPlaceStatus(any(M_Parkingplace.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertFailure(controller.deleteParkingPlace("{\"parkingPlaceID\":31}"), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("updateParkingPlaceDetails should answer the re-addressed parking place") + void updateParkingPlaceDetails_shouldAnswerReaddressedPlace() throws Exception { + M_Parkingplace stored = place(); + when(parkingPlaceServiceImpl.getParkingPlaceByID(PARKING_PLACE_ID)).thenReturn(stored); + when(parkingPlaceServiceImpl.updateParkingPlaceData(stored)).thenReturn(stored); + + String response = controller.updateParkingPlaceDetails( + "{\"parkingPlaceID\":31,\"parkingPlaceName\":\"Hosur parking\",\"areaHQAddress\":\"Hosur Road\"," + + "\"stateID\":29,\"districtID\":301,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "Hosur parking"); + assertEquals("Hosur Road", stored.getAreaHQAddress()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("updateParkingPlaceDetails should report the failure when the parking place is unknown") + void updateParkingPlaceDetails_shouldReportUnknownPlace() throws Exception { + when(parkingPlaceServiceImpl.getParkingPlaceByID(anyInt())).thenReturn(null); + + assertFailure(controller.updateParkingPlaceDetails("{\"parkingPlaceID\":-1}"), + OutputResponse.CODE_EXCEPTION); + } + + @Test + @DisplayName("getParkingPlacesProviderserviceMap should answer the parking places of the provider asked for") + void getParkingPlacesProviderserviceMap_shouldAnswerPlacesOfProvider() throws Exception { + when(parkingPlaceServiceImpl.getParkingPlaces(PSM_ID)).thenReturn(List.of(place())); + + assertSuccessContaining(controller.getParkingPlacesProviderserviceMap("{\"providerServiceMapID\":4001}"), + "Hosur parking"); + } + + @Test + @DisplayName("getParkingPlacesProviderserviceMap should report the failure when the lookup cannot be answered") + void getParkingPlacesProviderserviceMap_shouldReportLookupFailure() throws Exception { + when(parkingPlaceServiceImpl.getParkingPlaces(any())) + .thenThrow(new RuntimeException("connection reset")); + + assertFailure(controller.getParkingPlacesProviderserviceMap("{\"providerServiceMapID\":4001}"), + OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("getSubDistrictDetailsByParkingPlaceID should answer the taluks the parking place covers") + void getSubDistrictDetails_shouldAnswerCoveredTaluks() throws Exception { + when(parkingPlaceServiceImpl.getSubDistrict(PARKING_PLACE_ID)) + .thenReturn(List.of(new M_Parkingplace(PARKING_PLACE_ID, 3011, "Anekal"))); + + assertSuccessContaining(controller.getSubDistrictDetailsByParkingPlaceID("{\"parkingPlaceID\":31}"), + "Anekal"); + } + + @Test + @DisplayName("getSubDistrictDetailsByParkingPlaceID should report the failure when the lookup cannot be answered") + void getSubDistrictDetails_shouldReportLookupFailure() throws Exception { + when(parkingPlaceServiceImpl.getSubDistrict(any())).thenThrow(new RuntimeException("query timed out")); + + assertFailure(controller.getSubDistrictDetailsByParkingPlaceID("{\"parkingPlaceID\":31}"), + OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("getparkingPlacesbyzoneid should answer the parking places of the zone asked for") + void getparkingPlacesbyzoneid_shouldAnswerPlacesOfZone() throws Exception { + when(parkingPlaceServiceImpl.getAvailableParkingPlacesbyZoneID(9, PSM_ID)) + .thenReturn(new ArrayList<>(List.of(place()))); + + assertSuccessContaining( + controller.getparkingPlacesbyzoneid("{\"zoneID\":9,\"providerServiceMapID\":4001}"), + "Hosur parking"); + } + + @Test + @DisplayName("getparkingPlacesbyzoneid should report the failure when the lookup cannot be answered") + void getparkingPlacesbyzoneid_shouldReportLookupFailure() throws Exception { + when(parkingPlaceServiceImpl.getAvailableParkingPlacesbyZoneID(any(), any())) + .thenThrow(new RuntimeException("connection reset")); + + assertFailure(controller.getparkingPlacesbyzoneid("{\"zoneID\":9}"), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("a malformed request body should be rejected rather than reach the service") + void malformedBody_shouldBeRejected() throws Exception { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(runMalformed()), "malformed JSON must be refused"); + } + + private String runMalformed() throws Exception { + try { + return controller.getParkingPlaces("{not json"); + } catch (Exception e) { + OutputResponse output = new OutputResponse(); + output.setError(e); + return output.toString(); + } + } +} diff --git a/src/test/java/com/iemr/admin/controller/parkingPlace/ParkingPlaceTalukMappingControllerTest.java b/src/test/java/com/iemr/admin/controller/parkingPlace/ParkingPlaceTalukMappingControllerTest.java new file mode 100644 index 0000000..30821f2 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/parkingPlace/ParkingPlaceTalukMappingControllerTest.java @@ -0,0 +1,261 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.parkingPlace; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.DistrictBlock; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMapping; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMappingTO; +import com.iemr.admin.service.parkingPlace.ParkingPlaceTalukMappingServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The taluk mapping screen records which taluks a parking place covers, and + * offers the district's remaining taluks as the candidates for a new mapping. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ParkingPlaceTalukMappingController Test Suite") +class ParkingPlaceTalukMappingControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PARKING_PLACE_ID = 31; + private static final Integer MAP_ID = 7001; + private static final Integer DISTRICT_ID = 301; + + @Mock + private ParkingPlaceTalukMappingServiceImpl parkingPlaceTalukMappingServiceImpl; + + @InjectMocks + private ParkingPlaceTalukMappingController controller; + + private static ParkingplaceTalukMapping mapping() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(MAP_ID); + mapping.setParkingPlaceID(PARKING_PLACE_ID); + mapping.setDistrictID(DISTRICT_ID); + mapping.setDistrictBlockID(3011); + mapping.setProviderServiceMapID(PSM_ID); + mapping.setCreatedBy("admin"); + return mapping; + } + + private static ParkingplaceTalukMappingTO published() { + ParkingplaceTalukMappingTO to = new ParkingplaceTalukMappingTO(); + to.setPpSubDistrictMapID(MAP_ID); + to.setDistrictBlockName("Anekal"); + return to; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + private static void assertFailure(String response, int expectedCode) { + assertEquals(expectedCode, statusCodeOf(response), response); + } + + @Test + @DisplayName("parkingPlacesTalukMapping should answer the mappings it created") + void create_shouldAnswerCreatedMappings() { + when(parkingPlaceTalukMappingServiceImpl.saveParkingPlaceTalukMapping(anyList())) + .thenReturn(new ArrayList<>(List.of(mapping()))); + + assertSuccessContaining(controller.parkingPlacesTalukMapping(List.of(mapping())), "7001"); + } + + @Test + @DisplayName("parkingPlacesTalukMapping should report the failure when the mapping cannot be stored") + void create_shouldReportStorageFailure() { + when(parkingPlaceTalukMappingServiceImpl.saveParkingPlaceTalukMapping(anyList())) + .thenThrow(new RuntimeException("taluk already mapped")); + + assertFailure(controller.parkingPlacesTalukMapping(List.of(mapping())), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("updateparkingPlacesTalukMapping should answer the mapping it moved") + void update_shouldAnswerMovedMapping() { + ParkingplaceTalukMapping stored = mapping(); + when(parkingPlaceTalukMappingServiceImpl.findbyID(MAP_ID)).thenReturn(stored); + when(parkingPlaceTalukMappingServiceImpl.updateParkingPlaceTalukMapping(stored)).thenReturn(stored); + + assertSuccessContaining(controller.updateparkingPlacesTalukMapping(mapping()), "7001"); + assertEquals("admin", stored.getModifiedBy(), "the caller must be recorded as the one who moved it"); + } + + @Test + @DisplayName("updateparkingPlacesTalukMapping should leave the request alone when no mapping is named") + void update_shouldLeaveRequestAloneWithoutMapId() { + ParkingplaceTalukMapping request = mapping(); + request.setPpSubDistrictMapID(null); + + assertSuccessContaining(controller.updateparkingPlacesTalukMapping(request), "31"); + verify(parkingPlaceTalukMappingServiceImpl, never()).findbyID(anyInt()); + } + + @Test + @DisplayName("updateparkingPlacesTalukMapping should report the failure when the mapping is unknown") + void update_shouldReportUnknownMapping() { + when(parkingPlaceTalukMappingServiceImpl.findbyID(MAP_ID)).thenReturn(null); + + assertFailure(controller.updateparkingPlacesTalukMapping(mapping()), OutputResponse.CODE_EXCEPTION); + } + + @Test + @DisplayName("getparkingPlacesTalukMapping should answer the mapping asked for") + void getById_shouldAnswerNamedMapping() { + when(parkingPlaceTalukMappingServiceImpl.findbyID(MAP_ID)).thenReturn(mapping()); + + assertSuccessContaining(controller.getparkingPlacesTalukMapping(mapping()), "7001"); + } + + @Test + @DisplayName("getparkingPlacesTalukMapping should echo the request when no mapping is named") + void getById_shouldEchoRequestWithoutMapId() { + ParkingplaceTalukMapping request = mapping(); + request.setPpSubDistrictMapID(null); + + assertSuccessContaining(controller.getparkingPlacesTalukMapping(request), "31"); + verify(parkingPlaceTalukMappingServiceImpl, never()).findbyID(anyInt()); + } + + @Test + @DisplayName("getparkingPlacesTalukMapping should report the failure when the lookup cannot be answered") + void getById_shouldReportLookupFailure() { + when(parkingPlaceTalukMappingServiceImpl.findbyID(MAP_ID)) + .thenThrow(new RuntimeException("query timed out")); + + assertFailure(controller.getparkingPlacesTalukMapping(mapping()), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("getallparkingPlacesTalukMapping should answer every taluk the parking place covers") + void getAll_shouldAnswerCoveredTaluks() { + when(parkingPlaceTalukMappingServiceImpl.findbyProviderservicemapid(any())) + .thenReturn(List.of(published())); + + assertSuccessContaining(controller.getallparkingPlacesTalukMapping(mapping()), "Anekal"); + } + + @Test + @DisplayName("getallparkingPlacesTalukMapping should report the failure when the lookup cannot be answered") + void getAll_shouldReportLookupFailure() { + when(parkingPlaceTalukMappingServiceImpl.findbyProviderservicemapid(any())) + .thenThrow(new RuntimeException("connection reset")); + + assertFailure(controller.getallparkingPlacesTalukMapping(mapping()), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("getafilterparkingPlacesTalukMapping should narrow the mappings to the district asked for") + void getFiltered_shouldNarrowToDistrict() { + when(parkingPlaceTalukMappingServiceImpl.findbyParkingplaceAndDistrictID(any())) + .thenReturn(List.of(published())); + + assertSuccessContaining(controller.getafilterparkingPlacesTalukMapping(mapping()), "Anekal"); + } + + @Test + @DisplayName("getafilterparkingPlacesTalukMapping should report the failure when the lookup cannot be answered") + void getFiltered_shouldReportLookupFailure() { + when(parkingPlaceTalukMappingServiceImpl.findbyParkingplaceAndDistrictID(any())) + .thenThrow(new RuntimeException("query timed out")); + + assertFailure(controller.getafilterparkingPlacesTalukMapping(mapping()), OutputResponse.GENERIC_FAILURE); + } + + @Test + @DisplayName("activateparkingPlacesTalukMapping should answer the mapping whose status it changed") + void activate_shouldAnswerChangedMapping() { + ParkingplaceTalukMapping stored = mapping(); + ParkingplaceTalukMapping request = mapping(); + request.setDeleted(Boolean.TRUE); + when(parkingPlaceTalukMappingServiceImpl.findbyID(MAP_ID)).thenReturn(stored); + when(parkingPlaceTalukMappingServiceImpl.updateParkingPlaceTalukMapping(stored)).thenReturn(stored); + + assertSuccessContaining(controller.activateparkingPlacesTalukMapping(request), "7001"); + assertEquals(Boolean.TRUE, stored.getDeleted(), "the mapping must take the requested status"); + } + + @Test + @DisplayName("activateparkingPlacesTalukMapping should leave the request alone when no mapping is named") + void activate_shouldLeaveRequestAloneWithoutMapId() { + ParkingplaceTalukMapping request = mapping(); + request.setPpSubDistrictMapID(null); + + assertSuccessContaining(controller.activateparkingPlacesTalukMapping(request), "31"); + verify(parkingPlaceTalukMappingServiceImpl, never()).findbyID(anyInt()); + } + + @Test + @DisplayName("activateparkingPlacesTalukMapping should report the failure when the mapping is unknown") + void activate_shouldReportUnknownMapping() { + when(parkingPlaceTalukMappingServiceImpl.findbyID(MAP_ID)).thenReturn(null); + + assertFailure(controller.activateparkingPlacesTalukMapping(mapping()), OutputResponse.CODE_EXCEPTION); + } + + @Test + @DisplayName("getunmappedtaluk should offer the taluks that are still free") + void getunmappedtaluk_shouldOfferFreeTaluks() { + when(parkingPlaceTalukMappingServiceImpl.getunmappedtaluk(DISTRICT_ID, PSM_ID)) + .thenReturn(List.of(new DistrictBlock(3012, "Hoskote"))); + + assertSuccessContaining(controller.getunmappedtaluk(mapping()), "Hoskote"); + } + + @Test + @DisplayName("getunmappedtaluk should report the failure when the candidates cannot be worked out") + void getunmappedtaluk_shouldReportLookupFailure() { + when(parkingPlaceTalukMappingServiceImpl.getunmappedtaluk(any(), any())) + .thenThrow(new RuntimeException("query timed out")); + + assertFailure(controller.getunmappedtaluk(mapping()), OutputResponse.GENERIC_FAILURE); + } +} diff --git a/src/test/java/com/iemr/admin/controller/pharmacologicalcategory/PharmacologicalCategoryControllerTest.java b/src/test/java/com/iemr/admin/controller/pharmacologicalcategory/PharmacologicalCategoryControllerTest.java new file mode 100644 index 0000000..7ceb4be --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/pharmacologicalcategory/PharmacologicalCategoryControllerTest.java @@ -0,0 +1,169 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.pharmacologicalcategory; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.pharmacologicalcategory.M_Pharmacologicalcategory; +import com.iemr.admin.service.pharmacologicalcategory.PharmacologicalcategoryInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The pharmacological category master endpoints keep the pharmacological category catalogue an inventory operator picks from. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PharmacologicalCategoryController Test Suite") +class PharmacologicalCategoryControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private PharmacologicalcategoryInter pharmacologicalcategoryInter; + + @InjectMocks + private PharmacologicalCategoryController controller; + + private static M_Pharmacologicalcategory record(Integer id, String name) { + M_Pharmacologicalcategory record = new M_Pharmacologicalcategory(); + record.setPharmacologyCategoryID(id); + record.setPharmCategoryName(name); + return record; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("createPharmacologicalcategory should answer the records the service stored") + void create_shouldAnswerStoredRecords() { + when(pharmacologicalcategoryInter.createPharmacologicalcategory(anyList())).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "Analgesic")))); + + assertSuccessContaining(controller.createPharmacologicalcategory("[{\"pharmCategoryCode\":\"C-1\"}]"), "Analgesic"); + } + + @Test + @DisplayName("createPharmacologicalcategory should answer an error envelope when the store fails") + void create_shouldAnswerErrorEnvelopeOnFailure() { + when(pharmacologicalcategoryInter.createPharmacologicalcategory(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createPharmacologicalcategory("[{}]")); + } + + @Test + @DisplayName("getPharmacologicalcategory should answer the records of the provider") + void get_shouldAnswerProviderRecords() { + when(pharmacologicalcategoryInter.getPharmacologicalcategory(PSM_ID)).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "Analgesic")))); + + assertSuccessContaining(controller.getPharmacologicalcategory("{\"providerServiceMapID\":4001}"), "Analgesic"); + } + + @Test + @DisplayName("getPharmacologicalcategory should answer an error envelope when the lookup fails") + void get_shouldAnswerErrorEnvelopeOnFailure() { + when(pharmacologicalcategoryInter.getPharmacologicalcategory(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getPharmacologicalcategory("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("editPharmacologicalcategory should copy the edits onto the stored record") + void edit_shouldCopyEdits() { + M_Pharmacologicalcategory stored = record(RECORD_ID, "Analgesic"); + when(pharmacologicalcategoryInter.editPharmacologicalcategory(RECORD_ID)).thenReturn(stored); + when(pharmacologicalcategoryInter.saveEditedPharData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editPharmacologicalcategory("{\"pharmacologyCategoryID\":11,\"pharmCategoryDesc\":\"Pain relief\",\"modifiedBy\":\"admin\"}"), "Analgesic"); + assertEquals("Pain relief", stored.getPharmCategoryDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editPharmacologicalcategory should answer an error envelope for a record that does not exist") + void edit_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(pharmacologicalcategoryInter.editPharmacologicalcategory(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.editPharmacologicalcategory("{\"pharmacologyCategoryID\":11,\"pharmCategoryDesc\":\"Pain relief\",\"modifiedBy\":\"admin\"}"))); + } + + @Test + @DisplayName("deletePharmacologicalcategory should mark the record deleted") + void delete_shouldMarkRecordDeleted() { + M_Pharmacologicalcategory stored = record(RECORD_ID, "Analgesic"); + when(pharmacologicalcategoryInter.editPharmacologicalcategory(RECORD_ID)).thenReturn(stored); + when(pharmacologicalcategoryInter.saveEditedPharData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deletePharmacologicalcategory("{\"pharmacologyCategoryID\":11,\"deleted\":true}"), "Analgesic"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deletePharmacologicalcategory should answer an error envelope for a record that does not exist") + void delete_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(pharmacologicalcategoryInter.editPharmacologicalcategory(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.deletePharmacologicalcategory("{\"pharmacologyCategoryID\":11,\"deleted\":true}"))); + } + + @Test + @DisplayName("checkPharmacologicalcategoryCode should report whether the code is already taken") + void check_shouldReportWhetherCodeIsTaken() { + when(pharmacologicalcategoryInter.checkPharmacologicalcategoryCode(any())).thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkPharmacologicalcategoryCode("{\"pharmCategoryCode\":\"C-1\"}"), "true"); + } + + @Test + @DisplayName("checkPharmacologicalcategoryCode should answer an error envelope when the check fails") + void check_shouldAnswerErrorEnvelopeOnFailure() { + when(pharmacologicalcategoryInter.checkPharmacologicalcategoryCode(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkPharmacologicalcategoryCode("{\"pharmCategoryCode\":\"C-1\"}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardCallTypeControllerTest.java b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardCallTypeControllerTest.java new file mode 100644 index 0000000..67077e1 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardCallTypeControllerTest.java @@ -0,0 +1,288 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.provideronboard; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import com.iemr.admin.data.provideronboard.M_Calltype; +import com.iemr.admin.data.provideronboard.M_Subservice; +import com.iemr.admin.data.provideronboard.M_SubservicemasterPA; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The call type and sub-service endpoints define the call taxonomy a provider's + * agents work with. + */ +@DisplayName("ProviderOnBoardController call type Test Suite") +class ProviderOnBoardCallTypeControllerTest extends ProviderOnBoardFixture { + + private static M_Calltype callType(Integer id, String name) { + M_Calltype callType = new M_Calltype(); + callType.setCallTypeID(id); + callType.setCallType(name); + return callType; + } + + private static M_Subservice subService(Integer id, String name) { + M_Subservice subService = new M_Subservice(); + subService.setSubServiceID(id); + subService.setSubServiceName(name); + return subService; + } + + @Test + @DisplayName("saveCallTypeData should flatten the nested request into one call type per description") + void saveCallTypeData_shouldFlattenNestedRequest() { + ArrayList stored = new ArrayList<>(List.of(callType(51, "Medical Advice"))); + when(calltypeinter.saveCallList(anyList())).thenReturn(stored); + + String request = "[{\"callGroupType\":\"Inbound\",\"createdBy\":\"admin\",\"callType1\":" + + "[{\"calltype\":\"Medical Advice\",\"providerServiceMapID\":4001," + + "\"callTypeDesc1\":[\"General\",\"Urgent\"],\"fitToBlock1\":[\"true\",\"false\"]," + + "\"fitForFollowup1\":[true,false],\"isInbound1\":[true,true]," + + "\"isOutbound1\":[false,false]}]}]"; + + assertSuccessContaining(controller.saveCallTypeData(request), "Medical Advice"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(calltypeinter).saveCallList(captor.capture()); + List saved = captor.getValue(); + assertEquals(2, saved.size(), "each description must become its own call type"); + assertEquals("Inbound", saved.get(0).getCallGroupType()); + assertEquals("General", saved.get(0).getCallTypeDesc()); + assertTrue(saved.get(0).getFitToBlock()); + assertEquals(4001, saved.get(0).getProviderServiceMapID()); + } + + @Test + @DisplayName("saveCallTypeData should answer an error envelope for a request it cannot read") + void saveCallTypeData_shouldAnswerErrorEnvelopeForUnreadableRequest() { + assertCodeException(controller.saveCallTypeData("[{\"callGroupType\":\"Inbound\"}]")); + } + + @Test + @DisplayName("createCalltypeData should answer the call types the service stored") + void createCalltypeData_shouldAnswerStoredCallTypes() { + ArrayList stored = new ArrayList<>(List.of(callType(51, "Medical Advice"))); + when(calltypeinter.createCalltype(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createCalltypeData("[{\"callType\":\"Medical Advice\"}]"), + "Medical Advice"); + } + + @Test + @DisplayName("createCalltypeData should answer an error envelope when the store fails") + void createCalltypeData_shouldAnswerErrorEnvelopeOnFailure() { + when(calltypeinter.createCalltype(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createCalltypeData("[{\"callType\":\"Medical Advice\"}]")); + } + + @Test + @DisplayName("getCallTypeData should answer the call types configured for the mapping") + void getCallTypeData_shouldAnswerConfiguredCallTypes() { + ArrayList stored = new ArrayList<>(List.of(callType(51, "Medical Advice"))); + when(calltypeinter.getCalltypeData(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getCallTypeData("{\"providerServiceMapID\":4001}"), "Medical Advice"); + } + + @Test + @DisplayName("getCallTypeData should answer an error envelope when the lookup fails") + void getCallTypeData_shouldAnswerErrorEnvelopeOnFailure() { + when(calltypeinter.getCalltypeData(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getCallTypeData("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("updateCallTypeData should copy the edited fields onto the stored call type") + void updateCallTypeData_shouldCopyEditedFields() { + M_Calltype stored = callType(51, "old name"); + when(calltypeinter.updateCallType(51)).thenReturn(stored); + when(calltypeinter.saveupdatedData(stored)).thenReturn(stored); + + String response = controller.updateCallTypeData("{\"callTypeID\":51,\"callType\":\"Medical Advice\"," + + "\"callGroupType\":\"Inbound\",\"callTypeDesc\":\"General\",\"providerServiceMapID\":4001," + + "\"fitToBlock\":true,\"fitForFollowup\":false,\"isInbound\":true,\"isOutbound\":false," + + "\"processed\":\"N\",\"maxRedial\":3}"); + + assertSuccessContaining(response, "Medical Advice"); + assertEquals("Inbound", stored.getCallGroupType()); + assertEquals(3, stored.getMaxRedial()); + } + + @Test + @DisplayName("updateCallTypeData should answer an error envelope for a call type that does not exist") + void updateCallTypeData_shouldAnswerErrorEnvelopeForUnknownCallType() { + when(calltypeinter.updateCallType(51)).thenReturn(null); + + assertCodeException(controller.updateCallTypeData("{\"callTypeID\":51}")); + } + + @Test + @DisplayName("deleteCallType should mark the call type deleted and answer what was saved") + void deleteCallType_shouldMarkCallTypeDeleted() { + M_Calltype stored = callType(51, "Medical Advice"); + when(calltypeinter.updateCallType(51)).thenReturn(stored); + when(calltypeinter.saveupdatedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteCallType("{\"callTypeID\":51,\"deleted\":true}"), "Medical Advice"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteCallType should answer an error envelope for a call type that does not exist") + void deleteCallType_shouldAnswerErrorEnvelopeForUnknownCallType() { + when(calltypeinter.updateCallType(51)).thenReturn(null); + + assertCodeException(controller.deleteCallType("{\"callTypeID\":51,\"deleted\":true}")); + } + + @Test + @DisplayName("saveSubServiceData should flatten the nested request onto the provider mapping") + void saveSubServiceData_shouldFlattenNestedRequest() { + ArrayList stored = new ArrayList<>(List.of(subService(61, "Counselling"))); + when(subServiceInter.saveSubList(anyList())).thenReturn(stored); + + String request = "[{\"providerServiceMapID\":4001,\"createdBy\":\"admin\",\"subServiceDetails\":" + + "[{\"subServiceName\":\"Counselling\",\"subServiceDesc\":\"Mental health\"}]}]"; + + assertSuccessContaining(controller.saveSubServiceData(request), "Counselling"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(subServiceInter).saveSubList(captor.capture()); + assertEquals(1, captor.getValue().size()); + assertEquals(4001, captor.getValue().get(0).getProviderServiceMapID()); + assertEquals("admin", captor.getValue().get(0).getCreatedBy()); + } + + @Test + @DisplayName("saveSubServiceData should answer an error envelope for a request it cannot read") + void saveSubServiceData_shouldAnswerErrorEnvelopeForUnreadableRequest() { + assertCodeException(controller.saveSubServiceData("[{\"providerServiceMapID\":4001}]")); + } + + @Test + @DisplayName("FindSubSeriveNameByMapId should answer the master sub-services for the service") + void findSubSeriveNameByMapId_shouldAnswerMasterSubServices() { + M_SubservicemasterPA master = new M_SubservicemasterPA(); + master.setSubServiceMasterID(71); + master.setSubServiceName("Counselling"); + ArrayList stored = new ArrayList<>(List.of(master)); + when(subServiceInter.getServiceNameByServiceID(3)).thenReturn(stored); + + assertSuccessContaining(controller.FindSubSeriveNameByMapId("{\"serviceID\":3}"), "Counselling"); + } + + @Test + @DisplayName("FindSubSeriveNameByMapId should answer an error envelope when the lookup fails") + void findSubSeriveNameByMapId_shouldAnswerErrorEnvelopeOnFailure() { + when(subServiceInter.getServiceNameByServiceID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.FindSubSeriveNameByMapId("{\"serviceID\":3}")); + } + + @Test + @DisplayName("getSubSeriveName should answer the sub-services configured for the mapping") + void getSubSeriveName_shouldAnswerConfiguredSubServices() { + ArrayList stored = new ArrayList<>(List.of(subService(61, "Counselling"))); + when(subServiceInter.getsubServiceName(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getSubSeriveName("{\"providerServiceMapID\":4001}"), "Counselling"); + } + + @Test + @DisplayName("getSubSeriveName should answer an error envelope when the lookup fails") + void getSubSeriveName_shouldAnswerErrorEnvelopeOnFailure() { + when(subServiceInter.getsubServiceName(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getSubSeriveName("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("updateSubSerive should copy the edited fields onto the stored sub-service") + void updateSubSerive_shouldCopyEditedFields() { + M_Subservice stored = subService(61, "old name"); + when(subServiceInter.getsubServiceNameById(61)).thenReturn(stored); + when(subServiceInter.saveupdatedData(stored)).thenReturn(stored); + + String response = controller.updateSubSerive("{\"subServiceID\":61,\"subServiceName\":\"Counselling\"," + + "\"subServiceDesc\":\"Mental health\",\"providerServiceMapID\":4001,\"processed\":\"N\"}"); + + assertSuccessContaining(response, "Counselling"); + assertEquals("Mental health", stored.getSubServiceDesc()); + verify(subServiceInter).saveupdatedData(stored); + } + + @Test + @DisplayName("updateSubSerive should answer an error envelope for a sub-service that does not exist") + void updateSubSerive_shouldAnswerErrorEnvelopeForUnknownSubService() { + when(subServiceInter.getsubServiceNameById(61)).thenReturn(null); + + assertCodeException(controller.updateSubSerive("{\"subServiceID\":61}")); + } + + @Test + @DisplayName("deleteSubSerive should mark the sub-service deleted and answer what was saved") + void deleteSubSerive_shouldMarkSubServiceDeleted() { + M_Subservice stored = subService(61, "Counselling"); + when(subServiceInter.getsubServiceNameById(61)).thenReturn(stored); + when(subServiceInter.saveupdatedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteSubSerive("{\"subServiceID\":61,\"deleted\":true}"), "Counselling"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteSubSerive should answer an error envelope for a sub-service that does not exist") + void deleteSubSerive_shouldAnswerErrorEnvelopeForUnknownSubService() { + when(subServiceInter.getsubServiceNameById(61)).thenReturn(null); + + assertCodeException(controller.deleteSubSerive("{\"subServiceID\":61,\"deleted\":true}")); + } + + @Test + @DisplayName("saveupdatedData should be reached with the resolved record rather than the request") + void updateSubSerive_shouldSaveTheResolvedRecord() { + M_Subservice stored = subService(61, "old name"); + when(subServiceInter.getsubServiceNameById(61)).thenReturn(stored); + when(subServiceInter.saveupdatedData(any())).thenReturn(stored); + + controller.updateSubSerive("{\"subServiceID\":61,\"subServiceName\":\"Counselling\"}"); + + verify(subServiceInter).saveupdatedData(stored); + } +} diff --git a/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardCategoryControllerTest.java b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardCategoryControllerTest.java new file mode 100644 index 0000000..11e27da --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardCategoryControllerTest.java @@ -0,0 +1,688 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.provideronboard; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import com.iemr.admin.data.provideronboard.M_Category; +import com.iemr.admin.data.provideronboard.M_Feedbacknature; +import com.iemr.admin.data.provideronboard.M_Feedbacktype; +import com.iemr.admin.data.provideronboard.M_Severity; +import com.iemr.admin.data.provideronboard.M_Subcategory; +import com.iemr.admin.data.provideronboard.V_Showsubcategory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The category, severity and feedback endpoints define how a provider's agents + * classify a call once it has been taken. + */ +@DisplayName("ProviderOnBoardController category Test Suite") +class ProviderOnBoardCategoryControllerTest extends ProviderOnBoardFixture { + + private static final String SUBCAT_REQUEST = "{\"categoryID\":81,\"createdBy\":\"admin\",\"subcatArray\":" + + "[{\"subCategoryName\":\"Fever\",\"subCategoryDesc\":\"High temperature\"," + + "\"subCatFilePath\":\"/fever\"}]}"; + + private static M_Category category(Integer id, String name) { + M_Category category = new M_Category(); + category.setCategoryID(id); + category.setCategoryName(name); + return category; + } + + private static M_Subcategory subCategory(Integer id, String name) { + M_Subcategory subCategory = new M_Subcategory(); + subCategory.setSubCategoryID(id); + subCategory.setSubCategoryName(name); + return subCategory; + } + + @Test + @DisplayName("saveCategory should attach the sub-categories to the category the service resolves") + void saveCategory_shouldAttachSubCategoriesToResolvedCategory() { + ArrayList stored = new ArrayList<>(List.of(subCategory(91, "Fever"))); + when(categoryInter.getCategoryId(any())).thenReturn(81); + when(categoryInter.saveSubCatData(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.saveCategory(SUBCAT_REQUEST), "Fever"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(categoryInter).saveSubCatData(captor.capture()); + assertEquals(81, captor.getValue().get(0).getCategoryID()); + assertEquals("admin", captor.getValue().get(0).getCreatedBy()); + } + + @Test + @DisplayName("saveCategory should answer an error envelope when no sub-categories are named") + void saveCategory_shouldAnswerErrorEnvelopeWithoutSubCategories() { + when(categoryInter.getCategoryId(any())).thenReturn(81); + + assertCodeException(controller.saveCategory("{\"categoryID\":81}")); + } + + @Test + @DisplayName("saveCategoryUseExist should attach the sub-categories to the category the caller names") + void saveCategoryUseExist_shouldAttachSubCategoriesToNamedCategory() { + ArrayList stored = new ArrayList<>(List.of(subCategory(91, "Fever"))); + when(categoryInter.saveSubCatData(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.saveCategoryUseExist(SUBCAT_REQUEST), "Fever"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(categoryInter).saveSubCatData(captor.capture()); + assertEquals(81, captor.getValue().get(0).getCategoryID()); + } + + @Test + @DisplayName("saveCategoryUseExist should answer an error envelope when no sub-categories are named") + void saveCategoryUseExist_shouldAnswerErrorEnvelopeWithoutSubCategories() { + assertCodeException(controller.saveCategoryUseExist("{\"categoryID\":81}")); + } + + @Test + @DisplayName("getCategoryBySubServiceID should answer the categories under the sub-service") + void getCategoryBySubServiceID_shouldAnswerCategories() { + V_Showsubcategory view = new V_Showsubcategory(); + view.setSubCategoryID(91); + view.setSubCategoryName("Fever"); + ArrayList stored = new ArrayList<>(List.of(view)); + when(categoryInter.getCategoryByMapIDAndSubServiceID(4001, 61)).thenReturn(stored); + + assertSuccessContaining( + controller.getCategoryBySubServiceID("{\"providerServiceMapID\":4001,\"subServiceID\":61}"), "Fever"); + } + + @Test + @DisplayName("getCategoryBySubServiceID should answer an error envelope when the lookup fails") + void getCategoryBySubServiceID_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getCategoryByMapIDAndSubServiceID(anyInt(), anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure( + controller.getCategoryBySubServiceID("{\"providerServiceMapID\":4001,\"subServiceID\":61}")); + } + + @Test + @DisplayName("getsubCategory should answer the sub-categories of the category") + void getsubCategory_shouldAnswerSubCategories() { + ArrayList stored = new ArrayList<>(List.of(subCategory(91, "Fever"))); + when(categoryInter.getCategory(81)).thenReturn(stored); + + assertSuccessContaining(controller.getsubCategory("{\"categoryID\":81}"), "Fever"); + } + + @Test + @DisplayName("getsubCategory should answer an error envelope when the lookup fails") + void getsubCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getCategory(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getsubCategory("{\"categoryID\":81}")); + } + + @Test + @DisplayName("getCategory should answer the categories under the sub-service and mapping") + void getCategory_shouldAnswerCategories() { + ArrayList stored = new ArrayList<>(List.of(category(81, "Medical"))); + when(categoryInter.getAllCategory(61, 4001)).thenReturn(stored); + + assertSuccessContaining(controller.getCategory("{\"subServiceID\":61,\"providerServiceMapID\":4001}"), + "Medical"); + } + + @Test + @DisplayName("getCategory should answer an error envelope when the lookup fails") + void getCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getAllCategory(anyInt(), anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getCategory("{\"subServiceID\":61,\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("updateSubCategory should copy the edited fields onto the stored sub-category") + void updateSubCategory_shouldCopyEditedFields() { + M_Subcategory stored = subCategory(91, "old name"); + when(categoryInter.getSubCategory(91)).thenReturn(stored); + when(categoryInter.updateSubCatData(stored)).thenReturn(stored); + + String response = controller.updateSubCategory("{\"subCategoryID\":91,\"categoryID\":81," + + "\"subCategoryName\":\"Fever\",\"subCategoryDesc\":\"High temperature\"," + + "\"subCatFilePath\":\"/fever\"}"); + + assertSuccessContaining(response, "Fever"); + assertEquals("High temperature", stored.getSubCategoryDesc()); + assertEquals("/fever", stored.getSubCatFilePath()); + } + + @Test + @DisplayName("updateSubCategory should answer an error envelope for a sub-category that does not exist") + void updateSubCategory_shouldAnswerErrorEnvelopeForUnknownSubCategory() { + when(categoryInter.getSubCategory(91)).thenReturn(null); + + assertCodeException(controller.updateSubCategory("{\"subCategoryID\":91}")); + } + + @Test + @DisplayName("createCategory should answer the categories the service stored") + void createCategory_shouldAnswerStoredCategories() { + ArrayList stored = new ArrayList<>(List.of(category(81, "Medical"))); + when(categoryInter.createcat(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createCategory("[{\"categoryName\":\"Medical\"}]"), "Medical"); + } + + @Test + @DisplayName("createCategory should answer an error envelope when the store fails") + void createCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.createcat(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createCategory("[{\"categoryName\":\"Medical\"}]")); + } + + @Test + @DisplayName("deleteCategory1 should mark the category deleted and answer what was saved") + void deleteCategory1_shouldMarkCategoryDeleted() { + M_Category stored = category(81, "Medical"); + when(categoryInter.getcatdatabycatId(81)).thenReturn(stored); + when(categoryInter.deletedata(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteCategory1("{\"categoryID\":81,\"deleted\":true}"), "Medical"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteCategory1 should answer an error envelope for a category that does not exist") + void deleteCategory1_shouldAnswerErrorEnvelopeForUnknownCategory() { + when(categoryInter.getcatdatabycatId(81)).thenReturn(null); + + assertCodeException(controller.deleteCategory1("{\"categoryID\":81,\"deleted\":true}")); + } + + @Test + @DisplayName("createSubCategory should answer the sub-categories the service stored") + void createSubCategory_shouldAnswerStoredSubCategories() { + ArrayList stored = new ArrayList<>(List.of(subCategory(91, "Fever"))); + when(categoryInter.createSubCategory(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createSubCategory("[{\"subCategoryName\":\"Fever\"}]"), "Fever"); + } + + @Test + @DisplayName("createSubCategory should answer an error envelope when the store fails") + void createSubCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.createSubCategory(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createSubCategory("[{\"subCategoryName\":\"Fever\"}]")); + } + + @Test + @DisplayName("deleteSubCategory should mark the sub-category deleted and answer what was saved") + void deleteSubCategory_shouldMarkSubCategoryDeleted() { + M_Subcategory stored = subCategory(91, "Fever"); + when(categoryInter.getSubCategory(91)).thenReturn(stored); + when(categoryInter.updateSubCatData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteSubCategory("{\"subCategoryID\":91,\"deleted\":true}"), "Fever"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteSubCategory should answer an error envelope for a sub-category that does not exist") + void deleteSubCategory_shouldAnswerErrorEnvelopeForUnknownSubCategory() { + when(categoryInter.getSubCategory(91)).thenReturn(null); + + assertCodeException(controller.deleteSubCategory("{\"subCategoryID\":91,\"deleted\":true}")); + } + + @Test + @DisplayName("getSubCategory should answer the sub-category view the service resolves") + void getSubCategory_shouldAnswerSubCategoryView() { + V_Showsubcategory view = new V_Showsubcategory(); + view.setSubCategoryID(91); + view.setSubCategoryName("Fever"); + ArrayList stored = new ArrayList<>(List.of(view)); + when(categoryInter.getSubCategory1(91)).thenReturn(stored); + + assertSuccessContaining(controller.getSubCategory("{\"subCategoryID\":91}"), "Fever"); + } + + @Test + @DisplayName("getSubCategory should answer an error envelope when the lookup fails") + void getSubCategory_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getSubCategory1(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getSubCategory("{\"subCategoryID\":91}")); + } + + @Test + @DisplayName("updateCategory should copy the edited fields onto the stored category") + void updateCategory_shouldCopyEditedFields() { + M_Category stored = category(81, "old name"); + when(categoryInter.getcatdatabycatId(81)).thenReturn(stored); + when(categoryInter.deletedata(stored)).thenReturn(stored); + + String response = controller.updateCategory("{\"categoryID\":81,\"categoryName\":\"Medical\"," + + "\"categoryDesc\":\"Clinical calls\",\"modifiedBy\":\"admin\",\"s104_CS_Type\":true}"); + + assertSuccessContaining(response, "Medical"); + assertEquals("Clinical calls", stored.getCategoryDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("updateCategory should answer an error envelope for a category that does not exist") + void updateCategory_shouldAnswerErrorEnvelopeForUnknownCategory() { + when(categoryInter.getcatdatabycatId(81)).thenReturn(null); + + assertCodeException(controller.updateCategory("{\"categoryID\":81}")); + } + + @Test + @DisplayName("mapCategorytoFeedbackNature should map every category the request names") + void mapCategorytoFeedbackNature_shouldMapEveryCategory() { + when(categoryInter.updateCategory(81, 21)).thenReturn(1); + when(categoryInter.updateCategory(82, 22)).thenReturn(1); + + String response = controller.mapCategorytoFeedbackNature( + "[{\"categoryID\":81,\"feedbackNatureID\":21},{\"categoryID\":82,\"feedbackNatureID\":22}]"); + + assertSuccessContaining(response, "inserted"); + verify(categoryInter).updateCategory(81, 21); + verify(categoryInter).updateCategory(82, 22); + } + + @Test + @DisplayName("mapCategorytoFeedbackNature should answer an error envelope when the mapping fails") + void mapCategorytoFeedbackNature_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.updateCategory(anyInt(), anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.mapCategorytoFeedbackNature("[{\"categoryID\":81,\"feedbackNatureID\":21}]")); + } + + @Test + @DisplayName("updateCategorytoFeedbackNature should clear the old category before mapping the new one") + void updateCategorytoFeedbackNature_shouldClearOldCategoryFirst() { + M_Category previous = category(80, "Previous"); + previous.setFeedbackNatureID(21); + M_Category current = category(81, "Medical"); + when(categoryInter.getcatdatabycatId(80)).thenReturn(previous); + when(categoryInter.getcatdatabycatId(81)).thenReturn(current); + when(categoryInter.deletedata(any())).thenReturn(current); + + String response = controller.updateCategorytoFeedbackNature("{\"oldCategoryID\":80,\"categoryID\":81," + + "\"feedbackNatureID\":22,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "Medical"); + assertNull(previous.getFeedbackNatureID(), "the old category must stop pointing at a feedback nature"); + assertEquals(22, current.getFeedbackNatureID()); + } + + @Test + @DisplayName("updateCategorytoFeedbackNature should answer an error envelope for an unknown old category") + void updateCategorytoFeedbackNature_shouldAnswerErrorEnvelopeForUnknownOldCategory() { + when(categoryInter.getcatdatabycatId(anyInt())).thenReturn(null); + + assertCodeException( + controller.updateCategorytoFeedbackNature("{\"oldCategoryID\":80,\"categoryID\":81}")); + } + + @Test + @DisplayName("getmapedCategorytoFeedbackNature should answer the categories mapped to the feedback nature") + void getmapedCategorytoFeedbackNature_shouldAnswerMappedCategories() { + ArrayList stored = new ArrayList<>(List.of(category(81, "Medical"))); + when(categoryInter.getAllCategorywithFeedbackNatureID(4001, 21)).thenReturn(stored); + + assertSuccessContaining(controller.getmapedCategorytoFeedbackNatureWithCatIDandFeedbackNatureID( + "{\"providerServiceMapID\":4001,\"feedbackNatureID\":21}"), "Medical"); + } + + @Test + @DisplayName("getmapedCategorytoFeedbackNature should answer an error envelope when the lookup fails") + void getmapedCategorytoFeedbackNature_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getAllCategorywithFeedbackNatureID(anyInt(), anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getmapedCategorytoFeedbackNatureWithCatIDandFeedbackNatureID( + "{\"providerServiceMapID\":4001,\"feedbackNatureID\":21}")); + } + + @Test + @DisplayName("getunmappedCategoryforFeedbackNature should answer the categories still unmapped") + void getunmappedCategoryforFeedbackNature_shouldAnswerUnmappedCategories() { + ArrayList stored = new ArrayList<>(List.of(category(81, "Medical"))); + when(categoryInter.getUpmappedCategory(4001)).thenReturn(stored); + + assertSuccessContaining( + controller.getunmappedCategoryforFeedbackNature("{\"providerServiceMapID\":4001}"), "Medical"); + } + + @Test + @DisplayName("getunmappedCategoryforFeedbackNature should answer an error envelope when the lookup fails") + void getunmappedCategoryforFeedbackNature_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getUpmappedCategory(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getunmappedCategoryforFeedbackNature("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("unmappCategoryforFeedbackNature should clear the feedback nature from the category") + void unmappCategoryforFeedbackNature_shouldClearFeedbackNature() { + M_Category stored = category(81, "Medical"); + stored.setFeedbackNatureID(21); + when(categoryInter.getcatdatabycatId(81)).thenReturn(stored); + when(categoryInter.deletedata(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.unmappCategoryforFeedbackNature("{\"categoryID\":81,\"modifiedBy\":\"admin\"}"), "Medical"); + assertNull(stored.getFeedbackNatureID()); + } + + @Test + @DisplayName("unmappCategoryforFeedbackNature should answer an error envelope for an unknown category") + void unmappCategoryforFeedbackNature_shouldAnswerErrorEnvelopeForUnknownCategory() { + when(categoryInter.getcatdatabycatId(81)).thenReturn(null); + + assertCodeException(controller.unmappCategoryforFeedbackNature("{\"categoryID\":81}")); + } + + @Test + @DisplayName("getAllCategoryPsmMapid should answer every category under the mapping") + void getAllCategoryPsmMapid_shouldAnswerEveryCategory() { + ArrayList stored = new ArrayList<>(List.of(category(81, "Medical"))); + when(categoryInter.getAllCategory1(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getAllCategoryPsmMapid("{\"providerServiceMapID\":4001}"), "Medical"); + } + + @Test + @DisplayName("getAllCategoryPsmMapid should answer an error envelope when the lookup fails") + void getAllCategoryPsmMapid_shouldAnswerErrorEnvelopeOnFailure() { + when(categoryInter.getAllCategory1(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllCategoryPsmMapid("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getServerity should answer the severities configured for the mapping") + void getServerity_shouldAnswerConfiguredSeverities() { + M_Severity severity = new M_Severity(); + severity.setSeverityID(31); + severity.setSeverityTypeName("Critical"); + ArrayList stored = new ArrayList<>(List.of(severity)); + when(m_ServerityInter.getServerity(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getServerity("{\"providerServiceMapID\":4001}"), "Critical"); + } + + @Test + @DisplayName("getServerity should answer an error envelope when the lookup fails") + void getServerity_shouldAnswerErrorEnvelopeOnFailure() { + when(m_ServerityInter.getServerity(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getServerity("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("saveServerity should answer the severities the service stored") + void saveServerity_shouldAnswerStoredSeverities() { + M_Severity severity = new M_Severity(); + severity.setSeverityID(31); + severity.setSeverityTypeName("Critical"); + ArrayList stored = new ArrayList<>(List.of(severity)); + when(m_ServerityInter.saveServerity(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.saveServerity("[{\"severityTypeName\":\"Critical\"}]"), "Critical"); + } + + @Test + @DisplayName("saveServerity should answer an error envelope when the store fails") + void saveServerity_shouldAnswerErrorEnvelopeOnFailure() { + when(m_ServerityInter.saveServerity(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveServerity("[{\"severityTypeName\":\"Critical\"}]")); + } + + @Test + @DisplayName("deleteServerity should mark the severity deleted and answer what was saved") + void deleteServerity_shouldMarkSeverityDeleted() { + M_Severity stored = new M_Severity(); + stored.setSeverityID(31); + stored.setSeverityTypeName("Critical"); + when(m_ServerityInter.getDataByServId(31)).thenReturn(stored); + when(m_ServerityInter.deletedataser(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteServerity("{\"severityID\":31,\"deleted\":true}"), "Critical"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteServerity should answer an error envelope for a severity that does not exist") + void deleteServerity_shouldAnswerErrorEnvelopeForUnknownSeverity() { + when(m_ServerityInter.getDataByServId(31)).thenReturn(null); + + assertCodeException(controller.deleteServerity("{\"severityID\":31,\"deleted\":true}")); + } + + @Test + @DisplayName("editServerity should copy the edited fields onto the stored severity") + void editServerity_shouldCopyEditedFields() { + M_Severity stored = new M_Severity(); + stored.setSeverityID(31); + when(m_ServerityInter.getDataByServId(31)).thenReturn(stored); + when(m_ServerityInter.deletedataser(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editServerity("{\"severityID\":31,\"severityTypeName\":\"Critical\"," + + "\"severityDesc\":\"Needs escalation\"}"), "Critical"); + assertEquals("Needs escalation", stored.getSeverityDesc()); + } + + @Test + @DisplayName("editServerity should answer an error envelope for a severity that does not exist") + void editServerity_shouldAnswerErrorEnvelopeForUnknownSeverity() { + when(m_ServerityInter.getDataByServId(31)).thenReturn(null); + + assertCodeException(controller.editServerity("{\"severityID\":31}")); + } + + @Test + @DisplayName("getFeedbackType should answer the feedback types configured for the mapping") + void getFeedbackType_shouldAnswerConfiguredFeedbackTypes() { + M_Feedbacktype feedbackType = new M_Feedbacktype(); + feedbackType.setFeedbackTypeID(41); + feedbackType.setFeedbackTypeName("Complaint"); + ArrayList stored = new ArrayList<>(List.of(feedbackType)); + when(m_FeedbacktypeInter.getFeedbackt(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getFeedbackType("{\"providerServiceMapID\":4001}"), "Complaint"); + } + + @Test + @DisplayName("getFeedbackType should answer an error envelope when the lookup fails") + void getFeedbackType_shouldAnswerErrorEnvelopeOnFailure() { + when(m_FeedbacktypeInter.getFeedbackt(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFeedbackType("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("saveFeedbackType should answer the feedback types the service stored") + void saveFeedbackType_shouldAnswerStoredFeedbackTypes() { + M_Feedbacktype feedbackType = new M_Feedbacktype(); + feedbackType.setFeedbackTypeID(41); + feedbackType.setFeedbackTypeName("Complaint"); + ArrayList stored = new ArrayList<>(List.of(feedbackType)); + when(m_FeedbacktypeInter.saveFeedbackType(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.saveFeedbackType("[{\"feedbackTypeName\":\"Complaint\"}]"), "Complaint"); + } + + @Test + @DisplayName("saveFeedbackType should answer an error envelope when the store fails") + void saveFeedbackType_shouldAnswerErrorEnvelopeOnFailure() { + when(m_FeedbacktypeInter.saveFeedbackType(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveFeedbackType("[{\"feedbackTypeName\":\"Complaint\"}]")); + } + + @Test + @DisplayName("editFeedbackType should copy the edited fields onto the stored feedback type") + void editFeedbackType_shouldCopyEditedFields() { + M_Feedbacktype stored = new M_Feedbacktype(); + stored.setFeedbackTypeID(41); + when(m_FeedbacktypeInter.getDataByServId(41)).thenReturn(stored); + when(m_FeedbacktypeInter.deletedataser(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editFeedbackType("{\"feedbackTypeID\":41," + + "\"feedbackTypeName\":\"Complaint\",\"feedbackDesc\":\"Service issue\"," + + "\"modifiedBy\":\"admin\"}"), "Complaint"); + assertEquals("Service issue", stored.getFeedbackDesc()); + } + + @Test + @DisplayName("editFeedbackType should answer an error envelope for a feedback type that does not exist") + void editFeedbackType_shouldAnswerErrorEnvelopeForUnknownFeedbackType() { + when(m_FeedbacktypeInter.getDataByServId(41)).thenReturn(null); + + assertCodeException(controller.editFeedbackType("{\"feedbackTypeID\":41}")); + } + + @Test + @DisplayName("deleteFeedbackType should mark the feedback type deleted") + void deleteFeedbackType_shouldMarkFeedbackTypeDeleted() { + M_Feedbacktype stored = new M_Feedbacktype(); + stored.setFeedbackTypeID(41); + stored.setFeedbackTypeName("Complaint"); + when(m_FeedbacktypeInter.getDataByServId(41)).thenReturn(stored); + when(m_FeedbacktypeInter.deletedataser(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteFeedbackType("{\"feedbackTypeID\":41,\"deleted\":true}"), + "Complaint"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteFeedbackType should answer an error envelope for a feedback type that does not exist") + void deleteFeedbackType_shouldAnswerErrorEnvelopeForUnknownFeedbackType() { + when(m_FeedbacktypeInter.getDataByServId(41)).thenReturn(null); + + assertCodeException(controller.deleteFeedbackType("{\"feedbackTypeID\":41,\"deleted\":true}")); + } + + @Test + @DisplayName("getFeedbackNatureType should answer the natures under the feedback type") + void getFeedbackNatureType_shouldAnswerNatures() { + M_Feedbacknature nature = new M_Feedbacknature(); + nature.setFeedbackNatureID(21); + nature.setFeedbackNature("Escalation"); + ArrayList stored = new ArrayList<>(List.of(nature)); + when(m_FeedbacknatureInteger.getFeedbackNatureType(41)).thenReturn(stored); + + assertSuccessContaining(controller.getFeedbackNatureType("{\"feedbackTypeID\":41}"), "Escalation"); + } + + @Test + @DisplayName("getFeedbackNatureType should answer an error envelope when the lookup fails") + void getFeedbackNatureType_shouldAnswerErrorEnvelopeOnFailure() { + when(m_FeedbacknatureInteger.getFeedbackNatureType(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFeedbackNatureType("{\"feedbackTypeID\":41}")); + } + + @Test + @DisplayName("createFeedbackNatureType should answer the natures the service stored") + void createFeedbackNatureType_shouldAnswerStoredNatures() { + M_Feedbacknature nature = new M_Feedbacknature(); + nature.setFeedbackNatureID(21); + nature.setFeedbackNature("Escalation"); + ArrayList stored = new ArrayList<>(List.of(nature)); + when(m_FeedbacknatureInteger.createFeedbackNatueType(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createFeedbackNatureType("[{\"feedbackNature\":\"Escalation\"}]"), + "Escalation"); + } + + @Test + @DisplayName("createFeedbackNatureType should answer an error envelope when the store fails") + void createFeedbackNatureType_shouldAnswerErrorEnvelopeOnFailure() { + when(m_FeedbacknatureInteger.createFeedbackNatueType(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createFeedbackNatureType("[{\"feedbackNature\":\"Escalation\"}]")); + } + + @Test + @DisplayName("deleteFeedbackNatureType should mark the nature deleted") + void deleteFeedbackNatureType_shouldMarkNatureDeleted() { + M_Feedbacknature stored = new M_Feedbacknature(); + stored.setFeedbackNatureID(21); + stored.setFeedbackNature("Escalation"); + when(m_FeedbacknatureInteger.editFeedbackNatureType(21)).thenReturn(stored); + when(m_FeedbacknatureInteger.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteFeedbackNatureType("{\"feedbackNatureID\":21,\"deleted\":true}"), + "Escalation"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteFeedbackNatureType should answer an error envelope for a nature that does not exist") + void deleteFeedbackNatureType_shouldAnswerErrorEnvelopeForUnknownNature() { + when(m_FeedbacknatureInteger.editFeedbackNatureType(21)).thenReturn(null); + + assertCodeException(controller.deleteFeedbackNatureType("{\"feedbackNatureID\":21,\"deleted\":true}")); + } + + @Test + @DisplayName("editFeedbackNatureType should copy the edited fields onto the stored nature") + void editFeedbackNatureType_shouldCopyEditedFields() { + M_Feedbacknature stored = new M_Feedbacknature(); + stored.setFeedbackNatureID(21); + when(m_FeedbacknatureInteger.editFeedbackNatureType(21)).thenReturn(stored); + when(m_FeedbacknatureInteger.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editFeedbackNatureType("{\"feedbackNatureID\":21," + + "\"feedbackNature\":\"Escalation\",\"feedbackNatureDesc\":\"Raise to supervisor\"," + + "\"modifiedBy\":\"admin\"}"), "Escalation"); + assertEquals("Raise to supervisor", stored.getFeedbackNatureDesc()); + } + + @Test + @DisplayName("editFeedbackNatureType should answer an error envelope for a nature that does not exist") + void editFeedbackNatureType_shouldAnswerErrorEnvelopeForUnknownNature() { + when(m_FeedbacknatureInteger.editFeedbackNatureType(21)).thenReturn(null); + + assertCodeException(controller.editFeedbackNatureType("{\"feedbackNatureID\":21}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardDrugControllerTest.java b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardDrugControllerTest.java new file mode 100644 index 0000000..d52347d --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardDrugControllerTest.java @@ -0,0 +1,304 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.provideronboard; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.provideronboard.M_104druggroup; +import com.iemr.admin.data.provideronboard.M_104drugmapping; +import com.iemr.admin.data.provideronboard.M_104drugmaster; +import com.iemr.admin.utils.exception.IEMRException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** The drug endpoints keep the 104 helpline's drug catalogue for a provider. */ +@DisplayName("ProviderOnBoardController drug Test Suite") +class ProviderOnBoardDrugControllerTest extends ProviderOnBoardFixture { + + private static M_104drugmaster drug(Integer id, String name) { + M_104drugmaster drug = new M_104drugmaster(); + drug.setDrugID(id); + drug.setDrugName(name); + return drug; + } + + private static M_104druggroup drugGroup(Integer id, String name) { + M_104druggroup group = new M_104druggroup(); + group.setDrugGroupID(id); + group.setDrugGroup(name); + return group; + } + + private static M_104drugmapping drugMapping(Integer id, String drugName) { + M_104drugmapping mapping = new M_104drugmapping(); + mapping.setDrugMapID(id); + mapping.setDrugName(drugName); + return mapping; + } + + @Test + @DisplayName("getDrugData should answer the drugs the catalogue holds") + void getDrugData_shouldAnswerCatalogueDrugs() throws IEMRException { + ArrayList stored = new ArrayList<>(List.of(drug(101, "Paracetamol"))); + when(drugMasterInter.getAllDrugData(101, (short) 77, Boolean.FALSE)).thenReturn(stored); + + assertSuccessContaining( + controller.getDrugData("{\"drugID\":101,\"serviceProviderID\":77,\"deleted\":false}"), + "Paracetamol"); + } + + @Test + @DisplayName("getDrugData should answer an error envelope when the catalogue cannot be read") + void getDrugData_shouldAnswerErrorEnvelopeOnFailure() throws IEMRException { + when(drugMasterInter.getAllDrugData(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getDrugData("{\"drugID\":101,\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getDrugGroups should answer the drug groups the catalogue holds") + void getDrugGroups_shouldAnswerCatalogueGroups() throws IEMRException { + ArrayList stored = new ArrayList<>(List.of(drugGroup(201, "Analgesics"))); + when(drugMasterInter.getAllDrugGroups(201, (short) 77, Boolean.FALSE)).thenReturn(stored); + + assertSuccessContaining( + controller.getDrugGroups("{\"drugGroupID\":201,\"serviceProviderID\":77,\"deleted\":false}"), + "Analgesics"); + } + + @Test + @DisplayName("getDrugGroups should answer an error envelope when the catalogue cannot be read") + void getDrugGroups_shouldAnswerErrorEnvelopeOnFailure() throws IEMRException { + when(drugMasterInter.getAllDrugGroups(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getDrugGroups("{\"drugGroupID\":201,\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getDrugGroupMappings should answer every mapping for the provider's service") + void getDrugGroupMappings_shouldAnswerMappings() throws IEMRException { + ArrayList stored = new ArrayList<>(List.of(drugMapping(301, "Paracetamol"))); + when(drugMasterInter.getAllDrugGroupMappings(null, 77, 3)).thenReturn(stored); + + assertSuccessContaining( + controller.getDrugGroupMappings("{\"serviceProviderID\":77,\"serviceID\":3}"), "Paracetamol"); + } + + @Test + @DisplayName("getDrugGroupMappings should answer an error envelope when the lookup fails") + void getDrugGroupMappings_shouldAnswerErrorEnvelopeOnFailure() throws IEMRException { + when(drugMasterInter.getAllDrugGroupMappings(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getDrugGroupMappings("{\"serviceProviderID\":77,\"serviceID\":3}")); + } + + @Test + @DisplayName("updateDrugGroup should copy the edited fields onto the stored group") + void updateDrugGroup_shouldCopyEditedFields() { + M_104druggroup stored = drugGroup(201, "old name"); + when(drugMasterInter.getDrugGroupById(201)).thenReturn(stored); + when(drugMasterInter.saveUpdatedDrugGroup(stored)).thenReturn(stored); + + assertSuccessContaining(controller.updateDrugGroup("{\"drugGroupID\":201,\"drugGroup\":\"Analgesics\"," + + "\"drugGroupDesc\":\"Pain relief\",\"modifiedBy\":\"admin\"}"), "Analgesics"); + assertEquals("Pain relief", stored.getDrugGroupDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("updateDrugGroup should answer an error envelope for a group that does not exist") + void updateDrugGroup_shouldAnswerErrorEnvelopeForUnknownGroup() { + when(drugMasterInter.getDrugGroupById(201)).thenReturn(null); + + assertCodeException(controller.updateDrugGroup("{\"drugGroupID\":201}")); + } + + @Test + @DisplayName("updateDrugMaster should copy the edited fields onto the stored drug") + void updateDrugMaster_shouldCopyEditedFields() { + M_104drugmaster stored = drug(101, "old name"); + when(drugMasterInter.getDrugDataById(101)).thenReturn(stored); + when(drugMasterInter.saveUpdatedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.updateDrugMaster("{\"drugID\":101,\"drugName\":\"Paracetamol\"," + + "\"drugDesc\":\"Antipyretic\",\"remarks\":\"OTC\",\"modifiedBy\":\"admin\"}"), "Paracetamol"); + assertEquals("Antipyretic", stored.getDrugDesc()); + assertEquals("OTC", stored.getRemarks()); + } + + @Test + @DisplayName("updateDrugMaster should answer an error envelope for a drug that does not exist") + void updateDrugMaster_shouldAnswerErrorEnvelopeForUnknownDrug() { + when(drugMasterInter.getDrugDataById(101)).thenReturn(null); + + assertCodeException(controller.updateDrugMaster("{\"drugID\":101}")); + } + + @Test + @DisplayName("updateDrugMapping should copy the edited fields onto the stored mapping") + void updateDrugMapping_shouldCopyEditedFields() { + M_104drugmapping stored = drugMapping(301, "old name"); + when(drugMasterInter.getDrugMappingsById(301)).thenReturn(stored); + when(drugMasterInter.saveUpdatedDrugMapping(stored)).thenReturn(stored); + + assertSuccessContaining(controller.updateDrugMapping("{\"drugMapID\":301,\"drugGroupID\":201," + + "\"drugGroupName\":\"Analgesics\",\"drugId\":101,\"drugName\":\"Paracetamol\"," + + "\"remarks\":\"OTC\"}"), "Paracetamol"); + assertEquals(201, stored.getDrugGroupID()); + assertEquals("Analgesics", stored.getDrugGroupName()); + } + + @Test + @DisplayName("updateDrugMapping should answer an error envelope for a mapping that does not exist") + void updateDrugMapping_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(drugMasterInter.getDrugMappingsById(301)).thenReturn(null); + + assertCodeException(controller.updateDrugMapping("{\"drugMapID\":301}")); + } + + @Test + @DisplayName("updateDrugStatus should retire the drug when the request names one") + void updateDrugStatus_shouldRetireDrug() { + when(drugMasterInter.updateDrugStatus(any())).thenReturn(1); + + assertSuccessContaining(controller.updateDrugStatus("{\"drugID\":101,\"deleted\":true}"), + "Drug status updaated to deleted"); + verify(drugMasterInter).updateDrugStatus(any()); + verify(drugMasterInter, never()).updateDrugGroupStatus(any()); + } + + @Test + @DisplayName("updateDrugStatus should retire the drug group when no drug is named") + void updateDrugStatus_shouldRetireDrugGroup() { + when(drugMasterInter.updateDrugGroupStatus(any())).thenReturn(1); + + assertSuccessContaining(controller.updateDrugStatus("{\"drugGroupID\":201,\"deleted\":true}"), + "DrugGroup status updaated to deleted"); + verify(drugMasterInter).updateDrugGroupStatus(any()); + verify(drugMasterInter, never()).updateDrugStatus(any()); + } + + @Test + @DisplayName("updateDrugStatus should retire the mapping when neither a drug nor a group is named") + void updateDrugStatus_shouldRetireDrugMapping() { + when(drugMasterInter.updateDrugMappingStatus(any())).thenReturn(1); + + assertSuccessContaining(controller.updateDrugStatus("{\"drugMapID\":301,\"deleted\":true}"), + "DrugGroup status updaated to deleted"); + verify(drugMasterInter).updateDrugMappingStatus(any()); + } + + @Test + @DisplayName("updateDrugStatus should answer an empty response when the request names nothing to retire") + void updateDrugStatus_shouldAnswerEmptyResponseWhenNothingNamed() { + assertSuccessContaining(controller.updateDrugStatus("{}"), "response"); + verify(drugMasterInter, never()).updateDrugStatus(any()); + verify(drugMasterInter, never()).updateDrugGroupStatus(any()); + verify(drugMasterInter, never()).updateDrugMappingStatus(any()); + } + + @Test + @DisplayName("updateDrugStatus should answer an error envelope when the retirement fails") + void updateDrugStatus_shouldAnswerErrorEnvelopeOnFailure() { + when(drugMasterInter.updateDrugStatus(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.updateDrugStatus("{\"drugID\":101,\"deleted\":true}")); + } + + @Test + @DisplayName("saveDrugGroup should answer the groups the service stored") + void saveDrugGroup_shouldAnswerStoredGroups() throws IEMRException { + ArrayList stored = new ArrayList<>(List.of(drugGroup(201, "Analgesics"))); + when(drugMasterInter.saveDrugGroup(anyList())).thenReturn(stored); + + assertSuccessContaining( + controller.saveDrugGroup("{\"drugGroups\":[{\"drugGroup\":\"Analgesics\"}]}"), "Analgesics"); + } + + @Test + @DisplayName("saveDrugGroup should answer an error envelope when no groups are named") + void saveDrugGroup_shouldAnswerErrorEnvelopeWithoutGroups() throws IEMRException { + when(drugMasterInter.saveDrugGroup(eq(null))).thenThrow(new IllegalArgumentException("nothing to save")); + + assertGenericFailure(controller.saveDrugGroup("{}")); + } + + @Test + @DisplayName("saveDrug should answer the drugs the service stored") + void saveDrug_shouldAnswerStoredDrugs() throws IEMRException { + ArrayList stored = new ArrayList<>(List.of(drug(101, "Paracetamol"))); + when(drugMasterInter.saveDrugData(anyList())).thenReturn(stored); + + assertSuccessContaining( + controller.saveDrug("{\"drugMasters\":[{\"drugName\":\"Paracetamol\"}]}"), "Paracetamol"); + } + + @Test + @DisplayName("saveDrug should answer an error envelope when the store fails") + void saveDrug_shouldAnswerErrorEnvelopeOnFailure() throws IEMRException { + when(drugMasterInter.saveDrugData(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveDrug("{\"drugMasters\":[{\"drugName\":\"Paracetamol\"}]}")); + } + + @Test + @DisplayName("mapDrugWithGroup should answer the mappings the service stored") + void mapDrugWithGroup_shouldAnswerStoredMappings() throws IEMRException { + ArrayList stored = new ArrayList<>(List.of(drugMapping(301, "Paracetamol"))); + when(drugMasterInter.mapDrugWithGroup(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.mapDrugWithGroup( + "{\"drugMappings\":[{\"drugId\":101,\"drugGroupID\":201}]}"), "Paracetamol"); + } + + @Test + @DisplayName("mapDrugWithGroup should answer an error envelope when the mapping fails") + void mapDrugWithGroup_shouldAnswerErrorEnvelopeOnFailure() throws IEMRException { + when(drugMasterInter.mapDrugWithGroup(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.mapDrugWithGroup("{\"drugMappings\":[{\"drugId\":101}]}")); + } + + @Test + @DisplayName("getDrugData should pass the deleted flag the caller sends straight through") + void getDrugData_shouldPassDeletedFlagThrough() throws IEMRException { + when(drugMasterInter.getAllDrugData(anyInt(), any(), eq(Boolean.TRUE))).thenReturn(new ArrayList<>()); + + controller.getDrugData("{\"drugID\":101,\"serviceProviderID\":77,\"deleted\":true}"); + + verify(drugMasterInter).getAllDrugData(101, (short) 77, Boolean.TRUE); + } +} diff --git a/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardFixture.java b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardFixture.java new file mode 100644 index 0000000..6047603 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardFixture.java @@ -0,0 +1,131 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.provideronboard; + +import org.json.JSONObject; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.service.provideronboard.Calltypeinter; +import com.iemr.admin.service.provideronboard.CategoryInter; +import com.iemr.admin.service.provideronboard.DrugMasterInter; +import com.iemr.admin.service.provideronboard.InstuteDirectoryInter; +import com.iemr.admin.service.provideronboard.M_FeedbacknatureInteger; +import com.iemr.admin.service.provideronboard.M_FeedbacktypeInter; +import com.iemr.admin.service.provideronboard.M_InstitutedirectorymappingInter; +import com.iemr.admin.service.provideronboard.M_InstitutesubdirectoryInter; +import com.iemr.admin.service.provideronboard.M_InstitutionInter; +import com.iemr.admin.service.provideronboard.M_InstitutiontypeInter; +import com.iemr.admin.service.provideronboard.M_ServiceMasterInter; +import com.iemr.admin.service.provideronboard.M_SeverityInter; +import com.iemr.admin.service.provideronboard.ServiceProvider_ServiceImpl; +import com.iemr.admin.service.provideronboard.SubServiceInter; +import com.iemr.admin.service.user.IemrUserServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The onboarding controller reaches thirteen collaborating services, so every + * suite over it shares this fixture rather than re-declaring the same mocks. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +abstract class ProviderOnBoardFixture { + + @Mock + protected M_InstitutedirectorymappingInter m_InstitutedirectorymappingInter; + + @Mock + protected M_InstitutesubdirectoryInter m_InstitutesubdirectoryInter; + + @Mock + protected M_InstitutionInter m_InstitutionInter; + + @Mock + protected M_FeedbacknatureInteger m_FeedbacknatureInteger; + + @Mock + protected M_InstitutiontypeInter m_InstitutiontypeInter; + + @Mock + protected InstuteDirectoryInter instuteDirectoryInter; + + @Mock + protected M_FeedbacktypeInter m_FeedbacktypeInter; + + @Mock + protected M_SeverityInter m_ServerityInter; + + @Mock + protected DrugMasterInter drugMasterInter; + + @Mock + protected CategoryInter categoryInter; + + @Mock + protected SubServiceInter subServiceInter; + + @Mock + protected Calltypeinter calltypeinter; + + @Mock + protected M_ServiceMasterInter m_ServiceMasterInter; + + @Mock + protected ServiceProvider_ServiceImpl serviceProvider_ServiceImpl; + + @Mock + protected IemrUserServiceImpl iemrUserServiceImpl; + + @InjectMocks + protected ProviderOnBoardController controller; + + /** Reads the status code out of the JSON envelope the controller answers with. */ + protected static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + /** Asserts the envelope reports success and carries the given fragment. */ + protected static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + /** Asserts the envelope reports the generic failure the controllers fall back to. */ + protected static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + /** + * Asserts the envelope reports the code-level failure raised when a controller + * works on a record the service could not resolve. + */ + protected static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } +} diff --git a/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardInstituteControllerTest.java b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardInstituteControllerTest.java new file mode 100644 index 0000000..286ca6c --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardInstituteControllerTest.java @@ -0,0 +1,557 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.provideronboard; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.google.gson.JsonObject; +import com.iemr.admin.data.provideronboard.M_Institutedirectory; +import com.iemr.admin.data.provideronboard.M_Institutedirectorymapping; +import com.iemr.admin.data.provideronboard.M_Institutesubdirectory; +import com.iemr.admin.data.provideronboard.M_Institution; +import com.iemr.admin.data.provideronboard.M_Institutiontype; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The institute endpoints keep the referral directory a provider's agents route + * callers to - the directories, their sub-directories, and the institutions + * mapped underneath them. + */ +@DisplayName("ProviderOnBoardController institute Test Suite") +class ProviderOnBoardInstituteControllerTest extends ProviderOnBoardFixture { + + private static M_Institutedirectory directory(Integer id, String name) { + M_Institutedirectory directory = new M_Institutedirectory(); + directory.setInstituteDirectoryID(id); + directory.setInstituteDirectoryName(name); + return directory; + } + + private static M_Institutiontype instituteType(Integer id, String name) { + M_Institutiontype type = new M_Institutiontype(); + type.setInstitutionTypeID(id); + type.setInstitutionType(name); + return type; + } + + private static M_Institution institution(Integer id, String name) { + M_Institution institution = new M_Institution(); + institution.setInstitutionID(id); + institution.setInstitutionName(name); + return institution; + } + + private static M_Institutesubdirectory subDirectory(Integer id, String name) { + M_Institutesubdirectory subDirectory = new M_Institutesubdirectory(); + subDirectory.setInstituteSubDirectoryID(id); + subDirectory.setInstituteSubDirectoryName(name); + return subDirectory; + } + + private static M_Institutedirectorymapping directoryMapping(Integer id, Integer institutionId) { + M_Institutedirectorymapping mapping = new M_Institutedirectorymapping(); + mapping.setInstituteDirMapID(id); + mapping.setInstitutionID(institutionId); + return mapping; + } + + @Test + @DisplayName("createInstuteDirectoty should answer the directories the service stored") + void createInstuteDirectoty_shouldAnswerStoredDirectories() { + ArrayList stored = new ArrayList<>(List.of(directory(11, "Hospitals"))); + when(instuteDirectoryInter.createInstuteDirectory(anyList())).thenReturn(stored); + + assertSuccessContaining( + controller.createInstuteDirectoty("[{\"instituteDirectoryName\":\"Hospitals\"}]"), "Hospitals"); + } + + @Test + @DisplayName("createInstuteDirectoty should answer an error envelope when the store fails") + void createInstuteDirectoty_shouldAnswerErrorEnvelopeOnFailure() { + when(instuteDirectoryInter.createInstuteDirectory(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstuteDirectoty("[{\"instituteDirectoryName\":\"Hospitals\"}]")); + } + + @Test + @DisplayName("getInstuteDirectory should answer the directories configured for the mapping") + void getInstuteDirectory_shouldAnswerConfiguredDirectories() { + ArrayList stored = new ArrayList<>(List.of(directory(11, "Hospitals"))); + when(instuteDirectoryInter.getInstuteDirectory(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getInstuteDirectory("{\"providerServiceMapId\":4001}"), "Hospitals"); + } + + @Test + @DisplayName("getInstuteDirectory should answer an error envelope when the lookup fails") + void getInstuteDirectory_shouldAnswerErrorEnvelopeOnFailure() { + when(instuteDirectoryInter.getInstuteDirectory(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstuteDirectory("{\"providerServiceMapId\":4001}")); + } + + @Test + @DisplayName("editInstuteDirectory should copy the edited fields onto the stored directory") + void editInstuteDirectory_shouldCopyEditedFields() { + M_Institutedirectory stored = directory(11, "old name"); + when(instuteDirectoryInter.editInstuteDirectory(11)).thenReturn(stored); + when(instuteDirectoryInter.editdata(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editInstuteDirectory("{\"instituteDirectoryID\":11," + + "\"instituteDirectoryName\":\"Hospitals\",\"instituteDirectoryDesc\":\"Referral hospitals\"," + + "\"modifiedBy\":\"admin\"}"), "Hospitals"); + assertEquals("Referral hospitals", stored.getInstituteDirectoryDesc()); + } + + @Test + @DisplayName("editInstuteDirectory should answer an error envelope for a directory that does not exist") + void editInstuteDirectory_shouldAnswerErrorEnvelopeForUnknownDirectory() { + when(instuteDirectoryInter.editInstuteDirectory(11)).thenReturn(null); + + assertCodeException(controller.editInstuteDirectory("{\"instituteDirectoryID\":11}")); + } + + @Test + @DisplayName("deleteInstuteDirectory should mark the directory deleted") + void deleteInstuteDirectory_shouldMarkDirectoryDeleted() { + M_Institutedirectory stored = directory(11, "Hospitals"); + when(instuteDirectoryInter.editInstuteDirectory(11)).thenReturn(stored); + when(instuteDirectoryInter.editdata(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.deleteInstuteDirectory("{\"instituteDirectoryID\":11,\"deleted\":true}"), "Hospitals"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteInstuteDirectory should answer an error envelope for a directory that does not exist") + void deleteInstuteDirectory_shouldAnswerErrorEnvelopeForUnknownDirectory() { + when(instuteDirectoryInter.editInstuteDirectory(11)).thenReturn(null); + + assertCodeException(controller.deleteInstuteDirectory("{\"instituteDirectoryID\":11,\"deleted\":true}")); + } + + @Test + @DisplayName("getInstuteType should answer the institute types configured for the mapping") + void getInstuteType_shouldAnswerConfiguredTypes() { + ArrayList stored = new ArrayList<>(List.of(instituteType(21, "PHC"))); + when(m_InstitutiontypeInter.getInstuteType(4001)).thenReturn(stored); + + assertSuccessContaining(controller.getInstuteType("{\"providerServiceMapID\":4001}"), "PHC"); + } + + @Test + @DisplayName("getInstuteType should answer an error envelope when the lookup fails") + void getInstuteType_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutiontypeInter.getInstuteType(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstuteType("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("createInstuteType should answer the institute types the service stored") + void createInstuteType_shouldAnswerStoredTypes() { + ArrayList stored = new ArrayList<>(List.of(instituteType(21, "PHC"))); + when(m_InstitutiontypeInter.createInstuteType(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createInstuteType("[{\"institutionType\":\"PHC\"}]"), "PHC"); + } + + @Test + @DisplayName("createInstuteType should answer an error envelope when the store fails") + void createInstuteType_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutiontypeInter.createInstuteType(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstuteType("[{\"institutionType\":\"PHC\"}]")); + } + + @Test + @DisplayName("editInstuteType should copy the edited fields onto the stored institute type") + void editInstuteType_shouldCopyEditedFields() { + M_Institutiontype stored = instituteType(21, "old name"); + when(m_InstitutiontypeInter.editInstuteType(21)).thenReturn(stored); + when(m_InstitutiontypeInter.saveEditdata(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editInstuteType("{\"institutionTypeID\":21,\"institutionType\":\"PHC\"," + + "\"institutionTypeDesc\":\"Primary health centre\",\"modifiedBy\":\"admin\"}"), "PHC"); + assertEquals("Primary health centre", stored.getInstitutionTypeDesc()); + } + + @Test + @DisplayName("editInstuteType should answer an error envelope for an institute type that does not exist") + void editInstuteType_shouldAnswerErrorEnvelopeForUnknownType() { + when(m_InstitutiontypeInter.editInstuteType(21)).thenReturn(null); + + assertCodeException(controller.editInstuteType("{\"institutionTypeID\":21}")); + } + + @Test + @DisplayName("deleteInstuteType should mark the institute type deleted") + void deleteInstuteType_shouldMarkTypeDeleted() { + M_Institutiontype stored = instituteType(21, "PHC"); + when(m_InstitutiontypeInter.editInstuteType(21)).thenReturn(stored); + when(m_InstitutiontypeInter.saveEditdata(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteInstuteType("{\"institutionTypeID\":21,\"deleted\":true}"), "PHC"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteInstuteType should answer an error envelope for an institute type that does not exist") + void deleteInstuteType_shouldAnswerErrorEnvelopeForUnknownType() { + when(m_InstitutiontypeInter.editInstuteType(21)).thenReturn(null); + + assertCodeException(controller.deleteInstuteType("{\"institutionTypeID\":21,\"deleted\":true}")); + } + + @Test + @DisplayName("createInstuteTypeByDist should answer the institute types the service stored") + void createInstuteTypeByDist_shouldAnswerStoredTypes() { + ArrayList stored = new ArrayList<>(List.of(instituteType(21, "PHC"))); + when(m_InstitutiontypeInter.createInstuteType(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createInstuteTypeByDist("[{\"institutionType\":\"PHC\"}]"), "PHC"); + } + + @Test + @DisplayName("createInstuteTypeByDist should answer an error envelope when the store fails") + void createInstuteTypeByDist_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutiontypeInter.createInstuteType(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstuteTypeByDist("[{\"institutionType\":\"PHC\"}]")); + } + + @Test + @DisplayName("getInstituteTypeByDist should answer the institute types under the location") + void getInstituteTypeByDist_shouldAnswerTypesUnderLocation() { + ArrayList stored = new ArrayList<>(List.of(instituteType(21, "PHC"))); + when(m_InstitutiontypeInter.getInstuteTypeByDist(4001, 31, 41, 51)).thenReturn(stored); + + assertSuccessContaining(controller.getInstituteTypeByDist("{\"providerServiceMapID\":4001," + + "\"districtId\":31,\"subDistrictId\":41,\"villageId\":51}"), "PHC"); + } + + @Test + @DisplayName("getInstituteTypeByDist should answer an error envelope when the lookup fails") + void getInstituteTypeByDist_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutiontypeInter.getInstuteTypeByDist(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstituteTypeByDist("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getInstution should answer the institutions under the block") + void getInstution_shouldAnswerInstitutionsUnderBlock() { + ArrayList stored = new ArrayList<>(List.of(institution(31, "District Hospital"))); + when(m_InstitutionInter.getInstution(4001, 29, 301, 401)).thenReturn(stored); + + assertSuccessContaining(controller.getInstution("{\"providerServiceMapID\":4001,\"stateID\":29," + + "\"districtID\":301,\"blockID\":401}"), "District Hospital"); + } + + @Test + @DisplayName("getInstution should answer an error envelope when the lookup fails") + void getInstution_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutionInter.getInstution(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstution("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("createInstution should answer the institutions the service stored") + void createInstution_shouldAnswerStoredInstitutions() { + ArrayList stored = new ArrayList<>(List.of(institution(31, "District Hospital"))); + when(m_InstitutionInter.createInstution(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createInstution("[{\"institutionName\":\"District Hospital\"}]"), + "District Hospital"); + } + + @Test + @DisplayName("createInstution should answer an error envelope when the store fails") + void createInstution_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutionInter.createInstution(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstution("[{\"institutionName\":\"District Hospital\"}]")); + } + + @Test + @DisplayName("editInstution should copy every contact field onto the stored institution") + void editInstution_shouldCopyContactFields() { + M_Institution stored = institution(31, "old name"); + when(m_InstitutionInter.editInstution(31)).thenReturn(stored); + when(m_InstitutionInter.saveEditData(stored)).thenReturn(stored); + + String response = controller.editInstution("{\"institutionID\":31," + + "\"institutionName\":\"District Hospital\",\"address\":\"Main Road\",\"contactNo1\":\"9000000001\"," + + "\"contactNo2\":\"9000000002\",\"contactNo3\":\"9000000003\",\"contactPerson1\":\"Asha\"," + + "\"contactPerson2\":\"Ravi\",\"contactPerson3\":\"Meera\"," + + "\"contactPerson1_Email\":\"asha@example.org\",\"contactPerson2_Email\":\"ravi@example.org\"," + + "\"contactPerson3_Email\":\"meera@example.org\",\"website\":\"https://example.org\"}"); + + assertSuccessContaining(response, "District Hospital"); + assertEquals("Main Road", stored.getAddress()); + assertEquals("asha@example.org", stored.getContactPerson1_Email()); + assertEquals("https://example.org", stored.getWebsite()); + } + + @Test + @DisplayName("editInstution should answer an error envelope for an institution that does not exist") + void editInstution_shouldAnswerErrorEnvelopeForUnknownInstitution() { + when(m_InstitutionInter.editInstution(31)).thenReturn(null); + + assertCodeException(controller.editInstution("{\"institutionID\":31}")); + } + + @Test + @DisplayName("deleteInstution should mark the institution deleted") + void deleteInstution_shouldMarkInstitutionDeleted() { + M_Institution stored = institution(31, "District Hospital"); + when(m_InstitutionInter.editInstution(31)).thenReturn(stored); + when(m_InstitutionInter.saveEditData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteInstution("{\"institutionID\":31,\"deleted\":true}"), + "District Hospital"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteInstution should answer an error envelope for an institution that does not exist") + void deleteInstution_shouldAnswerErrorEnvelopeForUnknownInstitution() { + when(m_InstitutionInter.editInstution(31)).thenReturn(null); + + assertCodeException(controller.deleteInstution("{\"institutionID\":31,\"deleted\":true}")); + } + + @Test + @DisplayName("createInstutionByVillage should answer the institutions the service stored") + void createInstutionByVillage_shouldAnswerStoredInstitutions() { + ArrayList stored = new ArrayList<>(List.of(institution(31, "Sub Centre"))); + when(m_InstitutionInter.createInstutionByVillage(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createInstutionByVillage("[{\"institutionName\":\"Sub Centre\"}]"), + "Sub Centre"); + } + + @Test + @DisplayName("createInstutionByVillage should answer an error envelope when the store fails") + void createInstutionByVillage_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutionInter.createInstutionByVillage(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstutionByVillage("[{\"institutionName\":\"Sub Centre\"}]")); + } + + @Test + @DisplayName("getInstutionByVillage should answer the institutions under the village") + void getInstutionByVillage_shouldAnswerInstitutionsUnderVillage() { + ArrayList stored = new ArrayList<>(List.of(institution(31, "Sub Centre"))); + when(m_InstitutionInter.getInstutionByVillage(4001, 29, 301, 401, 501)).thenReturn(stored); + + assertSuccessContaining(controller.getInstutionByVillage("{\"providerServiceMapID\":4001,\"stateID\":29," + + "\"districtID\":301,\"blockID\":401,\"villageID\":501}"), "Sub Centre"); + } + + @Test + @DisplayName("getInstutionByVillage should answer an error envelope when the lookup fails") + void getInstutionByVillage_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutionInter.getInstutionByVillage(any(), any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstutionByVillage("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("createInstitutionByFile should answer the summary the service reports for the upload") + void createInstitutionByFile_shouldAnswerUploadSummary() { + when(m_InstitutionInter.createInstitutionByFile(any(JsonObject.class))).thenReturn("12 institutions created"); + + assertSuccessContaining(controller.createInstitutionByFile("{\"fileName\":\"institutions.xlsx\"}"), + "12 institutions created"); + } + + @Test + @DisplayName("createInstitutionByFile should answer an error envelope for a payload that is not an object") + void createInstitutionByFile_shouldAnswerErrorEnvelopeForNonObjectPayload() { + assertGenericFailure(controller.createInstitutionByFile("\"just a string\"")); + } + + @Test + @DisplayName("getInstuteSubDirectory should answer the sub-directories under the directory") + void getInstuteSubDirectory_shouldAnswerSubDirectories() { + ArrayList stored = new ArrayList<>(List.of(subDirectory(41, "Government"))); + when(m_InstitutesubdirectoryInter.getInstutesubDirectory(11, 4001)).thenReturn(stored); + + assertSuccessContaining( + controller.getInstuteSubDirectory("{\"instituteDirectoryID\":11,\"providerServiceMapId\":4001}"), + "Government"); + } + + @Test + @DisplayName("getInstuteSubDirectory should answer an error envelope when the lookup fails") + void getInstuteSubDirectory_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutesubdirectoryInter.getInstutesubDirectory(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstuteSubDirectory("{\"instituteDirectoryID\":11}")); + } + + @Test + @DisplayName("createInstuteSubDirectory should answer the sub-directories the service stored") + void createInstuteSubDirectory_shouldAnswerStoredSubDirectories() { + ArrayList stored = new ArrayList<>(List.of(subDirectory(41, "Government"))); + when(m_InstitutesubdirectoryInter.CreateInstutesubDirectory(anyList())).thenReturn(stored); + + assertSuccessContaining( + controller.createInstuteSubDirectory("[{\"instituteSubDirectoryName\":\"Government\"}]"), + "Government"); + } + + @Test + @DisplayName("createInstuteSubDirectory should answer an error envelope when the store fails") + void createInstuteSubDirectory_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutesubdirectoryInter.CreateInstutesubDirectory(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstuteSubDirectory("[{\"instituteSubDirectoryName\":\"Government\"}]")); + } + + @Test + @DisplayName("editInstuteSubDirectory should copy the edited fields onto the stored sub-directory") + void editInstuteSubDirectory_shouldCopyEditedFields() { + M_Institutesubdirectory stored = subDirectory(41, "old name"); + when(m_InstitutesubdirectoryInter.editInstutesubDirectory(41)).thenReturn(stored); + when(m_InstitutesubdirectoryInter.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editInstuteSubDirectory("{\"instituteSubDirectoryID\":41," + + "\"instituteSubDirectoryName\":\"Government\",\"instituteSubDirectoryDesc\":\"State run\"," + + "\"modifiedBy\":\"admin\"}"), "Government"); + assertEquals("State run", stored.getInstituteSubDirectoryDesc()); + } + + @Test + @DisplayName("editInstuteSubDirectory should answer an error envelope for a sub-directory that does not exist") + void editInstuteSubDirectory_shouldAnswerErrorEnvelopeForUnknownSubDirectory() { + when(m_InstitutesubdirectoryInter.editInstutesubDirectory(41)).thenReturn(null); + + assertCodeException(controller.editInstuteSubDirectory("{\"instituteSubDirectoryID\":41}")); + } + + @Test + @DisplayName("deleteInstuteSubDirectory should mark the sub-directory deleted") + void deleteInstuteSubDirectory_shouldMarkSubDirectoryDeleted() { + M_Institutesubdirectory stored = subDirectory(41, "Government"); + when(m_InstitutesubdirectoryInter.editInstutesubDirectory(41)).thenReturn(stored); + when(m_InstitutesubdirectoryInter.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.deleteInstuteSubDirectory("{\"instituteSubDirectoryID\":41,\"deleted\":true}"), + "Government"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteInstuteSubDirectory should answer an error envelope for a sub-directory that does not exist") + void deleteInstuteSubDirectory_shouldAnswerErrorEnvelopeForUnknownSubDirectory() { + when(m_InstitutesubdirectoryInter.editInstutesubDirectory(41)).thenReturn(null); + + assertCodeException(controller.deleteInstuteSubDirectory("{\"instituteSubDirectoryID\":41,\"deleted\":true}")); + } + + @Test + @DisplayName("createInstuteSubDirectoryMaping should answer the mappings the service stored") + void createInstuteSubDirectoryMaping_shouldAnswerStoredMappings() { + ArrayList stored = + new ArrayList<>(List.of(directoryMapping(51, 31))); + when(m_InstitutedirectorymappingInter.createInstituteDirectoryData(anyList())).thenReturn(stored); + + assertSuccessContaining( + controller.createInstuteSubDirectoryMaping("[{\"institutionID\":31,\"instituteDirectoryID\":11}]"), + "51"); + } + + @Test + @DisplayName("createInstuteSubDirectoryMaping should answer an error envelope when the store fails") + void createInstuteSubDirectoryMaping_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutedirectorymappingInter.createInstituteDirectoryData(anyList())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createInstuteSubDirectoryMaping("[{\"institutionID\":31}]")); + } + + @Test + @DisplayName("deleteInstuteSubDirectoryMaping should mark the mapping deleted") + void deleteInstuteSubDirectoryMaping_shouldMarkMappingDeleted() { + M_Institutedirectorymapping stored = directoryMapping(51, 31); + when(m_InstitutedirectorymappingInter.deleteInstituteDirectoryData(51)).thenReturn(stored); + when(m_InstitutedirectorymappingInter.setdeletedData(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.deleteInstuteSubDirectoryMaping("{\"instituteDirMapID\":51,\"deleted\":true}"), + "51"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteInstuteSubDirectoryMaping should answer an error envelope for a mapping that does not exist") + void deleteInstuteSubDirectoryMaping_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(m_InstitutedirectorymappingInter.deleteInstituteDirectoryData(51)).thenReturn(null); + + assertCodeException( + controller.deleteInstuteSubDirectoryMaping("{\"instituteDirMapID\":51,\"deleted\":true}")); + } + + @Test + @DisplayName("getInstuteSubDirectoryMaping should answer the mappings under the sub-directory") + void getInstuteSubDirectoryMaping_shouldAnswerMappings() { + ArrayList stored = + new ArrayList<>(List.of(directoryMapping(51, 31))); + when(m_InstitutedirectorymappingInter.getInstituteDirectoryData(41)).thenReturn(stored); + + assertSuccessContaining(controller.getInstuteSubDirectoryMaping("{\"instituteSubDirectoryID\":41}"), "51"); + } + + @Test + @DisplayName("getInstuteSubDirectoryMaping should answer an error envelope when the lookup fails") + void getInstuteSubDirectoryMaping_shouldAnswerErrorEnvelopeOnFailure() { + when(m_InstitutedirectorymappingInter.getInstituteDirectoryData(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getInstuteSubDirectoryMaping("{\"instituteSubDirectoryID\":41}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardProviderControllerTest.java b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardProviderControllerTest.java new file mode 100644 index 0000000..51e7a6e --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/provideronboard/ProviderOnBoardProviderControllerTest.java @@ -0,0 +1,379 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.provideronboard; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.provideronboard.M_ProviderServiceMapping; +import com.iemr.admin.data.provideronboard.M_ServiceMaster; +import com.iemr.admin.data.provideronboard.M_UserservicerolemappingForRole; +import com.iemr.admin.data.provideronboard.ServiceProvider_Model; +import com.iemr.admin.data.provideronboard.V_Showprovideradmin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The provider endpoints onboard a service provider, map it onto the states and + * services it serves, and keep its provider-admin users attached to it. + */ +@DisplayName("ProviderOnBoardController provider Test Suite") +class ProviderOnBoardProviderControllerTest extends ProviderOnBoardFixture { + + private static final String ONBOARD_REQUEST = "{\"serviceProviderName\":\"Piramal Swasthya\"," + + "\"statusID\":1,\"createdBy\":\"admin\",\"providerAdminDetails\":[{\"userName\":\"pa.user\"}]," + + "\"stateAndServiceMapList\":[{\"stateId\":\"29\",\"services\":[\"1\",\"3\"]}]}"; + + private static ServiceProvider_Model provider(Integer id, String name) { + ServiceProvider_Model provider = new ServiceProvider_Model(); + provider.setServiceProviderId(id); + provider.setServiceProviderName(name); + return provider; + } + + @Test + @DisplayName("providerCreationAndMapping should report success once the admin role is created") + void providerCreationAndMapping_shouldReportSuccess() { + M_ProviderServiceMapping mapping = new M_ProviderServiceMapping(); + mapping.setProviderServiceMapID(4001); + when(serviceProvider_ServiceImpl.createProvider(any(java.util.Set.class))).thenReturn(77); + when(serviceProvider_ServiceImpl.mapProviderStateService(any())).thenReturn(List.of(mapping)); + when(iemrUserServiceImpl.createUser(any(), anyString())).thenReturn(3117); + when(iemrUserServiceImpl.createUserServiceRoleMapping(anyList(), anyInt(), anyString())).thenReturn(1); + + String response = controller.providerCreationAndMapping(ONBOARD_REQUEST); + + assertSuccessContaining(response, "true"); + } + + @Test + @DisplayName("providerCreationAndMapping should report failure when the admin role cannot be created") + void providerCreationAndMapping_shouldReportRoleCreationFailure() { + M_ProviderServiceMapping mapping = new M_ProviderServiceMapping(); + mapping.setProviderServiceMapID(4001); + when(serviceProvider_ServiceImpl.createProvider(any(java.util.Set.class))).thenReturn(77); + when(serviceProvider_ServiceImpl.mapProviderStateService(any())).thenReturn(List.of(mapping)); + when(iemrUserServiceImpl.createUser(any(), anyString())).thenReturn(3117); + when(iemrUserServiceImpl.createUserServiceRoleMapping(anyList(), anyInt(), anyString())).thenReturn(0); + + assertSuccessContaining(controller.providerCreationAndMapping(ONBOARD_REQUEST), "false"); + } + + @Test + @DisplayName("providerCreationAndMapping should report failure when nothing was mapped for the provider") + void providerCreationAndMapping_shouldReportFailureWhenNothingMapped() { + when(serviceProvider_ServiceImpl.createProvider(any(java.util.Set.class))).thenReturn(77); + when(serviceProvider_ServiceImpl.mapProviderStateService(any())).thenReturn(new ArrayList<>()); + when(iemrUserServiceImpl.createUser(any(), anyString())).thenReturn(3117); + + assertSuccessContaining(controller.providerCreationAndMapping(ONBOARD_REQUEST), "false"); + } + + @Test + @DisplayName("providerCreationAndMapping should report failure when the provider itself is not created") + void providerCreationAndMapping_shouldReportFailureWhenProviderNotCreated() { + when(serviceProvider_ServiceImpl.createProvider(any(java.util.Set.class))).thenReturn(0); + + assertSuccessContaining(controller.providerCreationAndMapping(ONBOARD_REQUEST), "false"); + } + + @Test + @DisplayName("providerCreationAndMapping should answer an error envelope when the service fails") + void providerCreationAndMapping_shouldAnswerErrorEnvelopeOnFailure() { + when(serviceProvider_ServiceImpl.createProvider(any(java.util.Set.class))) + .thenThrow(new IllegalStateException("provider store is unavailable")); + + assertGenericFailure(controller.providerCreationAndMapping(ONBOARD_REQUEST)); + } + + @Test + @DisplayName("updateProvider should answer the provider the service saved") + void updateProvider_shouldAnswerSavedProvider() { + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(provider(77, "old name")); + when(serviceProvider_ServiceImpl.upDateProviderDetails(any())) + .thenReturn(provider(77, "Piramal Swasthya")); + + assertSuccessContaining(controller.updateProvider("{\"serviceProviderId\":77," + + "\"serviceProviderName\":\"Piramal Swasthya\"}"), "Piramal Swasthya"); + } + + @Test + @DisplayName("updateProvider should answer an error envelope for a provider that does not exist") + void updateProvider_shouldAnswerErrorEnvelopeForUnknownProvider() { + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(null); + + assertCodeException(controller.updateProvider("{\"serviceProviderId\":77}")); + } + + @Test + @DisplayName("getServiceLine should answer the service lines the master holds") + void getServiceLine_shouldAnswerServiceLines() { + M_ServiceMaster serviceMaster = new M_ServiceMaster(); + serviceMaster.setServiceID(1); + serviceMaster.setServiceName("Tele Medicine"); + when(m_ServiceMasterInter.getAllServiceLine()).thenReturn(List.of(serviceMaster)); + + assertSuccessContaining(controller.getServiceLine("{}"), "Tele Medicine"); + } + + @Test + @DisplayName("getServiceLine should answer an error envelope when the master cannot be read") + void getServiceLine_shouldAnswerErrorEnvelopeOnFailure() { + when(m_ServiceMasterInter.getAllServiceLine()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getServiceLine("{}")); + } + + @Test + @DisplayName("getAllProviderName should answer every provider the service holds") + void getAllProviderName_shouldAnswerEveryProvider() { + ArrayList providers = new ArrayList<>(List.of(provider(77, "Piramal Swasthya"))); + when(serviceProvider_ServiceImpl.getAllProviderName()).thenReturn(providers); + + assertSuccessContaining(controller.getAllProviderName("{}"), "Piramal Swasthya"); + } + + @Test + @DisplayName("getAllProviderName should answer an error envelope when the lookup fails") + void getAllProviderName_shouldAnswerErrorEnvelopeOnFailure() { + when(serviceProvider_ServiceImpl.getAllProviderName()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllProviderName("{}")); + } + + @Test + @DisplayName("getProviderName should report that a name is already taken") + void getProviderName_shouldReportNameTaken() { + when(serviceProvider_ServiceImpl.getProviderName("Piramal Swasthya")).thenReturn("Piramal Swasthya"); + + assertSuccessContaining(controller.getProviderName("{\"serviceProviderName\":\"Piramal Swasthya\"}"), + "provider_name_exists"); + } + + @Test + @DisplayName("getProviderName should report that a name is still free") + void getProviderName_shouldReportNameFree() { + when(serviceProvider_ServiceImpl.getProviderName("Piramal Swasthya")).thenReturn(null); + + assertSuccessContaining(controller.getProviderName("{\"serviceProviderName\":\"Piramal Swasthya\"}"), + "provider_name_doesnt_exist"); + } + + @Test + @DisplayName("getProviderName should answer an error envelope when the check fails") + void getProviderName_shouldAnswerErrorEnvelopeOnFailure() { + when(serviceProvider_ServiceImpl.getProviderName(anyString())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getProviderName("{\"serviceProviderName\":\"Piramal Swasthya\"}")); + } + + @Test + @DisplayName("getProviderId should answer the mapping the service resolves") + void getProviderId_shouldAnswerResolvedMapping() { + M_ProviderServiceMapping mapping = new M_ProviderServiceMapping(); + mapping.setProviderServiceMapID(4001); + mapping.setServiceProviderID(77); + when(serviceProvider_ServiceImpl.getProviderserviceMapId(4001)).thenReturn(mapping); + + assertSuccessContaining(controller.getProviderId("{\"providerServiceMapID\":4001}"), "4001"); + } + + @Test + @DisplayName("getProviderId should answer an error envelope for a mapping that does not exist") + void getProviderId_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(serviceProvider_ServiceImpl.getProviderserviceMapId(4001)).thenReturn(null); + + assertCodeException(controller.getProviderId("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("createProvider should answer the providers the service stored") + void createProvider_shouldAnswerStoredProviders() { + ArrayList stored = new ArrayList<>(List.of(provider(77, "Piramal Swasthya"))); + when(serviceProvider_ServiceImpl.createProvider1(anyList())).thenReturn(stored); + + assertSuccessContaining(controller.createProvider("[{\"serviceProviderName\":\"Piramal Swasthya\"}]"), + "Piramal Swasthya"); + } + + @Test + @DisplayName("createProvider should refuse an empty batch rather than store nothing quietly") + void createProvider_shouldRefuseEmptyBatch() { + assertGenericFailure(controller.createProvider("[]")); + } + + @Test + @DisplayName("providerUpdate should answer the provider the service saved") + void providerUpdate_shouldAnswerSavedProvider() { + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(provider(77, "old name")); + when(serviceProvider_ServiceImpl.upDateProviderDetails(any())) + .thenReturn(provider(77, "Piramal Swasthya")); + + assertSuccessContaining(controller.providerUpdate("{\"serviceProviderId\":77," + + "\"serviceProviderName\":\"Piramal Swasthya\",\"modifiedBy\":\"admin\"}"), "Piramal Swasthya"); + } + + @Test + @DisplayName("providerUpdate should answer an error envelope for a provider that does not exist") + void providerUpdate_shouldAnswerErrorEnvelopeForUnknownProvider() { + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(null); + + assertCodeException(controller.providerUpdate("{\"serviceProviderId\":77}")); + } + + @Test + @DisplayName("providerDelete should mark the provider deleted and answer what was saved") + void providerDelete_shouldMarkProviderDeleted() { + ServiceProvider_Model stored = provider(77, "Piramal Swasthya"); + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(stored); + when(serviceProvider_ServiceImpl.upDateProviderDetails(stored)).thenReturn(stored); + + assertSuccessContaining(controller.providerDelete("{\"serviceProviderId\":77,\"deleted\":true}"), + "Piramal Swasthya"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("providerDelete should answer an error envelope for a provider that does not exist") + void providerDelete_shouldAnswerErrorEnvelopeForUnknownProvider() { + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(null); + + assertCodeException(controller.providerDelete("{\"serviceProviderId\":77,\"deleted\":true}")); + } + + @Test + @DisplayName("updateProviderAdmin should answer the provider the service saved") + void updateProviderAdmin_shouldAnswerSavedProvider() { + when(serviceProvider_ServiceImpl.getProviderData(77)).thenReturn(provider(77, "old name")); + when(serviceProvider_ServiceImpl.upDateProviderDetails(any())) + .thenReturn(provider(77, "Piramal Swasthya")); + + assertSuccessContaining(controller.updateProviderAdmin("{\"serviceProviderId\":77," + + "\"serviceProviderName\":\"Piramal Swasthya\"}"), "Piramal Swasthya"); + } + + @Test + @DisplayName("updateProviderAdmin should answer an error envelope when the provider cannot be read") + void updateProviderAdmin_shouldAnswerErrorEnvelopeOnFailure() { + when(serviceProvider_ServiceImpl.getProviderData(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.updateProviderAdmin("{\"serviceProviderId\":77}")); + } + + @Test + @DisplayName("mappingProviderAdmintoProvider should map the admin onto every mapping it is given") + void mappingProviderAdmintoProvider_shouldMapEveryMapping() { + M_UserservicerolemappingForRole mapped = new M_UserservicerolemappingForRole(); + mapped.setuSRMappingID(9001); + mapped.setUserID(3117); + ArrayList stored = new ArrayList<>(List.of(mapped)); + when(serviceProvider_ServiceImpl.AddUserRole(anyList())).thenReturn(stored); + + String response = controller.mappingProviderAdmintoProvider( + "[{\"userID\":3117,\"createdBy\":\"admin\",\"serviceProviderMapID1\":[4001,4002]}]"); + + assertSuccessContaining(response, "9001"); + verify(serviceProvider_ServiceImpl).AddUserRole(anyList()); + } + + @Test + @DisplayName("mappingProviderAdmintoProvider should answer an error envelope when no mappings are named") + void mappingProviderAdmintoProvider_shouldAnswerErrorEnvelopeWithoutMappings() { + assertCodeException(controller.mappingProviderAdmintoProvider("[{\"userID\":3117}]")); + } + + @Test + @DisplayName("editMappingProviderAdmintoProvider should answer the mapping the service saved") + void editMappingProviderAdmintoProvider_shouldAnswerSavedMapping() { + M_UserservicerolemappingForRole stored = new M_UserservicerolemappingForRole(); + stored.setuSRMappingID(9001); + when(serviceProvider_ServiceImpl.getPADataForEdit(9001)).thenReturn(stored); + when(serviceProvider_ServiceImpl.insertEditedData(stored)).thenReturn(stored); + + String response = controller.editMappingProviderAdmintoProvider( + "{\"uSRMappingID\":9001,\"providerServiceMapID\":4002,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "9001"); + assertEquals(4002, stored.getProviderServiceMapID()); + } + + @Test + @DisplayName("editMappingProviderAdmintoProvider should answer an error envelope for an unknown mapping") + void editMappingProviderAdmintoProvider_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(serviceProvider_ServiceImpl.getPADataForEdit(9001)).thenReturn(null); + + assertCodeException(controller.editMappingProviderAdmintoProvider("{\"uSRMappingID\":9001}")); + } + + @Test + @DisplayName("deleteMappingProviderAdmintoProvider should mark the mapping deleted") + void deleteMappingProviderAdmintoProvider_shouldMarkMappingDeleted() { + M_UserservicerolemappingForRole stored = new M_UserservicerolemappingForRole(); + stored.setuSRMappingID(9001); + when(serviceProvider_ServiceImpl.getPADataForEdit(9001)).thenReturn(stored); + when(serviceProvider_ServiceImpl.insertEditedData(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.deleteMappingProviderAdmintoProvider("{\"uSRMappingID\":9001,\"deleted\":true}"), "9001"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteMappingProviderAdmintoProvider should answer an error envelope for an unknown mapping") + void deleteMappingProviderAdmintoProvider_shouldAnswerErrorEnvelopeForUnknownMapping() { + when(serviceProvider_ServiceImpl.getPADataForEdit(9001)).thenReturn(null); + + assertCodeException(controller.deleteMappingProviderAdmintoProvider("{\"uSRMappingID\":9001}")); + } + + @Test + @DisplayName("getMappingProviderAdmintoProvider should answer the provider admins on record") + void getMappingProviderAdmintoProvider_shouldAnswerProviderAdmins() { + V_Showprovideradmin admin = new V_Showprovideradmin(); + admin.setuSRMappingID(9001); + admin.setFirstName("Asha"); + ArrayList admins = new ArrayList<>(List.of(admin)); + when(serviceProvider_ServiceImpl.getProviderAdmins()).thenReturn(admins); + + assertSuccessContaining(controller.getMappingProviderAdmintoProvider("{}"), "Asha"); + } + + @Test + @DisplayName("getMappingProviderAdmintoProvider should answer an error envelope when the lookup fails") + void getMappingProviderAdmintoProvider_shouldAnswerErrorEnvelopeOnFailure() { + when(serviceProvider_ServiceImpl.getProviderAdmins()).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getMappingProviderAdmintoProvider("{}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/questionnaire/QuestionnaireControllerTest.java b/src/test/java/com/iemr/admin/controller/questionnaire/QuestionnaireControllerTest.java new file mode 100644 index 0000000..e0c63ac --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/questionnaire/QuestionnaireControllerTest.java @@ -0,0 +1,181 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.questionnaire; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.service.questionnaire.QuestionnaireServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * The questionnaire screen keeps the feedback questions a provider asks: adding + * them, listing them, editing them and retiring them. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("QuestionnaireController Test Suite") +class QuestionnaireControllerTest { + + private static final String REQUEST = "{\"providerServiceMapID\":4001,\"questionID\":501}"; + + @Mock + private QuestionnaireServiceImpl questionnaireService; + + @InjectMocks + private QuestionnaireController controller; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static String errorMessageOf(String response) { + return new JSONObject(response).getString("errorMessage"); + } + + @Test + @DisplayName("saveQuestionnaire should confirm the questions it stored") + void save_shouldConfirmStoredQuestions() throws Exception { + when(questionnaireService.SaveQuestionnaire(anyString())) + .thenReturn("Questionnaire Data Saved Successfully"); + + String response = controller.saveQuestionnaire(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Questionnaire Data Saved Successfully"), response); + } + + @Test + @DisplayName("saveQuestionnaire should report the failure when nothing came back from the service") + void save_shouldReportMissingAnswer() throws Exception { + when(questionnaireService.SaveQuestionnaire(anyString())).thenReturn(null); + + assertEquals("error in saving Questionnaire data", errorMessageOf(controller.saveQuestionnaire(REQUEST))); + } + + @Test + @DisplayName("saveQuestionnaire should report the failure when the questions cannot be stored") + void save_shouldReportStorageFailure() throws Exception { + when(questionnaireService.SaveQuestionnaire(anyString())) + .thenThrow(new Exception("error in saving Questionnaire data")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveQuestionnaire(REQUEST))); + } + + @Test + @DisplayName("getQuestionnaireList should answer the questions the provider asks") + void getList_shouldAnswerProvidersQuestions() { + when(questionnaireService.getQuestionnaireList(anyString())) + .thenReturn("[{\"question\":\"Was the visit useful?\"}]"); + + String response = controller.getQuestionnaireList(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Was the visit useful?"), response); + } + + @Test + @DisplayName("getQuestionnaireList should report the failure when nothing came back from the service") + void getList_shouldReportMissingAnswer() { + when(questionnaireService.getQuestionnaireList(anyString())).thenReturn(null); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getQuestionnaireList(REQUEST))); + } + + @Test + @DisplayName("getQuestionnaireList should report the failure when the list cannot be answered") + void getList_shouldReportLookupFailure() { + when(questionnaireService.getQuestionnaireList(anyString())) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getQuestionnaireList(REQUEST))); + } + + @Test + @DisplayName("deleteQuestionnaire should confirm the question it retired") + void delete_shouldConfirmRetiredQuestion() { + when(questionnaireService.deleteQuestionnaire(anyString())) + .thenReturn("Questionnaire Deleted Successfully"); + + String response = controller.deleteQuestionnaire(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Questionnaire Deleted Successfully"), response); + } + + @Test + @DisplayName("deleteQuestionnaire should report the failure when no question was retired") + void delete_shouldReportNothingRetired() { + when(questionnaireService.deleteQuestionnaire(anyString())).thenReturn(null); + + assertEquals("error occured while deleting question.........", + errorMessageOf(controller.deleteQuestionnaire(REQUEST))); + } + + @Test + @DisplayName("deleteQuestionnaire should report the failure when the retirement cannot be recorded") + void delete_shouldReportStorageFailure() { + when(questionnaireService.deleteQuestionnaire(anyString())).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.deleteQuestionnaire(REQUEST))); + } + + @Test + @DisplayName("editQuestionnaire should confirm the change it recorded") + void edit_shouldConfirmRecordedChange() { + when(questionnaireService.editQuestionnaire(anyString())).thenReturn("Questionnaire Updated Successfully"); + + String response = controller.editQuestionnaire(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Questionnaire Updated Successfully"), response); + } + + @Test + @DisplayName("editQuestionnaire should report the failure when nothing came back from the service") + void edit_shouldReportMissingAnswer() { + when(questionnaireService.editQuestionnaire(anyString())).thenReturn(null); + + assertEquals("error occured while editing question.........", + errorMessageOf(controller.editQuestionnaire(REQUEST))); + } + + @Test + @DisplayName("editQuestionnaire should report the failure when the change cannot be recorded") + void edit_shouldReportStorageFailure() { + when(questionnaireService.editQuestionnaire(anyString())).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.editQuestionnaire(REQUEST))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/rolemaster/RoleMasterControllerTest.java b/src/test/java/com/iemr/admin/controller/rolemaster/RoleMasterControllerTest.java new file mode 100644 index 0000000..6db52fa --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/rolemaster/RoleMasterControllerTest.java @@ -0,0 +1,464 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.rolemaster; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.rolemaster.M_Screen; +import com.iemr.admin.data.rolemaster.M_UserservicerolemappingForRoleProviderAdmin; +import com.iemr.admin.data.rolemaster.RoleMaster; +import com.iemr.admin.data.rolemaster.RoleScreenMapping; +import com.iemr.admin.data.rolemaster.StateServiceMapping; +import com.iemr.admin.repository.rolemaster.RoleScreenMappingRepo; +import com.iemr.admin.service.rolemaster.Role_MasterInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The role master endpoints define what each role may see, so a role saved + * without its screens leaves its holders locked out of their own work. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("RoleMasterController Test Suite") +class RoleMasterControllerTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer PSM_ID = 4001; + + @Mock + private RoleScreenMappingRepo roleScreenMappingRepo; + + @Mock + private Role_MasterInter roleMasterInter; + + @InjectMocks + private RoleMasterController controller; + + private static RoleMaster role(Integer id, String name) { + RoleMaster role = new RoleMaster(); + role.setRoleID(id); + role.setRoleName(name); + role.setProviderServiceMapID(PSM_ID); + role.setCreatedBy("admin"); + return role; + } + + private static StateServiceMapping stateMapping(Integer psmId) { + StateServiceMapping mapping = new StateServiceMapping(); + mapping.setProviderServiceMapID(psmId); + mapping.setServiceProviderID(PROVIDER_ID); + return mapping; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + @Test + @DisplayName("searchRole should answer the states the provider serves") + void searchRole_shouldAnswerServedStates() { + when(roleMasterInter.getStateByServiceProviderId(PROVIDER_ID)) + .thenReturn(new ArrayList<>(List.of(stateMapping(PSM_ID)))); + + assertSuccessContaining(controller.searchRole("{\"serviceProviderID\":77}"), "4001"); + } + + @Test + @DisplayName("searchRole should answer an error envelope when the lookup fails") + void searchRole_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getStateByServiceProviderId(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchRole("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getService should answer the service lines the provider runs in the state") + void getService_shouldAnswerServiceLinesInState() { + when(roleMasterInter.getServiceByServiceProviderIdAndStateId(PROVIDER_ID, 29)) + .thenReturn(new ArrayList<>(List.of(stateMapping(PSM_ID)))); + + assertSuccessContaining(controller.getService("{\"serviceProviderID\":77,\"stateID\":29}"), "4001"); + } + + @Test + @DisplayName("getService should answer an error envelope when the lookup fails") + void getService_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getServiceByServiceProviderIdAndStateId(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getService("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getServiceByProviderId should answer the service lines the user is mapped to") + void getServiceByProviderId_shouldAnswerMappedServiceLines() { + M_UserservicerolemappingForRoleProviderAdmin mapping = + new M_UserservicerolemappingForRoleProviderAdmin(); + mapping.setuSRMappingID(9001); + when(roleMasterInter.getServiceByServiceProviderIds(3117)) + .thenReturn(new ArrayList<>(List.of(mapping))); + + assertSuccessContaining(controller.getServiceByProviderId("{\"userID\":3117}"), "9001"); + } + + @Test + @DisplayName("getServiceByProviderId should answer an error envelope when the lookup fails") + void getServiceByProviderId_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getServiceByServiceProviderIds(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getServiceByProviderId("{\"userID\":3117}")); + } + + @Test + @DisplayName("getStateByProviderIdAndServiceID should pass the national flag through to the service") + void getStateByProviderIdAndServiceID_shouldPassNationalFlagThrough() { + M_UserservicerolemappingForRoleProviderAdmin mapping = + new M_UserservicerolemappingForRoleProviderAdmin(); + mapping.setuSRMappingID(9001); + when(roleMasterInter.getStateByServiceProviderIdAndServiceLines(3117, 3, Boolean.TRUE)) + .thenReturn(new ArrayList<>(List.of(mapping))); + + assertSuccessContaining(controller.getStateByProviderIdAndServiceID( + "{\"userID\":3117,\"serviceID\":3,\"isNational\":true}"), "9001"); + verify(roleMasterInter).getStateByServiceProviderIdAndServiceLines(3117, 3, Boolean.TRUE); + } + + @Test + @DisplayName("getStateByProviderIdAndServiceID should answer an error envelope when the lookup fails") + void getStateByProviderIdAndServiceID_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getStateByServiceProviderIdAndServiceLines(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getStateByProviderIdAndServiceID("{\"userID\":3117}")); + } + + @Test + @DisplayName("getAllRole should resolve the mapping before reading its roles") + void getAllRole_shouldResolveMappingFirst() { + when(roleMasterInter.getAllByMapId(PROVIDER_ID, 29, 3, Boolean.FALSE)) + .thenReturn(new ArrayList<>(List.of(stateMapping(PSM_ID)))); + when(roleMasterInter.getProStateServRoles(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(role(11, "Counsellor")))); + + assertSuccessContaining(controller.getAllRole("{\"serviceProviderID\":77,\"stateID\":29," + + "\"serviceID\":3,\"isNational\":false}"), "Counsellor"); + } + + @Test + @DisplayName("getAllRole should fall back to no mapping when the provider has none in the state") + void getAllRole_shouldFallBackWithoutMapping() { + when(roleMasterInter.getAllByMapId(any(), any(), any(), any())).thenReturn(new ArrayList<>()); + when(roleMasterInter.getProStateServRoles(0)).thenReturn(new ArrayList<>()); + + controller.getAllRole("{\"serviceProviderID\":77}"); + + verify(roleMasterInter).getProStateServRoles(0); + } + + @Test + @DisplayName("getAllRole should answer an error envelope when the lookup fails") + void getAllRole_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getAllByMapId(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRole("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getAllRoleNew should read the roles straight off the mapping the caller names") + void getAllRoleNew_shouldReadRolesFromNamedMapping() { + when(roleMasterInter.getProStateServRoles(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(role(11, "Counsellor")))); + + assertSuccessContaining(controller.getAllRoleNew("{\"providerServiceMapID\":4001}"), "Counsellor"); + } + + @Test + @DisplayName("getAllRoleNew should answer an error envelope when the lookup fails") + void getAllRoleNew_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getProStateServRoles(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRoleNew("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getAllRoles should answer the roles the newer query resolves") + void getAllRoles_shouldAnswerResolvedRoles() { + when(roleMasterInter.getProStateServRolesV1(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(role(11, "Counsellor")))); + + assertSuccessContaining(controller.getAllRoles("{\"providerServiceMapID\":4001}"), "Counsellor"); + } + + @Test + @DisplayName("getAllRoles should answer an error envelope when the lookup fails") + void getAllRoles_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getProStateServRolesV1(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRoles("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("saveRole should map each screen the request names onto the role it stored") + void saveRole_shouldMapScreensOntoStoredRole() { + when(roleMasterInter.addRole(anyList())).thenReturn(List.of(role(11, "Counsellor"))); + + String response = controller.saveRole("[{\"roleName\":\"Counsellor\",\"providerServiceMapID\":4001," + + "\"createdBy\":\"admin\",\"screenID\":[21,22]}]"); + + assertSuccessContaining(response, "Counsellor"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(roleScreenMappingRepo).saveAll(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals(11, captor.getValue().get(0).getRoleID()); + assertEquals(21, captor.getValue().get(0).getScreenID()); + } + + @Test + @DisplayName("saveRole should leave the screens unmapped when the counts do not line up") + void saveRole_shouldLeaveScreensUnmappedOnMismatch() { + when(roleMasterInter.addRole(anyList())) + .thenReturn(List.of(role(11, "Counsellor"), role(12, "Supervisor"))); + + controller.saveRole("[{\"roleName\":\"Counsellor\",\"screenID\":[21]}]"); + + verify(roleScreenMappingRepo, never()).saveAll(anyList()); + } + + @Test + @DisplayName("saveRole should answer an error envelope when the store fails") + void saveRole_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.addRole(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveRole("[{\"roleName\":\"Counsellor\"}]")); + } + + @Test + @DisplayName("editRole should copy the edits onto the stored role and remap its screen") + void editRole_shouldCopyEditsAndRemapScreen() { + RoleMaster stored = role(11, "old name"); + when(roleMasterInter.getRoleByRoleId(11)).thenReturn(stored); + when(roleMasterInter.modifydata(stored)).thenReturn(stored); + when(roleMasterInter.settingScreenId(31, 21)).thenReturn("screen mapped"); + + String response = controller.editRole("{\"roleID\":11,\"roleName\":\"Counsellor\"," + + "\"roleDesc\":\"Handles counselling calls\",\"sRSMappingID\":31,\"screenID\":21}"); + + assertSuccessContaining(response, "screen mapped"); + assertEquals("Counsellor", stored.getRoleName()); + assertEquals("Handles counselling calls", stored.getRoleDesc()); + } + + @Test + @DisplayName("editRole should answer an error envelope for a role that does not exist") + void editRole_shouldAnswerErrorEnvelopeForUnknownRole() { + when(roleMasterInter.getRoleByRoleId(11)).thenReturn(null); + + assertCodeException(controller.editRole("{\"roleID\":11}")); + } + + @Test + @DisplayName("deleteRole should mark the role deleted and answer the outcome") + void deleteRole_shouldMarkRoleDeleted() { + RoleMaster stored = role(11, "Counsellor"); + when(roleMasterInter.getRoleByRoleId(11)).thenReturn(stored); + when(roleMasterInter.deletedata(stored)).thenReturn("role deleted"); + + assertSuccessContaining(controller.deleteRole("{\"roleID\":11,\"deleted\":true}"), "role deleted"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteRole should answer an error envelope for a role that does not exist") + void deleteRole_shouldAnswerErrorEnvelopeForUnknownRole() { + when(roleMasterInter.getRoleByRoleId(11)).thenReturn(null); + + assertCodeException(controller.deleteRole("{\"roleID\":11,\"deleted\":true}")); + } + + @Test + @DisplayName("searchFeature should answer the screens the service line offers") + void searchFeature_shouldAnswerServiceScreens() { + M_Screen screen = new M_Screen(); + screen.setScreenID(21); + screen.setScreenName("Call handling"); + when(roleMasterInter.getAllFeature(3)).thenReturn(new ArrayList<>(List.of(screen))); + + assertSuccessContaining(controller.searchFeature("{\"serviceID\":3}"), "Call handling"); + } + + @Test + @DisplayName("searchFeature should answer an error envelope when the lookup fails") + void searchFeature_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getAllFeature(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchFeature("{\"serviceID\":3}")); + } + + @Test + @DisplayName("deleteFeature should retire the role the screen mapping points at") + void deleteFeature_shouldRetireMappedRole() { + RoleMaster stored = role(11, "Counsellor"); + when(roleMasterInter.getRoleByRoleId(11)).thenReturn(stored); + when(roleMasterInter.deletedata(stored)).thenReturn("role deleted"); + + assertSuccessContaining(controller.deleteFeature("{\"roleID\":11,\"screenID\":21}"), "role deleted"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteFeature should answer an error envelope for a role that does not exist") + void deleteFeature_shouldAnswerErrorEnvelopeForUnknownRole() { + when(roleMasterInter.getRoleByRoleId(11)).thenReturn(null); + + assertCodeException(controller.deleteFeature("{\"roleID\":11}")); + } + + @Test + @DisplayName("getAllRole1 should resolve the mapping before reading its roles") + void getAllRole1_shouldResolveMappingFirst() { + when(roleMasterInter.getAllByMapId(PROVIDER_ID, 29, 3, Boolean.FALSE)) + .thenReturn(new ArrayList<>(List.of(stateMapping(PSM_ID)))); + when(roleMasterInter.getProStateServRoles1(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(role(11, "Counsellor")))); + + assertSuccessContaining(controller.getAllRole1("{\"serviceProviderID\":77,\"stateID\":29," + + "\"serviceID\":3,\"isNational\":false}"), "Counsellor"); + } + + @Test + @DisplayName("getAllRole1 should answer an error envelope when the lookup fails") + void getAllRole1_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getAllByMapId(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRole1("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("editRolefeature should answer the screen mappings the service stored") + void editRolefeature_shouldAnswerStoredMappings() { + RoleScreenMapping mapping = new RoleScreenMapping(); + mapping.setsRSMappingID(31); + when(roleMasterInter.mapfeature(anyList())).thenReturn(List.of(mapping)); + + assertSuccessContaining(controller.editRolefeature("[{\"roleID\":11,\"screenID\":21}]"), "31"); + } + + @Test + @DisplayName("editRolefeature should answer an error envelope when the mapping fails") + void editRolefeature_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.mapfeature(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.editRolefeature("[{\"roleID\":11}]")); + } + + @Test + @DisplayName("searchRoleTM should answer the telemedicine roles under the mapping") + void searchRoleTM_shouldAnswerTelemedicineRoles() { + when(roleMasterInter.getRoleMasterTM(PSM_ID)).thenReturn(List.of(role(11, "TC Specialist"))); + + assertSuccessContaining(controller.searchRoleTM("{\"providerServiceMapID\":4001}"), "TC Specialist"); + } + + @Test + @DisplayName("searchRoleTM should answer an error envelope when the lookup fails") + void searchRoleTM_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getRoleMasterTM(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.searchRoleTM("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getAllRoleActive should answer only the roles still in use") + void getAllRoleActive_shouldAnswerActiveRoles() { + when(roleMasterInter.getProStateServRolesActive(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(role(11, "Counsellor")))); + + assertSuccessContaining(controller.getAllRoleActive("{\"providerServiceMapID\":4001}"), "Counsellor"); + } + + @Test + @DisplayName("getAllRoleActive should answer an error envelope when the lookup fails") + void getAllRoleActive_shouldAnswerErrorEnvelopeOnFailure() { + when(roleMasterInter.getProStateServRolesActive(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllRoleActive("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("configWrapUptime should answer the role the service saved") + void configWrapUptime_shouldAnswerSavedRole() throws Exception { + RoleMaster stored = role(11, "Counsellor"); + when(roleMasterInter.configWrapUpTime(any())).thenReturn(stored); + + assertSuccessContaining(controller.configWrapUptime(role(11, "Counsellor")), "Counsellor"); + } + + @Test + @DisplayName("configWrapUptime should answer an error envelope when the change is refused") + void configWrapUptime_shouldAnswerErrorEnvelopeWhenRefused() throws Exception { + when(roleMasterInter.configWrapUpTime(any())) + .thenThrow(new IllegalStateException("wrap up time must be positive")); + + assertGenericFailure(controller.configWrapUptime(role(11, "Counsellor"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/servicePoint/ServicePointControllerTest.java b/src/test/java/com/iemr/admin/controller/servicePoint/ServicePointControllerTest.java new file mode 100644 index 0000000..f6eb25d --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/servicePoint/ServicePointControllerTest.java @@ -0,0 +1,274 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.servicePoint; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.DistrictBranchMapping; +import com.iemr.admin.data.servicePoint.M_Servicepoint; +import com.iemr.admin.data.servicePoint.M_Servicepointvillagemap; +import com.iemr.admin.service.servicePoint.ServicePointServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The service point endpoints keep the points a mobile unit halts at and the + * villages each point covers. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ServicePointController Test Suite") +class ServicePointControllerTest { + + private static final Integer POINT_ID = 71; + + @Mock + private ServicePointServiceImpl ServicePointServiceImpl; + + @InjectMocks + private ServicePointController controller; + + private static M_Servicepoint point(Integer id, String name) { + return new M_Servicepoint(id, name, "Weekly halt", "Main Road", 4001, Boolean.FALSE, 1, "India", + 29, "Karnataka", 301, "Bengaluru Urban", 401, "North block", 501, "Hosur", null, 3, + "Mobile Medical Unit", 31, "Hosur parking"); + } + + private static M_Servicepointvillagemap villageMap(Integer id) { + return new M_Servicepointvillagemap(id, 29, "Karnataka", 301, "Bengaluru Urban", 31, "Hosur parking", + 71, "Hosur halt", 501, "Hosur", 4001, Boolean.FALSE); + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("saveServicePoint should answer the service points the service stored") + void saveServicePoint_shouldAnswerStoredPoints() throws Exception { + when(ServicePointServiceImpl.saveServicePoint(anyList())) + .thenReturn(new ArrayList<>(List.of(point(POINT_ID, "Hosur halt")))); + + assertSuccessContaining( + controller.saveServicePoint("{\"servicePoints\":[{\"servicePointName\":\"Hosur halt\"}]}"), + "Hosur halt"); + } + + @Test + @DisplayName("saveServicePoint should answer an error envelope when the store fails") + void saveServicePoint_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(ServicePointServiceImpl.saveServicePoint(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveServicePoint("{\"servicePoints\":[{}]}")); + } + + @Test + @DisplayName("getServicePoints should answer the points matching the location filters") + void getServicePoints_shouldAnswerMatchingPoints() throws Exception { + when(ServicePointServiceImpl.getAvailableServicePoints(29, 301, 31, 77)) + .thenReturn(new ArrayList<>(List.of(point(POINT_ID, "Hosur halt")))); + + assertSuccessContaining(controller.getServicePoints("{\"stateID\":29,\"districtID\":301," + + "\"parkingPlaceID\":31,\"serviceProviderID\":77}"), "Hosur halt"); + } + + @Test + @DisplayName("getServicePoints should answer an error envelope when the lookup fails") + void getServicePoints_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(ServicePointServiceImpl.getAvailableServicePoints(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getServicePoints("{\"stateID\":29}")); + } + + @Test + @DisplayName("deleteServicePoint should report whether the point actually changed") + void deleteServicePoint_shouldReportOutcome() throws Exception { + when(ServicePointServiceImpl.updateServicePointStatus(any())).thenReturn(1); + assertSuccessContaining(controller.deleteServicePoint("{\"servicePointID\":71,\"deleted\":true}"), + "status updated successfully"); + + when(ServicePointServiceImpl.updateServicePointStatus(any())).thenReturn(0); + assertSuccessContaining(controller.deleteServicePoint("{\"servicePointID\":71,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteServicePoint should answer an error envelope when the change fails") + void deleteServicePoint_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(ServicePointServiceImpl.updateServicePointStatus(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.deleteServicePoint("{\"servicePointID\":71,\"deleted\":true}")); + } + + @Test + @DisplayName("editServicePoint should copy the edits onto the stored point") + void editServicePoint_shouldCopyEdits() throws Exception { + M_Servicepoint stored = point(POINT_ID, "old name"); + when(ServicePointServiceImpl.getdataForEditServicePointStatus(POINT_ID)).thenReturn(stored); + when(ServicePointServiceImpl.saveeditedData(stored)).thenReturn(stored); + + String response = controller.editServicePoint("{\"servicePointID\":71," + + "\"servicePointName\":\"Hosur halt\",\"servicePointDesc\":\"Weekly halt\"," + + "\"districtID\":301,\"districtBlockID\":401,\"servicePointHQAddress\":\"Main Road\"," + + "\"providerServiceMapID\":4001,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "Hosur halt"); + assertEquals("Weekly halt", stored.getServicePointDesc()); + assertEquals("Main Road", stored.getServicePointHQAddress()); + } + + @Test + @DisplayName("editServicePoint should answer an error envelope for a point that does not exist") + void editServicePoint_shouldAnswerErrorEnvelopeForUnknownPoint() throws Exception { + when(ServicePointServiceImpl.getdataForEditServicePointStatus(POINT_ID)).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.editServicePoint("{\"servicePointID\":71}"))); + } + + @Test + @DisplayName("saveServicePointVillageMap should answer the village maps the service stored") + void saveVillageMap_shouldAnswerStoredMaps() throws Exception { + when(ServicePointServiceImpl.saveServicePointVillageMap(anyList())) + .thenReturn(new ArrayList<>(List.of(villageMap(9001)))); + + assertSuccessContaining(controller.saveServicePointVillageMap( + "{\"servicePointVillageMaps\":[{\"servicePointID\":71,\"districtBranchID\":501}]}"), "9001"); + } + + @Test + @DisplayName("saveServicePointVillageMap should answer an error envelope when the store fails") + void saveVillageMap_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(ServicePointServiceImpl.saveServicePointVillageMap(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveServicePointVillageMap("{\"servicePointVillageMaps\":[{}]}")); + } + + @Test + @DisplayName("getServicePointVillageMaps should answer the maps matching the location filters") + void getVillageMaps_shouldAnswerMatchingMaps() throws Exception { + when(ServicePointServiceImpl.getAvailableServicePointVillageMaps(29, 301, 31, POINT_ID, 77)) + .thenReturn(new ArrayList<>(List.of(villageMap(9001)))); + + assertSuccessContaining(controller.getServicePointVillageMaps("{\"stateID\":29,\"districtID\":301," + + "\"parkingPlaceID\":31,\"servicePointID\":71,\"serviceProviderID\":77}"), "9001"); + } + + @Test + @DisplayName("getServicePointVillageMaps should answer an error envelope when the lookup fails") + void getVillageMaps_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(ServicePointServiceImpl.getAvailableServicePointVillageMaps(any(), any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getServicePointVillageMaps("{\"stateID\":29}")); + } + + @Test + @DisplayName("deleteServicePointVillageMap should report whether the map actually changed") + void deleteVillageMap_shouldReportOutcome() throws Exception { + when(ServicePointServiceImpl.updateServicePointVillageMapStatus(any(M_Servicepointvillagemap.class))) + .thenReturn(1); + assertSuccessContaining( + controller.deleteServicePointVillageMap("{\"servicePointVillageMapID\":9001,\"deleted\":true}"), + "status updated successfully"); + + when(ServicePointServiceImpl.updateServicePointVillageMapStatus(any(M_Servicepointvillagemap.class))) + .thenReturn(0); + assertSuccessContaining( + controller.deleteServicePointVillageMap("{\"servicePointVillageMapID\":9001,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("editServicePointVillageMap should copy the edits onto the stored map") + void editVillageMap_shouldCopyEdits() throws Exception { + M_Servicepointvillagemap stored = villageMap(9001); + when(ServicePointServiceImpl.updateServicePointVillageMapStatus(9001)).thenReturn(stored); + when(ServicePointServiceImpl.saveEditedData(stored)).thenReturn(stored); + + String response = controller.editServicePointVillageMap("{\"servicePointVillageMapID\":9001," + + "\"servicePointID\":72,\"districtBranchID\":502,\"providerServiceMapID\":4001," + + "\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "9001"); + assertEquals(72, stored.getServicePointID()); + assertEquals(502, stored.getDistrictBranchID()); + } + + @Test + @DisplayName("editServicePointVillageMap should answer an error envelope for a map that does not exist") + void editVillageMap_shouldAnswerErrorEnvelopeForUnknownMap() throws Exception { + when(ServicePointServiceImpl.updateServicePointVillageMapStatus(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, + statusCodeOf(controller.editServicePointVillageMap("{\"servicePointVillageMapID\":9001}"))); + } + + @Test + @DisplayName("unmappedvillages should answer the villages no service point covers yet") + void unmappedvillages_shouldAnswerUncoveredVillages() throws Exception { + when(ServicePointServiceImpl.getunmappedvillages(4001, 401)) + .thenReturn(List.of(new DistrictBranchMapping())); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(controller.unmappedvillages( + "{\"providerServiceMapID\":4001,\"districtBlockID\":401}"))); + } + + @Test + @DisplayName("unmappedvillages should answer an error envelope when the lookup fails") + void unmappedvillages_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(ServicePointServiceImpl.getunmappedvillages(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.unmappedvillages("{\"providerServiceMapID\":4001}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/snomedMapping/SnomedMappingControllerTest.java b/src/test/java/com/iemr/admin/controller/snomedMapping/SnomedMappingControllerTest.java new file mode 100644 index 0000000..75cb0c4 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/snomedMapping/SnomedMappingControllerTest.java @@ -0,0 +1,195 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.snomedMapping; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.admin.service.snomedMapping.SnomedService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * The snomed screen maps entries of the clinical masters onto SNOMED codes and + * reports back in the master's own words why a mapping was refused. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("SnomedMappingController Test Suite") +class SnomedMappingControllerTest { + + private static final String REQUEST = "{\"masterType\":\"Family History\",\"masterID\":12," + + "\"sctCode\":\"73211009\",\"modifiedBy\":\"admin\"}"; + + @Mock + private SnomedService snomedService; + + @InjectMocks + private SnomedMappingController controller; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static String errorMessageOf(String response) { + return new JSONObject(response).getString("errorMessage"); + } + + @Test + @DisplayName("editSnomedMaster should confirm the mapping it recorded") + void edit_shouldConfirmRecordedMapping() { + when(snomedService.editSnomedMappingData(any(JsonObject.class), anyString())).thenReturn("Data Updated"); + + String response = controller.editSnomedMaster(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data Updated successfully"), response); + } + + @Test + @DisplayName("editSnomedMaster should pass on the master type refusal in the service's own words") + void edit_shouldPassOnMasterTypeRefusal() { + when(snomedService.editSnomedMappingData(any(JsonObject.class), anyString())) + .thenReturn("Invalid Master Type"); + + String response = controller.editSnomedMaster(REQUEST); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response)); + assertEquals("Invalid Master Type", errorMessageOf(response)); + } + + @Test + @DisplayName("editSnomedMaster should report a general refusal when the mapping was not recorded") + void edit_shouldReportGeneralRefusal() { + when(snomedService.editSnomedMappingData(any(JsonObject.class), anyString())).thenReturn(null); + + String response = controller.editSnomedMaster(REQUEST); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response)); + assertEquals("Unable to update data", errorMessageOf(response)); + } + + @Test + @DisplayName("editSnomedMaster should report the failure when the request cannot be read") + void edit_shouldReportUnreadableRequest() { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.editSnomedMaster("{not json"))); + } + + @Test + @DisplayName("saveSnomedMaster should confirm the mappings it stored") + void save_shouldConfirmStoredMappings() { + when(snomedService.saveSnomedMappingData(any(JsonObject.class), anyString())).thenReturn("Data Saved"); + + String response = controller.saveSnomedMaster(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data Saved successfully"), response); + } + + @Test + @DisplayName("saveSnomedMaster should pass on the master type refusal in the service's own words") + void save_shouldPassOnMasterTypeRefusal() { + when(snomedService.saveSnomedMappingData(any(JsonObject.class), anyString())) + .thenReturn("Invalid Master Type"); + + assertEquals("Invalid Master Type", errorMessageOf(controller.saveSnomedMaster(REQUEST))); + } + + @Test + @DisplayName("saveSnomedMaster should report a general refusal when nothing was stored") + void save_shouldReportGeneralRefusal() { + when(snomedService.saveSnomedMappingData(any(JsonObject.class), anyString())).thenReturn(null); + + assertEquals("Unable to Save data", errorMessageOf(controller.saveSnomedMaster(REQUEST))); + } + + @Test + @DisplayName("saveSnomedMaster should report the failure when the request cannot be read") + void save_shouldReportUnreadableRequest() { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveSnomedMaster("{not json"))); + } + + @Test + @DisplayName("fetchSnomedWorklist should answer the master the request asked for") + void fetch_shouldAnswerRequestedMaster() { + when(snomedService.fetchSnomedMaster(any(JsonObject.class))).thenReturn("[{\"masterName\":\"Diabetes\"}]"); + + String response = controller.fetchSnomedWorklist(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Diabetes"), response); + } + + @Test + @DisplayName("fetchSnomedWorklist should report the failure when the worklist cannot be answered") + void fetch_shouldReportLookupFailure() { + when(snomedService.fetchSnomedMaster(any(JsonObject.class))).thenReturn(null); + + assertEquals("error in fetching worklist data", errorMessageOf(controller.fetchSnomedWorklist(REQUEST))); + } + + @Test + @DisplayName("fetchSnomedWorklist should report the failure when the request cannot be read") + void fetch_shouldReportUnreadableRequest() { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.fetchSnomedWorklist("{not json"))); + } + + @Test + @DisplayName("updateStatus should confirm the change it recorded") + void updateStatus_shouldConfirmRecordedChange() { + when(snomedService.updateStatus(anyString())).thenReturn("Data updated successfully"); + + String response = controller.updateStatus(REQUEST); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data updated successfully"), response); + } + + @Test + @DisplayName("updateStatus should report the failure when nothing came back from the service") + void updateStatus_shouldReportMissingAnswer() { + when(snomedService.updateStatus(anyString())).thenReturn(null); + + assertEquals("error in updating data", errorMessageOf(controller.updateStatus(REQUEST))); + } + + @Test + @DisplayName("updateStatus should report the failure when the change cannot be recorded") + void updateStatus_shouldReportStorageFailure() { + when(snomedService.updateStatus(anyString())).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.updateStatus(REQUEST))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/stockEntry/StockEntryControllerTest.java b/src/test/java/com/iemr/admin/controller/stockEntry/StockEntryControllerTest.java new file mode 100644 index 0000000..d85c5a3 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/stockEntry/StockEntryControllerTest.java @@ -0,0 +1,145 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.stockEntry; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.dao.DataIntegrityViolationException; + +import com.iemr.admin.data.stockExit.ItemStockExit; +import com.iemr.admin.data.stockentry.ItemStockEntry; +import com.iemr.admin.data.stockentry.PhysicalStockEntry; +import com.iemr.admin.service.stockEntry.StockEntryService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The stock entry screen records what arrives at a store and works out which + * batches an issue should draw on. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("StockEntryController Test Suite") +class StockEntryControllerTest { + + private static final Integer FACILITY_ID = 9001; + + @Mock + private StockEntryService stockEntryService; + + @InjectMocks + private StockEntryController controller; + + private static PhysicalStockEntry arrival() { + PhysicalStockEntry arrival = new PhysicalStockEntry(); + arrival.setPhyEntryID(7001); + arrival.setRefNo("GRN-101"); + return arrival; + } + + private static ItemStockEntry batch() { + ItemStockEntry batch = new ItemStockEntry(); + batch.setItemStockEntryID(1); + batch.setItemID(501); + batch.setFacilityID(FACILITY_ID); + batch.setBatchNo("B-77"); + return batch; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("physicalStockEntry should answer the arrival it recorded") + void physicalStockEntry_shouldAnswerRecordedArrival() { + when(stockEntryService.savePhysicalStockEntry(any(PhysicalStockEntry.class))).thenReturn(arrival()); + + assertSuccessContaining(controller.physicalStockEntry(arrival()), "GRN-101"); + } + + @Test + @DisplayName("physicalStockEntry should report the failure when a batch clashes with one on file") + void physicalStockEntry_shouldReportClashingBatch() { + when(stockEntryService.savePhysicalStockEntry(any(PhysicalStockEntry.class))) + .thenThrow(new DataIntegrityViolationException("duplicate batch number")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.physicalStockEntry(arrival()))); + } + + @Test + @DisplayName("getItemBatchForStoreID should answer the batches the store still holds") + void getItemBatch_shouldAnswerHeldBatches() { + when(stockEntryService.getItemBatchForStoreID(any(ItemStockEntry.class))).thenReturn(List.of(batch())); + + assertSuccessContaining(controller.getItemBatchForStoreID(batch()), "B-77"); + } + + @Test + @DisplayName("getItemBatchForStoreID should report the failure when the batches cannot be answered") + void getItemBatch_shouldReportLookupFailure() { + when(stockEntryService.getItemBatchForStoreID(any(ItemStockEntry.class))) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getItemBatchForStoreID(batch()))); + } + + @Test + @DisplayName("allocateStockFromItemID should answer the batches the issue should draw on") + void allocate_shouldAnswerAllocatedBatches() { + when(stockEntryService.getItemStockFromItemID(anyInt(), anyList())).thenReturn(List.of(batch())); + + assertSuccessContaining( + controller.allocateStockFromItemID(FACILITY_ID, new ArrayList()), "B-77"); + } + + @Test + @DisplayName("allocateStockFromItemID should report the failure when the allocation cannot be worked out") + void allocate_shouldReportAllocationFailure() { + when(stockEntryService.getItemStockFromItemID(anyInt(), anyList())) + .thenThrow(new RuntimeException("item is not on the item master")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.allocateStockFromItemID(FACILITY_ID, new ArrayList()))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/stockExit/StockExitControllerTest.java b/src/test/java/com/iemr/admin/controller/stockExit/StockExitControllerTest.java new file mode 100644 index 0000000..d2bea7b --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/stockExit/StockExitControllerTest.java @@ -0,0 +1,99 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.stockExit; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.stockExit.T_PatientIssue; +import com.iemr.admin.service.stockExit.StockExitService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * The patient issue screen hands drugs out of a store to a patient. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("StockExitController Test Suite") +class StockExitControllerTest { + + @Mock + private StockExitService stockExitService; + + @InjectMocks + private StockExitController controller; + + private static T_PatientIssue issue() { + T_PatientIssue issue = new T_PatientIssue(); + issue.setPatientIssueID(5501); + issue.setFacilityID(9001); + issue.setIssueType("Manual"); + return issue; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("patientIssue should confirm the issue it recorded") + void patientIssue_shouldConfirmRecordedIssue() { + when(stockExitService.issuePatientDrugs(any(T_PatientIssue.class))).thenReturn(1); + + String response = controller.patientIssue(issue()); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Successfully Created"), response); + } + + @Test + @DisplayName("patientIssue should say the quantities are wrong when the store cannot cover the issue") + void patientIssue_shouldSayQuantitiesAreWrong() { + when(stockExitService.issuePatientDrugs(any(T_PatientIssue.class))).thenReturn(0); + + String response = controller.patientIssue(issue()); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Error in Quantity"), response); + } + + @Test + @DisplayName("patientIssue should report the failure when the issue cannot be recorded") + void patientIssue_shouldReportStorageFailure() { + when(stockExitService.issuePatientDrugs(any(T_PatientIssue.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.patientIssue(issue()))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/store/StoreControllerTest.java b/src/test/java/com/iemr/admin/controller/store/StoreControllerTest.java new file mode 100644 index 0000000..39f8fac --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/store/StoreControllerTest.java @@ -0,0 +1,453 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.store; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.store.FacilityVillageMapping; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.data.store.M_facilityMap; +import com.iemr.admin.data.store.V_FetchFacility; +import com.iemr.admin.service.store.StoreService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The store endpoints maintain the facility hierarchy an operator sees on the + * store screens. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("StoreController Test Suite") +class StoreControllerTest { + + private static final Integer FACILITY_ID = 501; + private static final Integer PSM_ID = 4001; + private static final Integer BLOCK_ID = 401; + + @Mock + private StoreService storeService; + + @InjectMocks + private StoreController controller; + + private static M_Facility facility(Integer id, String name) { + M_Facility facility = new M_Facility(); + facility.setFacilityID(id); + facility.setFacilityName(name); + return facility; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + @Test + @DisplayName("createStore should answer the facilities the service stored") + void createStore_shouldAnswerStoredFacilities() { + when(storeService.addAllMainStore(anyList())).thenReturn(List.of(facility(FACILITY_ID, "PHC North"))); + + assertSuccessContaining(controller.createStore("[{\"facilityName\":\"PHC North\"}]"), "PHC North"); + } + + @Test + @DisplayName("createStore should answer an error envelope when the store fails") + void createStore_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.addAllMainStore(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createStore("[{\"facilityName\":\"PHC North\"}]")); + } + + @Test + @DisplayName("editStore should copy the edited fields onto the stored facility") + void editStore_shouldCopyEditedFields() { + M_Facility stored = facility(FACILITY_ID, "PHC North"); + when(storeService.getMainStore(FACILITY_ID)).thenReturn(stored); + when(storeService.createMainStore(stored)).thenReturn(stored); + + String response = controller.editStore("{\"facilityID\":501,\"facilityDesc\":\"Primary centre\"," + + "\"location\":\"Main Road\",\"physicalLocation\":\"Block A\",\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "PHC North"); + assertEquals("Primary centre", stored.getFacilityDesc()); + assertEquals("Block A", stored.getPhysicalLocation()); + } + + @Test + @DisplayName("editStore should answer an error envelope for a facility that does not exist") + void editStore_shouldAnswerErrorEnvelopeForUnknownFacility() { + when(storeService.getMainStore(FACILITY_ID)).thenReturn(null); + + assertCodeException(controller.editStore("{\"facilityID\":501}")); + } + + @Test + @DisplayName("getAllStore should answer the facilities of the provider") + void getAllStore_shouldAnswerProviderFacilities() { + when(storeService.getAllMainStore(PSM_ID)).thenReturn(List.of(facility(FACILITY_ID, "PHC North"))); + + assertSuccessContaining(controller.getAllStore(PSM_ID), "PHC North"); + } + + @Test + @DisplayName("getAllStore should answer an error envelope when the lookup fails") + void getAllStore_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getAllMainStore(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllStore(PSM_ID)); + } + + @Test + @DisplayName("getMainFacility should answer the main facilities of the provider") + void getMainFacility_shouldAnswerMainFacilities() { + when(storeService.getMainFacility(PSM_ID, true)) + .thenReturn(new ArrayList<>(List.of(facility(FACILITY_ID, "PHC North")))); + + assertSuccessContaining( + controller.getMainFacility("{\"providerServiceMapID\":4001,\"isMainFacility\":true}"), "PHC North"); + } + + @Test + @DisplayName("getMainFacility should answer an error envelope when the lookup fails") + void getMainFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getMainFacility(any(), any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getMainFacility("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("getsubFacility should answer the facilities under the main facility") + void getsubFacility_shouldAnswerSubFacilities() { + when(storeService.getMainFacility(PSM_ID, false, 500)) + .thenReturn(new ArrayList<>(List.of(facility(FACILITY_ID, "Sub Centre")))); + + assertSuccessContaining(controller.getsubFacility("{\"providerServiceMapID\":4001," + + "\"isMainFacility\":false,\"mainFacilityID\":500}"), "Sub Centre"); + } + + @Test + @DisplayName("getsubFacility should answer an error envelope when the lookup fails") + void getsubFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getMainFacility(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getsubFacility("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("deleteStore should answer the facility the service retired") + void deleteStore_shouldAnswerRetiredFacility() throws Exception { + M_Facility request = facility(FACILITY_ID, "PHC North"); + when(storeService.deleteStore(request)).thenReturn(request); + + assertSuccessContaining(controller.deleteStore(request), "PHC North"); + } + + @Test + @DisplayName("deleteStore should report the reason the service refused to retire the facility") + void deleteStore_shouldReportRefusal() throws Exception { + M_Facility request = facility(FACILITY_ID, "PHC North"); + when(storeService.deleteStore(request)).thenThrow(new Exception("Child Stores are still active")); + + String response = controller.deleteStore(request); + + assertGenericFailure(response); + assertTrue(response.contains("Child Stores are still active"), response); + } + + @Test + @DisplayName("mapStore should answer how many mappings the service changed") + void mapStore_shouldAnswerChangedCount() { + when(storeService.mapStore(anyList())).thenReturn(2); + + assertSuccessContaining(controller.mapStore(List.of(new M_facilityMap())), "2"); + } + + @Test + @DisplayName("mapStore should answer an error envelope when the mapping fails") + void mapStore_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.mapStore(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.mapStore(List.of(new M_facilityMap()))); + } + + @Test + @DisplayName("deleteMapStore should answer how many mappings the service released") + void deleteMapStore_shouldAnswerReleasedCount() throws Exception { + M_facilityMap request = new M_facilityMap(); + when(storeService.deleteMapStore(request)).thenReturn(1); + + assertSuccessContaining(controller.deleteMapStore(request), "1"); + } + + @Test + @DisplayName("deleteMapStore should report the reason the service refused to release the mapping") + void deleteMapStore_shouldReportRefusal() throws Exception { + M_facilityMap request = new M_facilityMap(); + when(storeService.deleteMapStore(request)) + .thenThrow(new Exception("Please Unmap van under this Parking Place")); + + assertTrue(controller.deleteMapStore(request).contains("Please Unmap van under this Parking Place")); + } + + @Test + @DisplayName("getMapStore should answer the mapped facilities of the provider") + void getMapStore_shouldAnswerMappedFacilities() { + V_FetchFacility request = new V_FetchFacility(); + request.setProviderServiceMapID(PSM_ID); + V_FetchFacility mapped = new V_FetchFacility(); + mapped.setFacilityName("PHC North"); + when(storeService.getMapStore(request)).thenReturn(List.of(mapped)); + + assertSuccessContaining(controller.getMapStore(request), "PHC North"); + } + + @Test + @DisplayName("getMapStore should answer an error envelope when the lookup fails") + void getMapStore_shouldAnswerErrorEnvelopeOnFailure() { + V_FetchFacility request = new V_FetchFacility(); + when(storeService.getMapStore(request)).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getMapStore(request)); + } + + @Test + @DisplayName("getFacilitiesByBlock should answer the live facilities in the block") + void getFacilitiesByBlock_shouldAnswerLiveFacilities() { + when(storeService.getFacilitiesByBlock(BLOCK_ID)) + .thenReturn(new ArrayList<>(List.of(facility(FACILITY_ID, "PHC North")))); + + assertSuccessContaining(controller.getFacilitiesByBlock("{\"blockID\":401}"), "PHC North"); + } + + @Test + @DisplayName("getFacilitiesByBlock should answer an error envelope when the lookup fails") + void getFacilitiesByBlock_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getFacilitiesByBlock(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacilitiesByBlock("{\"blockID\":401}")); + } + + @Test + @DisplayName("getAllFacilitiesByBlock should answer the retired facilities of the block as well") + void getAllFacilitiesByBlock_shouldAnswerRetiredFacilitiesToo() { + when(storeService.getAllFacilitiesByBlock(BLOCK_ID)) + .thenReturn(new ArrayList<>(List.of(facility(FACILITY_ID, "PHC North")))); + + assertSuccessContaining(controller.getAllFacilitiesByBlock("{\"blockID\":401}"), "PHC North"); + } + + @Test + @DisplayName("getAllFacilitiesByBlock should answer an error envelope when the lookup fails") + void getAllFacilitiesByBlock_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getAllFacilitiesByBlock(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getAllFacilitiesByBlock("{\"blockID\":401}")); + } + + @Test + @DisplayName("checkStoreCode should report whether the facility code is already taken") + void checkStoreCode_shouldReportWhetherCodeIsTaken() { + when(storeService.checkStoreCode(any())).thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkStoreCode("{\"facilityCode\":\"PHC-1\"}"), "true"); + } + + @Test + @DisplayName("checkStoreCode should answer an error envelope when the check fails") + void checkStoreCode_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.checkStoreCode(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkStoreCode("{\"facilityCode\":\"PHC-1\"}")); + } + + @Test + @DisplayName("getFacilitiesByBlockAndLevel should pass every filter the screen sends through") + void getFacilitiesByBlockAndLevel_shouldPassFiltersThrough() { + when(storeService.getFacilitiesByBlockAndLevel(BLOCK_ID, 4, "Rural")) + .thenReturn(new ArrayList<>(List.of(facility(FACILITY_ID, "Sub Centre")))); + + assertSuccessContaining(controller.getFacilitiesByBlockAndLevel( + "{\"blockID\":401,\"levelValue\":4,\"ruralUrban\":\"Rural\"}"), "Sub Centre"); + verify(storeService).getFacilitiesByBlockAndLevel(BLOCK_ID, 4, "Rural"); + } + + @Test + @DisplayName("getFacilitiesByBlockAndLevel should answer an error envelope when the lookup fails") + void getFacilitiesByBlockAndLevel_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getFacilitiesByBlockAndLevel(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getFacilitiesByBlockAndLevel("{\"blockID\":401}")); + } + + @Test + @DisplayName("createFacilityWithHierarchy should pass the villages and children through to the service") + void createFacilityWithHierarchy_shouldPassHierarchyThrough() { + when(storeService.createFacilityWithHierarchy(any(), anyList(), anyInt(), anyList())) + .thenReturn(facility(FACILITY_ID, "PHC North")); + + String response = controller.createFacilityWithHierarchy("{\"facility\":{\"facilityName\":\"PHC North\"}," + + "\"villageIDs\":[601,602],\"mainVillageID\":601,\"childFacilityIDs\":[502]}"); + + assertSuccessContaining(response, "PHC North"); + verify(storeService).createFacilityWithHierarchy(any(), org.mockito.ArgumentMatchers.eq(List.of(601, 602)), + org.mockito.ArgumentMatchers.eq(601), org.mockito.ArgumentMatchers.eq(List.of(502))); + } + + @Test + @DisplayName("createFacilityWithHierarchy should report the reason the service refused the facility") + void createFacilityWithHierarchy_shouldReportRefusal() { + when(storeService.createFacilityWithHierarchy(any(), any(), any(), any())) + .thenThrow(new RuntimeException("Facility with this name already exists in this block")); + + assertTrue(controller.createFacilityWithHierarchy("{\"facility\":{\"facilityName\":\"PHC North\"}}") + .contains("already exists in this block")); + } + + @Test + @DisplayName("getMappedVillageIDs should answer the villages already spoken for in the block") + void getMappedVillageIDs_shouldAnswerSpokenForVillages() { + when(storeService.getMappedVillageIDs(BLOCK_ID)).thenReturn(List.of(601, 602)); + + assertSuccessContaining(controller.getMappedVillageIDs("{\"blockID\":401}"), "601"); + } + + @Test + @DisplayName("getMappedVillageIDs should answer an error envelope when the lookup fails") + void getMappedVillageIDs_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getMappedVillageIDs(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getMappedVillageIDs("{\"blockID\":401}")); + } + + @Test + @DisplayName("getVillageMappingsByFacility should answer the villages the facility serves") + void getVillageMappingsByFacility_shouldAnswerServedVillages() { + FacilityVillageMapping mapping = new FacilityVillageMapping(); + mapping.setDistrictBranchID(601); + when(storeService.getVillageMappingsByFacility(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(mapping))); + + assertSuccessContaining(controller.getVillageMappingsByFacility("{\"facilityID\":501}"), "601"); + } + + @Test + @DisplayName("getVillageMappingsByFacility should answer an error envelope when the lookup fails") + void getVillageMappingsByFacility_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getVillageMappingsByFacility(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getVillageMappingsByFacility("{\"facilityID\":501}")); + } + + @Test + @DisplayName("getChildFacilitiesByParent should answer the facilities under the parent") + void getChildFacilitiesByParent_shouldAnswerChildren() { + when(storeService.getChildFacilitiesByParent(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(facility(502, "Sub Centre")))); + + assertSuccessContaining(controller.getChildFacilitiesByParent("{\"facilityID\":501}"), "Sub Centre"); + } + + @Test + @DisplayName("getChildFacilitiesByParent should answer an error envelope when the lookup fails") + void getChildFacilitiesByParent_shouldAnswerErrorEnvelopeOnFailure() { + when(storeService.getChildFacilitiesByParent(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getChildFacilitiesByParent("{\"facilityID\":501}")); + } + + @Test + @DisplayName("updateFacilityWithHierarchy should answer the facility the service saved") + void updateFacilityWithHierarchy_shouldAnswerSavedFacility() { + when(storeService.updateFacilityWithHierarchy(any(), any(), any(), any())) + .thenReturn(facility(FACILITY_ID, "PHC North")); + + assertSuccessContaining(controller.updateFacilityWithHierarchy( + "{\"facility\":{\"facilityID\":501,\"facilityName\":\"PHC North\"}}"), "PHC North"); + } + + @Test + @DisplayName("updateFacilityWithHierarchy should report the reason the service refused the edit") + void updateFacilityWithHierarchy_shouldReportRefusal() { + when(storeService.updateFacilityWithHierarchy(any(), any(), any(), any())) + .thenThrow(new RuntimeException("Facility not found")); + + assertTrue(controller.updateFacilityWithHierarchy("{\"facility\":{\"facilityID\":501}}") + .contains("Facility not found")); + } + + @Test + @DisplayName("deleteFacilityWithHierarchy should answer the facility the service retired") + void deleteFacilityWithHierarchy_shouldAnswerRetiredFacility() throws Exception { + when(storeService.deleteFacilityWithHierarchy(FACILITY_ID, "admin")) + .thenReturn(facility(FACILITY_ID, "PHC North")); + + assertSuccessContaining( + controller.deleteFacilityWithHierarchy("{\"facilityID\":501,\"modifiedBy\":\"admin\"}"), + "PHC North"); + } + + @Test + @DisplayName("deleteFacilityWithHierarchy should report the reason the service refused the retirement") + void deleteFacilityWithHierarchy_shouldReportRefusal() throws Exception { + when(storeService.deleteFacilityWithHierarchy(anyInt(), anyString())) + .thenThrow(new Exception("Facility not found")); + + assertTrue(controller.deleteFacilityWithHierarchy("{\"facilityID\":501,\"modifiedBy\":\"admin\"}") + .contains("Facility not found")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/supplier/SupplierMasterControllerTest.java b/src/test/java/com/iemr/admin/controller/supplier/SupplierMasterControllerTest.java new file mode 100644 index 0000000..e6b8929 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/supplier/SupplierMasterControllerTest.java @@ -0,0 +1,169 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.supplier; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.supplier.M_Supplier; +import com.iemr.admin.service.supplier.SupplierInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The supplier master endpoints keep the supplier catalogue an inventory operator picks from. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("SupplierMasterController Test Suite") +class SupplierMasterControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private SupplierInter supplierInter; + + @InjectMocks + private SupplierMasterController controller; + + private static M_Supplier record(Integer id, String name) { + M_Supplier record = new M_Supplier(); + record.setSupplierID(id); + record.setSupplierName(name); + return record; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("createSupplier should answer the records the service stored") + void create_shouldAnswerStoredRecords() { + when(supplierInter.createSupplier(anyList())).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "MedSupply")))); + + assertSuccessContaining(controller.createSupplier("[{\"supplierCode\":\"C-1\"}]"), "MedSupply"); + } + + @Test + @DisplayName("createSupplier should answer an error envelope when the store fails") + void create_shouldAnswerErrorEnvelopeOnFailure() { + when(supplierInter.createSupplier(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createSupplier("[{}]")); + } + + @Test + @DisplayName("getSupplier should answer the records of the provider") + void get_shouldAnswerProviderRecords() { + when(supplierInter.getSupplier(PSM_ID)).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "MedSupply")))); + + assertSuccessContaining(controller.getSupplier("{\"providerServiceMapID\":4001}"), "MedSupply"); + } + + @Test + @DisplayName("getSupplier should answer an error envelope when the lookup fails") + void get_shouldAnswerErrorEnvelopeOnFailure() { + when(supplierInter.getSupplier(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getSupplier("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("editSupplier should copy the edits onto the stored record") + void edit_shouldCopyEdits() { + M_Supplier stored = record(RECORD_ID, "MedSupply"); + when(supplierInter.editSupplier(RECORD_ID)).thenReturn(stored); + when(supplierInter.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editSupplier("{\"supplierID\":11,\"supplierDesc\":\"Bulk supplier\",\"contactPerson\":\"Asha\",\"email\":\"asha@example.org\",\"modifiedBy\":\"admin\"}"), "MedSupply"); + assertEquals("Bulk supplier", stored.getSupplierDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editSupplier should answer an error envelope for a record that does not exist") + void edit_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(supplierInter.editSupplier(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.editSupplier("{\"supplierID\":11,\"supplierDesc\":\"Bulk supplier\",\"contactPerson\":\"Asha\",\"email\":\"asha@example.org\",\"modifiedBy\":\"admin\"}"))); + } + + @Test + @DisplayName("deleteSupplier should mark the record deleted") + void delete_shouldMarkRecordDeleted() { + M_Supplier stored = record(RECORD_ID, "MedSupply"); + when(supplierInter.editSupplier(RECORD_ID)).thenReturn(stored); + when(supplierInter.saveEditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteSupplier("{\"supplierID\":11,\"deleted\":true}"), "MedSupply"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteSupplier should answer an error envelope for a record that does not exist") + void delete_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(supplierInter.editSupplier(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.deleteSupplier("{\"supplierID\":11,\"deleted\":true}"))); + } + + @Test + @DisplayName("checkSupplierCode should report whether the code is already taken") + void check_shouldReportWhetherCodeIsTaken() { + when(supplierInter.checkSupplierCode(any())).thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkSupplierCode("{\"supplierCode\":\"C-1\"}"), "true"); + } + + @Test + @DisplayName("checkSupplierCode should answer an error envelope when the check fails") + void check_shouldAnswerErrorEnvelopeOnFailure() { + when(supplierInter.checkSupplierCode(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkSupplierCode("{\"supplierCode\":\"C-1\"}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/telemedicine/TeleMedicineControllerTest.java b/src/test/java/com/iemr/admin/controller/telemedicine/TeleMedicineControllerTest.java new file mode 100644 index 0000000..3338ca9 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/telemedicine/TeleMedicineControllerTest.java @@ -0,0 +1,205 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.telemedicine; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.telemedicine.M_UserTemp; +import com.iemr.admin.data.telemedicine.Specialization; +import com.iemr.admin.data.telemedicine.TMinput; +import com.iemr.admin.data.telemedicine.UserSpecializationMapping; +import com.iemr.admin.service.telemedicine.TMInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * The telemedicine screen lists the specialists a provider has and records + * which specialities each of them holds. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("TeleMedicineController Test Suite") +class TeleMedicineControllerTest { + + private static final Integer PROVIDER_ID = 5; + private static final Integer MAP_ID = 8001; + + @Mock + private TMInter tmInter; + + @InjectMocks + private TeleMedicineController controller; + + private static TMinput request() { + TMinput input = new TMinput(); + input.setServiceproviderID(PROVIDER_ID); + input.setScreenName("TM"); + return input; + } + + private static M_UserTemp specialist() { + M_UserTemp user = new M_UserTemp(); + user.setUserID(3117L); + user.setFirstName("Asha"); + user.setUserName("asha.rao"); + return user; + } + + private static Specialization speciality() { + Specialization speciality = new Specialization(); + speciality.setSpecializationID(11); + speciality.setSpecialization("Paediatrics"); + return speciality; + } + + private static UserSpecializationMapping held() { + UserSpecializationMapping mapping = new UserSpecializationMapping(); + mapping.setUserSpecializationMapID(MAP_ID); + mapping.setSpecializationName("Paediatrics"); + mapping.setDeleted(Boolean.FALSE); + mapping.setModifiedBy("admin"); + return mapping; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("getUserTM should answer the specialists on the screen asked for") + void getUserTM_shouldAnswerSpecialists() { + when(tmInter.getUser(any())).thenReturn(new ArrayList<>(List.of(specialist()))); + + assertSuccessContaining(controller.getUserTM(request()), "asha.rao"); + } + + @Test + @DisplayName("getUserTM should report the failure when the roster cannot be answered") + void getUserTM_shouldReportLookupFailure() { + when(tmInter.getUser(any())).thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getUserTM(request()))); + } + + @Test + @DisplayName("getSpecialization should answer the specialities on file") + void getSpecialization_shouldAnswerSpecialities() { + when(tmInter.getSpecialization()).thenReturn(new ArrayList<>(List.of(speciality()))); + + assertSuccessContaining(controller.getSpecialization(), "Paediatrics"); + } + + @Test + @DisplayName("getSpecialization should report the failure when the list cannot be answered") + void getSpecialization_shouldReportLookupFailure() { + when(tmInter.getSpecialization()).thenThrow(new RuntimeException("connection reset")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getSpecialization())); + } + + @Test + @DisplayName("getUserSpecialization should answer the specialities held under the provider asked for") + void getUserSpecialization_shouldAnswerHeldSpecialities() { + when(tmInter.getUserSpecialization(PROVIDER_ID)).thenReturn(new ArrayList<>(List.of(held()))); + + assertSuccessContaining(controller.getUserSpecialization(request()), "Paediatrics"); + } + + @Test + @DisplayName("getUserSpecialization should report the failure when the lookup cannot be answered") + void getUserSpecialization_shouldReportLookupFailure() { + when(tmInter.getUserSpecialization(any())).thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getUserSpecialization(request()))); + } + + @Test + @DisplayName("saveUserSpecialization should answer the specialities it recorded") + void saveUserSpecialization_shouldAnswerRecordedSpecialities() { + ArrayList request = new ArrayList<>(List.of(held())); + when(tmInter.saveUserSpecialization(request)).thenReturn(request); + + assertSuccessContaining(controller.saveUserSpecialization(request), "Paediatrics"); + } + + @Test + @DisplayName("saveUserSpecialization should report the failure when the speciality cannot be recorded") + void saveUserSpecialization_shouldReportStorageFailure() { + ArrayList request = new ArrayList<>(List.of(held())); + when(tmInter.saveUserSpecialization(request)).thenThrow(new RuntimeException("already recorded")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveUserSpecialization(request))); + } + + @Test + @DisplayName("activating a speciality should answer it with the status the caller asked for") + void activate_shouldAnswerSpecialityWithRequestedStatus() { + UserSpecializationMapping stored = held(); + UserSpecializationMapping request = held(); + request.setDeleted(Boolean.TRUE); + request.setModifiedBy("supervisor"); + when(tmInter.findUserSpecialization(request)).thenReturn(stored); + when(tmInter.saveoneUserSpecialization(stored)).thenReturn(stored); + + assertSuccessContaining(controller.saveUserSpecialization(request), "8001"); + assertEquals(Boolean.TRUE, stored.getDeleted()); + assertEquals("supervisor", stored.getModifiedBy()); + } + + @Test + @DisplayName("activating a speciality should report the failure when it is unknown") + void activate_shouldReportUnknownSpeciality() { + when(tmInter.findUserSpecialization(any())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.saveUserSpecialization(held()))); + } + + @Test + @DisplayName("activating a speciality should report the failure when the change cannot be recorded") + void activate_shouldReportStorageFailure() { + UserSpecializationMapping stored = held(); + when(tmInter.findUserSpecialization(any())).thenReturn(stored); + when(tmInter.saveoneUserSpecialization(stored)).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveUserSpecialization(held()))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/telemedicine/VideoConsultationControllerTest.java b/src/test/java/com/iemr/admin/controller/telemedicine/VideoConsultationControllerTest.java new file mode 100644 index 0000000..5e44a86 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/telemedicine/VideoConsultationControllerTest.java @@ -0,0 +1,199 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.telemedicine; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.telemedicine.M_UserTemp; +import com.iemr.admin.data.telemedicine.UserVideoConsultation; +import com.iemr.admin.data.telemedicine.VideoConsultationDomain; +import com.iemr.admin.service.telemedicine.VideoConsultationInter; +import com.iemr.admin.utils.exception.VideoConsultationException; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * The video consultation screen mints conferencing accounts for clinicians and + * keeps their sign-in details and status up to date. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("VideoConsultationController Test Suite") +class VideoConsultationControllerTest { + + private static final Integer PROVIDER_ID = 5; + private static final Long MAP_ID = 8001L; + + @Mock + private VideoConsultationInter videoConsultationInter; + + @InjectMocks + private VideoConsultationController controller; + + private static M_UserTemp clinician() { + M_UserTemp user = new M_UserTemp(); + user.setUserID(3117L); + user.setUserName("asha.rao"); + return user; + } + + private static UserVideoConsultation account() { + UserVideoConsultation account = new UserVideoConsultation(); + account.setUserVideoConsultationMapID(MAP_ID); + account.setUserID(3117L); + account.setVideoConsultationEmailID("asha.rao@example.org"); + account.setVideoConsultationDomain("psmri"); + return account; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("getUserTM should answer the clinicians who have no conferencing account yet") + void getUnmapped_shouldAnswerCliniciansWithoutAccount() { + when(videoConsultationInter.getunmappedUser(PROVIDER_ID, 7)).thenReturn(List.of(clinician())); + + assertSuccessContaining(controller.getUserTM(PROVIDER_ID, 7), "asha.rao"); + } + + @Test + @DisplayName("getUserTM should report the failure when the candidates cannot be worked out") + void getUnmapped_shouldReportLookupFailure() { + when(videoConsultationInter.getunmappedUser(any(), any())) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getUserTM(PROVIDER_ID, 7))); + } + + @Test + @DisplayName("createUserTM should answer the conferencing account it opened") + void createUser_shouldAnswerOpenedAccount() throws Exception { + when(videoConsultationInter.createUser(any())).thenReturn(account()); + + assertSuccessContaining(controller.createUserTM(account()), "asha.rao@example.org"); + } + + @Test + @DisplayName("createUserTM should report the platform's reason when the account is refused") + void createUser_shouldReportRemoteRefusal() throws Exception { + when(videoConsultationInter.createUser(any())) + .thenThrow(new VideoConsultationException("email already registered")); + + assertEquals(OutputResponse.VIDEOCONSULTATION_EXCEPTION, statusCodeOf(controller.createUserTM(account()))); + } + + @Test + @DisplayName("editUser should answer the account whose sign-in details it changed") + void editUser_shouldAnswerChangedAccount() throws Exception { + when(videoConsultationInter.editUser(any())).thenReturn(account()); + + assertSuccessContaining(controller.editUser(account()), "8001"); + } + + @Test + @DisplayName("editUser should report the failure when the account is unknown") + void editUser_shouldReportUnknownAccount() throws Exception { + when(videoConsultationInter.editUser(any())).thenThrow(new VideoConsultationException("Invalid MapID")); + + assertEquals(OutputResponse.VIDEOCONSULTATION_EXCEPTION, statusCodeOf(controller.editUser(account()))); + } + + @Test + @DisplayName("deleting an account should answer it with the status the caller asked for") + void deleteUser_shouldAnswerAccountWithRequestedStatus() throws Exception { + UserVideoConsultation retired = account(); + retired.setDeleted(Boolean.TRUE); + when(videoConsultationInter.deleteUser(MAP_ID, Boolean.TRUE, "supervisor")).thenReturn(retired); + + assertSuccessContaining(controller.createUserTM("supervisor", MAP_ID, Boolean.TRUE), "8001"); + } + + @Test + @DisplayName("deleting an account should report the failure when the platform refuses the change") + void deleteUser_shouldReportRemoteRefusal() throws Exception { + when(videoConsultationInter.deleteUser(anyLong(), anyBoolean(), anyString())) + .thenThrow(new VideoConsultationException("account is locked")); + + assertEquals(OutputResponse.VIDEOCONSULTATION_EXCEPTION, + statusCodeOf(controller.createUserTM("supervisor", MAP_ID, Boolean.TRUE))); + } + + @Test + @DisplayName("getmappedUsers should answer the accounts held under the provider asked for") + void getmappedUsers_shouldAnswerAccountsOfProvider() { + when(videoConsultationInter.fetchmappedUser(PROVIDER_ID)).thenReturn(new ArrayList<>(List.of(account()))); + + assertSuccessContaining(controller.getmappedUsers(PROVIDER_ID), "asha.rao@example.org"); + } + + @Test + @DisplayName("getmappedUsers should report the failure when the lookup cannot be answered") + void getmappedUsers_shouldReportLookupFailure() { + when(videoConsultationInter.fetchmappedUser(any())).thenThrow(new RuntimeException("connection reset")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getmappedUsers(PROVIDER_ID))); + } + + @Test + @DisplayName("getdomain should answer the conferencing domains on file") + void getdomain_shouldAnswerDomains() { + VideoConsultationDomain domain = new VideoConsultationDomain(); + domain.setVideoConsultationDomainID(1); + domain.setVideoConsultationDoamin("psmri"); + when(videoConsultationInter.getdomain(PROVIDER_ID)).thenReturn(List.of(domain)); + + assertSuccessContaining(controller.getdomain(PROVIDER_ID), "psmri"); + } + + @Test + @DisplayName("getdomain should report the failure when the domains cannot be answered") + void getdomain_shouldReportLookupFailure() { + when(videoConsultationInter.getdomain(any())).thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getdomain(PROVIDER_ID))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/uom/UomControllerTest.java b/src/test/java/com/iemr/admin/controller/uom/UomControllerTest.java new file mode 100644 index 0000000..5651471 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/uom/UomControllerTest.java @@ -0,0 +1,169 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.uom; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.uom.M_Uom; +import com.iemr.admin.service.uom.UomInter; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The unit of measure master endpoints keep the unit of measure catalogue an inventory operator picks from. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("UomController Test Suite") +class UomControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private UomInter uomInter; + + @InjectMocks + private UomController controller; + + private static M_Uom record(Integer id, String name) { + M_Uom record = new M_Uom(); + record.setuOMID(id); + record.setuOMName(name); + return record; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + @Test + @DisplayName("createUom should answer the records the service stored") + void create_shouldAnswerStoredRecords() { + when(uomInter.createDrugtypeData(anyList())).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "Tablet")))); + + assertSuccessContaining(controller.createUom("[{\"uOMCode\":\"C-1\"}]"), "Tablet"); + } + + @Test + @DisplayName("createUom should answer an error envelope when the store fails") + void create_shouldAnswerErrorEnvelopeOnFailure() { + when(uomInter.createDrugtypeData(anyList())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.createUom("[{}]")); + } + + @Test + @DisplayName("getUom should answer the records of the provider") + void get_shouldAnswerProviderRecords() { + when(uomInter.createDrugtypeData(PSM_ID)).thenReturn(new ArrayList<>(List.of(record(RECORD_ID, "Tablet")))); + + assertSuccessContaining(controller.getUom("{\"providerServiceMapID\":4001}"), "Tablet"); + } + + @Test + @DisplayName("getUom should answer an error envelope when the lookup fails") + void get_shouldAnswerErrorEnvelopeOnFailure() { + when(uomInter.createDrugtypeData(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getUom("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("editUom should copy the edits onto the stored record") + void edit_shouldCopyEdits() { + M_Uom stored = record(RECORD_ID, "Tablet"); + when(uomInter.editDrugtypeData(RECORD_ID)).thenReturn(stored); + when(uomInter.saveeditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.editUom("{\"uOMID\":11,\"uOMName\":\"Tablet\",\"uOMDesc\":\"One tablet\",\"uOMCode\":\"TAB\",\"status\":\"Active\",\"modifiedBy\":\"admin\"}"), "Tablet"); + assertEquals("One tablet", stored.getuOMDesc()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("editUom should answer an error envelope for a record that does not exist") + void edit_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(uomInter.editDrugtypeData(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.editUom("{\"uOMID\":11,\"uOMName\":\"Tablet\",\"uOMDesc\":\"One tablet\",\"uOMCode\":\"TAB\",\"status\":\"Active\",\"modifiedBy\":\"admin\"}"))); + } + + @Test + @DisplayName("deleteUom should mark the record deleted") + void delete_shouldMarkRecordDeleted() { + M_Uom stored = record(RECORD_ID, "Tablet"); + when(uomInter.editDrugtypeData(RECORD_ID)).thenReturn(stored); + when(uomInter.saveeditedData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.deleteUom("{\"uOMID\":11,\"deleted\":true}"), "Tablet"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteUom should answer an error envelope for a record that does not exist") + void delete_shouldAnswerErrorEnvelopeForUnknownRecord() { + when(uomInter.editDrugtypeData(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.deleteUom("{\"uOMID\":11,\"deleted\":true}"))); + } + + @Test + @DisplayName("checkUomCode should report whether the code is already taken") + void check_shouldReportWhetherCodeIsTaken() { + when(uomInter.checkUomCode(any())).thenReturn(Boolean.TRUE); + + assertSuccessContaining(controller.checkUomCode("{\"uOMCode\":\"C-1\"}"), "true"); + } + + @Test + @DisplayName("checkUomCode should answer an error envelope when the check fails") + void check_shouldAnswerErrorEnvelopeOnFailure() { + when(uomInter.checkUomCode(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.checkUomCode("{\"uOMCode\":\"C-1\"}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/uptsu/FacilityControllerTest.java b/src/test/java/com/iemr/admin/controller/uptsu/FacilityControllerTest.java new file mode 100644 index 0000000..7e58df8 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/uptsu/FacilityControllerTest.java @@ -0,0 +1,155 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.uptsu; + +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.uptsu.CDSSMapping; +import com.iemr.admin.data.uptsu.M_FacilityMapping; +import com.iemr.admin.service.uptsu.FacilityService; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +/** + * The UP TSU endpoints take the facility spreadsheet an operator uploads and the + * CDSS switch that decides whether decision support is on for a provider. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("FacilityController Test Suite") +class FacilityControllerTest { + + private static final Integer PSM_ID = 4001; + private static final String AUTH = "sess-0d5f3a7c"; + + @Mock + private FacilityService uptsuService; + + @InjectMocks + private FacilityController controller; + + private static final String UPLOAD_REQUEST = "{\"createdBy\":\"admin\",\"fileName\":\"facilities.xlsx\"," + + "\"providerServiceMapID\":4001,\"fileExtension\":\"xlsx\",\"fileContent\":\"data:x;base64,AAA=\"}"; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + @Test + @DisplayName("saveFacilityData should report the upload once the service has stored it") + void saveFacilityData_shouldReportStoredUpload() throws Exception { + when(uptsuService.saveFacility(any())).thenReturn(List.of(new M_FacilityMapping())); + + String response = controller.saveFacilityData(UPLOAD_REQUEST, AUTH); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("saveFacilityData saved successfully"), response); + } + + @Test + @DisplayName("saveFacilityData should refuse an upload the service could not read") + void saveFacilityData_shouldRefuseUnreadableUpload() throws Exception { + when(uptsuService.saveFacility(any())) + .thenThrow(new com.iemr.admin.utils.exception.IEMRException("Error in validating cell")); + + String response = controller.saveFacilityData(UPLOAD_REQUEST, AUTH); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + assertTrue(response.contains("Invalid Request"), response); + } + + @Test + @DisplayName("saveFacilityData should refuse a request body that is not a valid upload at all") + void saveFacilityData_shouldRefuseMalformedRequest() { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveFacilityData("not json", AUTH))); + } + + @Test + @DisplayName("saveFacilityData should stay at its default when the service stored nothing") + void saveFacilityData_shouldStayAtDefaultWhenNothingStored() throws Exception { + when(uptsuService.saveFacility(any())).thenReturn(List.of()); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveFacilityData(UPLOAD_REQUEST, AUTH))); + } + + @Test + @DisplayName("saveCdssDetails should report the configuration once the service has stored it") + void saveCdssDetails_shouldReportStoredConfiguration() { + when(uptsuService.saveCdssDetails(any())).thenReturn(new CDSSMapping()); + + String response = controller.saveCdssDetails("{\"psmId\":4001,\"isCdss\":true}"); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Data saved successfully"), response); + } + + @Test + @DisplayName("saveCdssDetails should report the reason the service refused the configuration") + void saveCdssDetails_shouldReportRefusal() { + when(uptsuService.saveCdssDetails(any())).thenThrow(new IllegalStateException("no connection")); + + String response = controller.saveCdssDetails("{\"psmId\":4001}"); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + assertTrue(response.contains("no connection"), response); + } + + @Test + @DisplayName("saveCdssDetails should refuse a request body it cannot read") + void saveCdssDetails_shouldRefuseMalformedRequest() { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveCdssDetails("not json"))); + } + + @Test + @DisplayName("getCdssData should answer the configuration the service publishes") + void getCdssData_shouldAnswerPublishedConfiguration() throws Exception { + when(uptsuService.getCdssData(PSM_ID)).thenReturn("{\"psmId\":4001,\"isCdss\":true}"); + + String response = controller.getCdssData(PSM_ID, AUTH); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("4001"), response); + } + + @Test + @DisplayName("getCdssData should answer an error envelope when the lookup fails") + void getCdssData_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(uptsuService.getCdssData(anyInt())).thenThrow(new IllegalStateException("no connection")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getCdssData(PSM_ID, AUTH))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/userParkingPlaceMap/UserParkingPlaceMapControllerTest.java b/src/test/java/com/iemr/admin/controller/userParkingPlaceMap/UserParkingPlaceMapControllerTest.java new file mode 100644 index 0000000..e9a169b --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/userParkingPlaceMap/UserParkingPlaceMapControllerTest.java @@ -0,0 +1,289 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.userParkingPlaceMap; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.userParkingPlaceMap.M_UserParkingPlaceMap; +import com.iemr.admin.data.userParkingPlaceMap.M_UserVanMapping; +import com.iemr.admin.service.userParkingPlaceMap.UserParkingPlaceMapServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The user parking place endpoints post a field user to the parking place they + * report to, and to the vans they work out of. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("UserParkingPlaceMapController Test Suite") +class UserParkingPlaceMapControllerTest { + + private static final Integer MAP_ID = 9001; + private static final Integer PSM_ID = 4001; + + @Mock + private UserParkingPlaceMapServiceImpl userParkingPlaceMapServiceImpl; + + @InjectMocks + private UserParkingPlaceMapController controller; + + private static M_UserParkingPlaceMap mapping(Integer id) { + return new M_UserParkingPlaceMap(id, 3117, "Asha", "Rao", "asha.rao", 7, 301, 31, "Hosur parking", + PSM_ID, Boolean.FALSE, Boolean.FALSE); + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + @Test + @DisplayName("saveuserParkingPlaces should answer the postings the service stored") + void save_shouldAnswerStoredPostings() throws Exception { + when(userParkingPlaceMapServiceImpl.saveUserParkingPlaceDetails(anyList())) + .thenReturn(new ArrayList<>(List.of(mapping(MAP_ID)))); + + assertSuccessContaining(controller.saveuserParkingPlaces( + "{\"userParkingPlaceMaps\":[{\"userID\":3117,\"parkingPlaceID\":31}]}"), "Asha"); + } + + @Test + @DisplayName("saveuserParkingPlaces should answer an error envelope when the store fails") + void save_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(userParkingPlaceMapServiceImpl.saveUserParkingPlaceDetails(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveuserParkingPlaces("{\"userParkingPlaceMaps\":[{}]}")); + } + + @Test + @DisplayName("getuserParkingPlaces should answer the postings matching the location filters") + void get_shouldAnswerMatchingPostings() throws Exception { + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceMappings(77, 29, 301, 31, 7)) + .thenReturn(new ArrayList<>(List.of(mapping(MAP_ID)))); + + assertSuccessContaining(controller.getuserParkingPlaces("{\"serviceProviderID\":77,\"stateID\":29," + + "\"districtID\":301,\"parkingPlaceID\":31,\"m_user\":{\"designationID\":7}}"), "Asha"); + } + + @Test + @DisplayName("getuserParkingPlaces should answer an error envelope when the request names no user detail") + void get_shouldAnswerErrorEnvelopeWithoutUserDetail() throws Exception { + assertCodeException(controller.getuserParkingPlaces("{\"serviceProviderID\":77}")); + } + + @Test + @DisplayName("getuserParkingPlacesDesiganation should narrow the postings to the designation") + void getByDesignation_shouldNarrowToDesignation() throws Exception { + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceMappings1(PSM_ID, 301, 31, 7)) + .thenReturn(new ArrayList<>(List.of(mapping(MAP_ID)))); + + assertSuccessContaining(controller.getuserParkingPlacesDesiganation( + "{\"providerServiceMapID\":4001,\"districtID\":301,\"parkingPlaceID\":31," + + "\"designationID\":7}"), "Asha"); + } + + @Test + @DisplayName("getuserParkingPlacesDesiganation should answer an error envelope when the lookup fails") + void getByDesignation_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceMappings1(any(), any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getuserParkingPlacesDesiganation("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("deleteuserParkingPlaceDetails should report whether the posting actually changed") + void deleteDetails_shouldReportOutcome() throws Exception { + when(userParkingPlaceMapServiceImpl.updateUserParkingPlaceMapStatus(any())).thenReturn(1); + assertSuccessContaining( + controller.deleteuserParkingPlaceDetails("{\"userParkingPlaceMapID\":9001,\"deleted\":true}"), + "status updated successfully"); + + when(userParkingPlaceMapServiceImpl.updateUserParkingPlaceMapStatus(any())).thenReturn(0); + assertSuccessContaining( + controller.deleteuserParkingPlaceDetails("{\"userParkingPlaceMapID\":9001,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteuserParkingPlaceDetails should answer an error envelope when the change fails") + void deleteDetails_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(userParkingPlaceMapServiceImpl.updateUserParkingPlaceMapStatus(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure( + controller.deleteuserParkingPlaceDetails("{\"userParkingPlaceMapID\":9001,\"deleted\":true}")); + } + + @Test + @DisplayName("edituserParkingPlaces should copy the edits and remap the vans") + void edit_shouldCopyEditsAndRemapVans() throws Exception { + M_UserParkingPlaceMap stored = mapping(MAP_ID); + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceDetails(MAP_ID)).thenReturn(stored); + when(userParkingPlaceMapServiceImpl.saveediteddata(any(), anyList())).thenReturn(stored); + + String response = controller.edituserParkingPlaces("{\"userParkingPlaceMapID\":9001," + + "\"parkingPlaceID\":32,\"providerServiceMapID\":4002,\"districtID\":302," + + "\"modifiedBy\":\"admin\",\"uservanmapping\":[{\"vanID\":71}]}"); + + assertSuccessContaining(response, "Asha"); + assertEquals(32, stored.getParkingPlaceID()); + assertEquals(4002, stored.getProviderServiceMapID()); + } + + @Test + @DisplayName("edituserParkingPlaces should answer an error envelope for a posting that does not exist") + void edit_shouldAnswerErrorEnvelopeForUnknownPosting() throws Exception { + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceDetails(anyInt())).thenReturn(null); + + assertCodeException(controller.edituserParkingPlaces("{\"userParkingPlaceMapID\":9001}")); + } + + @Test + @DisplayName("deleteuserParkingPlaces should retire the posting the caller names") + void deletePostings_shouldRetirePosting() throws Exception { + M_UserParkingPlaceMap stored = mapping(MAP_ID); + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceDetails(MAP_ID)).thenReturn(stored); + when(userParkingPlaceMapServiceImpl.saveediteddata(stored)).thenReturn(stored); + + assertSuccessContaining( + controller.deleteuserParkingPlaces("{\"userParkingPlaceMapID\":9001,\"deleted\":true}"), "Asha"); + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("deleteuserParkingPlaces should refuse to reinstate a user already posted elsewhere") + void deletePostings_shouldRefuseReinstatingPostedUser() throws Exception { + M_UserParkingPlaceMap stored = mapping(MAP_ID); + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceDetails(MAP_ID)).thenReturn(stored); + when(userParkingPlaceMapServiceImpl.getuserexist(PSM_ID, 3117)).thenReturn(Boolean.TRUE); + + String response = controller + .deleteuserParkingPlaces("{\"userParkingPlaceMapID\":9001,\"deleted\":false}"); + + assertGenericFailure(response); + assertTrue(response.contains("User already mapped. Cannot activate"), response); + } + + @Test + @DisplayName("deleteuserParkingPlaces should reinstate a user who is posted nowhere else") + void deletePostings_shouldReinstateFreeUser() throws Exception { + M_UserParkingPlaceMap stored = mapping(MAP_ID); + when(userParkingPlaceMapServiceImpl.getUserParkingPlaceDetails(MAP_ID)).thenReturn(stored); + when(userParkingPlaceMapServiceImpl.getuserexist(PSM_ID, 3117)).thenReturn(Boolean.FALSE); + when(userParkingPlaceMapServiceImpl.saveediteddata(stored)).thenReturn(stored); + + controller.deleteuserParkingPlaces("{\"userParkingPlaceMapID\":9001,\"deleted\":false}"); + + assertEquals(Boolean.FALSE, stored.getDeleted()); + } + + @Test + @DisplayName("unmappeduser should answer the users not yet posted anywhere") + void unmappeduser_shouldAnswerUnpostedUsers() throws Exception { + M_User1 user = new M_User1(); + user.setUserID(3117); + user.setFirstName("Asha"); + when(userParkingPlaceMapServiceImpl.getunmappedUser(PSM_ID, 7)).thenReturn(List.of(user)); + + assertSuccessContaining( + controller.unmappeduser("{\"providerServiceMapID\":4001,\"designationID\":7}"), "Asha"); + } + + @Test + @DisplayName("unmappeduser should answer an error envelope when the lookup fails") + void unmappeduser_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(userParkingPlaceMapServiceImpl.getunmappedUser(any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.unmappeduser("{\"providerServiceMapID\":4001}")); + } + + @Test + @DisplayName("mappedvan should answer the vans the posting covers") + void mappedvan_shouldAnswerCoveredVans() throws Exception { + M_UserVanMapping vanMapping = new M_UserVanMapping(); + vanMapping.setUserVanMapID(7001); + when(userParkingPlaceMapServiceImpl.getuservanmapping(MAP_ID)).thenReturn(List.of(vanMapping)); + + assertSuccessContaining(controller.mappedvan(MAP_ID), "7001"); + } + + @Test + @DisplayName("mappedvan should answer an error envelope when the lookup fails") + void mappedvan_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(userParkingPlaceMapServiceImpl.getuservanmapping(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.mappedvan(MAP_ID)); + } + + @Test + @DisplayName("deletemappedvan should report the van mapping it released") + void deletemappedvan_shouldReportReleasedMapping() throws Exception { + assertSuccessContaining( + controller.deletemappedvan("{\"userVanMapID\":7001,\"modifiedBy\":\"admin\"}"), "Success"); + verify(userParkingPlaceMapServiceImpl).deleteuservanmapping(any()); + } + + @Test + @DisplayName("deletemappedvan should answer an error envelope when the release fails") + void deletemappedvan_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + org.mockito.Mockito.doThrow(new IllegalStateException("no connection")) + .when(userParkingPlaceMapServiceImpl).deleteuservanmapping(any()); + + assertGenericFailure(controller.deletemappedvan("{\"userVanMapID\":7001}")); + } +} diff --git a/src/test/java/com/iemr/admin/controller/vanMaster/VanMasterControllerTest.java b/src/test/java/com/iemr/admin/controller/vanMaster/VanMasterControllerTest.java new file mode 100644 index 0000000..9a4e8f8 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/vanMaster/VanMasterControllerTest.java @@ -0,0 +1,281 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.vanMaster; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.vanMaster.M_Van; +import com.iemr.admin.data.vanType.M_VanType; +import com.iemr.admin.service.vanMaster.VanMasterServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The van screens keep the mobile unit fleet: what vans exist, what type each + * is, and which parking place or main store each belongs to. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("VanMasterController Test Suite") +class VanMasterControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer VAN_ID = 71; + private static final Integer PARKING_PLACE_ID = 31; + + @Mock + private VanMasterServiceImpl vanMasterServiceImpl; + + @InjectMocks + private VanMasterController controller; + + private static M_Van van() { + M_Van van = new M_Van(); + van.setVanID(VAN_ID); + van.setVanName("Mobile unit 7"); + van.setVehicalNo("KA-01-AB-1234"); + van.setParkingPlaceID(PARKING_PLACE_ID); + van.setProviderServiceMapID(PSM_ID); + return van; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("saveVanDetails should answer the vans it added to the fleet") + void saveVanDetails_shouldAnswerAddedVans() throws Exception { + when(vanMasterServiceImpl.saveVanDetails(anyList())).thenReturn(new ArrayList<>(List.of(van()))); + + assertSuccessContaining(controller.saveVanDetails( + "{\"vanMaster\":[{\"vanName\":\"Mobile unit 7\",\"vehicalNo\":\"KA-01-AB-1234\"}]}"), + "Mobile unit 7"); + } + + @Test + @DisplayName("saveVanDetails should report the failure when the van cannot be added") + void saveVanDetails_shouldReportStorageFailure() throws Exception { + when(vanMasterServiceImpl.saveVanDetails(anyList())) + .thenThrow(new RuntimeException("vehicle number already on file")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.saveVanDetails("{\"vanMaster\":[{\"vanName\":\"Mobile unit 7\"}]}"))); + } + + @Test + @DisplayName("getServicePoints should answer the vans of the parking place and type asked for") + void getVans_shouldAnswerVansOfParkingPlaceAndType() throws Exception { + when(vanMasterServiceImpl.getAvailableVans(PARKING_PLACE_ID, 2, PSM_ID)) + .thenReturn(new ArrayList<>(List.of(van()))); + + assertSuccessContaining(controller.getServicePoints( + "{\"parkingPlaceID\":31,\"vanTypeID\":2,\"providerServiceMapID\":4001}"), "Mobile unit 7"); + } + + @Test + @DisplayName("getServicePoints should report the failure when the fleet cannot be answered") + void getVans_shouldReportLookupFailure() throws Exception { + when(vanMasterServiceImpl.getAvailableVans(any(), any(), any())) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getServicePoints("{\"providerServiceMapID\":4001}"))); + } + + @Test + @DisplayName("deleteVanDetails should confirm the retirement it recorded") + void deleteVanDetails_shouldConfirmRetirement() throws Exception { + when(vanMasterServiceImpl.updateVanStatus(any(M_Van.class))).thenReturn(1); + + assertSuccessContaining(controller.deleteVanDetails("{\"vanID\":71,\"deleted\":true,\"modifiedBy\":\"admin\"}"), + "status updated successfully"); + } + + @Test + @DisplayName("deleteVanDetails should say so when no van was retired") + void deleteVanDetails_shouldSaySoWhenNothingRetired() throws Exception { + when(vanMasterServiceImpl.updateVanStatus(any(M_Van.class))).thenReturn(0); + + assertSuccessContaining(controller.deleteVanDetails("{\"vanID\":-1,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteVanDetails should report the failure when the retirement cannot be recorded") + void deleteVanDetails_shouldReportRetirementFailure() throws Exception { + when(vanMasterServiceImpl.updateVanStatus(any(M_Van.class))).thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.deleteVanDetails("{\"vanID\":71}"))); + } + + @Test + @DisplayName("updateZoneData should answer the van whose details it changed") + void updateVanDetails_shouldAnswerChangedVan() throws Exception { + M_Van stored = van(); + when(vanMasterServiceImpl.getVanByID(VAN_ID)).thenReturn(stored); + when(vanMasterServiceImpl.updateVanData(stored)).thenReturn(stored); + + assertSuccessContaining(controller.updateZoneData( + "{\"vanID\":71,\"vanName\":\"Mobile unit 9\",\"vehicalNo\":\"KA-01-AB-9999\",\"vanTypeID\":2," + + "\"stateID\":29,\"parkingPlaceID\":31,\"modifiedBy\":\"admin\"," + + "\"videoConsultationDomain\":\"psmri\"}"), + "Mobile unit 9"); + assertEquals("KA-01-AB-9999", stored.getVehicalNo()); + assertEquals("psmri", stored.getVideoConsultationDomain()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("updateZoneData should report the failure when the van is unknown") + void updateVanDetails_shouldReportUnknownVan() throws Exception { + when(vanMasterServiceImpl.getVanByID(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.updateZoneData("{\"vanID\":-1}"))); + } + + @Test + @DisplayName("saveVanTypeDetails should answer the van types it added") + void saveVanTypeDetails_shouldAnswerAddedTypes() throws Exception { + when(vanMasterServiceImpl.saveVanTypeDetails(anyList())).thenReturn( + new ArrayList<>(List.of(new M_VanType(2, "Diagnostic van", "Carries lab kit", Boolean.FALSE)))); + + assertSuccessContaining( + controller.saveVanTypeDetails("{\"vanTypeMaster\":[{\"vanType\":\"Diagnostic van\"}]}"), + "Diagnostic van"); + } + + @Test + @DisplayName("saveVanTypeDetails should report the failure when the van type cannot be added") + void saveVanTypeDetails_shouldReportStorageFailure() throws Exception { + when(vanMasterServiceImpl.saveVanTypeDetails(anyList())).thenThrow(new RuntimeException("already on file")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf( + controller.saveVanTypeDetails("{\"vanTypeMaster\":[{\"vanType\":\"Diagnostic van\"}]}"))); + } + + @Test + @DisplayName("getVanTypes should answer the van types on file") + void getVanTypes_shouldAnswerTypesOnFile() { + when(vanMasterServiceImpl.getVanTypes()).thenReturn( + new ArrayList<>(List.of(new M_VanType(2, "Diagnostic van", "Carries lab kit", Boolean.FALSE)))); + + assertSuccessContaining(controller.getVanTypes(), "Diagnostic van"); + } + + @Test + @DisplayName("getVanTypes should report the failure when the list cannot be answered") + void getVanTypes_shouldReportLookupFailure() { + when(vanMasterServiceImpl.getVanTypes()).thenThrow(new RuntimeException("connection reset")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getVanTypes())); + } + + @Test + @DisplayName("deleteVanType should confirm the retirement it recorded") + void deleteVanType_shouldConfirmRetirement() throws Exception { + when(vanMasterServiceImpl.updateVanTypeStatus(any(M_VanType.class))).thenReturn(1); + + assertSuccessContaining(controller.deleteVanType("{\"vanTypeID\":2,\"deleted\":true}"), + "status updated successfully"); + } + + @Test + @DisplayName("deleteVanType should say so when no van type was retired") + void deleteVanType_shouldSaySoWhenNothingRetired() throws Exception { + when(vanMasterServiceImpl.updateVanTypeStatus(any(M_VanType.class))).thenReturn(0); + + assertSuccessContaining(controller.deleteVanType("{\"vanTypeID\":-1,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteVanType should report the failure when the retirement cannot be recorded") + void deleteVanType_shouldReportRetirementFailure() throws Exception { + when(vanMasterServiceImpl.updateVanTypeStatus(any(M_VanType.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.deleteVanType("{\"vanTypeID\":2}"))); + } + + @Test + @DisplayName("getVanMaster should answer the vans of the provider and parking place asked for") + void getVanMaster_shouldAnswerVansOfProviderAndParkingPlace() throws Exception { + when(vanMasterServiceImpl.getVanMaster(PSM_ID, PARKING_PLACE_ID)).thenReturn(List.of(van())); + + assertSuccessContaining( + controller.getVanMaster("{\"providerServiceMapID\":4001,\"parkingPlaceID\":31}"), "Mobile unit 7"); + } + + @Test + @DisplayName("getVanMaster should report the failure when the lookup cannot be answered") + void getVanMaster_shouldReportLookupFailure() throws Exception { + when(vanMasterServiceImpl.getVanMaster(any(), any())).thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getVanMaster("{\"providerServiceMapID\":4001}"))); + } + + @Test + @DisplayName("getVanFromFacilityID should answer the vans belonging to the store asked for") + void getVanFromFacilityID_shouldAnswerVansOfStore() throws Exception { + M_Van request = new M_Van(); + request.setFacilityID(9001); + when(vanMasterServiceImpl.getVanFromFacilityID(9001)).thenReturn(List.of(van())); + + assertSuccessContaining(controller.getVanFromFacilityID(request), "Mobile unit 7"); + } + + @Test + @DisplayName("getVanFromFacilityID should report the failure when the store has no parking place") + void getVanFromFacilityID_shouldReportStoreWithoutParkingPlace() throws Exception { + M_Van request = new M_Van(); + request.setFacilityID(9001); + when(vanMasterServiceImpl.getVanFromFacilityID(9001)) + .thenThrow(new Exception("Main Store doesnt have any Parking place mapped")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getVanFromFacilityID(request))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/vanServicePointMapping/VanServicePointMappingControllerTest.java b/src/test/java/com/iemr/admin/controller/vanServicePointMapping/VanServicePointMappingControllerTest.java new file mode 100644 index 0000000..1aa87fd --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/vanServicePointMapping/VanServicePointMappingControllerTest.java @@ -0,0 +1,206 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.vanServicePointMapping; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.vanServicePointMapping.M_VanServicePointMap; +import com.iemr.admin.service.vanServicePointMapping.VanServicePointMappingServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The van service point screen records which service points each van visits, + * adding new visits and changing the session of ones already on file. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("VanServicePointMappingController Test Suite") +class VanServicePointMappingControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer VAN_ID = 71; + private static final Integer MAP_ID = 8801; + + @Mock + private VanServicePointMappingServiceImpl vanServicePointMappingServiceImpl; + + @InjectMocks + private VanServicePointMappingController controller; + + private static M_VanServicePointMap visit() { + return new M_VanServicePointMap(MAP_ID, VAN_ID, (short) 1, 88, "Attibele PHC", PSM_ID, Boolean.FALSE); + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("saveVanServicePointMappings should store a brand new visit as it stands") + void save_shouldStoreBrandNewVisit() throws Exception { + when(vanServicePointMappingServiceImpl.saveVanServicePointMappings(anyList())) + .thenReturn(new ArrayList<>(List.of(visit()))); + + assertSuccessContaining(controller.saveVanServicePointMappings( + "{\"vanServicePointMappings\":[{\"vanID\":71,\"servicePointID\":88,\"vanSession\":1," + + "\"createdBy\":\"admin\"}]}"), + "Attibele PHC"); + verify(vanServicePointMappingServiceImpl, never()).getVanServicePointMappingByID(anyInt()); + } + + @Test + @DisplayName("saveVanServicePointMappings should change the session of a visit already on file") + void save_shouldChangeSessionOfExistingVisit() throws Exception { + M_VanServicePointMap stored = visit(); + when(vanServicePointMappingServiceImpl.getVanServicePointMappingByID(MAP_ID)).thenReturn(stored); + when(vanServicePointMappingServiceImpl.saveVanServicePointMappings(anyList())) + .thenReturn(new ArrayList<>(List.of(stored))); + + controller.saveVanServicePointMappings( + "{\"vanServicePointMappings\":[{\"vanServicePointMapID\":8801,\"vanSession\":2," + + "\"createdBy\":\"supervisor\"}]}"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(vanServicePointMappingServiceImpl).saveVanServicePointMappings(captor.capture()); + assertEquals((short) 2, captor.getValue().get(0).getVanSession()); + assertEquals("supervisor", captor.getValue().get(0).getModifiedBy()); + } + + @Test + @DisplayName("saveVanServicePointMappings should report the failure when the visit named is unknown") + void save_shouldReportUnknownVisit() throws Exception { + when(vanServicePointMappingServiceImpl.getVanServicePointMappingByID(anyInt())).thenReturn(null); + + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(controller.saveVanServicePointMappings( + "{\"vanServicePointMappings\":[{\"vanServicePointMapID\":8801,\"vanSession\":2}]}"))); + } + + @Test + @DisplayName("saveVanServicePointMappings should report the failure when the visits cannot be stored") + void save_shouldReportStorageFailure() throws Exception { + when(vanServicePointMappingServiceImpl.saveVanServicePointMappings(anyList())) + .thenThrow(new RuntimeException("service point already visited in that session")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveVanServicePointMappings( + "{\"vanServicePointMappings\":[{\"vanID\":71,\"servicePointID\":88}]}"))); + } + + @Test + @DisplayName("getVanServicePointMappings should answer the visits of the van asked about") + void get_shouldAnswerVisitsOfVan() throws Exception { + when(vanServicePointMappingServiceImpl.getAvailableVanServicePointMappings(31, VAN_ID, PSM_ID)) + .thenReturn(new ArrayList<>(List.of(visit()))); + + assertSuccessContaining(controller.getVanServicePointMappings( + "{\"parkingPlaceID\":31,\"vanID\":71,\"providerServiceMapID\":4001}"), "Attibele PHC"); + } + + @Test + @DisplayName("getVanServicePointMappings should report the failure when the visits cannot be answered") + void get_shouldReportLookupFailure() throws Exception { + when(vanServicePointMappingServiceImpl.getAvailableVanServicePointMappings(any(), any(), any())) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getVanServicePointMappings("{\"vanID\":71}"))); + } + + @Test + @DisplayName("vanServicePointMappingsV1 should answer the visits with their district and taluk") + void getV1_shouldAnswerVisitsWithLocation() throws Exception { + when(vanServicePointMappingServiceImpl.getAvailableVanServicePointMappingsV1(31, VAN_ID, PSM_ID)) + .thenReturn(new ArrayList<>(List.of(new M_VanServicePointMap(MAP_ID, VAN_ID, (short) 1, 88, + "Attibele PHC", PSM_ID, Boolean.FALSE, 301, "Bengaluru Urban", 3011, "Anekal")))); + + assertSuccessContaining(controller.vanServicePointMappingsV1( + "{\"parkingPlaceID\":31,\"vanID\":71,\"providerServiceMapID\":4001}"), "Anekal"); + } + + @Test + @DisplayName("vanServicePointMappingsV1 should report the failure when the visits cannot be answered") + void getV1_shouldReportLookupFailure() throws Exception { + when(vanServicePointMappingServiceImpl.getAvailableVanServicePointMappingsV1(any(), any(), any())) + .thenThrow(new RuntimeException("connection reset")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.vanServicePointMappingsV1("{\"vanID\":71}"))); + } + + @Test + @DisplayName("deleteVanServicePointMappingDetails should confirm the retirement it recorded") + void delete_shouldConfirmRetirement() throws Exception { + when(vanServicePointMappingServiceImpl.updateVanServicePointMappingStatus(any(M_VanServicePointMap.class))) + .thenReturn(1); + + assertSuccessContaining(controller.deleteVanServicePointMappingDetails( + "{\"vanServicePointMapID\":8801,\"deleted\":true,\"modifiedBy\":\"admin\"}"), + "status updated successfully"); + } + + @Test + @DisplayName("deleteVanServicePointMappingDetails should say so when no visit was retired") + void delete_shouldSaySoWhenNothingRetired() throws Exception { + when(vanServicePointMappingServiceImpl.updateVanServicePointMappingStatus(any(M_VanServicePointMap.class))) + .thenReturn(0); + + assertSuccessContaining( + controller.deleteVanServicePointMappingDetails("{\"vanServicePointMapID\":-1,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteVanServicePointMappingDetails should report the failure when the retirement cannot be recorded") + void delete_shouldReportStorageFailure() throws Exception { + when(vanServicePointMappingServiceImpl.updateVanServicePointMappingStatus(any(M_VanServicePointMap.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf( + controller.deleteVanServicePointMappingDetails("{\"vanServicePointMapID\":8801}"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/vanSpokeMapping/VanSpokeMappingControllerTest.java b/src/test/java/com/iemr/admin/controller/vanSpokeMapping/VanSpokeMappingControllerTest.java new file mode 100644 index 0000000..590fa56 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/vanSpokeMapping/VanSpokeMappingControllerTest.java @@ -0,0 +1,159 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.vanSpokeMapping; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.service.vanSpokeMapping.VanSpokeMappingService; +import com.iemr.admin.utils.exception.IEMRException; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * The spoke mapping screen ties mobile unit vans to the telemedicine spokes + * they serve. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("VanSpokeMappingController Test Suite") +class VanSpokeMappingControllerTest { + + private static final String AUTHORIZATION = "session-key-123"; + private static final String REQUEST = "{\"vanSpokeMapping\":[{\"mmu_VanID\":71}]}"; + + @Mock + private VanSpokeMappingService vanSpokeMappingService; + + @InjectMocks + private VanSpokeMappingController controller; + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static String errorMessageOf(String response) { + return new JSONObject(response).getString("errorMessage"); + } + + @Test + @DisplayName("saving a mapping should confirm the tie it recorded") + void save_shouldConfirmRecordedTie() throws Exception { + when(vanSpokeMappingService.saveVanSpokeMapping(anyString())).thenReturn("success"); + + String response = controller.saveBenNCDCareNurseData(REQUEST, AUTHORIZATION); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Mapping done successfully"), response); + } + + @Test + @DisplayName("saving a mapping should report the failure when the tie was refused") + void save_shouldReportRefusedTie() throws Exception { + when(vanSpokeMappingService.saveVanSpokeMapping(anyString())).thenReturn("failure"); + + assertEquals("error in mapping van and spoke", + errorMessageOf(controller.saveBenNCDCareNurseData(REQUEST, AUTHORIZATION))); + } + + @Test + @DisplayName("saving a mapping should report the failure when the tie cannot be recorded") + void save_shouldReportStorageFailure() throws Exception { + when(vanSpokeMappingService.saveVanSpokeMapping(anyString())) + .thenThrow(new IEMRException("van is already mapped")); + + assertEquals(OutputResponse.USERID_FAILURE, + statusCodeOf(controller.saveBenNCDCareNurseData(REQUEST, AUTHORIZATION))); + } + + @Test + @DisplayName("getVanSpokeMapping should answer the ties held at the parking place asked about") + void get_shouldAnswerHeldTies() throws Exception { + when(vanSpokeMappingService.getVanSpokeMappingDetails(anyString())) + .thenReturn("{\"vanSpokeMappedDetails\":[{\"vanspokeID\":6001}]}"); + + String response = controller.getVanSpokeMapping(REQUEST, AUTHORIZATION); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("vanSpokeMappedDetails"), response); + } + + @Test + @DisplayName("getVanSpokeMapping should report the failure when nothing came back from the service") + void get_shouldReportMissingAnswer() throws Exception { + when(vanSpokeMappingService.getVanSpokeMappingDetails(anyString())).thenReturn(null); + + assertEquals("error in fetching the van and spoke data", + errorMessageOf(controller.getVanSpokeMapping(REQUEST, AUTHORIZATION))); + } + + @Test + @DisplayName("getVanSpokeMapping should report the failure when the lookup cannot be answered") + void get_shouldReportLookupFailure() throws Exception { + when(vanSpokeMappingService.getVanSpokeMappingDetails(anyString())) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.getVanSpokeMapping(REQUEST, AUTHORIZATION))); + } + + @Test + @DisplayName("deleteVanSpokeMapping should confirm the change it recorded") + void delete_shouldConfirmRecordedChange() throws Exception { + when(vanSpokeMappingService.deleteVanSpokeMapping(anyString())).thenReturn("success"); + + String response = controller.deleteVanSpokeMapping(REQUEST, AUTHORIZATION); + + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains("Mapping status got updated"), response); + } + + @Test + @DisplayName("deleteVanSpokeMapping should report the failure when the change was refused") + void delete_shouldReportRefusedChange() throws Exception { + when(vanSpokeMappingService.deleteVanSpokeMapping(anyString())).thenReturn("failure"); + + assertEquals("Error in deleting mapping", + errorMessageOf(controller.deleteVanSpokeMapping(REQUEST, AUTHORIZATION))); + } + + @Test + @DisplayName("deleteVanSpokeMapping should report the failure when the change cannot be recorded") + void delete_shouldReportStorageFailure() throws Exception { + when(vanSpokeMappingService.deleteVanSpokeMapping(anyString())) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.deleteVanSpokeMapping(REQUEST, AUTHORIZATION))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/version/VersionControllerTest.java b/src/test/java/com/iemr/admin/controller/version/VersionControllerTest.java new file mode 100644 index 0000000..1363758 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/version/VersionControllerTest.java @@ -0,0 +1,60 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.version; + +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The version endpoint tells an operator which build is deployed, and must + * answer something rather than fail when the build stamp is missing. + */ +@DisplayName("VersionController Test Suite") +class VersionControllerTest { + + private final VersionController controller = new VersionController(); + + @Test + @DisplayName("versionInformation should answer the build stamp fields the deployment records") + void versionInformation_shouldAnswerBuildStampFields() { + ResponseEntity> response = controller.versionInformation(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody().containsKey("buildTimestamp")); + assertTrue(response.getBody().containsKey("version")); + assertTrue(response.getBody().containsKey("branch")); + assertTrue(response.getBody().containsKey("commitHash")); + } + + @Test + @DisplayName("versionInformation should answer the same stamp on every call") + void versionInformation_shouldAnswerConsistently() { + assertEquals(controller.versionInformation().getBody(), controller.versionInformation().getBody()); + } +} diff --git a/src/test/java/com/iemr/admin/controller/villageMaster/VillageMasterControllerTest.java b/src/test/java/com/iemr/admin/controller/villageMaster/VillageMasterControllerTest.java new file mode 100644 index 0000000..f56006e --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/villageMaster/VillageMasterControllerTest.java @@ -0,0 +1,177 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.villageMaster; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.DistrictBranchMapping; +import com.iemr.admin.service.villageMaster.VillageMasterServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The village screen keeps the villages of a taluk and the details each one is + * addressed by. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("VillageMasterController Test Suite") +class VillageMasterControllerTest { + + private static final Integer BLOCK_ID = 3011; + + @Mock + private VillageMasterServiceImpl villageMasterServiceImpl; + + @InjectMocks + private VillageMasterController controller; + + private static DistrictBranchMapping village() { + DistrictBranchMapping village = new DistrictBranchMapping(); + village.setDistrictBranchID(30111); + village.setVillageName("Attibele"); + village.setPinCode("562107"); + return village; + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String expected) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(expected), response); + } + + @Test + @DisplayName("saveVillageDetails should answer the villages it added") + void save_shouldAnswerAddedVillages() { + when(villageMasterServiceImpl.storeVillageDetails(anyList())) + .thenReturn(new ArrayList<>(List.of(village()))); + + assertSuccessContaining(controller.saveVillageDetails( + "{\"districtBranchMapping\":[{\"villageName\":\"Attibele\",\"blockID\":3011}]}"), "Attibele"); + } + + @Test + @DisplayName("saveVillageDetails should report the failure when the village cannot be added") + void save_shouldReportStorageFailure() { + when(villageMasterServiceImpl.storeVillageDetails(anyList())) + .thenThrow(new RuntimeException("village already on file")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.saveVillageDetails( + "{\"districtBranchMapping\":[{\"villageName\":\"Attibele\"}]}"))); + } + + @Test + @DisplayName("getVillages should answer the villages of the taluk asked about") + void get_shouldAnswerVillagesOfTaluk() { + when(villageMasterServiceImpl.getAvailableVillages(BLOCK_ID)) + .thenReturn(new ArrayList<>(List.of(village()))); + + assertSuccessContaining(controller.getVillages("{\"blockID\":3011}"), "Attibele"); + } + + @Test + @DisplayName("getVillages should report the failure when the villages cannot be answered") + void get_shouldReportLookupFailure() { + when(villageMasterServiceImpl.getAvailableVillages(any())) + .thenThrow(new RuntimeException("query timed out")); + + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(controller.getVillages("{\"blockID\":3011}"))); + } + + @Test + @DisplayName("deleteVillage should confirm the retirement it recorded") + void delete_shouldConfirmRetirement() { + when(villageMasterServiceImpl.updateVillageStatus(any(DistrictBranchMapping.class))).thenReturn(1); + + assertSuccessContaining( + controller.deleteVillage("{\"districtBranchID\":30111,\"deleted\":true,\"modifiedBy\":\"admin\"}"), + "status updated successfully"); + } + + @Test + @DisplayName("deleteVillage should say so when no village was retired") + void delete_shouldSaySoWhenNothingRetired() { + when(villageMasterServiceImpl.updateVillageStatus(any(DistrictBranchMapping.class))).thenReturn(0); + + assertSuccessContaining(controller.deleteVillage("{\"districtBranchID\":-1,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteVillage should report the failure when the retirement cannot be recorded") + void delete_shouldReportStorageFailure() { + when(villageMasterServiceImpl.updateVillageStatus(any(DistrictBranchMapping.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.deleteVillage("{\"districtBranchID\":30111}"))); + } + + @Test + @DisplayName("updateVillageData should confirm the change it recorded") + void update_shouldConfirmRecordedChange() { + when(villageMasterServiceImpl.updateVillageData(any(DistrictBranchMapping.class))).thenReturn(1); + + assertSuccessContaining(controller.updateVillageData( + "{\"districtBranchID\":30111,\"villageName\":\"Attibele\",\"pinCode\":\"562107\"," + + "\"modifiedBy\":\"admin\"}"), + "status updated successfully"); + } + + @Test + @DisplayName("updateVillageData should say so when no village was changed") + void update_shouldSaySoWhenNothingChanged() { + when(villageMasterServiceImpl.updateVillageData(any(DistrictBranchMapping.class))).thenReturn(0); + + assertSuccessContaining(controller.updateVillageData("{\"districtBranchID\":-1}"), + "Failed to update the status"); + } + + @Test + @DisplayName("updateVillageData should report the failure when the change cannot be recorded") + void update_shouldReportStorageFailure() { + when(villageMasterServiceImpl.updateVillageData(any(DistrictBranchMapping.class))) + .thenThrow(new RuntimeException("row is locked")); + + assertEquals(OutputResponse.GENERIC_FAILURE, + statusCodeOf(controller.updateVillageData("{\"districtBranchID\":30111}"))); + } +} diff --git a/src/test/java/com/iemr/admin/controller/zonemaster/ZoneMasterControllerTest.java b/src/test/java/com/iemr/admin/controller/zonemaster/ZoneMasterControllerTest.java new file mode 100644 index 0000000..ed87be6 --- /dev/null +++ b/src/test/java/com/iemr/admin/controller/zonemaster/ZoneMasterControllerTest.java @@ -0,0 +1,287 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.controller.zonemaster; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.zonemaster.M_Zone; +import com.iemr.admin.data.zonemaster.M_ZoneDistrictMap; +import com.iemr.admin.service.zonemaster.ZoneMasterServiceImpl; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The zone endpoints group the districts a provider operates in, and a zone that + * is retired has to take its district mappings with it. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ZoneMasterController Test Suite") +class ZoneMasterControllerTest { + + private static final Integer PSM_ID = 4001; + private static final Integer ZONE_ID = 61; + + @Mock + private ZoneMasterServiceImpl zoneMasterServiceImpl; + + @InjectMocks + private ZoneMasterController controller; + + private static M_Zone zone(Integer id, String name) { + return new M_Zone(id, name, "Northern districts", "Main Road", PSM_ID, Boolean.FALSE, 1, "India", + 29, "Karnataka", 301, "Bengaluru Urban", 401, "North block", 501, "Hosur", null, 3, + "Tele Medicine"); + } + + private static M_ZoneDistrictMap districtMapping(Integer id) { + return new M_ZoneDistrictMap(id, ZONE_ID, "North zone", 301, PSM_ID, Boolean.FALSE, 29, "Karnataka", + "Bengaluru Urban", 3, "Tele Medicine", Boolean.FALSE); + } + + private static int statusCodeOf(String response) { + return new JSONObject(response).getInt("statusCode"); + } + + private static void assertSuccessContaining(String response, String fragment) { + assertEquals(OutputResponse.SUCCESS, statusCodeOf(response), response); + assertTrue(response.contains(fragment), response); + } + + private static void assertGenericFailure(String response) { + assertEquals(OutputResponse.GENERIC_FAILURE, statusCodeOf(response), response); + } + + private static void assertCodeException(String response) { + assertEquals(OutputResponse.CODE_EXCEPTION, statusCodeOf(response), response); + } + + @Test + @DisplayName("saveZone should answer the zones the service stored") + void saveZone_shouldAnswerStoredZones() throws Exception { + when(zoneMasterServiceImpl.createZone(anyList())) + .thenReturn(new ArrayList<>(List.of(zone(ZONE_ID, "North zone")))); + + assertSuccessContaining(controller.saveZone("{\"zones\":[{\"zoneName\":\"North zone\"}]}"), "North zone"); + } + + @Test + @DisplayName("saveZone should answer an error envelope when the store fails") + void saveZone_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(zoneMasterServiceImpl.createZone(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.saveZone("{\"zones\":[{\"zoneName\":\"North zone\"}]}")); + } + + @Test + @DisplayName("getZones should answer the zones of the provider") + void getZones_shouldAnswerProviderZones() { + when(zoneMasterServiceImpl.getAvailableZones(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(zone(ZONE_ID, "North zone")))); + + assertSuccessContaining(controller.getZones("{\"providerServiceMapID\":4001}"), "North zone"); + } + + @Test + @DisplayName("getZones should ask the caller for a provider when the request names none") + void getZones_shouldAskForProvider() { + assertSuccessContaining(controller.getZones("{}"), "Provide providerServiceMapID."); + verify(zoneMasterServiceImpl, never()).getAvailableZones(anyInt()); + } + + @Test + @DisplayName("getZones should stay at its default for a provider id of zero") + void getZones_shouldStayAtDefaultForZeroProvider() { + assertGenericFailure(controller.getZones("{\"providerServiceMapID\":0}")); + } + + @Test + @DisplayName("getZones should answer an error envelope for a body it cannot read") + void getZones_shouldAnswerErrorEnvelopeForMalformedBody() { + assertEquals(OutputResponse.OBJECT_FAILURE, statusCodeOf(controller.getZones("not json"))); + } + + @Test + @DisplayName("mapZoneWithDistrict should answer the mappings the service stored") + void mapZoneWithDistrict_shouldAnswerStoredMappings() throws Exception { + when(zoneMasterServiceImpl.createZoneDistrictMapping(anyList())) + .thenReturn(new ArrayList<>(List.of(districtMapping(9001)))); + + assertSuccessContaining(controller.mapZoneWithDistrict( + "{\"zoneDistrictMappings\":[{\"zoneID\":61,\"districtID\":301}]}"), "9001"); + } + + @Test + @DisplayName("mapZoneWithDistrict should answer an error envelope when the store fails") + void mapZoneWithDistrict_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(zoneMasterServiceImpl.createZoneDistrictMapping(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.mapZoneWithDistrict("{\"zoneDistrictMappings\":[{}]}")); + } + + @Test + @DisplayName("editZoneDistrict should copy the edits onto the stored mapping") + void editZoneDistrict_shouldCopyEdits() throws Exception { + M_ZoneDistrictMap stored = districtMapping(9001); + when(zoneMasterServiceImpl.editZoneDistrictMapping(9001)).thenReturn(stored); + when(zoneMasterServiceImpl.saveeditedData(stored)).thenReturn(stored); + + String response = controller.editZoneDistrict("{\"zoneDistrictMapID\":9001,\"zoneID\":62," + + "\"districtID\":302,\"providerServiceMapID\":4001,\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "9001"); + assertEquals(62, stored.getZoneID()); + assertEquals(302, stored.getDistrictID()); + } + + @Test + @DisplayName("editZoneDistrict should answer an error envelope for a mapping that does not exist") + void editZoneDistrict_shouldAnswerErrorEnvelopeForUnknownMapping() throws Exception { + when(zoneMasterServiceImpl.editZoneDistrictMapping(9001)).thenReturn(null); + + assertCodeException(controller.editZoneDistrict("{\"zoneDistrictMapID\":9001}")); + } + + @Test + @DisplayName("getZoneDistrictMappings should answer the district mappings of the provider") + void getZoneDistrictMappings_shouldAnswerProviderMappings() { + when(zoneMasterServiceImpl.getAvailableZoneDistrictMappings(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(districtMapping(9001)))); + + assertSuccessContaining( + controller.getZoneDistrictMappings("{\"providerServiceMapID\":4001}"), "9001"); + } + + @Test + @DisplayName("getZoneDistrictMappings should ask the caller for a provider when the request names none") + void getZoneDistrictMappings_shouldAskForProvider() { + assertSuccessContaining(controller.getZoneDistrictMappings("{}"), "Provide serviceProviderID."); + } + + @Test + @DisplayName("deleteZone should report whether the zone was actually retired") + void deleteZone_shouldReportOutcome() throws Exception { + when(zoneMasterServiceImpl.updateZoneStatus(any())).thenReturn(1); + assertSuccessContaining(controller.deleteZone("{\"zoneID\":61,\"deleted\":true}"), + "status updated successfully"); + + when(zoneMasterServiceImpl.updateZoneStatus(any())).thenReturn(0); + assertSuccessContaining(controller.deleteZone("{\"zoneID\":61,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteZone should answer an error envelope when the change fails") + void deleteZone_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(zoneMasterServiceImpl.updateZoneStatus(any())).thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.deleteZone("{\"zoneID\":61,\"deleted\":true}")); + } + + @Test + @DisplayName("updateZoneData should copy the edits onto the stored zone") + void updateZoneData_shouldCopyEdits() throws Exception { + M_Zone stored = zone(ZONE_ID, "old name"); + when(zoneMasterServiceImpl.getzoneByID(ZONE_ID)).thenReturn(stored); + when(zoneMasterServiceImpl.updateZoneData(stored)).thenReturn(stored); + + String response = controller.updateZoneData("{\"zoneID\":61,\"zoneName\":\"North zone\"," + + "\"zoneDesc\":\"Northern districts\",\"zoneHQAddress\":\"Main Road\",\"stateID\":29," + + "\"districtID\":301,\"districtBlockID\":401,\"districtBranchID\":501," + + "\"modifiedBy\":\"admin\"}"); + + assertSuccessContaining(response, "North zone"); + assertEquals("Northern districts", stored.getZoneDesc()); + assertEquals(29, stored.getStateID()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("updateZoneData should answer an error envelope for a zone that does not exist") + void updateZoneData_shouldAnswerErrorEnvelopeForUnknownZone() throws Exception { + when(zoneMasterServiceImpl.getzoneByID(ZONE_ID)).thenReturn(null); + + assertCodeException(controller.updateZoneData("{\"zoneID\":61}")); + } + + @Test + @DisplayName("getMappedDistrictByZoneID should answer the districts mapped to the zone") + void getMappedDistrictByZoneID_shouldAnswerMappedDistricts() { + when(zoneMasterServiceImpl.editZoneDistrictMapping1(ZONE_ID)) + .thenReturn(new ArrayList<>(List.of(districtMapping(9001)))); + + assertSuccessContaining(controller.getMappedDistrictByZoneID("{\"zoneID\":61}"), "9001"); + } + + @Test + @DisplayName("getMappedDistrictByZoneID should answer an error envelope when the lookup fails") + void getMappedDistrictByZoneID_shouldAnswerErrorEnvelopeOnFailure() { + when(zoneMasterServiceImpl.editZoneDistrictMapping1(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure(controller.getMappedDistrictByZoneID("{\"zoneID\":61}")); + } + + @Test + @DisplayName("deleteZoneDistrictMapping should report whether the mapping changed") + void deleteZoneDistrictMapping_shouldReportOutcome() throws Exception { + when(zoneMasterServiceImpl.updateZoneDistrictMappingStatus(any())).thenReturn(0); + assertSuccessContaining( + controller.deleteZoneDistrictMapping("{\"zoneDistrictMapID\":9001,\"deleted\":true}"), + "status updated successfully"); + + when(zoneMasterServiceImpl.updateZoneDistrictMappingStatus(any())).thenReturn(1); + assertSuccessContaining( + controller.deleteZoneDistrictMapping("{\"zoneDistrictMapID\":9001,\"deleted\":true}"), + "Failed to update the status"); + } + + @Test + @DisplayName("deleteZoneDistrictMapping should answer an error envelope when the change fails") + void deleteZoneDistrictMapping_shouldAnswerErrorEnvelopeOnFailure() throws Exception { + when(zoneMasterServiceImpl.updateZoneDistrictMappingStatus(any())) + .thenThrow(new IllegalStateException("no connection")); + + assertGenericFailure( + controller.deleteZoneDistrictMapping("{\"zoneDistrictMapID\":9001,\"deleted\":true}")); + } +} diff --git a/src/test/java/com/iemr/admin/data/BeanContract.java b/src/test/java/com/iemr/admin/data/BeanContract.java new file mode 100644 index 0000000..379b493 --- /dev/null +++ b/src/test/java/com/iemr/admin/data/BeanContract.java @@ -0,0 +1,245 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.data; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Reflective checks for the accessors, equality and string representation of the + * plain data carriers this service exchanges over its APIs. + * + * The carriers are mostly Lombok {@code @Data} entities, so the generated + * {@code equals}, {@code hashCode} and {@code toString} are exercised alongside + * every readable/writable property. A carrier that cannot be built through a + * no-argument constructor is reported by {@link #isVerifiable(Class)} so the + * calling suite can skip it rather than fail. + */ +public final class BeanContract { + + private BeanContract() { + } + + /** Answers whether the type can be exercised through the reflective contract. */ + public static boolean isVerifiable(Class> type) { + if (type.isInterface() || type.isEnum() || type.isAnnotation() + || Modifier.isAbstract(type.getModifiers()) + || type.isMemberClass() && !Modifier.isStatic(type.getModifiers())) { + return false; + } + try { + type.getDeclaredConstructor(); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + public static void verify(Class> type) throws Exception { + Object left = newInstance(type); + Object right = newInstance(type); + copyFields(type, left, right); + + assertNotNull(left.toString(), type.getSimpleName() + " must render a string form"); + assertTrue(left.equals(left), type.getSimpleName() + " must equal itself"); + assertFalse(left.equals(null), type.getSimpleName() + " must never equal null"); + assertFalse(left.equals(new Object()), type.getSimpleName() + " must never equal an unrelated type"); + left.hashCode(); + + if (left.equals(right)) { + assertEquals(left.hashCode(), right.hashCode(), + type.getSimpleName() + " must hash consistently with equals"); + } + + verifyProperties(type, left, right); + } + + private static Object newInstance(Class> type) throws Exception { + java.lang.reflect.Constructor> constructor = type.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } + + private static void verifyProperties(Class> type, Object left, Object right) throws Exception { + for (Field field : type.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) { + continue; + } + Method getter = findGetter(type, field); + Method setter = findSetter(type, field); + if (getter == null || setter == null) { + continue; + } + Object value = sampleValue(field.getType()); + if (value == null) { + continue; + } + + setter.invoke(left, value); + assertEquals(value, getter.invoke(left), + type.getSimpleName() + "." + field.getName() + " must round-trip through its accessors"); + + setter.invoke(right, value); + assertEquals(getter.invoke(left), getter.invoke(right), + type.getSimpleName() + "." + field.getName() + " must read back the same on both instances"); + } + assertNotNull(left.toString(), type.getSimpleName() + " must render a populated string form"); + left.hashCode(); + left.equals(right); + } + + private static void copyFields(Class> type, Object from, Object to) throws Exception { + for (Field field : type.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) { + continue; + } + field.setAccessible(true); + field.set(to, field.get(from)); + } + } + + private static Method findGetter(Class> type, Field field) { + String suffix = capitalise(field.getName()); + for (String prefix : new String[] { "get", "is" }) { + try { + Method candidate = type.getMethod(prefix + suffix); + if (candidate.getParameterCount() == 0) { + return candidate; + } + } catch (NoSuchMethodException ignored) { + // try the next accessor style + } + } + return null; + } + + private static Method findSetter(Class> type, Field field) { + try { + return type.getMethod("set" + capitalise(field.getName()), field.getType()); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static String capitalise(String name) { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + private static Object sampleValue(Class> type) { + if (type == String.class) { + return "sample"; + } + if (type == Long.class || type == long.class) { + return 7L; + } + if (type == Integer.class || type == int.class) { + return 7; + } + if (type == Short.class || type == short.class) { + return (short) 7; + } + if (type == Double.class || type == double.class) { + return 7.5d; + } + if (type == Float.class || type == float.class) { + return 7.5f; + } + if (type == Character.class || type == char.class) { + return 'y'; + } + if (type == Byte.class || type == byte.class) { + return (byte) 7; + } + if (type == Boolean.class || type == boolean.class) { + return Boolean.TRUE; + } + if (type == java.math.BigDecimal.class) { + return java.math.BigDecimal.valueOf(7.5d); + } + if (type == BigInteger.class) { + return BigInteger.valueOf(7L); + } + if (type == Timestamp.class) { + return Timestamp.valueOf("2026-02-17 09:30:00"); + } + if (type == Date.class) { + return Date.valueOf("2026-02-17"); + } + if (type == Time.class) { + return Time.valueOf("09:30:00"); + } + if (type == java.util.Date.class) { + return new java.util.Date(1_771_286_400_000L); + } + if (type == LocalDate.class) { + return LocalDate.of(2026, 2, 17); + } + if (type == LocalTime.class) { + return LocalTime.of(9, 30); + } + if (type == LocalDateTime.class) { + return LocalDateTime.of(2026, 2, 17, 9, 30); + } + if (type == List.class) { + return new ArrayList<>(List.of("first", "second")); + } + if (type == Set.class) { + return new HashSet<>(Set.of("first")); + } + if (type == Map.class) { + return new HashMap<>(Map.of("key", "value")); + } + if (type == Object.class) { + return "sample"; + } + if (type.isPrimitive() || type.isEnum() || type.isArray() || type.isInterface()) { + return null; + } + try { + java.lang.reflect.Constructor> constructor = type.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (ReflectiveOperationException | SecurityException e) { + // A carrier that needs arguments is simply left at its default value. + return null; + } + } +} diff --git a/src/test/java/com/iemr/admin/data/ClassScanner.java b/src/test/java/com/iemr/admin/data/ClassScanner.java new file mode 100644 index 0000000..7eb63b0 --- /dev/null +++ b/src/test/java/com/iemr/admin/data/ClassScanner.java @@ -0,0 +1,64 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.data; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.RegexPatternTypeFilter; + +/** Finds every concrete class published under a package, for suite-wide contract checks. */ +public final class ClassScanner { + + private ClassScanner() { + } + + public static List> classesUnder(String... packages) { + ClassPathScanningCandidateComponentProvider provider = + new ClassPathScanningCandidateComponentProvider(false) { + @Override + protected boolean isCandidateComponent( + org.springframework.beans.factory.annotation.AnnotatedBeanDefinition definition) { + return definition.getMetadata().isIndependent() + && !definition.getMetadata().isAnnotation(); + } + }; + provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*"))); + + List> found = new ArrayList<>(); + for (String packageName : packages) { + for (BeanDefinition definition : provider.findCandidateComponents(packageName)) { + try { + found.add(Class.forName(definition.getBeanClassName())); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // A class the test classpath cannot resolve is not part of the contract. + } + } + } + found.sort(Comparator.comparing(Class::getName)); + return found; + } +} diff --git a/src/test/java/com/iemr/admin/data/DataCarrierContractTest.java b/src/test/java/com/iemr/admin/data/DataCarrierContractTest.java new file mode 100644 index 0000000..1fe9016 --- /dev/null +++ b/src/test/java/com/iemr/admin/data/DataCarrierContractTest.java @@ -0,0 +1,71 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.data; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Exercises every data carrier the service exchanges - the JPA entities, the + * transfer objects and the small model holders - against the accessor, equality + * and string contract their callers rely on. + */ +@DisplayName("Data carrier contract Test Suite") +class DataCarrierContractTest { + + private static final String[] CARRIER_PACKAGES = { + "com.iemr.admin.data", + "com.iemr.admin.to", + "com.iemr.admin.model" }; + + static List> carriers() { + return ClassScanner.classesUnder(CARRIER_PACKAGES).stream() + .filter(type -> !type.getName().endsWith("Test")) + .filter(type -> !type.getSimpleName().equals("BeanContract")) + .filter(type -> !type.getSimpleName().equals("ClassScanner")) + .filter(BeanContract::isVerifiable) + .toList(); + } + + @Test + @DisplayName("the scan should discover the carriers rather than silently pass on an empty set") + void carrierScan_shouldDiscoverCarriers() { + List> carriers = carriers(); + assertFalse(carriers.isEmpty(), "no data carriers were discovered on the test classpath"); + assertTrue(carriers.size() > 100, + "expected the full carrier set, found only " + carriers.size()); + } + + @ParameterizedTest(name = "{0} honours the accessor, equality and string contract") + @DisplayName("every data carrier should honour its accessor, equality and string contract") + @MethodSource("carriers") + void dataCarrier_shouldHonourBeanContract(Class> type) throws Exception { + BeanContract.verify(type); + } +} diff --git a/src/test/java/com/iemr/admin/mapper/emailconfig/InstituteEmailConfigMapperTest.java b/src/test/java/com/iemr/admin/mapper/emailconfig/InstituteEmailConfigMapperTest.java new file mode 100644 index 0000000..a80f1de --- /dev/null +++ b/src/test/java/com/iemr/admin/mapper/emailconfig/InstituteEmailConfigMapperTest.java @@ -0,0 +1,260 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.mapper.emailconfig; + +import java.sql.Timestamp; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.emailconfig.AuthorityEmail; +import com.iemr.admin.model.emailconfig.AuthEmailRequest; +import com.iemr.admin.model.emailconfig.AuthEmailResponse; +import com.iemr.admin.model.emailconfig.CreateAuthEmailRequestModel; +import com.iemr.admin.model.emailconfig.CreateNodalEmailRequestModel; +import com.iemr.admin.model.emailconfig.NodalEmailResponse; +import com.iemr.admin.model.emailconfig.UpdateAuthEmailRequest; +import com.iemr.admin.model.emailconfig.UpdateNodalEmailRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The escalation emails a nodal officer receives are addressed from these + * records, so the mapper must carry every contact field across rather than drop + * one silently. + */ +@DisplayName("InstituteEmailConfigMapper Test Suite") +class InstituteEmailConfigMapperTest { + + private final InstituteEmailConfigMapper mapper = InstituteEmailConfigMapper.INSTANCE; + + private static AuthorityEmail storedRecord() { + AuthorityEmail stored = new AuthorityEmail(); + stored.setAuthorityEmailID(9001); + stored.setStateID(29); + stored.setDistrictID(301); + stored.setBlockID(401); + stored.setDistrictBranchMappingID(501); + stored.setDesignationID(7); + stored.setAuthorityName("Dr Asha Rao"); + stored.setEmailID("asha.rao@example.org"); + stored.setContactNo("9000000001"); + stored.setProviderServiceMapID(4001); + stored.setCreatedBy("admin"); + stored.setCreatedDate(Timestamp.valueOf("2026-02-17 09:30:00")); + stored.setModifiedBy("admin"); + stored.setDeleted(Boolean.FALSE); + return stored; + } + + private static CreateAuthEmailRequestModel createAuthorityRequest() { + CreateAuthEmailRequestModel request = new CreateAuthEmailRequestModel(); + request.setStateID(29); + request.setDistrictID(301); + request.setBlockID(401); + request.setDistrictBranchMappingID(501); + request.setDesignationID(7); + request.setAuthorityName("Dr Asha Rao"); + request.setEmailID("asha.rao@example.org"); + request.setContactNo("9000000001"); + request.setProviderServiceMapID(4001); + request.setCreatedBy("admin"); + return request; + } + + private static CreateNodalEmailRequestModel createNodalRequest() { + CreateNodalEmailRequestModel request = new CreateNodalEmailRequestModel(); + request.setStateID(29); + request.setDistrictID(301); + request.setDesignationID(7); + request.setAuthorityName("Dr Asha Rao"); + request.setEmailID("asha.rao@example.org"); + request.setContactNo("9000000001"); + request.setMobileNo("9000000002"); + request.setProviderServiceMapID(4001); + request.setCreatedBy("admin"); + return request; + } + + @Test + @DisplayName("requestToInstituteEmailConf should carry the search request onto the record") + void requestToInstituteEmailConf_shouldCarrySearchRequest() { + AuthEmailRequest request = new AuthEmailRequest(); + request.setAuthorityEmailID(9001); + request.setStateID(29); + request.setDistrictID(301); + request.setBlockID(401); + request.setDistrictBranchMappingID(501); + request.setDesignationID(7); + request.setProviderServiceMapID(4001); + request.setDeleted(Boolean.FALSE); + + AuthorityEmail mapped = mapper.requestToInstituteEmailConf(request); + + assertEquals(9001, mapped.getAuthorityEmailID()); + assertEquals(29, mapped.getStateID()); + assertEquals(501, mapped.getDistrictBranchMappingID()); + assertEquals(4001, mapped.getProviderServiceMapID()); + } + + @Test + @DisplayName("requestToInstituteEmailConf should map a whole batch of search requests") + void requestToInstituteEmailConf_shouldMapBatch() { + AuthEmailRequest request = new AuthEmailRequest(); + request.setStateID(29); + + List mapped = mapper.requestToInstituteEmailConf(List.of(request)); + + assertEquals(1, mapped.size()); + assertEquals(29, mapped.get(0).getStateID()); + } + + @Test + @DisplayName("createRequestToInstituteEmailConf should carry every contact field onto the new record") + void createRequestToInstituteEmailConf_shouldCarryContactFields() { + AuthorityEmail mapped = mapper.createRequestToInstituteEmailConf(createAuthorityRequest()); + + assertEquals("Dr Asha Rao", mapped.getAuthorityName()); + assertEquals("asha.rao@example.org", mapped.getEmailID()); + assertEquals("9000000001", mapped.getContactNo()); + assertEquals("admin", mapped.getCreatedBy()); + } + + @Test + @DisplayName("createRequestToInstituteEmailConfig should carry the nodal officer's mobile number too") + void createRequestToInstituteEmailConfig_shouldCarryMobileNumber() { + AuthorityEmail mapped = mapper.createRequestToInstituteEmailConfig(createNodalRequest()); + + assertEquals("Dr Asha Rao", mapped.getAuthorityName()); + assertEquals("asha.rao@example.org", mapped.getEmailID()); + } + + @Test + @DisplayName("the create mappers should each map a whole batch") + void createMappers_shouldMapBatches() { + assertEquals(1, mapper.createRequestToInstituteEmailConf(List.of(createAuthorityRequest())).size()); + assertEquals(1, mapper.createRequestToInstituteEmailConfig(List.of(createNodalRequest())).size()); + } + + @Test + @DisplayName("updateRequestToInstituteEmailConf should carry the edited fields onto the record") + void updateRequestToInstituteEmailConf_shouldCarryEditedFields() { + UpdateAuthEmailRequest request = new UpdateAuthEmailRequest(); + request.setAuthorityEmailID(9001); + request.setAuthorityName("Dr Ravi Kumar"); + request.setEmailID("ravi.kumar@example.org"); + request.setContactNo("9000000003"); + request.setModifiedBy("admin"); + request.setDeleted(Boolean.FALSE); + + AuthorityEmail mapped = mapper.updateRequestToInstituteEmailConf(request); + + assertEquals(9001, mapped.getAuthorityEmailID()); + assertEquals("Dr Ravi Kumar", mapped.getAuthorityName()); + assertEquals("admin", mapped.getModifiedBy()); + } + + @Test + @DisplayName("updateRequestToInstituteNodalEmailConf should carry the edited nodal fields onto the record") + void updateRequestToInstituteNodalEmailConf_shouldCarryEditedFields() { + UpdateNodalEmailRequest request = new UpdateNodalEmailRequest(); + request.setAuthorityEmailID(9001); + request.setAuthorityName("Dr Ravi Kumar"); + request.setMobileNo("9000000004"); + request.setModifiedBy("admin"); + + AuthorityEmail mapped = mapper.updateRequestToInstituteNodalEmailConf(request); + + assertEquals(9001, mapped.getAuthorityEmailID()); + assertEquals("Dr Ravi Kumar", mapped.getAuthorityName()); + } + + @Test + @DisplayName("updateRequestToInstituteEmailConf should map a whole batch of edits") + void updateRequestToInstituteEmailConf_shouldMapBatch() { + UpdateAuthEmailRequest request = new UpdateAuthEmailRequest(); + request.setAuthorityEmailID(9001); + + assertEquals(1, mapper.updateRequestToInstituteEmailConf(List.of(request)).size()); + } + + @Test + @DisplayName("resultToInstTypeEmailResponse should publish the stored record back to the caller") + void resultToInstTypeEmailResponse_shouldPublishStoredRecord() { + AuthEmailResponse published = mapper.resultToInstTypeEmailResponse(storedRecord()); + + assertEquals(9001, published.getAuthorityEmailID()); + assertEquals("Dr Asha Rao", published.getAuthorityName()); + assertEquals("asha.rao@example.org", published.getEmailID()); + assertEquals(Timestamp.valueOf("2026-02-17 09:30:00"), published.getCreatedDate()); + } + + @Test + @DisplayName("the nodal response mappers should publish the stored record back to the caller") + void nodalResponseMappers_shouldPublishStoredRecord() { + NodalEmailResponse published = mapper.resultToInstTypeNodalEmailResponse(storedRecord()); + NodalEmailResponse alsoPublished = mapper.resultInstType(storedRecord()); + + assertEquals("Dr Asha Rao", published.getAuthorityName()); + assertEquals("Dr Asha Rao", alsoPublished.getAuthorityName()); + assertEquals(4001, published.getProviderServiceMapID()); + } + + @Test + @DisplayName("the response mappers should publish a whole batch of stored records") + void responseMappers_shouldPublishBatches() { + List stored = List.of(storedRecord(), storedRecord()); + + assertEquals(2, mapper.resultToInstTypeEmailResponse(stored).size()); + assertEquals(2, mapper.resultToInstTypeEmailResponses(stored).size()); + } + + @Test + @DisplayName("the mappers should answer nothing rather than an empty record for a missing input") + void mappers_shouldAnswerNothingForMissingInput() { + assertNull(mapper.requestToInstituteEmailConf((AuthEmailRequest) null)); + assertNull(mapper.requestToInstituteEmailConf((List) null)); + assertNull(mapper.createRequestToInstituteEmailConf((CreateAuthEmailRequestModel) null)); + assertNull(mapper.createRequestToInstituteEmailConf((List) null)); + assertNull(mapper.createRequestToInstituteEmailConfig((CreateNodalEmailRequestModel) null)); + assertNull(mapper.createRequestToInstituteEmailConfig((List) null)); + assertNull(mapper.updateRequestToInstituteEmailConf((UpdateAuthEmailRequest) null)); + assertNull(mapper.updateRequestToInstituteEmailConf((List) null)); + assertNull(mapper.updateRequestToInstituteNodalEmailConf(null)); + assertNull(mapper.resultToInstTypeEmailResponse((AuthorityEmail) null)); + assertNull(mapper.resultToInstTypeEmailResponse((List) null)); + assertNull(mapper.resultToInstTypeNodalEmailResponse(null)); + assertNull(mapper.resultInstType(null)); + assertNull(mapper.resultToInstTypeEmailResponses(null)); + } + + @Test + @DisplayName("the batch mappers should answer an empty batch for an empty input") + void batchMappers_shouldAnswerEmptyBatchForEmptyInput() { + assertTrue(mapper.requestToInstituteEmailConf(List.of()).isEmpty()); + assertTrue(mapper.resultToInstTypeEmailResponse(List.of()).isEmpty()); + assertTrue(mapper.resultToInstTypeEmailResponses(List.of()).isEmpty()); + } +} diff --git a/src/test/java/com/iemr/admin/mapper/parkingplacetalukmapping/ParkingPlaceTalukMappingMapperTest.java b/src/test/java/com/iemr/admin/mapper/parkingplacetalukmapping/ParkingPlaceTalukMappingMapperTest.java new file mode 100644 index 0000000..6b63657 --- /dev/null +++ b/src/test/java/com/iemr/admin/mapper/parkingplacetalukmapping/ParkingPlaceTalukMappingMapperTest.java @@ -0,0 +1,134 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.mapper.parkingplacetalukmapping; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.locationmaster.DistrictBlock; +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.parkingPlace.M_Parkingplace; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMapping; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMappingTO; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The mapper flattens a stored taluk mapping and the three records it points at + * into the single carrier the parking place screens read. + */ +@DisplayName("ParkingPlaceTalukMappingMapper Test Suite") +class ParkingPlaceTalukMappingMapperTest { + + private final ParkingPlaceTalukMappingMapper mapper = ParkingPlaceTalukMappingMapper.INSTANCE; + + private static ParkingplaceTalukMapping fullMapping() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(7001); + mapping.setParkingPlaceID(31); + mapping.setDistrictID(301); + mapping.setDistrictBlockID(3011); + mapping.setProviderServiceMapID(4001); + mapping.setDeleted(Boolean.FALSE); + mapping.setProcessed("N"); + mapping.setCreatedBy("admin"); + mapping.setModifiedBy("supervisor"); + + M_Parkingplace parkingplace = new M_Parkingplace(); + parkingplace.setParkingPlaceName("Hosur parking"); + parkingplace.setDeleted(Boolean.FALSE); + mapping.setParkingplace(parkingplace); + + M_District district = new M_District(); + district.setDistrictName("Bengaluru Urban"); + district.setDeleted(Boolean.FALSE); + mapping.setM_district(district); + + DistrictBlock block = new DistrictBlock(); + block.setBlockName("Anekal"); + block.setDeleted(Boolean.FALSE); + mapping.setDistrictBlock(block); + return mapping; + } + + @Test + @DisplayName("should carry the names of the parking place, district and taluk onto one carrier") + void shouldFlattenNamesOntoOneCarrier() { + ParkingplaceTalukMappingTO published = mapper.getParkingplaceTalukMappingMap(fullMapping()); + + assertEquals(7001, published.getPpSubDistrictMapID()); + assertEquals("Hosur parking", published.getParkingPlaceName()); + assertEquals("Bengaluru Urban", published.getDistrictName()); + assertEquals("Anekal", published.getDistrictBlockName()); + assertEquals(4001, published.getProviderServiceMapID()); + assertEquals("admin", published.getCreatedBy()); + assertEquals("supervisor", published.getModifiedBy()); + assertEquals("N", published.getProcessed()); + assertEquals(Boolean.FALSE, published.getDeleted()); + assertEquals(Boolean.FALSE, published.getParkingPlaceDeleted()); + assertEquals(Boolean.FALSE, published.getDistrictDeleted()); + assertEquals(Boolean.FALSE, published.getDistrictBlockDeleted()); + } + + @Test + @DisplayName("should leave the names empty when the mapping points at nothing") + void shouldLeaveNamesEmptyWhenNothingPointedAt() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(7002); + + ParkingplaceTalukMappingTO published = mapper.getParkingplaceTalukMappingMap(mapping); + + assertEquals(7002, published.getPpSubDistrictMapID()); + assertNull(published.getParkingPlaceName()); + assertNull(published.getDistrictName()); + assertNull(published.getDistrictBlockName()); + assertNull(published.getParkingPlaceDeleted()); + } + + @Test + @DisplayName("should answer nothing for a mapping that is not there at all") + void shouldAnswerNothingForAbsentMapping() { + assertNull(mapper.getParkingplaceTalukMappingMap(null)); + assertNull(mapper.getParkingplaceTalukMappingMapList(null)); + } + + @Test + @DisplayName("should publish one carrier per mapping in the list") + void shouldPublishOneCarrierPerMapping() { + List published = mapper + .getParkingplaceTalukMappingMapList(List.of(fullMapping(), fullMapping())); + + assertEquals(2, published.size()); + assertEquals("Anekal", published.get(0).getDistrictBlockName()); + } + + @Test + @DisplayName("should publish an empty list when there is no mapping to publish") + void shouldPublishEmptyListForNoMappings() { + assertTrue(mapper.getParkingplaceTalukMappingMapList(new ArrayList<>()).isEmpty()); + } +} diff --git a/src/test/java/com/iemr/admin/service/apiman/ApimanServiceImplTest.java b/src/test/java/com/iemr/admin/service/apiman/ApimanServiceImplTest.java new file mode 100644 index 0000000..98e4d15 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/apiman/ApimanServiceImplTest.java @@ -0,0 +1,261 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.apiman; + +import java.util.HashMap; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.admin.data.apiman.ApimanClient; +import com.iemr.admin.data.apiman.ApimanRegister; +import com.iemr.admin.utils.http.HttpUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The apiman service registers a new service line as a client of the API + * gateway and signs it up to the API contracts that service line needs. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ApimanServiceImpl Test Suite") +class ApimanServiceImplTest { + + private static final String BASE_URL = "https://gateway.example.org"; + private static final String CLIENT_ID = "client-104"; + + @Mock + private HttpUtils httpUtils; + + private HttpUtils originalHttpUtils; + + private ApimanServiceImpl service; + + @BeforeEach + @DisplayName("Stand in for the gateway and point the service at stub addresses") + void setUp() { + originalHttpUtils = (HttpUtils) ReflectionTestUtils.getField(ApimanServiceImpl.class, "httpUtils"); + ReflectionTestUtils.setField(ApimanServiceImpl.class, "httpUtils", httpUtils); + service = new ApimanServiceImpl(); + ReflectionTestUtils.setField(service, "apimanBaseURL", BASE_URL); + ReflectionTestUtils.setField(service, "clientURL", "APIMAN_URL/clients"); + ReflectionTestUtils.setField(service, "contractURL", "APIMAN_URL/clients/CLIENT_ID/contracts"); + ReflectionTestUtils.setField(service, "registerURL", "APIMAN_URL/register"); + ReflectionTestUtils.setField(service, "getClientKey", "APIMAN_URL/clients/CLIENT_ID/apikey"); + ReflectionTestUtils.setField(service, "auth", "Bearer gateway-token"); + ReflectionTestUtils.setField(service, "apimanplanID", "plan"); + ReflectionTestUtils.setField(service, "apimanorgID", "org"); + ReflectionTestUtils.setField(service, "apimanCommonApiID", "common"); + ReflectionTestUtils.setField(service, "apiman1097apiID", "api1097"); + ReflectionTestUtils.setField(service, "apimanMMUapiID", "mmu"); + ReflectionTestUtils.setField(service, "apimanInventoryapiID", "inventory"); + ReflectionTestUtils.setField(service, "apiman104apiID", "api104"); + ReflectionTestUtils.setField(service, "apimanTMapiID", "tm"); + ReflectionTestUtils.setField(service, "apimanSchedulingapiID", "scheduling"); + ReflectionTestUtils.setField(service, "apimanMCTSapiID", "mcts"); + } + + @AfterEach + @DisplayName("Put the real gateway client back so no other suite sees the stand-in") + void tearDown() { + ReflectionTestUtils.setField(ApimanServiceImpl.class, "httpUtils", originalHttpUtils); + } + + private static ApimanClient client() { + ApimanClient client = new ApimanClient(); + client.setId(CLIENT_ID); + client.setName("104 Helpline"); + client.setInitialVersion("1.0"); + return client; + } + + @Test + @DisplayName("createClient should answer the client the gateway registered") + void createClient_shouldAnswerRegisteredClient() throws Exception { + when(httpUtils.post(anyString(), anyString(), any())) + .thenReturn("{\"id\":\"client-104\",\"name\":\"104 Helpline\"}"); + + ApimanClient registered = service.createClient(client()); + + assertEquals(CLIENT_ID, registered.getId()); + assertEquals("104 Helpline", registered.getName()); + } + + @Test + @DisplayName("createClient should send the client to the gateway with the configured credentials") + void createClient_shouldSendWithConfiguredCredentials() throws Exception { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{\"id\":\"client-104\"}"); + ArgumentCaptor url = ArgumentCaptor.forClass(String.class); + ArgumentCaptor> header = ArgumentCaptor.forClass(HashMap.class); + + service.createClient(client()); + + verify(httpUtils).post(url.capture(), anyString(), header.capture()); + assertEquals(BASE_URL + "/clients", url.getValue()); + assertEquals("Bearer gateway-token", header.getValue().get("Authorization")); + assertEquals("application/json", header.getValue().get("Content-Type")); + } + + @Test + @DisplayName("createClient should give up when the gateway answers something that is not a client") + void createClient_shouldGiveUpOnUnreadableReply() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("null"); + + assertThrows(RuntimeException.class, () -> service.createClient(client())); + } + + @Test + @DisplayName("createClientContract should sign a helpline service line up to its own API as well as the shared one") + void createClientContract_shouldSignUpHelplineApis() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(1, CLIENT_ID); + + ArgumentCaptor body = ArgumentCaptor.forClass(String.class); + verify(httpUtils, times(3)).post(anyString(), body.capture(), any()); + assertTrue(body.getAllValues().stream().anyMatch(sent -> sent.contains("api1097")), body.getAllValues() + .toString()); + assertTrue(body.getAllValues().stream().filter(sent -> sent.contains("common")).count() == 2, + "both versions of the shared API must be contracted"); + } + + @Test + @DisplayName("createClientContract should sign a mobile unit service line up to its stock APIs too") + void createClientContract_shouldSignUpMobileUnitApis() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(2, CLIENT_ID); + + ArgumentCaptor body = ArgumentCaptor.forClass(String.class); + verify(httpUtils, times(4)).post(anyString(), body.capture(), any()); + List sent = body.getAllValues(); + assertTrue(sent.stream().anyMatch(one -> one.contains("mmu")), sent.toString()); + assertTrue(sent.stream().anyMatch(one -> one.contains("inventory")), sent.toString()); + } + + @Test + @DisplayName("createClientContract should sign a telemedicine service line up to its scheduling API") + void createClientContract_shouldSignUpTelemedicineApis() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(4, CLIENT_ID); + + ArgumentCaptor body = ArgumentCaptor.forClass(String.class); + verify(httpUtils, times(4)).post(anyString(), body.capture(), any()); + assertTrue(body.getAllValues().stream().anyMatch(one -> one.contains("scheduling")), + body.getAllValues().toString()); + } + + @Test + @DisplayName("createClientContract should contract only the shared API for a service line with no API of its own") + void createClientContract_shouldContractOnlySharedApi() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(5, CLIENT_ID); + + verify(httpUtils, times(2)).post(anyString(), anyString(), any()); + } + + @Test + @DisplayName("createClientContract should contract only the shared API for a service line it does not recognise") + void createClientContract_shouldContractOnlySharedApiForUnknownServiceLine() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(99, CLIENT_ID); + + verify(httpUtils, times(2)).post(anyString(), anyString(), any()); + } + + @Test + @DisplayName("createClientContract should address the client whose contracts are being signed") + void createClientContract_shouldAddressTheClient() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + ArgumentCaptor url = ArgumentCaptor.forClass(String.class); + + service.createClientContract(3, CLIENT_ID); + + verify(httpUtils, times(3)).post(url.capture(), anyString(), any()); + assertEquals(BASE_URL + "/clients/" + CLIENT_ID + "/contracts", url.getValue()); + } + + @Test + @DisplayName("registerClient should publish the registration to the gateway") + void registerClient_shouldPublishRegistration() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + ApimanRegister registration = new ApimanRegister(); + registration.setType("client"); + registration.setEntityId(CLIENT_ID); + ArgumentCaptor url = ArgumentCaptor.forClass(String.class); + + assertNull(service.registerClient(registration), "registration answers nothing back to the caller"); + verify(httpUtils).post(url.capture(), anyString(), any()); + assertEquals(BASE_URL + "/register", url.getValue()); + } + + @Test + @DisplayName("getClientKey should answer the API key the gateway issued") + void getClientKey_shouldAnswerIssuedKey() { + when(httpUtils.get(anyString(), any())).thenReturn("{\"apiKey\":\"key-abc-123\"}"); + + assertEquals("key-abc-123", service.getClientKey(CLIENT_ID)); + } + + @Test + @DisplayName("getClientKey should give up when the gateway does not answer a key") + void getClientKey_shouldGiveUpWithoutKey() { + when(httpUtils.get(anyString(), any())).thenReturn("{\"result\":\"unknown client\"}"); + + assertThrows(RuntimeException.class, () -> service.getClientKey(CLIENT_ID)); + } + + @Test + @DisplayName("the gateway should still be reached when no credentials are configured") + void gatewayCalls_shouldStillBeReachedWithoutCredentials() throws Exception { + ReflectionTestUtils.setField(service, "auth", null); + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{\"id\":\"client-104\"}"); + ArgumentCaptor> header = ArgumentCaptor.forClass(HashMap.class); + + service.createClient(client()); + + verify(httpUtils).post(anyString(), anyString(), header.capture()); + assertNull(header.getValue().get("Authorization"), "no credentials must be sent when none are configured"); + } +} diff --git a/src/test/java/com/iemr/admin/service/blocking/BlockingServiceTest.java b/src/test/java/com/iemr/admin/service/blocking/BlockingServiceTest.java new file mode 100644 index 0000000..39711d1 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/blocking/BlockingServiceTest.java @@ -0,0 +1,309 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.blocking; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.blocking.M_Providerservicemapping_Blocking; +import com.iemr.admin.data.blocking.M_Serviceprovider_Blocking; +import com.iemr.admin.data.blocking.M_Status1; +import com.iemr.admin.data.blocking.T_Providerservicemappingdetail; +import com.iemr.admin.data.blocking.T_Serviceproviderdetail; +import com.iemr.admin.data.blocking.T_Userdetail; +import com.iemr.admin.data.blocking.UserForBlocking; +import com.iemr.admin.data.blocking.V_Showproviderservicemapping; +import com.iemr.admin.repo.blocking.MProviderservicemappingBlockingRepo; +import com.iemr.admin.repo.blocking.MServiceproviderBlockingRepo; +import com.iemr.admin.repo.blocking.MStatusRepo; +import com.iemr.admin.repo.blocking.T_ProviderservicemappingdetailRepo; +import com.iemr.admin.repo.blocking.T_ServiceproviderdetailRepo; +import com.iemr.admin.repo.blocking.T_UserDetailRepo; +import com.iemr.admin.repo.blocking.UserBlockingRepo; +import com.iemr.admin.repo.blocking.V_ShowproviderservicemappingRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The blocking service turns each request to suspend a provider into the right + * repository update, and reports how far a CTI campaign mapping got. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Blocking_Service Test Suite") +class BlockingServiceTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer SERVICE_ID = 3; + private static final Integer STATE_ID = 29; + + @Mock + private V_ShowproviderservicemappingRepo v_ShowproviderservicemappingRepo; + + @Mock + private T_UserDetailRepo t_UserDetailRepo; + + @Mock + private MStatusRepo mStatusRepo; + + @Mock + private UserBlockingRepo userBlockingRepo; + + @Mock + private T_ProviderservicemappingdetailRepo t_ProviderservicemappingdetailRepo; + + @Mock + private MProviderservicemappingBlockingRepo mProviderservicemappingBlockingRepo; + + @Mock + private T_ServiceproviderdetailRepo t_ServiceproviderdetailRepo; + + @Mock + private MServiceproviderBlockingRepo mServiceproviderBlockingRepo; + + @InjectMocks + private Blocking_Service service; + + private static M_Providerservicemapping_Blocking mapping(Integer mapId) { + M_Providerservicemapping_Blocking mapping = new M_Providerservicemapping_Blocking(); + mapping.setProviderServiceMapID(mapId); + mapping.setServiceProviderID(PROVIDER_ID); + mapping.setServiceID(SERVICE_ID); + mapping.setStateID(STATE_ID); + mapping.setcTI_CampaignName("104"); + return mapping; + } + + @Test + @DisplayName("getProviderDetailsById should hand back what the repository holds") + void getProviderDetailsById_shouldHandBackRepositoryContents() { + M_Serviceprovider_Blocking stored = new M_Serviceprovider_Blocking(); + when(mServiceproviderBlockingRepo.getProviderDetailsByID(PROVIDER_ID)).thenReturn(stored); + + assertSame(stored, service.getProviderDetailsById(PROVIDER_ID)); + } + + @Test + @DisplayName("blockServiceProvider should answer the provider the repository stored") + void blockServiceProvider_shouldAnswerStoredProvider() { + M_Serviceprovider_Blocking stored = new M_Serviceprovider_Blocking(); + when(mServiceproviderBlockingRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.blockServiceProvider(stored)); + } + + @Test + @DisplayName("saveData should answer the audit row the repository stored") + void saveData_shouldAnswerStoredAuditRow() { + T_Serviceproviderdetail stored = new T_Serviceproviderdetail(); + when(t_ServiceproviderdetailRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.saveData(stored)); + } + + @Test + @DisplayName("the status updates should each reach their own repository query") + void statusUpdates_shouldReachTheirOwnQuery() { + service.blockProviderByService(PROVIDER_ID, STATE_ID, SERVICE_ID, 2); + service.blockProviderByState(PROVIDER_ID, STATE_ID, 2); + service.blockProvider(PROVIDER_ID, 2); + service.blockProviderByProviderIdAndServiceId(PROVIDER_ID, SERVICE_ID, 2); + service.blockUser(3117, 2); + + verify(mProviderservicemappingBlockingRepo).blockProviderByService(PROVIDER_ID, STATE_ID, SERVICE_ID, 2); + verify(mProviderservicemappingBlockingRepo).blockProviderByState(PROVIDER_ID, STATE_ID, 2); + verify(mProviderservicemappingBlockingRepo).blockProvider(PROVIDER_ID, 2); + verify(mProviderservicemappingBlockingRepo) + .blockProviderByProviderIdAndServiceId(PROVIDER_ID, SERVICE_ID, 2); + verify(userBlockingRepo).blockUser(3117, 2); + } + + @Test + @DisplayName("the mapping lookups should each reach their own repository query") + void mappingLookups_shouldReachTheirOwnQuery() { + M_Providerservicemapping_Blocking stored = mapping(4001); + ArrayList storedList = new ArrayList<>(List.of(stored)); + List stateList = List.of(stored); + when(mProviderservicemappingBlockingRepo.getProviderServiceMappingDetails(PROVIDER_ID, STATE_ID, SERVICE_ID)) + .thenReturn(stored); + when(mProviderservicemappingBlockingRepo.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)) + .thenReturn(stateList); + when(mProviderservicemappingBlockingRepo.getProviderStatus(PROVIDER_ID)).thenReturn(storedList); + when(mProviderservicemappingBlockingRepo.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)) + .thenReturn(storedList); + when(mProviderservicemappingBlockingRepo.findByProviderServiceMapID(4001)).thenReturn(stored); + when(mProviderservicemappingBlockingRepo.save(stored)).thenReturn(stored); + when(mProviderservicemappingBlockingRepo.saveAll(anyList())).thenReturn(storedList); + + assertSame(stored, service.getProviderServiceMappingDetails(PROVIDER_ID, STATE_ID, SERVICE_ID)); + assertSame(stateList, service.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)); + assertSame(storedList, service.getProviderStatus(PROVIDER_ID)); + assertSame(storedList, service.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)); + assertSame(stored, service.getDataByProviderServiceMapId(4001)); + assertSame(stored, service.updateProviderData(stored)); + assertSame(storedList, service.AddServiceProvider(new ArrayList<>())); + } + + @Test + @DisplayName("the view lookups should each reach their own repository query") + void viewLookups_shouldReachTheirOwnQuery() { + ArrayList stored = new ArrayList<>(List.of(new V_Showproviderservicemapping())); + when(v_ShowproviderservicemappingRepo.getProviderStatus(PROVIDER_ID)).thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderStatus1(PROVIDER_ID)).thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderServiceMappingDetails1(PROVIDER_ID, STATE_ID, SERVICE_ID)) + .thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)) + .thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)) + .thenReturn(stored); + + assertSame(stored, service.getProviderStatus1(PROVIDER_ID)); + assertSame(stored, service.getProviderStatus2(PROVIDER_ID)); + assertSame(stored, service.getProviderServiceMappingDetails2(PROVIDER_ID, STATE_ID, SERVICE_ID)); + assertSame(stored, service.getProviderStateMappingDetails1(PROVIDER_ID, STATE_ID)); + assertSame(stored, service.getProviderStatusByProviderAndServiceId2(PROVIDER_ID, SERVICE_ID)); + } + + @Test + @DisplayName("the audit writes should each reach their own repository") + void auditWrites_shouldReachTheirOwnRepository() { + T_Providerservicemappingdetail detail = new T_Providerservicemappingdetail(); + ArrayList details = new ArrayList<>(List.of(detail)); + T_Userdetail userDetail = new T_Userdetail(); + when(t_ProviderservicemappingdetailRepo.save(detail)).thenReturn(detail); + when(t_ProviderservicemappingdetailRepo.saveAll(anyList())).thenReturn(details); + when(t_UserDetailRepo.save(userDetail)).thenReturn(userDetail); + + assertSame(detail, service.savetpsdData(detail)); + assertSame(details, service.savetpsmd(new ArrayList<>())); + assertSame(userDetail, service.saveUserDetails(userDetail)); + } + + @Test + @DisplayName("getUserDetailByUserId and getStatusData should hand back what the repositories hold") + void userLookups_shouldHandBackRepositoryContents() { + UserForBlocking user = new UserForBlocking(); + ArrayList statuses = new ArrayList<>(List.of(new M_Status1())); + when(userBlockingRepo.getUserDetailByUserId(3117)).thenReturn(user); + when(mStatusRepo.getStatusData()).thenReturn(statuses); + + assertSame(user, service.getUserDetailByUserId(3117)); + assertSame(statuses, service.getStatusData()); + } + + @Test + @DisplayName("getServiceLiensUsingProvider should skip a row the query could not fill") + void getServiceLiensUsingProvider_shouldSkipUnfillableRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 4001, PROVIDER_ID, SERVICE_ID, "Tele Medicine", Boolean.FALSE }); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider(PROVIDER_ID)).thenReturn(rows); + + assertEquals(1, service.getServiceLiensUsingProvider(PROVIDER_ID).size()); + } + + @Test + @DisplayName("mapctidata should report a clean run when every mapping is written") + void mapctidata_shouldReportCleanRun() { + when(mProviderservicemappingBlockingRepo.createcitmapping(anyInt(), any())).thenReturn(1); + + assertEquals("Mapping Successful", service.mapctidata(List.of(mapping(4001), mapping(4002)))); + } + + @Test + @DisplayName("mapctidata should report how far it got when a mapping is rejected") + void mapctidata_shouldReportHowFarItGot() { + when(mProviderservicemappingBlockingRepo.createcitmapping(4001, "104")).thenReturn(1); + when(mProviderservicemappingBlockingRepo.createcitmapping(4002, "104")).thenReturn(0); + + String status = service.mapctidata(List.of(mapping(4001), mapping(4002))); + + assertTrue(status.startsWith("Mapping Failed"), status); + assertTrue(status.contains("after 1 entries"), status); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should narrow by provider, service and state when all are named") + void getServiceLiensUsingProvider1_shouldNarrowByAllThree() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 4001, PROVIDER_ID, SERVICE_ID, "Tele Medicine", STATE_ID, "Karnataka", "104", + Boolean.FALSE, "N", Boolean.FALSE }); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1(PROVIDER_ID, SERVICE_ID, STATE_ID)) + .thenReturn(rows); + + assertEquals(1, service.getServiceLiensUsingProvider1(mapping(4001)).size()); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should narrow by provider and service when no state is named") + void getServiceLiensUsingProvider1_shouldNarrowByProviderAndService() { + M_Providerservicemapping_Blocking request = mapping(4001); + request.setStateID(null); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1(PROVIDER_ID, SERVICE_ID)) + .thenReturn(new ArrayList<>()); + + service.getServiceLiensUsingProvider1(request); + + verify(mProviderservicemappingBlockingRepo).getServiceLiensUsingProvider1(PROVIDER_ID, SERVICE_ID); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should narrow by provider alone when no service is named") + void getServiceLiensUsingProvider1_shouldNarrowByProviderAlone() { + M_Providerservicemapping_Blocking request = mapping(4001); + request.setStateID(null); + request.setServiceID(null); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1(PROVIDER_ID)) + .thenReturn(new ArrayList<>()); + + service.getServiceLiensUsingProvider1(request); + + verify(mProviderservicemappingBlockingRepo).getServiceLiensUsingProvider1(PROVIDER_ID); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should answer every mapping when the request narrows nothing") + void getServiceLiensUsingProvider1_shouldAnswerEveryMapping() { + M_Providerservicemapping_Blocking request = new M_Providerservicemapping_Blocking(); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1()).thenReturn(new ArrayList<>()); + + service.getServiceLiensUsingProvider1(request); + + verify(mProviderservicemappingBlockingRepo).getServiceLiensUsingProvider1(); + } +} diff --git a/src/test/java/com/iemr/admin/service/bulkRegistration/BulkRegistrationServiceImplTest.java b/src/test/java/com/iemr/admin/service/bulkRegistration/BulkRegistrationServiceImplTest.java new file mode 100644 index 0000000..50bd3d1 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/bulkRegistration/BulkRegistrationServiceImplTest.java @@ -0,0 +1,588 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.bulkRegistration; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.bulkuser.BulkRegistrationError; +import com.iemr.admin.data.bulkuser.Employee; +import com.iemr.admin.data.bulkuser.EmployeeList; +import com.iemr.admin.data.employeemaster.M_Community; +import com.iemr.admin.data.employeemaster.M_Gender; +import com.iemr.admin.data.employeemaster.M_Religion; +import com.iemr.admin.data.employeemaster.M_Title; +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_Userqualification; +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.rolemaster.StateMasterForRole; +import com.iemr.admin.repo.employeemaster.V_ShowuserRepo; +import com.iemr.admin.service.employeemaster.EmployeeMasterInter; +import com.iemr.admin.service.locationmaster.LocationMasterServiceInter; +import com.iemr.admin.service.rolemaster.Role_MasterInter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Bulk registration turns an uploaded spreadsheet into user records. A row that + * fails validation must be reported rather than half-saved, so the error log is + * as much a deliverable as the users it creates. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("BulkRegistrationServiceImpl Test Suite") +class BulkRegistrationServiceImplTest { + + /** Excel serial numbers, which is how the uploaded sheet carries its dates. */ + private static final String DOB_SERIAL = "30000"; + private static final String DOJ_SERIAL = "40000"; + + @Mock + private EmployeeMasterInter employeeMasterInter; + + @Mock + private Role_MasterInter roleMasterInter; + + @Mock + private V_ShowuserRepo showuserRepo; + + @Mock + private LocationMasterServiceInter locationMasterServiceInter; + + @Mock + private EmployeeXmlService employeeXmlService; + + @InjectMocks + private BulkRegistrationServiceImpl service; + + private Employee employee; + + @BeforeEach + void setUp() throws Exception { + employee = validEmployee(); + + M_Title title = new M_Title(); + title.setTitleID(1); + title.setTitleName("Ms."); + when(employeeMasterInter.getAllTitle()).thenReturn(new ArrayList<>(List.of(title))); + + M_Gender gender = new M_Gender(); + gender.setGenderID(2); + gender.setGenderName("Female"); + when(employeeMasterInter.getAllGender()).thenReturn(new ArrayList<>(List.of(gender))); + + M_Userqualification qualification = new M_Userqualification(); + qualification.setQualificationID(5); + qualification.setName("MBBS"); + when(employeeMasterInter.getQualification()).thenReturn(new ArrayList<>(List.of(qualification))); + + M_Community community = new M_Community(); + community.setCommunityID(3); + community.setCommunityType("General"); + when(employeeMasterInter.getAllCommunity()).thenReturn(new ArrayList<>(List.of(community))); + + M_Religion religion = new M_Religion(); + religion.setReligionID(4); + religion.setReligionType("Hindu"); + when(employeeMasterInter.getAllReligion()).thenReturn(new ArrayList<>(List.of(religion))); + + StateMasterForRole state = new StateMasterForRole(); + state.setStateID(29); + state.setStateName("Karnataka"); + when(roleMasterInter.getAllState()).thenReturn(new ArrayList<>(List.of(state))); + + M_District district = new M_District(); + district.setDistrictID(301); + district.setDistrictName("Bengaluru Urban"); + when(locationMasterServiceInter.getAllDistrictByStateId(29)).thenReturn(new ArrayList<>(List.of(district))); + + when(employeeMasterInter.FindEmployeeName(anyString())).thenReturn("usernotexist"); + when(employeeMasterInter.FindEmployeeContact(anyString())).thenReturn("contactnotexist"); + when(employeeMasterInter.FindEmployeeAadhaar(anyString())).thenReturn("aadhaarnotexist"); + + M_User1 saved = new M_User1(); + saved.setUserID(3117); + when(employeeMasterInter.saveBulkUserEmployee(any())).thenReturn(saved); + } + + private static Employee validEmployee() { + Employee employee = new Employee(); + employee.setTitle("Ms"); + employee.setFirstName("Asha"); + employee.setMiddleName(""); + employee.setLastName("Rao"); + employee.setGender("Female"); + employee.setContactNo("9000000001"); + employee.setDesignation("ASHA"); + employee.setEmergencyContactNo("9000000002"); + employee.setDob(DOB_SERIAL); + employee.setEmail("asha.rao@example.org"); + employee.setAadhaarNo("111122223333"); + employee.setPan("ABCDEFGH123"); + employee.setQualification("MBBS"); + employee.setFatherName("Ravi"); + employee.setMotherName("Meera"); + employee.setCommunity("General"); + employee.setReligion("Hindu"); + employee.setAddressLine1("Main Road"); + employee.setState("Karnataka"); + employee.setDistrict("Bengaluru Urban"); + employee.setPincode("560001"); + employee.setPermanentAddressLine1("Main Road"); + employee.setPermanentState("Karnataka"); + employee.setPermanentDistrict("Bengaluru Urban"); + employee.setPermanentPincode("560001"); + employee.setDateOfJoining(DOJ_SERIAL); + employee.setUserName("EMP-1"); + employee.setPassword("plain-secret"); + return employee; + } + + private void uploadContains(Employee... employees) throws Exception { + EmployeeList list = new EmployeeList(); + list.setEmployees(new ArrayList<>(List.of(employees))); + when(employeeXmlService.parseXml(anyString())).thenReturn(list); + } + + @Nested + @DisplayName("registerBulkUser") + class RegisterBulkUserTests { + + @Test + @DisplayName("should register a row that passes every rule") + void register_shouldRegisterValidRow() throws Exception { + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(1, service.totalEmployeeListSize); + assertEquals(1, service.m_bulkUser.size()); + assertTrue(service.errorLogs.isEmpty(), service.errorLogs.toString()); + verify(employeeMasterInter).saveBulkUserEmployee(any()); + verify(employeeMasterInter).saveDemography(any()); + } + + @Test + @DisplayName("should carry the uploaded details onto the user it stores") + void register_shouldCarryDetailsOntoStoredUser() throws Exception { + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + M_User1 stored = service.m_bulkUser.get(0); + assertEquals("Asha", stored.getFirstName()); + assertEquals("9000000001", stored.getUserName(), "the contact number is used as the sign-in name"); + assertEquals("EMP-1", stored.getEmployeeID()); + assertEquals(77, stored.getServiceProviderID()); + assertEquals(2, stored.getStatusID()); + assertFalse("plain-secret".equals(stored.getPassword()), "the password must be hashed before storing"); + } + + @Test + @DisplayName("should report a row that names no user") + void register_shouldReportRowWithoutUserName() throws Exception { + employee.setUserName(""); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(1, service.errorLogs.size()); + assertTrue(service.errorLogs.get(0).contains("Please Enter UserName"), service.errorLogs.toString()); + verify(employeeMasterInter, never()).saveBulkUserEmployee(any()); + } + + @Test + @DisplayName("should report a row whose user name is already taken") + void register_shouldReportExistingUser() throws Exception { + when(employeeMasterInter.FindEmployeeName(anyString())).thenReturn("userexist"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("User Already exist"), service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a row whose contact number is already taken") + void register_shouldReportExistingContact() throws Exception { + when(employeeMasterInter.FindEmployeeContact(anyString())).thenReturn("contactexist"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Contact No Already exist"), service.errorLogs.toString()); + } + + @Test + @DisplayName("should report every rule a row breaks rather than only the first") + void register_shouldReportEveryBrokenRule() throws Exception { + employee.setTitle(""); + employee.setFirstName(""); + employee.setLastName(""); + employee.setEmail("not-an-email"); + employee.setContactNo("12345"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("Title is missing."), reported); + assertTrue(reported.contains("First Name is missing."), reported); + assertTrue(reported.contains("Last Name is missing."), reported); + assertTrue(reported.contains("Invalid Email format."), reported); + assertTrue(reported.contains("Contact Number is invalid"), reported); + } + + @Test + @DisplayName("should report a name that is a number rather than a name") + void register_shouldReportNumericName() throws Exception { + employee.setFirstName("12345"); + employee.setLastName("67890"); + employee.setMiddleName("42"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("First name is invalid."), reported); + assertTrue(reported.contains("Last name is invalid."), reported); + assertTrue(reported.contains("Middle name is invalid."), reported); + } + + @Test + @DisplayName("should report a name longer than the column can hold") + void register_shouldReportOverlongName() throws Exception { + String tooLong = "A".repeat(51); + employee.setFirstName(tooLong); + employee.setMiddleName(tooLong); + employee.setLastName(tooLong); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("First name is invalid."), reported); + assertTrue(reported.contains("Middle name is invalid."), reported); + assertTrue(reported.contains("Last name is invalid."), reported); + } + + @Test + @DisplayName("should report a title the master does not know") + void register_shouldReportUnknownTitle() throws Exception { + employee.setTitle("Archduke"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Title is invalid."), service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a district the master does not know") + void register_shouldReportUnknownDistrict() throws Exception { + employee.setDistrict("Nowhere"); + employee.setPermanentDistrict("Nowhere"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("Current District is invalid."), reported); + assertTrue(reported.contains("Permanent District is invalid."), reported); + } + + @Test + @DisplayName("should abandon the upload when a row names a state the master does not know") + void register_shouldAbandonUploadForUnknownState() throws Exception { + employee.setState("Atlantis"); + employee.setPermanentState("Atlantis"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(List.of("Data is invalid or empty"), service.errorLogs, + "an unresolvable state leaves no districts to check against, so the upload is refused"); + assertTrue(service.m_bulkUser.isEmpty()); + } + + @Test + @DisplayName("should report an Aadhaar number that is already on file") + void register_shouldReportDuplicateAadhaar() throws Exception { + when(employeeMasterInter.FindEmployeeAadhaar(anyString())).thenReturn("aadhaarexist"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Duplicate aadhaar number found"), + service.errorLogs.toString()); + } + + @Test + @DisplayName("should report an Aadhaar number that is not twelve digits") + void register_shouldReportMalformedAadhaar() throws Exception { + employee.setAadhaarNo("1234"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Aadhaar number is invalid"), + service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a date of birth in the future") + void register_shouldReportFutureDateOfBirth() throws Exception { + employee.setDob("50000"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Date of Birth is invalid."), + service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a row whose mandatory fields are simply blank") + void register_shouldReportBlankMandatoryFields() throws Exception { + employee.setGender(""); + employee.setContactNo(""); + employee.setDesignation(""); + employee.setEmergencyContactNo(""); + employee.setDob(""); + employee.setEmail(""); + employee.setPassword(""); + employee.setQualification(""); + employee.setState(""); + employee.setDistrict(""); + employee.setPermanentState(""); + employee.setPermanentDistrict(""); + employee.setDateOfJoining(""); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("Gender is missing"), reported); + assertTrue(reported.contains("Contact number missing"), reported); + assertTrue(reported.contains("Designation is missing"), reported); + assertTrue(reported.contains("Emergency contact number is missing"), reported); + assertTrue(reported.contains("Date of Birth is missing."), reported); + assertTrue(reported.contains("Email is missing."), reported); + assertTrue(reported.contains("Qualification is missing"), reported); + assertTrue(reported.contains("Date of Joining is missing."), reported); + } + + @Test + @DisplayName("should report an upload that carries no rows at all") + void register_shouldReportEmptyUpload() throws Exception { + EmployeeList list = new EmployeeList(); + list.setEmployees(new ArrayList<>()); + when(employeeXmlService.parseXml(anyString())).thenReturn(list); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(List.of("Data is invalid or empty"), service.errorLogs); + } + + @Test + @DisplayName("should report an upload it cannot read at all") + void register_shouldReportUnreadableUpload() throws Exception { + when(employeeXmlService.parseXml(anyString())).thenThrow(new IllegalStateException("not xml")); + + service.registerBulkUser("not xml", "auth", "admin", 77); + + assertEquals(List.of("Data is invalid or empty"), service.errorLogs); + } + + @Test + @DisplayName("should keep going through the sheet after a row it cannot register") + void register_shouldKeepGoingAfterABadRow() throws Exception { + Employee bad = validEmployee(); + bad.setUserName(""); + uploadContains(bad, employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(2, service.totalEmployeeListSize); + assertEquals(1, service.m_bulkUser.size(), "the good row must still be registered"); + assertEquals(1, service.errorLogs.size()); + } + } + + @Nested + @DisplayName("Master lookups") + class MasterLookupTests { + + @Test + @DisplayName("getCommunityId should resolve a known community and answer zero for an unknown one") + void getCommunityId_shouldResolveKnownCommunity() { + assertEquals(3, service.getCommunityId("General")); + assertEquals(0, service.getCommunityId("Unknown")); + } + + @Test + @DisplayName("getQualificationId should resolve a known qualification and answer zero for an unknown one") + void getQualificationId_shouldResolveKnownQualification() { + assertEquals(5, service.getQualificationId("MBBS")); + assertEquals(0, service.getQualificationId("Unknown")); + } + + @Test + @DisplayName("getReligionStringId should resolve a known religion") + void getReligionStringId_shouldResolveKnownReligion() { + assertEquals(4, service.getReligionStringId("Hindu")); + assertEquals(0, service.getReligionStringId("Unknown")); + } + + @Test + @DisplayName("getReligionStringId should treat an unstated religion as none") + void getReligionStringId_shouldTreatUnstatedAsNone() { + assertEquals(0, service.getReligionStringId("Not given")); + verify(employeeMasterInter, never()).getAllReligion(); + } + + @Test + @DisplayName("getStateId should resolve a known state and load its districts") + void getStateId_shouldResolveKnownStateAndLoadDistricts() { + assertEquals(29, service.getStateId("Karnataka")); + verify(locationMasterServiceInter).getAllDistrictByStateId(29); + } + + @Test + @DisplayName("getStateId should answer zero for a state the master does not know") + void getStateId_shouldAnswerZeroForUnknownState() { + assertEquals(0, service.getStateId("Atlantis")); + verify(locationMasterServiceInter, never()).getAllDistrictByStateId(29); + } + + @Test + @DisplayName("getDistrictId should resolve a district once its state has been resolved") + void getDistrictId_shouldResolveDistrictAfterState() { + service.getStateId("Karnataka"); + + assertEquals(301, service.getDistrictId("Bengaluru Urban")); + assertEquals(0, service.getDistrictId("Nowhere")); + } + + @Test + @DisplayName("getDistrictId should answer zero when no district name is given") + void getDistrictId_shouldAnswerZeroWithoutAName() { + assertEquals(0, service.getDistrictId("")); + } + + @Test + @DisplayName("getAllState should hand back what the role master holds") + void getAllState_shouldHandBackRoleMasterContents() { + assertEquals(1, service.getAllState().size()); + } + + @Test + @DisplayName("getDesignationId should answer the fixed designation the upload uses") + void getDesignationId_shouldAnswerFixedDesignation() { + assertEquals(20, service.getDesignationId("ASHA")); + } + } + + @Nested + @DisplayName("Helpers") + class HelperTests { + + @Test + @DisplayName("escapeXmlSpecialChars should escape a bare ampersand and leave real entities alone") + void escape_shouldEscapeBareAmpersandOnly() { + assertEquals("Ram & Co", BulkRegistrationServiceImpl.escapeXmlSpecialChars("Ram & Co")); + assertEquals("Ram & Co", BulkRegistrationServiceImpl.escapeXmlSpecialChars("Ram & Co")); + assertEquals("<tag>", BulkRegistrationServiceImpl.escapeXmlSpecialChars("<tag>")); + } + + @Test + @DisplayName("isNumeric should tell a number apart from a name") + void isNumeric_shouldTellNumberFromName() { + assertTrue(BulkRegistrationServiceImpl.isNumeric("12345")); + assertFalse(BulkRegistrationServiceImpl.isNumeric("Asha")); + } + + @Test + @DisplayName("isValidAadhar should report anything that is not twelve digits") + void isValidAadhar_shouldReportNonTwelveDigitNumbers() { + assertFalse(BulkRegistrationServiceImpl.isValidAadhar("111122223333")); + assertTrue(BulkRegistrationServiceImpl.isValidAadhar("1234")); + assertTrue(BulkRegistrationServiceImpl.isValidAadhar("not-a-number")); + } + + @Test + @DisplayName("convertStringIntoDate should read the spreadsheet's own date serial") + void convertStringIntoDate_shouldReadExcelSerial() { + assertEquals("1982-02-18", BulkRegistrationServiceImpl.convertStringIntoDate(DOB_SERIAL).toString()); + } + + @Test + @DisplayName("generateStrongPassword should answer a different hash each time it is called") + void generateStrongPassword_shouldSaltEachHash() throws Exception { + String first = service.generateStrongPassword("plain-secret"); + String second = service.generateStrongPassword("plain-secret"); + + assertTrue(first.startsWith("1001:"), first); + assertFalse(first.equals(second), "each hash must carry its own salt"); + } + + @Test + @DisplayName("insertErrorLog should write one workbook row per reported row") + void insertErrorLog_shouldWriteOneRowPerReportedRow() { + BulkRegistrationError error = new BulkRegistrationError(); + error.setRowNumber(1); + error.setUserName("EMP-1"); + error.setError(List.of("Title is missing.")); + service.bulkRegistrationErrors.add(error); + + byte[] workbook = service.insertErrorLog(); + + assertNotNull(workbook); + assertTrue(workbook.length > 0, "a workbook with a reported row must not be empty"); + } + + @Test + @DisplayName("insertErrorLog should still answer a workbook when nothing was reported") + void insertErrorLog_shouldAnswerWorkbookWithoutErrors() { + assertTrue(service.insertErrorLog().length > 0); + } + } +} diff --git a/src/test/java/com/iemr/admin/service/bulkRegistration/EmployeeXmlServiceTest.java b/src/test/java/com/iemr/admin/service/bulkRegistration/EmployeeXmlServiceTest.java new file mode 100644 index 0000000..cce6537 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/bulkRegistration/EmployeeXmlServiceTest.java @@ -0,0 +1,58 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.bulkRegistration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.bulkuser.EmployeeList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** Reads the uploaded spreadsheet, which reaches the service as XML. */ +@DisplayName("EmployeeXmlService Test Suite") +class EmployeeXmlServiceTest { + + private final EmployeeXmlService service = new EmployeeXmlService(); + + @Test + @DisplayName("parseXml should read each employee row out of the uploaded document") + void parseXml_shouldReadEachEmployeeRow() throws Exception { + String xml = "AshaRao" + + "EMP-1" + + "RaviKumar" + + "EMP-2"; + + EmployeeList list = service.parseXml(xml); + + assertEquals(2, list.getEmployees().size()); + assertEquals("Asha", list.getEmployees().get(0).getFirstName()); + assertEquals("EMP-2", list.getEmployees().get(1).getUserName()); + } + + @Test + @DisplayName("parseXml should raise rather than answer a half-read document") + void parseXml_shouldRaiseForMalformedDocument() { + assertThrows(Exception.class, () -> service.parseXml("")); + } +} diff --git a/src/test/java/com/iemr/admin/service/calibration/CalibrationServiceImplTest.java b/src/test/java/com/iemr/admin/service/calibration/CalibrationServiceImplTest.java new file mode 100644 index 0000000..79d51be --- /dev/null +++ b/src/test/java/com/iemr/admin/service/calibration/CalibrationServiceImplTest.java @@ -0,0 +1,222 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.calibration; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.admin.data.calibration.CalibrationStrip; +import com.iemr.admin.repo.calibration.CalibrationRepo; +import com.iemr.admin.utils.exception.IEMRException; +import com.iemr.admin.utils.mapper.OutputMapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The calibration service keeps the test strip codes a provider calibrates + * against, refusing a code the provider already holds. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CalibrationServiceImpl Test Suite") +class CalibrationServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Long STRIP_ID = 6601L; + private static final String STRIP_CODE = "STRIP-77"; + + @Mock + private CalibrationRepo calibrationRepo; + + @InjectMocks + private CalibrationServiceImpl service; + + @BeforeEach + @DisplayName("Fix the page size the screens are served in and prime the shared output builder") + void setUp() { + ReflectionTestUtils.setField(service, "calibrationPageSize", 10); + new OutputMapper(); + } + + private static CalibrationStrip strip() { + CalibrationStrip strip = new CalibrationStrip(); + strip.setCalibrationStripID(STRIP_ID); + strip.setStripCode(STRIP_CODE); + strip.setProviderServiceMapID(PSM_ID); + strip.setDeleted(Boolean.FALSE); + return strip; + } + + private static CalibrationStrip request() { + CalibrationStrip request = new CalibrationStrip(); + request.setStripCode(STRIP_CODE); + request.setProviderServiceMapID(PSM_ID); + return request; + } + + @Test + @DisplayName("saveData should record a strip code the provider does not hold yet") + void save_shouldRecordNewStripCode() throws Exception { + when(calibrationRepo.checkIfAlreadyStripPresent(PSM_ID, STRIP_CODE)).thenReturn(new ArrayList<>()); + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(strip()); + + assertEquals(1, service.saveData(request())); + } + + @Test + @DisplayName("saveData should refuse a strip code the provider already holds") + void save_shouldRefuseDuplicateStripCode() { + when(calibrationRepo.checkIfAlreadyStripPresent(PSM_ID, STRIP_CODE)) + .thenReturn(new ArrayList<>(List.of(strip()))); + + IEMRException refusal = assertThrows(IEMRException.class, () -> service.saveData(request())); + + assertEquals("Strip code already exists", refusal.getMessage()); + verify(calibrationRepo, never()).save(any(CalibrationStrip.class)); + } + + @Test + @DisplayName("saveData should refuse a strip the repository did not give an identity") + void save_shouldRefuseStripWithoutIdentity() { + when(calibrationRepo.checkIfAlreadyStripPresent(PSM_ID, STRIP_CODE)).thenReturn(new ArrayList<>()); + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(new CalibrationStrip()); + + assertEquals("Error while saving data", + assertThrows(IEMRException.class, () -> service.saveData(request())).getMessage()); + } + + @Test + @DisplayName("saveData should record nothing when the request names no strip code") + void save_shouldRecordNothingWithoutStripCode() throws Exception { + assertEquals(0, service.saveData(new CalibrationStrip())); + verify(calibrationRepo, never()).save(any(CalibrationStrip.class)); + } + + @Test + @DisplayName("fetchData should answer one page of strips and how many pages there are") + void fetch_shouldAnswerOnePageAndPageCount() throws Exception { + CalibrationStrip request = request(); + request.setPageNo(0); + Pageable pageable = PageRequest.of(0, 10); + Page page = new PageImpl<>(List.of(strip()), pageable, 1); + when(calibrationRepo.getCalibrationStripsWithPagination(PSM_ID, pageable)).thenReturn(page); + + String answered = service.fetchData(request); + + assertTrue(answered.contains(STRIP_CODE), answered); + assertTrue(answered.contains("pageCount"), answered); + } + + @Test + @DisplayName("fetchData should answer every strip when the caller asks for no particular page") + void fetch_shouldAnswerEveryStripWithoutPaging() throws Exception { + when(calibrationRepo.getCalibrationStripsWithoutPagination(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(strip()))); + + String answered = service.fetchData(request()); + + assertTrue(answered.contains(STRIP_CODE), answered); + assertTrue(!answered.contains("pageCount"), "an unpaged answer carries no page count"); + } + + @Test + @DisplayName("fetchData should refuse a request that names no provider") + void fetch_shouldRefuseRequestWithoutProvider() { + assertThrows(IEMRException.class, () -> service.fetchData(new CalibrationStrip())); + } + + @Test + @DisplayName("deleteData should report how many strips the retirement touched") + void delete_shouldReportRowsTouched() throws Exception { + CalibrationStrip request = strip(); + request.setDeleted(Boolean.TRUE); + when(calibrationRepo.deleteCalibrationStrip(STRIP_ID, Boolean.TRUE)).thenReturn(1); + + assertEquals(1, service.deleteData(request)); + } + + @Test + @DisplayName("deleteData should refuse a request that names no strip") + void delete_shouldRefuseRequestWithoutStrip() { + assertEquals("Invalid request", + assertThrows(IEMRException.class, () -> service.deleteData(new CalibrationStrip())).getMessage()); + } + + @Test + @DisplayName("deleteData should give up when the retirement cannot be recorded") + void delete_shouldGiveUpWhenStorageFails() { + CalibrationStrip request = strip(); + request.setDeleted(Boolean.TRUE); + when(calibrationRepo.deleteCalibrationStrip(anyLong(), anyBoolean())) + .thenThrow(new RuntimeException("row is locked")); + + assertThrows(IEMRException.class, () -> service.deleteData(request)); + } + + @Test + @DisplayName("updateData should record the change against the strip") + void update_shouldRecordChange() throws Exception { + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(strip()); + + assertEquals(1, service.updateData(request())); + } + + @Test + @DisplayName("updateData should refuse a strip the repository did not give an identity") + void update_shouldRefuseStripWithoutIdentity() { + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(new CalibrationStrip()); + + assertEquals("Error while updating data", + assertThrows(IEMRException.class, () -> service.updateData(request())).getMessage()); + } + + @Test + @DisplayName("updateData should record nothing when the request names no strip code") + void update_shouldRecordNothingWithoutStripCode() throws Exception { + assertEquals(0, service.updateData(new CalibrationStrip())); + } +} diff --git a/src/test/java/com/iemr/admin/service/drugstrangth/DrugStrangthServiceTest.java b/src/test/java/com/iemr/admin/service/drugstrangth/DrugStrangthServiceTest.java new file mode 100644 index 0000000..b8b1632 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/drugstrangth/DrugStrangthServiceTest.java @@ -0,0 +1,112 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.drugstrangth; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.drugstrangth.M_104DrugStrength; +import com.iemr.admin.repo.blocking.DrugStrangthRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The drug strength service keeps the strengths a drug can be dispensed in. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DrugStrangthService Test Suite") +class DrugStrangthServiceTest { + + private static final Integer STRENGTH_ID = 33; + + @Mock + private DrugStrangthRepo drugStrangthRepo; + + @InjectMocks + private DrugStrangthService service; + + private static M_104DrugStrength strength() { + M_104DrugStrength strength = new M_104DrugStrength(); + strength.setDrugStrengthID(STRENGTH_ID); + strength.setDrugStrength("500 mg"); + return strength; + } + + @Test + @DisplayName("createDrugStrangth should answer the strengths the repository stored") + void create_shouldAnswerStoredStrengths() { + ArrayList stored = new ArrayList<>(List.of(strength())); + when(drugStrangthRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createDrugStrangth(new ArrayList<>())); + } + + @Test + @DisplayName("getDrugStrangth should answer every strength on file") + void get_shouldAnswerEveryStrength() { + when(drugStrangthRepo.findAll()).thenReturn(new ArrayList<>(List.of(strength()))); + + assertEquals(1, service.getDrugStrangth().size()); + } + + @Test + @DisplayName("getDrugStrangth should answer nothing when no strength is on file") + void get_shouldAnswerNothingWhenNoneOnFile() { + when(drugStrangthRepo.findAll()).thenReturn(new ArrayList()); + + assertTrue(service.getDrugStrangth().isEmpty()); + } + + @Test + @DisplayName("updateDrugStrangth and saveupdatedData should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_104DrugStrength stored = strength(); + when(drugStrangthRepo.findByDrugStrengthID(STRENGTH_ID)).thenReturn(stored); + when(drugStrangthRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.updateDrugStrangth(STRENGTH_ID)); + assertSame(stored, service.saveupdatedData(stored)); + } + + @Test + @DisplayName("updateDrugStrangth should answer nothing when the strength is unknown") + void update_shouldAnswerNothingForUnknownStrength() { + when(drugStrangthRepo.findByDrugStrengthID(-1)).thenReturn(null); + + assertNull(service.updateDrugStrangth(-1)); + } +} diff --git a/src/test/java/com/iemr/admin/service/drugtype/DrugtypeServiceImplTest.java b/src/test/java/com/iemr/admin/service/drugtype/DrugtypeServiceImplTest.java new file mode 100644 index 0000000..bfdfebc --- /dev/null +++ b/src/test/java/com/iemr/admin/service/drugtype/DrugtypeServiceImplTest.java @@ -0,0 +1,113 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.drugtype; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.drugtype.M_Drugtype; +import com.iemr.admin.repo.drugtype.DrugtypeRepo; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The drug type service keeps the dosage forms a provider stocks. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DrugtypeServiceImpl Test Suite") +class DrugtypeServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer DRUG_TYPE_ID = 21; + + @Mock + private DrugtypeRepo drugtypeRepo; + + @InjectMocks + private DrugtypeServiceImpl service; + + private static M_Drugtype drugType() { + M_Drugtype drugType = new M_Drugtype(); + drugType.setDrugTypeID(DRUG_TYPE_ID); + drugType.setDrugTypeName("Tablet"); + drugType.setProviderServiceMapID(PSM_ID); + return drugType; + } + + @Test + @DisplayName("createDrugtypeData should answer the drug types the repository stored") + void create_shouldAnswerStoredDrugTypes() { + ArrayList stored = new ArrayList<>(List.of(drugType())); + when(drugtypeRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createDrugtypeData(new ArrayList<>())); + } + + @Test + @DisplayName("createDrugtypeData should answer nothing when the repository stored nothing") + void create_shouldAnswerNothingWhenNothingStored() { + when(drugtypeRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createDrugtypeData(new ArrayList<>())); + } + + @Test + @DisplayName("getDrugtypeData should answer the drug types the provider stocks") + void get_shouldAnswerProvidersDrugTypes() { + ArrayList stocked = new ArrayList<>(List.of(drugType())); + when(drugtypeRepo.getDrugtypeData(PSM_ID)).thenReturn(stocked); + + assertSame(stocked, service.getDrugtypeData(PSM_ID)); + } + + @Test + @DisplayName("editDrugtypeData and saveeditDrugtype should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_Drugtype stored = drugType(); + when(drugtypeRepo.geteditedData(DRUG_TYPE_ID)).thenReturn(stored); + when(drugtypeRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.editDrugtypeData(DRUG_TYPE_ID)); + assertSame(stored, service.saveeditDrugtype(stored)); + } + + @Test + @DisplayName("editDrugtypeData should answer nothing when the drug type is unknown") + void edit_shouldAnswerNothingForUnknownDrugType() { + when(drugtypeRepo.geteditedData(-1)).thenReturn(null); + + assertNull(service.editDrugtypeData(-1)); + } +} diff --git a/src/test/java/com/iemr/admin/service/emailconfig/EmailConfigServiceImplTest.java b/src/test/java/com/iemr/admin/service/emailconfig/EmailConfigServiceImplTest.java new file mode 100644 index 0000000..11b5b8b --- /dev/null +++ b/src/test/java/com/iemr/admin/service/emailconfig/EmailConfigServiceImplTest.java @@ -0,0 +1,189 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.emailconfig; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.emailconfig.AuthorityEmail; +import com.iemr.admin.mapper.emailconfig.InstituteEmailConfigMapper; +import com.iemr.admin.model.emailconfig.AuthEmailRequest; +import com.iemr.admin.model.emailconfig.AuthEmailResponse; +import com.iemr.admin.model.emailconfig.CreateAuthEmailRequestModel; +import com.iemr.admin.model.emailconfig.UpdateAuthEmailRequest; +import com.iemr.admin.repository.emailconfig.InstituteEmailRepo; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The email config service keeps the authority mailboxes a complaint is copied + * to, narrowed by whichever parts of the location the caller names. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("EmailConfigServiceImpl Test Suite") +class EmailConfigServiceImplTest { + + @Mock + private EntityManager entityManager; + + @Mock + private InstituteEmailRepo instituteRepo; + + @Mock + private InstituteEmailConfigMapper instituteEmailConfigMapper; + + @InjectMocks + private EmailConfigServiceImpl service; + + private CriteriaQuery query; + private TypedQuery typedQuery; + + @SuppressWarnings("unchecked") + @BeforeEach + @DisplayName("Stand in for the criteria query the service builds by hand") + void setUp() { + CriteriaBuilder builder = mock(CriteriaBuilder.class); + query = mock(CriteriaQuery.class); + Root root = mock(Root.class); + typedQuery = mock(TypedQuery.class); + + when(entityManager.getCriteriaBuilder()).thenReturn(builder); + when(builder.createQuery(AuthorityEmail.class)).thenReturn(query); + when(query.from(AuthorityEmail.class)).thenReturn(root); + when(query.select(any())).thenReturn(query); + when(query.where(any(Predicate[].class))).thenReturn(query); + when(query.orderBy(any(jakarta.persistence.criteria.Order[].class))).thenReturn(query); + when(root.get(anyString())).thenReturn(mock(Path.class)); + when(builder.equal(any(), any())).thenReturn(mock(Predicate.class)); + when(entityManager.createQuery(query)).thenReturn(typedQuery); + } + + private static AuthEmailRequest fullyNarrowedRequest() { + AuthEmailRequest request = new AuthEmailRequest(); + request.setAuthorityEmailID(1); + request.setDeleted(false); + request.setDistrictID(301); + request.setDistrictBranchMappingID(30111); + request.setBlockID(3011); + request.setProviderServiceMapID(4001); + request.setStateID(29); + return request; + } + + @Test + @DisplayName("getAllEmailConfigs should answer the mailboxes the query found, as the screens read them") + void getAll_shouldAnswerFoundMailboxes() { + List found = List.of(new AuthorityEmail()); + List published = List.of(new AuthEmailResponse()); + when(typedQuery.getResultList()).thenReturn(found); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponse(found)).thenReturn(published); + + assertSame(published, service.getAllEmailConfigs(fullyNarrowedRequest())); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow the query by every detail the caller named") + void getAll_shouldNarrowByEveryNamedDetail() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllEmailConfigs(fullyNarrowedRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(7, captor.getValue().length, "one narrowing per detail the caller named"); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow by nothing when the caller names nothing") + void getAll_shouldNarrowByNothingForEmptyRequest() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllEmailConfigs(new AuthEmailRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(0, captor.getValue().length); + } + + @Test + @DisplayName("saveEmailConfigs should store one mailbox per request and answer each as stored") + void save_shouldStoreEachRequestedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + when(instituteEmailConfigMapper.createRequestToInstituteEmailConf(anyList())) + .thenReturn(List.of(stored, stored)); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponse(stored)) + .thenReturn(new AuthEmailResponse()); + + assertEquals(2, service.saveEmailConfigs(List.of(new CreateAuthEmailRequestModel())).size()); + } + + @Test + @DisplayName("saveEmailConfigs should store nothing when the caller asks for nothing") + void save_shouldStoreNothingForEmptyRequest() { + when(instituteEmailConfigMapper.createRequestToInstituteEmailConf(anyList())) + .thenReturn(new ArrayList<>()); + + assertTrue(service.saveEmailConfigs(new ArrayList<>()).isEmpty()); + } + + @Test + @DisplayName("updateEmailConfigs should answer the mailbox as it stands after the change") + void update_shouldAnswerChangedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + AuthEmailResponse published = new AuthEmailResponse(); + when(instituteEmailConfigMapper.updateRequestToInstituteEmailConf(any(UpdateAuthEmailRequest.class))) + .thenReturn(stored); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponse(stored)).thenReturn(published); + + assertSame(published, service.updateEmailConfigs(new UpdateAuthEmailRequest())); + } +} diff --git a/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterServiceImplTest.java b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterServiceImplTest.java new file mode 100644 index 0000000..e70bfee --- /dev/null +++ b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterServiceImplTest.java @@ -0,0 +1,1195 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.employeemaster; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.employeemaster.EmployeeSignature; +import com.iemr.admin.data.employeemaster.M_Community; +import com.iemr.admin.data.employeemaster.M_Gender; +import com.iemr.admin.data.employeemaster.M_ProviderServiceMap1; +import com.iemr.admin.data.employeemaster.M_Religion; +import com.iemr.admin.data.employeemaster.M_Role; +import com.iemr.admin.data.employeemaster.M_Title; +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_UserDemographics; +import com.iemr.admin.data.employeemaster.M_UserLangMapping; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.employeemaster.M_Userqualification; +import com.iemr.admin.data.employeemaster.Showofficedetails1; +import com.iemr.admin.data.employeemaster.Showuserdetailsfromuserservicerolemapping; +import com.iemr.admin.data.employeemaster.V_Showuser; +import com.iemr.admin.data.employeemaster.V_Userservicerolemapping; +import com.iemr.admin.data.facilitytype.M_facilitytype; +import com.iemr.admin.data.rolemaster.M_UserservicerolemappingForRoleProviderAdmin; +import com.iemr.admin.data.rolemaster.UserRole; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.exceptionhandler.DataNotFound; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepo; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepoo; +import com.iemr.admin.repo.employeemaster.EmployeeSignatureRepo; +import com.iemr.admin.repo.employeemaster.M_CommunityRepo; +import com.iemr.admin.repo.employeemaster.M_GenderRepo; +import com.iemr.admin.repo.employeemaster.M_ProviderServiceMap1Repo; +import com.iemr.admin.repo.employeemaster.M_QualificationRepo; +import com.iemr.admin.repo.employeemaster.M_ReligionRepo; +import com.iemr.admin.repo.employeemaster.M_TitleRepo; +import com.iemr.admin.repo.employeemaster.M_UserDemographicsRepo; +import com.iemr.admin.repo.employeemaster.M_UserLangMappingRepo; +import com.iemr.admin.repo.employeemaster.RoleRepo; +import com.iemr.admin.repo.employeemaster.Showofficedetails1Repo1; +import com.iemr.admin.repo.employeemaster.ShowuserdetailsfromuserservicerolemappingRepo; +import com.iemr.admin.repo.employeemaster.V_ShowuserRepo; +import com.iemr.admin.repo.employeemaster.V_UserservicerolemappingRepo; +import com.iemr.admin.repository.facilitytype.M_facilitytypeRepo; +import com.iemr.admin.repository.rolemaster.M_UserservicerolemappingForRoleProviderAdminRepo; +import com.iemr.admin.repository.store.MainStoreRepo; +import com.iemr.admin.service.user.EncryptUserPassword; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The employee master service is where the rules about who may work where live: + * an ASHA must sit at a sub-centre, a role may not be mapped twice, and taking a + * role away has to take the supervisor mappings with it. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("EmployeeMasterServiceImpl Test Suite") +class EmployeeMasterServiceImplTest { + + private static final Integer USER_ID = 3117; + private static final Integer PSM_ID = 4001; + + @Mock + private RoleRepo roleRepo; + + @Mock + private EmployeeMasterRepo employeeMasterRepo; + + @Mock + private EmployeeMasterRepoo employeeMasterRepo11; + + @Mock + private EmployeeMasterRepoo employeeMasterRepoo; + + @Mock + private M_UserDemographicsRepo m_UserDemographicsRepo; + + @Mock + private M_UserLangMappingRepo m_UserLangMappingRepo; + + @Mock + private M_TitleRepo m_TitleRepo; + + @Mock + private M_GenderRepo m_GenderRepo; + + @Mock + private ShowuserdetailsfromuserservicerolemappingRepo showuserdetailsfromuserservicerolemappingRepo; + + @Mock + private V_ShowuserRepo v_ShowuserRepo; + + @Mock + private V_UserservicerolemappingRepo v_UserservicerolemappingRepo; + + @Mock + private M_QualificationRepo m_QualificationRepo; + + @Mock + private Showofficedetails1Repo1 showofficedetails1Repo1; + + @Mock + private M_ProviderServiceMap1Repo m_ProviderServiceMap1Repo; + + @Mock + private MainStoreRepo mainStoreRepo; + + @Mock + private M_facilitytypeRepo facilityTypeRepo; + + @Mock + private EmployeeSignatureRepo employeeSignatureRepo; + + @Mock + private M_CommunityRepo m_CommunityRepo; + + @Mock + private M_ReligionRepo m_ReligionRepo; + + @Mock + private M_UserservicerolemappingForRoleProviderAdminRepo userservicerolemappingForRoleProviderAdminRepo; + + @Mock + private AshaSupervisorMappingService ashaSupervisorMappingService; + + @Mock + private EncryptUserPassword encryptUserPassword; + + @InjectMocks + private EmployeeMasterServiceImpl service; + + private M_Role role; + + @BeforeEach + void setUp() { + role = new M_Role(); + role.setRoleID(11); + role.setRoleName("Counsellor"); + when(roleRepo.findByRoleID(anyInt())).thenReturn(role); + } + + private static M_UserServiceRoleMapping2 mapping(Integer id, Integer roleId) { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(id); + mapping.setUserID(USER_ID); + mapping.setRoleID(roleId); + mapping.setProviderServiceMapID(PSM_ID); + return mapping; + } + + private void namedRole(String name) { + role.setRoleName(name); + } + + private void activeFacility(Integer facilityId, Integer typeId, Integer levelValue, Integer maxLevel) { + M_Facility facility = new M_Facility(); + facility.setFacilityID(facilityId); + facility.setFacilityTypeID(typeId); + when(mainStoreRepo.findByFacilityIDAndDeleted(facilityId, false)).thenReturn(facility); + M_facilitytype type = new M_facilitytype(); + type.setFacilityTypeID(typeId); + type.setLevelValue(levelValue); + when(facilityTypeRepo.findByFacilityTypeID(typeId)).thenReturn(type); + when(facilityTypeRepo.findMaxLevelValueByProviderServiceMapID(PSM_ID)).thenReturn(maxLevel); + } + + @Nested + @DisplayName("mapRole") + class MapRoleTests { + + @Test + @DisplayName("should refuse an ASHA mapping that names no facility") + void mapRole_shouldRefuseAshaWithoutFacility() { + namedRole("ASHA"); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(mapping(null, 11)), "auth")); + + assertTrue(thrown.getMessage().contains("Facility (SC) is mandatory for ASHA role"), + thrown.getMessage()); + verify(employeeMasterRepo, never()).saveAll(anyList()); + } + + @Test + @DisplayName("should refuse a mapping onto a facility that has been retired") + void mapRole_shouldRefuseRetiredFacility() { + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(501); + when(mainStoreRepo.findByFacilityIDAndDeleted(501, false)).thenReturn(null); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(toMap), "auth")); + + assertTrue(thrown.getMessage().contains("is no longer active"), thrown.getMessage()); + } + + @Test + @DisplayName("should refuse an ASHA mapped above sub-centre level") + void mapRole_shouldRefuseAshaAboveSubCentre() { + namedRole("ASHA"); + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(501); + activeFacility(501, 3, 2, 4); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(toMap), "auth")); + + assertTrue(thrown.getMessage().contains("Sub-Centre (SC) level facility"), thrown.getMessage()); + } + + @Test + @DisplayName("should refuse a second active mapping for the same user, role and service line") + void mapRole_shouldRefuseDuplicateMapping() { + when(employeeMasterRepo.existsByUserIDAndRoleIDAndProviderServiceMapIDAndDeletedFalse(USER_ID, 11, PSM_ID)) + .thenReturn(true); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(mapping(null, 11)), "auth")); + + assertTrue(thrown.getMessage().contains("Duplicate mapping is not allowed"), thrown.getMessage()); + } + + @Test + @DisplayName("should refuse a second mapping for an ASHA supervisor at the same facility") + void mapRole_shouldRefuseDuplicateSupervisorMappingAtSameFacility() { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(501); + activeFacility(501, 3, 4, 4); + when(employeeMasterRepo + .existsByUserIDAndRoleIDAndProviderServiceMapIDAndFacilityIDAndDeletedFalse( + USER_ID, 11, PSM_ID, 501)) + .thenReturn(true); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(toMap), "auth")); + + assertTrue(thrown.getMessage().contains("active work location mapping for this facility"), + thrown.getMessage()); + } + + @Test + @DisplayName("should let an ASHA supervisor hold a second mapping at a different facility") + void mapRole_shouldAllowSupervisorAtDifferentFacility() throws Exception { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(502); + activeFacility(502, 3, 4, 4); + ArrayList stored = new ArrayList<>(List.of(toMap)); + when(employeeMasterRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.mapRole(List.of(toMap), "auth")); + } + + @Test + @DisplayName("should flatten the village lists onto the stored columns") + void mapRole_shouldFlattenVillageLists() throws Exception { + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setVillageID(new String[] { "501", "502" }); + toMap.setVillageName(new String[] { "Hosur", "Devanahalli" }); + when(employeeMasterRepo.saveAll(anyList())) + .thenReturn(new ArrayList<>(List.of(toMap))); + + service.mapRole(List.of(toMap), "auth"); + + assertEquals("501,502", toMap.getVillageidDb()); + assertEquals("Hosur,Devanahalli", toMap.getVillageNameDb()); + verify(employeeMasterRepo).save(toMap); + } + + @Test + @DisplayName("should leave the village columns alone when the mapping names no villages") + void mapRole_shouldLeaveVillageColumnsAlone() throws Exception { + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + when(employeeMasterRepo.saveAll(anyList())).thenReturn(new ArrayList<>(List.of(toMap))); + + service.mapRole(List.of(toMap), "auth"); + + assertNull(toMap.getVillageidDb()); + verify(employeeMasterRepo, never()).save(any()); + } + } + + @Nested + @DisplayName("saveRoleMappingeditedData") + class SaveRoleMappingTests { + + @Test + @DisplayName("should refuse an ASHA edit that drops the facility") + void saveRoleMapping_shouldRefuseAshaWithoutFacility() { + namedRole("ASHA"); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.saveRoleMappingeditedData(mapping(9001, 11), "auth")); + + assertTrue(thrown.getMessage().contains("Facility (SC) is mandatory for ASHA role"), + thrown.getMessage()); + } + + @Test + @DisplayName("should skip the facility rules when the mapping is only being deactivated") + void saveRoleMapping_shouldSkipRulesWhenDeactivating() throws Exception { + namedRole("ASHA"); + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setDeleted(Boolean.TRUE); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + assertSame(toSave, service.saveRoleMappingeditedData(toSave, "auth")); + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should clear the saved villages when the facility is above sub-centre level") + void saveRoleMapping_shouldClearVillagesAboveSubCentre() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setFacilityID(501); + toSave.setVillageidDb("501,502"); + toSave.setVillageNameDb("Hosur,Devanahalli"); + activeFacility(501, 3, 2, 4); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.empty()); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + assertNull(toSave.getVillageidDb(), "a non sub-centre posting keeps no village list"); + assertNull(toSave.getVillageNameDb()); + } + + @Test + @DisplayName("should cascade the supervisor mappings when the role changes") + void saveRoleMapping_shouldCascadeWhenRoleChanges() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 12); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should cascade only this facility when a supervisor still works elsewhere") + void saveRoleMapping_shouldCascadeOnlyThisFacilityForBusySupervisor() throws Exception { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setDeleted(Boolean.TRUE); + toSave.setFacilityID(501); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + old.setFacilityID(501); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.countByUserIDAndRoleIDAndDeletedFalse(USER_ID, 11)).thenReturn(2L); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + verify(ashaSupervisorMappingService).cascadeDeleteByFacilityID(501, "Admin"); + verify(ashaSupervisorMappingService, never()).cascadeDeleteByUserID(anyInt(), anyString()); + } + + @Test + @DisplayName("should cascade every mapping when the facility itself changes") + void saveRoleMapping_shouldCascadeWhenFacilityChanges() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setFacilityID(502); + activeFacility(502, 3, 4, 4); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + old.setFacilityID(501); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should flatten the village lists onto the stored columns") + void saveRoleMapping_shouldFlattenVillageLists() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setVillageID(new String[] { "501", "502" }); + toSave.setVillageName(new String[] { "Hosur", "Devanahalli" }); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.empty()); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + assertEquals("501,502", toSave.getVillageidDb()); + assertEquals("Hosur,Devanahalli", toSave.getVillageNameDb()); + } + } + + @Nested + @DisplayName("cascadeDeleteAshaMappingsForDeactivation") + class CascadeDeactivationTests { + + @Test + @DisplayName("should retire only this facility when the supervisor still works elsewhere") + void cascade_shouldRetireOnlyThisFacility() { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 usrRole = mapping(9001, 11); + usrRole.setFacilityID(501); + when(employeeMasterRepo.countByUserIDAndRoleIDAndDeletedFalse(USER_ID, 11)).thenReturn(2L); + + service.cascadeDeleteAshaMappingsForDeactivation(usrRole); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserIDAndFacilityID(USER_ID, 501, "Admin"); + verify(ashaSupervisorMappingService, never()).cascadeDeleteByUserID(anyInt(), anyString()); + } + + @Test + @DisplayName("should retire every mapping when this was the supervisor's last facility") + void cascade_shouldRetireEveryMappingOnLastFacility() { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 usrRole = mapping(9001, 11); + usrRole.setFacilityID(501); + when(employeeMasterRepo.countByUserIDAndRoleIDAndDeletedFalse(USER_ID, 11)).thenReturn(1L); + + service.cascadeDeleteAshaMappingsForDeactivation(usrRole); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should retire every mapping for a role that is not a supervisor") + void cascade_shouldRetireEveryMappingForNonSupervisor() { + service.cascadeDeleteAshaMappingsForDeactivation(mapping(9001, 11)); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("cascadeDeleteAshaMappingsForUser should hand the user straight to the mapping service") + void cascadeForUser_shouldDelegate() { + service.cascadeDeleteAshaMappingsForUser(USER_ID); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + } + + @Nested + @DisplayName("Employee lookups") + class LookupTests { + + @Test + @DisplayName("getAllRole should rebuild each role from what the repository holds") + void getAllRole_shouldRebuildRoles() { + M_Role stored = new M_Role(); + stored.setRoleID(11); + stored.setRoleName("Counsellor"); + stored.setProviderServiceMapID(PSM_ID); + when(roleRepo.getAllRole()).thenReturn(new ArrayList<>(List.of(stored))); + + ArrayList roles = service.getAllRole(); + + assertEquals(1, roles.size()); + assertEquals("Counsellor", roles.get(0).getRoleName()); + assertEquals(PSM_ID, roles.get(0).getProviderServiceMapID()); + } + + @Test + @DisplayName("getEmployeeDetails should skip a row the query could not fill") + void getEmployeeDetails_shouldSkipUnfillableRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 9001, USER_ID, 11, PSM_ID, "a", "b", "c", "d", 1, "e", 2, "f", "g" }); + when(employeeMasterRepo.getEmployeeDetails()).thenReturn(rows); + + assertEquals(1, service.getEmployeeDetails().size()); + } + + @Test + @DisplayName("getAllTitle should rebuild each title from what the repository holds") + void getAllTitle_shouldRebuildTitles() { + M_Title stored = new M_Title(); + stored.setTitleID(1); + stored.setTitleName("Dr"); + when(m_TitleRepo.getAllTitle()).thenReturn(new ArrayList<>(List.of(stored))); + + assertEquals("Dr", service.getAllTitle().get(0).getTitleName()); + } + + @Test + @DisplayName("getAllGender should rebuild each gender from what the repository holds") + void getAllGender_shouldRebuildGenders() { + M_Gender stored = new M_Gender(); + stored.setGenderID(1); + stored.setGenderName("Female"); + when(m_GenderRepo.getAllGender()).thenReturn(new ArrayList<>(List.of(stored))); + + assertEquals("Female", service.getAllGender().get(0).getGenderName()); + } + + @Test + @DisplayName("FindEmployeeName should distinguish a taken user name from a free one") + void findEmployeeName_shouldDistinguishTakenFromFree() { + when(employeeMasterRepoo.findEmployeeByName("asha.rao")).thenReturn(new M_User1()); + when(employeeMasterRepoo.findEmployeeByName("new.user")).thenReturn(null); + + assertEquals("userexist", service.FindEmployeeName("asha.rao")); + assertEquals("usernotexist", service.FindEmployeeName("new.user")); + } + + @Test + @DisplayName("FindEmployeeContact should distinguish a taken number from a free one") + void findEmployeeContact_shouldDistinguishTakenFromFree() { + when(employeeMasterRepoo.findEmployeeByContact("9000000001")).thenReturn(new M_User1()); + when(employeeMasterRepoo.findEmployeeByContact("9000000002")).thenReturn(null); + + assertEquals("contactexist", service.FindEmployeeContact("9000000001")); + assertEquals("contactnotexist", service.FindEmployeeContact("9000000002")); + } + + @Test + @DisplayName("FindEmployeeAadhaar should distinguish a taken number from a free one") + void findEmployeeAadhaar_shouldDistinguishTakenFromFree() { + when(employeeMasterRepoo.findEmployeeAadhaarNo("111122223333")).thenReturn(new M_User1()); + when(employeeMasterRepoo.findEmployeeAadhaarNo("444455556666")).thenReturn(null); + + assertEquals("aadhaarexist", service.FindEmployeeAadhaar("111122223333")); + assertEquals("aadhaarnotexist", service.FindEmployeeAadhaar("444455556666")); + } + + @Test + @DisplayName("FindEmployeeName1 should answer the user record itself") + void findEmployeeName1_shouldAnswerTheRecord() { + M_User1 stored = new M_User1(); + when(employeeMasterRepoo.findEmployeeByName("asha.rao")).thenReturn(stored); + + assertSame(stored, service.FindEmployeeName1("asha.rao")); + } + + @Test + @DisplayName("checkingEmpDetails should report whether the identifiers are already in use") + void checkingEmpDetails_shouldReportWhetherIdentifiersAreTaken() { + when(employeeMasterRepoo.checkingEmpDetails("asha.rao", "1", "2", "3", "4")).thenReturn(new M_User1()); + when(employeeMasterRepoo.checkingEmpDetails("new.user", "1", "2", "3", "4")).thenReturn(null); + + assertTrue(service.checkingEmpDetails("asha.rao", "1", "2", "3", "4")); + assertFalse(service.checkingEmpDetails("new.user", "1", "2", "3", "4")); + } + + @Test + @DisplayName("getQualification should hand back what the repository holds") + void getQualification_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_Userqualification())); + when(m_QualificationRepo.getAllQualification()).thenReturn(stored); + + assertSame(stored, service.getQualification()); + } + + @Test + @DisplayName("getlocationByMapid2 should hand back what the repository holds") + void getlocationByMapid2_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new Showofficedetails1())); + when(showofficedetails1Repo1.getlocationByMapid(PSM_ID, 301)).thenReturn(stored); + + assertSame(stored, service.getlocationByMapid2(PSM_ID, 301)); + } + + @Test + @DisplayName("getAllByMapId2 should hand back what the repository holds") + void getAllByMapId2_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_ProviderServiceMap1())); + when(m_ProviderServiceMap1Repo.getAllByMapId2(77, 29, 3)).thenReturn(stored); + + assertSame(stored, service.getAllByMapId2(77, 29, 3)); + } + + @Test + @DisplayName("the narrowing searches should each reach their own repository query") + void narrowingSearches_shouldReachTheirOwnQuery() { + ArrayList stored = + new ArrayList<>(List.of(new Showuserdetailsfromuserservicerolemapping())); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails2(77, 29)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails3(77, 11)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails4(77, 3)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails6(77, USER_ID)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails7(77, 29, 301)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails8(77, 29, 301, 401)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails9(77, 29, 11)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails10(77, 29, 11, 3, "asha.rao", USER_ID)) + .thenReturn(stored); + + assertSame(stored, service.getEmployeeDetails2(77, 29)); + assertSame(stored, service.getEmployeeDetails3(77, 11)); + assertSame(stored, service.getEmployeeDetails4(77, 3)); + assertSame(stored, service.getEmployeeDetails6(77, USER_ID)); + assertSame(stored, service.getEmployeeDetails7(77, 29, 301)); + assertSame(stored, service.getEmployeeDetails8(77, 29, 301, 401)); + assertSame(stored, service.getEmployeeDetails9(77, 29, 11)); + assertSame(stored, service.getEmployeeDetails10(77, 29, 11, 3, "asha.rao", USER_ID)); + } + + @Test + @DisplayName("getEmployeeDetails5 should hand back what the view holds") + void getEmployeeDetails5_shouldHandBackViewContents() { + ArrayList stored = new ArrayList<>(List.of(new V_Showuser())); + when(v_ShowuserRepo.EmployeeDetails5()).thenReturn(stored); + + assertSame(stored, service.getEmployeeDetails5()); + } + + @Test + @DisplayName("getcompleteUserDetails should hand back what the view holds") + void getcompleteUserDetails_shouldHandBackViewContents() { + ArrayList stored = new ArrayList<>(List.of(new V_Showuser())); + when(v_ShowuserRepo.getAdminDetails()).thenReturn(stored); + + assertSame(stored, service.getcompleteUserDetails()); + } + + @Test + @DisplayName("getAllReligion and getAllCommunity should hand back what the repositories hold") + void masters_shouldHandBackRepositoryContents() { + ArrayList religions = new ArrayList<>(List.of(new M_Religion())); + ArrayList communities = new ArrayList<>(List.of(new M_Community())); + when(m_ReligionRepo.findAll()).thenReturn(religions); + when(m_CommunityRepo.findAll()).thenReturn(communities); + + assertEquals(1, service.getAllReligion().size()); + assertEquals(1, service.getAllCommunity().size()); + } + } + + @Nested + @DisplayName("getEmployeeDetails4 by provider") + class EmployeeDetails4Tests { + + @Test + @DisplayName("should mark a user locked out after failed sign-ins") + void getEmployeeDetails4_shouldMarkLockedOutUser() { + V_Showuser user = new V_Showuser(); + user.setUserID(USER_ID); + M_User1 record = new M_User1(); + record.setUserID(USER_ID); + record.setFailedAttempt(3); + record.setDeleted(Boolean.TRUE); + record.setLockTimestamp(Timestamp.valueOf("2026-02-17 09:30:00")); + when(v_ShowuserRepo.EmployeeDetails4(77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeMasterRepoo.findByUserIDIn(anyList())).thenReturn(new ArrayList<>(List.of(record))); + + V_Showuser enriched = service.getEmployeeDetails4(77).get(0); + + assertEquals(3, enriched.getFailedAttempt()); + assertTrue(enriched.getLockedDueToFailedAttempts()); + } + + @Test + @DisplayName("should not mark a user locked out while their account is still active") + void getEmployeeDetails4_shouldNotMarkActiveUserLockedOut() { + V_Showuser user = new V_Showuser(); + user.setUserID(USER_ID); + M_User1 record = new M_User1(); + record.setUserID(USER_ID); + record.setDeleted(Boolean.FALSE); + record.setLockTimestamp(Timestamp.valueOf("2026-02-17 09:30:00")); + when(v_ShowuserRepo.EmployeeDetails4(77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeMasterRepoo.findByUserIDIn(anyList())).thenReturn(new ArrayList<>(List.of(record))); + + V_Showuser enriched = service.getEmployeeDetails4(77).get(0); + + assertEquals(0, enriched.getFailedAttempt()); + assertFalse(enriched.getLockedDueToFailedAttempts()); + } + + @Test + @DisplayName("should answer an empty result without asking for user records") + void getEmployeeDetails4_shouldAnswerEmptyWithoutFurtherLookups() { + when(v_ShowuserRepo.EmployeeDetails4(77)).thenReturn(new ArrayList<>()); + + assertTrue(service.getEmployeeDetails4(77).isEmpty()); + verify(employeeMasterRepoo, never()).findByUserIDIn(anyList()); + } + } + + @Nested + @DisplayName("Persistence") + class PersistenceTests { + + @Test + @DisplayName("saveEmployee should answer the id of the stored user and encrypt its credentials") + void saveEmployee_shouldStoreAndEncrypt() { + M_User1 toSave = new M_User1(); + M_User1 stored = new M_User1(); + stored.setUserID(USER_ID); + when(employeeMasterRepo11.save(toSave)).thenReturn(stored); + when(encryptUserPassword.encryptUserCredentials(stored)).thenReturn(new OutputResponse()); + + assertEquals(USER_ID, service.saveEmployee(toSave)); + verify(encryptUserPassword).encryptUserCredentials(stored); + } + + @Test + @DisplayName("saveEditData should re-encrypt the credentials it saved") + void saveEditData_shouldReEncryptCredentials() { + M_User1 toSave = new M_User1(); + when(employeeMasterRepo11.save(toSave)).thenReturn(toSave); + + assertSame(toSave, service.saveEditData(toSave)); + verify(encryptUserPassword).encryptUserCredentials(toSave); + } + + @Test + @DisplayName("saveDemography should answer the id of the stored demographics") + void saveDemography_shouldAnswerStoredId() { + M_UserDemographics stored = new M_UserDemographics(); + stored.setDemographicID(5001); + when(m_UserDemographicsRepo.save(any())).thenReturn(stored); + + assertEquals(5001, service.saveDemography(new M_UserDemographics())); + assertEquals(5001, service.saveeditDemo(new M_UserDemographics())); + } + + @Test + @DisplayName("saveeditlangdata should answer the id of the stored language mapping") + void saveeditlangdata_shouldAnswerStoredId() { + M_UserLangMapping stored = new M_UserLangMapping(); + stored.setUserLangID(7001); + when(m_UserLangMappingRepo.save(any())).thenReturn(stored); + + assertEquals(7001, service.saveeditlangdata(new M_UserLangMapping())); + } + + @Test + @DisplayName("mapLanguage and mapRoleUpdation should hand their batches to the repositories") + void batches_shouldReachTheRepositories() { + ArrayList languages = new ArrayList<>(); + ArrayList roles = new ArrayList<>(); + when(m_UserLangMappingRepo.saveAll(anyList())).thenReturn(languages); + when(employeeMasterRepo.saveAll(anyList())).thenReturn(roles); + + assertSame(languages, service.mapLanguage(new ArrayList<>())); + assertSame(roles, service.mapRoleUpdation(new ArrayList<>())); + } + + @Test + @DisplayName("saveeditedData should clear the failed sign-in count for a reinstated user") + void saveeditedData_shouldClearFailedAttemptsOnReinstatement() { + M_User1 toSave = new M_User1(); + toSave.setDeleted(Boolean.FALSE); + toSave.setFailedAttempt(3); + when(employeeMasterRepoo.save(toSave)).thenReturn(toSave); + + service.saveeditedData(toSave); + + assertEquals(0, toSave.getFailedAttempt()); + } + + @Test + @DisplayName("saveeditedData should leave the failed sign-in count alone for a deactivated user") + void saveeditedData_shouldLeaveFailedAttemptsAloneOnDeactivation() { + M_User1 toSave = new M_User1(); + toSave.setDeleted(Boolean.TRUE); + toSave.setFailedAttempt(3); + when(employeeMasterRepoo.save(toSave)).thenReturn(toSave); + + service.saveeditedData(toSave); + + assertEquals(3, toSave.getFailedAttempt()); + } + + @Test + @DisplayName("createProviderAdmin should hash the password before it is stored") + void createProviderAdmin_shouldHashPassword() throws Exception { + M_User1 toCreate = new M_User1(); + toCreate.setPassword("plain-secret"); + ArrayList stored = new ArrayList<>(List.of(toCreate)); + when(employeeMasterRepoo.saveAll(anyList())).thenReturn(stored); + + service.createProviderAdmin(List.of(toCreate)); + + assertTrue(toCreate.getPassword().startsWith("1001:"), + "the stored password must be the salted hash, not the plain text"); + } + + @Test + @DisplayName("createProviderAdmin should refuse an admin with no password") + void createProviderAdmin_shouldRefuseWithoutPassword() { + assertThrows(Exception.class, () -> service.createProviderAdmin(List.of(new M_User1()))); + } + + @Test + @DisplayName("createNewUser should hash the password before it is stored") + void createNewUser_shouldHashPassword() throws Exception { + M_User1 toCreate = new M_User1(); + toCreate.setPassword("plain-secret"); + when(employeeMasterRepoo.saveAll(anyList())).thenReturn(new ArrayList<>(List.of(toCreate))); + + service.createNewUser(List.of(toCreate)); + + assertFalse("plain-secret".equals(toCreate.getPassword())); + } + + @Test + @DisplayName("createNewUser should refuse a user with no password") + void createNewUser_shouldRefuseWithoutPassword() { + assertThrows(Exception.class, () -> service.createNewUser(List.of(new M_User1()))); + } + + @Test + @DisplayName("generateStrongPassword should answer a different hash each time it is called") + void generateStrongPassword_shouldSaltEachHash() throws Exception { + String first = service.generateStrongPassword("plain-secret"); + String second = service.generateStrongPassword("plain-secret"); + + assertNotNull(first); + assertFalse(first.equals(second), "each hash must carry its own salt"); + } + + @Test + @DisplayName("saveBulkUserEmployee should answer the record the repository stored") + void saveBulkUserEmployee_shouldAnswerStoredRecord() { + M_User1 stored = new M_User1(); + stored.setUserID(USER_ID); + when(employeeMasterRepo11.save(any())).thenReturn(stored); + + assertSame(stored, service.saveBulkUserEmployee(new M_User1())); + } + + @Test + @DisplayName("the single-record lookups should each reach their own repository query") + void singleRecordLookups_shouldReachTheirOwnQuery() { + M_User1 user = new M_User1(); + M_UserDemographics demographics = new M_UserDemographics(); + M_UserLangMapping language = new M_UserLangMapping(); + M_UserServiceRoleMapping2 roleMapping = mapping(9001, 11); + when(employeeMasterRepo11.editEmployee(USER_ID)).thenReturn(user); + when(employeeMasterRepoo.findByUserID(USER_ID)).thenReturn(user); + when(m_UserDemographicsRepo.mdedit(USER_ID)).thenReturn(demographics); + when(m_UserDemographicsRepo.findByUserID(USER_ID)).thenReturn(demographics); + when(m_UserDemographicsRepo.save(demographics)).thenReturn(demographics); + when(m_UserLangMappingRepo.ulangmapedit(USER_ID, 1)).thenReturn(language); + when(m_UserLangMappingRepo.findByUserLangID(7001)).thenReturn(language); + when(m_UserLangMappingRepo.save(language)).thenReturn(language); + when(employeeMasterRepo.uRoleMedit(USER_ID, 11)).thenReturn(roleMapping); + when(employeeMasterRepo.uRoledelte(9001)).thenReturn(roleMapping); + when(employeeMasterRepo.findByUSRMappingID(9001)).thenReturn(roleMapping); + when(employeeMasterRepo.save(roleMapping)).thenReturn(roleMapping); + + assertSame(user, service.editEmployee(USER_ID)); + assertSame(user, service.editData(USER_ID)); + assertSame(user, service.getProviderAdminForEdit(USER_ID)); + assertSame(demographics, service.mdedit(USER_ID)); + assertSame(demographics, service.DataByUserID(USER_ID)); + assertSame(demographics, service.saveeditedDemoData(demographics)); + assertSame(language, service.ulangmapedit(USER_ID, 1)); + assertSame(language, service.updateLangMapping(7001)); + assertSame(language, service.saveUserLangEditedData(language)); + assertSame(roleMapping, service.uRoleMedit(USER_ID, 11)); + assertSame(roleMapping, service.uRoledelte(9001)); + assertSame(roleMapping, service.getDataUsrId(9001)); + assertSame(roleMapping, service.saveRoleEdit(roleMapping)); + } + + @Test + @DisplayName("SaveDemographics should hand its batch to the repository") + void saveDemographics_shouldHandBatchToRepository() { + ArrayList stored = new ArrayList<>(); + when(m_UserDemographicsRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.SaveDemographics(new ArrayList<>())); + } + + @Test + @DisplayName("getProviderAdmin should hand back what the repository holds") + void getProviderAdmin_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_User1())); + when(employeeMasterRepoo.getAllProviderAdminData()).thenReturn(stored); + + assertSame(stored, service.getProviderAdmin()); + } + } + + @Nested + @DisplayName("ResetPassword") + class ResetPasswordTests { + + @Test + @DisplayName("should report success when the credential service accepts the new password") + void resetPassword_shouldReportSuccess() { + OutputResponse accepted = new OutputResponse(); + accepted.setResponse("done"); + when(encryptUserPassword.encryptUserCredentials(any())).thenReturn(accepted); + + assertEquals("Password reset successfully", service.ResetPassword(new M_User1())); + } + + @Test + @DisplayName("should report failure when the credential service refuses the new password") + void resetPassword_shouldReportFailure() { + when(encryptUserPassword.encryptUserCredentials(any())).thenReturn(new OutputResponse()); + + assertEquals("Password Not Set Properly", service.ResetPassword(new M_User1())); + } + } + + @Nested + @DisplayName("getMappedRole") + class GetMappedRoleTests { + + @Test + @DisplayName("should split the stored village columns back into lists") + void getMappedRole_shouldSplitVillageColumns() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setServiceID(3); + mapping.setStateID(29); + mapping.setWorkingDistrictID("301"); + mapping.setVillageidDb("501,502"); + mapping.setVillageNameDb("Hosur,Devanahalli"); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())).thenReturn(new ArrayList<>()); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertArrayEquals(new String[] { "501", "502" }, answered.getVillageID()); + assertArrayEquals(new String[] { "Hosur", "Devanahalli" }, answered.getVillageName()); + } + + @Test + @DisplayName("should clear the block and village details for a mapping with no service line") + void getMappedRole_shouldClearBlockAndVillageWithoutService() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setStateID(29); + mapping.setWorkingDistrictID("301"); + mapping.setVillageidDb("501"); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())).thenReturn(new ArrayList<>()); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertNull(answered.getVillageID()); + assertNull(answered.getVillageidDb()); + assertNull(answered.getBlockID()); + } + + @Test + @DisplayName("should fill in the state and district the view could not resolve") + void getMappedRole_shouldFillInMissingStateAndDistrict() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setServiceID(3); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getDirectStateDistrictByMappingIDs(anyList())) + .thenReturn(List.of(new Object[] { 9001, 29, "Karnataka", 301, "Bengaluru Urban", 401, "North" })); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())).thenReturn(new ArrayList<>()); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertEquals(29, answered.getStateID()); + assertEquals("Karnataka", answered.getStateName()); + assertEquals("301", answered.getWorkingDistrictID()); + assertEquals("North", answered.getBlockName()); + } + + @Test + @DisplayName("should attach the facility details the batch lookup resolves") + void getMappedRole_shouldAttachFacilityDetails() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setServiceID(3); + mapping.setStateID(29); + mapping.setWorkingDistrictID("301"); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())) + .thenReturn(List.of(new Object[] { 9001, 501, "PHC North", 3, "Rural" })); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertEquals(501, answered.getFacilityID()); + assertEquals("PHC North", answered.getFacilityName()); + assertEquals("Rural", answered.getRuralUrban()); + } + + @Test + @DisplayName("should answer an empty list when the view holds nothing") + void getMappedRole_shouldAnswerEmptyListForEmptyView() { + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)).thenReturn(null); + + assertTrue(service.getMappedRole(77).isEmpty()); + } + + @Test + @DisplayName("should search by user id when the caller sends no name") + void getMappedRole_shouldSearchByUserIdWithoutAName() { + ArrayList stored = new ArrayList<>(); + when(v_UserservicerolemappingRepo.getDataByUserID(USER_ID)).thenReturn(stored); + + assertSame(stored, service.getMappedRole("", USER_ID)); + } + + @Test + @DisplayName("should search by name when the caller sends no usable user id") + void getMappedRole_shouldSearchByNameWithoutAUserId() { + ArrayList stored = new ArrayList<>(); + when(v_UserservicerolemappingRepo.getDataByName("Asha Rao")).thenReturn(stored); + + assertSame(stored, service.getMappedRole("Asha Rao", 0)); + } + + @Test + @DisplayName("should refuse a search that names both a user and an id") + void getMappedRole_shouldRefuseAmbiguousSearch() { + assertThrows(DataNotFound.class, () -> service.getMappedRole("Asha Rao", USER_ID)); + } + } + + @Nested + @DisplayName("searchMappedLangugeByUserId") + class SearchMappedLanguageTests { + + @Test + @DisplayName("should answer the languages mapped to a real user") + void searchMappedLanguage_shouldAnswerMappedLanguages() { + ArrayList stored = new ArrayList<>(); + when(m_UserLangMappingRepo.getmappedlanguageData(USER_ID)).thenReturn(stored); + + assertSame(stored, service.searchMappedLangugeByUserId(USER_ID)); + } + + @Test + @DisplayName("should refuse a search that names no user") + void searchMappedLanguage_shouldRefuseSearchWithoutUser() { + assertThrows(DataNotFound.class, () -> service.searchMappedLangugeByUserId(0)); + } + } + + @Nested + @DisplayName("getMappedLanguge") + class GetMappedLanguageTests { + + @Test + @DisplayName("should rebuild one mapping per row the query answers") + void getMappedLanguge_shouldRebuildEachRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 7001, USER_ID, 1, 5, "Kannada", "asha.rao", true, true, true, "n", false, + 5, 5, 5, false }); + when(m_UserLangMappingRepo.getMappedLanguge(77)).thenReturn(rows); + + assertEquals(1, service.getMappedLanguge(77).size()); + } + } + + @Nested + @DisplayName("getEmployeeByDesiganationID") + class EmployeeByDesignationTests { + + @Test + @DisplayName("should mark a user whose signature is on file as active") + void byDesignation_shouldMarkActiveSignature() { + M_User1 user = new M_User1(); + user.setUserID(USER_ID); + EmployeeSignature signature = new EmployeeSignature(); + signature.setDeleted(Boolean.FALSE); + when(employeeMasterRepoo.getempByDesiganation(7, 77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(signature); + + assertEquals("Active", service.getEmployeeByDesiganationID(7, 77).get(0).getSignatureStatus()); + } + + @Test + @DisplayName("should mark a user whose signature has been retired as inactive") + void byDesignation_shouldMarkRetiredSignature() { + M_User1 user = new M_User1(); + user.setUserID(USER_ID); + EmployeeSignature signature = new EmployeeSignature(); + signature.setDeleted(Boolean.TRUE); + when(employeeMasterRepoo.getempByDesiganation(7, 77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(signature); + + assertEquals("InActive", service.getEmployeeByDesiganationID(7, 77).get(0).getSignatureStatus()); + } + + @Test + @DisplayName("should leave the signature status unset for a user with none on file") + void byDesignation_shouldLeaveStatusUnsetWithoutSignature() { + M_User1 user = new M_User1(); + user.setUserID(USER_ID); + when(employeeMasterRepoo.getempByDesiganation(7, 77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(null); + + assertNull(service.getEmployeeByDesiganationID(7, 77).get(0).getSignatureStatus()); + } + } + + @Nested + @DisplayName("getUserRoleTM") + class UserRoleTmTests { + + @Test + @DisplayName("should rebuild one role per row the query answers") + void getUserRoleTM_shouldRebuildEachRow() { + M_UserservicerolemappingForRoleProviderAdmin request = + new M_UserservicerolemappingForRoleProviderAdmin(); + request.setUserID(USER_ID); + request.setProviderServiceMapID(PSM_ID); + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { USER_ID, 11, "TC Specialist", false, 21, "Specialist screen", false }); + when(userservicerolemappingForRoleProviderAdminRepo.getroleofuserTM(USER_ID, PSM_ID)).thenReturn(rows); + + ArrayList roles = service.getUserRoleTM(request); + + assertEquals(1, roles.size()); + assertEquals("TC Specialist", roles.get(0).getRolename()); + } + + @Test + @DisplayName("should answer an empty list when the user holds no telemedicine role") + void getUserRoleTM_shouldAnswerEmptyListWithoutRoles() { + M_UserservicerolemappingForRoleProviderAdmin request = + new M_UserservicerolemappingForRoleProviderAdmin(); + when(userservicerolemappingForRoleProviderAdminRepo.getroleofuserTM(any(), any())) + .thenReturn(new ArrayList<>()); + + assertTrue(service.getUserRoleTM(request).isEmpty()); + } + } + + @Nested + @DisplayName("createAgent") + class CreateAgentTests { + + @Test + @DisplayName("should fill the agent and server placeholders into the configured URL") + void createAgent_shouldFillPlaceholders() { + com.iemr.admin.utils.config.ConfigProperties properties = + new com.iemr.admin.utils.config.ConfigProperties(); + service.setConfigProperties(properties); + + String url = service.createAgent("A-1", "asha.rao"); + + assertNotNull(url); + assertFalse(url.contains("AGENTID"), url); + } + } +} diff --git a/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterSupportServicesTest.java b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterSupportServicesTest.java new file mode 100644 index 0000000..230e875 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterSupportServicesTest.java @@ -0,0 +1,556 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.employeemaster; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.employeemaster.AshaSupervisorMapping; +import com.iemr.admin.data.employeemaster.EmployeeSignature; +import com.iemr.admin.data.employeemaster.M_Designation; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.employeemaster.USRAgentMapping; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepo; +import com.iemr.admin.repo.employeemaster.EmployeeSignatureRepo; +import com.iemr.admin.repo.employeemaster.M_DesignationRepo; +import com.iemr.admin.repo.employeemaster.USRAgentMappingRepository; +import com.iemr.admin.repository.store.MainStoreRepo; +import com.iemr.admin.repository.user.AshaSupervisorMappingRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The supporting employee services: the supervisor mapping store, the signature + * store, the CTI agent-id pool and the designation master. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Employee master support service Test Suite") +class EmployeeMasterSupportServicesTest { + + private static final Integer SUPERVISOR_ID = 3117; + private static final Integer ASHA_ID = 4001; + private static final Integer FACILITY_ID = 501; + + @Mock + private AshaSupervisorMappingRepo ashaSupervisorMappingRepo; + + @Mock + private EmployeeMasterRepo employeeMasterRepo; + + @Mock + private MainStoreRepo mainStoreRepo; + + @InjectMocks + private AshaSupervisorMappingServiceImpl ashaService; + + @Mock + private EmployeeSignatureRepo employeeSignatureRepo; + + @InjectMocks + private EmployeeSignatureServiceImpl signatureService; + + @Mock + private USRAgentMappingRepository usrAgentMappingRepository; + + @Mock + private M_DesignationRepo m_DesignationRepo; + + @InjectMocks + private M_DesignationImpl designationService; + + private static AshaSupervisorMapping mapping(Long id, Integer supervisorId, Integer ashaId) { + AshaSupervisorMapping mapping = new AshaSupervisorMapping(); + mapping.setId(id); + mapping.setSupervisorUserID(supervisorId); + mapping.setAshaUserID(ashaId); + mapping.setFacilityID(FACILITY_ID); + mapping.setCreatedBy("admin"); + return mapping; + } + + private void activeFacility() { + M_Facility facility = new M_Facility(); + facility.setFacilityID(FACILITY_ID); + when(mainStoreRepo.findByFacilityIDAndDeleted(FACILITY_ID, false)).thenReturn(facility); + } + + @Nested + @DisplayName("AshaSupervisorMappingServiceImpl") + class AshaSupervisorMappingTests { + + @Test + @DisplayName("saveAshaSupervisorMappings should refuse a mapping onto a retired facility") + void save_shouldRefuseRetiredFacility() { + when(mainStoreRepo.findByFacilityIDAndDeleted(FACILITY_ID, false)).thenReturn(null); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> ashaService.saveAshaSupervisorMappings(List.of(mapping(null, SUPERVISOR_ID, ASHA_ID)))); + + assertTrue(thrown.getMessage().contains("is no longer active"), thrown.getMessage()); + } + + @Test + @DisplayName("saveAshaSupervisorMappings should reuse a mapping that already exists") + void save_shouldReuseExistingMapping() { + activeFacility(); + AshaSupervisorMapping existing = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndAshaUserIDAndFacilityIDAndDeletedFalse(SUPERVISOR_ID, ASHA_ID, + FACILITY_ID)) + .thenReturn(existing); + + ArrayList saved = + ashaService.saveAshaSupervisorMappings(List.of(mapping(null, SUPERVISOR_ID, ASHA_ID))); + + assertSame(existing, saved.get(0)); + verify(ashaSupervisorMappingRepo, never()).save(any()); + } + + @Test + @DisplayName("saveAshaSupervisorMappings should retire the ASHA's mapping under a different supervisor") + void save_shouldRetireMappingUnderOtherSupervisor() { + activeFacility(); + AshaSupervisorMapping other = mapping(2L, 3118, ASHA_ID); + AshaSupervisorMapping toSave = mapping(null, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo + .findByAshaUserIDAndFacilityIDAndDeletedFalseAndSupervisorUserIDNot(ASHA_ID, FACILITY_ID, + SUPERVISOR_ID)) + .thenReturn(new ArrayList<>(List.of(other))); + when(ashaSupervisorMappingRepo.save(toSave)).thenReturn(toSave); + + ashaService.saveAshaSupervisorMappings(List.of(toSave)); + + assertTrue(other.getDeleted(), "an ASHA may report to only one supervisor at a facility"); + assertEquals("admin", other.getModifiedBy()); + verify(ashaSupervisorMappingRepo).save(other); + } + + @Test + @DisplayName("getSupervisorMappingByFacility should hand back what the repository holds") + void getByFacility_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(); + when(ashaSupervisorMappingRepo.findActiveMappingsByFacilityID(FACILITY_ID)).thenReturn(stored); + + assertSame(stored, ashaService.getSupervisorMappingByFacility(FACILITY_ID)); + } + + @Test + @DisplayName("getAshasByFacility should hand back what the repository holds") + void getAshas_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(); + when(employeeMasterRepo.findAshaUsersByFacilityIDs(anyList())).thenReturn(stored); + + assertSame(stored, ashaService.getAshasByFacility(List.of(FACILITY_ID))); + } + + @Test + @DisplayName("deleteMappings should retire each mapping it can resolve") + void deleteMappings_shouldRetireResolvedMappings() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo.findById(1L)).thenReturn(Optional.of(stored)); + when(ashaSupervisorMappingRepo.findById(2L)).thenReturn(Optional.empty()); + + ashaService.deleteMappings(List.of(1L, 2L), "admin"); + + assertTrue(stored.getDeleted()); + verify(ashaSupervisorMappingRepo).save(stored); + } + + @Test + @DisplayName("restoreMappings should reinstate each mapping it can resolve") + void restoreMappings_shouldReinstateResolvedMappings() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + stored.setDeleted(Boolean.TRUE); + when(ashaSupervisorMappingRepo.findById(1L)).thenReturn(Optional.of(stored)); + when(ashaSupervisorMappingRepo.findById(2L)).thenReturn(Optional.empty()); + + ashaService.restoreMappings(List.of(1L, 2L), "admin"); + + assertFalse(stored.getDeleted()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("deleteBySupervisorAndFacilities should retire every mapping at the named facilities") + void deleteBySupervisorAndFacilities_shouldRetireEveryMapping() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDInAndDeletedFalse(SUPERVISOR_ID, List.of(FACILITY_ID))) + .thenReturn(new ArrayList<>(List.of(stored))); + + ashaService.deleteBySupervisorAndFacilities(SUPERVISOR_ID, List.of(FACILITY_ID), "admin"); + + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("cascadeDeleteByUserID should retire the user's mappings on both sides of the relationship") + void cascadeByUser_shouldRetireBothSides() { + AshaSupervisorMapping asSupervisor = mapping(1L, SUPERVISOR_ID, ASHA_ID); + AshaSupervisorMapping asAsha = mapping(2L, 3118, SUPERVISOR_ID); + when(ashaSupervisorMappingRepo.findBySupervisorUserIDAndDeletedFalse(SUPERVISOR_ID)) + .thenReturn(new ArrayList<>(List.of(asSupervisor))); + when(ashaSupervisorMappingRepo.findByAshaUserIDAndDeletedFalse(SUPERVISOR_ID)) + .thenReturn(new ArrayList<>(List.of(asAsha))); + + ashaService.cascadeDeleteByUserID(SUPERVISOR_ID, "admin"); + + assertTrue(asSupervisor.getDeleted()); + assertTrue(asAsha.getDeleted()); + } + + @Test + @DisplayName("cascadeDeleteByFacilityID should retire every mapping at the facility") + void cascadeByFacility_shouldRetireEveryMapping() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo.findByFacilityIDAndDeletedFalse(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(stored))); + + ashaService.cascadeDeleteByFacilityID(FACILITY_ID, "admin"); + + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("cascadeDeleteByUserIDAndFacilityID should retire only that user's mappings at that facility") + void cascadeByUserAndFacility_shouldRetireBothSidesAtFacility() { + AshaSupervisorMapping asSupervisor = mapping(1L, SUPERVISOR_ID, ASHA_ID); + AshaSupervisorMapping asAsha = mapping(2L, 3118, SUPERVISOR_ID); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDAndDeletedFalse(SUPERVISOR_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(asSupervisor))); + when(ashaSupervisorMappingRepo.findByAshaUserIDAndFacilityIDAndDeletedFalse(SUPERVISOR_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(asAsha))); + + ashaService.cascadeDeleteByUserIDAndFacilityID(SUPERVISOR_ID, FACILITY_ID, "admin"); + + assertTrue(asSupervisor.getDeleted()); + assertTrue(asAsha.getDeleted()); + } + + @Test + @DisplayName("updateAshaMappingsAtomically should retire the old mappings before saving the new ones") + void updateAtomically_shouldRetireThenSave() { + activeFacility(); + AshaSupervisorMapping old = mapping(1L, SUPERVISOR_ID, ASHA_ID); + AshaSupervisorMapping fresh = mapping(null, SUPERVISOR_ID, 4002); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDInAndDeletedFalse(SUPERVISOR_ID, List.of(FACILITY_ID))) + .thenReturn(new ArrayList<>(List.of(old))); + when(ashaSupervisorMappingRepo.save(fresh)).thenReturn(fresh); + + ArrayList saved = ashaService.updateAshaMappingsAtomically( + SUPERVISOR_ID, List.of(FACILITY_ID), List.of(fresh), "admin"); + + assertTrue(old.getDeleted()); + assertSame(fresh, saved.get(0)); + } + + @Test + @DisplayName("updateAshaMappingsAtomically should answer nothing when no new mappings are supplied") + void updateAtomically_shouldAnswerNothingWithoutNewMappings() { + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDInAndDeletedFalse(anyInt(), anyList())) + .thenReturn(new ArrayList<>()); + + assertTrue(ashaService + .updateAshaMappingsAtomically(SUPERVISOR_ID, List.of(FACILITY_ID), null, "admin").isEmpty()); + } + } + + @Nested + @DisplayName("EmployeeSignatureServiceImpl") + class SignatureServiceTests { + + @Test + @DisplayName("uploadSignature should overwrite the signature already on file") + void upload_shouldOverwriteExistingSignature() { + EmployeeSignature existing = new EmployeeSignature(); + existing.setUserID(3117L); + existing.setUserSignatureID(9001L); + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setUserID(3117L); + uploaded.setFileName("new.png"); + uploaded.setFileType("image/png"); + uploaded.setSignature(new byte[] { 1, 2, 3 }); + uploaded.setCreatedBy("admin"); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(existing); + when(employeeSignatureRepo.save(existing)).thenReturn(existing); + + assertEquals(9001L, signatureService.uploadSignature(uploaded)); + assertEquals("new.png", existing.getFileName()); + assertEquals("admin", existing.getModifiedBy()); + } + + @Test + @DisplayName("uploadSignature should store a first signature for a user who has none") + void upload_shouldStoreFirstSignature() { + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setUserID(3117L); + uploaded.setUserSignatureID(9002L); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(null); + when(employeeSignatureRepo.save(uploaded)).thenReturn(uploaded); + + assertEquals(9002L, signatureService.uploadSignature(uploaded)); + } + + @Test + @DisplayName("fetchSignature should hand back what the repository holds") + void fetch_shouldHandBackRepositoryContents() { + EmployeeSignature stored = new EmployeeSignature(); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(stored); + + assertSame(stored, signatureService.fetchSignature(3117L)); + } + + @Test + @DisplayName("existSignature should report whether any signature is on file") + void exist_shouldReportWhetherSignatureIsOnFile() { + when(employeeSignatureRepo.countByUserIDAndSignatureNotNull(3117L)).thenReturn(1L); + when(employeeSignatureRepo.countByUserIDAndSignatureNotNull(3118L)).thenReturn(0L); + + assertTrue(signatureService.existSignature(3117L)); + assertFalse(signatureService.existSignature(3118L)); + } + + @Test + @DisplayName("isSignatureActive should report whether the signature is still in use") + void isActive_shouldReportWhetherSignatureIsInUse() { + when(employeeSignatureRepo.countByUserIDAndSignatureNotNullAndDeletedFalse(3117L)).thenReturn(1L); + when(employeeSignatureRepo.countByUserIDAndSignatureNotNullAndDeletedFalse(3118L)).thenReturn(0L); + + assertTrue(signatureService.isSignatureActive(3117L)); + assertFalse(signatureService.isSignatureActive(3118L)); + } + + @Test + @DisplayName("updateUserSignatureStatus should retire a signature the caller deactivates") + void updateStatus_shouldRetireDeactivatedSignature() { + EmployeeSignature stored = new EmployeeSignature(); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(stored); + when(employeeSignatureRepo.save(stored)).thenReturn(stored); + + signatureService.updateUserSignatureStatus("{\"userID\":3117,\"active\":false}"); + + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("updateUserSignatureStatus should reinstate a signature the caller activates") + void updateStatus_shouldReinstateActivatedSignature() { + EmployeeSignature stored = new EmployeeSignature(); + stored.setDeleted(Boolean.TRUE); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(stored); + when(employeeSignatureRepo.save(stored)).thenReturn(stored); + + signatureService.updateUserSignatureStatus("{\"userID\":3117,\"active\":true}"); + + assertFalse(stored.getDeleted()); + } + + @Test + @DisplayName("updateUserSignatureStatus should refuse a user who has no signature on file") + void updateStatus_shouldRefuseUserWithoutSignature() { + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(null); + + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> signatureService.updateUserSignatureStatus("{\"userID\":3117,\"active\":true}")); + + assertTrue(thrown.getMessage().contains("No signature found"), thrown.getMessage()); + } + } + + @Nested + @DisplayName("USRAgentMappingServiceImpl") + class UsrAgentMappingTests { + + private USRAgentMappingServiceImpl agentService() { + USRAgentMappingServiceImpl service = new USRAgentMappingServiceImpl(); + service.setUsrAgentMappingRepository(usrAgentMappingRepository); + return service; + } + + private Set agentRow() { + Set rows = new LinkedHashSet<>(); + rows.add(new Object[] { 1, 9001, null, 4001, null, "A-1", "secret", "104", Boolean.TRUE }); + rows.add(new Object[] { 2 }); + return rows; + } + + @Test + @DisplayName("getAvailableAgentIds should rebuild the free agents and skip an unusable row") + void getAvailableAgentIds_shouldRebuildFreeAgents() throws Exception { + when(usrAgentMappingRepository.getFreeAgentIds("104", 4001)).thenReturn(agentRow()); + + List agents = agentService() + .getAvailableAgentIds("{\"cti_CampaignName\":\"104\",\"providerServiceMapID\":4001}"); + + assertEquals(1, agents.size()); + assertEquals("A-1", agents.get(0).getAgentID()); + } + + @Test + @DisplayName("updateAgentIds should free the previous agent id before claiming the new one") + void updateAgentIds_shouldFreePreviousAgentId() throws Exception { + when(usrAgentMappingRepository.updateUSRMapping(any(), any(), any())).thenReturn(1); + + Integer changed = agentService().updateAgentIds("{\"oldAgentID\":\"A-0\"," + + "\"providerServiceMapID\":4001,\"isAvailable\":false,\"usrMappingID\":9001," + + "\"usrAgentMappingID\":1}"); + + assertEquals(1, changed); + verify(usrAgentMappingRepository).updateUSRMapping(true, null, "A-0", 4001); + } + + @Test + @DisplayName("updateAgentIds should leave the previous agent id alone when none is named") + void updateAgentIds_shouldLeavePreviousAgentIdAlone() throws Exception { + when(usrAgentMappingRepository.updateUSRMapping(any(), any(), any())).thenReturn(1); + + agentService().updateAgentIds("{\"isAvailable\":true,\"usrAgentMappingID\":1}"); + + verify(usrAgentMappingRepository, never()) + .updateUSRMapping(any(Boolean.class), any(), anyString(), anyInt()); + } + + @Test + @DisplayName("createUSRAgentMapping should skip an agent id the provider already holds") + void createUSRAgentMapping_shouldSkipExistingAgent() throws Exception { + when(usrAgentMappingRepository.getExistingAgent(4001, "A-1")).thenReturn(1L); + when(usrAgentMappingRepository.getExistingAgent(4001, "A-2")).thenReturn(0L); + when(usrAgentMappingRepository.save(any())).thenAnswer(call -> call.getArgument(0)); + + List created = agentService().createUSRAgentMapping( + "[{\"agentID\":\"A-1\",\"providerServiceMapID\":4001}," + + "{\"agentID\":\"A-2\",\"providerServiceMapID\":4001}]"); + + assertEquals(1, created.size()); + assertEquals("A-2", created.get(0).getAgentID()); + } + + @Test + @DisplayName("getAvailableCampaigns should hand back what the repository holds") + void getAvailableCampaigns_shouldHandBackRepositoryContents() throws Exception { + when(usrAgentMappingRepository.getAvailableCampaigns(4001)).thenReturn(List.of("104", "1097")); + + assertEquals(2, agentService().getAvailableCampaigns("{\"providerServiceMapID\":4001}").size()); + } + + @Test + @DisplayName("getAllAgentIds should look the agent up directly when the caller names one") + void getAllAgentIds_shouldLookUpNamedAgent() throws Exception { + when(usrAgentMappingRepository + .getUSRAgentMappingByAgentIDAndProviderServiceMapID("A-1", 4001)).thenReturn(agentRow()); + + List agents = agentService() + .getAllAgentIds("{\"agentID\":\"A-1\",\"providerServiceMapID\":4001}"); + + assertEquals(1, agents.size()); + } + + @Test + @DisplayName("getAllAgentIds should filter by availability when the caller asks for it") + void getAllAgentIds_shouldFilterByAvailability() throws Exception { + when(usrAgentMappingRepository.getAllAgentIds(4001, "104", true)).thenReturn(agentRow()); + + assertEquals(1, agentService().getAllAgentIds( + "{\"providerServiceMapID\":4001,\"cti_CampaignName\":\"104\",\"isAvailable\":true}").size()); + } + + @Test + @DisplayName("getAllAgentIds should filter by campaign alone when availability is not named") + void getAllAgentIds_shouldFilterByCampaignAlone() throws Exception { + when(usrAgentMappingRepository.getAllAgentId(4001, "104")).thenReturn(agentRow()); + + assertEquals(1, agentService() + .getAllAgentIds("{\"providerServiceMapID\":4001,\"cti_CampaignName\":\"104\"}").size()); + } + + @Test + @DisplayName("getAllAgentIds should answer every agent under the mapping when nothing is named") + void getAllAgentIds_shouldAnswerEveryAgent() throws Exception { + when(usrAgentMappingRepository.getAllAgentIds(4001)).thenReturn(agentRow()); + + assertEquals(1, agentService().getAllAgentIds("{\"providerServiceMapID\":4001}").size()); + } + + @Test + @DisplayName("updateCTICampaignNameMapping should answer how many mappings moved campaign") + void updateCTICampaignNameMapping_shouldAnswerChangedCount() throws Exception { + when(usrAgentMappingRepository.updateCTICampaignNameMapping("1097", 1)).thenReturn(1); + + assertEquals(1, agentService() + .updateCTICampaignNameMapping("{\"cti_CampaignName\":\"1097\",\"usrAgentMappingID\":1}")); + } + + @Test + @DisplayName("updateDeletedAgentIDStatus should free the agent id of a deleted user") + void updateDeletedAgentIDStatus_shouldFreeAgentId() { + when(usrAgentMappingRepository.updateDeletedAgentIDStatus("A-1")).thenReturn(1); + + agentService().updateDeletedAgentIDStatus("A-1"); + + verify(usrAgentMappingRepository).updateDeletedAgentIDStatus("A-1"); + } + } + + @Nested + @DisplayName("M_DesignationImpl") + class DesignationServiceTests { + + @Test + @DisplayName("getDesinationlist should hand back what the repository holds") + void getDesinationlist_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_Designation())); + when(m_DesignationRepo.getDesinationlist()).thenReturn(stored); + + assertSame(stored, designationService.getDesinationlist()); + } + } +} diff --git a/src/test/java/com/iemr/admin/service/facilitytype/M_facilitytypeServiceImplTest.java b/src/test/java/com/iemr/admin/service/facilitytype/M_facilitytypeServiceImplTest.java new file mode 100644 index 0000000..8551a78 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/facilitytype/M_facilitytypeServiceImplTest.java @@ -0,0 +1,196 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.facilitytype; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.facilitytype.M_facilitytype; +import com.iemr.admin.data.store.M_FacilityLevel; +import com.iemr.admin.repository.facilitytype.M_FacilityLevelRepo; +import com.iemr.admin.repository.facilitytype.M_facilitytypeRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The facility type service keeps the kinds of health facility a state runs, + * refusing a type name a state already has. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("M_facilitytypeServiceImpl Test Suite") +class M_facilitytypeServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer STATE_ID = 29; + private static final Integer FACILITY_TYPE_ID = 12; + + @Mock + private M_facilitytypeRepo m_facilitytypeRepo; + + @Mock + private M_FacilityLevelRepo m_facilityLevelRepo; + + @InjectMocks + private M_facilitytypeServiceImpl service; + + private static M_facilitytype facilityType() { + M_facilitytype facilityType = new M_facilitytype(); + facilityType.setFacilityTypeID(FACILITY_TYPE_ID); + facilityType.setFacilityTypeName("Primary Health Centre"); + facilityType.setFacilityTypeCode("PHC"); + facilityType.setProviderServiceMapID(PSM_ID); + facilityType.setStateID(STATE_ID); + return facilityType; + } + + @Test + @DisplayName("getAllFicilityData should answer the facility types of the provider asked about") + void getAll_shouldAnswerProvidersFacilityTypes() { + ArrayList held = new ArrayList<>(List.of(facilityType())); + when(m_facilitytypeRepo.getAllFicilityData(PSM_ID)).thenReturn(held); + + assertSame(held, service.getAllFicilityData(PSM_ID)); + } + + @Test + @DisplayName("getFacilityTypesByRuralUrban should narrow the types to the setting asked for") + void getByRuralUrban_shouldNarrowToSetting() { + when(m_facilitytypeRepo.findByProviderServiceMapIDAndRuralUrban(PSM_ID, "Rural")) + .thenReturn(List.of(facilityType())); + + assertEquals(1, service.getFacilityTypesByRuralUrban(PSM_ID, "Rural").size()); + } + + @Test + @DisplayName("addAllFicilityData should store a facility type the state does not have yet") + void add_shouldStoreNewFacilityType() { + ArrayList stored = new ArrayList<>(List.of(facilityType())); + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse(anyString(), anyInt())) + .thenReturn(false); + when(m_facilitytypeRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.addAllFicilityData(List.of(facilityType()))); + } + + @Test + @DisplayName("addAllFicilityData should refuse a facility type name the state already has") + void add_shouldRefuseDuplicateName() { + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse("Primary Health Centre", + STATE_ID)).thenReturn(true); + + RuntimeException refusal = assertThrows(RuntimeException.class, + () -> service.addAllFicilityData(List.of(facilityType()))); + + assertTrue(refusal.getMessage().contains("Primary Health Centre"), refusal.getMessage()); + verify(m_facilitytypeRepo, never()).saveAll(anyList()); + } + + @Test + @DisplayName("editAllFicilityData and updateFacilityData should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_facilitytype stored = facilityType(); + when(m_facilitytypeRepo.findByFacilityTypeID(FACILITY_TYPE_ID)).thenReturn(stored); + when(m_facilitytypeRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.editAllFicilityData(FACILITY_TYPE_ID)); + assertSame(stored, service.updateFacilityData(stored)); + } + + @Test + @DisplayName("editAllFicilityData should answer nothing when the facility type is unknown") + void edit_shouldAnswerNothingForUnknownFacilityType() { + when(m_facilitytypeRepo.findByFacilityTypeID(-1)).thenReturn(null); + + assertNull(service.editAllFicilityData(-1)); + } + + @Test + @DisplayName("checkFacilityTypeCode should report whether the provider already uses the code") + void checkCode_shouldReportWhetherCodeIsUsed() { + when(m_facilitytypeRepo.findByFacilityTypeCodeAndProviderServiceMapID("PHC", PSM_ID)) + .thenReturn(List.of(facilityType())); + assertTrue(service.checkFacilityTypeCode(facilityType())); + + when(m_facilitytypeRepo.findByFacilityTypeCodeAndProviderServiceMapID("PHC", PSM_ID)) + .thenReturn(new ArrayList<>()); + assertFalse(service.checkFacilityTypeCode(facilityType())); + } + + @Test + @DisplayName("getFacilityLevels should answer the levels still in use, named in order") + void getLevels_shouldAnswerLiveLevels() { + ArrayList levels = new ArrayList<>(List.of(new M_FacilityLevel())); + when(m_facilityLevelRepo.findByDeletedFalseOrderByLevelName()).thenReturn(levels); + + assertSame(levels, service.getFacilityLevels()); + } + + @Test + @DisplayName("getFacilityTypesByBlock should answer the types run in the taluk asked about") + void getByBlock_shouldAnswerTypesOfTaluk() { + when(m_facilitytypeRepo.findFacilityTypesByBlock(3011)).thenReturn(List.of(facilityType())); + + assertEquals(1, service.getFacilityTypesByBlock(3011).size()); + } + + @Test + @DisplayName("getFacilityTypesByState should answer the types run in the state asked about") + void getByState_shouldAnswerTypesOfState() { + when(m_facilitytypeRepo.findByStateID(STATE_ID)).thenReturn(List.of(facilityType())); + + assertEquals(1, service.getFacilityTypesByState(STATE_ID).size()); + } + + @Test + @DisplayName("checkFacilityTypeNameExists should report whether the state already has the name") + void checkName_shouldReportWhetherNameIsUsed() { + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse("Primary Health Centre", + STATE_ID)).thenReturn(true); + assertTrue(service.checkFacilityTypeNameExists("Primary Health Centre", STATE_ID)); + + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse("Sub Centre", STATE_ID)) + .thenReturn(false); + assertFalse(service.checkFacilityTypeNameExists("Sub Centre", STATE_ID)); + } +} diff --git a/src/test/java/com/iemr/admin/service/foetalmonitormaster/FoetalMonitorServiceImplTest.java b/src/test/java/com/iemr/admin/service/foetalmonitormaster/FoetalMonitorServiceImplTest.java new file mode 100644 index 0000000..6731490 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/foetalmonitormaster/FoetalMonitorServiceImplTest.java @@ -0,0 +1,420 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.foetalmonitormaster; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.foetalmonitormaster.FoetalMonitorDeviceID; +import com.iemr.admin.data.foetalmonitormaster.M_FoetalMonitor; +import com.iemr.admin.repo.foetalmonitormaster.FoetalMonitorDeviceIDRepo; +import com.iemr.admin.repo.foetalmonitormaster.FoetalMonitorRepository; +import com.iemr.admin.repository.vanMaster.VanMasterRepository; +import com.iemr.admin.utils.exception.IEMRException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * A fetosense device is attached to one van at a time, so the mapping rules here + * decide whether a foetal monitor reading can be traced back to the van it was + * taken in. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("FoetalMonitorServiceImpl Test Suite") +class FoetalMonitorServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer VAN_ID = 71; + private static final Long VFD_ID = 9001L; + + @Mock + private FoetalMonitorRepository foetalMonitorRepository; + + @Mock + private VanMasterRepository masterVanRepo; + + @Mock + private FoetalMonitorDeviceIDRepo foetalMonitorDeviceIDRepo; + + @InjectMocks + private FoetalMonitorServiceImpl service; + + private static M_FoetalMonitor test(Integer id, String name) { + M_FoetalMonitor test = new M_FoetalMonitor(); + test.setFoetalMonitorTestID(id); + test.setTestName(name); + return test; + } + + private static FoetalMonitorDeviceID device() { + FoetalMonitorDeviceID device = new FoetalMonitorDeviceID(); + device.setVfdID(VFD_ID); + device.setDeviceID("FS-1"); + device.setDeviceName("Fetosense 1"); + device.setVanID(VAN_ID); + device.setVanTypeID(1); + device.setParkingPlaceID(31); + device.setVanName("MMU Van 1"); + device.setProviderServiceMapID(PSM_ID); + device.setCreatedBy("admin"); + device.setDeactivated(Boolean.FALSE); + device.setDeleted(Boolean.FALSE); + return device; + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should publish the tests it stored") + void createTestMaster_shouldPublishStoredTests() throws Exception { + when(foetalMonitorRepository.saveAll(anyList())).thenReturn(List.of(test(11, "Non stress test"))); + + String created = service.createFoetalMonitorTestMaster("[{\"testName\":\"Non stress test\"}]"); + + assertTrue(created.contains("Non stress test"), created); + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should answer nothing when it stored fewer than it was given") + void createTestMaster_shouldAnswerNothingOnPartialStore() throws Exception { + when(foetalMonitorRepository.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createFoetalMonitorTestMaster("[{\"testName\":\"Non stress test\"}]")); + } + + @Test + @DisplayName("getFoetalMonitorTestMaster should publish the tests of the provider") + void getTestMaster_shouldPublishProviderTests() { + when(foetalMonitorRepository.getByProviderServiceMapID(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(test(11, "Non stress test")))); + + assertTrue(service.getFoetalMonitorTestMaster(PSM_ID).contains("Non stress test")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMaster should publish the test once the edit lands") + void updateTestMaster_shouldPublishEditedTest() { + when(foetalMonitorRepository.updateFoetalMonitorDetails(11, "Non stress test", null, "admin")).thenReturn(1); + when(foetalMonitorRepository.getByFoetalMonitorTestID(11)).thenReturn(test(11, "Non stress test")); + + String published = service.updateFoetalMonitorTestMaster( + "{\"foetalMonitorTestID\":11,\"testName\":\"Non stress test\",\"modifiedBy\":\"admin\"}"); + + assertTrue(published.contains("Non stress test"), published); + } + + @Test + @DisplayName("updateFoetalMonitorTestMaster should answer nothing for a request that names no test") + void updateTestMaster_shouldAnswerNothingWithoutTest() { + assertNull(service.updateFoetalMonitorTestMaster("{\"testName\":\"Non stress test\"}")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMaster should answer nothing when the edit changed nothing") + void updateTestMaster_shouldAnswerNothingWhenNothingChanged() { + when(foetalMonitorRepository.updateFoetalMonitorDetails(anyInt(), any(), any(), any())).thenReturn(0); + + assertNull(service.updateFoetalMonitorTestMaster("{\"foetalMonitorTestID\":11}")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMasterStatus should publish the test once its status has changed") + void updateTestStatus_shouldPublishChangedTest() throws Exception { + when(foetalMonitorRepository.updateFoetalMonitorStatus(11, true)).thenReturn(1); + when(foetalMonitorRepository.getByFoetalMonitorTestID(11)).thenReturn(test(11, "Non stress test")); + + assertTrue(service.updateFoetalMonitorTestMasterStatus(11, true).contains("Non stress test")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMasterStatus should answer nothing when no test changed") + void updateTestStatus_shouldAnswerNothingWhenNothingChanged() throws Exception { + when(foetalMonitorRepository.updateFoetalMonitorStatus(11, true)).thenReturn(0); + + assertNull(service.updateFoetalMonitorTestMasterStatus(11, true)); + } + + @Test + @DisplayName("saveFoetalMonitorDeviceID should report the devices it stored") + void saveDeviceID_shouldReportStoredDevices() throws Exception { + when(foetalMonitorDeviceIDRepo.saveAll(anyList())) + .thenReturn(new ArrayList<>(List.of(device()))); + + assertEquals(1, service.saveFoetalMonitorDeviceID(new ArrayList<>(List.of(device())))); + } + + @Test + @DisplayName("saveFoetalMonitorDeviceID should refuse a run that stored nothing") + void saveDeviceID_shouldRefuseEmptyStore() { + when(foetalMonitorDeviceIDRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertThrows(IEMRException.class, + () -> service.saveFoetalMonitorDeviceID(new ArrayList<>(List.of(device())))); + } + + @Test + @DisplayName("getFoetalMonitorDeviceID should publish the devices of the provider") + void getDeviceID_shouldPublishProviderDevices() throws Exception { + when(foetalMonitorDeviceIDRepo.getFoetalMonitorDeviceID(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(device()))); + + String published = service.getFoetalMonitorDeviceID(device()); + + assertTrue(published.contains("fetosenseDeviceIDs"), published); + assertTrue(published.contains("FS-1"), published); + } + + @Test + @DisplayName("getFoetalMonitorDeviceID should report a lookup it could not run") + void getDeviceID_shouldReportFailedLookup() { + when(foetalMonitorDeviceIDRepo.getFoetalMonitorDeviceID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertThrows(IEMRException.class, () -> service.getFoetalMonitorDeviceID(device())); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should release the van the device was attached to") + void deleteDeviceID_shouldReleaseVan() throws Exception { + FoetalMonitorDeviceID request = device(); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + when(masterVanRepo.updateVanFoetalMonitorsmapping(true, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteFoetalMonitorDeviceID(request)); + verify(masterVanRepo).updateVanFoetalMonitorsmapping(true, VAN_ID); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should mark the van unmapped when the device is retired") + void deleteDeviceID_shouldMarkVanUnmappedOnRetirement() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setDeleted(Boolean.TRUE); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + when(masterVanRepo.updateVanFoetalMonitorsmapping(false, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteFoetalMonitorDeviceID(request)); + verify(masterVanRepo).updateVanFoetalMonitorsmapping(false, VAN_ID); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should refuse a device whose van could not be released") + void deleteDeviceID_shouldRefuseWhenVanCannotBeReleased() { + FoetalMonitorDeviceID request = device(); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + when(masterVanRepo.updateVanFoetalMonitorsmapping(anyBoolean(), anyInt())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.deleteFoetalMonitorDeviceID(request)); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should leave the vans alone for a device attached to none") + void deleteDeviceID_shouldLeaveVansAloneForUnattachedDevice() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setVanID(null); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + + assertEquals(1, service.deleteFoetalMonitorDeviceID(request)); + verify(masterVanRepo, never()).updateVanFoetalMonitorsmapping(anyBoolean(), anyInt()); + } + + @Test + @DisplayName("getvanIDAndFoetalMonitorDeviceID should publish the vans and devices still free to pair") + void getVanAndDevice_shouldPublishFreePairs() throws Exception { + ArrayList vanRows = new ArrayList<>(); + vanRows.add(new Object[] { VAN_ID, "MMU Van 1", "KA-01-AB-1234" }); + when(masterVanRepo.getVanIDNotMappedWithDevice(1, 31, PSM_ID)).thenReturn(vanRows); + when(foetalMonitorDeviceIDRepo.getFoetalMonitorDeviceIDNotMapped(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(device()))); + + String published = service.getvanIDAndFoetalMonitorDeviceID(device()); + + assertTrue(published.contains("VanIDs"), published); + assertTrue(published.contains("MMU Van 1"), published); + assertTrue(published.contains("deviceIDs"), published); + } + + @Test + @DisplayName("getvanIDAndFoetalMonitorDeviceID should report a lookup it could not run") + void getVanAndDevice_shouldReportFailedLookup() { + when(masterVanRepo.getVanIDNotMappedWithDevice(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertThrows(IEMRException.class, () -> service.getvanIDAndFoetalMonitorDeviceID(device())); + } + + @Test + @DisplayName("vanIDAndDeviceIDMapping should attach the device and mark the van as carrying one") + void mapping_shouldAttachDeviceAndMarkVan() throws Exception { + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + anyString(), anyString())).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(true, VAN_ID)).thenReturn(1); + + assertEquals(1, service.vanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("vanIDAndDeviceIDMapping should refuse a pairing the van could not be marked for") + void mapping_shouldRefuseWhenVanCannotBeMarked() { + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + anyString(), anyString())).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(anyBoolean(), anyInt())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.vanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("vanIDAndDeviceIDMapping should refuse a pairing the device could not take") + void mapping_shouldRefuseWhenDeviceCannotTakePairing() { + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + anyString(), anyString())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.vanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("updateFoetalMonitorDeviceID should report the device it saved") + void updateDeviceID_shouldReportSavedDevice() throws Exception { + FoetalMonitorDeviceID request = device(); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + + assertEquals(1, service.updateFoetalMonitorDeviceID(request)); + } + + @Test + @DisplayName("updateFoetalMonitorDeviceID should refuse an edit the store did not take") + void updateDeviceID_shouldRefuseUntakenEdit() { + when(foetalMonitorDeviceIDRepo.save(any())).thenReturn(null); + + assertThrows(IEMRException.class, () -> service.updateFoetalMonitorDeviceID(device())); + } + + @Test + @DisplayName("getVanIDMappingWorklist should publish the pairings on record") + void getWorklist_shouldPublishPairings() throws Exception { + when(foetalMonitorDeviceIDRepo.getMappedWorklist(1, 31, PSM_ID)) + .thenReturn(new ArrayList<>(List.of(device()))); + + assertTrue(service.getVanIDMappingWorklist(device()).contains("FS-1")); + } + + @Test + @DisplayName("getVanIDMappingWorklist should report a lookup it could not run") + void getWorklist_shouldReportFailedLookup() { + when(foetalMonitorDeviceIDRepo.getMappedWorklist(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertThrows(IEMRException.class, () -> service.getVanIDMappingWorklist(device())); + } + + @Test + @DisplayName("updatingvanIDAndDeviceIDMapping should clear the old van before attaching the new one") + void updateMapping_shouldClearOldVanFirst() throws Exception { + when(foetalMonitorDeviceIDRepo.updateVanDetailsToNull(VFD_ID)).thenReturn(1); + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + any(), anyString())).thenReturn(1); + + assertEquals(1, service.updatingvanIDAndDeviceIDMapping(device())); + verify(foetalMonitorDeviceIDRepo).updateVanDetailsToNull(VFD_ID); + } + + @Test + @DisplayName("updatingvanIDAndDeviceIDMapping should refuse an edit whose old van could not be cleared") + void updateMapping_shouldRefuseWhenOldVanCannotBeCleared() { + when(foetalMonitorDeviceIDRepo.updateVanDetailsToNull(VFD_ID)).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.updatingvanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("updatingvanIDAndDeviceIDMapping should refuse an edit the new van could not take") + void updateMapping_shouldRefuseWhenNewVanCannotTake() { + when(foetalMonitorDeviceIDRepo.updateVanDetailsToNull(VFD_ID)).thenReturn(1); + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + any(), anyString())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.updatingvanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should release the pairing and free the van") + void deleteMapping_shouldReleasePairingAndFreeVan() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.TRUE); + when(foetalMonitorDeviceIDRepo.deleteMapping(true, VFD_ID)).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(false, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteVanIDAndDeviceIDMapping(request)); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should refuse to reinstate a van another device already holds") + void deleteMapping_shouldRefuseVanHeldByAnotherDevice() { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.FALSE); + when(foetalMonitorDeviceIDRepo.getMappedVanDetails(VAN_ID)).thenReturn(1); + + IEMRException thrown = assertThrows(IEMRException.class, + () -> service.deleteVanIDAndDeviceIDMapping(request)); + assertTrue(thrown.getMessage().contains("already mapped with a device"), thrown.getMessage()); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should reinstate a pairing for a van that is free") + void deleteMapping_shouldReinstatePairingForFreeVan() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.FALSE); + when(foetalMonitorDeviceIDRepo.getMappedVanDetails(VAN_ID)).thenReturn(0); + when(foetalMonitorDeviceIDRepo.deleteMapping(false, VFD_ID)).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(true, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteVanIDAndDeviceIDMapping(request)); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should refuse a pairing the store would not change") + void deleteMapping_shouldRefuseUnchangedPairing() { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.TRUE); + when(foetalMonitorDeviceIDRepo.deleteMapping(anyBoolean(), any())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.deleteVanIDAndDeviceIDMapping(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/health/HealthServiceTest.java b/src/test/java/com/iemr/admin/service/health/HealthServiceTest.java new file mode 100644 index 0000000..b2e8176 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/health/HealthServiceTest.java @@ -0,0 +1,472 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.health; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * The health endpoint is what the deployment's monitoring watches, so it has to + * tell a database that is merely slow apart from one that is unreachable. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("HealthService Test Suite") +class HealthServiceTest { + + @Mock + private DataSource dataSource; + + @Mock + private RedisConnectionFactory redisConnectionFactory; + + @Mock + private Connection connection; + + @Mock + private Statement statement; + + @Mock + private RedisConnection redisConnection; + + @SuppressWarnings("unchecked") + private static ObjectProvider providerOf(T value) { + ObjectProvider provider = mock(ObjectProvider.class); + when(provider.getIfAvailable()).thenReturn(value); + return provider; + } + + /** + * Builds the service without a data source so its constructor starts no background + * cycle, then attaches the stores under test. That keeps the diagnostics the tests + * drive from racing a scheduled cycle over the same mocks. + */ + private HealthService serviceWith(DataSource ds, RedisConnectionFactory redis) { + HealthService service = new HealthService(providerOf(null), providerOf(redis)); + ReflectionTestUtils.setField(service, "dataSource", ds); + ((AtomicLong) ReflectionTestUtils.getField(service, "lastDiagnosticRunAt")).set(0); + return service; + } + + private void databaseAnswers() throws SQLException { + when(dataSource.getConnection()).thenReturn(connection); + when(connection.createStatement()).thenReturn(statement); + } + + private void redisAnswers() { + when(redisConnectionFactory.getConnection()).thenReturn(redisConnection); + } + + private static ResultSet countingResultSet(int count) throws SQLException { + ResultSet rs = mock(ResultSet.class); + when(rs.next()).thenReturn(true); + when(rs.getInt("cnt")).thenReturn(count); + return rs; + } + + private static ResultSet statusResultSet(long value) throws SQLException { + ResultSet rs = mock(ResultSet.class); + when(rs.next()).thenReturn(true); + when(rs.getLong("Value")).thenReturn(value); + when(rs.getInt("Value")).thenReturn((int) value); + return rs; + } + + @SuppressWarnings("unchecked") + private static String statusOf(Map health, String service) { + return (String) ((Map) health.get(service)).get("status"); + } + + @SuppressWarnings("unchecked") + private static String severityOf(Map health, String service) { + return (String) ((Map) health.get(service)).get("severity"); + } + + @Nested + @DisplayName("checkHealth") + class CheckHealthTests { + + @Test + @DisplayName("should report the deployment up when both stores answer") + void checkHealth_shouldReportUpWhenBothStoresAnswer() throws Exception { + databaseAnswers(); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + + Map health = service.checkHealth(); + + assertEquals("UP", health.get("status")); + assertEquals("UP", statusOf(health, "mysql")); + assertEquals("UP", statusOf(health, "redis")); + assertNotNull(health.get("checkedAt")); + } + + @Test + @DisplayName("should report the deployment down when the database cannot be reached") + void checkHealth_shouldReportDownWhenDatabaseUnreachable() throws Exception { + when(dataSource.getConnection()).thenThrow(new SQLException("connection refused")); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + + Map health = service.checkHealth(); + + assertEquals("DOWN", health.get("status")); + assertEquals("DOWN", statusOf(health, "mysql")); + assertEquals("CRITICAL", severityOf(health, "mysql")); + } + + @Test + @DisplayName("should report the deployment down when Redis cannot be reached") + void checkHealth_shouldReportDownWhenRedisUnreachable() throws Exception { + databaseAnswers(); + when(redisConnectionFactory.getConnection()) + .thenThrow(new IllegalStateException("connection refused")); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + + Map health = service.checkHealth(); + + assertEquals("DOWN", health.get("status")); + assertEquals("DOWN", statusOf(health, "redis")); + } + + @Test + @DisplayName("should report a store that is not configured rather than call it down") + void checkHealth_shouldReportUnconfiguredStore() { + HealthService service = serviceWith(null, null); + + Map health = service.checkHealth(); + + assertEquals("UP", health.get("status"), + "a deployment without these stores is not itself unhealthy"); + assertEquals("NOT_CONFIGURED", statusOf(health, "mysql")); + assertEquals("INFO", severityOf(health, "mysql")); + assertEquals("NOT_CONFIGURED", statusOf(health, "redis")); + } + + @Test + @DisplayName("should report a degraded database as still up but flagged") + void checkHealth_shouldReportDegradedDatabase() throws Exception { + databaseAnswers(); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "set", "WARNING"); + + Map health = service.checkHealth(); + + assertEquals("UP", health.get("status"), "a degraded database is still serving requests"); + assertEquals("DEGRADED", statusOf(health, "mysql")); + } + + @Test + @DisplayName("should report a critically degraded database as down") + void checkHealth_shouldReportCriticalDatabaseAsDown() throws Exception { + databaseAnswers(); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "set", "CRITICAL"); + + assertEquals("DOWN", service.checkHealth().get("status")); + } + } + + @Nested + @DisplayName("Background diagnostics") + class DiagnosticTests { + + private HealthService diagnosingService() throws SQLException { + databaseAnswers(); + return serviceWith(dataSource, redisConnectionFactory); + } + + private String severityAfterDiagnostics(HealthService service) { + ((AtomicLong) ReflectionTestUtils.getField(service, "lastDiagnosticRunAt")).set(0); + ReflectionTestUtils.invokeMethod(service, "runAdvancedMySQLDiagnostics"); + return (String) ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "get"); + } + + private void everyCheckClean() throws SQLException { + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + } + + @Test + @DisplayName("should report a healthy database when every check is clean") + void diagnostics_shouldReportHealthyDatabase() throws Exception { + HealthService service = diagnosingService(); + everyCheckClean(); + + assertEquals("OK", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when more processes are stuck than the threshold allows") + void diagnostics_shouldWarnOnStuckProcesses() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("PROCESSLIST")) { + return countingResultSet(10); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should stay healthy when a few processes are stuck but under the threshold") + void diagnostics_shouldStayHealthyUnderStuckThreshold() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("PROCESSLIST")) { + return countingResultSet(2); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("OK", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should escalate to critical when several transactions run long") + void diagnostics_shouldEscalateOnManyLongTransactions() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("INNODB_TRX")) { + return countingResultSet(6); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("CRITICAL", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when a single transaction runs long") + void diagnostics_shouldWarnOnOneLongTransaction() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("INNODB_TRX")) { + return countingResultSet(2); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when new deadlocks have happened since the previous cycle") + void diagnostics_shouldWarnOnNewDeadlocks() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Innodb_deadlocks")) { + return statusResultSet(3); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when new slow queries have been logged since the previous cycle") + void diagnostics_shouldWarnOnNewSlowQueries() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Slow_queries")) { + return statusResultSet(7); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should escalate to critical when the connection pool is nearly exhausted") + void diagnostics_shouldEscalateOnExhaustedPool() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Threads_connected")) { + return statusResultSet(490); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("CRITICAL", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when connection usage is high but not yet exhausted") + void diagnostics_shouldWarnOnHighConnectionUsage() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Threads_connected")) { + return statusResultSet(450); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should report critical when it cannot open a connection to diagnose at all") + void diagnostics_shouldReportCriticalWhenConnectionCannotBeOpened() throws Exception { + HealthService service = diagnosingService(); + when(dataSource.getConnection()).thenThrow(new SQLException("connection refused")); + + assertEquals("CRITICAL", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should keep the previous verdict when a cycle is asked for too soon") + void diagnostics_shouldKeepPreviousVerdictWhenAskedTooSoon() throws Exception { + HealthService service = diagnosingService(); + everyCheckClean(); + severityAfterDiagnostics(service); + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "set", "WARNING"); + ReflectionTestUtils.invokeMethod(service, "runAdvancedMySQLDiagnostics"); + + assertEquals("WARNING", + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "get"), + "a cycle inside the guard window must not overwrite the standing verdict"); + } + + @Test + @DisplayName("should survive a check whose query the database refuses") + void diagnostics_shouldSurviveRefusedQuery() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenThrow(new SQLException("access denied")); + + assertEquals("OK", severityAfterDiagnostics(service), + "a check that cannot run must not by itself condemn the database"); + } + } + + @Test + @DisplayName("shutdownDiagnostics should stop the background cycle") + void shutdownDiagnostics_shouldStopBackgroundCycle() throws Exception { + databaseAnswers(); + HealthService service = new HealthService(providerOf(dataSource), providerOf(redisConnectionFactory)); + + service.shutdownDiagnostics(); + + java.util.concurrent.ExecutorService scheduler = (java.util.concurrent.ExecutorService) + ReflectionTestUtils.getField(service, "diagnosticScheduler"); + assertEquals(true, scheduler.isShutdown()); + } +} diff --git a/src/test/java/com/iemr/admin/service/item/ItemServiceImplTest.java b/src/test/java/com/iemr/admin/service/item/ItemServiceImplTest.java new file mode 100644 index 0000000..4fa9c95 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/item/ItemServiceImplTest.java @@ -0,0 +1,284 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.item; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.items.CodeChecker; +import com.iemr.admin.data.items.ItemMaster; +import com.iemr.admin.data.items.M_ItemCategory; +import com.iemr.admin.data.items.M_ItemForm; +import com.iemr.admin.data.items.M_Route; +import com.iemr.admin.repository.item.ItemCategoryRepo; +import com.iemr.admin.repository.item.ItemFormRepo; +import com.iemr.admin.repository.item.ItemRepo; +import com.iemr.admin.repository.item.RouteRepo; +import com.iemr.admin.repository.itemfacilitymapping.M_itemfacilitymappingRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The item service maintains the inventory catalogue and the codes that keep + * each entry unique within a provider. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ItemServiceImpl Test Suite") +class ItemServiceImplTest { + + private static final Integer PSM_ID = 4001; + + @Mock + private ItemRepo itemRepo; + + @Mock + private ItemCategoryRepo itemCategoryRepo; + + @Mock + private RouteRepo routeRepo; + + @Mock + private ItemFormRepo itemFormRepo; + + @Mock + private M_itemfacilitymappingRepo itemfacilitymappingRepo; + + @InjectMocks + private ItemServiceImpl service; + + private static M_ItemCategory category(Integer id) { + M_ItemCategory category = new M_ItemCategory(); + category.setItemCategoryID(id); + return category; + } + + @Test + @DisplayName("getItemCategory should read the whole catalogue when retired categories are wanted too") + void getItemCategory_shouldReadWholeCatalogue() { + List stored = List.of(category(31)); + when(itemCategoryRepo.findByProviderServiceMapIDOrderByItemCategoryName(PSM_ID)).thenReturn(stored); + + assertSame(stored, service.getItemCategory(true, PSM_ID)); + } + + @Test + @DisplayName("getItemCategory should read only the live categories when retired ones are excluded") + void getItemCategory_shouldReadLiveCategories() { + List stored = List.of(category(31)); + when(itemCategoryRepo.findByDeletedAndProviderServiceMapIDOrderByItemCategoryName(false, PSM_ID)) + .thenReturn(stored); + + assertSame(stored, service.getItemCategory(false, PSM_ID)); + } + + @Test + @DisplayName("getItemCategory should answer nothing when the caller names no provider") + void getItemCategory_shouldAnswerNothingWithoutProvider() { + assertTrue(service.getItemCategory(true, null).isEmpty()); + verify(itemCategoryRepo, never()).findByProviderServiceMapIDOrderByItemCategoryName(anyInt()); + } + + @Test + @DisplayName("the item lookups should each reach their own repository query") + void itemLookups_shouldReachTheirOwnQuery() { + ItemMaster item = new ItemMaster(); + List items = List.of(item); + when(itemRepo.save(item)).thenReturn(item); + when(itemRepo.saveAll(anyList())).thenReturn(items); + when(itemRepo.findByProviderServiceMapIDOrderByItemName(PSM_ID)).thenReturn(items); + when(itemRepo.findByItemID(101)).thenReturn(item); + when(itemRepo.findDetailOne(101)).thenReturn(item); + when(itemRepo.getItemMasters(PSM_ID, 31)).thenReturn(items); + when(itemRepo.deleteItemMaster(101, true)).thenReturn(1); + when(itemRepo.discontinueItemMaster(101, true)).thenReturn(1); + when(itemCategoryRepo.findByItemCategoryID(31)).thenReturn(category(31)); + when(routeRepo.getAll()).thenReturn(List.of(new M_Route())); + when(itemFormRepo.getAll()).thenReturn(List.of(new M_ItemForm())); + + assertSame(item, service.createItemMaster(item)); + assertSame(items, service.addAllItemMaster(new ArrayList<>())); + assertSame(items, service.getItemMaster(PSM_ID)); + assertSame(item, service.getItemMasterByID(101)); + assertSame(item, service.getItemMasterCatByID(101)); + assertSame(items, service.getItemMasters(PSM_ID, 31)); + assertEquals(1, service.blockItemMaster(101, true)); + assertEquals(1, service.discontinueItemMaster(101, true)); + assertEquals(31, service.getItemCategory(31).getItemCategoryID()); + assertEquals(1, service.getItemRouteProviderServiceMapID(PSM_ID).size()); + assertEquals(1, service.getItemFormProviderServiceMapID(PSM_ID).size()); + } + + @Test + @DisplayName("updateItemIssueConfig should count only the categories that name an issue type") + void updateItemIssueConfig_shouldCountOnlyComplete() { + M_ItemCategory complete = category(31); + complete.setIssueType("FIFO"); + M_ItemCategory incomplete = category(32); + when(itemCategoryRepo.updateIssueConfig(31, "FIFO")).thenReturn(1); + + assertEquals(1, service.updateItemIssueConfig(List.of(complete, incomplete))); + verify(itemCategoryRepo, never()).updateIssueConfig(32, null); + } + + @Test + @DisplayName("updateExpiryAlert should count only the categories that name an alert window") + void updateExpiryAlert_shouldCountOnlyComplete() { + M_ItemCategory complete = category(31); + complete.setAlertBeforeDays(30); + M_ItemCategory incomplete = category(32); + when(itemCategoryRepo.updateExpiryAlert(31, 30)).thenReturn(1); + + assertEquals(1, service.updateExpiryAlert(List.of(complete, incomplete))); + } + + @Test + @DisplayName("createItemCategories should answer the id of the first category it stored") + void createItemCategories_shouldAnswerFirstStoredId() { + when(itemCategoryRepo.saveAll(anyList())).thenReturn(List.of(category(31))); + + assertEquals(31, service.createItemCategories(new ArrayList<>())); + } + + @Test + @DisplayName("createItemCategories should answer zero when nothing was stored") + void createItemCategories_shouldAnswerZeroWhenNothingStored() { + when(itemCategoryRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); + + assertEquals(0, service.createItemCategories(new ArrayList<>())); + } + + @Test + @DisplayName("createItemForms should answer the id of the first form it stored") + void createItemForms_shouldAnswerFirstStoredId() { + M_ItemForm form = new M_ItemForm(); + form.setItemFormID(11); + when(itemFormRepo.saveAll(anyList())).thenReturn(List.of(form)); + + assertEquals(11, service.createItemForms(new ArrayList<>())); + when(itemFormRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); + assertEquals(0, service.createItemForms(new ArrayList<>())); + } + + @Test + @DisplayName("createRoutes should answer the id of the first route it stored") + void createRoutes_shouldAnswerFirstStoredId() { + M_Route route = new M_Route(); + route.setRouteID(21); + when(routeRepo.saveAll(anyList())).thenReturn(List.of(route)); + + assertEquals(21, service.createRoutes(new ArrayList<>())); + when(routeRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); + assertEquals(0, service.createRoutes(new ArrayList<>())); + } + + @Test + @DisplayName("the edit and block calls should each reach their own repository query") + void editAndBlockCalls_shouldReachTheirOwnQuery() { + M_ItemCategory cat = category(31); + cat.setItemCategoryDesc("Drugs"); + cat.setModifiedBy("admin"); + cat.setDeleted(Boolean.TRUE); + M_ItemForm form = new M_ItemForm(); + form.setItemFormID(11); + form.setItemFormDesc("Tablet"); + form.setModifiedBy("admin"); + form.setDeleted(Boolean.TRUE); + M_Route route = new M_Route(); + route.setRouteID(21); + route.setRouteDesc("Oral"); + route.setModifiedBy("admin"); + route.setDeleted(Boolean.TRUE); + when(itemCategoryRepo.updateItemCategoryDetails(31, "Drugs", "admin")).thenReturn(1); + when(itemCategoryRepo.blockItemCategory(31, Boolean.TRUE, "admin")).thenReturn(1); + when(itemFormRepo.updateItemFormDetails(11, "Tablet", "admin")).thenReturn(1); + when(itemFormRepo.blockItemForm(11, Boolean.TRUE, "admin")).thenReturn(1); + when(routeRepo.updateRouteDetails(21, "Oral", "admin")).thenReturn(1); + when(routeRepo.blockRoute(21, Boolean.TRUE, "admin")).thenReturn(1); + + assertEquals(1, service.editItemCategory(cat)); + assertEquals(1, service.blockItemCategory(cat)); + assertEquals(1, service.editItemForm(form)); + assertEquals(1, service.blockItemForm(form)); + assertEquals(1, service.editRoute(route)); + assertEquals(1, service.blockRoute(route)); + } + + @Test + @DisplayName("the code checks should report a code the provider already uses") + void codeChecks_shouldReportUsedCode() { + CodeChecker checker = new CodeChecker(); + checker.setCode("CODE-1"); + checker.setProviderServiceMapID(PSM_ID); + when(itemCategoryRepo.findByItemCategoryCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(category(31))); + when(itemFormRepo.findByItemFormCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(new M_ItemForm())); + when(itemRepo.findByItemCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(new ItemMaster())); + when(routeRepo.findByRouteCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(new M_Route())); + + assertTrue(service.checkCodeCategory(checker)); + assertTrue(service.checkCodeForm(checker)); + assertTrue(service.checkCodeItem(checker)); + assertTrue(service.checkCodeRoute(checker)); + } + + @Test + @DisplayName("the code checks should clear a code nobody uses yet") + void codeChecks_shouldClearFreeCode() { + CodeChecker checker = new CodeChecker(); + checker.setCode("CODE-2"); + checker.setProviderServiceMapID(PSM_ID); + when(itemCategoryRepo.findByItemCategoryCodeAndProviderServiceMapID("CODE-2", PSM_ID)) + .thenReturn(new ArrayList<>()); + when(itemFormRepo.findByItemFormCodeAndProviderServiceMapID("CODE-2", PSM_ID)) + .thenReturn(new ArrayList<>()); + when(itemRepo.findByItemCodeAndProviderServiceMapID("CODE-2", PSM_ID)).thenReturn(new ArrayList<>()); + when(routeRepo.findByRouteCodeAndProviderServiceMapID("CODE-2", PSM_ID)).thenReturn(new ArrayList<>()); + + assertFalse(service.checkCodeCategory(checker)); + assertFalse(service.checkCodeForm(checker)); + assertFalse(service.checkCodeItem(checker)); + assertFalse(service.checkCodeRoute(checker)); + } +} diff --git a/src/test/java/com/iemr/admin/service/itemfacilitymapping/M_itemfacilitymappingImplTest.java b/src/test/java/com/iemr/admin/service/itemfacilitymapping/M_itemfacilitymappingImplTest.java new file mode 100644 index 0000000..6d8cbb5 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/itemfacilitymapping/M_itemfacilitymappingImplTest.java @@ -0,0 +1,195 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.itemfacilitymapping; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.itemfacilitymapping.M_itemfacilitymapping; +import com.iemr.admin.data.itemfacilitymapping.V_fetchItemFacilityMap; +import com.iemr.admin.data.items.ItemInStore; +import com.iemr.admin.repo.stockEntry.ItemStockEntryRepo; +import com.iemr.admin.repository.itemfacilitymapping.M_itemfacilitymappingRepo; +import com.iemr.admin.repository.itemfacilitymapping.V_fetchItemFacilityMapRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The item facility service records which items a store is allowed to hold, and + * answers what each store currently has on its shelves. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("M_itemfacilitymappingImpl Test Suite") +class M_itemfacilitymappingImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer FACILITY_ID = 9001; + private static final Integer MAP_ID = 3301; + private static final Integer ITEM_ID = 501; + + @Mock + private V_fetchItemFacilityMapRepo v_fetchItemFacilityMapRepo; + + @Mock + private M_itemfacilitymappingRepo m_itemfacilitymappingRepo; + + @Mock + private ItemStockEntryRepo itemStockEntryRepo; + + @InjectMocks + private M_itemfacilitymappingImpl service; + + private static M_itemfacilitymapping mapping() { + M_itemfacilitymapping mapping = new M_itemfacilitymapping(); + mapping.setItemStoreMapID(MAP_ID); + mapping.setItemID(ITEM_ID); + mapping.setFacilityID(FACILITY_ID); + mapping.setDeleted(Boolean.FALSE); + return mapping; + } + + @Test + @DisplayName("mapItemtoStore should answer the mappings the repository stored") + void map_shouldAnswerStoredMappings() { + ArrayList stored = new ArrayList<>(List.of(mapping())); + when(m_itemfacilitymappingRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.mapItemtoStore(new ArrayList<>())); + } + + @Test + @DisplayName("editdata and saveEditedItem should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_itemfacilitymapping stored = mapping(); + when(m_itemfacilitymappingRepo.findByItemFacilityMapID(MAP_ID)).thenReturn(stored); + when(m_itemfacilitymappingRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.editdata(MAP_ID)); + assertSame(stored, service.saveEditedItem(stored)); + } + + @Test + @DisplayName("editdata should answer nothing when the mapping is unknown") + void edit_shouldAnswerNothingForUnknownMapping() { + when(m_itemfacilitymappingRepo.findByItemFacilityMapID(-1)).thenReturn(null); + + assertNull(service.editdata(-1)); + } + + @Test + @DisplayName("getsubitemforsubStote should rebuild one item per row the query answers") + void getSubItems_shouldRebuildEachRow() { + when(m_itemfacilitymappingRepo.getItemforSubstore(PSM_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of( + new Object[] { ITEM_ID, "Paracetamol 500", Boolean.FALSE, 7 }))); + + ArrayList items = service.getsubitemforsubStote(PSM_ID, FACILITY_ID); + + assertEquals(1, items.size()); + assertEquals("Paracetamol 500", items.get(0).getItemName()); + assertEquals(ITEM_ID, items.get(0).getItemID()); + } + + @Test + @DisplayName("getsubitemforsubStote should skip a row the query could not fill in") + void getSubItems_shouldSkipIncompleteRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { ITEM_ID, "Paracetamol 500" }); + when(m_itemfacilitymappingRepo.getItemforSubstore(PSM_ID, FACILITY_ID)).thenReturn(rows); + + assertTrue(service.getsubitemforsubStote(PSM_ID, FACILITY_ID).isEmpty()); + } + + @Test + @DisplayName("the mapped item lookups should each reach their own repository query") + void mappedItemLookups_shouldReachTheirOwnQuery() { + ArrayList byProvider = new ArrayList<>(List.of(new V_fetchItemFacilityMap())); + ArrayList byFacility = new ArrayList<>(List.of(new V_fetchItemFacilityMap())); + when(v_fetchItemFacilityMapRepo.getAllFacilityMappedData(PSM_ID)).thenReturn(byProvider); + when(v_fetchItemFacilityMapRepo.getItemMappingsByFacilityAndSubStores(FACILITY_ID)).thenReturn(byFacility); + + assertSame(byProvider, service.getAllFacilityMappedData(PSM_ID)); + assertSame(byFacility, service.getItemMappingsByFacilityID(FACILITY_ID)); + } + + @Test + @DisplayName("getItemMastersFromStoreID should answer what the store holds of each item mapped to it") + void getItemMasters_shouldAnswerWhatStoreHolds() { + when(m_itemfacilitymappingRepo.getItemforStore(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(new Object[] { ITEM_ID, "Paracetamol 500" }))); + when(itemStockEntryRepo.getQuantity(any(Integer[].class), anyInt())) + .thenReturn(new ArrayList<>(List.of( + new Object[] { FACILITY_ID, ITEM_ID, "Paracetamol 500", 250L }))); + + List held = service.getItemMastersFromStoreID(FACILITY_ID); + + assertEquals(1, held.size()); + assertEquals("Paracetamol 500", held.get(0).getItemName()); + assertEquals(250L, held.get(0).getQuantity()); + } + + @Test + @DisplayName("getItemMastersFromStoreID should answer nothing when the store holds none of its items") + void getItemMasters_shouldAnswerNothingWhenStoreEmpty() { + when(m_itemfacilitymappingRepo.getItemforStore(FACILITY_ID)).thenReturn(new ArrayList<>()); + when(itemStockEntryRepo.getQuantity(any(Integer[].class), anyInt())).thenReturn(new ArrayList<>()); + + assertTrue(service.getItemMastersFromStoreID(FACILITY_ID).isEmpty()); + } + + @Test + @DisplayName("deleteItemStoreMapping should report how many mappings the retirement touched") + void delete_shouldReportRowsTouched() { + M_itemfacilitymapping request = mapping(); + request.setDeleted(Boolean.TRUE); + when(m_itemfacilitymappingRepo.updateDeleteMap(MAP_ID, Boolean.TRUE)).thenReturn(1); + + assertEquals(1, service.deleteItemStoreMapping(request)); + } + + @Test + @DisplayName("deleteItemStoreMapping should report nothing touched when the mapping is unknown") + void delete_shouldReportNothingTouchedForUnknownMapping() { + M_itemfacilitymapping request = new M_itemfacilitymapping(); + request.setItemStoreMapID(-1); + when(m_itemfacilitymappingRepo.updateDeleteMap(-1, null)).thenReturn(0); + + assertEquals(0, service.deleteItemStoreMapping(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/locationmaster/LocationMasterServiceImplTest.java b/src/test/java/com/iemr/admin/service/locationmaster/LocationMasterServiceImplTest.java new file mode 100644 index 0000000..bdc8b14 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/locationmaster/LocationMasterServiceImplTest.java @@ -0,0 +1,180 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.locationmaster; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.locationmaster.M_ProviderServiceAddMapping; +import com.iemr.admin.data.locationmaster.Showofficedetails; +import com.iemr.admin.data.locationmaster.StateServiceMapping1; +import com.iemr.admin.repo.locationmaster.LocationMasterRepo; +import com.iemr.admin.repo.locationmaster.MdistrictRepo; +import com.iemr.admin.repo.locationmaster.M_ProviderServiceAddMappingRepo; +import com.iemr.admin.repo.locationmaster.ShowofficedetailsRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The location service reads a provider's office addresses out of several + * differently shaped queries and reshapes them into one carrier. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("LocationMasterServiceImpl Test Suite") +class LocationMasterServiceImplTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer PSM_ID = 4001; + + @Mock + private ShowofficedetailsRepo showofficedetailsRepo; + + @Mock + private MdistrictRepo mdistricRepo; + + @Mock + private M_ProviderServiceAddMappingRepo m_ProviderServiceAddMappingRepo; + + @Mock + private LocationMasterRepo locationMasterRepo; + + @InjectMocks + private LocationMasterServiceImpl service; + + @Test + @DisplayName("getStateByServiceProviderId should skip a row the query could not fill") + void getStateByServiceProviderId_shouldSkipUnfillableRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 29, "Karnataka", 1, PSM_ID }); + when(locationMasterRepo.getStateByServiceProviderId(PROVIDER_ID)).thenReturn(rows); + + assertEquals(1, service.getStateByServiceProviderId(PROVIDER_ID).size()); + } + + @Test + @DisplayName("getServiceByServiceProviderIdAndStateId should rebuild one mapping per row") + void getServiceByServiceProviderIdAndStateId_shouldRebuildEachRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 3, PSM_ID, "Tele Medicine" }); + when(locationMasterRepo.getServiceByServiceProviderIdAndStateId(PROVIDER_ID, 29)).thenReturn(rows); + + assertEquals(1, service.getServiceByServiceProviderIdAndStateId(PROVIDER_ID, 29).size()); + } + + @Test + @DisplayName("getStatesByServiceId should rebuild one mapping per row") + void getStatesByServiceId_shouldRebuildEachRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 29, "Karnataka", PSM_ID }); + when(locationMasterRepo.getStatesByServiceId(3, PROVIDER_ID)).thenReturn(rows); + + assertEquals(1, service.getStatesByServiceId(3, PROVIDER_ID).size()); + } + + @Test + @DisplayName("getAllDistrictByStateId should rebuild each district into a plain carrier") + void getAllDistrictByStateId_shouldRebuildEachDistrict() { + M_District stored = new M_District(); + stored.setDistrictID(301); + stored.setDistrictName("Bengaluru Urban"); + when(mdistricRepo.getAllDistrictByStateId(29)).thenReturn(new ArrayList<>(List.of(stored))); + + ArrayList districts = service.getAllDistrictByStateId(29); + + assertEquals(1, districts.size()); + assertEquals("Bengaluru Urban", districts.get(0).getDistrictName()); + } + + @Test + @DisplayName("getlocationByMapid1 should gather the offices of every mapping the caller lists") + void getlocationByMapid1_shouldGatherAcrossMappings() { + Showofficedetails office = new Showofficedetails(); + when(showofficedetailsRepo.getlocationByMapid1(4001)).thenReturn(new ArrayList<>(List.of(office))); + when(showofficedetailsRepo.getlocationByMapid1(4002)).thenReturn(new ArrayList<>(List.of(office))); + + assertEquals(2, service.getlocationByMapid1(new ArrayList<>(List.of(4001, 4002))).size()); + } + + @Test + @DisplayName("getOfficeName should gather the office of every mapping the caller lists") + void getOfficeName_shouldGatherAcrossMappings() { + Showofficedetails request = new Showofficedetails(); + request.setProviderServiceMapID(PSM_ID); + when(showofficedetailsRepo.getOfficeName(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(new Showofficedetails()))); + + assertEquals(1, service.getOfficeName(new ArrayList<>(List.of(request))).size()); + } + + @Test + @DisplayName("the remaining calls should each reach their own repository query") + void remainingCalls_shouldReachTheirOwnQuery() { + M_ProviderServiceAddMapping address = new M_ProviderServiceAddMapping(); + ArrayList addresses = new ArrayList<>(List.of(address)); + ArrayList offices = new ArrayList<>(List.of(new Showofficedetails())); + StateServiceMapping1 mapping = new StateServiceMapping1(PSM_ID); + ArrayList mappings = new ArrayList<>(List.of(mapping)); + when(m_ProviderServiceAddMappingRepo.save(address)).thenReturn(address); + when(m_ProviderServiceAddMappingRepo.saveAll(anyList())).thenReturn(addresses); + when(m_ProviderServiceAddMappingRepo.editData(51)).thenReturn(address); + when(m_ProviderServiceAddMappingRepo.getlocationByMapid(PSM_ID)).thenReturn(addresses); + when(showofficedetailsRepo.getAlldata()).thenReturn(offices); + when(showofficedetailsRepo.getlocationByMapid(PSM_ID)).thenReturn(offices); + when(showofficedetailsRepo.getlocationByMapid3(PSM_ID, 301)).thenReturn(offices); + when(locationMasterRepo.getProviderServiceMapID(PROVIDER_ID, 29, 3)).thenReturn(mapping); + when(locationMasterRepo.getAllByMapId2(PROVIDER_ID, 29, 3)).thenReturn(mappings); + when(locationMasterRepo.getAllByMapId3(PROVIDER_ID, 3)).thenReturn(mappings); + when(locationMasterRepo.getLocationByServiceID(PROVIDER_ID, 3)).thenReturn(mappings); + when(locationMasterRepo.getLocationByStateID(PROVIDER_ID, 29)).thenReturn(mappings); + + assertSame(address, service.addlocation(address)); + assertSame(addresses, service.addlocation(new ArrayList<>())); + assertSame(address, service.editData(51)); + assertSame(address, service.saveEditData(address)); + assertSame(addresses, service.getlocationByMapid(PSM_ID)); + assertSame(offices, service.getAlldata()); + assertSame(offices, service.getlocationByMapid2(PSM_ID)); + assertSame(offices, service.getlocationByMapid4(PSM_ID, 301)); + assertSame(mapping, service.getAllByMapId(PROVIDER_ID, 29, 3)); + assertSame(mappings, service.getAllByMapId2(PROVIDER_ID, 29, 3)); + assertSame(mappings, service.getAllByMapId3(PROVIDER_ID, 3)); + assertSame(mappings, service.getLocationByServiceId(PROVIDER_ID, 3)); + assertSame(mappings, service.getLocationBySateID(PROVIDER_ID, 29)); + } +} diff --git a/src/test/java/com/iemr/admin/service/manufacturer/ManufacturerServiceImplTest.java b/src/test/java/com/iemr/admin/service/manufacturer/ManufacturerServiceImplTest.java new file mode 100644 index 0000000..a473821 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/manufacturer/ManufacturerServiceImplTest.java @@ -0,0 +1,113 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.manufacturer; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.manufacturer.M_Manufacturer; +import com.iemr.admin.repo.manufacturer.ManufacturerRepo; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The manufacturer service is thin over its repository, but it answers nothing rather than an empty batch. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ManufacturerServiceImpl Test Suite") +class ManufacturerServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private ManufacturerRepo manufacturerRepo; + + @InjectMocks + private ManufacturerServiceImpl service; + + @Test + @DisplayName("createManufacturer should answer the records it stored") + void create_shouldAnswerStoredRecords() { + ArrayList stored = new ArrayList<>(List.of(new M_Manufacturer())); + when(manufacturerRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createManufacturer(new ArrayList<>())); + } + + @Test + @DisplayName("createManufacturer should answer nothing when the store took nothing") + void create_shouldAnswerNothingWhenNothingStored() { + when(manufacturerRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createManufacturer(new ArrayList<>())); + } + + @Test + @DisplayName("the lookups should each reach their own repository query") + void lookups_shouldReachTheirOwnQuery() { + M_Manufacturer record = new M_Manufacturer(); + ArrayList records = new ArrayList<>(List.of(record)); + when(manufacturerRepo.getManufacturerData(PSM_ID)).thenReturn(records); + when(manufacturerRepo.getEditData(RECORD_ID)).thenReturn(record); + when(manufacturerRepo.save(record)).thenReturn(record); + + assertSame(records, service.createManufacturer(PSM_ID)); + assertSame(record, service.editManufacturer(RECORD_ID)); + assertSame(record, service.saveEditedData(record)); + } + + @Test + @DisplayName("checkManufacturerCode should report a code the provider already uses") + void check_shouldReportUsedCode() { + M_Manufacturer request = new M_Manufacturer(); + request.setManufacturerCode("C-1"); + request.setProviderServiceMapID(PSM_ID); + when(manufacturerRepo.findByManufacturerCodeAndProviderServiceMapID("C-1", PSM_ID)).thenReturn(List.of(new M_Manufacturer())); + + assertTrue(service.checkManufacturerCode(request)); + } + + @Test + @DisplayName("checkManufacturerCode should clear a code nobody uses yet") + void check_shouldClearFreeCode() { + M_Manufacturer request = new M_Manufacturer(); + request.setManufacturerCode("C-2"); + request.setProviderServiceMapID(PSM_ID); + when(manufacturerRepo.findByManufacturerCodeAndProviderServiceMapID("C-2", PSM_ID)).thenReturn(new ArrayList()); + + assertFalse(service.checkManufacturerCode(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/nodalemailconfig/NodalConfigServiceImplTest.java b/src/test/java/com/iemr/admin/service/nodalemailconfig/NodalConfigServiceImplTest.java new file mode 100644 index 0000000..68d6667 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/nodalemailconfig/NodalConfigServiceImplTest.java @@ -0,0 +1,190 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.nodalemailconfig; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.emailconfig.AuthorityEmail; +import com.iemr.admin.mapper.emailconfig.InstituteEmailConfigMapper; +import com.iemr.admin.model.emailconfig.NodalEmailRequest; +import com.iemr.admin.model.emailconfig.NodalEmailResponse; +import com.iemr.admin.model.emailconfig.CreateNodalEmailRequestModel; +import com.iemr.admin.model.emailconfig.UpdateNodalEmailRequest; +import com.iemr.admin.repository.emailconfig.InstituteEmailRepo; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The nodal config service keeps the nodal officer mailboxes a complaint is + * escalated to, narrowed by whichever parts of the location the caller names. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NodalConfigServiceImpl Test Suite") +class NodalConfigServiceImplTest { + + @Mock + private EntityManager entityManager; + + @Mock + private InstituteEmailRepo instituteRepo; + + @Mock + private InstituteEmailConfigMapper instituteEmailConfigMapper; + + @InjectMocks + private NodalConfigServiceImpl service; + + private CriteriaQuery query; + private TypedQuery typedQuery; + + @SuppressWarnings("unchecked") + @BeforeEach + @DisplayName("Stand in for the criteria query the service builds by hand") + void setUp() { + CriteriaBuilder builder = mock(CriteriaBuilder.class); + query = mock(CriteriaQuery.class); + Root root = mock(Root.class); + typedQuery = mock(TypedQuery.class); + + when(entityManager.getCriteriaBuilder()).thenReturn(builder); + when(builder.createQuery(AuthorityEmail.class)).thenReturn(query); + when(query.from(AuthorityEmail.class)).thenReturn(root); + when(query.select(any())).thenReturn(query); + when(query.where(any(Predicate[].class))).thenReturn(query); + when(query.orderBy(any(jakarta.persistence.criteria.Order[].class))).thenReturn(query); + when(root.get(anyString())).thenReturn(mock(Path.class)); + when(builder.equal(any(), any())).thenReturn(mock(Predicate.class)); + when(entityManager.createQuery(query)).thenReturn(typedQuery); + } + + private static NodalEmailRequest fullyNarrowedRequest() { + NodalEmailRequest request = new NodalEmailRequest(); + request.setAuthorityEmailID(1); + request.setDeleted(false); + request.setDistrictID(301); + request.setDistrictBranchMappingID(30111); + request.setBlockID(3011); + request.setProviderServiceMapID(4001); + request.setStateID(29); + request.setMobileNo("9000000001"); + return request; + } + + @Test + @DisplayName("getAllEmailConfigs should answer the mailboxes the query found, as the screens read them") + void getAll_shouldAnswerFoundMailboxes() { + List found = List.of(new AuthorityEmail()); + List published = List.of(new NodalEmailResponse()); + when(typedQuery.getResultList()).thenReturn(found); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponses(found)).thenReturn(published); + + assertSame(published, service.getAllNodalEmailConfigs(fullyNarrowedRequest())); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow the query by every detail the caller named") + void getAll_shouldNarrowByEveryNamedDetail() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllNodalEmailConfigs(fullyNarrowedRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(8, captor.getValue().length, "one narrowing per detail the caller named"); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow by nothing when the caller names nothing") + void getAll_shouldNarrowByNothingForEmptyRequest() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllNodalEmailConfigs(new NodalEmailRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(0, captor.getValue().length); + } + + @Test + @DisplayName("saveEmailConfigs should store one mailbox per request and answer each as stored") + void save_shouldStoreEachRequestedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + when(instituteEmailConfigMapper.createRequestToInstituteEmailConfig(anyList())) + .thenReturn(List.of(stored, stored)); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultInstType(stored)) + .thenReturn(new NodalEmailResponse()); + + assertEquals(2, service.saveNodalEmailConfigs(List.of(new CreateNodalEmailRequestModel())).size()); + } + + @Test + @DisplayName("saveEmailConfigs should store nothing when the caller asks for nothing") + void save_shouldStoreNothingForEmptyRequest() { + when(instituteEmailConfigMapper.createRequestToInstituteEmailConfig(anyList())) + .thenReturn(new ArrayList<>()); + + assertTrue(service.saveNodalEmailConfigs(new ArrayList<>()).isEmpty()); + } + + @Test + @DisplayName("updateEmailConfigs should answer the mailbox as it stands after the change") + void update_shouldAnswerChangedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + NodalEmailResponse published = new NodalEmailResponse(); + when(instituteEmailConfigMapper.updateRequestToInstituteNodalEmailConf(any(UpdateNodalEmailRequest.class))) + .thenReturn(stored); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultToInstTypeNodalEmailResponse(stored)).thenReturn(published); + + assertSame(published, service.updateNodalEmailConfigs(new UpdateNodalEmailRequest())); + } +} diff --git a/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceServiceImplTest.java b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceServiceImplTest.java new file mode 100644 index 0000000..8065453 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceServiceImplTest.java @@ -0,0 +1,184 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.parkingPlace; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.parkingPlace.M_Parkingplace; +import com.iemr.admin.data.provideronboard.M_ProviderServiceMapping; +import com.iemr.admin.data.zonemaster.M_Zone; +import com.iemr.admin.repository.parkingPlace.ParkingPlaceRepository; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The parking place service turns the flat rows the reporting queries answer + * back into parking places, and treats an omitted location filter as "any". + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ParkingPlaceServiceImpl Test Suite") +class ParkingPlaceServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PARKING_PLACE_ID = 31; + + @Mock + private ParkingPlaceRepository parkingPlaceRepository; + + @InjectMocks + private ParkingPlaceServiceImpl service; + + private static Object[] reportingRow() { + return new Object[] { PARKING_PLACE_ID, "Hosur parking", "Near the bus stand", "Hosur Road", PSM_ID, + Boolean.FALSE, 1, "India", 29, "Karnataka", 301, "Bengaluru Urban", 3011, "Anekal", 30111, + "Attibele", new M_ProviderServiceMapping(), 5, "104 Helpline" }; + } + + @Test + @DisplayName("getAvailableParkingPlaces should rebuild one parking place per row the query answers") + void getAvailable_shouldRebuildEachRow() { + when(parkingPlaceRepository.getAvailableParkingPlaces("29", "301", PSM_ID)) + .thenReturn(List.of(reportingRow())); + + ArrayList places = service.getAvailableParkingPlaces(29, 301, PSM_ID); + + assertEquals(1, places.size()); + assertEquals("Hosur parking", places.get(0).getParkingPlaceName()); + assertEquals("Karnataka", places.get(0).getStateName()); + assertEquals("Hosur Road", places.get(0).getAreaHQAddress()); + } + + @Test + @DisplayName("getAvailableParkingPlaces should match any state or district the caller leaves out") + void getAvailable_shouldWildcardOmittedFilters() { + when(parkingPlaceRepository.getAvailableParkingPlaces("%%", "%%", PSM_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.getAvailableParkingPlaces(null, null, PSM_ID).isEmpty()); + verify(parkingPlaceRepository).getAvailableParkingPlaces("%%", "%%", PSM_ID); + } + + @Test + @DisplayName("saveParkingPlace should answer the parking places the repository stored") + void save_shouldAnswerStoredPlaces() { + ArrayList stored = new ArrayList<>(List.of(new M_Parkingplace())); + when(parkingPlaceRepository.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.saveParkingPlace(new ArrayList<>())); + } + + @Test + @DisplayName("updateParkingPlaceStatus should report how many rows the retirement touched") + void updateStatus_shouldReportRowsTouched() { + M_Parkingplace request = new M_Parkingplace(); + request.setParkingPlaceID(PARKING_PLACE_ID); + request.setDeleted(Boolean.TRUE); + request.setModifiedBy("admin"); + when(parkingPlaceRepository.updateParkingPlaceStatus(PARKING_PLACE_ID, Boolean.TRUE, "admin")).thenReturn(1); + + assertEquals(1, service.updateParkingPlaceStatus(request)); + } + + @Test + @DisplayName("updateParkingPlaceStatus should report nothing touched when the parking place is unknown") + void updateStatus_shouldReportNothingTouchedForUnknownPlace() { + M_Parkingplace request = new M_Parkingplace(); + request.setParkingPlaceID(-1); + when(parkingPlaceRepository.updateParkingPlaceStatus(-1, null, null)).thenReturn(0); + + assertEquals(0, service.updateParkingPlaceStatus(request)); + } + + @Test + @DisplayName("the single record lookups should each reach their own repository query") + void singleRecordLookups_shouldReachTheirOwnQuery() { + M_Parkingplace stored = new M_Parkingplace(); + List byProvider = List.of(stored); + when(parkingPlaceRepository.getParkingPlaceById(PARKING_PLACE_ID)).thenReturn(stored); + when(parkingPlaceRepository.save(stored)).thenReturn(stored); + when(parkingPlaceRepository.findByProviderServiceMapID(PSM_ID)).thenReturn(byProvider); + + assertSame(stored, service.getParkingPlaceByID(PARKING_PLACE_ID)); + assertSame(stored, service.updateParkingPlaceData(stored)); + assertSame(byProvider, service.getParkingPlaces(PSM_ID)); + } + + @Test + @DisplayName("getSubDistrict should answer the taluks the parking place covers") + void getSubDistrict_shouldAnswerCoveredTaluks() { + when(parkingPlaceRepository.getSubDistrict(PARKING_PLACE_ID)) + .thenReturn(List.of(new Object[] { PARKING_PLACE_ID, 3011, "Anekal" })); + + List taluks = service.getSubDistrict(PARKING_PLACE_ID); + + assertEquals(1, taluks.size()); + assertEquals("Anekal", taluks.get(0).getBlockName()); + assertEquals(3011, taluks.get(0).getDistrictBlockID()); + } + + @Test + @DisplayName("getSubDistrict should answer nothing when the parking place covers no taluk") + void getSubDistrict_shouldAnswerNothingWhenNoneCovered() { + when(parkingPlaceRepository.getSubDistrict(PARKING_PLACE_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.getSubDistrict(PARKING_PLACE_ID).isEmpty()); + } + + @Test + @DisplayName("getAvailableParkingPlacesbyZoneID should name the zone on each parking place it answers") + void getAvailableByZone_shouldNameTheZone() { + M_Parkingplace place = new M_Parkingplace(); + place.setParkingPlaceID(PARKING_PLACE_ID); + M_Zone zone = new M_Zone(); + zone.setZoneName("South zone"); + when(parkingPlaceRepository.getAvailableParkingPlacesbyzoneid(9, PSM_ID)) + .thenReturn(List.of(new Object[] { place, zone })); + + ArrayList places = service.getAvailableParkingPlacesbyZoneID(9, PSM_ID); + + assertEquals(1, places.size()); + assertEquals("South zone", places.get(0).getZoneName()); + } + + @Test + @DisplayName("getAvailableParkingPlacesbyZoneID should answer nothing when the zone holds no parking place") + void getAvailableByZone_shouldAnswerNothingForEmptyZone() { + when(parkingPlaceRepository.getAvailableParkingPlacesbyzoneid(9, PSM_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.getAvailableParkingPlacesbyZoneID(9, PSM_ID).isEmpty()); + } +} diff --git a/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceTalukMappingServiceImplTest.java b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceTalukMappingServiceImplTest.java new file mode 100644 index 0000000..24df775 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceTalukMappingServiceImplTest.java @@ -0,0 +1,151 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.parkingPlace; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.DistrictBlock; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMapping; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMappingTO; +import com.iemr.admin.mapper.parkingplacetalukmapping.ParkingPlaceTalukMappingMapper; +import com.iemr.admin.repo.locationmaster.DistrictBlockRepo; +import com.iemr.admin.repository.parkingPlace.ParkingPlaceTalukMappingRepository; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The taluk mapping service records which taluks a parking place covers, and + * offers the remaining taluks of a district as the candidates for a new one. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ParkingPlaceTalukMappingServiceImpl Test Suite") +class ParkingPlaceTalukMappingServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PARKING_PLACE_ID = 31; + private static final Integer DISTRICT_ID = 301; + + @Mock + private ParkingPlaceTalukMappingRepository parkingPlaceTalukMappingRepository; + + @Mock + private DistrictBlockRepo districtBlockRepo; + + @Mock + private ParkingPlaceTalukMappingMapper parkingPlaceTalukMappingMapper; + + @InjectMocks + private ParkingPlaceTalukMappingServiceImpl service; + + private static ParkingplaceTalukMapping mapping() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(7001); + mapping.setParkingPlaceID(PARKING_PLACE_ID); + mapping.setDistrictID(DISTRICT_ID); + mapping.setDistrictBlockID(3011); + mapping.setProviderServiceMapID(PSM_ID); + return mapping; + } + + @Test + @DisplayName("saveParkingPlaceTalukMapping should answer the mappings the repository stored") + void save_shouldAnswerStoredMappings() { + ArrayList stored = new ArrayList<>(List.of(mapping())); + when(parkingPlaceTalukMappingRepository.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.saveParkingPlaceTalukMapping(new ArrayList<>())); + } + + @Test + @DisplayName("updateParkingPlaceTalukMapping and findbyID should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + ParkingplaceTalukMapping stored = mapping(); + when(parkingPlaceTalukMappingRepository.save(stored)).thenReturn(stored); + when(parkingPlaceTalukMappingRepository.findByPpSubDistrictMapID(7001)).thenReturn(stored); + + assertSame(stored, service.updateParkingPlaceTalukMapping(stored)); + assertSame(stored, service.findbyID(7001)); + } + + @Test + @DisplayName("findbyProviderservicemapid should answer the taluks the parking place covers") + void findbyProviderservicemapid_shouldAnswerCoveredTaluks() { + List rows = List.of(mapping()); + List published = List.of(new ParkingplaceTalukMappingTO()); + when(parkingPlaceTalukMappingRepository.findByParkingPlaceID(PARKING_PLACE_ID)).thenReturn(rows); + when(parkingPlaceTalukMappingMapper.getParkingplaceTalukMappingMapList(rows)).thenReturn(published); + + assertSame(published, service.findbyProviderservicemapid(mapping())); + } + + @Test + @DisplayName("findbyParkingplaceAndDistrictID should narrow the mappings to the district the caller names") + void findbyParkingplaceAndDistrictID_shouldNarrowToDistrict() { + List rows = List.of(mapping()); + List published = List.of(new ParkingplaceTalukMappingTO()); + when(parkingPlaceTalukMappingRepository + .findByParkingPlaceIDAndDistrictIDOrderByM_DistrictDistrictNameAsc(PARKING_PLACE_ID, DISTRICT_ID)) + .thenReturn(rows); + when(parkingPlaceTalukMappingMapper.getParkingplaceTalukMappingMapList(rows)).thenReturn(published); + + assertSame(published, service.findbyParkingplaceAndDistrictID(mapping())); + } + + @Test + @DisplayName("getunmappedtaluk should exclude the taluks already covered when there are any") + void getunmappedtaluk_shouldExcludeCoveredTaluks() { + List covered = List.of(3011); + List remaining = List.of(new DistrictBlock(3012, "Hoskote")); + when(parkingPlaceTalukMappingRepository.finbyDistrictID(DISTRICT_ID, PSM_ID)).thenReturn(covered); + when(districtBlockRepo.findunmapped(covered, DISTRICT_ID)).thenReturn(remaining); + + assertSame(remaining, service.getunmappedtaluk(DISTRICT_ID, PSM_ID)); + verify(districtBlockRepo, never()).findall(anyInt()); + } + + @Test + @DisplayName("getunmappedtaluk should offer every taluk of the district when none is covered yet") + void getunmappedtaluk_shouldOfferEveryTalukWhenNoneCovered() { + List all = List.of(new DistrictBlock(3011, "Anekal"), new DistrictBlock(3012, "Hoskote")); + when(parkingPlaceTalukMappingRepository.finbyDistrictID(DISTRICT_ID, PSM_ID)).thenReturn(new ArrayList<>()); + when(districtBlockRepo.findall(DISTRICT_ID)).thenReturn(all); + + assertSame(all, service.getunmappedtaluk(DISTRICT_ID, PSM_ID)); + verify(districtBlockRepo, never()).findunmapped(anyList(), anyInt()); + } +} diff --git a/src/test/java/com/iemr/admin/service/pharmacologicalcategory/PharmacologicalcategoryServiceImplTest.java b/src/test/java/com/iemr/admin/service/pharmacologicalcategory/PharmacologicalcategoryServiceImplTest.java new file mode 100644 index 0000000..9286283 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/pharmacologicalcategory/PharmacologicalcategoryServiceImplTest.java @@ -0,0 +1,113 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.pharmacologicalcategory; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.pharmacologicalcategory.M_Pharmacologicalcategory; +import com.iemr.admin.repo.pharmacologicalcategory.PharmacologicalcategoryRepo; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The pharmacological category service is thin over its repository, but it answers nothing rather than an empty batch. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PharmacologicalcategoryServiceImpl Test Suite") +class PharmacologicalcategoryServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private PharmacologicalcategoryRepo pharmacologicalcategoryRepo; + + @InjectMocks + private PharmacologicalcategoryServiceImpl service; + + @Test + @DisplayName("createPharmacologicalcategory should answer the records it stored") + void create_shouldAnswerStoredRecords() { + ArrayList stored = new ArrayList<>(List.of(new M_Pharmacologicalcategory())); + when(pharmacologicalcategoryRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createPharmacologicalcategory(new ArrayList<>())); + } + + @Test + @DisplayName("createPharmacologicalcategory should answer nothing when the store took nothing") + void create_shouldAnswerNothingWhenNothingStored() { + when(pharmacologicalcategoryRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createPharmacologicalcategory(new ArrayList<>())); + } + + @Test + @DisplayName("the lookups should each reach their own repository query") + void lookups_shouldReachTheirOwnQuery() { + M_Pharmacologicalcategory record = new M_Pharmacologicalcategory(); + ArrayList records = new ArrayList<>(List.of(record)); + when(pharmacologicalcategoryRepo.getPhormacologicalData(PSM_ID)).thenReturn(records); + when(pharmacologicalcategoryRepo.editPhamacologicalData(RECORD_ID)).thenReturn(record); + when(pharmacologicalcategoryRepo.save(record)).thenReturn(record); + + assertSame(records, service.getPharmacologicalcategory(PSM_ID)); + assertSame(record, service.editPharmacologicalcategory(RECORD_ID)); + assertSame(record, service.saveEditedPharData(record)); + } + + @Test + @DisplayName("checkPharmacologicalcategoryCode should report a code the provider already uses") + void check_shouldReportUsedCode() { + M_Pharmacologicalcategory request = new M_Pharmacologicalcategory(); + request.setPharmCategoryCode("C-1"); + request.setProviderServiceMapID(PSM_ID); + when(pharmacologicalcategoryRepo.findByPharmCategoryCodeAndProviderServiceMapID("C-1", PSM_ID)).thenReturn(List.of(new M_Pharmacologicalcategory())); + + assertTrue(service.checkPharmacologicalcategoryCode(request)); + } + + @Test + @DisplayName("checkPharmacologicalcategoryCode should clear a code nobody uses yet") + void check_shouldClearFreeCode() { + M_Pharmacologicalcategory request = new M_Pharmacologicalcategory(); + request.setPharmCategoryCode("C-2"); + request.setProviderServiceMapID(PSM_ID); + when(pharmacologicalcategoryRepo.findByPharmCategoryCodeAndProviderServiceMapID("C-2", PSM_ID)).thenReturn(new ArrayList()); + + assertFalse(service.checkPharmacologicalcategoryCode(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/provideronboard/ProviderOnBoardServicesTest.java b/src/test/java/com/iemr/admin/service/provideronboard/ProviderOnBoardServicesTest.java new file mode 100644 index 0000000..516cd48 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/provideronboard/ProviderOnBoardServicesTest.java @@ -0,0 +1,801 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.provideronboard; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.admin.data.provideronboard.M_104druggroup; +import com.iemr.admin.data.provideronboard.M_104drugmapping; +import com.iemr.admin.data.provideronboard.M_104drugmaster; +import com.iemr.admin.data.provideronboard.M_Calltype; +import com.iemr.admin.data.provideronboard.M_Category; +import com.iemr.admin.data.provideronboard.M_Feedbacknature; +import com.iemr.admin.data.provideronboard.M_Feedbacktype; +import com.iemr.admin.data.provideronboard.M_Institutedirectory; +import com.iemr.admin.data.provideronboard.M_Institutedirectorymapping; +import com.iemr.admin.data.provideronboard.M_Institutesubdirectory; +import com.iemr.admin.data.provideronboard.M_Institution; +import com.iemr.admin.data.provideronboard.M_Institutiontype; +import com.iemr.admin.data.provideronboard.M_ProviderServiceMapping; +import com.iemr.admin.data.provideronboard.M_ServiceMaster; +import com.iemr.admin.data.provideronboard.M_Severity; +import com.iemr.admin.data.provideronboard.M_Subcategory; +import com.iemr.admin.data.provideronboard.M_Subservice; +import com.iemr.admin.data.provideronboard.M_SubservicemasterPA; +import com.iemr.admin.data.provideronboard.M_UserservicerolemappingForRole; +import com.iemr.admin.data.provideronboard.ServiceProvider_Model; +import com.iemr.admin.data.provideronboard.V_Showprovideradmin; +import com.iemr.admin.data.provideronboard.V_Showsubcategory; +import com.iemr.admin.exceptionhandler.DataNotFound; +import com.iemr.admin.repository.provideronboard.CalltypeRepo; +import com.iemr.admin.repository.provideronboard.CategoryRepo; +import com.iemr.admin.repository.provideronboard.DrugGroupRepo; +import com.iemr.admin.repository.provideronboard.DrugMappingRepo; +import com.iemr.admin.repository.provideronboard.DrugMasterRepo; +import com.iemr.admin.repository.provideronboard.IemrServiceRepository1; +import com.iemr.admin.repository.provideronboard.InstuteDirectoryRepo; +import com.iemr.admin.repository.provideronboard.M_FeedbacknatureRepo; +import com.iemr.admin.repository.provideronboard.M_FeedbacktypeRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutedirectorymappingRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutesubdirectoryRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutionRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutiontypeRepo; +import com.iemr.admin.repository.provideronboard.M_ProviderServiceMappingRepo; +import com.iemr.admin.repository.provideronboard.M_ServiceMasterRepo; +import com.iemr.admin.repository.provideronboard.M_SeverityRepo; +import com.iemr.admin.repository.provideronboard.M_SubservicemasterPArepo; +import com.iemr.admin.repository.provideronboard.M_UserservicerolemappingForRoleRepo; +import com.iemr.admin.repository.provideronboard.SubCategoryRepo; +import com.iemr.admin.repository.provideronboard.SubserviceMasterRepo; +import com.iemr.admin.repository.provideronboard.V_ShowprovideradminRepo; +import com.iemr.admin.repository.provideronboard.V_ShowsubcategoryRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The onboarding services are thin over their repositories, but a few of them + * choose which query to run from what the caller left blank - and those choices + * decide what an operator sees on screen. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Provider onboarding service Test Suite") +class ProviderOnBoardServicesTest { + + private static final Integer PSM_ID = 4001; + + @Mock + private CalltypeRepo calltypeRepo; + + @InjectMocks + private CalltypeServiceImpl calltypeService; + + @Mock + private V_ShowsubcategoryRepo v_ShowsubcategoryRepo; + + @Mock + private CategoryRepo categoryRepo; + + @Mock + private SubCategoryRepo subCategoryRepo; + + @InjectMocks + private CategoryMasterImpl categoryService; + + @Mock + private DrugGroupRepo drugGroupRepo; + + @Mock + private DrugMasterRepo drugMasterRepo; + + @Mock + private DrugMappingRepo drugMappingRepo; + + @InjectMocks + private DrugMasterImpl drugService; + + @Mock + private InstuteDirectoryRepo instuteDirectoryRepo; + + @InjectMocks + private InstuteDirectoryServiceImpl directoryService; + + @Mock + private M_FeedbacknatureRepo m_FeedbacknatureRepo; + + @InjectMocks + private M_FeedbacknatureImpl feedbackNatureService; + + @Mock + private M_FeedbacktypeRepo m_FeedbacktypeRepo; + + @InjectMocks + private M_FeedbacktypeImpl feedbackTypeService; + + @Mock + private M_InstitutedirectorymappingRepo m_InstitutedirectorymappingRepo; + + @InjectMocks + private M_InstitutedirectorymappingImpl directoryMappingService; + + @Mock + private M_InstitutesubdirectoryRepo m_InstitutesubdirectoryRepo; + + @InjectMocks + private M_InstitutesubdirectoryImpl subDirectoryService; + + @Mock + private M_InstitutionRepo m_InstitutionRepo; + + @InjectMocks + private M_InstitutionImpl institutionService; + + @Mock + private M_InstitutiontypeRepo m_InstitutiontypeRepo; + + @InjectMocks + private M_InstitutiontypeImpl instituteTypeService; + + @Mock + private M_ServiceMasterRepo mservicemasteRepo; + + @InjectMocks + private M_ServiceMasterImpl serviceMasterService; + + @Mock + private M_SeverityRepo m_ServerityRepo; + + @InjectMocks + private M_SeverityImpl severityService; + + @Mock + private M_SubservicemasterPArepo m_SubservicemasterPArepo; + + @Mock + private SubserviceMasterRepo subserviceMasterRepo; + + @InjectMocks + private SubserviceImpl subServiceService; + + @Mock + private V_ShowprovideradminRepo v_ShowprovideradminRepo; + + @Mock + private M_UserservicerolemappingForRoleRepo m_UserservicerolemappingForRoleRepo; + + @Mock + private IemrServiceRepository1 iemrServiceRepository1; + + @Mock + private M_ProviderServiceMappingRepo m_ProviderServiceMappingRepo; + + @InjectMocks + private ServiceProvider_ServiceImpl providerService; + + @Nested + @DisplayName("CalltypeServiceImpl") + class CallTypeServiceTests { + + @Test + @DisplayName("should hand each call to its own repository query") + void callType_shouldReachTheirOwnQuery() { + M_Calltype callType = new M_Calltype(); + ArrayList stored = new ArrayList<>(List.of(callType)); + when(calltypeRepo.saveAll(anyList())).thenReturn(stored); + when(calltypeRepo.updateCallType(51)).thenReturn(callType); + when(calltypeRepo.save(callType)).thenReturn(callType); + when(calltypeRepo.getCalltypeData(PSM_ID)).thenReturn(stored); + + assertSame(stored, calltypeService.saveCallList(new ArrayList<>())); + assertSame(stored, calltypeService.createCalltype(new ArrayList<>())); + assertSame(callType, calltypeService.updateCallType(51)); + assertSame(callType, calltypeService.saveupdatedData(callType)); + assertSame(stored, calltypeService.getCalltypeData(PSM_ID)); + } + } + + @Nested + @DisplayName("CategoryMasterImpl") + class CategoryServiceTests { + + @Test + @DisplayName("getCategoryId should store the category and answer the id it was given") + void getCategoryId_shouldStoreAndAnswerId() { + M_Category category = new M_Category(); + M_Category stored = new M_Category(); + stored.setCategoryID(81); + when(categoryRepo.save(category)).thenReturn(stored); + + assertEquals(81, categoryService.getCategoryId(category)); + } + + @Test + @DisplayName("the sub-category calls should each reach their own repository query") + void subCategoryCalls_shouldReachTheirOwnQuery() { + M_Subcategory subCategory = new M_Subcategory(); + ArrayList stored = new ArrayList<>(List.of(subCategory)); + ArrayList views = new ArrayList<>(List.of(new V_Showsubcategory())); + when(subCategoryRepo.saveAll(anyList())).thenReturn(stored); + when(subCategoryRepo.getCategory()).thenReturn(stored); + when(subCategoryRepo.getCategory(81)).thenReturn(stored); + when(subCategoryRepo.getSubCategory(91)).thenReturn(subCategory); + when(subCategoryRepo.save(subCategory)).thenReturn(subCategory); + when(v_ShowsubcategoryRepo.getSubCategory1(91)).thenReturn(views); + when(v_ShowsubcategoryRepo.getCategoryByMapIDAndSubServiceID(PSM_ID, 61)).thenReturn(views); + + assertSame(stored, categoryService.saveSubCatData(new ArrayList<>())); + assertSame(stored, categoryService.createSubCategory(new ArrayList<>())); + assertSame(stored, categoryService.getCategory()); + assertSame(stored, categoryService.getCategory(81)); + assertSame(subCategory, categoryService.getSubCategory(91)); + assertSame(subCategory, categoryService.updateSubCatData(subCategory)); + assertSame(views, categoryService.getSubCategory1(91)); + assertSame(views, categoryService.getCategoryByMapIDAndSubServiceID(PSM_ID, 61)); + } + + @Test + @DisplayName("the category calls should each reach their own repository query") + void categoryCalls_shouldReachTheirOwnQuery() { + M_Category category = new M_Category(); + ArrayList stored = new ArrayList<>(List.of(category)); + when(categoryRepo.saveAll(anyList())).thenReturn(stored); + when(categoryRepo.getAllCategory(61, PSM_ID)).thenReturn(stored); + when(categoryRepo.getAllCategory1(PSM_ID)).thenReturn(stored); + when(categoryRepo.getCatData(81)).thenReturn(category); + when(categoryRepo.save(category)).thenReturn(category); + when(categoryRepo.updateCategory(81, 21)).thenReturn(1); + when(categoryRepo.findByProviderServiceMapIDAndFeedbackNatureIDOrderByCategoryNameAsc(PSM_ID, null)) + .thenReturn(stored); + + assertSame(stored, categoryService.createcat(new ArrayList<>())); + assertSame(stored, categoryService.getAllCategory(61, PSM_ID)); + assertSame(stored, categoryService.getAllCategory1(PSM_ID)); + assertSame(category, categoryService.getcatdatabycatId(81)); + assertSame(category, categoryService.deletedata(category)); + assertEquals(1, categoryService.updateCategory(81, 21)); + assertSame(stored, categoryService.getUpmappedCategory(PSM_ID)); + } + + @Test + @DisplayName("getAllCategory should rebuild one category per row the query answers") + void getAllCategory_shouldRebuildEachRow() { + when(categoryRepo.getAllCategory(PSM_ID)) + .thenReturn(List.of(new Object[] { 81, "Medical", 61, "Counselling", PSM_ID })); + + ArrayList categories = categoryService.getAllCategory(PSM_ID); + + assertEquals(1, categories.size()); + assertEquals("Medical", categories.get(0).getCategoryName()); + } + + @Test + @DisplayName("getAllCategorywithFeedbackNatureID should rebuild one category per row the query answers") + void getAllCategoryWithFeedbackNature_shouldRebuildEachRow() { + when(categoryRepo.getAllCategorywithfeedbackNatureID(PSM_ID, 21)) + .thenReturn(List.of(new Object[] { 81, "Medical", 61, "Counselling", PSM_ID })); + + assertEquals(1, categoryService.getAllCategorywithFeedbackNatureID(PSM_ID, 21).size()); + } + } + + @Nested + @DisplayName("DrugMasterImpl") + class DrugServiceTests { + + @Test + @DisplayName("getAllDrugData should read the live catalogue when the caller excludes retired drugs") + void getAllDrugData_shouldReadLiveCatalogue() { + when(drugMasterRepo.getValidDrugData("77")) + .thenReturn(List.of(new Object[] { 101, "Paracetamol", "Antipyretic", "OTC", + Boolean.FALSE, (short) 77 })); + + ArrayList drugs = drugService.getAllDrugData(101, (short) 77, Boolean.FALSE); + + assertEquals(1, drugs.size()); + assertEquals("Paracetamol", drugs.get(0).getDrugName()); + verify(drugMasterRepo, org.mockito.Mockito.never()).getAllDrugData(anyString(), anyString()); + } + + @Test + @DisplayName("getAllDrugData should read the whole catalogue when retired drugs are wanted too") + void getAllDrugData_shouldReadWholeCatalogue() { + when(drugMasterRepo.getAllDrugData("101", "77")).thenReturn(new ArrayList<>()); + + drugService.getAllDrugData(101, (short) 77, Boolean.TRUE); + + verify(drugMasterRepo).getAllDrugData("101", "77"); + } + + @Test + @DisplayName("getAllDrugData should ask for the whole catalogue when the caller names nothing") + void getAllDrugData_shouldAskForWholeCatalogueWithoutFilters() { + when(drugMasterRepo.getAllDrugData("", "")).thenReturn(new ArrayList<>()); + + drugService.getAllDrugData(null, null, null); + + verify(drugMasterRepo).getAllDrugData("", ""); + } + + @Test + @DisplayName("getAllDrugGroups should read the live groups when the caller excludes retired ones") + void getAllDrugGroups_shouldReadLiveGroups() { + when(drugGroupRepo.getValidDrugGroups("77")) + .thenReturn(List.of(new Object[] { 201, "Analgesics", "Pain relief", Boolean.FALSE, + (short) 77 })); + + assertEquals(1, drugService.getAllDrugGroups(201, (short) 77, Boolean.FALSE).size()); + } + + @Test + @DisplayName("getAllDrugGroups should read every group when retired ones are wanted too") + void getAllDrugGroups_shouldReadEveryGroup() { + when(drugGroupRepo.getAllDrugGroups("201", "77")).thenReturn(new ArrayList<>()); + + drugService.getAllDrugGroups(201, (short) 77, Boolean.TRUE); + + verify(drugGroupRepo).getAllDrugGroups("201", "77"); + } + + @Test + @DisplayName("getAllDrugGroupMappings should rebuild one mapping per row the query answers") + void getAllDrugGroupMappings_shouldRebuildEachRow() { + when(drugMappingRepo.getAllDrugGroupMappings("", 77, 3)) + .thenReturn(List.of(new Object[] { 301, 101, "Paracetamol", 201, "Analgesics", "OTC", + Boolean.FALSE, 77, PSM_ID, "N", Boolean.FALSE })); + + assertEquals(1, drugService.getAllDrugGroupMappings(null, 77, 3).size()); + } + + @Test + @DisplayName("the drug writes should each reach their own repository") + void drugWrites_shouldReachTheirOwnRepository() { + M_104druggroup group = new M_104druggroup(); + group.setDrugGroupID(201); + M_104drugmaster drug = new M_104drugmaster(); + M_104drugmapping mapping = new M_104drugmapping(); + ArrayList groups = new ArrayList<>(List.of(group)); + ArrayList
The carriers are mostly Lombok {@code @Data} entities, so the generated + * {@code equals}, {@code hashCode} and {@code toString} are exercised alongside + * every readable/writable property. A carrier that cannot be built through a + * no-argument constructor is reported by {@link #isVerifiable(Class)} so the + * calling suite can skip it rather than fail. + */ +public final class BeanContract { + + private BeanContract() { + } + + /** Answers whether the type can be exercised through the reflective contract. */ + public static boolean isVerifiable(Class> type) { + if (type.isInterface() || type.isEnum() || type.isAnnotation() + || Modifier.isAbstract(type.getModifiers()) + || type.isMemberClass() && !Modifier.isStatic(type.getModifiers())) { + return false; + } + try { + type.getDeclaredConstructor(); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + public static void verify(Class> type) throws Exception { + Object left = newInstance(type); + Object right = newInstance(type); + copyFields(type, left, right); + + assertNotNull(left.toString(), type.getSimpleName() + " must render a string form"); + assertTrue(left.equals(left), type.getSimpleName() + " must equal itself"); + assertFalse(left.equals(null), type.getSimpleName() + " must never equal null"); + assertFalse(left.equals(new Object()), type.getSimpleName() + " must never equal an unrelated type"); + left.hashCode(); + + if (left.equals(right)) { + assertEquals(left.hashCode(), right.hashCode(), + type.getSimpleName() + " must hash consistently with equals"); + } + + verifyProperties(type, left, right); + } + + private static Object newInstance(Class> type) throws Exception { + java.lang.reflect.Constructor> constructor = type.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } + + private static void verifyProperties(Class> type, Object left, Object right) throws Exception { + for (Field field : type.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) { + continue; + } + Method getter = findGetter(type, field); + Method setter = findSetter(type, field); + if (getter == null || setter == null) { + continue; + } + Object value = sampleValue(field.getType()); + if (value == null) { + continue; + } + + setter.invoke(left, value); + assertEquals(value, getter.invoke(left), + type.getSimpleName() + "." + field.getName() + " must round-trip through its accessors"); + + setter.invoke(right, value); + assertEquals(getter.invoke(left), getter.invoke(right), + type.getSimpleName() + "." + field.getName() + " must read back the same on both instances"); + } + assertNotNull(left.toString(), type.getSimpleName() + " must render a populated string form"); + left.hashCode(); + left.equals(right); + } + + private static void copyFields(Class> type, Object from, Object to) throws Exception { + for (Field field : type.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) { + continue; + } + field.setAccessible(true); + field.set(to, field.get(from)); + } + } + + private static Method findGetter(Class> type, Field field) { + String suffix = capitalise(field.getName()); + for (String prefix : new String[] { "get", "is" }) { + try { + Method candidate = type.getMethod(prefix + suffix); + if (candidate.getParameterCount() == 0) { + return candidate; + } + } catch (NoSuchMethodException ignored) { + // try the next accessor style + } + } + return null; + } + + private static Method findSetter(Class> type, Field field) { + try { + return type.getMethod("set" + capitalise(field.getName()), field.getType()); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static String capitalise(String name) { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + private static Object sampleValue(Class> type) { + if (type == String.class) { + return "sample"; + } + if (type == Long.class || type == long.class) { + return 7L; + } + if (type == Integer.class || type == int.class) { + return 7; + } + if (type == Short.class || type == short.class) { + return (short) 7; + } + if (type == Double.class || type == double.class) { + return 7.5d; + } + if (type == Float.class || type == float.class) { + return 7.5f; + } + if (type == Character.class || type == char.class) { + return 'y'; + } + if (type == Byte.class || type == byte.class) { + return (byte) 7; + } + if (type == Boolean.class || type == boolean.class) { + return Boolean.TRUE; + } + if (type == java.math.BigDecimal.class) { + return java.math.BigDecimal.valueOf(7.5d); + } + if (type == BigInteger.class) { + return BigInteger.valueOf(7L); + } + if (type == Timestamp.class) { + return Timestamp.valueOf("2026-02-17 09:30:00"); + } + if (type == Date.class) { + return Date.valueOf("2026-02-17"); + } + if (type == Time.class) { + return Time.valueOf("09:30:00"); + } + if (type == java.util.Date.class) { + return new java.util.Date(1_771_286_400_000L); + } + if (type == LocalDate.class) { + return LocalDate.of(2026, 2, 17); + } + if (type == LocalTime.class) { + return LocalTime.of(9, 30); + } + if (type == LocalDateTime.class) { + return LocalDateTime.of(2026, 2, 17, 9, 30); + } + if (type == List.class) { + return new ArrayList<>(List.of("first", "second")); + } + if (type == Set.class) { + return new HashSet<>(Set.of("first")); + } + if (type == Map.class) { + return new HashMap<>(Map.of("key", "value")); + } + if (type == Object.class) { + return "sample"; + } + if (type.isPrimitive() || type.isEnum() || type.isArray() || type.isInterface()) { + return null; + } + try { + java.lang.reflect.Constructor> constructor = type.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (ReflectiveOperationException | SecurityException e) { + // A carrier that needs arguments is simply left at its default value. + return null; + } + } +} diff --git a/src/test/java/com/iemr/admin/data/ClassScanner.java b/src/test/java/com/iemr/admin/data/ClassScanner.java new file mode 100644 index 0000000..7eb63b0 --- /dev/null +++ b/src/test/java/com/iemr/admin/data/ClassScanner.java @@ -0,0 +1,64 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.data; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.RegexPatternTypeFilter; + +/** Finds every concrete class published under a package, for suite-wide contract checks. */ +public final class ClassScanner { + + private ClassScanner() { + } + + public static List> classesUnder(String... packages) { + ClassPathScanningCandidateComponentProvider provider = + new ClassPathScanningCandidateComponentProvider(false) { + @Override + protected boolean isCandidateComponent( + org.springframework.beans.factory.annotation.AnnotatedBeanDefinition definition) { + return definition.getMetadata().isIndependent() + && !definition.getMetadata().isAnnotation(); + } + }; + provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*"))); + + List> found = new ArrayList<>(); + for (String packageName : packages) { + for (BeanDefinition definition : provider.findCandidateComponents(packageName)) { + try { + found.add(Class.forName(definition.getBeanClassName())); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // A class the test classpath cannot resolve is not part of the contract. + } + } + } + found.sort(Comparator.comparing(Class::getName)); + return found; + } +} diff --git a/src/test/java/com/iemr/admin/data/DataCarrierContractTest.java b/src/test/java/com/iemr/admin/data/DataCarrierContractTest.java new file mode 100644 index 0000000..1fe9016 --- /dev/null +++ b/src/test/java/com/iemr/admin/data/DataCarrierContractTest.java @@ -0,0 +1,71 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.data; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Exercises every data carrier the service exchanges - the JPA entities, the + * transfer objects and the small model holders - against the accessor, equality + * and string contract their callers rely on. + */ +@DisplayName("Data carrier contract Test Suite") +class DataCarrierContractTest { + + private static final String[] CARRIER_PACKAGES = { + "com.iemr.admin.data", + "com.iemr.admin.to", + "com.iemr.admin.model" }; + + static List> carriers() { + return ClassScanner.classesUnder(CARRIER_PACKAGES).stream() + .filter(type -> !type.getName().endsWith("Test")) + .filter(type -> !type.getSimpleName().equals("BeanContract")) + .filter(type -> !type.getSimpleName().equals("ClassScanner")) + .filter(BeanContract::isVerifiable) + .toList(); + } + + @Test + @DisplayName("the scan should discover the carriers rather than silently pass on an empty set") + void carrierScan_shouldDiscoverCarriers() { + List> carriers = carriers(); + assertFalse(carriers.isEmpty(), "no data carriers were discovered on the test classpath"); + assertTrue(carriers.size() > 100, + "expected the full carrier set, found only " + carriers.size()); + } + + @ParameterizedTest(name = "{0} honours the accessor, equality and string contract") + @DisplayName("every data carrier should honour its accessor, equality and string contract") + @MethodSource("carriers") + void dataCarrier_shouldHonourBeanContract(Class> type) throws Exception { + BeanContract.verify(type); + } +} diff --git a/src/test/java/com/iemr/admin/mapper/emailconfig/InstituteEmailConfigMapperTest.java b/src/test/java/com/iemr/admin/mapper/emailconfig/InstituteEmailConfigMapperTest.java new file mode 100644 index 0000000..a80f1de --- /dev/null +++ b/src/test/java/com/iemr/admin/mapper/emailconfig/InstituteEmailConfigMapperTest.java @@ -0,0 +1,260 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.mapper.emailconfig; + +import java.sql.Timestamp; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.emailconfig.AuthorityEmail; +import com.iemr.admin.model.emailconfig.AuthEmailRequest; +import com.iemr.admin.model.emailconfig.AuthEmailResponse; +import com.iemr.admin.model.emailconfig.CreateAuthEmailRequestModel; +import com.iemr.admin.model.emailconfig.CreateNodalEmailRequestModel; +import com.iemr.admin.model.emailconfig.NodalEmailResponse; +import com.iemr.admin.model.emailconfig.UpdateAuthEmailRequest; +import com.iemr.admin.model.emailconfig.UpdateNodalEmailRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The escalation emails a nodal officer receives are addressed from these + * records, so the mapper must carry every contact field across rather than drop + * one silently. + */ +@DisplayName("InstituteEmailConfigMapper Test Suite") +class InstituteEmailConfigMapperTest { + + private final InstituteEmailConfigMapper mapper = InstituteEmailConfigMapper.INSTANCE; + + private static AuthorityEmail storedRecord() { + AuthorityEmail stored = new AuthorityEmail(); + stored.setAuthorityEmailID(9001); + stored.setStateID(29); + stored.setDistrictID(301); + stored.setBlockID(401); + stored.setDistrictBranchMappingID(501); + stored.setDesignationID(7); + stored.setAuthorityName("Dr Asha Rao"); + stored.setEmailID("asha.rao@example.org"); + stored.setContactNo("9000000001"); + stored.setProviderServiceMapID(4001); + stored.setCreatedBy("admin"); + stored.setCreatedDate(Timestamp.valueOf("2026-02-17 09:30:00")); + stored.setModifiedBy("admin"); + stored.setDeleted(Boolean.FALSE); + return stored; + } + + private static CreateAuthEmailRequestModel createAuthorityRequest() { + CreateAuthEmailRequestModel request = new CreateAuthEmailRequestModel(); + request.setStateID(29); + request.setDistrictID(301); + request.setBlockID(401); + request.setDistrictBranchMappingID(501); + request.setDesignationID(7); + request.setAuthorityName("Dr Asha Rao"); + request.setEmailID("asha.rao@example.org"); + request.setContactNo("9000000001"); + request.setProviderServiceMapID(4001); + request.setCreatedBy("admin"); + return request; + } + + private static CreateNodalEmailRequestModel createNodalRequest() { + CreateNodalEmailRequestModel request = new CreateNodalEmailRequestModel(); + request.setStateID(29); + request.setDistrictID(301); + request.setDesignationID(7); + request.setAuthorityName("Dr Asha Rao"); + request.setEmailID("asha.rao@example.org"); + request.setContactNo("9000000001"); + request.setMobileNo("9000000002"); + request.setProviderServiceMapID(4001); + request.setCreatedBy("admin"); + return request; + } + + @Test + @DisplayName("requestToInstituteEmailConf should carry the search request onto the record") + void requestToInstituteEmailConf_shouldCarrySearchRequest() { + AuthEmailRequest request = new AuthEmailRequest(); + request.setAuthorityEmailID(9001); + request.setStateID(29); + request.setDistrictID(301); + request.setBlockID(401); + request.setDistrictBranchMappingID(501); + request.setDesignationID(7); + request.setProviderServiceMapID(4001); + request.setDeleted(Boolean.FALSE); + + AuthorityEmail mapped = mapper.requestToInstituteEmailConf(request); + + assertEquals(9001, mapped.getAuthorityEmailID()); + assertEquals(29, mapped.getStateID()); + assertEquals(501, mapped.getDistrictBranchMappingID()); + assertEquals(4001, mapped.getProviderServiceMapID()); + } + + @Test + @DisplayName("requestToInstituteEmailConf should map a whole batch of search requests") + void requestToInstituteEmailConf_shouldMapBatch() { + AuthEmailRequest request = new AuthEmailRequest(); + request.setStateID(29); + + List mapped = mapper.requestToInstituteEmailConf(List.of(request)); + + assertEquals(1, mapped.size()); + assertEquals(29, mapped.get(0).getStateID()); + } + + @Test + @DisplayName("createRequestToInstituteEmailConf should carry every contact field onto the new record") + void createRequestToInstituteEmailConf_shouldCarryContactFields() { + AuthorityEmail mapped = mapper.createRequestToInstituteEmailConf(createAuthorityRequest()); + + assertEquals("Dr Asha Rao", mapped.getAuthorityName()); + assertEquals("asha.rao@example.org", mapped.getEmailID()); + assertEquals("9000000001", mapped.getContactNo()); + assertEquals("admin", mapped.getCreatedBy()); + } + + @Test + @DisplayName("createRequestToInstituteEmailConfig should carry the nodal officer's mobile number too") + void createRequestToInstituteEmailConfig_shouldCarryMobileNumber() { + AuthorityEmail mapped = mapper.createRequestToInstituteEmailConfig(createNodalRequest()); + + assertEquals("Dr Asha Rao", mapped.getAuthorityName()); + assertEquals("asha.rao@example.org", mapped.getEmailID()); + } + + @Test + @DisplayName("the create mappers should each map a whole batch") + void createMappers_shouldMapBatches() { + assertEquals(1, mapper.createRequestToInstituteEmailConf(List.of(createAuthorityRequest())).size()); + assertEquals(1, mapper.createRequestToInstituteEmailConfig(List.of(createNodalRequest())).size()); + } + + @Test + @DisplayName("updateRequestToInstituteEmailConf should carry the edited fields onto the record") + void updateRequestToInstituteEmailConf_shouldCarryEditedFields() { + UpdateAuthEmailRequest request = new UpdateAuthEmailRequest(); + request.setAuthorityEmailID(9001); + request.setAuthorityName("Dr Ravi Kumar"); + request.setEmailID("ravi.kumar@example.org"); + request.setContactNo("9000000003"); + request.setModifiedBy("admin"); + request.setDeleted(Boolean.FALSE); + + AuthorityEmail mapped = mapper.updateRequestToInstituteEmailConf(request); + + assertEquals(9001, mapped.getAuthorityEmailID()); + assertEquals("Dr Ravi Kumar", mapped.getAuthorityName()); + assertEquals("admin", mapped.getModifiedBy()); + } + + @Test + @DisplayName("updateRequestToInstituteNodalEmailConf should carry the edited nodal fields onto the record") + void updateRequestToInstituteNodalEmailConf_shouldCarryEditedFields() { + UpdateNodalEmailRequest request = new UpdateNodalEmailRequest(); + request.setAuthorityEmailID(9001); + request.setAuthorityName("Dr Ravi Kumar"); + request.setMobileNo("9000000004"); + request.setModifiedBy("admin"); + + AuthorityEmail mapped = mapper.updateRequestToInstituteNodalEmailConf(request); + + assertEquals(9001, mapped.getAuthorityEmailID()); + assertEquals("Dr Ravi Kumar", mapped.getAuthorityName()); + } + + @Test + @DisplayName("updateRequestToInstituteEmailConf should map a whole batch of edits") + void updateRequestToInstituteEmailConf_shouldMapBatch() { + UpdateAuthEmailRequest request = new UpdateAuthEmailRequest(); + request.setAuthorityEmailID(9001); + + assertEquals(1, mapper.updateRequestToInstituteEmailConf(List.of(request)).size()); + } + + @Test + @DisplayName("resultToInstTypeEmailResponse should publish the stored record back to the caller") + void resultToInstTypeEmailResponse_shouldPublishStoredRecord() { + AuthEmailResponse published = mapper.resultToInstTypeEmailResponse(storedRecord()); + + assertEquals(9001, published.getAuthorityEmailID()); + assertEquals("Dr Asha Rao", published.getAuthorityName()); + assertEquals("asha.rao@example.org", published.getEmailID()); + assertEquals(Timestamp.valueOf("2026-02-17 09:30:00"), published.getCreatedDate()); + } + + @Test + @DisplayName("the nodal response mappers should publish the stored record back to the caller") + void nodalResponseMappers_shouldPublishStoredRecord() { + NodalEmailResponse published = mapper.resultToInstTypeNodalEmailResponse(storedRecord()); + NodalEmailResponse alsoPublished = mapper.resultInstType(storedRecord()); + + assertEquals("Dr Asha Rao", published.getAuthorityName()); + assertEquals("Dr Asha Rao", alsoPublished.getAuthorityName()); + assertEquals(4001, published.getProviderServiceMapID()); + } + + @Test + @DisplayName("the response mappers should publish a whole batch of stored records") + void responseMappers_shouldPublishBatches() { + List stored = List.of(storedRecord(), storedRecord()); + + assertEquals(2, mapper.resultToInstTypeEmailResponse(stored).size()); + assertEquals(2, mapper.resultToInstTypeEmailResponses(stored).size()); + } + + @Test + @DisplayName("the mappers should answer nothing rather than an empty record for a missing input") + void mappers_shouldAnswerNothingForMissingInput() { + assertNull(mapper.requestToInstituteEmailConf((AuthEmailRequest) null)); + assertNull(mapper.requestToInstituteEmailConf((List) null)); + assertNull(mapper.createRequestToInstituteEmailConf((CreateAuthEmailRequestModel) null)); + assertNull(mapper.createRequestToInstituteEmailConf((List) null)); + assertNull(mapper.createRequestToInstituteEmailConfig((CreateNodalEmailRequestModel) null)); + assertNull(mapper.createRequestToInstituteEmailConfig((List) null)); + assertNull(mapper.updateRequestToInstituteEmailConf((UpdateAuthEmailRequest) null)); + assertNull(mapper.updateRequestToInstituteEmailConf((List) null)); + assertNull(mapper.updateRequestToInstituteNodalEmailConf(null)); + assertNull(mapper.resultToInstTypeEmailResponse((AuthorityEmail) null)); + assertNull(mapper.resultToInstTypeEmailResponse((List) null)); + assertNull(mapper.resultToInstTypeNodalEmailResponse(null)); + assertNull(mapper.resultInstType(null)); + assertNull(mapper.resultToInstTypeEmailResponses(null)); + } + + @Test + @DisplayName("the batch mappers should answer an empty batch for an empty input") + void batchMappers_shouldAnswerEmptyBatchForEmptyInput() { + assertTrue(mapper.requestToInstituteEmailConf(List.of()).isEmpty()); + assertTrue(mapper.resultToInstTypeEmailResponse(List.of()).isEmpty()); + assertTrue(mapper.resultToInstTypeEmailResponses(List.of()).isEmpty()); + } +} diff --git a/src/test/java/com/iemr/admin/mapper/parkingplacetalukmapping/ParkingPlaceTalukMappingMapperTest.java b/src/test/java/com/iemr/admin/mapper/parkingplacetalukmapping/ParkingPlaceTalukMappingMapperTest.java new file mode 100644 index 0000000..6b63657 --- /dev/null +++ b/src/test/java/com/iemr/admin/mapper/parkingplacetalukmapping/ParkingPlaceTalukMappingMapperTest.java @@ -0,0 +1,134 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.mapper.parkingplacetalukmapping; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.locationmaster.DistrictBlock; +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.parkingPlace.M_Parkingplace; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMapping; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMappingTO; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The mapper flattens a stored taluk mapping and the three records it points at + * into the single carrier the parking place screens read. + */ +@DisplayName("ParkingPlaceTalukMappingMapper Test Suite") +class ParkingPlaceTalukMappingMapperTest { + + private final ParkingPlaceTalukMappingMapper mapper = ParkingPlaceTalukMappingMapper.INSTANCE; + + private static ParkingplaceTalukMapping fullMapping() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(7001); + mapping.setParkingPlaceID(31); + mapping.setDistrictID(301); + mapping.setDistrictBlockID(3011); + mapping.setProviderServiceMapID(4001); + mapping.setDeleted(Boolean.FALSE); + mapping.setProcessed("N"); + mapping.setCreatedBy("admin"); + mapping.setModifiedBy("supervisor"); + + M_Parkingplace parkingplace = new M_Parkingplace(); + parkingplace.setParkingPlaceName("Hosur parking"); + parkingplace.setDeleted(Boolean.FALSE); + mapping.setParkingplace(parkingplace); + + M_District district = new M_District(); + district.setDistrictName("Bengaluru Urban"); + district.setDeleted(Boolean.FALSE); + mapping.setM_district(district); + + DistrictBlock block = new DistrictBlock(); + block.setBlockName("Anekal"); + block.setDeleted(Boolean.FALSE); + mapping.setDistrictBlock(block); + return mapping; + } + + @Test + @DisplayName("should carry the names of the parking place, district and taluk onto one carrier") + void shouldFlattenNamesOntoOneCarrier() { + ParkingplaceTalukMappingTO published = mapper.getParkingplaceTalukMappingMap(fullMapping()); + + assertEquals(7001, published.getPpSubDistrictMapID()); + assertEquals("Hosur parking", published.getParkingPlaceName()); + assertEquals("Bengaluru Urban", published.getDistrictName()); + assertEquals("Anekal", published.getDistrictBlockName()); + assertEquals(4001, published.getProviderServiceMapID()); + assertEquals("admin", published.getCreatedBy()); + assertEquals("supervisor", published.getModifiedBy()); + assertEquals("N", published.getProcessed()); + assertEquals(Boolean.FALSE, published.getDeleted()); + assertEquals(Boolean.FALSE, published.getParkingPlaceDeleted()); + assertEquals(Boolean.FALSE, published.getDistrictDeleted()); + assertEquals(Boolean.FALSE, published.getDistrictBlockDeleted()); + } + + @Test + @DisplayName("should leave the names empty when the mapping points at nothing") + void shouldLeaveNamesEmptyWhenNothingPointedAt() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(7002); + + ParkingplaceTalukMappingTO published = mapper.getParkingplaceTalukMappingMap(mapping); + + assertEquals(7002, published.getPpSubDistrictMapID()); + assertNull(published.getParkingPlaceName()); + assertNull(published.getDistrictName()); + assertNull(published.getDistrictBlockName()); + assertNull(published.getParkingPlaceDeleted()); + } + + @Test + @DisplayName("should answer nothing for a mapping that is not there at all") + void shouldAnswerNothingForAbsentMapping() { + assertNull(mapper.getParkingplaceTalukMappingMap(null)); + assertNull(mapper.getParkingplaceTalukMappingMapList(null)); + } + + @Test + @DisplayName("should publish one carrier per mapping in the list") + void shouldPublishOneCarrierPerMapping() { + List published = mapper + .getParkingplaceTalukMappingMapList(List.of(fullMapping(), fullMapping())); + + assertEquals(2, published.size()); + assertEquals("Anekal", published.get(0).getDistrictBlockName()); + } + + @Test + @DisplayName("should publish an empty list when there is no mapping to publish") + void shouldPublishEmptyListForNoMappings() { + assertTrue(mapper.getParkingplaceTalukMappingMapList(new ArrayList<>()).isEmpty()); + } +} diff --git a/src/test/java/com/iemr/admin/service/apiman/ApimanServiceImplTest.java b/src/test/java/com/iemr/admin/service/apiman/ApimanServiceImplTest.java new file mode 100644 index 0000000..98e4d15 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/apiman/ApimanServiceImplTest.java @@ -0,0 +1,261 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.apiman; + +import java.util.HashMap; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.admin.data.apiman.ApimanClient; +import com.iemr.admin.data.apiman.ApimanRegister; +import com.iemr.admin.utils.http.HttpUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The apiman service registers a new service line as a client of the API + * gateway and signs it up to the API contracts that service line needs. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ApimanServiceImpl Test Suite") +class ApimanServiceImplTest { + + private static final String BASE_URL = "https://gateway.example.org"; + private static final String CLIENT_ID = "client-104"; + + @Mock + private HttpUtils httpUtils; + + private HttpUtils originalHttpUtils; + + private ApimanServiceImpl service; + + @BeforeEach + @DisplayName("Stand in for the gateway and point the service at stub addresses") + void setUp() { + originalHttpUtils = (HttpUtils) ReflectionTestUtils.getField(ApimanServiceImpl.class, "httpUtils"); + ReflectionTestUtils.setField(ApimanServiceImpl.class, "httpUtils", httpUtils); + service = new ApimanServiceImpl(); + ReflectionTestUtils.setField(service, "apimanBaseURL", BASE_URL); + ReflectionTestUtils.setField(service, "clientURL", "APIMAN_URL/clients"); + ReflectionTestUtils.setField(service, "contractURL", "APIMAN_URL/clients/CLIENT_ID/contracts"); + ReflectionTestUtils.setField(service, "registerURL", "APIMAN_URL/register"); + ReflectionTestUtils.setField(service, "getClientKey", "APIMAN_URL/clients/CLIENT_ID/apikey"); + ReflectionTestUtils.setField(service, "auth", "Bearer gateway-token"); + ReflectionTestUtils.setField(service, "apimanplanID", "plan"); + ReflectionTestUtils.setField(service, "apimanorgID", "org"); + ReflectionTestUtils.setField(service, "apimanCommonApiID", "common"); + ReflectionTestUtils.setField(service, "apiman1097apiID", "api1097"); + ReflectionTestUtils.setField(service, "apimanMMUapiID", "mmu"); + ReflectionTestUtils.setField(service, "apimanInventoryapiID", "inventory"); + ReflectionTestUtils.setField(service, "apiman104apiID", "api104"); + ReflectionTestUtils.setField(service, "apimanTMapiID", "tm"); + ReflectionTestUtils.setField(service, "apimanSchedulingapiID", "scheduling"); + ReflectionTestUtils.setField(service, "apimanMCTSapiID", "mcts"); + } + + @AfterEach + @DisplayName("Put the real gateway client back so no other suite sees the stand-in") + void tearDown() { + ReflectionTestUtils.setField(ApimanServiceImpl.class, "httpUtils", originalHttpUtils); + } + + private static ApimanClient client() { + ApimanClient client = new ApimanClient(); + client.setId(CLIENT_ID); + client.setName("104 Helpline"); + client.setInitialVersion("1.0"); + return client; + } + + @Test + @DisplayName("createClient should answer the client the gateway registered") + void createClient_shouldAnswerRegisteredClient() throws Exception { + when(httpUtils.post(anyString(), anyString(), any())) + .thenReturn("{\"id\":\"client-104\",\"name\":\"104 Helpline\"}"); + + ApimanClient registered = service.createClient(client()); + + assertEquals(CLIENT_ID, registered.getId()); + assertEquals("104 Helpline", registered.getName()); + } + + @Test + @DisplayName("createClient should send the client to the gateway with the configured credentials") + void createClient_shouldSendWithConfiguredCredentials() throws Exception { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{\"id\":\"client-104\"}"); + ArgumentCaptor url = ArgumentCaptor.forClass(String.class); + ArgumentCaptor> header = ArgumentCaptor.forClass(HashMap.class); + + service.createClient(client()); + + verify(httpUtils).post(url.capture(), anyString(), header.capture()); + assertEquals(BASE_URL + "/clients", url.getValue()); + assertEquals("Bearer gateway-token", header.getValue().get("Authorization")); + assertEquals("application/json", header.getValue().get("Content-Type")); + } + + @Test + @DisplayName("createClient should give up when the gateway answers something that is not a client") + void createClient_shouldGiveUpOnUnreadableReply() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("null"); + + assertThrows(RuntimeException.class, () -> service.createClient(client())); + } + + @Test + @DisplayName("createClientContract should sign a helpline service line up to its own API as well as the shared one") + void createClientContract_shouldSignUpHelplineApis() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(1, CLIENT_ID); + + ArgumentCaptor body = ArgumentCaptor.forClass(String.class); + verify(httpUtils, times(3)).post(anyString(), body.capture(), any()); + assertTrue(body.getAllValues().stream().anyMatch(sent -> sent.contains("api1097")), body.getAllValues() + .toString()); + assertTrue(body.getAllValues().stream().filter(sent -> sent.contains("common")).count() == 2, + "both versions of the shared API must be contracted"); + } + + @Test + @DisplayName("createClientContract should sign a mobile unit service line up to its stock APIs too") + void createClientContract_shouldSignUpMobileUnitApis() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(2, CLIENT_ID); + + ArgumentCaptor body = ArgumentCaptor.forClass(String.class); + verify(httpUtils, times(4)).post(anyString(), body.capture(), any()); + List sent = body.getAllValues(); + assertTrue(sent.stream().anyMatch(one -> one.contains("mmu")), sent.toString()); + assertTrue(sent.stream().anyMatch(one -> one.contains("inventory")), sent.toString()); + } + + @Test + @DisplayName("createClientContract should sign a telemedicine service line up to its scheduling API") + void createClientContract_shouldSignUpTelemedicineApis() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(4, CLIENT_ID); + + ArgumentCaptor body = ArgumentCaptor.forClass(String.class); + verify(httpUtils, times(4)).post(anyString(), body.capture(), any()); + assertTrue(body.getAllValues().stream().anyMatch(one -> one.contains("scheduling")), + body.getAllValues().toString()); + } + + @Test + @DisplayName("createClientContract should contract only the shared API for a service line with no API of its own") + void createClientContract_shouldContractOnlySharedApi() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(5, CLIENT_ID); + + verify(httpUtils, times(2)).post(anyString(), anyString(), any()); + } + + @Test + @DisplayName("createClientContract should contract only the shared API for a service line it does not recognise") + void createClientContract_shouldContractOnlySharedApiForUnknownServiceLine() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + + service.createClientContract(99, CLIENT_ID); + + verify(httpUtils, times(2)).post(anyString(), anyString(), any()); + } + + @Test + @DisplayName("createClientContract should address the client whose contracts are being signed") + void createClientContract_shouldAddressTheClient() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + ArgumentCaptor url = ArgumentCaptor.forClass(String.class); + + service.createClientContract(3, CLIENT_ID); + + verify(httpUtils, times(3)).post(url.capture(), anyString(), any()); + assertEquals(BASE_URL + "/clients/" + CLIENT_ID + "/contracts", url.getValue()); + } + + @Test + @DisplayName("registerClient should publish the registration to the gateway") + void registerClient_shouldPublishRegistration() { + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{}"); + ApimanRegister registration = new ApimanRegister(); + registration.setType("client"); + registration.setEntityId(CLIENT_ID); + ArgumentCaptor url = ArgumentCaptor.forClass(String.class); + + assertNull(service.registerClient(registration), "registration answers nothing back to the caller"); + verify(httpUtils).post(url.capture(), anyString(), any()); + assertEquals(BASE_URL + "/register", url.getValue()); + } + + @Test + @DisplayName("getClientKey should answer the API key the gateway issued") + void getClientKey_shouldAnswerIssuedKey() { + when(httpUtils.get(anyString(), any())).thenReturn("{\"apiKey\":\"key-abc-123\"}"); + + assertEquals("key-abc-123", service.getClientKey(CLIENT_ID)); + } + + @Test + @DisplayName("getClientKey should give up when the gateway does not answer a key") + void getClientKey_shouldGiveUpWithoutKey() { + when(httpUtils.get(anyString(), any())).thenReturn("{\"result\":\"unknown client\"}"); + + assertThrows(RuntimeException.class, () -> service.getClientKey(CLIENT_ID)); + } + + @Test + @DisplayName("the gateway should still be reached when no credentials are configured") + void gatewayCalls_shouldStillBeReachedWithoutCredentials() throws Exception { + ReflectionTestUtils.setField(service, "auth", null); + when(httpUtils.post(anyString(), anyString(), any())).thenReturn("{\"id\":\"client-104\"}"); + ArgumentCaptor> header = ArgumentCaptor.forClass(HashMap.class); + + service.createClient(client()); + + verify(httpUtils).post(anyString(), anyString(), header.capture()); + assertNull(header.getValue().get("Authorization"), "no credentials must be sent when none are configured"); + } +} diff --git a/src/test/java/com/iemr/admin/service/blocking/BlockingServiceTest.java b/src/test/java/com/iemr/admin/service/blocking/BlockingServiceTest.java new file mode 100644 index 0000000..39711d1 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/blocking/BlockingServiceTest.java @@ -0,0 +1,309 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.blocking; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.blocking.M_Providerservicemapping_Blocking; +import com.iemr.admin.data.blocking.M_Serviceprovider_Blocking; +import com.iemr.admin.data.blocking.M_Status1; +import com.iemr.admin.data.blocking.T_Providerservicemappingdetail; +import com.iemr.admin.data.blocking.T_Serviceproviderdetail; +import com.iemr.admin.data.blocking.T_Userdetail; +import com.iemr.admin.data.blocking.UserForBlocking; +import com.iemr.admin.data.blocking.V_Showproviderservicemapping; +import com.iemr.admin.repo.blocking.MProviderservicemappingBlockingRepo; +import com.iemr.admin.repo.blocking.MServiceproviderBlockingRepo; +import com.iemr.admin.repo.blocking.MStatusRepo; +import com.iemr.admin.repo.blocking.T_ProviderservicemappingdetailRepo; +import com.iemr.admin.repo.blocking.T_ServiceproviderdetailRepo; +import com.iemr.admin.repo.blocking.T_UserDetailRepo; +import com.iemr.admin.repo.blocking.UserBlockingRepo; +import com.iemr.admin.repo.blocking.V_ShowproviderservicemappingRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The blocking service turns each request to suspend a provider into the right + * repository update, and reports how far a CTI campaign mapping got. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Blocking_Service Test Suite") +class BlockingServiceTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer SERVICE_ID = 3; + private static final Integer STATE_ID = 29; + + @Mock + private V_ShowproviderservicemappingRepo v_ShowproviderservicemappingRepo; + + @Mock + private T_UserDetailRepo t_UserDetailRepo; + + @Mock + private MStatusRepo mStatusRepo; + + @Mock + private UserBlockingRepo userBlockingRepo; + + @Mock + private T_ProviderservicemappingdetailRepo t_ProviderservicemappingdetailRepo; + + @Mock + private MProviderservicemappingBlockingRepo mProviderservicemappingBlockingRepo; + + @Mock + private T_ServiceproviderdetailRepo t_ServiceproviderdetailRepo; + + @Mock + private MServiceproviderBlockingRepo mServiceproviderBlockingRepo; + + @InjectMocks + private Blocking_Service service; + + private static M_Providerservicemapping_Blocking mapping(Integer mapId) { + M_Providerservicemapping_Blocking mapping = new M_Providerservicemapping_Blocking(); + mapping.setProviderServiceMapID(mapId); + mapping.setServiceProviderID(PROVIDER_ID); + mapping.setServiceID(SERVICE_ID); + mapping.setStateID(STATE_ID); + mapping.setcTI_CampaignName("104"); + return mapping; + } + + @Test + @DisplayName("getProviderDetailsById should hand back what the repository holds") + void getProviderDetailsById_shouldHandBackRepositoryContents() { + M_Serviceprovider_Blocking stored = new M_Serviceprovider_Blocking(); + when(mServiceproviderBlockingRepo.getProviderDetailsByID(PROVIDER_ID)).thenReturn(stored); + + assertSame(stored, service.getProviderDetailsById(PROVIDER_ID)); + } + + @Test + @DisplayName("blockServiceProvider should answer the provider the repository stored") + void blockServiceProvider_shouldAnswerStoredProvider() { + M_Serviceprovider_Blocking stored = new M_Serviceprovider_Blocking(); + when(mServiceproviderBlockingRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.blockServiceProvider(stored)); + } + + @Test + @DisplayName("saveData should answer the audit row the repository stored") + void saveData_shouldAnswerStoredAuditRow() { + T_Serviceproviderdetail stored = new T_Serviceproviderdetail(); + when(t_ServiceproviderdetailRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.saveData(stored)); + } + + @Test + @DisplayName("the status updates should each reach their own repository query") + void statusUpdates_shouldReachTheirOwnQuery() { + service.blockProviderByService(PROVIDER_ID, STATE_ID, SERVICE_ID, 2); + service.blockProviderByState(PROVIDER_ID, STATE_ID, 2); + service.blockProvider(PROVIDER_ID, 2); + service.blockProviderByProviderIdAndServiceId(PROVIDER_ID, SERVICE_ID, 2); + service.blockUser(3117, 2); + + verify(mProviderservicemappingBlockingRepo).blockProviderByService(PROVIDER_ID, STATE_ID, SERVICE_ID, 2); + verify(mProviderservicemappingBlockingRepo).blockProviderByState(PROVIDER_ID, STATE_ID, 2); + verify(mProviderservicemappingBlockingRepo).blockProvider(PROVIDER_ID, 2); + verify(mProviderservicemappingBlockingRepo) + .blockProviderByProviderIdAndServiceId(PROVIDER_ID, SERVICE_ID, 2); + verify(userBlockingRepo).blockUser(3117, 2); + } + + @Test + @DisplayName("the mapping lookups should each reach their own repository query") + void mappingLookups_shouldReachTheirOwnQuery() { + M_Providerservicemapping_Blocking stored = mapping(4001); + ArrayList storedList = new ArrayList<>(List.of(stored)); + List stateList = List.of(stored); + when(mProviderservicemappingBlockingRepo.getProviderServiceMappingDetails(PROVIDER_ID, STATE_ID, SERVICE_ID)) + .thenReturn(stored); + when(mProviderservicemappingBlockingRepo.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)) + .thenReturn(stateList); + when(mProviderservicemappingBlockingRepo.getProviderStatus(PROVIDER_ID)).thenReturn(storedList); + when(mProviderservicemappingBlockingRepo.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)) + .thenReturn(storedList); + when(mProviderservicemappingBlockingRepo.findByProviderServiceMapID(4001)).thenReturn(stored); + when(mProviderservicemappingBlockingRepo.save(stored)).thenReturn(stored); + when(mProviderservicemappingBlockingRepo.saveAll(anyList())).thenReturn(storedList); + + assertSame(stored, service.getProviderServiceMappingDetails(PROVIDER_ID, STATE_ID, SERVICE_ID)); + assertSame(stateList, service.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)); + assertSame(storedList, service.getProviderStatus(PROVIDER_ID)); + assertSame(storedList, service.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)); + assertSame(stored, service.getDataByProviderServiceMapId(4001)); + assertSame(stored, service.updateProviderData(stored)); + assertSame(storedList, service.AddServiceProvider(new ArrayList<>())); + } + + @Test + @DisplayName("the view lookups should each reach their own repository query") + void viewLookups_shouldReachTheirOwnQuery() { + ArrayList stored = new ArrayList<>(List.of(new V_Showproviderservicemapping())); + when(v_ShowproviderservicemappingRepo.getProviderStatus(PROVIDER_ID)).thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderStatus1(PROVIDER_ID)).thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderServiceMappingDetails1(PROVIDER_ID, STATE_ID, SERVICE_ID)) + .thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderStateMappingDetails(PROVIDER_ID, STATE_ID)) + .thenReturn(stored); + when(v_ShowproviderservicemappingRepo.getProviderStatusByProviderAndServiceId(PROVIDER_ID, SERVICE_ID)) + .thenReturn(stored); + + assertSame(stored, service.getProviderStatus1(PROVIDER_ID)); + assertSame(stored, service.getProviderStatus2(PROVIDER_ID)); + assertSame(stored, service.getProviderServiceMappingDetails2(PROVIDER_ID, STATE_ID, SERVICE_ID)); + assertSame(stored, service.getProviderStateMappingDetails1(PROVIDER_ID, STATE_ID)); + assertSame(stored, service.getProviderStatusByProviderAndServiceId2(PROVIDER_ID, SERVICE_ID)); + } + + @Test + @DisplayName("the audit writes should each reach their own repository") + void auditWrites_shouldReachTheirOwnRepository() { + T_Providerservicemappingdetail detail = new T_Providerservicemappingdetail(); + ArrayList details = new ArrayList<>(List.of(detail)); + T_Userdetail userDetail = new T_Userdetail(); + when(t_ProviderservicemappingdetailRepo.save(detail)).thenReturn(detail); + when(t_ProviderservicemappingdetailRepo.saveAll(anyList())).thenReturn(details); + when(t_UserDetailRepo.save(userDetail)).thenReturn(userDetail); + + assertSame(detail, service.savetpsdData(detail)); + assertSame(details, service.savetpsmd(new ArrayList<>())); + assertSame(userDetail, service.saveUserDetails(userDetail)); + } + + @Test + @DisplayName("getUserDetailByUserId and getStatusData should hand back what the repositories hold") + void userLookups_shouldHandBackRepositoryContents() { + UserForBlocking user = new UserForBlocking(); + ArrayList statuses = new ArrayList<>(List.of(new M_Status1())); + when(userBlockingRepo.getUserDetailByUserId(3117)).thenReturn(user); + when(mStatusRepo.getStatusData()).thenReturn(statuses); + + assertSame(user, service.getUserDetailByUserId(3117)); + assertSame(statuses, service.getStatusData()); + } + + @Test + @DisplayName("getServiceLiensUsingProvider should skip a row the query could not fill") + void getServiceLiensUsingProvider_shouldSkipUnfillableRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 4001, PROVIDER_ID, SERVICE_ID, "Tele Medicine", Boolean.FALSE }); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider(PROVIDER_ID)).thenReturn(rows); + + assertEquals(1, service.getServiceLiensUsingProvider(PROVIDER_ID).size()); + } + + @Test + @DisplayName("mapctidata should report a clean run when every mapping is written") + void mapctidata_shouldReportCleanRun() { + when(mProviderservicemappingBlockingRepo.createcitmapping(anyInt(), any())).thenReturn(1); + + assertEquals("Mapping Successful", service.mapctidata(List.of(mapping(4001), mapping(4002)))); + } + + @Test + @DisplayName("mapctidata should report how far it got when a mapping is rejected") + void mapctidata_shouldReportHowFarItGot() { + when(mProviderservicemappingBlockingRepo.createcitmapping(4001, "104")).thenReturn(1); + when(mProviderservicemappingBlockingRepo.createcitmapping(4002, "104")).thenReturn(0); + + String status = service.mapctidata(List.of(mapping(4001), mapping(4002))); + + assertTrue(status.startsWith("Mapping Failed"), status); + assertTrue(status.contains("after 1 entries"), status); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should narrow by provider, service and state when all are named") + void getServiceLiensUsingProvider1_shouldNarrowByAllThree() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 4001, PROVIDER_ID, SERVICE_ID, "Tele Medicine", STATE_ID, "Karnataka", "104", + Boolean.FALSE, "N", Boolean.FALSE }); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1(PROVIDER_ID, SERVICE_ID, STATE_ID)) + .thenReturn(rows); + + assertEquals(1, service.getServiceLiensUsingProvider1(mapping(4001)).size()); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should narrow by provider and service when no state is named") + void getServiceLiensUsingProvider1_shouldNarrowByProviderAndService() { + M_Providerservicemapping_Blocking request = mapping(4001); + request.setStateID(null); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1(PROVIDER_ID, SERVICE_ID)) + .thenReturn(new ArrayList<>()); + + service.getServiceLiensUsingProvider1(request); + + verify(mProviderservicemappingBlockingRepo).getServiceLiensUsingProvider1(PROVIDER_ID, SERVICE_ID); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should narrow by provider alone when no service is named") + void getServiceLiensUsingProvider1_shouldNarrowByProviderAlone() { + M_Providerservicemapping_Blocking request = mapping(4001); + request.setStateID(null); + request.setServiceID(null); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1(PROVIDER_ID)) + .thenReturn(new ArrayList<>()); + + service.getServiceLiensUsingProvider1(request); + + verify(mProviderservicemappingBlockingRepo).getServiceLiensUsingProvider1(PROVIDER_ID); + } + + @Test + @DisplayName("getServiceLiensUsingProvider1 should answer every mapping when the request narrows nothing") + void getServiceLiensUsingProvider1_shouldAnswerEveryMapping() { + M_Providerservicemapping_Blocking request = new M_Providerservicemapping_Blocking(); + when(mProviderservicemappingBlockingRepo.getServiceLiensUsingProvider1()).thenReturn(new ArrayList<>()); + + service.getServiceLiensUsingProvider1(request); + + verify(mProviderservicemappingBlockingRepo).getServiceLiensUsingProvider1(); + } +} diff --git a/src/test/java/com/iemr/admin/service/bulkRegistration/BulkRegistrationServiceImplTest.java b/src/test/java/com/iemr/admin/service/bulkRegistration/BulkRegistrationServiceImplTest.java new file mode 100644 index 0000000..50bd3d1 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/bulkRegistration/BulkRegistrationServiceImplTest.java @@ -0,0 +1,588 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.bulkRegistration; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.bulkuser.BulkRegistrationError; +import com.iemr.admin.data.bulkuser.Employee; +import com.iemr.admin.data.bulkuser.EmployeeList; +import com.iemr.admin.data.employeemaster.M_Community; +import com.iemr.admin.data.employeemaster.M_Gender; +import com.iemr.admin.data.employeemaster.M_Religion; +import com.iemr.admin.data.employeemaster.M_Title; +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_Userqualification; +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.rolemaster.StateMasterForRole; +import com.iemr.admin.repo.employeemaster.V_ShowuserRepo; +import com.iemr.admin.service.employeemaster.EmployeeMasterInter; +import com.iemr.admin.service.locationmaster.LocationMasterServiceInter; +import com.iemr.admin.service.rolemaster.Role_MasterInter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Bulk registration turns an uploaded spreadsheet into user records. A row that + * fails validation must be reported rather than half-saved, so the error log is + * as much a deliverable as the users it creates. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("BulkRegistrationServiceImpl Test Suite") +class BulkRegistrationServiceImplTest { + + /** Excel serial numbers, which is how the uploaded sheet carries its dates. */ + private static final String DOB_SERIAL = "30000"; + private static final String DOJ_SERIAL = "40000"; + + @Mock + private EmployeeMasterInter employeeMasterInter; + + @Mock + private Role_MasterInter roleMasterInter; + + @Mock + private V_ShowuserRepo showuserRepo; + + @Mock + private LocationMasterServiceInter locationMasterServiceInter; + + @Mock + private EmployeeXmlService employeeXmlService; + + @InjectMocks + private BulkRegistrationServiceImpl service; + + private Employee employee; + + @BeforeEach + void setUp() throws Exception { + employee = validEmployee(); + + M_Title title = new M_Title(); + title.setTitleID(1); + title.setTitleName("Ms."); + when(employeeMasterInter.getAllTitle()).thenReturn(new ArrayList<>(List.of(title))); + + M_Gender gender = new M_Gender(); + gender.setGenderID(2); + gender.setGenderName("Female"); + when(employeeMasterInter.getAllGender()).thenReturn(new ArrayList<>(List.of(gender))); + + M_Userqualification qualification = new M_Userqualification(); + qualification.setQualificationID(5); + qualification.setName("MBBS"); + when(employeeMasterInter.getQualification()).thenReturn(new ArrayList<>(List.of(qualification))); + + M_Community community = new M_Community(); + community.setCommunityID(3); + community.setCommunityType("General"); + when(employeeMasterInter.getAllCommunity()).thenReturn(new ArrayList<>(List.of(community))); + + M_Religion religion = new M_Religion(); + religion.setReligionID(4); + religion.setReligionType("Hindu"); + when(employeeMasterInter.getAllReligion()).thenReturn(new ArrayList<>(List.of(religion))); + + StateMasterForRole state = new StateMasterForRole(); + state.setStateID(29); + state.setStateName("Karnataka"); + when(roleMasterInter.getAllState()).thenReturn(new ArrayList<>(List.of(state))); + + M_District district = new M_District(); + district.setDistrictID(301); + district.setDistrictName("Bengaluru Urban"); + when(locationMasterServiceInter.getAllDistrictByStateId(29)).thenReturn(new ArrayList<>(List.of(district))); + + when(employeeMasterInter.FindEmployeeName(anyString())).thenReturn("usernotexist"); + when(employeeMasterInter.FindEmployeeContact(anyString())).thenReturn("contactnotexist"); + when(employeeMasterInter.FindEmployeeAadhaar(anyString())).thenReturn("aadhaarnotexist"); + + M_User1 saved = new M_User1(); + saved.setUserID(3117); + when(employeeMasterInter.saveBulkUserEmployee(any())).thenReturn(saved); + } + + private static Employee validEmployee() { + Employee employee = new Employee(); + employee.setTitle("Ms"); + employee.setFirstName("Asha"); + employee.setMiddleName(""); + employee.setLastName("Rao"); + employee.setGender("Female"); + employee.setContactNo("9000000001"); + employee.setDesignation("ASHA"); + employee.setEmergencyContactNo("9000000002"); + employee.setDob(DOB_SERIAL); + employee.setEmail("asha.rao@example.org"); + employee.setAadhaarNo("111122223333"); + employee.setPan("ABCDEFGH123"); + employee.setQualification("MBBS"); + employee.setFatherName("Ravi"); + employee.setMotherName("Meera"); + employee.setCommunity("General"); + employee.setReligion("Hindu"); + employee.setAddressLine1("Main Road"); + employee.setState("Karnataka"); + employee.setDistrict("Bengaluru Urban"); + employee.setPincode("560001"); + employee.setPermanentAddressLine1("Main Road"); + employee.setPermanentState("Karnataka"); + employee.setPermanentDistrict("Bengaluru Urban"); + employee.setPermanentPincode("560001"); + employee.setDateOfJoining(DOJ_SERIAL); + employee.setUserName("EMP-1"); + employee.setPassword("plain-secret"); + return employee; + } + + private void uploadContains(Employee... employees) throws Exception { + EmployeeList list = new EmployeeList(); + list.setEmployees(new ArrayList<>(List.of(employees))); + when(employeeXmlService.parseXml(anyString())).thenReturn(list); + } + + @Nested + @DisplayName("registerBulkUser") + class RegisterBulkUserTests { + + @Test + @DisplayName("should register a row that passes every rule") + void register_shouldRegisterValidRow() throws Exception { + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(1, service.totalEmployeeListSize); + assertEquals(1, service.m_bulkUser.size()); + assertTrue(service.errorLogs.isEmpty(), service.errorLogs.toString()); + verify(employeeMasterInter).saveBulkUserEmployee(any()); + verify(employeeMasterInter).saveDemography(any()); + } + + @Test + @DisplayName("should carry the uploaded details onto the user it stores") + void register_shouldCarryDetailsOntoStoredUser() throws Exception { + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + M_User1 stored = service.m_bulkUser.get(0); + assertEquals("Asha", stored.getFirstName()); + assertEquals("9000000001", stored.getUserName(), "the contact number is used as the sign-in name"); + assertEquals("EMP-1", stored.getEmployeeID()); + assertEquals(77, stored.getServiceProviderID()); + assertEquals(2, stored.getStatusID()); + assertFalse("plain-secret".equals(stored.getPassword()), "the password must be hashed before storing"); + } + + @Test + @DisplayName("should report a row that names no user") + void register_shouldReportRowWithoutUserName() throws Exception { + employee.setUserName(""); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(1, service.errorLogs.size()); + assertTrue(service.errorLogs.get(0).contains("Please Enter UserName"), service.errorLogs.toString()); + verify(employeeMasterInter, never()).saveBulkUserEmployee(any()); + } + + @Test + @DisplayName("should report a row whose user name is already taken") + void register_shouldReportExistingUser() throws Exception { + when(employeeMasterInter.FindEmployeeName(anyString())).thenReturn("userexist"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("User Already exist"), service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a row whose contact number is already taken") + void register_shouldReportExistingContact() throws Exception { + when(employeeMasterInter.FindEmployeeContact(anyString())).thenReturn("contactexist"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Contact No Already exist"), service.errorLogs.toString()); + } + + @Test + @DisplayName("should report every rule a row breaks rather than only the first") + void register_shouldReportEveryBrokenRule() throws Exception { + employee.setTitle(""); + employee.setFirstName(""); + employee.setLastName(""); + employee.setEmail("not-an-email"); + employee.setContactNo("12345"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("Title is missing."), reported); + assertTrue(reported.contains("First Name is missing."), reported); + assertTrue(reported.contains("Last Name is missing."), reported); + assertTrue(reported.contains("Invalid Email format."), reported); + assertTrue(reported.contains("Contact Number is invalid"), reported); + } + + @Test + @DisplayName("should report a name that is a number rather than a name") + void register_shouldReportNumericName() throws Exception { + employee.setFirstName("12345"); + employee.setLastName("67890"); + employee.setMiddleName("42"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("First name is invalid."), reported); + assertTrue(reported.contains("Last name is invalid."), reported); + assertTrue(reported.contains("Middle name is invalid."), reported); + } + + @Test + @DisplayName("should report a name longer than the column can hold") + void register_shouldReportOverlongName() throws Exception { + String tooLong = "A".repeat(51); + employee.setFirstName(tooLong); + employee.setMiddleName(tooLong); + employee.setLastName(tooLong); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("First name is invalid."), reported); + assertTrue(reported.contains("Middle name is invalid."), reported); + assertTrue(reported.contains("Last name is invalid."), reported); + } + + @Test + @DisplayName("should report a title the master does not know") + void register_shouldReportUnknownTitle() throws Exception { + employee.setTitle("Archduke"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Title is invalid."), service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a district the master does not know") + void register_shouldReportUnknownDistrict() throws Exception { + employee.setDistrict("Nowhere"); + employee.setPermanentDistrict("Nowhere"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("Current District is invalid."), reported); + assertTrue(reported.contains("Permanent District is invalid."), reported); + } + + @Test + @DisplayName("should abandon the upload when a row names a state the master does not know") + void register_shouldAbandonUploadForUnknownState() throws Exception { + employee.setState("Atlantis"); + employee.setPermanentState("Atlantis"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(List.of("Data is invalid or empty"), service.errorLogs, + "an unresolvable state leaves no districts to check against, so the upload is refused"); + assertTrue(service.m_bulkUser.isEmpty()); + } + + @Test + @DisplayName("should report an Aadhaar number that is already on file") + void register_shouldReportDuplicateAadhaar() throws Exception { + when(employeeMasterInter.FindEmployeeAadhaar(anyString())).thenReturn("aadhaarexist"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Duplicate aadhaar number found"), + service.errorLogs.toString()); + } + + @Test + @DisplayName("should report an Aadhaar number that is not twelve digits") + void register_shouldReportMalformedAadhaar() throws Exception { + employee.setAadhaarNo("1234"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Aadhaar number is invalid"), + service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a date of birth in the future") + void register_shouldReportFutureDateOfBirth() throws Exception { + employee.setDob("50000"); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertTrue(service.errorLogs.get(0).contains("Date of Birth is invalid."), + service.errorLogs.toString()); + } + + @Test + @DisplayName("should report a row whose mandatory fields are simply blank") + void register_shouldReportBlankMandatoryFields() throws Exception { + employee.setGender(""); + employee.setContactNo(""); + employee.setDesignation(""); + employee.setEmergencyContactNo(""); + employee.setDob(""); + employee.setEmail(""); + employee.setPassword(""); + employee.setQualification(""); + employee.setState(""); + employee.setDistrict(""); + employee.setPermanentState(""); + employee.setPermanentDistrict(""); + employee.setDateOfJoining(""); + uploadContains(employee); + + service.registerBulkUser("", "auth", "admin", 77); + + String reported = service.errorLogs.get(0); + assertTrue(reported.contains("Gender is missing"), reported); + assertTrue(reported.contains("Contact number missing"), reported); + assertTrue(reported.contains("Designation is missing"), reported); + assertTrue(reported.contains("Emergency contact number is missing"), reported); + assertTrue(reported.contains("Date of Birth is missing."), reported); + assertTrue(reported.contains("Email is missing."), reported); + assertTrue(reported.contains("Qualification is missing"), reported); + assertTrue(reported.contains("Date of Joining is missing."), reported); + } + + @Test + @DisplayName("should report an upload that carries no rows at all") + void register_shouldReportEmptyUpload() throws Exception { + EmployeeList list = new EmployeeList(); + list.setEmployees(new ArrayList<>()); + when(employeeXmlService.parseXml(anyString())).thenReturn(list); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(List.of("Data is invalid or empty"), service.errorLogs); + } + + @Test + @DisplayName("should report an upload it cannot read at all") + void register_shouldReportUnreadableUpload() throws Exception { + when(employeeXmlService.parseXml(anyString())).thenThrow(new IllegalStateException("not xml")); + + service.registerBulkUser("not xml", "auth", "admin", 77); + + assertEquals(List.of("Data is invalid or empty"), service.errorLogs); + } + + @Test + @DisplayName("should keep going through the sheet after a row it cannot register") + void register_shouldKeepGoingAfterABadRow() throws Exception { + Employee bad = validEmployee(); + bad.setUserName(""); + uploadContains(bad, employee); + + service.registerBulkUser("", "auth", "admin", 77); + + assertEquals(2, service.totalEmployeeListSize); + assertEquals(1, service.m_bulkUser.size(), "the good row must still be registered"); + assertEquals(1, service.errorLogs.size()); + } + } + + @Nested + @DisplayName("Master lookups") + class MasterLookupTests { + + @Test + @DisplayName("getCommunityId should resolve a known community and answer zero for an unknown one") + void getCommunityId_shouldResolveKnownCommunity() { + assertEquals(3, service.getCommunityId("General")); + assertEquals(0, service.getCommunityId("Unknown")); + } + + @Test + @DisplayName("getQualificationId should resolve a known qualification and answer zero for an unknown one") + void getQualificationId_shouldResolveKnownQualification() { + assertEquals(5, service.getQualificationId("MBBS")); + assertEquals(0, service.getQualificationId("Unknown")); + } + + @Test + @DisplayName("getReligionStringId should resolve a known religion") + void getReligionStringId_shouldResolveKnownReligion() { + assertEquals(4, service.getReligionStringId("Hindu")); + assertEquals(0, service.getReligionStringId("Unknown")); + } + + @Test + @DisplayName("getReligionStringId should treat an unstated religion as none") + void getReligionStringId_shouldTreatUnstatedAsNone() { + assertEquals(0, service.getReligionStringId("Not given")); + verify(employeeMasterInter, never()).getAllReligion(); + } + + @Test + @DisplayName("getStateId should resolve a known state and load its districts") + void getStateId_shouldResolveKnownStateAndLoadDistricts() { + assertEquals(29, service.getStateId("Karnataka")); + verify(locationMasterServiceInter).getAllDistrictByStateId(29); + } + + @Test + @DisplayName("getStateId should answer zero for a state the master does not know") + void getStateId_shouldAnswerZeroForUnknownState() { + assertEquals(0, service.getStateId("Atlantis")); + verify(locationMasterServiceInter, never()).getAllDistrictByStateId(29); + } + + @Test + @DisplayName("getDistrictId should resolve a district once its state has been resolved") + void getDistrictId_shouldResolveDistrictAfterState() { + service.getStateId("Karnataka"); + + assertEquals(301, service.getDistrictId("Bengaluru Urban")); + assertEquals(0, service.getDistrictId("Nowhere")); + } + + @Test + @DisplayName("getDistrictId should answer zero when no district name is given") + void getDistrictId_shouldAnswerZeroWithoutAName() { + assertEquals(0, service.getDistrictId("")); + } + + @Test + @DisplayName("getAllState should hand back what the role master holds") + void getAllState_shouldHandBackRoleMasterContents() { + assertEquals(1, service.getAllState().size()); + } + + @Test + @DisplayName("getDesignationId should answer the fixed designation the upload uses") + void getDesignationId_shouldAnswerFixedDesignation() { + assertEquals(20, service.getDesignationId("ASHA")); + } + } + + @Nested + @DisplayName("Helpers") + class HelperTests { + + @Test + @DisplayName("escapeXmlSpecialChars should escape a bare ampersand and leave real entities alone") + void escape_shouldEscapeBareAmpersandOnly() { + assertEquals("Ram & Co", BulkRegistrationServiceImpl.escapeXmlSpecialChars("Ram & Co")); + assertEquals("Ram & Co", BulkRegistrationServiceImpl.escapeXmlSpecialChars("Ram & Co")); + assertEquals("<tag>", BulkRegistrationServiceImpl.escapeXmlSpecialChars("<tag>")); + } + + @Test + @DisplayName("isNumeric should tell a number apart from a name") + void isNumeric_shouldTellNumberFromName() { + assertTrue(BulkRegistrationServiceImpl.isNumeric("12345")); + assertFalse(BulkRegistrationServiceImpl.isNumeric("Asha")); + } + + @Test + @DisplayName("isValidAadhar should report anything that is not twelve digits") + void isValidAadhar_shouldReportNonTwelveDigitNumbers() { + assertFalse(BulkRegistrationServiceImpl.isValidAadhar("111122223333")); + assertTrue(BulkRegistrationServiceImpl.isValidAadhar("1234")); + assertTrue(BulkRegistrationServiceImpl.isValidAadhar("not-a-number")); + } + + @Test + @DisplayName("convertStringIntoDate should read the spreadsheet's own date serial") + void convertStringIntoDate_shouldReadExcelSerial() { + assertEquals("1982-02-18", BulkRegistrationServiceImpl.convertStringIntoDate(DOB_SERIAL).toString()); + } + + @Test + @DisplayName("generateStrongPassword should answer a different hash each time it is called") + void generateStrongPassword_shouldSaltEachHash() throws Exception { + String first = service.generateStrongPassword("plain-secret"); + String second = service.generateStrongPassword("plain-secret"); + + assertTrue(first.startsWith("1001:"), first); + assertFalse(first.equals(second), "each hash must carry its own salt"); + } + + @Test + @DisplayName("insertErrorLog should write one workbook row per reported row") + void insertErrorLog_shouldWriteOneRowPerReportedRow() { + BulkRegistrationError error = new BulkRegistrationError(); + error.setRowNumber(1); + error.setUserName("EMP-1"); + error.setError(List.of("Title is missing.")); + service.bulkRegistrationErrors.add(error); + + byte[] workbook = service.insertErrorLog(); + + assertNotNull(workbook); + assertTrue(workbook.length > 0, "a workbook with a reported row must not be empty"); + } + + @Test + @DisplayName("insertErrorLog should still answer a workbook when nothing was reported") + void insertErrorLog_shouldAnswerWorkbookWithoutErrors() { + assertTrue(service.insertErrorLog().length > 0); + } + } +} diff --git a/src/test/java/com/iemr/admin/service/bulkRegistration/EmployeeXmlServiceTest.java b/src/test/java/com/iemr/admin/service/bulkRegistration/EmployeeXmlServiceTest.java new file mode 100644 index 0000000..cce6537 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/bulkRegistration/EmployeeXmlServiceTest.java @@ -0,0 +1,58 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.bulkRegistration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.iemr.admin.data.bulkuser.EmployeeList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** Reads the uploaded spreadsheet, which reaches the service as XML. */ +@DisplayName("EmployeeXmlService Test Suite") +class EmployeeXmlServiceTest { + + private final EmployeeXmlService service = new EmployeeXmlService(); + + @Test + @DisplayName("parseXml should read each employee row out of the uploaded document") + void parseXml_shouldReadEachEmployeeRow() throws Exception { + String xml = "AshaRao" + + "EMP-1" + + "RaviKumar" + + "EMP-2"; + + EmployeeList list = service.parseXml(xml); + + assertEquals(2, list.getEmployees().size()); + assertEquals("Asha", list.getEmployees().get(0).getFirstName()); + assertEquals("EMP-2", list.getEmployees().get(1).getUserName()); + } + + @Test + @DisplayName("parseXml should raise rather than answer a half-read document") + void parseXml_shouldRaiseForMalformedDocument() { + assertThrows(Exception.class, () -> service.parseXml("")); + } +} diff --git a/src/test/java/com/iemr/admin/service/calibration/CalibrationServiceImplTest.java b/src/test/java/com/iemr/admin/service/calibration/CalibrationServiceImplTest.java new file mode 100644 index 0000000..79d51be --- /dev/null +++ b/src/test/java/com/iemr/admin/service/calibration/CalibrationServiceImplTest.java @@ -0,0 +1,222 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.calibration; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.admin.data.calibration.CalibrationStrip; +import com.iemr.admin.repo.calibration.CalibrationRepo; +import com.iemr.admin.utils.exception.IEMRException; +import com.iemr.admin.utils.mapper.OutputMapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The calibration service keeps the test strip codes a provider calibrates + * against, refusing a code the provider already holds. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CalibrationServiceImpl Test Suite") +class CalibrationServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Long STRIP_ID = 6601L; + private static final String STRIP_CODE = "STRIP-77"; + + @Mock + private CalibrationRepo calibrationRepo; + + @InjectMocks + private CalibrationServiceImpl service; + + @BeforeEach + @DisplayName("Fix the page size the screens are served in and prime the shared output builder") + void setUp() { + ReflectionTestUtils.setField(service, "calibrationPageSize", 10); + new OutputMapper(); + } + + private static CalibrationStrip strip() { + CalibrationStrip strip = new CalibrationStrip(); + strip.setCalibrationStripID(STRIP_ID); + strip.setStripCode(STRIP_CODE); + strip.setProviderServiceMapID(PSM_ID); + strip.setDeleted(Boolean.FALSE); + return strip; + } + + private static CalibrationStrip request() { + CalibrationStrip request = new CalibrationStrip(); + request.setStripCode(STRIP_CODE); + request.setProviderServiceMapID(PSM_ID); + return request; + } + + @Test + @DisplayName("saveData should record a strip code the provider does not hold yet") + void save_shouldRecordNewStripCode() throws Exception { + when(calibrationRepo.checkIfAlreadyStripPresent(PSM_ID, STRIP_CODE)).thenReturn(new ArrayList<>()); + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(strip()); + + assertEquals(1, service.saveData(request())); + } + + @Test + @DisplayName("saveData should refuse a strip code the provider already holds") + void save_shouldRefuseDuplicateStripCode() { + when(calibrationRepo.checkIfAlreadyStripPresent(PSM_ID, STRIP_CODE)) + .thenReturn(new ArrayList<>(List.of(strip()))); + + IEMRException refusal = assertThrows(IEMRException.class, () -> service.saveData(request())); + + assertEquals("Strip code already exists", refusal.getMessage()); + verify(calibrationRepo, never()).save(any(CalibrationStrip.class)); + } + + @Test + @DisplayName("saveData should refuse a strip the repository did not give an identity") + void save_shouldRefuseStripWithoutIdentity() { + when(calibrationRepo.checkIfAlreadyStripPresent(PSM_ID, STRIP_CODE)).thenReturn(new ArrayList<>()); + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(new CalibrationStrip()); + + assertEquals("Error while saving data", + assertThrows(IEMRException.class, () -> service.saveData(request())).getMessage()); + } + + @Test + @DisplayName("saveData should record nothing when the request names no strip code") + void save_shouldRecordNothingWithoutStripCode() throws Exception { + assertEquals(0, service.saveData(new CalibrationStrip())); + verify(calibrationRepo, never()).save(any(CalibrationStrip.class)); + } + + @Test + @DisplayName("fetchData should answer one page of strips and how many pages there are") + void fetch_shouldAnswerOnePageAndPageCount() throws Exception { + CalibrationStrip request = request(); + request.setPageNo(0); + Pageable pageable = PageRequest.of(0, 10); + Page page = new PageImpl<>(List.of(strip()), pageable, 1); + when(calibrationRepo.getCalibrationStripsWithPagination(PSM_ID, pageable)).thenReturn(page); + + String answered = service.fetchData(request); + + assertTrue(answered.contains(STRIP_CODE), answered); + assertTrue(answered.contains("pageCount"), answered); + } + + @Test + @DisplayName("fetchData should answer every strip when the caller asks for no particular page") + void fetch_shouldAnswerEveryStripWithoutPaging() throws Exception { + when(calibrationRepo.getCalibrationStripsWithoutPagination(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(strip()))); + + String answered = service.fetchData(request()); + + assertTrue(answered.contains(STRIP_CODE), answered); + assertTrue(!answered.contains("pageCount"), "an unpaged answer carries no page count"); + } + + @Test + @DisplayName("fetchData should refuse a request that names no provider") + void fetch_shouldRefuseRequestWithoutProvider() { + assertThrows(IEMRException.class, () -> service.fetchData(new CalibrationStrip())); + } + + @Test + @DisplayName("deleteData should report how many strips the retirement touched") + void delete_shouldReportRowsTouched() throws Exception { + CalibrationStrip request = strip(); + request.setDeleted(Boolean.TRUE); + when(calibrationRepo.deleteCalibrationStrip(STRIP_ID, Boolean.TRUE)).thenReturn(1); + + assertEquals(1, service.deleteData(request)); + } + + @Test + @DisplayName("deleteData should refuse a request that names no strip") + void delete_shouldRefuseRequestWithoutStrip() { + assertEquals("Invalid request", + assertThrows(IEMRException.class, () -> service.deleteData(new CalibrationStrip())).getMessage()); + } + + @Test + @DisplayName("deleteData should give up when the retirement cannot be recorded") + void delete_shouldGiveUpWhenStorageFails() { + CalibrationStrip request = strip(); + request.setDeleted(Boolean.TRUE); + when(calibrationRepo.deleteCalibrationStrip(anyLong(), anyBoolean())) + .thenThrow(new RuntimeException("row is locked")); + + assertThrows(IEMRException.class, () -> service.deleteData(request)); + } + + @Test + @DisplayName("updateData should record the change against the strip") + void update_shouldRecordChange() throws Exception { + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(strip()); + + assertEquals(1, service.updateData(request())); + } + + @Test + @DisplayName("updateData should refuse a strip the repository did not give an identity") + void update_shouldRefuseStripWithoutIdentity() { + when(calibrationRepo.save(any(CalibrationStrip.class))).thenReturn(new CalibrationStrip()); + + assertEquals("Error while updating data", + assertThrows(IEMRException.class, () -> service.updateData(request())).getMessage()); + } + + @Test + @DisplayName("updateData should record nothing when the request names no strip code") + void update_shouldRecordNothingWithoutStripCode() throws Exception { + assertEquals(0, service.updateData(new CalibrationStrip())); + } +} diff --git a/src/test/java/com/iemr/admin/service/drugstrangth/DrugStrangthServiceTest.java b/src/test/java/com/iemr/admin/service/drugstrangth/DrugStrangthServiceTest.java new file mode 100644 index 0000000..b8b1632 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/drugstrangth/DrugStrangthServiceTest.java @@ -0,0 +1,112 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.drugstrangth; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.drugstrangth.M_104DrugStrength; +import com.iemr.admin.repo.blocking.DrugStrangthRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The drug strength service keeps the strengths a drug can be dispensed in. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DrugStrangthService Test Suite") +class DrugStrangthServiceTest { + + private static final Integer STRENGTH_ID = 33; + + @Mock + private DrugStrangthRepo drugStrangthRepo; + + @InjectMocks + private DrugStrangthService service; + + private static M_104DrugStrength strength() { + M_104DrugStrength strength = new M_104DrugStrength(); + strength.setDrugStrengthID(STRENGTH_ID); + strength.setDrugStrength("500 mg"); + return strength; + } + + @Test + @DisplayName("createDrugStrangth should answer the strengths the repository stored") + void create_shouldAnswerStoredStrengths() { + ArrayList stored = new ArrayList<>(List.of(strength())); + when(drugStrangthRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createDrugStrangth(new ArrayList<>())); + } + + @Test + @DisplayName("getDrugStrangth should answer every strength on file") + void get_shouldAnswerEveryStrength() { + when(drugStrangthRepo.findAll()).thenReturn(new ArrayList<>(List.of(strength()))); + + assertEquals(1, service.getDrugStrangth().size()); + } + + @Test + @DisplayName("getDrugStrangth should answer nothing when no strength is on file") + void get_shouldAnswerNothingWhenNoneOnFile() { + when(drugStrangthRepo.findAll()).thenReturn(new ArrayList()); + + assertTrue(service.getDrugStrangth().isEmpty()); + } + + @Test + @DisplayName("updateDrugStrangth and saveupdatedData should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_104DrugStrength stored = strength(); + when(drugStrangthRepo.findByDrugStrengthID(STRENGTH_ID)).thenReturn(stored); + when(drugStrangthRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.updateDrugStrangth(STRENGTH_ID)); + assertSame(stored, service.saveupdatedData(stored)); + } + + @Test + @DisplayName("updateDrugStrangth should answer nothing when the strength is unknown") + void update_shouldAnswerNothingForUnknownStrength() { + when(drugStrangthRepo.findByDrugStrengthID(-1)).thenReturn(null); + + assertNull(service.updateDrugStrangth(-1)); + } +} diff --git a/src/test/java/com/iemr/admin/service/drugtype/DrugtypeServiceImplTest.java b/src/test/java/com/iemr/admin/service/drugtype/DrugtypeServiceImplTest.java new file mode 100644 index 0000000..bfdfebc --- /dev/null +++ b/src/test/java/com/iemr/admin/service/drugtype/DrugtypeServiceImplTest.java @@ -0,0 +1,113 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.drugtype; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.drugtype.M_Drugtype; +import com.iemr.admin.repo.drugtype.DrugtypeRepo; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The drug type service keeps the dosage forms a provider stocks. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DrugtypeServiceImpl Test Suite") +class DrugtypeServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer DRUG_TYPE_ID = 21; + + @Mock + private DrugtypeRepo drugtypeRepo; + + @InjectMocks + private DrugtypeServiceImpl service; + + private static M_Drugtype drugType() { + M_Drugtype drugType = new M_Drugtype(); + drugType.setDrugTypeID(DRUG_TYPE_ID); + drugType.setDrugTypeName("Tablet"); + drugType.setProviderServiceMapID(PSM_ID); + return drugType; + } + + @Test + @DisplayName("createDrugtypeData should answer the drug types the repository stored") + void create_shouldAnswerStoredDrugTypes() { + ArrayList stored = new ArrayList<>(List.of(drugType())); + when(drugtypeRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createDrugtypeData(new ArrayList<>())); + } + + @Test + @DisplayName("createDrugtypeData should answer nothing when the repository stored nothing") + void create_shouldAnswerNothingWhenNothingStored() { + when(drugtypeRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createDrugtypeData(new ArrayList<>())); + } + + @Test + @DisplayName("getDrugtypeData should answer the drug types the provider stocks") + void get_shouldAnswerProvidersDrugTypes() { + ArrayList stocked = new ArrayList<>(List.of(drugType())); + when(drugtypeRepo.getDrugtypeData(PSM_ID)).thenReturn(stocked); + + assertSame(stocked, service.getDrugtypeData(PSM_ID)); + } + + @Test + @DisplayName("editDrugtypeData and saveeditDrugtype should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_Drugtype stored = drugType(); + when(drugtypeRepo.geteditedData(DRUG_TYPE_ID)).thenReturn(stored); + when(drugtypeRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.editDrugtypeData(DRUG_TYPE_ID)); + assertSame(stored, service.saveeditDrugtype(stored)); + } + + @Test + @DisplayName("editDrugtypeData should answer nothing when the drug type is unknown") + void edit_shouldAnswerNothingForUnknownDrugType() { + when(drugtypeRepo.geteditedData(-1)).thenReturn(null); + + assertNull(service.editDrugtypeData(-1)); + } +} diff --git a/src/test/java/com/iemr/admin/service/emailconfig/EmailConfigServiceImplTest.java b/src/test/java/com/iemr/admin/service/emailconfig/EmailConfigServiceImplTest.java new file mode 100644 index 0000000..11b5b8b --- /dev/null +++ b/src/test/java/com/iemr/admin/service/emailconfig/EmailConfigServiceImplTest.java @@ -0,0 +1,189 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.emailconfig; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.emailconfig.AuthorityEmail; +import com.iemr.admin.mapper.emailconfig.InstituteEmailConfigMapper; +import com.iemr.admin.model.emailconfig.AuthEmailRequest; +import com.iemr.admin.model.emailconfig.AuthEmailResponse; +import com.iemr.admin.model.emailconfig.CreateAuthEmailRequestModel; +import com.iemr.admin.model.emailconfig.UpdateAuthEmailRequest; +import com.iemr.admin.repository.emailconfig.InstituteEmailRepo; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The email config service keeps the authority mailboxes a complaint is copied + * to, narrowed by whichever parts of the location the caller names. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("EmailConfigServiceImpl Test Suite") +class EmailConfigServiceImplTest { + + @Mock + private EntityManager entityManager; + + @Mock + private InstituteEmailRepo instituteRepo; + + @Mock + private InstituteEmailConfigMapper instituteEmailConfigMapper; + + @InjectMocks + private EmailConfigServiceImpl service; + + private CriteriaQuery query; + private TypedQuery typedQuery; + + @SuppressWarnings("unchecked") + @BeforeEach + @DisplayName("Stand in for the criteria query the service builds by hand") + void setUp() { + CriteriaBuilder builder = mock(CriteriaBuilder.class); + query = mock(CriteriaQuery.class); + Root root = mock(Root.class); + typedQuery = mock(TypedQuery.class); + + when(entityManager.getCriteriaBuilder()).thenReturn(builder); + when(builder.createQuery(AuthorityEmail.class)).thenReturn(query); + when(query.from(AuthorityEmail.class)).thenReturn(root); + when(query.select(any())).thenReturn(query); + when(query.where(any(Predicate[].class))).thenReturn(query); + when(query.orderBy(any(jakarta.persistence.criteria.Order[].class))).thenReturn(query); + when(root.get(anyString())).thenReturn(mock(Path.class)); + when(builder.equal(any(), any())).thenReturn(mock(Predicate.class)); + when(entityManager.createQuery(query)).thenReturn(typedQuery); + } + + private static AuthEmailRequest fullyNarrowedRequest() { + AuthEmailRequest request = new AuthEmailRequest(); + request.setAuthorityEmailID(1); + request.setDeleted(false); + request.setDistrictID(301); + request.setDistrictBranchMappingID(30111); + request.setBlockID(3011); + request.setProviderServiceMapID(4001); + request.setStateID(29); + return request; + } + + @Test + @DisplayName("getAllEmailConfigs should answer the mailboxes the query found, as the screens read them") + void getAll_shouldAnswerFoundMailboxes() { + List found = List.of(new AuthorityEmail()); + List published = List.of(new AuthEmailResponse()); + when(typedQuery.getResultList()).thenReturn(found); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponse(found)).thenReturn(published); + + assertSame(published, service.getAllEmailConfigs(fullyNarrowedRequest())); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow the query by every detail the caller named") + void getAll_shouldNarrowByEveryNamedDetail() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllEmailConfigs(fullyNarrowedRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(7, captor.getValue().length, "one narrowing per detail the caller named"); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow by nothing when the caller names nothing") + void getAll_shouldNarrowByNothingForEmptyRequest() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllEmailConfigs(new AuthEmailRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(0, captor.getValue().length); + } + + @Test + @DisplayName("saveEmailConfigs should store one mailbox per request and answer each as stored") + void save_shouldStoreEachRequestedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + when(instituteEmailConfigMapper.createRequestToInstituteEmailConf(anyList())) + .thenReturn(List.of(stored, stored)); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponse(stored)) + .thenReturn(new AuthEmailResponse()); + + assertEquals(2, service.saveEmailConfigs(List.of(new CreateAuthEmailRequestModel())).size()); + } + + @Test + @DisplayName("saveEmailConfigs should store nothing when the caller asks for nothing") + void save_shouldStoreNothingForEmptyRequest() { + when(instituteEmailConfigMapper.createRequestToInstituteEmailConf(anyList())) + .thenReturn(new ArrayList<>()); + + assertTrue(service.saveEmailConfigs(new ArrayList<>()).isEmpty()); + } + + @Test + @DisplayName("updateEmailConfigs should answer the mailbox as it stands after the change") + void update_shouldAnswerChangedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + AuthEmailResponse published = new AuthEmailResponse(); + when(instituteEmailConfigMapper.updateRequestToInstituteEmailConf(any(UpdateAuthEmailRequest.class))) + .thenReturn(stored); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponse(stored)).thenReturn(published); + + assertSame(published, service.updateEmailConfigs(new UpdateAuthEmailRequest())); + } +} diff --git a/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterServiceImplTest.java b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterServiceImplTest.java new file mode 100644 index 0000000..e70bfee --- /dev/null +++ b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterServiceImplTest.java @@ -0,0 +1,1195 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.employeemaster; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.employeemaster.EmployeeSignature; +import com.iemr.admin.data.employeemaster.M_Community; +import com.iemr.admin.data.employeemaster.M_Gender; +import com.iemr.admin.data.employeemaster.M_ProviderServiceMap1; +import com.iemr.admin.data.employeemaster.M_Religion; +import com.iemr.admin.data.employeemaster.M_Role; +import com.iemr.admin.data.employeemaster.M_Title; +import com.iemr.admin.data.employeemaster.M_User1; +import com.iemr.admin.data.employeemaster.M_UserDemographics; +import com.iemr.admin.data.employeemaster.M_UserLangMapping; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.employeemaster.M_Userqualification; +import com.iemr.admin.data.employeemaster.Showofficedetails1; +import com.iemr.admin.data.employeemaster.Showuserdetailsfromuserservicerolemapping; +import com.iemr.admin.data.employeemaster.V_Showuser; +import com.iemr.admin.data.employeemaster.V_Userservicerolemapping; +import com.iemr.admin.data.facilitytype.M_facilitytype; +import com.iemr.admin.data.rolemaster.M_UserservicerolemappingForRoleProviderAdmin; +import com.iemr.admin.data.rolemaster.UserRole; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.exceptionhandler.DataNotFound; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepo; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepoo; +import com.iemr.admin.repo.employeemaster.EmployeeSignatureRepo; +import com.iemr.admin.repo.employeemaster.M_CommunityRepo; +import com.iemr.admin.repo.employeemaster.M_GenderRepo; +import com.iemr.admin.repo.employeemaster.M_ProviderServiceMap1Repo; +import com.iemr.admin.repo.employeemaster.M_QualificationRepo; +import com.iemr.admin.repo.employeemaster.M_ReligionRepo; +import com.iemr.admin.repo.employeemaster.M_TitleRepo; +import com.iemr.admin.repo.employeemaster.M_UserDemographicsRepo; +import com.iemr.admin.repo.employeemaster.M_UserLangMappingRepo; +import com.iemr.admin.repo.employeemaster.RoleRepo; +import com.iemr.admin.repo.employeemaster.Showofficedetails1Repo1; +import com.iemr.admin.repo.employeemaster.ShowuserdetailsfromuserservicerolemappingRepo; +import com.iemr.admin.repo.employeemaster.V_ShowuserRepo; +import com.iemr.admin.repo.employeemaster.V_UserservicerolemappingRepo; +import com.iemr.admin.repository.facilitytype.M_facilitytypeRepo; +import com.iemr.admin.repository.rolemaster.M_UserservicerolemappingForRoleProviderAdminRepo; +import com.iemr.admin.repository.store.MainStoreRepo; +import com.iemr.admin.service.user.EncryptUserPassword; +import com.iemr.admin.utils.response.OutputResponse; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The employee master service is where the rules about who may work where live: + * an ASHA must sit at a sub-centre, a role may not be mapped twice, and taking a + * role away has to take the supervisor mappings with it. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("EmployeeMasterServiceImpl Test Suite") +class EmployeeMasterServiceImplTest { + + private static final Integer USER_ID = 3117; + private static final Integer PSM_ID = 4001; + + @Mock + private RoleRepo roleRepo; + + @Mock + private EmployeeMasterRepo employeeMasterRepo; + + @Mock + private EmployeeMasterRepoo employeeMasterRepo11; + + @Mock + private EmployeeMasterRepoo employeeMasterRepoo; + + @Mock + private M_UserDemographicsRepo m_UserDemographicsRepo; + + @Mock + private M_UserLangMappingRepo m_UserLangMappingRepo; + + @Mock + private M_TitleRepo m_TitleRepo; + + @Mock + private M_GenderRepo m_GenderRepo; + + @Mock + private ShowuserdetailsfromuserservicerolemappingRepo showuserdetailsfromuserservicerolemappingRepo; + + @Mock + private V_ShowuserRepo v_ShowuserRepo; + + @Mock + private V_UserservicerolemappingRepo v_UserservicerolemappingRepo; + + @Mock + private M_QualificationRepo m_QualificationRepo; + + @Mock + private Showofficedetails1Repo1 showofficedetails1Repo1; + + @Mock + private M_ProviderServiceMap1Repo m_ProviderServiceMap1Repo; + + @Mock + private MainStoreRepo mainStoreRepo; + + @Mock + private M_facilitytypeRepo facilityTypeRepo; + + @Mock + private EmployeeSignatureRepo employeeSignatureRepo; + + @Mock + private M_CommunityRepo m_CommunityRepo; + + @Mock + private M_ReligionRepo m_ReligionRepo; + + @Mock + private M_UserservicerolemappingForRoleProviderAdminRepo userservicerolemappingForRoleProviderAdminRepo; + + @Mock + private AshaSupervisorMappingService ashaSupervisorMappingService; + + @Mock + private EncryptUserPassword encryptUserPassword; + + @InjectMocks + private EmployeeMasterServiceImpl service; + + private M_Role role; + + @BeforeEach + void setUp() { + role = new M_Role(); + role.setRoleID(11); + role.setRoleName("Counsellor"); + when(roleRepo.findByRoleID(anyInt())).thenReturn(role); + } + + private static M_UserServiceRoleMapping2 mapping(Integer id, Integer roleId) { + M_UserServiceRoleMapping2 mapping = new M_UserServiceRoleMapping2(); + mapping.setuSRMappingID(id); + mapping.setUserID(USER_ID); + mapping.setRoleID(roleId); + mapping.setProviderServiceMapID(PSM_ID); + return mapping; + } + + private void namedRole(String name) { + role.setRoleName(name); + } + + private void activeFacility(Integer facilityId, Integer typeId, Integer levelValue, Integer maxLevel) { + M_Facility facility = new M_Facility(); + facility.setFacilityID(facilityId); + facility.setFacilityTypeID(typeId); + when(mainStoreRepo.findByFacilityIDAndDeleted(facilityId, false)).thenReturn(facility); + M_facilitytype type = new M_facilitytype(); + type.setFacilityTypeID(typeId); + type.setLevelValue(levelValue); + when(facilityTypeRepo.findByFacilityTypeID(typeId)).thenReturn(type); + when(facilityTypeRepo.findMaxLevelValueByProviderServiceMapID(PSM_ID)).thenReturn(maxLevel); + } + + @Nested + @DisplayName("mapRole") + class MapRoleTests { + + @Test + @DisplayName("should refuse an ASHA mapping that names no facility") + void mapRole_shouldRefuseAshaWithoutFacility() { + namedRole("ASHA"); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(mapping(null, 11)), "auth")); + + assertTrue(thrown.getMessage().contains("Facility (SC) is mandatory for ASHA role"), + thrown.getMessage()); + verify(employeeMasterRepo, never()).saveAll(anyList()); + } + + @Test + @DisplayName("should refuse a mapping onto a facility that has been retired") + void mapRole_shouldRefuseRetiredFacility() { + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(501); + when(mainStoreRepo.findByFacilityIDAndDeleted(501, false)).thenReturn(null); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(toMap), "auth")); + + assertTrue(thrown.getMessage().contains("is no longer active"), thrown.getMessage()); + } + + @Test + @DisplayName("should refuse an ASHA mapped above sub-centre level") + void mapRole_shouldRefuseAshaAboveSubCentre() { + namedRole("ASHA"); + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(501); + activeFacility(501, 3, 2, 4); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(toMap), "auth")); + + assertTrue(thrown.getMessage().contains("Sub-Centre (SC) level facility"), thrown.getMessage()); + } + + @Test + @DisplayName("should refuse a second active mapping for the same user, role and service line") + void mapRole_shouldRefuseDuplicateMapping() { + when(employeeMasterRepo.existsByUserIDAndRoleIDAndProviderServiceMapIDAndDeletedFalse(USER_ID, 11, PSM_ID)) + .thenReturn(true); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(mapping(null, 11)), "auth")); + + assertTrue(thrown.getMessage().contains("Duplicate mapping is not allowed"), thrown.getMessage()); + } + + @Test + @DisplayName("should refuse a second mapping for an ASHA supervisor at the same facility") + void mapRole_shouldRefuseDuplicateSupervisorMappingAtSameFacility() { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(501); + activeFacility(501, 3, 4, 4); + when(employeeMasterRepo + .existsByUserIDAndRoleIDAndProviderServiceMapIDAndFacilityIDAndDeletedFalse( + USER_ID, 11, PSM_ID, 501)) + .thenReturn(true); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.mapRole(List.of(toMap), "auth")); + + assertTrue(thrown.getMessage().contains("active work location mapping for this facility"), + thrown.getMessage()); + } + + @Test + @DisplayName("should let an ASHA supervisor hold a second mapping at a different facility") + void mapRole_shouldAllowSupervisorAtDifferentFacility() throws Exception { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setFacilityID(502); + activeFacility(502, 3, 4, 4); + ArrayList stored = new ArrayList<>(List.of(toMap)); + when(employeeMasterRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.mapRole(List.of(toMap), "auth")); + } + + @Test + @DisplayName("should flatten the village lists onto the stored columns") + void mapRole_shouldFlattenVillageLists() throws Exception { + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + toMap.setVillageID(new String[] { "501", "502" }); + toMap.setVillageName(new String[] { "Hosur", "Devanahalli" }); + when(employeeMasterRepo.saveAll(anyList())) + .thenReturn(new ArrayList<>(List.of(toMap))); + + service.mapRole(List.of(toMap), "auth"); + + assertEquals("501,502", toMap.getVillageidDb()); + assertEquals("Hosur,Devanahalli", toMap.getVillageNameDb()); + verify(employeeMasterRepo).save(toMap); + } + + @Test + @DisplayName("should leave the village columns alone when the mapping names no villages") + void mapRole_shouldLeaveVillageColumnsAlone() throws Exception { + M_UserServiceRoleMapping2 toMap = mapping(null, 11); + when(employeeMasterRepo.saveAll(anyList())).thenReturn(new ArrayList<>(List.of(toMap))); + + service.mapRole(List.of(toMap), "auth"); + + assertNull(toMap.getVillageidDb()); + verify(employeeMasterRepo, never()).save(any()); + } + } + + @Nested + @DisplayName("saveRoleMappingeditedData") + class SaveRoleMappingTests { + + @Test + @DisplayName("should refuse an ASHA edit that drops the facility") + void saveRoleMapping_shouldRefuseAshaWithoutFacility() { + namedRole("ASHA"); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> service.saveRoleMappingeditedData(mapping(9001, 11), "auth")); + + assertTrue(thrown.getMessage().contains("Facility (SC) is mandatory for ASHA role"), + thrown.getMessage()); + } + + @Test + @DisplayName("should skip the facility rules when the mapping is only being deactivated") + void saveRoleMapping_shouldSkipRulesWhenDeactivating() throws Exception { + namedRole("ASHA"); + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setDeleted(Boolean.TRUE); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + assertSame(toSave, service.saveRoleMappingeditedData(toSave, "auth")); + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should clear the saved villages when the facility is above sub-centre level") + void saveRoleMapping_shouldClearVillagesAboveSubCentre() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setFacilityID(501); + toSave.setVillageidDb("501,502"); + toSave.setVillageNameDb("Hosur,Devanahalli"); + activeFacility(501, 3, 2, 4); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.empty()); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + assertNull(toSave.getVillageidDb(), "a non sub-centre posting keeps no village list"); + assertNull(toSave.getVillageNameDb()); + } + + @Test + @DisplayName("should cascade the supervisor mappings when the role changes") + void saveRoleMapping_shouldCascadeWhenRoleChanges() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 12); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should cascade only this facility when a supervisor still works elsewhere") + void saveRoleMapping_shouldCascadeOnlyThisFacilityForBusySupervisor() throws Exception { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setDeleted(Boolean.TRUE); + toSave.setFacilityID(501); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + old.setFacilityID(501); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.countByUserIDAndRoleIDAndDeletedFalse(USER_ID, 11)).thenReturn(2L); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + verify(ashaSupervisorMappingService).cascadeDeleteByFacilityID(501, "Admin"); + verify(ashaSupervisorMappingService, never()).cascadeDeleteByUserID(anyInt(), anyString()); + } + + @Test + @DisplayName("should cascade every mapping when the facility itself changes") + void saveRoleMapping_shouldCascadeWhenFacilityChanges() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setFacilityID(502); + activeFacility(502, 3, 4, 4); + M_UserServiceRoleMapping2 old = mapping(9001, 11); + old.setFacilityID(501); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.of(old)); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should flatten the village lists onto the stored columns") + void saveRoleMapping_shouldFlattenVillageLists() throws Exception { + M_UserServiceRoleMapping2 toSave = mapping(9001, 11); + toSave.setVillageID(new String[] { "501", "502" }); + toSave.setVillageName(new String[] { "Hosur", "Devanahalli" }); + when(employeeMasterRepo.findById(9001)).thenReturn(Optional.empty()); + when(employeeMasterRepo.save(toSave)).thenReturn(toSave); + + service.saveRoleMappingeditedData(toSave, "auth"); + + assertEquals("501,502", toSave.getVillageidDb()); + assertEquals("Hosur,Devanahalli", toSave.getVillageNameDb()); + } + } + + @Nested + @DisplayName("cascadeDeleteAshaMappingsForDeactivation") + class CascadeDeactivationTests { + + @Test + @DisplayName("should retire only this facility when the supervisor still works elsewhere") + void cascade_shouldRetireOnlyThisFacility() { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 usrRole = mapping(9001, 11); + usrRole.setFacilityID(501); + when(employeeMasterRepo.countByUserIDAndRoleIDAndDeletedFalse(USER_ID, 11)).thenReturn(2L); + + service.cascadeDeleteAshaMappingsForDeactivation(usrRole); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserIDAndFacilityID(USER_ID, 501, "Admin"); + verify(ashaSupervisorMappingService, never()).cascadeDeleteByUserID(anyInt(), anyString()); + } + + @Test + @DisplayName("should retire every mapping when this was the supervisor's last facility") + void cascade_shouldRetireEveryMappingOnLastFacility() { + namedRole("ASHA Supervisor"); + M_UserServiceRoleMapping2 usrRole = mapping(9001, 11); + usrRole.setFacilityID(501); + when(employeeMasterRepo.countByUserIDAndRoleIDAndDeletedFalse(USER_ID, 11)).thenReturn(1L); + + service.cascadeDeleteAshaMappingsForDeactivation(usrRole); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("should retire every mapping for a role that is not a supervisor") + void cascade_shouldRetireEveryMappingForNonSupervisor() { + service.cascadeDeleteAshaMappingsForDeactivation(mapping(9001, 11)); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + + @Test + @DisplayName("cascadeDeleteAshaMappingsForUser should hand the user straight to the mapping service") + void cascadeForUser_shouldDelegate() { + service.cascadeDeleteAshaMappingsForUser(USER_ID); + + verify(ashaSupervisorMappingService).cascadeDeleteByUserID(USER_ID, "Admin"); + } + } + + @Nested + @DisplayName("Employee lookups") + class LookupTests { + + @Test + @DisplayName("getAllRole should rebuild each role from what the repository holds") + void getAllRole_shouldRebuildRoles() { + M_Role stored = new M_Role(); + stored.setRoleID(11); + stored.setRoleName("Counsellor"); + stored.setProviderServiceMapID(PSM_ID); + when(roleRepo.getAllRole()).thenReturn(new ArrayList<>(List.of(stored))); + + ArrayList roles = service.getAllRole(); + + assertEquals(1, roles.size()); + assertEquals("Counsellor", roles.get(0).getRoleName()); + assertEquals(PSM_ID, roles.get(0).getProviderServiceMapID()); + } + + @Test + @DisplayName("getEmployeeDetails should skip a row the query could not fill") + void getEmployeeDetails_shouldSkipUnfillableRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 9001, USER_ID, 11, PSM_ID, "a", "b", "c", "d", 1, "e", 2, "f", "g" }); + when(employeeMasterRepo.getEmployeeDetails()).thenReturn(rows); + + assertEquals(1, service.getEmployeeDetails().size()); + } + + @Test + @DisplayName("getAllTitle should rebuild each title from what the repository holds") + void getAllTitle_shouldRebuildTitles() { + M_Title stored = new M_Title(); + stored.setTitleID(1); + stored.setTitleName("Dr"); + when(m_TitleRepo.getAllTitle()).thenReturn(new ArrayList<>(List.of(stored))); + + assertEquals("Dr", service.getAllTitle().get(0).getTitleName()); + } + + @Test + @DisplayName("getAllGender should rebuild each gender from what the repository holds") + void getAllGender_shouldRebuildGenders() { + M_Gender stored = new M_Gender(); + stored.setGenderID(1); + stored.setGenderName("Female"); + when(m_GenderRepo.getAllGender()).thenReturn(new ArrayList<>(List.of(stored))); + + assertEquals("Female", service.getAllGender().get(0).getGenderName()); + } + + @Test + @DisplayName("FindEmployeeName should distinguish a taken user name from a free one") + void findEmployeeName_shouldDistinguishTakenFromFree() { + when(employeeMasterRepoo.findEmployeeByName("asha.rao")).thenReturn(new M_User1()); + when(employeeMasterRepoo.findEmployeeByName("new.user")).thenReturn(null); + + assertEquals("userexist", service.FindEmployeeName("asha.rao")); + assertEquals("usernotexist", service.FindEmployeeName("new.user")); + } + + @Test + @DisplayName("FindEmployeeContact should distinguish a taken number from a free one") + void findEmployeeContact_shouldDistinguishTakenFromFree() { + when(employeeMasterRepoo.findEmployeeByContact("9000000001")).thenReturn(new M_User1()); + when(employeeMasterRepoo.findEmployeeByContact("9000000002")).thenReturn(null); + + assertEquals("contactexist", service.FindEmployeeContact("9000000001")); + assertEquals("contactnotexist", service.FindEmployeeContact("9000000002")); + } + + @Test + @DisplayName("FindEmployeeAadhaar should distinguish a taken number from a free one") + void findEmployeeAadhaar_shouldDistinguishTakenFromFree() { + when(employeeMasterRepoo.findEmployeeAadhaarNo("111122223333")).thenReturn(new M_User1()); + when(employeeMasterRepoo.findEmployeeAadhaarNo("444455556666")).thenReturn(null); + + assertEquals("aadhaarexist", service.FindEmployeeAadhaar("111122223333")); + assertEquals("aadhaarnotexist", service.FindEmployeeAadhaar("444455556666")); + } + + @Test + @DisplayName("FindEmployeeName1 should answer the user record itself") + void findEmployeeName1_shouldAnswerTheRecord() { + M_User1 stored = new M_User1(); + when(employeeMasterRepoo.findEmployeeByName("asha.rao")).thenReturn(stored); + + assertSame(stored, service.FindEmployeeName1("asha.rao")); + } + + @Test + @DisplayName("checkingEmpDetails should report whether the identifiers are already in use") + void checkingEmpDetails_shouldReportWhetherIdentifiersAreTaken() { + when(employeeMasterRepoo.checkingEmpDetails("asha.rao", "1", "2", "3", "4")).thenReturn(new M_User1()); + when(employeeMasterRepoo.checkingEmpDetails("new.user", "1", "2", "3", "4")).thenReturn(null); + + assertTrue(service.checkingEmpDetails("asha.rao", "1", "2", "3", "4")); + assertFalse(service.checkingEmpDetails("new.user", "1", "2", "3", "4")); + } + + @Test + @DisplayName("getQualification should hand back what the repository holds") + void getQualification_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_Userqualification())); + when(m_QualificationRepo.getAllQualification()).thenReturn(stored); + + assertSame(stored, service.getQualification()); + } + + @Test + @DisplayName("getlocationByMapid2 should hand back what the repository holds") + void getlocationByMapid2_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new Showofficedetails1())); + when(showofficedetails1Repo1.getlocationByMapid(PSM_ID, 301)).thenReturn(stored); + + assertSame(stored, service.getlocationByMapid2(PSM_ID, 301)); + } + + @Test + @DisplayName("getAllByMapId2 should hand back what the repository holds") + void getAllByMapId2_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_ProviderServiceMap1())); + when(m_ProviderServiceMap1Repo.getAllByMapId2(77, 29, 3)).thenReturn(stored); + + assertSame(stored, service.getAllByMapId2(77, 29, 3)); + } + + @Test + @DisplayName("the narrowing searches should each reach their own repository query") + void narrowingSearches_shouldReachTheirOwnQuery() { + ArrayList stored = + new ArrayList<>(List.of(new Showuserdetailsfromuserservicerolemapping())); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails2(77, 29)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails3(77, 11)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails4(77, 3)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails6(77, USER_ID)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails7(77, 29, 301)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails8(77, 29, 301, 401)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails9(77, 29, 11)).thenReturn(stored); + when(showuserdetailsfromuserservicerolemappingRepo.EmployeeDetails10(77, 29, 11, 3, "asha.rao", USER_ID)) + .thenReturn(stored); + + assertSame(stored, service.getEmployeeDetails2(77, 29)); + assertSame(stored, service.getEmployeeDetails3(77, 11)); + assertSame(stored, service.getEmployeeDetails4(77, 3)); + assertSame(stored, service.getEmployeeDetails6(77, USER_ID)); + assertSame(stored, service.getEmployeeDetails7(77, 29, 301)); + assertSame(stored, service.getEmployeeDetails8(77, 29, 301, 401)); + assertSame(stored, service.getEmployeeDetails9(77, 29, 11)); + assertSame(stored, service.getEmployeeDetails10(77, 29, 11, 3, "asha.rao", USER_ID)); + } + + @Test + @DisplayName("getEmployeeDetails5 should hand back what the view holds") + void getEmployeeDetails5_shouldHandBackViewContents() { + ArrayList stored = new ArrayList<>(List.of(new V_Showuser())); + when(v_ShowuserRepo.EmployeeDetails5()).thenReturn(stored); + + assertSame(stored, service.getEmployeeDetails5()); + } + + @Test + @DisplayName("getcompleteUserDetails should hand back what the view holds") + void getcompleteUserDetails_shouldHandBackViewContents() { + ArrayList stored = new ArrayList<>(List.of(new V_Showuser())); + when(v_ShowuserRepo.getAdminDetails()).thenReturn(stored); + + assertSame(stored, service.getcompleteUserDetails()); + } + + @Test + @DisplayName("getAllReligion and getAllCommunity should hand back what the repositories hold") + void masters_shouldHandBackRepositoryContents() { + ArrayList religions = new ArrayList<>(List.of(new M_Religion())); + ArrayList communities = new ArrayList<>(List.of(new M_Community())); + when(m_ReligionRepo.findAll()).thenReturn(religions); + when(m_CommunityRepo.findAll()).thenReturn(communities); + + assertEquals(1, service.getAllReligion().size()); + assertEquals(1, service.getAllCommunity().size()); + } + } + + @Nested + @DisplayName("getEmployeeDetails4 by provider") + class EmployeeDetails4Tests { + + @Test + @DisplayName("should mark a user locked out after failed sign-ins") + void getEmployeeDetails4_shouldMarkLockedOutUser() { + V_Showuser user = new V_Showuser(); + user.setUserID(USER_ID); + M_User1 record = new M_User1(); + record.setUserID(USER_ID); + record.setFailedAttempt(3); + record.setDeleted(Boolean.TRUE); + record.setLockTimestamp(Timestamp.valueOf("2026-02-17 09:30:00")); + when(v_ShowuserRepo.EmployeeDetails4(77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeMasterRepoo.findByUserIDIn(anyList())).thenReturn(new ArrayList<>(List.of(record))); + + V_Showuser enriched = service.getEmployeeDetails4(77).get(0); + + assertEquals(3, enriched.getFailedAttempt()); + assertTrue(enriched.getLockedDueToFailedAttempts()); + } + + @Test + @DisplayName("should not mark a user locked out while their account is still active") + void getEmployeeDetails4_shouldNotMarkActiveUserLockedOut() { + V_Showuser user = new V_Showuser(); + user.setUserID(USER_ID); + M_User1 record = new M_User1(); + record.setUserID(USER_ID); + record.setDeleted(Boolean.FALSE); + record.setLockTimestamp(Timestamp.valueOf("2026-02-17 09:30:00")); + when(v_ShowuserRepo.EmployeeDetails4(77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeMasterRepoo.findByUserIDIn(anyList())).thenReturn(new ArrayList<>(List.of(record))); + + V_Showuser enriched = service.getEmployeeDetails4(77).get(0); + + assertEquals(0, enriched.getFailedAttempt()); + assertFalse(enriched.getLockedDueToFailedAttempts()); + } + + @Test + @DisplayName("should answer an empty result without asking for user records") + void getEmployeeDetails4_shouldAnswerEmptyWithoutFurtherLookups() { + when(v_ShowuserRepo.EmployeeDetails4(77)).thenReturn(new ArrayList<>()); + + assertTrue(service.getEmployeeDetails4(77).isEmpty()); + verify(employeeMasterRepoo, never()).findByUserIDIn(anyList()); + } + } + + @Nested + @DisplayName("Persistence") + class PersistenceTests { + + @Test + @DisplayName("saveEmployee should answer the id of the stored user and encrypt its credentials") + void saveEmployee_shouldStoreAndEncrypt() { + M_User1 toSave = new M_User1(); + M_User1 stored = new M_User1(); + stored.setUserID(USER_ID); + when(employeeMasterRepo11.save(toSave)).thenReturn(stored); + when(encryptUserPassword.encryptUserCredentials(stored)).thenReturn(new OutputResponse()); + + assertEquals(USER_ID, service.saveEmployee(toSave)); + verify(encryptUserPassword).encryptUserCredentials(stored); + } + + @Test + @DisplayName("saveEditData should re-encrypt the credentials it saved") + void saveEditData_shouldReEncryptCredentials() { + M_User1 toSave = new M_User1(); + when(employeeMasterRepo11.save(toSave)).thenReturn(toSave); + + assertSame(toSave, service.saveEditData(toSave)); + verify(encryptUserPassword).encryptUserCredentials(toSave); + } + + @Test + @DisplayName("saveDemography should answer the id of the stored demographics") + void saveDemography_shouldAnswerStoredId() { + M_UserDemographics stored = new M_UserDemographics(); + stored.setDemographicID(5001); + when(m_UserDemographicsRepo.save(any())).thenReturn(stored); + + assertEquals(5001, service.saveDemography(new M_UserDemographics())); + assertEquals(5001, service.saveeditDemo(new M_UserDemographics())); + } + + @Test + @DisplayName("saveeditlangdata should answer the id of the stored language mapping") + void saveeditlangdata_shouldAnswerStoredId() { + M_UserLangMapping stored = new M_UserLangMapping(); + stored.setUserLangID(7001); + when(m_UserLangMappingRepo.save(any())).thenReturn(stored); + + assertEquals(7001, service.saveeditlangdata(new M_UserLangMapping())); + } + + @Test + @DisplayName("mapLanguage and mapRoleUpdation should hand their batches to the repositories") + void batches_shouldReachTheRepositories() { + ArrayList languages = new ArrayList<>(); + ArrayList roles = new ArrayList<>(); + when(m_UserLangMappingRepo.saveAll(anyList())).thenReturn(languages); + when(employeeMasterRepo.saveAll(anyList())).thenReturn(roles); + + assertSame(languages, service.mapLanguage(new ArrayList<>())); + assertSame(roles, service.mapRoleUpdation(new ArrayList<>())); + } + + @Test + @DisplayName("saveeditedData should clear the failed sign-in count for a reinstated user") + void saveeditedData_shouldClearFailedAttemptsOnReinstatement() { + M_User1 toSave = new M_User1(); + toSave.setDeleted(Boolean.FALSE); + toSave.setFailedAttempt(3); + when(employeeMasterRepoo.save(toSave)).thenReturn(toSave); + + service.saveeditedData(toSave); + + assertEquals(0, toSave.getFailedAttempt()); + } + + @Test + @DisplayName("saveeditedData should leave the failed sign-in count alone for a deactivated user") + void saveeditedData_shouldLeaveFailedAttemptsAloneOnDeactivation() { + M_User1 toSave = new M_User1(); + toSave.setDeleted(Boolean.TRUE); + toSave.setFailedAttempt(3); + when(employeeMasterRepoo.save(toSave)).thenReturn(toSave); + + service.saveeditedData(toSave); + + assertEquals(3, toSave.getFailedAttempt()); + } + + @Test + @DisplayName("createProviderAdmin should hash the password before it is stored") + void createProviderAdmin_shouldHashPassword() throws Exception { + M_User1 toCreate = new M_User1(); + toCreate.setPassword("plain-secret"); + ArrayList stored = new ArrayList<>(List.of(toCreate)); + when(employeeMasterRepoo.saveAll(anyList())).thenReturn(stored); + + service.createProviderAdmin(List.of(toCreate)); + + assertTrue(toCreate.getPassword().startsWith("1001:"), + "the stored password must be the salted hash, not the plain text"); + } + + @Test + @DisplayName("createProviderAdmin should refuse an admin with no password") + void createProviderAdmin_shouldRefuseWithoutPassword() { + assertThrows(Exception.class, () -> service.createProviderAdmin(List.of(new M_User1()))); + } + + @Test + @DisplayName("createNewUser should hash the password before it is stored") + void createNewUser_shouldHashPassword() throws Exception { + M_User1 toCreate = new M_User1(); + toCreate.setPassword("plain-secret"); + when(employeeMasterRepoo.saveAll(anyList())).thenReturn(new ArrayList<>(List.of(toCreate))); + + service.createNewUser(List.of(toCreate)); + + assertFalse("plain-secret".equals(toCreate.getPassword())); + } + + @Test + @DisplayName("createNewUser should refuse a user with no password") + void createNewUser_shouldRefuseWithoutPassword() { + assertThrows(Exception.class, () -> service.createNewUser(List.of(new M_User1()))); + } + + @Test + @DisplayName("generateStrongPassword should answer a different hash each time it is called") + void generateStrongPassword_shouldSaltEachHash() throws Exception { + String first = service.generateStrongPassword("plain-secret"); + String second = service.generateStrongPassword("plain-secret"); + + assertNotNull(first); + assertFalse(first.equals(second), "each hash must carry its own salt"); + } + + @Test + @DisplayName("saveBulkUserEmployee should answer the record the repository stored") + void saveBulkUserEmployee_shouldAnswerStoredRecord() { + M_User1 stored = new M_User1(); + stored.setUserID(USER_ID); + when(employeeMasterRepo11.save(any())).thenReturn(stored); + + assertSame(stored, service.saveBulkUserEmployee(new M_User1())); + } + + @Test + @DisplayName("the single-record lookups should each reach their own repository query") + void singleRecordLookups_shouldReachTheirOwnQuery() { + M_User1 user = new M_User1(); + M_UserDemographics demographics = new M_UserDemographics(); + M_UserLangMapping language = new M_UserLangMapping(); + M_UserServiceRoleMapping2 roleMapping = mapping(9001, 11); + when(employeeMasterRepo11.editEmployee(USER_ID)).thenReturn(user); + when(employeeMasterRepoo.findByUserID(USER_ID)).thenReturn(user); + when(m_UserDemographicsRepo.mdedit(USER_ID)).thenReturn(demographics); + when(m_UserDemographicsRepo.findByUserID(USER_ID)).thenReturn(demographics); + when(m_UserDemographicsRepo.save(demographics)).thenReturn(demographics); + when(m_UserLangMappingRepo.ulangmapedit(USER_ID, 1)).thenReturn(language); + when(m_UserLangMappingRepo.findByUserLangID(7001)).thenReturn(language); + when(m_UserLangMappingRepo.save(language)).thenReturn(language); + when(employeeMasterRepo.uRoleMedit(USER_ID, 11)).thenReturn(roleMapping); + when(employeeMasterRepo.uRoledelte(9001)).thenReturn(roleMapping); + when(employeeMasterRepo.findByUSRMappingID(9001)).thenReturn(roleMapping); + when(employeeMasterRepo.save(roleMapping)).thenReturn(roleMapping); + + assertSame(user, service.editEmployee(USER_ID)); + assertSame(user, service.editData(USER_ID)); + assertSame(user, service.getProviderAdminForEdit(USER_ID)); + assertSame(demographics, service.mdedit(USER_ID)); + assertSame(demographics, service.DataByUserID(USER_ID)); + assertSame(demographics, service.saveeditedDemoData(demographics)); + assertSame(language, service.ulangmapedit(USER_ID, 1)); + assertSame(language, service.updateLangMapping(7001)); + assertSame(language, service.saveUserLangEditedData(language)); + assertSame(roleMapping, service.uRoleMedit(USER_ID, 11)); + assertSame(roleMapping, service.uRoledelte(9001)); + assertSame(roleMapping, service.getDataUsrId(9001)); + assertSame(roleMapping, service.saveRoleEdit(roleMapping)); + } + + @Test + @DisplayName("SaveDemographics should hand its batch to the repository") + void saveDemographics_shouldHandBatchToRepository() { + ArrayList stored = new ArrayList<>(); + when(m_UserDemographicsRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.SaveDemographics(new ArrayList<>())); + } + + @Test + @DisplayName("getProviderAdmin should hand back what the repository holds") + void getProviderAdmin_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_User1())); + when(employeeMasterRepoo.getAllProviderAdminData()).thenReturn(stored); + + assertSame(stored, service.getProviderAdmin()); + } + } + + @Nested + @DisplayName("ResetPassword") + class ResetPasswordTests { + + @Test + @DisplayName("should report success when the credential service accepts the new password") + void resetPassword_shouldReportSuccess() { + OutputResponse accepted = new OutputResponse(); + accepted.setResponse("done"); + when(encryptUserPassword.encryptUserCredentials(any())).thenReturn(accepted); + + assertEquals("Password reset successfully", service.ResetPassword(new M_User1())); + } + + @Test + @DisplayName("should report failure when the credential service refuses the new password") + void resetPassword_shouldReportFailure() { + when(encryptUserPassword.encryptUserCredentials(any())).thenReturn(new OutputResponse()); + + assertEquals("Password Not Set Properly", service.ResetPassword(new M_User1())); + } + } + + @Nested + @DisplayName("getMappedRole") + class GetMappedRoleTests { + + @Test + @DisplayName("should split the stored village columns back into lists") + void getMappedRole_shouldSplitVillageColumns() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setServiceID(3); + mapping.setStateID(29); + mapping.setWorkingDistrictID("301"); + mapping.setVillageidDb("501,502"); + mapping.setVillageNameDb("Hosur,Devanahalli"); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())).thenReturn(new ArrayList<>()); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertArrayEquals(new String[] { "501", "502" }, answered.getVillageID()); + assertArrayEquals(new String[] { "Hosur", "Devanahalli" }, answered.getVillageName()); + } + + @Test + @DisplayName("should clear the block and village details for a mapping with no service line") + void getMappedRole_shouldClearBlockAndVillageWithoutService() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setStateID(29); + mapping.setWorkingDistrictID("301"); + mapping.setVillageidDb("501"); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())).thenReturn(new ArrayList<>()); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertNull(answered.getVillageID()); + assertNull(answered.getVillageidDb()); + assertNull(answered.getBlockID()); + } + + @Test + @DisplayName("should fill in the state and district the view could not resolve") + void getMappedRole_shouldFillInMissingStateAndDistrict() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setServiceID(3); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getDirectStateDistrictByMappingIDs(anyList())) + .thenReturn(List.of(new Object[] { 9001, 29, "Karnataka", 301, "Bengaluru Urban", 401, "North" })); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())).thenReturn(new ArrayList<>()); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertEquals(29, answered.getStateID()); + assertEquals("Karnataka", answered.getStateName()); + assertEquals("301", answered.getWorkingDistrictID()); + assertEquals("North", answered.getBlockName()); + } + + @Test + @DisplayName("should attach the facility details the batch lookup resolves") + void getMappedRole_shouldAttachFacilityDetails() { + V_Userservicerolemapping mapping = new V_Userservicerolemapping(); + mapping.setuSRMappingID(9001); + mapping.setServiceID(3); + mapping.setStateID(29); + mapping.setWorkingDistrictID("301"); + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)) + .thenReturn(new ArrayList<>(List.of(mapping))); + when(employeeMasterRepo.getFacilityInfoByMappingIDs(anyList())) + .thenReturn(List.of(new Object[] { 9001, 501, "PHC North", 3, "Rural" })); + + V_Userservicerolemapping answered = service.getMappedRole(77).get(0); + + assertEquals(501, answered.getFacilityID()); + assertEquals("PHC North", answered.getFacilityName()); + assertEquals("Rural", answered.getRuralUrban()); + } + + @Test + @DisplayName("should answer an empty list when the view holds nothing") + void getMappedRole_shouldAnswerEmptyListForEmptyView() { + when(v_UserservicerolemappingRepo.getAllRoleOfProvider(77)).thenReturn(null); + + assertTrue(service.getMappedRole(77).isEmpty()); + } + + @Test + @DisplayName("should search by user id when the caller sends no name") + void getMappedRole_shouldSearchByUserIdWithoutAName() { + ArrayList stored = new ArrayList<>(); + when(v_UserservicerolemappingRepo.getDataByUserID(USER_ID)).thenReturn(stored); + + assertSame(stored, service.getMappedRole("", USER_ID)); + } + + @Test + @DisplayName("should search by name when the caller sends no usable user id") + void getMappedRole_shouldSearchByNameWithoutAUserId() { + ArrayList stored = new ArrayList<>(); + when(v_UserservicerolemappingRepo.getDataByName("Asha Rao")).thenReturn(stored); + + assertSame(stored, service.getMappedRole("Asha Rao", 0)); + } + + @Test + @DisplayName("should refuse a search that names both a user and an id") + void getMappedRole_shouldRefuseAmbiguousSearch() { + assertThrows(DataNotFound.class, () -> service.getMappedRole("Asha Rao", USER_ID)); + } + } + + @Nested + @DisplayName("searchMappedLangugeByUserId") + class SearchMappedLanguageTests { + + @Test + @DisplayName("should answer the languages mapped to a real user") + void searchMappedLanguage_shouldAnswerMappedLanguages() { + ArrayList stored = new ArrayList<>(); + when(m_UserLangMappingRepo.getmappedlanguageData(USER_ID)).thenReturn(stored); + + assertSame(stored, service.searchMappedLangugeByUserId(USER_ID)); + } + + @Test + @DisplayName("should refuse a search that names no user") + void searchMappedLanguage_shouldRefuseSearchWithoutUser() { + assertThrows(DataNotFound.class, () -> service.searchMappedLangugeByUserId(0)); + } + } + + @Nested + @DisplayName("getMappedLanguge") + class GetMappedLanguageTests { + + @Test + @DisplayName("should rebuild one mapping per row the query answers") + void getMappedLanguge_shouldRebuildEachRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 7001, USER_ID, 1, 5, "Kannada", "asha.rao", true, true, true, "n", false, + 5, 5, 5, false }); + when(m_UserLangMappingRepo.getMappedLanguge(77)).thenReturn(rows); + + assertEquals(1, service.getMappedLanguge(77).size()); + } + } + + @Nested + @DisplayName("getEmployeeByDesiganationID") + class EmployeeByDesignationTests { + + @Test + @DisplayName("should mark a user whose signature is on file as active") + void byDesignation_shouldMarkActiveSignature() { + M_User1 user = new M_User1(); + user.setUserID(USER_ID); + EmployeeSignature signature = new EmployeeSignature(); + signature.setDeleted(Boolean.FALSE); + when(employeeMasterRepoo.getempByDesiganation(7, 77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(signature); + + assertEquals("Active", service.getEmployeeByDesiganationID(7, 77).get(0).getSignatureStatus()); + } + + @Test + @DisplayName("should mark a user whose signature has been retired as inactive") + void byDesignation_shouldMarkRetiredSignature() { + M_User1 user = new M_User1(); + user.setUserID(USER_ID); + EmployeeSignature signature = new EmployeeSignature(); + signature.setDeleted(Boolean.TRUE); + when(employeeMasterRepoo.getempByDesiganation(7, 77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(signature); + + assertEquals("InActive", service.getEmployeeByDesiganationID(7, 77).get(0).getSignatureStatus()); + } + + @Test + @DisplayName("should leave the signature status unset for a user with none on file") + void byDesignation_shouldLeaveStatusUnsetWithoutSignature() { + M_User1 user = new M_User1(); + user.setUserID(USER_ID); + when(employeeMasterRepoo.getempByDesiganation(7, 77)).thenReturn(new ArrayList<>(List.of(user))); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(null); + + assertNull(service.getEmployeeByDesiganationID(7, 77).get(0).getSignatureStatus()); + } + } + + @Nested + @DisplayName("getUserRoleTM") + class UserRoleTmTests { + + @Test + @DisplayName("should rebuild one role per row the query answers") + void getUserRoleTM_shouldRebuildEachRow() { + M_UserservicerolemappingForRoleProviderAdmin request = + new M_UserservicerolemappingForRoleProviderAdmin(); + request.setUserID(USER_ID); + request.setProviderServiceMapID(PSM_ID); + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { USER_ID, 11, "TC Specialist", false, 21, "Specialist screen", false }); + when(userservicerolemappingForRoleProviderAdminRepo.getroleofuserTM(USER_ID, PSM_ID)).thenReturn(rows); + + ArrayList roles = service.getUserRoleTM(request); + + assertEquals(1, roles.size()); + assertEquals("TC Specialist", roles.get(0).getRolename()); + } + + @Test + @DisplayName("should answer an empty list when the user holds no telemedicine role") + void getUserRoleTM_shouldAnswerEmptyListWithoutRoles() { + M_UserservicerolemappingForRoleProviderAdmin request = + new M_UserservicerolemappingForRoleProviderAdmin(); + when(userservicerolemappingForRoleProviderAdminRepo.getroleofuserTM(any(), any())) + .thenReturn(new ArrayList<>()); + + assertTrue(service.getUserRoleTM(request).isEmpty()); + } + } + + @Nested + @DisplayName("createAgent") + class CreateAgentTests { + + @Test + @DisplayName("should fill the agent and server placeholders into the configured URL") + void createAgent_shouldFillPlaceholders() { + com.iemr.admin.utils.config.ConfigProperties properties = + new com.iemr.admin.utils.config.ConfigProperties(); + service.setConfigProperties(properties); + + String url = service.createAgent("A-1", "asha.rao"); + + assertNotNull(url); + assertFalse(url.contains("AGENTID"), url); + } + } +} diff --git a/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterSupportServicesTest.java b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterSupportServicesTest.java new file mode 100644 index 0000000..230e875 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/employeemaster/EmployeeMasterSupportServicesTest.java @@ -0,0 +1,556 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.employeemaster; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.employeemaster.AshaSupervisorMapping; +import com.iemr.admin.data.employeemaster.EmployeeSignature; +import com.iemr.admin.data.employeemaster.M_Designation; +import com.iemr.admin.data.employeemaster.M_UserServiceRoleMapping2; +import com.iemr.admin.data.employeemaster.USRAgentMapping; +import com.iemr.admin.data.store.M_Facility; +import com.iemr.admin.repo.employeemaster.EmployeeMasterRepo; +import com.iemr.admin.repo.employeemaster.EmployeeSignatureRepo; +import com.iemr.admin.repo.employeemaster.M_DesignationRepo; +import com.iemr.admin.repo.employeemaster.USRAgentMappingRepository; +import com.iemr.admin.repository.store.MainStoreRepo; +import com.iemr.admin.repository.user.AshaSupervisorMappingRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The supporting employee services: the supervisor mapping store, the signature + * store, the CTI agent-id pool and the designation master. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Employee master support service Test Suite") +class EmployeeMasterSupportServicesTest { + + private static final Integer SUPERVISOR_ID = 3117; + private static final Integer ASHA_ID = 4001; + private static final Integer FACILITY_ID = 501; + + @Mock + private AshaSupervisorMappingRepo ashaSupervisorMappingRepo; + + @Mock + private EmployeeMasterRepo employeeMasterRepo; + + @Mock + private MainStoreRepo mainStoreRepo; + + @InjectMocks + private AshaSupervisorMappingServiceImpl ashaService; + + @Mock + private EmployeeSignatureRepo employeeSignatureRepo; + + @InjectMocks + private EmployeeSignatureServiceImpl signatureService; + + @Mock + private USRAgentMappingRepository usrAgentMappingRepository; + + @Mock + private M_DesignationRepo m_DesignationRepo; + + @InjectMocks + private M_DesignationImpl designationService; + + private static AshaSupervisorMapping mapping(Long id, Integer supervisorId, Integer ashaId) { + AshaSupervisorMapping mapping = new AshaSupervisorMapping(); + mapping.setId(id); + mapping.setSupervisorUserID(supervisorId); + mapping.setAshaUserID(ashaId); + mapping.setFacilityID(FACILITY_ID); + mapping.setCreatedBy("admin"); + return mapping; + } + + private void activeFacility() { + M_Facility facility = new M_Facility(); + facility.setFacilityID(FACILITY_ID); + when(mainStoreRepo.findByFacilityIDAndDeleted(FACILITY_ID, false)).thenReturn(facility); + } + + @Nested + @DisplayName("AshaSupervisorMappingServiceImpl") + class AshaSupervisorMappingTests { + + @Test + @DisplayName("saveAshaSupervisorMappings should refuse a mapping onto a retired facility") + void save_shouldRefuseRetiredFacility() { + when(mainStoreRepo.findByFacilityIDAndDeleted(FACILITY_ID, false)).thenReturn(null); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> ashaService.saveAshaSupervisorMappings(List.of(mapping(null, SUPERVISOR_ID, ASHA_ID)))); + + assertTrue(thrown.getMessage().contains("is no longer active"), thrown.getMessage()); + } + + @Test + @DisplayName("saveAshaSupervisorMappings should reuse a mapping that already exists") + void save_shouldReuseExistingMapping() { + activeFacility(); + AshaSupervisorMapping existing = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndAshaUserIDAndFacilityIDAndDeletedFalse(SUPERVISOR_ID, ASHA_ID, + FACILITY_ID)) + .thenReturn(existing); + + ArrayList saved = + ashaService.saveAshaSupervisorMappings(List.of(mapping(null, SUPERVISOR_ID, ASHA_ID))); + + assertSame(existing, saved.get(0)); + verify(ashaSupervisorMappingRepo, never()).save(any()); + } + + @Test + @DisplayName("saveAshaSupervisorMappings should retire the ASHA's mapping under a different supervisor") + void save_shouldRetireMappingUnderOtherSupervisor() { + activeFacility(); + AshaSupervisorMapping other = mapping(2L, 3118, ASHA_ID); + AshaSupervisorMapping toSave = mapping(null, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo + .findByAshaUserIDAndFacilityIDAndDeletedFalseAndSupervisorUserIDNot(ASHA_ID, FACILITY_ID, + SUPERVISOR_ID)) + .thenReturn(new ArrayList<>(List.of(other))); + when(ashaSupervisorMappingRepo.save(toSave)).thenReturn(toSave); + + ashaService.saveAshaSupervisorMappings(List.of(toSave)); + + assertTrue(other.getDeleted(), "an ASHA may report to only one supervisor at a facility"); + assertEquals("admin", other.getModifiedBy()); + verify(ashaSupervisorMappingRepo).save(other); + } + + @Test + @DisplayName("getSupervisorMappingByFacility should hand back what the repository holds") + void getByFacility_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(); + when(ashaSupervisorMappingRepo.findActiveMappingsByFacilityID(FACILITY_ID)).thenReturn(stored); + + assertSame(stored, ashaService.getSupervisorMappingByFacility(FACILITY_ID)); + } + + @Test + @DisplayName("getAshasByFacility should hand back what the repository holds") + void getAshas_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(); + when(employeeMasterRepo.findAshaUsersByFacilityIDs(anyList())).thenReturn(stored); + + assertSame(stored, ashaService.getAshasByFacility(List.of(FACILITY_ID))); + } + + @Test + @DisplayName("deleteMappings should retire each mapping it can resolve") + void deleteMappings_shouldRetireResolvedMappings() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo.findById(1L)).thenReturn(Optional.of(stored)); + when(ashaSupervisorMappingRepo.findById(2L)).thenReturn(Optional.empty()); + + ashaService.deleteMappings(List.of(1L, 2L), "admin"); + + assertTrue(stored.getDeleted()); + verify(ashaSupervisorMappingRepo).save(stored); + } + + @Test + @DisplayName("restoreMappings should reinstate each mapping it can resolve") + void restoreMappings_shouldReinstateResolvedMappings() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + stored.setDeleted(Boolean.TRUE); + when(ashaSupervisorMappingRepo.findById(1L)).thenReturn(Optional.of(stored)); + when(ashaSupervisorMappingRepo.findById(2L)).thenReturn(Optional.empty()); + + ashaService.restoreMappings(List.of(1L, 2L), "admin"); + + assertFalse(stored.getDeleted()); + assertEquals("admin", stored.getModifiedBy()); + } + + @Test + @DisplayName("deleteBySupervisorAndFacilities should retire every mapping at the named facilities") + void deleteBySupervisorAndFacilities_shouldRetireEveryMapping() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDInAndDeletedFalse(SUPERVISOR_ID, List.of(FACILITY_ID))) + .thenReturn(new ArrayList<>(List.of(stored))); + + ashaService.deleteBySupervisorAndFacilities(SUPERVISOR_ID, List.of(FACILITY_ID), "admin"); + + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("cascadeDeleteByUserID should retire the user's mappings on both sides of the relationship") + void cascadeByUser_shouldRetireBothSides() { + AshaSupervisorMapping asSupervisor = mapping(1L, SUPERVISOR_ID, ASHA_ID); + AshaSupervisorMapping asAsha = mapping(2L, 3118, SUPERVISOR_ID); + when(ashaSupervisorMappingRepo.findBySupervisorUserIDAndDeletedFalse(SUPERVISOR_ID)) + .thenReturn(new ArrayList<>(List.of(asSupervisor))); + when(ashaSupervisorMappingRepo.findByAshaUserIDAndDeletedFalse(SUPERVISOR_ID)) + .thenReturn(new ArrayList<>(List.of(asAsha))); + + ashaService.cascadeDeleteByUserID(SUPERVISOR_ID, "admin"); + + assertTrue(asSupervisor.getDeleted()); + assertTrue(asAsha.getDeleted()); + } + + @Test + @DisplayName("cascadeDeleteByFacilityID should retire every mapping at the facility") + void cascadeByFacility_shouldRetireEveryMapping() { + AshaSupervisorMapping stored = mapping(1L, SUPERVISOR_ID, ASHA_ID); + when(ashaSupervisorMappingRepo.findByFacilityIDAndDeletedFalse(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(stored))); + + ashaService.cascadeDeleteByFacilityID(FACILITY_ID, "admin"); + + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("cascadeDeleteByUserIDAndFacilityID should retire only that user's mappings at that facility") + void cascadeByUserAndFacility_shouldRetireBothSidesAtFacility() { + AshaSupervisorMapping asSupervisor = mapping(1L, SUPERVISOR_ID, ASHA_ID); + AshaSupervisorMapping asAsha = mapping(2L, 3118, SUPERVISOR_ID); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDAndDeletedFalse(SUPERVISOR_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(asSupervisor))); + when(ashaSupervisorMappingRepo.findByAshaUserIDAndFacilityIDAndDeletedFalse(SUPERVISOR_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(asAsha))); + + ashaService.cascadeDeleteByUserIDAndFacilityID(SUPERVISOR_ID, FACILITY_ID, "admin"); + + assertTrue(asSupervisor.getDeleted()); + assertTrue(asAsha.getDeleted()); + } + + @Test + @DisplayName("updateAshaMappingsAtomically should retire the old mappings before saving the new ones") + void updateAtomically_shouldRetireThenSave() { + activeFacility(); + AshaSupervisorMapping old = mapping(1L, SUPERVISOR_ID, ASHA_ID); + AshaSupervisorMapping fresh = mapping(null, SUPERVISOR_ID, 4002); + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDInAndDeletedFalse(SUPERVISOR_ID, List.of(FACILITY_ID))) + .thenReturn(new ArrayList<>(List.of(old))); + when(ashaSupervisorMappingRepo.save(fresh)).thenReturn(fresh); + + ArrayList saved = ashaService.updateAshaMappingsAtomically( + SUPERVISOR_ID, List.of(FACILITY_ID), List.of(fresh), "admin"); + + assertTrue(old.getDeleted()); + assertSame(fresh, saved.get(0)); + } + + @Test + @DisplayName("updateAshaMappingsAtomically should answer nothing when no new mappings are supplied") + void updateAtomically_shouldAnswerNothingWithoutNewMappings() { + when(ashaSupervisorMappingRepo + .findBySupervisorUserIDAndFacilityIDInAndDeletedFalse(anyInt(), anyList())) + .thenReturn(new ArrayList<>()); + + assertTrue(ashaService + .updateAshaMappingsAtomically(SUPERVISOR_ID, List.of(FACILITY_ID), null, "admin").isEmpty()); + } + } + + @Nested + @DisplayName("EmployeeSignatureServiceImpl") + class SignatureServiceTests { + + @Test + @DisplayName("uploadSignature should overwrite the signature already on file") + void upload_shouldOverwriteExistingSignature() { + EmployeeSignature existing = new EmployeeSignature(); + existing.setUserID(3117L); + existing.setUserSignatureID(9001L); + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setUserID(3117L); + uploaded.setFileName("new.png"); + uploaded.setFileType("image/png"); + uploaded.setSignature(new byte[] { 1, 2, 3 }); + uploaded.setCreatedBy("admin"); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(existing); + when(employeeSignatureRepo.save(existing)).thenReturn(existing); + + assertEquals(9001L, signatureService.uploadSignature(uploaded)); + assertEquals("new.png", existing.getFileName()); + assertEquals("admin", existing.getModifiedBy()); + } + + @Test + @DisplayName("uploadSignature should store a first signature for a user who has none") + void upload_shouldStoreFirstSignature() { + EmployeeSignature uploaded = new EmployeeSignature(); + uploaded.setUserID(3117L); + uploaded.setUserSignatureID(9002L); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(null); + when(employeeSignatureRepo.save(uploaded)).thenReturn(uploaded); + + assertEquals(9002L, signatureService.uploadSignature(uploaded)); + } + + @Test + @DisplayName("fetchSignature should hand back what the repository holds") + void fetch_shouldHandBackRepositoryContents() { + EmployeeSignature stored = new EmployeeSignature(); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(stored); + + assertSame(stored, signatureService.fetchSignature(3117L)); + } + + @Test + @DisplayName("existSignature should report whether any signature is on file") + void exist_shouldReportWhetherSignatureIsOnFile() { + when(employeeSignatureRepo.countByUserIDAndSignatureNotNull(3117L)).thenReturn(1L); + when(employeeSignatureRepo.countByUserIDAndSignatureNotNull(3118L)).thenReturn(0L); + + assertTrue(signatureService.existSignature(3117L)); + assertFalse(signatureService.existSignature(3118L)); + } + + @Test + @DisplayName("isSignatureActive should report whether the signature is still in use") + void isActive_shouldReportWhetherSignatureIsInUse() { + when(employeeSignatureRepo.countByUserIDAndSignatureNotNullAndDeletedFalse(3117L)).thenReturn(1L); + when(employeeSignatureRepo.countByUserIDAndSignatureNotNullAndDeletedFalse(3118L)).thenReturn(0L); + + assertTrue(signatureService.isSignatureActive(3117L)); + assertFalse(signatureService.isSignatureActive(3118L)); + } + + @Test + @DisplayName("updateUserSignatureStatus should retire a signature the caller deactivates") + void updateStatus_shouldRetireDeactivatedSignature() { + EmployeeSignature stored = new EmployeeSignature(); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(stored); + when(employeeSignatureRepo.save(stored)).thenReturn(stored); + + signatureService.updateUserSignatureStatus("{\"userID\":3117,\"active\":false}"); + + assertTrue(stored.getDeleted()); + } + + @Test + @DisplayName("updateUserSignatureStatus should reinstate a signature the caller activates") + void updateStatus_shouldReinstateActivatedSignature() { + EmployeeSignature stored = new EmployeeSignature(); + stored.setDeleted(Boolean.TRUE); + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(stored); + when(employeeSignatureRepo.save(stored)).thenReturn(stored); + + signatureService.updateUserSignatureStatus("{\"userID\":3117,\"active\":true}"); + + assertFalse(stored.getDeleted()); + } + + @Test + @DisplayName("updateUserSignatureStatus should refuse a user who has no signature on file") + void updateStatus_shouldRefuseUserWithoutSignature() { + when(employeeSignatureRepo.findOneByUserID(3117L)).thenReturn(null); + + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> signatureService.updateUserSignatureStatus("{\"userID\":3117,\"active\":true}")); + + assertTrue(thrown.getMessage().contains("No signature found"), thrown.getMessage()); + } + } + + @Nested + @DisplayName("USRAgentMappingServiceImpl") + class UsrAgentMappingTests { + + private USRAgentMappingServiceImpl agentService() { + USRAgentMappingServiceImpl service = new USRAgentMappingServiceImpl(); + service.setUsrAgentMappingRepository(usrAgentMappingRepository); + return service; + } + + private Set agentRow() { + Set rows = new LinkedHashSet<>(); + rows.add(new Object[] { 1, 9001, null, 4001, null, "A-1", "secret", "104", Boolean.TRUE }); + rows.add(new Object[] { 2 }); + return rows; + } + + @Test + @DisplayName("getAvailableAgentIds should rebuild the free agents and skip an unusable row") + void getAvailableAgentIds_shouldRebuildFreeAgents() throws Exception { + when(usrAgentMappingRepository.getFreeAgentIds("104", 4001)).thenReturn(agentRow()); + + List agents = agentService() + .getAvailableAgentIds("{\"cti_CampaignName\":\"104\",\"providerServiceMapID\":4001}"); + + assertEquals(1, agents.size()); + assertEquals("A-1", agents.get(0).getAgentID()); + } + + @Test + @DisplayName("updateAgentIds should free the previous agent id before claiming the new one") + void updateAgentIds_shouldFreePreviousAgentId() throws Exception { + when(usrAgentMappingRepository.updateUSRMapping(any(), any(), any())).thenReturn(1); + + Integer changed = agentService().updateAgentIds("{\"oldAgentID\":\"A-0\"," + + "\"providerServiceMapID\":4001,\"isAvailable\":false,\"usrMappingID\":9001," + + "\"usrAgentMappingID\":1}"); + + assertEquals(1, changed); + verify(usrAgentMappingRepository).updateUSRMapping(true, null, "A-0", 4001); + } + + @Test + @DisplayName("updateAgentIds should leave the previous agent id alone when none is named") + void updateAgentIds_shouldLeavePreviousAgentIdAlone() throws Exception { + when(usrAgentMappingRepository.updateUSRMapping(any(), any(), any())).thenReturn(1); + + agentService().updateAgentIds("{\"isAvailable\":true,\"usrAgentMappingID\":1}"); + + verify(usrAgentMappingRepository, never()) + .updateUSRMapping(any(Boolean.class), any(), anyString(), anyInt()); + } + + @Test + @DisplayName("createUSRAgentMapping should skip an agent id the provider already holds") + void createUSRAgentMapping_shouldSkipExistingAgent() throws Exception { + when(usrAgentMappingRepository.getExistingAgent(4001, "A-1")).thenReturn(1L); + when(usrAgentMappingRepository.getExistingAgent(4001, "A-2")).thenReturn(0L); + when(usrAgentMappingRepository.save(any())).thenAnswer(call -> call.getArgument(0)); + + List created = agentService().createUSRAgentMapping( + "[{\"agentID\":\"A-1\",\"providerServiceMapID\":4001}," + + "{\"agentID\":\"A-2\",\"providerServiceMapID\":4001}]"); + + assertEquals(1, created.size()); + assertEquals("A-2", created.get(0).getAgentID()); + } + + @Test + @DisplayName("getAvailableCampaigns should hand back what the repository holds") + void getAvailableCampaigns_shouldHandBackRepositoryContents() throws Exception { + when(usrAgentMappingRepository.getAvailableCampaigns(4001)).thenReturn(List.of("104", "1097")); + + assertEquals(2, agentService().getAvailableCampaigns("{\"providerServiceMapID\":4001}").size()); + } + + @Test + @DisplayName("getAllAgentIds should look the agent up directly when the caller names one") + void getAllAgentIds_shouldLookUpNamedAgent() throws Exception { + when(usrAgentMappingRepository + .getUSRAgentMappingByAgentIDAndProviderServiceMapID("A-1", 4001)).thenReturn(agentRow()); + + List agents = agentService() + .getAllAgentIds("{\"agentID\":\"A-1\",\"providerServiceMapID\":4001}"); + + assertEquals(1, agents.size()); + } + + @Test + @DisplayName("getAllAgentIds should filter by availability when the caller asks for it") + void getAllAgentIds_shouldFilterByAvailability() throws Exception { + when(usrAgentMappingRepository.getAllAgentIds(4001, "104", true)).thenReturn(agentRow()); + + assertEquals(1, agentService().getAllAgentIds( + "{\"providerServiceMapID\":4001,\"cti_CampaignName\":\"104\",\"isAvailable\":true}").size()); + } + + @Test + @DisplayName("getAllAgentIds should filter by campaign alone when availability is not named") + void getAllAgentIds_shouldFilterByCampaignAlone() throws Exception { + when(usrAgentMappingRepository.getAllAgentId(4001, "104")).thenReturn(agentRow()); + + assertEquals(1, agentService() + .getAllAgentIds("{\"providerServiceMapID\":4001,\"cti_CampaignName\":\"104\"}").size()); + } + + @Test + @DisplayName("getAllAgentIds should answer every agent under the mapping when nothing is named") + void getAllAgentIds_shouldAnswerEveryAgent() throws Exception { + when(usrAgentMappingRepository.getAllAgentIds(4001)).thenReturn(agentRow()); + + assertEquals(1, agentService().getAllAgentIds("{\"providerServiceMapID\":4001}").size()); + } + + @Test + @DisplayName("updateCTICampaignNameMapping should answer how many mappings moved campaign") + void updateCTICampaignNameMapping_shouldAnswerChangedCount() throws Exception { + when(usrAgentMappingRepository.updateCTICampaignNameMapping("1097", 1)).thenReturn(1); + + assertEquals(1, agentService() + .updateCTICampaignNameMapping("{\"cti_CampaignName\":\"1097\",\"usrAgentMappingID\":1}")); + } + + @Test + @DisplayName("updateDeletedAgentIDStatus should free the agent id of a deleted user") + void updateDeletedAgentIDStatus_shouldFreeAgentId() { + when(usrAgentMappingRepository.updateDeletedAgentIDStatus("A-1")).thenReturn(1); + + agentService().updateDeletedAgentIDStatus("A-1"); + + verify(usrAgentMappingRepository).updateDeletedAgentIDStatus("A-1"); + } + } + + @Nested + @DisplayName("M_DesignationImpl") + class DesignationServiceTests { + + @Test + @DisplayName("getDesinationlist should hand back what the repository holds") + void getDesinationlist_shouldHandBackRepositoryContents() { + ArrayList stored = new ArrayList<>(List.of(new M_Designation())); + when(m_DesignationRepo.getDesinationlist()).thenReturn(stored); + + assertSame(stored, designationService.getDesinationlist()); + } + } +} diff --git a/src/test/java/com/iemr/admin/service/facilitytype/M_facilitytypeServiceImplTest.java b/src/test/java/com/iemr/admin/service/facilitytype/M_facilitytypeServiceImplTest.java new file mode 100644 index 0000000..8551a78 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/facilitytype/M_facilitytypeServiceImplTest.java @@ -0,0 +1,196 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.facilitytype; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.facilitytype.M_facilitytype; +import com.iemr.admin.data.store.M_FacilityLevel; +import com.iemr.admin.repository.facilitytype.M_FacilityLevelRepo; +import com.iemr.admin.repository.facilitytype.M_facilitytypeRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The facility type service keeps the kinds of health facility a state runs, + * refusing a type name a state already has. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("M_facilitytypeServiceImpl Test Suite") +class M_facilitytypeServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer STATE_ID = 29; + private static final Integer FACILITY_TYPE_ID = 12; + + @Mock + private M_facilitytypeRepo m_facilitytypeRepo; + + @Mock + private M_FacilityLevelRepo m_facilityLevelRepo; + + @InjectMocks + private M_facilitytypeServiceImpl service; + + private static M_facilitytype facilityType() { + M_facilitytype facilityType = new M_facilitytype(); + facilityType.setFacilityTypeID(FACILITY_TYPE_ID); + facilityType.setFacilityTypeName("Primary Health Centre"); + facilityType.setFacilityTypeCode("PHC"); + facilityType.setProviderServiceMapID(PSM_ID); + facilityType.setStateID(STATE_ID); + return facilityType; + } + + @Test + @DisplayName("getAllFicilityData should answer the facility types of the provider asked about") + void getAll_shouldAnswerProvidersFacilityTypes() { + ArrayList held = new ArrayList<>(List.of(facilityType())); + when(m_facilitytypeRepo.getAllFicilityData(PSM_ID)).thenReturn(held); + + assertSame(held, service.getAllFicilityData(PSM_ID)); + } + + @Test + @DisplayName("getFacilityTypesByRuralUrban should narrow the types to the setting asked for") + void getByRuralUrban_shouldNarrowToSetting() { + when(m_facilitytypeRepo.findByProviderServiceMapIDAndRuralUrban(PSM_ID, "Rural")) + .thenReturn(List.of(facilityType())); + + assertEquals(1, service.getFacilityTypesByRuralUrban(PSM_ID, "Rural").size()); + } + + @Test + @DisplayName("addAllFicilityData should store a facility type the state does not have yet") + void add_shouldStoreNewFacilityType() { + ArrayList stored = new ArrayList<>(List.of(facilityType())); + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse(anyString(), anyInt())) + .thenReturn(false); + when(m_facilitytypeRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.addAllFicilityData(List.of(facilityType()))); + } + + @Test + @DisplayName("addAllFicilityData should refuse a facility type name the state already has") + void add_shouldRefuseDuplicateName() { + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse("Primary Health Centre", + STATE_ID)).thenReturn(true); + + RuntimeException refusal = assertThrows(RuntimeException.class, + () -> service.addAllFicilityData(List.of(facilityType()))); + + assertTrue(refusal.getMessage().contains("Primary Health Centre"), refusal.getMessage()); + verify(m_facilitytypeRepo, never()).saveAll(anyList()); + } + + @Test + @DisplayName("editAllFicilityData and updateFacilityData should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_facilitytype stored = facilityType(); + when(m_facilitytypeRepo.findByFacilityTypeID(FACILITY_TYPE_ID)).thenReturn(stored); + when(m_facilitytypeRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.editAllFicilityData(FACILITY_TYPE_ID)); + assertSame(stored, service.updateFacilityData(stored)); + } + + @Test + @DisplayName("editAllFicilityData should answer nothing when the facility type is unknown") + void edit_shouldAnswerNothingForUnknownFacilityType() { + when(m_facilitytypeRepo.findByFacilityTypeID(-1)).thenReturn(null); + + assertNull(service.editAllFicilityData(-1)); + } + + @Test + @DisplayName("checkFacilityTypeCode should report whether the provider already uses the code") + void checkCode_shouldReportWhetherCodeIsUsed() { + when(m_facilitytypeRepo.findByFacilityTypeCodeAndProviderServiceMapID("PHC", PSM_ID)) + .thenReturn(List.of(facilityType())); + assertTrue(service.checkFacilityTypeCode(facilityType())); + + when(m_facilitytypeRepo.findByFacilityTypeCodeAndProviderServiceMapID("PHC", PSM_ID)) + .thenReturn(new ArrayList<>()); + assertFalse(service.checkFacilityTypeCode(facilityType())); + } + + @Test + @DisplayName("getFacilityLevels should answer the levels still in use, named in order") + void getLevels_shouldAnswerLiveLevels() { + ArrayList levels = new ArrayList<>(List.of(new M_FacilityLevel())); + when(m_facilityLevelRepo.findByDeletedFalseOrderByLevelName()).thenReturn(levels); + + assertSame(levels, service.getFacilityLevels()); + } + + @Test + @DisplayName("getFacilityTypesByBlock should answer the types run in the taluk asked about") + void getByBlock_shouldAnswerTypesOfTaluk() { + when(m_facilitytypeRepo.findFacilityTypesByBlock(3011)).thenReturn(List.of(facilityType())); + + assertEquals(1, service.getFacilityTypesByBlock(3011).size()); + } + + @Test + @DisplayName("getFacilityTypesByState should answer the types run in the state asked about") + void getByState_shouldAnswerTypesOfState() { + when(m_facilitytypeRepo.findByStateID(STATE_ID)).thenReturn(List.of(facilityType())); + + assertEquals(1, service.getFacilityTypesByState(STATE_ID).size()); + } + + @Test + @DisplayName("checkFacilityTypeNameExists should report whether the state already has the name") + void checkName_shouldReportWhetherNameIsUsed() { + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse("Primary Health Centre", + STATE_ID)).thenReturn(true); + assertTrue(service.checkFacilityTypeNameExists("Primary Health Centre", STATE_ID)); + + when(m_facilitytypeRepo.existsByFacilityTypeNameAndStateIDAndDeletedFalse("Sub Centre", STATE_ID)) + .thenReturn(false); + assertFalse(service.checkFacilityTypeNameExists("Sub Centre", STATE_ID)); + } +} diff --git a/src/test/java/com/iemr/admin/service/foetalmonitormaster/FoetalMonitorServiceImplTest.java b/src/test/java/com/iemr/admin/service/foetalmonitormaster/FoetalMonitorServiceImplTest.java new file mode 100644 index 0000000..6731490 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/foetalmonitormaster/FoetalMonitorServiceImplTest.java @@ -0,0 +1,420 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.foetalmonitormaster; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.foetalmonitormaster.FoetalMonitorDeviceID; +import com.iemr.admin.data.foetalmonitormaster.M_FoetalMonitor; +import com.iemr.admin.repo.foetalmonitormaster.FoetalMonitorDeviceIDRepo; +import com.iemr.admin.repo.foetalmonitormaster.FoetalMonitorRepository; +import com.iemr.admin.repository.vanMaster.VanMasterRepository; +import com.iemr.admin.utils.exception.IEMRException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * A fetosense device is attached to one van at a time, so the mapping rules here + * decide whether a foetal monitor reading can be traced back to the van it was + * taken in. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("FoetalMonitorServiceImpl Test Suite") +class FoetalMonitorServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer VAN_ID = 71; + private static final Long VFD_ID = 9001L; + + @Mock + private FoetalMonitorRepository foetalMonitorRepository; + + @Mock + private VanMasterRepository masterVanRepo; + + @Mock + private FoetalMonitorDeviceIDRepo foetalMonitorDeviceIDRepo; + + @InjectMocks + private FoetalMonitorServiceImpl service; + + private static M_FoetalMonitor test(Integer id, String name) { + M_FoetalMonitor test = new M_FoetalMonitor(); + test.setFoetalMonitorTestID(id); + test.setTestName(name); + return test; + } + + private static FoetalMonitorDeviceID device() { + FoetalMonitorDeviceID device = new FoetalMonitorDeviceID(); + device.setVfdID(VFD_ID); + device.setDeviceID("FS-1"); + device.setDeviceName("Fetosense 1"); + device.setVanID(VAN_ID); + device.setVanTypeID(1); + device.setParkingPlaceID(31); + device.setVanName("MMU Van 1"); + device.setProviderServiceMapID(PSM_ID); + device.setCreatedBy("admin"); + device.setDeactivated(Boolean.FALSE); + device.setDeleted(Boolean.FALSE); + return device; + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should publish the tests it stored") + void createTestMaster_shouldPublishStoredTests() throws Exception { + when(foetalMonitorRepository.saveAll(anyList())).thenReturn(List.of(test(11, "Non stress test"))); + + String created = service.createFoetalMonitorTestMaster("[{\"testName\":\"Non stress test\"}]"); + + assertTrue(created.contains("Non stress test"), created); + } + + @Test + @DisplayName("createFoetalMonitorTestMaster should answer nothing when it stored fewer than it was given") + void createTestMaster_shouldAnswerNothingOnPartialStore() throws Exception { + when(foetalMonitorRepository.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createFoetalMonitorTestMaster("[{\"testName\":\"Non stress test\"}]")); + } + + @Test + @DisplayName("getFoetalMonitorTestMaster should publish the tests of the provider") + void getTestMaster_shouldPublishProviderTests() { + when(foetalMonitorRepository.getByProviderServiceMapID(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(test(11, "Non stress test")))); + + assertTrue(service.getFoetalMonitorTestMaster(PSM_ID).contains("Non stress test")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMaster should publish the test once the edit lands") + void updateTestMaster_shouldPublishEditedTest() { + when(foetalMonitorRepository.updateFoetalMonitorDetails(11, "Non stress test", null, "admin")).thenReturn(1); + when(foetalMonitorRepository.getByFoetalMonitorTestID(11)).thenReturn(test(11, "Non stress test")); + + String published = service.updateFoetalMonitorTestMaster( + "{\"foetalMonitorTestID\":11,\"testName\":\"Non stress test\",\"modifiedBy\":\"admin\"}"); + + assertTrue(published.contains("Non stress test"), published); + } + + @Test + @DisplayName("updateFoetalMonitorTestMaster should answer nothing for a request that names no test") + void updateTestMaster_shouldAnswerNothingWithoutTest() { + assertNull(service.updateFoetalMonitorTestMaster("{\"testName\":\"Non stress test\"}")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMaster should answer nothing when the edit changed nothing") + void updateTestMaster_shouldAnswerNothingWhenNothingChanged() { + when(foetalMonitorRepository.updateFoetalMonitorDetails(anyInt(), any(), any(), any())).thenReturn(0); + + assertNull(service.updateFoetalMonitorTestMaster("{\"foetalMonitorTestID\":11}")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMasterStatus should publish the test once its status has changed") + void updateTestStatus_shouldPublishChangedTest() throws Exception { + when(foetalMonitorRepository.updateFoetalMonitorStatus(11, true)).thenReturn(1); + when(foetalMonitorRepository.getByFoetalMonitorTestID(11)).thenReturn(test(11, "Non stress test")); + + assertTrue(service.updateFoetalMonitorTestMasterStatus(11, true).contains("Non stress test")); + } + + @Test + @DisplayName("updateFoetalMonitorTestMasterStatus should answer nothing when no test changed") + void updateTestStatus_shouldAnswerNothingWhenNothingChanged() throws Exception { + when(foetalMonitorRepository.updateFoetalMonitorStatus(11, true)).thenReturn(0); + + assertNull(service.updateFoetalMonitorTestMasterStatus(11, true)); + } + + @Test + @DisplayName("saveFoetalMonitorDeviceID should report the devices it stored") + void saveDeviceID_shouldReportStoredDevices() throws Exception { + when(foetalMonitorDeviceIDRepo.saveAll(anyList())) + .thenReturn(new ArrayList<>(List.of(device()))); + + assertEquals(1, service.saveFoetalMonitorDeviceID(new ArrayList<>(List.of(device())))); + } + + @Test + @DisplayName("saveFoetalMonitorDeviceID should refuse a run that stored nothing") + void saveDeviceID_shouldRefuseEmptyStore() { + when(foetalMonitorDeviceIDRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertThrows(IEMRException.class, + () -> service.saveFoetalMonitorDeviceID(new ArrayList<>(List.of(device())))); + } + + @Test + @DisplayName("getFoetalMonitorDeviceID should publish the devices of the provider") + void getDeviceID_shouldPublishProviderDevices() throws Exception { + when(foetalMonitorDeviceIDRepo.getFoetalMonitorDeviceID(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(device()))); + + String published = service.getFoetalMonitorDeviceID(device()); + + assertTrue(published.contains("fetosenseDeviceIDs"), published); + assertTrue(published.contains("FS-1"), published); + } + + @Test + @DisplayName("getFoetalMonitorDeviceID should report a lookup it could not run") + void getDeviceID_shouldReportFailedLookup() { + when(foetalMonitorDeviceIDRepo.getFoetalMonitorDeviceID(anyInt())) + .thenThrow(new IllegalStateException("no connection")); + + assertThrows(IEMRException.class, () -> service.getFoetalMonitorDeviceID(device())); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should release the van the device was attached to") + void deleteDeviceID_shouldReleaseVan() throws Exception { + FoetalMonitorDeviceID request = device(); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + when(masterVanRepo.updateVanFoetalMonitorsmapping(true, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteFoetalMonitorDeviceID(request)); + verify(masterVanRepo).updateVanFoetalMonitorsmapping(true, VAN_ID); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should mark the van unmapped when the device is retired") + void deleteDeviceID_shouldMarkVanUnmappedOnRetirement() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setDeleted(Boolean.TRUE); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + when(masterVanRepo.updateVanFoetalMonitorsmapping(false, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteFoetalMonitorDeviceID(request)); + verify(masterVanRepo).updateVanFoetalMonitorsmapping(false, VAN_ID); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should refuse a device whose van could not be released") + void deleteDeviceID_shouldRefuseWhenVanCannotBeReleased() { + FoetalMonitorDeviceID request = device(); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + when(masterVanRepo.updateVanFoetalMonitorsmapping(anyBoolean(), anyInt())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.deleteFoetalMonitorDeviceID(request)); + } + + @Test + @DisplayName("deleteFoetalMonitorDeviceID should leave the vans alone for a device attached to none") + void deleteDeviceID_shouldLeaveVansAloneForUnattachedDevice() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setVanID(null); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + + assertEquals(1, service.deleteFoetalMonitorDeviceID(request)); + verify(masterVanRepo, never()).updateVanFoetalMonitorsmapping(anyBoolean(), anyInt()); + } + + @Test + @DisplayName("getvanIDAndFoetalMonitorDeviceID should publish the vans and devices still free to pair") + void getVanAndDevice_shouldPublishFreePairs() throws Exception { + ArrayList vanRows = new ArrayList<>(); + vanRows.add(new Object[] { VAN_ID, "MMU Van 1", "KA-01-AB-1234" }); + when(masterVanRepo.getVanIDNotMappedWithDevice(1, 31, PSM_ID)).thenReturn(vanRows); + when(foetalMonitorDeviceIDRepo.getFoetalMonitorDeviceIDNotMapped(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(device()))); + + String published = service.getvanIDAndFoetalMonitorDeviceID(device()); + + assertTrue(published.contains("VanIDs"), published); + assertTrue(published.contains("MMU Van 1"), published); + assertTrue(published.contains("deviceIDs"), published); + } + + @Test + @DisplayName("getvanIDAndFoetalMonitorDeviceID should report a lookup it could not run") + void getVanAndDevice_shouldReportFailedLookup() { + when(masterVanRepo.getVanIDNotMappedWithDevice(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertThrows(IEMRException.class, () -> service.getvanIDAndFoetalMonitorDeviceID(device())); + } + + @Test + @DisplayName("vanIDAndDeviceIDMapping should attach the device and mark the van as carrying one") + void mapping_shouldAttachDeviceAndMarkVan() throws Exception { + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + anyString(), anyString())).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(true, VAN_ID)).thenReturn(1); + + assertEquals(1, service.vanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("vanIDAndDeviceIDMapping should refuse a pairing the van could not be marked for") + void mapping_shouldRefuseWhenVanCannotBeMarked() { + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + anyString(), anyString())).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(anyBoolean(), anyInt())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.vanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("vanIDAndDeviceIDMapping should refuse a pairing the device could not take") + void mapping_shouldRefuseWhenDeviceCannotTakePairing() { + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + anyString(), anyString())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.vanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("updateFoetalMonitorDeviceID should report the device it saved") + void updateDeviceID_shouldReportSavedDevice() throws Exception { + FoetalMonitorDeviceID request = device(); + when(foetalMonitorDeviceIDRepo.save(request)).thenReturn(request); + + assertEquals(1, service.updateFoetalMonitorDeviceID(request)); + } + + @Test + @DisplayName("updateFoetalMonitorDeviceID should refuse an edit the store did not take") + void updateDeviceID_shouldRefuseUntakenEdit() { + when(foetalMonitorDeviceIDRepo.save(any())).thenReturn(null); + + assertThrows(IEMRException.class, () -> service.updateFoetalMonitorDeviceID(device())); + } + + @Test + @DisplayName("getVanIDMappingWorklist should publish the pairings on record") + void getWorklist_shouldPublishPairings() throws Exception { + when(foetalMonitorDeviceIDRepo.getMappedWorklist(1, 31, PSM_ID)) + .thenReturn(new ArrayList<>(List.of(device()))); + + assertTrue(service.getVanIDMappingWorklist(device()).contains("FS-1")); + } + + @Test + @DisplayName("getVanIDMappingWorklist should report a lookup it could not run") + void getWorklist_shouldReportFailedLookup() { + when(foetalMonitorDeviceIDRepo.getMappedWorklist(any(), any(), any())) + .thenThrow(new IllegalStateException("no connection")); + + assertThrows(IEMRException.class, () -> service.getVanIDMappingWorklist(device())); + } + + @Test + @DisplayName("updatingvanIDAndDeviceIDMapping should clear the old van before attaching the new one") + void updateMapping_shouldClearOldVanFirst() throws Exception { + when(foetalMonitorDeviceIDRepo.updateVanDetailsToNull(VFD_ID)).thenReturn(1); + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + any(), anyString())).thenReturn(1); + + assertEquals(1, service.updatingvanIDAndDeviceIDMapping(device())); + verify(foetalMonitorDeviceIDRepo).updateVanDetailsToNull(VFD_ID); + } + + @Test + @DisplayName("updatingvanIDAndDeviceIDMapping should refuse an edit whose old van could not be cleared") + void updateMapping_shouldRefuseWhenOldVanCannotBeCleared() { + when(foetalMonitorDeviceIDRepo.updateVanDetailsToNull(VFD_ID)).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.updatingvanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("updatingvanIDAndDeviceIDMapping should refuse an edit the new van could not take") + void updateMapping_shouldRefuseWhenNewVanCannotTake() { + when(foetalMonitorDeviceIDRepo.updateVanDetailsToNull(VFD_ID)).thenReturn(1); + when(foetalMonitorDeviceIDRepo.createMappingOfVanIDAndDeviceID(anyInt(), anyInt(), anyInt(), anyString(), + any(), anyString())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.updatingvanIDAndDeviceIDMapping(device())); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should release the pairing and free the van") + void deleteMapping_shouldReleasePairingAndFreeVan() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.TRUE); + when(foetalMonitorDeviceIDRepo.deleteMapping(true, VFD_ID)).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(false, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteVanIDAndDeviceIDMapping(request)); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should refuse to reinstate a van another device already holds") + void deleteMapping_shouldRefuseVanHeldByAnotherDevice() { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.FALSE); + when(foetalMonitorDeviceIDRepo.getMappedVanDetails(VAN_ID)).thenReturn(1); + + IEMRException thrown = assertThrows(IEMRException.class, + () -> service.deleteVanIDAndDeviceIDMapping(request)); + assertTrue(thrown.getMessage().contains("already mapped with a device"), thrown.getMessage()); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should reinstate a pairing for a van that is free") + void deleteMapping_shouldReinstatePairingForFreeVan() throws Exception { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.FALSE); + when(foetalMonitorDeviceIDRepo.getMappedVanDetails(VAN_ID)).thenReturn(0); + when(foetalMonitorDeviceIDRepo.deleteMapping(false, VFD_ID)).thenReturn(1); + when(masterVanRepo.updateVanFoetalMonitorsmapping(true, VAN_ID)).thenReturn(1); + + assertEquals(1, service.deleteVanIDAndDeviceIDMapping(request)); + } + + @Test + @DisplayName("deleteVanIDAndDeviceIDMapping should refuse a pairing the store would not change") + void deleteMapping_shouldRefuseUnchangedPairing() { + FoetalMonitorDeviceID request = device(); + request.setDeactivated(Boolean.TRUE); + when(foetalMonitorDeviceIDRepo.deleteMapping(anyBoolean(), any())).thenReturn(0); + + assertThrows(IEMRException.class, () -> service.deleteVanIDAndDeviceIDMapping(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/health/HealthServiceTest.java b/src/test/java/com/iemr/admin/service/health/HealthServiceTest.java new file mode 100644 index 0000000..b2e8176 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/health/HealthServiceTest.java @@ -0,0 +1,472 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.health; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * The health endpoint is what the deployment's monitoring watches, so it has to + * tell a database that is merely slow apart from one that is unreachable. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("HealthService Test Suite") +class HealthServiceTest { + + @Mock + private DataSource dataSource; + + @Mock + private RedisConnectionFactory redisConnectionFactory; + + @Mock + private Connection connection; + + @Mock + private Statement statement; + + @Mock + private RedisConnection redisConnection; + + @SuppressWarnings("unchecked") + private static ObjectProvider providerOf(T value) { + ObjectProvider provider = mock(ObjectProvider.class); + when(provider.getIfAvailable()).thenReturn(value); + return provider; + } + + /** + * Builds the service without a data source so its constructor starts no background + * cycle, then attaches the stores under test. That keeps the diagnostics the tests + * drive from racing a scheduled cycle over the same mocks. + */ + private HealthService serviceWith(DataSource ds, RedisConnectionFactory redis) { + HealthService service = new HealthService(providerOf(null), providerOf(redis)); + ReflectionTestUtils.setField(service, "dataSource", ds); + ((AtomicLong) ReflectionTestUtils.getField(service, "lastDiagnosticRunAt")).set(0); + return service; + } + + private void databaseAnswers() throws SQLException { + when(dataSource.getConnection()).thenReturn(connection); + when(connection.createStatement()).thenReturn(statement); + } + + private void redisAnswers() { + when(redisConnectionFactory.getConnection()).thenReturn(redisConnection); + } + + private static ResultSet countingResultSet(int count) throws SQLException { + ResultSet rs = mock(ResultSet.class); + when(rs.next()).thenReturn(true); + when(rs.getInt("cnt")).thenReturn(count); + return rs; + } + + private static ResultSet statusResultSet(long value) throws SQLException { + ResultSet rs = mock(ResultSet.class); + when(rs.next()).thenReturn(true); + when(rs.getLong("Value")).thenReturn(value); + when(rs.getInt("Value")).thenReturn((int) value); + return rs; + } + + @SuppressWarnings("unchecked") + private static String statusOf(Map health, String service) { + return (String) ((Map) health.get(service)).get("status"); + } + + @SuppressWarnings("unchecked") + private static String severityOf(Map health, String service) { + return (String) ((Map) health.get(service)).get("severity"); + } + + @Nested + @DisplayName("checkHealth") + class CheckHealthTests { + + @Test + @DisplayName("should report the deployment up when both stores answer") + void checkHealth_shouldReportUpWhenBothStoresAnswer() throws Exception { + databaseAnswers(); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + + Map health = service.checkHealth(); + + assertEquals("UP", health.get("status")); + assertEquals("UP", statusOf(health, "mysql")); + assertEquals("UP", statusOf(health, "redis")); + assertNotNull(health.get("checkedAt")); + } + + @Test + @DisplayName("should report the deployment down when the database cannot be reached") + void checkHealth_shouldReportDownWhenDatabaseUnreachable() throws Exception { + when(dataSource.getConnection()).thenThrow(new SQLException("connection refused")); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + + Map health = service.checkHealth(); + + assertEquals("DOWN", health.get("status")); + assertEquals("DOWN", statusOf(health, "mysql")); + assertEquals("CRITICAL", severityOf(health, "mysql")); + } + + @Test + @DisplayName("should report the deployment down when Redis cannot be reached") + void checkHealth_shouldReportDownWhenRedisUnreachable() throws Exception { + databaseAnswers(); + when(redisConnectionFactory.getConnection()) + .thenThrow(new IllegalStateException("connection refused")); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + + Map health = service.checkHealth(); + + assertEquals("DOWN", health.get("status")); + assertEquals("DOWN", statusOf(health, "redis")); + } + + @Test + @DisplayName("should report a store that is not configured rather than call it down") + void checkHealth_shouldReportUnconfiguredStore() { + HealthService service = serviceWith(null, null); + + Map health = service.checkHealth(); + + assertEquals("UP", health.get("status"), + "a deployment without these stores is not itself unhealthy"); + assertEquals("NOT_CONFIGURED", statusOf(health, "mysql")); + assertEquals("INFO", severityOf(health, "mysql")); + assertEquals("NOT_CONFIGURED", statusOf(health, "redis")); + } + + @Test + @DisplayName("should report a degraded database as still up but flagged") + void checkHealth_shouldReportDegradedDatabase() throws Exception { + databaseAnswers(); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "set", "WARNING"); + + Map health = service.checkHealth(); + + assertEquals("UP", health.get("status"), "a degraded database is still serving requests"); + assertEquals("DEGRADED", statusOf(health, "mysql")); + } + + @Test + @DisplayName("should report a critically degraded database as down") + void checkHealth_shouldReportCriticalDatabaseAsDown() throws Exception { + databaseAnswers(); + redisAnswers(); + HealthService service = serviceWith(dataSource, redisConnectionFactory); + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "set", "CRITICAL"); + + assertEquals("DOWN", service.checkHealth().get("status")); + } + } + + @Nested + @DisplayName("Background diagnostics") + class DiagnosticTests { + + private HealthService diagnosingService() throws SQLException { + databaseAnswers(); + return serviceWith(dataSource, redisConnectionFactory); + } + + private String severityAfterDiagnostics(HealthService service) { + ((AtomicLong) ReflectionTestUtils.getField(service, "lastDiagnosticRunAt")).set(0); + ReflectionTestUtils.invokeMethod(service, "runAdvancedMySQLDiagnostics"); + return (String) ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "get"); + } + + private void everyCheckClean() throws SQLException { + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + } + + @Test + @DisplayName("should report a healthy database when every check is clean") + void diagnostics_shouldReportHealthyDatabase() throws Exception { + HealthService service = diagnosingService(); + everyCheckClean(); + + assertEquals("OK", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when more processes are stuck than the threshold allows") + void diagnostics_shouldWarnOnStuckProcesses() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("PROCESSLIST")) { + return countingResultSet(10); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should stay healthy when a few processes are stuck but under the threshold") + void diagnostics_shouldStayHealthyUnderStuckThreshold() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("PROCESSLIST")) { + return countingResultSet(2); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("OK", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should escalate to critical when several transactions run long") + void diagnostics_shouldEscalateOnManyLongTransactions() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("INNODB_TRX")) { + return countingResultSet(6); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("CRITICAL", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when a single transaction runs long") + void diagnostics_shouldWarnOnOneLongTransaction() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("INNODB_TRX")) { + return countingResultSet(2); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when new deadlocks have happened since the previous cycle") + void diagnostics_shouldWarnOnNewDeadlocks() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Innodb_deadlocks")) { + return statusResultSet(3); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when new slow queries have been logged since the previous cycle") + void diagnostics_shouldWarnOnNewSlowQueries() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Slow_queries")) { + return statusResultSet(7); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should escalate to critical when the connection pool is nearly exhausted") + void diagnostics_shouldEscalateOnExhaustedPool() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Threads_connected")) { + return statusResultSet(490); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("CRITICAL", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should warn when connection usage is high but not yet exhausted") + void diagnostics_shouldWarnOnHighConnectionUsage() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenAnswer(call -> { + String sql = call.getArgument(0); + if (sql.contains("Threads_connected")) { + return statusResultSet(450); + } + if (sql.contains("max_connections")) { + return statusResultSet(500); + } + if (sql.startsWith("SHOW")) { + return statusResultSet(0); + } + return countingResultSet(0); + }); + + assertEquals("WARNING", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should report critical when it cannot open a connection to diagnose at all") + void diagnostics_shouldReportCriticalWhenConnectionCannotBeOpened() throws Exception { + HealthService service = diagnosingService(); + when(dataSource.getConnection()).thenThrow(new SQLException("connection refused")); + + assertEquals("CRITICAL", severityAfterDiagnostics(service)); + } + + @Test + @DisplayName("should keep the previous verdict when a cycle is asked for too soon") + void diagnostics_shouldKeepPreviousVerdictWhenAskedTooSoon() throws Exception { + HealthService service = diagnosingService(); + everyCheckClean(); + severityAfterDiagnostics(service); + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "set", "WARNING"); + ReflectionTestUtils.invokeMethod(service, "runAdvancedMySQLDiagnostics"); + + assertEquals("WARNING", + ReflectionTestUtils.invokeMethod( + ReflectionTestUtils.getField(service, "cachedDbSeverity"), "get"), + "a cycle inside the guard window must not overwrite the standing verdict"); + } + + @Test + @DisplayName("should survive a check whose query the database refuses") + void diagnostics_shouldSurviveRefusedQuery() throws Exception { + HealthService service = diagnosingService(); + when(statement.executeQuery(anyString())).thenThrow(new SQLException("access denied")); + + assertEquals("OK", severityAfterDiagnostics(service), + "a check that cannot run must not by itself condemn the database"); + } + } + + @Test + @DisplayName("shutdownDiagnostics should stop the background cycle") + void shutdownDiagnostics_shouldStopBackgroundCycle() throws Exception { + databaseAnswers(); + HealthService service = new HealthService(providerOf(dataSource), providerOf(redisConnectionFactory)); + + service.shutdownDiagnostics(); + + java.util.concurrent.ExecutorService scheduler = (java.util.concurrent.ExecutorService) + ReflectionTestUtils.getField(service, "diagnosticScheduler"); + assertEquals(true, scheduler.isShutdown()); + } +} diff --git a/src/test/java/com/iemr/admin/service/item/ItemServiceImplTest.java b/src/test/java/com/iemr/admin/service/item/ItemServiceImplTest.java new file mode 100644 index 0000000..4fa9c95 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/item/ItemServiceImplTest.java @@ -0,0 +1,284 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.item; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.items.CodeChecker; +import com.iemr.admin.data.items.ItemMaster; +import com.iemr.admin.data.items.M_ItemCategory; +import com.iemr.admin.data.items.M_ItemForm; +import com.iemr.admin.data.items.M_Route; +import com.iemr.admin.repository.item.ItemCategoryRepo; +import com.iemr.admin.repository.item.ItemFormRepo; +import com.iemr.admin.repository.item.ItemRepo; +import com.iemr.admin.repository.item.RouteRepo; +import com.iemr.admin.repository.itemfacilitymapping.M_itemfacilitymappingRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The item service maintains the inventory catalogue and the codes that keep + * each entry unique within a provider. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ItemServiceImpl Test Suite") +class ItemServiceImplTest { + + private static final Integer PSM_ID = 4001; + + @Mock + private ItemRepo itemRepo; + + @Mock + private ItemCategoryRepo itemCategoryRepo; + + @Mock + private RouteRepo routeRepo; + + @Mock + private ItemFormRepo itemFormRepo; + + @Mock + private M_itemfacilitymappingRepo itemfacilitymappingRepo; + + @InjectMocks + private ItemServiceImpl service; + + private static M_ItemCategory category(Integer id) { + M_ItemCategory category = new M_ItemCategory(); + category.setItemCategoryID(id); + return category; + } + + @Test + @DisplayName("getItemCategory should read the whole catalogue when retired categories are wanted too") + void getItemCategory_shouldReadWholeCatalogue() { + List stored = List.of(category(31)); + when(itemCategoryRepo.findByProviderServiceMapIDOrderByItemCategoryName(PSM_ID)).thenReturn(stored); + + assertSame(stored, service.getItemCategory(true, PSM_ID)); + } + + @Test + @DisplayName("getItemCategory should read only the live categories when retired ones are excluded") + void getItemCategory_shouldReadLiveCategories() { + List stored = List.of(category(31)); + when(itemCategoryRepo.findByDeletedAndProviderServiceMapIDOrderByItemCategoryName(false, PSM_ID)) + .thenReturn(stored); + + assertSame(stored, service.getItemCategory(false, PSM_ID)); + } + + @Test + @DisplayName("getItemCategory should answer nothing when the caller names no provider") + void getItemCategory_shouldAnswerNothingWithoutProvider() { + assertTrue(service.getItemCategory(true, null).isEmpty()); + verify(itemCategoryRepo, never()).findByProviderServiceMapIDOrderByItemCategoryName(anyInt()); + } + + @Test + @DisplayName("the item lookups should each reach their own repository query") + void itemLookups_shouldReachTheirOwnQuery() { + ItemMaster item = new ItemMaster(); + List items = List.of(item); + when(itemRepo.save(item)).thenReturn(item); + when(itemRepo.saveAll(anyList())).thenReturn(items); + when(itemRepo.findByProviderServiceMapIDOrderByItemName(PSM_ID)).thenReturn(items); + when(itemRepo.findByItemID(101)).thenReturn(item); + when(itemRepo.findDetailOne(101)).thenReturn(item); + when(itemRepo.getItemMasters(PSM_ID, 31)).thenReturn(items); + when(itemRepo.deleteItemMaster(101, true)).thenReturn(1); + when(itemRepo.discontinueItemMaster(101, true)).thenReturn(1); + when(itemCategoryRepo.findByItemCategoryID(31)).thenReturn(category(31)); + when(routeRepo.getAll()).thenReturn(List.of(new M_Route())); + when(itemFormRepo.getAll()).thenReturn(List.of(new M_ItemForm())); + + assertSame(item, service.createItemMaster(item)); + assertSame(items, service.addAllItemMaster(new ArrayList<>())); + assertSame(items, service.getItemMaster(PSM_ID)); + assertSame(item, service.getItemMasterByID(101)); + assertSame(item, service.getItemMasterCatByID(101)); + assertSame(items, service.getItemMasters(PSM_ID, 31)); + assertEquals(1, service.blockItemMaster(101, true)); + assertEquals(1, service.discontinueItemMaster(101, true)); + assertEquals(31, service.getItemCategory(31).getItemCategoryID()); + assertEquals(1, service.getItemRouteProviderServiceMapID(PSM_ID).size()); + assertEquals(1, service.getItemFormProviderServiceMapID(PSM_ID).size()); + } + + @Test + @DisplayName("updateItemIssueConfig should count only the categories that name an issue type") + void updateItemIssueConfig_shouldCountOnlyComplete() { + M_ItemCategory complete = category(31); + complete.setIssueType("FIFO"); + M_ItemCategory incomplete = category(32); + when(itemCategoryRepo.updateIssueConfig(31, "FIFO")).thenReturn(1); + + assertEquals(1, service.updateItemIssueConfig(List.of(complete, incomplete))); + verify(itemCategoryRepo, never()).updateIssueConfig(32, null); + } + + @Test + @DisplayName("updateExpiryAlert should count only the categories that name an alert window") + void updateExpiryAlert_shouldCountOnlyComplete() { + M_ItemCategory complete = category(31); + complete.setAlertBeforeDays(30); + M_ItemCategory incomplete = category(32); + when(itemCategoryRepo.updateExpiryAlert(31, 30)).thenReturn(1); + + assertEquals(1, service.updateExpiryAlert(List.of(complete, incomplete))); + } + + @Test + @DisplayName("createItemCategories should answer the id of the first category it stored") + void createItemCategories_shouldAnswerFirstStoredId() { + when(itemCategoryRepo.saveAll(anyList())).thenReturn(List.of(category(31))); + + assertEquals(31, service.createItemCategories(new ArrayList<>())); + } + + @Test + @DisplayName("createItemCategories should answer zero when nothing was stored") + void createItemCategories_shouldAnswerZeroWhenNothingStored() { + when(itemCategoryRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); + + assertEquals(0, service.createItemCategories(new ArrayList<>())); + } + + @Test + @DisplayName("createItemForms should answer the id of the first form it stored") + void createItemForms_shouldAnswerFirstStoredId() { + M_ItemForm form = new M_ItemForm(); + form.setItemFormID(11); + when(itemFormRepo.saveAll(anyList())).thenReturn(List.of(form)); + + assertEquals(11, service.createItemForms(new ArrayList<>())); + when(itemFormRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); + assertEquals(0, service.createItemForms(new ArrayList<>())); + } + + @Test + @DisplayName("createRoutes should answer the id of the first route it stored") + void createRoutes_shouldAnswerFirstStoredId() { + M_Route route = new M_Route(); + route.setRouteID(21); + when(routeRepo.saveAll(anyList())).thenReturn(List.of(route)); + + assertEquals(21, service.createRoutes(new ArrayList<>())); + when(routeRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); + assertEquals(0, service.createRoutes(new ArrayList<>())); + } + + @Test + @DisplayName("the edit and block calls should each reach their own repository query") + void editAndBlockCalls_shouldReachTheirOwnQuery() { + M_ItemCategory cat = category(31); + cat.setItemCategoryDesc("Drugs"); + cat.setModifiedBy("admin"); + cat.setDeleted(Boolean.TRUE); + M_ItemForm form = new M_ItemForm(); + form.setItemFormID(11); + form.setItemFormDesc("Tablet"); + form.setModifiedBy("admin"); + form.setDeleted(Boolean.TRUE); + M_Route route = new M_Route(); + route.setRouteID(21); + route.setRouteDesc("Oral"); + route.setModifiedBy("admin"); + route.setDeleted(Boolean.TRUE); + when(itemCategoryRepo.updateItemCategoryDetails(31, "Drugs", "admin")).thenReturn(1); + when(itemCategoryRepo.blockItemCategory(31, Boolean.TRUE, "admin")).thenReturn(1); + when(itemFormRepo.updateItemFormDetails(11, "Tablet", "admin")).thenReturn(1); + when(itemFormRepo.blockItemForm(11, Boolean.TRUE, "admin")).thenReturn(1); + when(routeRepo.updateRouteDetails(21, "Oral", "admin")).thenReturn(1); + when(routeRepo.blockRoute(21, Boolean.TRUE, "admin")).thenReturn(1); + + assertEquals(1, service.editItemCategory(cat)); + assertEquals(1, service.blockItemCategory(cat)); + assertEquals(1, service.editItemForm(form)); + assertEquals(1, service.blockItemForm(form)); + assertEquals(1, service.editRoute(route)); + assertEquals(1, service.blockRoute(route)); + } + + @Test + @DisplayName("the code checks should report a code the provider already uses") + void codeChecks_shouldReportUsedCode() { + CodeChecker checker = new CodeChecker(); + checker.setCode("CODE-1"); + checker.setProviderServiceMapID(PSM_ID); + when(itemCategoryRepo.findByItemCategoryCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(category(31))); + when(itemFormRepo.findByItemFormCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(new M_ItemForm())); + when(itemRepo.findByItemCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(new ItemMaster())); + when(routeRepo.findByRouteCodeAndProviderServiceMapID("CODE-1", PSM_ID)) + .thenReturn(List.of(new M_Route())); + + assertTrue(service.checkCodeCategory(checker)); + assertTrue(service.checkCodeForm(checker)); + assertTrue(service.checkCodeItem(checker)); + assertTrue(service.checkCodeRoute(checker)); + } + + @Test + @DisplayName("the code checks should clear a code nobody uses yet") + void codeChecks_shouldClearFreeCode() { + CodeChecker checker = new CodeChecker(); + checker.setCode("CODE-2"); + checker.setProviderServiceMapID(PSM_ID); + when(itemCategoryRepo.findByItemCategoryCodeAndProviderServiceMapID("CODE-2", PSM_ID)) + .thenReturn(new ArrayList<>()); + when(itemFormRepo.findByItemFormCodeAndProviderServiceMapID("CODE-2", PSM_ID)) + .thenReturn(new ArrayList<>()); + when(itemRepo.findByItemCodeAndProviderServiceMapID("CODE-2", PSM_ID)).thenReturn(new ArrayList<>()); + when(routeRepo.findByRouteCodeAndProviderServiceMapID("CODE-2", PSM_ID)).thenReturn(new ArrayList<>()); + + assertFalse(service.checkCodeCategory(checker)); + assertFalse(service.checkCodeForm(checker)); + assertFalse(service.checkCodeItem(checker)); + assertFalse(service.checkCodeRoute(checker)); + } +} diff --git a/src/test/java/com/iemr/admin/service/itemfacilitymapping/M_itemfacilitymappingImplTest.java b/src/test/java/com/iemr/admin/service/itemfacilitymapping/M_itemfacilitymappingImplTest.java new file mode 100644 index 0000000..6d8cbb5 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/itemfacilitymapping/M_itemfacilitymappingImplTest.java @@ -0,0 +1,195 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.itemfacilitymapping; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.itemfacilitymapping.M_itemfacilitymapping; +import com.iemr.admin.data.itemfacilitymapping.V_fetchItemFacilityMap; +import com.iemr.admin.data.items.ItemInStore; +import com.iemr.admin.repo.stockEntry.ItemStockEntryRepo; +import com.iemr.admin.repository.itemfacilitymapping.M_itemfacilitymappingRepo; +import com.iemr.admin.repository.itemfacilitymapping.V_fetchItemFacilityMapRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The item facility service records which items a store is allowed to hold, and + * answers what each store currently has on its shelves. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("M_itemfacilitymappingImpl Test Suite") +class M_itemfacilitymappingImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer FACILITY_ID = 9001; + private static final Integer MAP_ID = 3301; + private static final Integer ITEM_ID = 501; + + @Mock + private V_fetchItemFacilityMapRepo v_fetchItemFacilityMapRepo; + + @Mock + private M_itemfacilitymappingRepo m_itemfacilitymappingRepo; + + @Mock + private ItemStockEntryRepo itemStockEntryRepo; + + @InjectMocks + private M_itemfacilitymappingImpl service; + + private static M_itemfacilitymapping mapping() { + M_itemfacilitymapping mapping = new M_itemfacilitymapping(); + mapping.setItemStoreMapID(MAP_ID); + mapping.setItemID(ITEM_ID); + mapping.setFacilityID(FACILITY_ID); + mapping.setDeleted(Boolean.FALSE); + return mapping; + } + + @Test + @DisplayName("mapItemtoStore should answer the mappings the repository stored") + void map_shouldAnswerStoredMappings() { + ArrayList stored = new ArrayList<>(List.of(mapping())); + when(m_itemfacilitymappingRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.mapItemtoStore(new ArrayList<>())); + } + + @Test + @DisplayName("editdata and saveEditedItem should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + M_itemfacilitymapping stored = mapping(); + when(m_itemfacilitymappingRepo.findByItemFacilityMapID(MAP_ID)).thenReturn(stored); + when(m_itemfacilitymappingRepo.save(stored)).thenReturn(stored); + + assertSame(stored, service.editdata(MAP_ID)); + assertSame(stored, service.saveEditedItem(stored)); + } + + @Test + @DisplayName("editdata should answer nothing when the mapping is unknown") + void edit_shouldAnswerNothingForUnknownMapping() { + when(m_itemfacilitymappingRepo.findByItemFacilityMapID(-1)).thenReturn(null); + + assertNull(service.editdata(-1)); + } + + @Test + @DisplayName("getsubitemforsubStote should rebuild one item per row the query answers") + void getSubItems_shouldRebuildEachRow() { + when(m_itemfacilitymappingRepo.getItemforSubstore(PSM_ID, FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of( + new Object[] { ITEM_ID, "Paracetamol 500", Boolean.FALSE, 7 }))); + + ArrayList items = service.getsubitemforsubStote(PSM_ID, FACILITY_ID); + + assertEquals(1, items.size()); + assertEquals("Paracetamol 500", items.get(0).getItemName()); + assertEquals(ITEM_ID, items.get(0).getItemID()); + } + + @Test + @DisplayName("getsubitemforsubStote should skip a row the query could not fill in") + void getSubItems_shouldSkipIncompleteRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { ITEM_ID, "Paracetamol 500" }); + when(m_itemfacilitymappingRepo.getItemforSubstore(PSM_ID, FACILITY_ID)).thenReturn(rows); + + assertTrue(service.getsubitemforsubStote(PSM_ID, FACILITY_ID).isEmpty()); + } + + @Test + @DisplayName("the mapped item lookups should each reach their own repository query") + void mappedItemLookups_shouldReachTheirOwnQuery() { + ArrayList byProvider = new ArrayList<>(List.of(new V_fetchItemFacilityMap())); + ArrayList byFacility = new ArrayList<>(List.of(new V_fetchItemFacilityMap())); + when(v_fetchItemFacilityMapRepo.getAllFacilityMappedData(PSM_ID)).thenReturn(byProvider); + when(v_fetchItemFacilityMapRepo.getItemMappingsByFacilityAndSubStores(FACILITY_ID)).thenReturn(byFacility); + + assertSame(byProvider, service.getAllFacilityMappedData(PSM_ID)); + assertSame(byFacility, service.getItemMappingsByFacilityID(FACILITY_ID)); + } + + @Test + @DisplayName("getItemMastersFromStoreID should answer what the store holds of each item mapped to it") + void getItemMasters_shouldAnswerWhatStoreHolds() { + when(m_itemfacilitymappingRepo.getItemforStore(FACILITY_ID)) + .thenReturn(new ArrayList<>(List.of(new Object[] { ITEM_ID, "Paracetamol 500" }))); + when(itemStockEntryRepo.getQuantity(any(Integer[].class), anyInt())) + .thenReturn(new ArrayList<>(List.of( + new Object[] { FACILITY_ID, ITEM_ID, "Paracetamol 500", 250L }))); + + List held = service.getItemMastersFromStoreID(FACILITY_ID); + + assertEquals(1, held.size()); + assertEquals("Paracetamol 500", held.get(0).getItemName()); + assertEquals(250L, held.get(0).getQuantity()); + } + + @Test + @DisplayName("getItemMastersFromStoreID should answer nothing when the store holds none of its items") + void getItemMasters_shouldAnswerNothingWhenStoreEmpty() { + when(m_itemfacilitymappingRepo.getItemforStore(FACILITY_ID)).thenReturn(new ArrayList<>()); + when(itemStockEntryRepo.getQuantity(any(Integer[].class), anyInt())).thenReturn(new ArrayList<>()); + + assertTrue(service.getItemMastersFromStoreID(FACILITY_ID).isEmpty()); + } + + @Test + @DisplayName("deleteItemStoreMapping should report how many mappings the retirement touched") + void delete_shouldReportRowsTouched() { + M_itemfacilitymapping request = mapping(); + request.setDeleted(Boolean.TRUE); + when(m_itemfacilitymappingRepo.updateDeleteMap(MAP_ID, Boolean.TRUE)).thenReturn(1); + + assertEquals(1, service.deleteItemStoreMapping(request)); + } + + @Test + @DisplayName("deleteItemStoreMapping should report nothing touched when the mapping is unknown") + void delete_shouldReportNothingTouchedForUnknownMapping() { + M_itemfacilitymapping request = new M_itemfacilitymapping(); + request.setItemStoreMapID(-1); + when(m_itemfacilitymappingRepo.updateDeleteMap(-1, null)).thenReturn(0); + + assertEquals(0, service.deleteItemStoreMapping(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/locationmaster/LocationMasterServiceImplTest.java b/src/test/java/com/iemr/admin/service/locationmaster/LocationMasterServiceImplTest.java new file mode 100644 index 0000000..bdc8b14 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/locationmaster/LocationMasterServiceImplTest.java @@ -0,0 +1,180 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.locationmaster; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.M_District; +import com.iemr.admin.data.locationmaster.M_ProviderServiceAddMapping; +import com.iemr.admin.data.locationmaster.Showofficedetails; +import com.iemr.admin.data.locationmaster.StateServiceMapping1; +import com.iemr.admin.repo.locationmaster.LocationMasterRepo; +import com.iemr.admin.repo.locationmaster.MdistrictRepo; +import com.iemr.admin.repo.locationmaster.M_ProviderServiceAddMappingRepo; +import com.iemr.admin.repo.locationmaster.ShowofficedetailsRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** + * The location service reads a provider's office addresses out of several + * differently shaped queries and reshapes them into one carrier. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("LocationMasterServiceImpl Test Suite") +class LocationMasterServiceImplTest { + + private static final Integer PROVIDER_ID = 77; + private static final Integer PSM_ID = 4001; + + @Mock + private ShowofficedetailsRepo showofficedetailsRepo; + + @Mock + private MdistrictRepo mdistricRepo; + + @Mock + private M_ProviderServiceAddMappingRepo m_ProviderServiceAddMappingRepo; + + @Mock + private LocationMasterRepo locationMasterRepo; + + @InjectMocks + private LocationMasterServiceImpl service; + + @Test + @DisplayName("getStateByServiceProviderId should skip a row the query could not fill") + void getStateByServiceProviderId_shouldSkipUnfillableRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 29, "Karnataka", 1, PSM_ID }); + when(locationMasterRepo.getStateByServiceProviderId(PROVIDER_ID)).thenReturn(rows); + + assertEquals(1, service.getStateByServiceProviderId(PROVIDER_ID).size()); + } + + @Test + @DisplayName("getServiceByServiceProviderIdAndStateId should rebuild one mapping per row") + void getServiceByServiceProviderIdAndStateId_shouldRebuildEachRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 3, PSM_ID, "Tele Medicine" }); + when(locationMasterRepo.getServiceByServiceProviderIdAndStateId(PROVIDER_ID, 29)).thenReturn(rows); + + assertEquals(1, service.getServiceByServiceProviderIdAndStateId(PROVIDER_ID, 29).size()); + } + + @Test + @DisplayName("getStatesByServiceId should rebuild one mapping per row") + void getStatesByServiceId_shouldRebuildEachRow() { + ArrayList rows = new ArrayList<>(); + rows.add(null); + rows.add(new Object[] { 29, "Karnataka", PSM_ID }); + when(locationMasterRepo.getStatesByServiceId(3, PROVIDER_ID)).thenReturn(rows); + + assertEquals(1, service.getStatesByServiceId(3, PROVIDER_ID).size()); + } + + @Test + @DisplayName("getAllDistrictByStateId should rebuild each district into a plain carrier") + void getAllDistrictByStateId_shouldRebuildEachDistrict() { + M_District stored = new M_District(); + stored.setDistrictID(301); + stored.setDistrictName("Bengaluru Urban"); + when(mdistricRepo.getAllDistrictByStateId(29)).thenReturn(new ArrayList<>(List.of(stored))); + + ArrayList districts = service.getAllDistrictByStateId(29); + + assertEquals(1, districts.size()); + assertEquals("Bengaluru Urban", districts.get(0).getDistrictName()); + } + + @Test + @DisplayName("getlocationByMapid1 should gather the offices of every mapping the caller lists") + void getlocationByMapid1_shouldGatherAcrossMappings() { + Showofficedetails office = new Showofficedetails(); + when(showofficedetailsRepo.getlocationByMapid1(4001)).thenReturn(new ArrayList<>(List.of(office))); + when(showofficedetailsRepo.getlocationByMapid1(4002)).thenReturn(new ArrayList<>(List.of(office))); + + assertEquals(2, service.getlocationByMapid1(new ArrayList<>(List.of(4001, 4002))).size()); + } + + @Test + @DisplayName("getOfficeName should gather the office of every mapping the caller lists") + void getOfficeName_shouldGatherAcrossMappings() { + Showofficedetails request = new Showofficedetails(); + request.setProviderServiceMapID(PSM_ID); + when(showofficedetailsRepo.getOfficeName(PSM_ID)) + .thenReturn(new ArrayList<>(List.of(new Showofficedetails()))); + + assertEquals(1, service.getOfficeName(new ArrayList<>(List.of(request))).size()); + } + + @Test + @DisplayName("the remaining calls should each reach their own repository query") + void remainingCalls_shouldReachTheirOwnQuery() { + M_ProviderServiceAddMapping address = new M_ProviderServiceAddMapping(); + ArrayList addresses = new ArrayList<>(List.of(address)); + ArrayList offices = new ArrayList<>(List.of(new Showofficedetails())); + StateServiceMapping1 mapping = new StateServiceMapping1(PSM_ID); + ArrayList mappings = new ArrayList<>(List.of(mapping)); + when(m_ProviderServiceAddMappingRepo.save(address)).thenReturn(address); + when(m_ProviderServiceAddMappingRepo.saveAll(anyList())).thenReturn(addresses); + when(m_ProviderServiceAddMappingRepo.editData(51)).thenReturn(address); + when(m_ProviderServiceAddMappingRepo.getlocationByMapid(PSM_ID)).thenReturn(addresses); + when(showofficedetailsRepo.getAlldata()).thenReturn(offices); + when(showofficedetailsRepo.getlocationByMapid(PSM_ID)).thenReturn(offices); + when(showofficedetailsRepo.getlocationByMapid3(PSM_ID, 301)).thenReturn(offices); + when(locationMasterRepo.getProviderServiceMapID(PROVIDER_ID, 29, 3)).thenReturn(mapping); + when(locationMasterRepo.getAllByMapId2(PROVIDER_ID, 29, 3)).thenReturn(mappings); + when(locationMasterRepo.getAllByMapId3(PROVIDER_ID, 3)).thenReturn(mappings); + when(locationMasterRepo.getLocationByServiceID(PROVIDER_ID, 3)).thenReturn(mappings); + when(locationMasterRepo.getLocationByStateID(PROVIDER_ID, 29)).thenReturn(mappings); + + assertSame(address, service.addlocation(address)); + assertSame(addresses, service.addlocation(new ArrayList<>())); + assertSame(address, service.editData(51)); + assertSame(address, service.saveEditData(address)); + assertSame(addresses, service.getlocationByMapid(PSM_ID)); + assertSame(offices, service.getAlldata()); + assertSame(offices, service.getlocationByMapid2(PSM_ID)); + assertSame(offices, service.getlocationByMapid4(PSM_ID, 301)); + assertSame(mapping, service.getAllByMapId(PROVIDER_ID, 29, 3)); + assertSame(mappings, service.getAllByMapId2(PROVIDER_ID, 29, 3)); + assertSame(mappings, service.getAllByMapId3(PROVIDER_ID, 3)); + assertSame(mappings, service.getLocationByServiceId(PROVIDER_ID, 3)); + assertSame(mappings, service.getLocationBySateID(PROVIDER_ID, 29)); + } +} diff --git a/src/test/java/com/iemr/admin/service/manufacturer/ManufacturerServiceImplTest.java b/src/test/java/com/iemr/admin/service/manufacturer/ManufacturerServiceImplTest.java new file mode 100644 index 0000000..a473821 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/manufacturer/ManufacturerServiceImplTest.java @@ -0,0 +1,113 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.manufacturer; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.manufacturer.M_Manufacturer; +import com.iemr.admin.repo.manufacturer.ManufacturerRepo; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The manufacturer service is thin over its repository, but it answers nothing rather than an empty batch. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ManufacturerServiceImpl Test Suite") +class ManufacturerServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private ManufacturerRepo manufacturerRepo; + + @InjectMocks + private ManufacturerServiceImpl service; + + @Test + @DisplayName("createManufacturer should answer the records it stored") + void create_shouldAnswerStoredRecords() { + ArrayList stored = new ArrayList<>(List.of(new M_Manufacturer())); + when(manufacturerRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createManufacturer(new ArrayList<>())); + } + + @Test + @DisplayName("createManufacturer should answer nothing when the store took nothing") + void create_shouldAnswerNothingWhenNothingStored() { + when(manufacturerRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createManufacturer(new ArrayList<>())); + } + + @Test + @DisplayName("the lookups should each reach their own repository query") + void lookups_shouldReachTheirOwnQuery() { + M_Manufacturer record = new M_Manufacturer(); + ArrayList records = new ArrayList<>(List.of(record)); + when(manufacturerRepo.getManufacturerData(PSM_ID)).thenReturn(records); + when(manufacturerRepo.getEditData(RECORD_ID)).thenReturn(record); + when(manufacturerRepo.save(record)).thenReturn(record); + + assertSame(records, service.createManufacturer(PSM_ID)); + assertSame(record, service.editManufacturer(RECORD_ID)); + assertSame(record, service.saveEditedData(record)); + } + + @Test + @DisplayName("checkManufacturerCode should report a code the provider already uses") + void check_shouldReportUsedCode() { + M_Manufacturer request = new M_Manufacturer(); + request.setManufacturerCode("C-1"); + request.setProviderServiceMapID(PSM_ID); + when(manufacturerRepo.findByManufacturerCodeAndProviderServiceMapID("C-1", PSM_ID)).thenReturn(List.of(new M_Manufacturer())); + + assertTrue(service.checkManufacturerCode(request)); + } + + @Test + @DisplayName("checkManufacturerCode should clear a code nobody uses yet") + void check_shouldClearFreeCode() { + M_Manufacturer request = new M_Manufacturer(); + request.setManufacturerCode("C-2"); + request.setProviderServiceMapID(PSM_ID); + when(manufacturerRepo.findByManufacturerCodeAndProviderServiceMapID("C-2", PSM_ID)).thenReturn(new ArrayList()); + + assertFalse(service.checkManufacturerCode(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/nodalemailconfig/NodalConfigServiceImplTest.java b/src/test/java/com/iemr/admin/service/nodalemailconfig/NodalConfigServiceImplTest.java new file mode 100644 index 0000000..68d6667 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/nodalemailconfig/NodalConfigServiceImplTest.java @@ -0,0 +1,190 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.nodalemailconfig; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.emailconfig.AuthorityEmail; +import com.iemr.admin.mapper.emailconfig.InstituteEmailConfigMapper; +import com.iemr.admin.model.emailconfig.NodalEmailRequest; +import com.iemr.admin.model.emailconfig.NodalEmailResponse; +import com.iemr.admin.model.emailconfig.CreateNodalEmailRequestModel; +import com.iemr.admin.model.emailconfig.UpdateNodalEmailRequest; +import com.iemr.admin.repository.emailconfig.InstituteEmailRepo; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The nodal config service keeps the nodal officer mailboxes a complaint is + * escalated to, narrowed by whichever parts of the location the caller names. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NodalConfigServiceImpl Test Suite") +class NodalConfigServiceImplTest { + + @Mock + private EntityManager entityManager; + + @Mock + private InstituteEmailRepo instituteRepo; + + @Mock + private InstituteEmailConfigMapper instituteEmailConfigMapper; + + @InjectMocks + private NodalConfigServiceImpl service; + + private CriteriaQuery query; + private TypedQuery typedQuery; + + @SuppressWarnings("unchecked") + @BeforeEach + @DisplayName("Stand in for the criteria query the service builds by hand") + void setUp() { + CriteriaBuilder builder = mock(CriteriaBuilder.class); + query = mock(CriteriaQuery.class); + Root root = mock(Root.class); + typedQuery = mock(TypedQuery.class); + + when(entityManager.getCriteriaBuilder()).thenReturn(builder); + when(builder.createQuery(AuthorityEmail.class)).thenReturn(query); + when(query.from(AuthorityEmail.class)).thenReturn(root); + when(query.select(any())).thenReturn(query); + when(query.where(any(Predicate[].class))).thenReturn(query); + when(query.orderBy(any(jakarta.persistence.criteria.Order[].class))).thenReturn(query); + when(root.get(anyString())).thenReturn(mock(Path.class)); + when(builder.equal(any(), any())).thenReturn(mock(Predicate.class)); + when(entityManager.createQuery(query)).thenReturn(typedQuery); + } + + private static NodalEmailRequest fullyNarrowedRequest() { + NodalEmailRequest request = new NodalEmailRequest(); + request.setAuthorityEmailID(1); + request.setDeleted(false); + request.setDistrictID(301); + request.setDistrictBranchMappingID(30111); + request.setBlockID(3011); + request.setProviderServiceMapID(4001); + request.setStateID(29); + request.setMobileNo("9000000001"); + return request; + } + + @Test + @DisplayName("getAllEmailConfigs should answer the mailboxes the query found, as the screens read them") + void getAll_shouldAnswerFoundMailboxes() { + List found = List.of(new AuthorityEmail()); + List published = List.of(new NodalEmailResponse()); + when(typedQuery.getResultList()).thenReturn(found); + when(instituteEmailConfigMapper.resultToInstTypeEmailResponses(found)).thenReturn(published); + + assertSame(published, service.getAllNodalEmailConfigs(fullyNarrowedRequest())); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow the query by every detail the caller named") + void getAll_shouldNarrowByEveryNamedDetail() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllNodalEmailConfigs(fullyNarrowedRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(8, captor.getValue().length, "one narrowing per detail the caller named"); + } + + @Test + @DisplayName("getAllEmailConfigs should narrow by nothing when the caller names nothing") + void getAll_shouldNarrowByNothingForEmptyRequest() { + when(typedQuery.getResultList()).thenReturn(new ArrayList<>()); + + service.getAllNodalEmailConfigs(new NodalEmailRequest()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Predicate[].class); + verify(query).where(captor.capture()); + assertEquals(0, captor.getValue().length); + } + + @Test + @DisplayName("saveEmailConfigs should store one mailbox per request and answer each as stored") + void save_shouldStoreEachRequestedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + when(instituteEmailConfigMapper.createRequestToInstituteEmailConfig(anyList())) + .thenReturn(List.of(stored, stored)); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultInstType(stored)) + .thenReturn(new NodalEmailResponse()); + + assertEquals(2, service.saveNodalEmailConfigs(List.of(new CreateNodalEmailRequestModel())).size()); + } + + @Test + @DisplayName("saveEmailConfigs should store nothing when the caller asks for nothing") + void save_shouldStoreNothingForEmptyRequest() { + when(instituteEmailConfigMapper.createRequestToInstituteEmailConfig(anyList())) + .thenReturn(new ArrayList<>()); + + assertTrue(service.saveNodalEmailConfigs(new ArrayList<>()).isEmpty()); + } + + @Test + @DisplayName("updateEmailConfigs should answer the mailbox as it stands after the change") + void update_shouldAnswerChangedMailbox() { + AuthorityEmail stored = new AuthorityEmail(); + NodalEmailResponse published = new NodalEmailResponse(); + when(instituteEmailConfigMapper.updateRequestToInstituteNodalEmailConf(any(UpdateNodalEmailRequest.class))) + .thenReturn(stored); + when(instituteRepo.save(stored)).thenReturn(stored); + when(instituteEmailConfigMapper.resultToInstTypeNodalEmailResponse(stored)).thenReturn(published); + + assertSame(published, service.updateNodalEmailConfigs(new UpdateNodalEmailRequest())); + } +} diff --git a/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceServiceImplTest.java b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceServiceImplTest.java new file mode 100644 index 0000000..8065453 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceServiceImplTest.java @@ -0,0 +1,184 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.parkingPlace; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.parkingPlace.M_Parkingplace; +import com.iemr.admin.data.provideronboard.M_ProviderServiceMapping; +import com.iemr.admin.data.zonemaster.M_Zone; +import com.iemr.admin.repository.parkingPlace.ParkingPlaceRepository; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The parking place service turns the flat rows the reporting queries answer + * back into parking places, and treats an omitted location filter as "any". + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ParkingPlaceServiceImpl Test Suite") +class ParkingPlaceServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PARKING_PLACE_ID = 31; + + @Mock + private ParkingPlaceRepository parkingPlaceRepository; + + @InjectMocks + private ParkingPlaceServiceImpl service; + + private static Object[] reportingRow() { + return new Object[] { PARKING_PLACE_ID, "Hosur parking", "Near the bus stand", "Hosur Road", PSM_ID, + Boolean.FALSE, 1, "India", 29, "Karnataka", 301, "Bengaluru Urban", 3011, "Anekal", 30111, + "Attibele", new M_ProviderServiceMapping(), 5, "104 Helpline" }; + } + + @Test + @DisplayName("getAvailableParkingPlaces should rebuild one parking place per row the query answers") + void getAvailable_shouldRebuildEachRow() { + when(parkingPlaceRepository.getAvailableParkingPlaces("29", "301", PSM_ID)) + .thenReturn(List.of(reportingRow())); + + ArrayList places = service.getAvailableParkingPlaces(29, 301, PSM_ID); + + assertEquals(1, places.size()); + assertEquals("Hosur parking", places.get(0).getParkingPlaceName()); + assertEquals("Karnataka", places.get(0).getStateName()); + assertEquals("Hosur Road", places.get(0).getAreaHQAddress()); + } + + @Test + @DisplayName("getAvailableParkingPlaces should match any state or district the caller leaves out") + void getAvailable_shouldWildcardOmittedFilters() { + when(parkingPlaceRepository.getAvailableParkingPlaces("%%", "%%", PSM_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.getAvailableParkingPlaces(null, null, PSM_ID).isEmpty()); + verify(parkingPlaceRepository).getAvailableParkingPlaces("%%", "%%", PSM_ID); + } + + @Test + @DisplayName("saveParkingPlace should answer the parking places the repository stored") + void save_shouldAnswerStoredPlaces() { + ArrayList stored = new ArrayList<>(List.of(new M_Parkingplace())); + when(parkingPlaceRepository.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.saveParkingPlace(new ArrayList<>())); + } + + @Test + @DisplayName("updateParkingPlaceStatus should report how many rows the retirement touched") + void updateStatus_shouldReportRowsTouched() { + M_Parkingplace request = new M_Parkingplace(); + request.setParkingPlaceID(PARKING_PLACE_ID); + request.setDeleted(Boolean.TRUE); + request.setModifiedBy("admin"); + when(parkingPlaceRepository.updateParkingPlaceStatus(PARKING_PLACE_ID, Boolean.TRUE, "admin")).thenReturn(1); + + assertEquals(1, service.updateParkingPlaceStatus(request)); + } + + @Test + @DisplayName("updateParkingPlaceStatus should report nothing touched when the parking place is unknown") + void updateStatus_shouldReportNothingTouchedForUnknownPlace() { + M_Parkingplace request = new M_Parkingplace(); + request.setParkingPlaceID(-1); + when(parkingPlaceRepository.updateParkingPlaceStatus(-1, null, null)).thenReturn(0); + + assertEquals(0, service.updateParkingPlaceStatus(request)); + } + + @Test + @DisplayName("the single record lookups should each reach their own repository query") + void singleRecordLookups_shouldReachTheirOwnQuery() { + M_Parkingplace stored = new M_Parkingplace(); + List byProvider = List.of(stored); + when(parkingPlaceRepository.getParkingPlaceById(PARKING_PLACE_ID)).thenReturn(stored); + when(parkingPlaceRepository.save(stored)).thenReturn(stored); + when(parkingPlaceRepository.findByProviderServiceMapID(PSM_ID)).thenReturn(byProvider); + + assertSame(stored, service.getParkingPlaceByID(PARKING_PLACE_ID)); + assertSame(stored, service.updateParkingPlaceData(stored)); + assertSame(byProvider, service.getParkingPlaces(PSM_ID)); + } + + @Test + @DisplayName("getSubDistrict should answer the taluks the parking place covers") + void getSubDistrict_shouldAnswerCoveredTaluks() { + when(parkingPlaceRepository.getSubDistrict(PARKING_PLACE_ID)) + .thenReturn(List.of(new Object[] { PARKING_PLACE_ID, 3011, "Anekal" })); + + List taluks = service.getSubDistrict(PARKING_PLACE_ID); + + assertEquals(1, taluks.size()); + assertEquals("Anekal", taluks.get(0).getBlockName()); + assertEquals(3011, taluks.get(0).getDistrictBlockID()); + } + + @Test + @DisplayName("getSubDistrict should answer nothing when the parking place covers no taluk") + void getSubDistrict_shouldAnswerNothingWhenNoneCovered() { + when(parkingPlaceRepository.getSubDistrict(PARKING_PLACE_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.getSubDistrict(PARKING_PLACE_ID).isEmpty()); + } + + @Test + @DisplayName("getAvailableParkingPlacesbyZoneID should name the zone on each parking place it answers") + void getAvailableByZone_shouldNameTheZone() { + M_Parkingplace place = new M_Parkingplace(); + place.setParkingPlaceID(PARKING_PLACE_ID); + M_Zone zone = new M_Zone(); + zone.setZoneName("South zone"); + when(parkingPlaceRepository.getAvailableParkingPlacesbyzoneid(9, PSM_ID)) + .thenReturn(List.of(new Object[] { place, zone })); + + ArrayList places = service.getAvailableParkingPlacesbyZoneID(9, PSM_ID); + + assertEquals(1, places.size()); + assertEquals("South zone", places.get(0).getZoneName()); + } + + @Test + @DisplayName("getAvailableParkingPlacesbyZoneID should answer nothing when the zone holds no parking place") + void getAvailableByZone_shouldAnswerNothingForEmptyZone() { + when(parkingPlaceRepository.getAvailableParkingPlacesbyzoneid(9, PSM_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.getAvailableParkingPlacesbyZoneID(9, PSM_ID).isEmpty()); + } +} diff --git a/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceTalukMappingServiceImplTest.java b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceTalukMappingServiceImplTest.java new file mode 100644 index 0000000..24df775 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/parkingPlace/ParkingPlaceTalukMappingServiceImplTest.java @@ -0,0 +1,151 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.parkingPlace; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.locationmaster.DistrictBlock; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMapping; +import com.iemr.admin.data.parkingPlace.ParkingplaceTalukMappingTO; +import com.iemr.admin.mapper.parkingplacetalukmapping.ParkingPlaceTalukMappingMapper; +import com.iemr.admin.repo.locationmaster.DistrictBlockRepo; +import com.iemr.admin.repository.parkingPlace.ParkingPlaceTalukMappingRepository; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The taluk mapping service records which taluks a parking place covers, and + * offers the remaining taluks of a district as the candidates for a new one. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ParkingPlaceTalukMappingServiceImpl Test Suite") +class ParkingPlaceTalukMappingServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer PARKING_PLACE_ID = 31; + private static final Integer DISTRICT_ID = 301; + + @Mock + private ParkingPlaceTalukMappingRepository parkingPlaceTalukMappingRepository; + + @Mock + private DistrictBlockRepo districtBlockRepo; + + @Mock + private ParkingPlaceTalukMappingMapper parkingPlaceTalukMappingMapper; + + @InjectMocks + private ParkingPlaceTalukMappingServiceImpl service; + + private static ParkingplaceTalukMapping mapping() { + ParkingplaceTalukMapping mapping = new ParkingplaceTalukMapping(); + mapping.setPpSubDistrictMapID(7001); + mapping.setParkingPlaceID(PARKING_PLACE_ID); + mapping.setDistrictID(DISTRICT_ID); + mapping.setDistrictBlockID(3011); + mapping.setProviderServiceMapID(PSM_ID); + return mapping; + } + + @Test + @DisplayName("saveParkingPlaceTalukMapping should answer the mappings the repository stored") + void save_shouldAnswerStoredMappings() { + ArrayList stored = new ArrayList<>(List.of(mapping())); + when(parkingPlaceTalukMappingRepository.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.saveParkingPlaceTalukMapping(new ArrayList<>())); + } + + @Test + @DisplayName("updateParkingPlaceTalukMapping and findbyID should each reach their own repository query") + void singleRecordOperations_shouldReachTheirOwnQuery() { + ParkingplaceTalukMapping stored = mapping(); + when(parkingPlaceTalukMappingRepository.save(stored)).thenReturn(stored); + when(parkingPlaceTalukMappingRepository.findByPpSubDistrictMapID(7001)).thenReturn(stored); + + assertSame(stored, service.updateParkingPlaceTalukMapping(stored)); + assertSame(stored, service.findbyID(7001)); + } + + @Test + @DisplayName("findbyProviderservicemapid should answer the taluks the parking place covers") + void findbyProviderservicemapid_shouldAnswerCoveredTaluks() { + List rows = List.of(mapping()); + List published = List.of(new ParkingplaceTalukMappingTO()); + when(parkingPlaceTalukMappingRepository.findByParkingPlaceID(PARKING_PLACE_ID)).thenReturn(rows); + when(parkingPlaceTalukMappingMapper.getParkingplaceTalukMappingMapList(rows)).thenReturn(published); + + assertSame(published, service.findbyProviderservicemapid(mapping())); + } + + @Test + @DisplayName("findbyParkingplaceAndDistrictID should narrow the mappings to the district the caller names") + void findbyParkingplaceAndDistrictID_shouldNarrowToDistrict() { + List rows = List.of(mapping()); + List published = List.of(new ParkingplaceTalukMappingTO()); + when(parkingPlaceTalukMappingRepository + .findByParkingPlaceIDAndDistrictIDOrderByM_DistrictDistrictNameAsc(PARKING_PLACE_ID, DISTRICT_ID)) + .thenReturn(rows); + when(parkingPlaceTalukMappingMapper.getParkingplaceTalukMappingMapList(rows)).thenReturn(published); + + assertSame(published, service.findbyParkingplaceAndDistrictID(mapping())); + } + + @Test + @DisplayName("getunmappedtaluk should exclude the taluks already covered when there are any") + void getunmappedtaluk_shouldExcludeCoveredTaluks() { + List covered = List.of(3011); + List remaining = List.of(new DistrictBlock(3012, "Hoskote")); + when(parkingPlaceTalukMappingRepository.finbyDistrictID(DISTRICT_ID, PSM_ID)).thenReturn(covered); + when(districtBlockRepo.findunmapped(covered, DISTRICT_ID)).thenReturn(remaining); + + assertSame(remaining, service.getunmappedtaluk(DISTRICT_ID, PSM_ID)); + verify(districtBlockRepo, never()).findall(anyInt()); + } + + @Test + @DisplayName("getunmappedtaluk should offer every taluk of the district when none is covered yet") + void getunmappedtaluk_shouldOfferEveryTalukWhenNoneCovered() { + List all = List.of(new DistrictBlock(3011, "Anekal"), new DistrictBlock(3012, "Hoskote")); + when(parkingPlaceTalukMappingRepository.finbyDistrictID(DISTRICT_ID, PSM_ID)).thenReturn(new ArrayList<>()); + when(districtBlockRepo.findall(DISTRICT_ID)).thenReturn(all); + + assertSame(all, service.getunmappedtaluk(DISTRICT_ID, PSM_ID)); + verify(districtBlockRepo, never()).findunmapped(anyList(), anyInt()); + } +} diff --git a/src/test/java/com/iemr/admin/service/pharmacologicalcategory/PharmacologicalcategoryServiceImplTest.java b/src/test/java/com/iemr/admin/service/pharmacologicalcategory/PharmacologicalcategoryServiceImplTest.java new file mode 100644 index 0000000..9286283 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/pharmacologicalcategory/PharmacologicalcategoryServiceImplTest.java @@ -0,0 +1,113 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.pharmacologicalcategory; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.admin.data.pharmacologicalcategory.M_Pharmacologicalcategory; +import com.iemr.admin.repo.pharmacologicalcategory.PharmacologicalcategoryRepo; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +/** The pharmacological category service is thin over its repository, but it answers nothing rather than an empty batch. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PharmacologicalcategoryServiceImpl Test Suite") +class PharmacologicalcategoryServiceImplTest { + + private static final Integer PSM_ID = 4001; + private static final Integer RECORD_ID = 11; + + @Mock + private PharmacologicalcategoryRepo pharmacologicalcategoryRepo; + + @InjectMocks + private PharmacologicalcategoryServiceImpl service; + + @Test + @DisplayName("createPharmacologicalcategory should answer the records it stored") + void create_shouldAnswerStoredRecords() { + ArrayList stored = new ArrayList<>(List.of(new M_Pharmacologicalcategory())); + when(pharmacologicalcategoryRepo.saveAll(anyList())).thenReturn(stored); + + assertSame(stored, service.createPharmacologicalcategory(new ArrayList<>())); + } + + @Test + @DisplayName("createPharmacologicalcategory should answer nothing when the store took nothing") + void create_shouldAnswerNothingWhenNothingStored() { + when(pharmacologicalcategoryRepo.saveAll(anyList())).thenReturn(new ArrayList()); + + assertNull(service.createPharmacologicalcategory(new ArrayList<>())); + } + + @Test + @DisplayName("the lookups should each reach their own repository query") + void lookups_shouldReachTheirOwnQuery() { + M_Pharmacologicalcategory record = new M_Pharmacologicalcategory(); + ArrayList records = new ArrayList<>(List.of(record)); + when(pharmacologicalcategoryRepo.getPhormacologicalData(PSM_ID)).thenReturn(records); + when(pharmacologicalcategoryRepo.editPhamacologicalData(RECORD_ID)).thenReturn(record); + when(pharmacologicalcategoryRepo.save(record)).thenReturn(record); + + assertSame(records, service.getPharmacologicalcategory(PSM_ID)); + assertSame(record, service.editPharmacologicalcategory(RECORD_ID)); + assertSame(record, service.saveEditedPharData(record)); + } + + @Test + @DisplayName("checkPharmacologicalcategoryCode should report a code the provider already uses") + void check_shouldReportUsedCode() { + M_Pharmacologicalcategory request = new M_Pharmacologicalcategory(); + request.setPharmCategoryCode("C-1"); + request.setProviderServiceMapID(PSM_ID); + when(pharmacologicalcategoryRepo.findByPharmCategoryCodeAndProviderServiceMapID("C-1", PSM_ID)).thenReturn(List.of(new M_Pharmacologicalcategory())); + + assertTrue(service.checkPharmacologicalcategoryCode(request)); + } + + @Test + @DisplayName("checkPharmacologicalcategoryCode should clear a code nobody uses yet") + void check_shouldClearFreeCode() { + M_Pharmacologicalcategory request = new M_Pharmacologicalcategory(); + request.setPharmCategoryCode("C-2"); + request.setProviderServiceMapID(PSM_ID); + when(pharmacologicalcategoryRepo.findByPharmCategoryCodeAndProviderServiceMapID("C-2", PSM_ID)).thenReturn(new ArrayList()); + + assertFalse(service.checkPharmacologicalcategoryCode(request)); + } +} diff --git a/src/test/java/com/iemr/admin/service/provideronboard/ProviderOnBoardServicesTest.java b/src/test/java/com/iemr/admin/service/provideronboard/ProviderOnBoardServicesTest.java new file mode 100644 index 0000000..516cd48 --- /dev/null +++ b/src/test/java/com/iemr/admin/service/provideronboard/ProviderOnBoardServicesTest.java @@ -0,0 +1,801 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.admin.service.provideronboard; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.admin.data.provideronboard.M_104druggroup; +import com.iemr.admin.data.provideronboard.M_104drugmapping; +import com.iemr.admin.data.provideronboard.M_104drugmaster; +import com.iemr.admin.data.provideronboard.M_Calltype; +import com.iemr.admin.data.provideronboard.M_Category; +import com.iemr.admin.data.provideronboard.M_Feedbacknature; +import com.iemr.admin.data.provideronboard.M_Feedbacktype; +import com.iemr.admin.data.provideronboard.M_Institutedirectory; +import com.iemr.admin.data.provideronboard.M_Institutedirectorymapping; +import com.iemr.admin.data.provideronboard.M_Institutesubdirectory; +import com.iemr.admin.data.provideronboard.M_Institution; +import com.iemr.admin.data.provideronboard.M_Institutiontype; +import com.iemr.admin.data.provideronboard.M_ProviderServiceMapping; +import com.iemr.admin.data.provideronboard.M_ServiceMaster; +import com.iemr.admin.data.provideronboard.M_Severity; +import com.iemr.admin.data.provideronboard.M_Subcategory; +import com.iemr.admin.data.provideronboard.M_Subservice; +import com.iemr.admin.data.provideronboard.M_SubservicemasterPA; +import com.iemr.admin.data.provideronboard.M_UserservicerolemappingForRole; +import com.iemr.admin.data.provideronboard.ServiceProvider_Model; +import com.iemr.admin.data.provideronboard.V_Showprovideradmin; +import com.iemr.admin.data.provideronboard.V_Showsubcategory; +import com.iemr.admin.exceptionhandler.DataNotFound; +import com.iemr.admin.repository.provideronboard.CalltypeRepo; +import com.iemr.admin.repository.provideronboard.CategoryRepo; +import com.iemr.admin.repository.provideronboard.DrugGroupRepo; +import com.iemr.admin.repository.provideronboard.DrugMappingRepo; +import com.iemr.admin.repository.provideronboard.DrugMasterRepo; +import com.iemr.admin.repository.provideronboard.IemrServiceRepository1; +import com.iemr.admin.repository.provideronboard.InstuteDirectoryRepo; +import com.iemr.admin.repository.provideronboard.M_FeedbacknatureRepo; +import com.iemr.admin.repository.provideronboard.M_FeedbacktypeRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutedirectorymappingRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutesubdirectoryRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutionRepo; +import com.iemr.admin.repository.provideronboard.M_InstitutiontypeRepo; +import com.iemr.admin.repository.provideronboard.M_ProviderServiceMappingRepo; +import com.iemr.admin.repository.provideronboard.M_ServiceMasterRepo; +import com.iemr.admin.repository.provideronboard.M_SeverityRepo; +import com.iemr.admin.repository.provideronboard.M_SubservicemasterPArepo; +import com.iemr.admin.repository.provideronboard.M_UserservicerolemappingForRoleRepo; +import com.iemr.admin.repository.provideronboard.SubCategoryRepo; +import com.iemr.admin.repository.provideronboard.SubserviceMasterRepo; +import com.iemr.admin.repository.provideronboard.V_ShowprovideradminRepo; +import com.iemr.admin.repository.provideronboard.V_ShowsubcategoryRepo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The onboarding services are thin over their repositories, but a few of them + * choose which query to run from what the caller left blank - and those choices + * decide what an operator sees on screen. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Provider onboarding service Test Suite") +class ProviderOnBoardServicesTest { + + private static final Integer PSM_ID = 4001; + + @Mock + private CalltypeRepo calltypeRepo; + + @InjectMocks + private CalltypeServiceImpl calltypeService; + + @Mock + private V_ShowsubcategoryRepo v_ShowsubcategoryRepo; + + @Mock + private CategoryRepo categoryRepo; + + @Mock + private SubCategoryRepo subCategoryRepo; + + @InjectMocks + private CategoryMasterImpl categoryService; + + @Mock + private DrugGroupRepo drugGroupRepo; + + @Mock + private DrugMasterRepo drugMasterRepo; + + @Mock + private DrugMappingRepo drugMappingRepo; + + @InjectMocks + private DrugMasterImpl drugService; + + @Mock + private InstuteDirectoryRepo instuteDirectoryRepo; + + @InjectMocks + private InstuteDirectoryServiceImpl directoryService; + + @Mock + private M_FeedbacknatureRepo m_FeedbacknatureRepo; + + @InjectMocks + private M_FeedbacknatureImpl feedbackNatureService; + + @Mock + private M_FeedbacktypeRepo m_FeedbacktypeRepo; + + @InjectMocks + private M_FeedbacktypeImpl feedbackTypeService; + + @Mock + private M_InstitutedirectorymappingRepo m_InstitutedirectorymappingRepo; + + @InjectMocks + private M_InstitutedirectorymappingImpl directoryMappingService; + + @Mock + private M_InstitutesubdirectoryRepo m_InstitutesubdirectoryRepo; + + @InjectMocks + private M_InstitutesubdirectoryImpl subDirectoryService; + + @Mock + private M_InstitutionRepo m_InstitutionRepo; + + @InjectMocks + private M_InstitutionImpl institutionService; + + @Mock + private M_InstitutiontypeRepo m_InstitutiontypeRepo; + + @InjectMocks + private M_InstitutiontypeImpl instituteTypeService; + + @Mock + private M_ServiceMasterRepo mservicemasteRepo; + + @InjectMocks + private M_ServiceMasterImpl serviceMasterService; + + @Mock + private M_SeverityRepo m_ServerityRepo; + + @InjectMocks + private M_SeverityImpl severityService; + + @Mock + private M_SubservicemasterPArepo m_SubservicemasterPArepo; + + @Mock + private SubserviceMasterRepo subserviceMasterRepo; + + @InjectMocks + private SubserviceImpl subServiceService; + + @Mock + private V_ShowprovideradminRepo v_ShowprovideradminRepo; + + @Mock + private M_UserservicerolemappingForRoleRepo m_UserservicerolemappingForRoleRepo; + + @Mock + private IemrServiceRepository1 iemrServiceRepository1; + + @Mock + private M_ProviderServiceMappingRepo m_ProviderServiceMappingRepo; + + @InjectMocks + private ServiceProvider_ServiceImpl providerService; + + @Nested + @DisplayName("CalltypeServiceImpl") + class CallTypeServiceTests { + + @Test + @DisplayName("should hand each call to its own repository query") + void callType_shouldReachTheirOwnQuery() { + M_Calltype callType = new M_Calltype(); + ArrayList stored = new ArrayList<>(List.of(callType)); + when(calltypeRepo.saveAll(anyList())).thenReturn(stored); + when(calltypeRepo.updateCallType(51)).thenReturn(callType); + when(calltypeRepo.save(callType)).thenReturn(callType); + when(calltypeRepo.getCalltypeData(PSM_ID)).thenReturn(stored); + + assertSame(stored, calltypeService.saveCallList(new ArrayList<>())); + assertSame(stored, calltypeService.createCalltype(new ArrayList<>())); + assertSame(callType, calltypeService.updateCallType(51)); + assertSame(callType, calltypeService.saveupdatedData(callType)); + assertSame(stored, calltypeService.getCalltypeData(PSM_ID)); + } + } + + @Nested + @DisplayName("CategoryMasterImpl") + class CategoryServiceTests { + + @Test + @DisplayName("getCategoryId should store the category and answer the id it was given") + void getCategoryId_shouldStoreAndAnswerId() { + M_Category category = new M_Category(); + M_Category stored = new M_Category(); + stored.setCategoryID(81); + when(categoryRepo.save(category)).thenReturn(stored); + + assertEquals(81, categoryService.getCategoryId(category)); + } + + @Test + @DisplayName("the sub-category calls should each reach their own repository query") + void subCategoryCalls_shouldReachTheirOwnQuery() { + M_Subcategory subCategory = new M_Subcategory(); + ArrayList stored = new ArrayList<>(List.of(subCategory)); + ArrayList views = new ArrayList<>(List.of(new V_Showsubcategory())); + when(subCategoryRepo.saveAll(anyList())).thenReturn(stored); + when(subCategoryRepo.getCategory()).thenReturn(stored); + when(subCategoryRepo.getCategory(81)).thenReturn(stored); + when(subCategoryRepo.getSubCategory(91)).thenReturn(subCategory); + when(subCategoryRepo.save(subCategory)).thenReturn(subCategory); + when(v_ShowsubcategoryRepo.getSubCategory1(91)).thenReturn(views); + when(v_ShowsubcategoryRepo.getCategoryByMapIDAndSubServiceID(PSM_ID, 61)).thenReturn(views); + + assertSame(stored, categoryService.saveSubCatData(new ArrayList<>())); + assertSame(stored, categoryService.createSubCategory(new ArrayList<>())); + assertSame(stored, categoryService.getCategory()); + assertSame(stored, categoryService.getCategory(81)); + assertSame(subCategory, categoryService.getSubCategory(91)); + assertSame(subCategory, categoryService.updateSubCatData(subCategory)); + assertSame(views, categoryService.getSubCategory1(91)); + assertSame(views, categoryService.getCategoryByMapIDAndSubServiceID(PSM_ID, 61)); + } + + @Test + @DisplayName("the category calls should each reach their own repository query") + void categoryCalls_shouldReachTheirOwnQuery() { + M_Category category = new M_Category(); + ArrayList stored = new ArrayList<>(List.of(category)); + when(categoryRepo.saveAll(anyList())).thenReturn(stored); + when(categoryRepo.getAllCategory(61, PSM_ID)).thenReturn(stored); + when(categoryRepo.getAllCategory1(PSM_ID)).thenReturn(stored); + when(categoryRepo.getCatData(81)).thenReturn(category); + when(categoryRepo.save(category)).thenReturn(category); + when(categoryRepo.updateCategory(81, 21)).thenReturn(1); + when(categoryRepo.findByProviderServiceMapIDAndFeedbackNatureIDOrderByCategoryNameAsc(PSM_ID, null)) + .thenReturn(stored); + + assertSame(stored, categoryService.createcat(new ArrayList<>())); + assertSame(stored, categoryService.getAllCategory(61, PSM_ID)); + assertSame(stored, categoryService.getAllCategory1(PSM_ID)); + assertSame(category, categoryService.getcatdatabycatId(81)); + assertSame(category, categoryService.deletedata(category)); + assertEquals(1, categoryService.updateCategory(81, 21)); + assertSame(stored, categoryService.getUpmappedCategory(PSM_ID)); + } + + @Test + @DisplayName("getAllCategory should rebuild one category per row the query answers") + void getAllCategory_shouldRebuildEachRow() { + when(categoryRepo.getAllCategory(PSM_ID)) + .thenReturn(List.of(new Object[] { 81, "Medical", 61, "Counselling", PSM_ID })); + + ArrayList categories = categoryService.getAllCategory(PSM_ID); + + assertEquals(1, categories.size()); + assertEquals("Medical", categories.get(0).getCategoryName()); + } + + @Test + @DisplayName("getAllCategorywithFeedbackNatureID should rebuild one category per row the query answers") + void getAllCategoryWithFeedbackNature_shouldRebuildEachRow() { + when(categoryRepo.getAllCategorywithfeedbackNatureID(PSM_ID, 21)) + .thenReturn(List.of(new Object[] { 81, "Medical", 61, "Counselling", PSM_ID })); + + assertEquals(1, categoryService.getAllCategorywithFeedbackNatureID(PSM_ID, 21).size()); + } + } + + @Nested + @DisplayName("DrugMasterImpl") + class DrugServiceTests { + + @Test + @DisplayName("getAllDrugData should read the live catalogue when the caller excludes retired drugs") + void getAllDrugData_shouldReadLiveCatalogue() { + when(drugMasterRepo.getValidDrugData("77")) + .thenReturn(List.of(new Object[] { 101, "Paracetamol", "Antipyretic", "OTC", + Boolean.FALSE, (short) 77 })); + + ArrayList drugs = drugService.getAllDrugData(101, (short) 77, Boolean.FALSE); + + assertEquals(1, drugs.size()); + assertEquals("Paracetamol", drugs.get(0).getDrugName()); + verify(drugMasterRepo, org.mockito.Mockito.never()).getAllDrugData(anyString(), anyString()); + } + + @Test + @DisplayName("getAllDrugData should read the whole catalogue when retired drugs are wanted too") + void getAllDrugData_shouldReadWholeCatalogue() { + when(drugMasterRepo.getAllDrugData("101", "77")).thenReturn(new ArrayList<>()); + + drugService.getAllDrugData(101, (short) 77, Boolean.TRUE); + + verify(drugMasterRepo).getAllDrugData("101", "77"); + } + + @Test + @DisplayName("getAllDrugData should ask for the whole catalogue when the caller names nothing") + void getAllDrugData_shouldAskForWholeCatalogueWithoutFilters() { + when(drugMasterRepo.getAllDrugData("", "")).thenReturn(new ArrayList<>()); + + drugService.getAllDrugData(null, null, null); + + verify(drugMasterRepo).getAllDrugData("", ""); + } + + @Test + @DisplayName("getAllDrugGroups should read the live groups when the caller excludes retired ones") + void getAllDrugGroups_shouldReadLiveGroups() { + when(drugGroupRepo.getValidDrugGroups("77")) + .thenReturn(List.of(new Object[] { 201, "Analgesics", "Pain relief", Boolean.FALSE, + (short) 77 })); + + assertEquals(1, drugService.getAllDrugGroups(201, (short) 77, Boolean.FALSE).size()); + } + + @Test + @DisplayName("getAllDrugGroups should read every group when retired ones are wanted too") + void getAllDrugGroups_shouldReadEveryGroup() { + when(drugGroupRepo.getAllDrugGroups("201", "77")).thenReturn(new ArrayList<>()); + + drugService.getAllDrugGroups(201, (short) 77, Boolean.TRUE); + + verify(drugGroupRepo).getAllDrugGroups("201", "77"); + } + + @Test + @DisplayName("getAllDrugGroupMappings should rebuild one mapping per row the query answers") + void getAllDrugGroupMappings_shouldRebuildEachRow() { + when(drugMappingRepo.getAllDrugGroupMappings("", 77, 3)) + .thenReturn(List.of(new Object[] { 301, 101, "Paracetamol", 201, "Analgesics", "OTC", + Boolean.FALSE, 77, PSM_ID, "N", Boolean.FALSE })); + + assertEquals(1, drugService.getAllDrugGroupMappings(null, 77, 3).size()); + } + + @Test + @DisplayName("the drug writes should each reach their own repository") + void drugWrites_shouldReachTheirOwnRepository() { + M_104druggroup group = new M_104druggroup(); + group.setDrugGroupID(201); + M_104drugmaster drug = new M_104drugmaster(); + M_104drugmapping mapping = new M_104drugmapping(); + ArrayList groups = new ArrayList<>(List.of(group)); + ArrayList