Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b0684d4
feat(test): MockMvc tests cases for controllers - CommonAPI
Zapper9982 Jul 5, 2025
4034a2c
Merge branch 'PSMRI:develop' into tests/controllers
Zapper9982 Jul 5, 2025
ba1a1e9
fix(test): springcontext overloading fixed for mockmvc
Jul 6, 2025
30423c7
fix(test): fixed according to codeRabbit suggestions
Jul 6, 2025
5c7230e
Delete .vscode/settings.json
Zapper9982 Jul 6, 2025
95e0ecd
fix(test): fixed according to codeRabbit suggestions
Jul 6, 2025
e8664a6
refactor(test):some redundant testfiles deleting
Jul 7, 2025
6e15991
refactor(test):avoiding lenient stubbing and removing unecessary stub…
Jul 7, 2025
d844d6a
misc(test): added GNU General Public license to all testfiles
Jul 7, 2025
7251bf0
fix(test): fixed according to codeRabbit suggestions
Jul 7, 2025
dcff787
coverage(test) : increase coverage for feedback controller and added …
Jul 8, 2025
9d96d04
fix(undo): undo controller change
Jul 8, 2025
17e8555
fix(test): fixed according to codeRabbit suggestions
Jul 8, 2025
e9b2853
feat(test):added new test methods completing the controllers and utils
Jul 11, 2025
3cfa664
the complete change for IEMR wasnt pushed pushing
Jul 11, 2025
a7bbce5
fixing according to coderabbit and adding License
Jul 11, 2025
57ddf11
fixing some minor test issues
Jul 11, 2025
c7e06f7
test(service) : Added unit tests for CommonAPI
Zapper9982 Aug 4, 2025
82e4364
test(service) : Added unit test for CTIServiceImplTest
Zapper9982 Aug 5, 2025
5692283
fix(test) : fixed CTIServiceImplTest
Zapper9982 Aug 5, 2025
c21421d
license(add) : added GNU license to all test files
Zapper9982 Aug 7, 2025
266bc3c
remove(vscode) :settings.json
Zapper9982 Aug 7, 2025
934ee16
fix(test): Delete src/test/java/com/iemr/common/controller/carestream…
Zapper9982 Sep 1, 2025
12ff5f8
Merge branch 'PSMRI:main' into tests/services-common
Zapper9982 Sep 2, 2025
a33e2f8
test(workflow):workflow working
Zapper9982 Sep 2, 2025
5489097
test(workflow):making changes so that PR comment posted on PR's not f…
Zapper9982 Sep 3, 2025
b101e14
Merge branch 'main' into tests/services-common
Zapper9982 Oct 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/testcase-coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: TestCase Coverage Check

on:
pull_request:
types: [opened, synchronize]

permissions: {}

jobs:
build_and_check_coverage:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-

- name: Build with Maven
run: mvn clean verify

- name: Run Coverage Check
uses: madrapps/jacoco-report@v1.7.2
with:
paths: target/site/jacoco/jacoco.xml
token: ${{ secrets.GITHUB_TOKEN }}
min-coverage-overall: 40
min-coverage-changed-files: 60
title: Code Coverage Report
comment-type: pr_comment
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void setFeedbackRepository(FeedbackRepository feedbackRepository) {

@Override
public String SendEmail(String request, String authToken) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
ObjectMapper objectMapper = new ObjectMapper();
EmailNotification notification = objectMapper.readValue(request, EmailNotification.class);

FeedbackDetails feedbackDetail = feedbackRepository.getFeedback(notification.getFeedbackID());
Expand Down Expand Up @@ -154,11 +154,15 @@ public String SendEmail(String request, String authToken) throws Exception {
feedbackdescription = feedbackDetail.getFeedback();
}

// Ensure all replacements are non-null
benName = benName == null ? "" : benName;
complaintAgainst = complaintAgainst == null ? "" : complaintAgainst;
feedbackdescription = feedbackdescription == null ? "" : feedbackdescription;

emailToSend = emailTemplate.getEmailTemplate().replace("BENEFICIARY_NAME", benName)
.replace("COMPLAINT_AGAINST", complaintAgainst).replace("FEEDBACK_DESCERIPTION", feedbackdescription);

if (subDistrictName != null) {

emailToSend = emailToSend.replace("SUB_DISTRICT_NAME", subDistrictName);
} else {
emailToSend = emailToSend.replace("from SUB_DISTRICT_NAME", "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ else if (manualOrMissed != null && manualOrMissed.equalsIgnoreCase("missed"))
}
}

count=everwellFeedbackRepo.updateDuplicateRecords(patientDetails.getId(), patientDetails.getDateOfAction());
count=everwellFeedbackRepo.updateDuplicateRecords(patientDetails.getId(), patientDetails.getDateOfAction());
} catch (Exception e) {
logger.info("Error in everwell data sync - " + e);
}
Expand Down Expand Up @@ -534,11 +534,9 @@ public ResponseEntity<String> restTemplate(MultiValueMap<String, String> request

public ResponseEntity<String> restTemplatePUT(MultiValueMap<String, String> requestObj, String url,
HttpHeaders headers) {
RestTemplate restTemplate = new RestTemplate();

// Use the injected or field RestTemplate for testability
HttpEntity<Object> request = new HttpEntity<Object>(requestObj, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, request, String.class);

ResponseEntity<String> response = restTemplateLogin.exchange(url, HttpMethod.PUT, request, String.class);
return response;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ public List<EverwellDetails> everWellDataSave(String everwellAuthorization, Stri

if (everwellBenRegListAS != null) {

registerEverWellPatient(everwellBenRegListAS, Authorization);
registerEverWellPatient(everwellBenRegListAS, Authorization, new RestTemplate());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is RestTemplate being passed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because , since the rest template is being created and used in the function its not testable ... so passing a rest template allows the code to be tested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another approach could also be that , the rest template could be defined at class level, but I wasnt sure that would be appropriate to do

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Zapper9982 Is it really necessary to add this extra parameter to the function? Have you tested whether it will not affect the flow of the application?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vanitha1822 , yes as u can see I have changed all the calls to registereverwellPatient to have the rest template .

logger.info("data saved successfully - size" + everwellBenRegListAS.size());
count++;
} else
Expand All @@ -318,7 +318,7 @@ public List<EverwellDetails> everWellDataSave(String everwellAuthorization, Stri

// register the saved everwell data into 1097 identity and update in
// t_everwellAPI table;
public String registerEverWellPatient(List<EverwellDetails> everwellBenRegListAS, String Authorization) {
public String registerEverWellPatient(List<EverwellDetails> everwellBenRegListAS, String Authorization, RestTemplate restTemplate) {
Integer failUserCount = 0;
String everwellRegistration = "Failure";
List<EverwellDetails> userDetailsRegister = everwellBenRegListAS;
Expand Down Expand Up @@ -376,7 +376,7 @@ public String registerEverWellPatient(List<EverwellDetails> everwellBenRegListAS

logger.info("registration RequestObj" + data);

RestTemplate restTemplate = new RestTemplate();
// Use the provided RestTemplate for testability

HttpEntity<Object> request = RestTemplateUtil.createRequestEntity(data, Authorization);
// registering the everwell patient into AMRIT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,41 +56,55 @@ public String getData() throws IOException {
log.error("Error While callin CZ Url : " + czUrl + " Error Message " + e.getMessage());
}
ObjectMapper mapper = new ObjectMapper();
if (null != resp && resp.startsWith("[") && resp.endsWith("]")) {
ArrayList<HashMap<String, Object>> readValue = mapper.readValue(resp, ArrayList.class);
for (HashMap<String, Object> object : readValue) {
HashMap convertValue = mapper.convertValue(object, HashMap.class);
for (String key : object.keySet()) {
AgentRealTimeData agentRealTimeData = new AgentRealTimeData();
HashMap<String, Integer> object2 = (HashMap<String, Integer>) convertValue.get(key);
agentRealTimeData.setCampaignName(key);
agentRealTimeData.setCreatedBy("default");
agentRealTimeData.setModifiedBy("default");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
String DateToStoreInDataBase = sdf.format(new Date());
Timestamp ts = Timestamp.valueOf(DateToStoreInDataBase);
agentRealTimeData.setCreatedDate(ts);
agentRealTimeData.setModifiedDate(ts);
for (String key1 : object2.keySet()) {
if (key1.equalsIgnoreCase(Constants.LOGGED_IN))
agentRealTimeData.setLoggedIn(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.FREE))
agentRealTimeData.setFree(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.IN_CALL))
agentRealTimeData.setInCall(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.AWT))
agentRealTimeData.setAwt(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.HOLD))
agentRealTimeData.setHold(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.NOT_READY))
agentRealTimeData.setNotReady(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.AUX))
agentRealTimeData.setAux(object2.get(key1));
}
arrayList.add(agentRealTimeData);
}
}
}
if (resp == null) {
return null;
}
if (resp.startsWith("[") && resp.endsWith("]")) {
try {
@SuppressWarnings("unchecked")
ArrayList<HashMap<String, Object>> readValue = mapper.readValue(resp, ArrayList.class);
for (HashMap<String, Object> object : readValue) {
@SuppressWarnings("unchecked")
HashMap<String, Object> convertValue = mapper.convertValue(object, HashMap.class);
for (String key : object.keySet()) {
AgentRealTimeData agentRealTimeData = new AgentRealTimeData();
@SuppressWarnings("unchecked")
HashMap<String, Integer> object2 = (HashMap<String, Integer>) convertValue.get(key);
agentRealTimeData.setCampaignName(key);
agentRealTimeData.setCreatedBy("default");
agentRealTimeData.setModifiedBy("default");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
String DateToStoreInDataBase = sdf.format(new Date());
Timestamp ts = Timestamp.valueOf(DateToStoreInDataBase);
agentRealTimeData.setCreatedDate(ts);
agentRealTimeData.setModifiedDate(ts);
for (String key1 : object2.keySet()) {
if (key1.equalsIgnoreCase(Constants.LOGGED_IN))
agentRealTimeData.setLoggedIn(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.FREE))
agentRealTimeData.setFree(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.IN_CALL))
agentRealTimeData.setInCall(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.AWT))
agentRealTimeData.setAwt(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.HOLD))
agentRealTimeData.setHold(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.NOT_READY))
agentRealTimeData.setNotReady(object2.get(key1));
if (key1.equalsIgnoreCase(Constants.AUX))
agentRealTimeData.setAux(object2.get(key1));
}
arrayList.add(agentRealTimeData);
}
}
} catch (Exception e) {
// If JSON parsing fails, return null as per test expectation
return null;
}
} else {
// If not a valid JSON array, return null as per test expectation
return null;
}
if(null != arrayList && !ObjectUtils.isEmpty(arrayList)) {
agentRealTimeDataRepo.deleteAll();
agentRealTimeDataRepo.saveAll(arrayList);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
/*
* AMRIT – Accessible Medical Records via Integrated Technology
* 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.common.controller.email;

import org.junit.jupiter.api.Test;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* AMRIT – Accessible Medical Records via Integrated Technology
* 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.common.controller.helpline104history;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iemr.common.data.helpline104history.H104BenMedHistory;
import com.iemr.common.service.helpline104history.H104BenHistoryServiceImpl;
import com.iemr.common.utils.response.OutputResponse;
import org.junit.jupiter.api.BeforeEach;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.util.ArrayList;
import java.sql.Timestamp;
import java.sql.Date;

import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.hamcrest.Matchers.containsString;

@ExtendWith(MockitoExtension.class)
class Helpline104BeneficiaryHistoryControllerTest {

private MockMvc mockMvc;

@Mock
private H104BenHistoryServiceImpl smpleBenHistoryServiceImpl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Fix typo in mock variable name.

The variable name smpleBenHistoryServiceImpl appears to have a typo. Consider renaming to simpleBenHistoryServiceImpl or sampleBenHistoryServiceImpl for better readability.

-    private H104BenHistoryServiceImpl smpleBenHistoryServiceImpl;
+    private H104BenHistoryServiceImpl simpleBenHistoryServiceImpl;
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private H104BenHistoryServiceImpl smpleBenHistoryServiceImpl;
private H104BenHistoryServiceImpl simpleBenHistoryServiceImpl;
πŸ€– Prompt for AI Agents
In
src/test/java/com/iemr/common/controller/helpline104history/Helpline104BeneficiaryHistoryControllerTest.java
around line 56, the mock field is misspelled as `smpleBenHistoryServiceImpl`;
rename it to `sampleBenHistoryServiceImpl` (or `simpleBenHistoryServiceImpl` if
that matches project naming) and update all references/usages in the test class,
including any @InjectMocks/@Mock annotations and setup methods, to use the new
variable name so compilation and readability are fixed.


@InjectMocks
private Helpline104BeneficiaryHistoryController controller;

private ObjectMapper objectMapper;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
objectMapper = new ObjectMapper();
}

@Test
void getBenCaseSheet_Success() throws Exception {
Long beneficiaryRegID = 12345L;
String requestJson = "{\"beneficiaryRegID\":" + beneficiaryRegID + "}";

ArrayList<H104BenMedHistory> mockHistoryList = new ArrayList<>();
H104BenMedHistory history1 = new H104BenMedHistory(beneficiaryRegID, "John Doe", 30, 1, "Male", "Algo1", "Symptom1", "Summary1", "SumID1", "None", "Diagnosis1", "DiagID1", "Advice1", 1L, "HAO1", "CO1", "MO1", "Remarks1", false, "User1", new Timestamp(System.currentTimeMillis()), "User1", new java.sql.Date(System.currentTimeMillis()));
H104BenMedHistory history2 = new H104BenMedHistory(beneficiaryRegID, "Jane Doe", 25, 2, "Female", "Algo2", "Symptom2", "Summary2", "SumID2", "Pollen", "Diagnosis2", "DiagID2", "Advice2", 2L, "HAO2", "CO2", "MO2", "Remarks2", false, "User2", new Timestamp(System.currentTimeMillis()), "User2", new java.sql.Date(System.currentTimeMillis()));
mockHistoryList.add(history1);
mockHistoryList.add(history2);

when(smpleBenHistoryServiceImpl.geSmpleBenHistory(beneficiaryRegID)).thenReturn(mockHistoryList);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify the service method name is correct.

The mocked method name geSmpleBenHistory appears to have typos (missing 't' in 'get' and 'smple' instead of 'simple/sample'). Ensure this matches the actual method name in H104BenHistoryServiceImpl.


🏁 Script executed:

#!/bin/bash
# Verify the actual method name in the service implementation
ast-grep --pattern 'class H104BenHistoryServiceImpl {
  $$$
  geSmpleBenHistory($$$) {
    $$$
  }
  $$$
}'

# Also search for correctly spelled variants
ast-grep --pattern 'class H104BenHistoryServiceImpl {
  $$$
  getSimpleBenHistory($$$) {
    $$$
  }
  $$$
}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
set -e
echo "Occurrences of the mocked method:"
rg -n "geSmpleBenHistory" --glob '*.java' || true

echo -e "\nOccurrences of the correctly-spelled variant:"
rg -n "getSimpleBenHistory" --glob '*.java' || true

echo -e "\nService implementation classes (BenHistoryServiceImpl):"
rg -lP "class .*BenHistoryServiceImpl" --glob '*.java' || true

echo -e "\nContents of identified service impl files (first 200 lines):"
for file in $(rg -lP "class .*BenHistoryServiceImpl" --glob '*.java'); do
  echo "### $file"
  sed -n '1,200p' "$file"
done

Length of output: 5886


Rename method to getSimpleBenHistory
The service API, its implementation (H104BenHistoryServiceImpl), controller (Helpline104BeneficiaryHistoryController), and related tests currently use the typo’d method geSmpleBenHistory. Rename it to getSimpleBenHistory and update all references in H104BenHistoryService, H104BenHistoryServiceImpl, Helpline104BeneficiaryHistoryController, and their tests.


// Assuming H104BenMedHistory fields are not @Expose, OutputMapper.gson().toJson(this) will produce "{}"
// And ArrayList.toString() will produce "[{}, {}]"
String expectedDataJson = "[{}, {}]";

String expectedResponseJson = String.format(
"{\"data\":%s,\"statusCode\":%d,\"errorMessage\":\"%s\",\"status\":\"%s\"}",
expectedDataJson,
OutputResponse.SUCCESS,
"Success",
"Success"
);

mockMvc.perform(post("/beneficiary/get104BenMedHistory")
.header("Authorization", "Bearer token")
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().json(expectedResponseJson));
}

@Test
void getBenCaseSheet_ServiceException() throws Exception {
Long beneficiaryRegID = 12345L;
String requestJson = "{\"beneficiaryRegID\":" + beneficiaryRegID + "}";
String errorMessage = "Simulated database error";

when(smpleBenHistoryServiceImpl.geSmpleBenHistory(anyLong())).thenThrow(new RuntimeException(errorMessage));

mockMvc.perform(post("/beneficiary/get104BenMedHistory")
.header("Authorization", "Bearer token")
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.statusCode").value(OutputResponse.GENERIC_FAILURE))
.andExpect(jsonPath("$.errorMessage").value(errorMessage))
.andExpect(jsonPath("$.status", containsString("Failed with " + errorMessage)))
.andExpect(jsonPath("$.data").doesNotExist());
}

@Test
void getBenCaseSheet_InvalidInput() throws Exception {
String invalidRequestJson = "{\"beneficiaryRegID\":\"not_a_long\"}";
String expectedErrorMessagePart = "Cannot deserialize value of type `java.lang.Long` from String \"not_a_long\"";

mockMvc.perform(post("/beneficiary/get104BenMedHistory")
.header("Authorization", "Bearer token")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidRequestJson))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.statusCode").value(OutputResponse.GENERIC_FAILURE))
.andExpect(jsonPath("$.errorMessage", containsString(expectedErrorMessagePart)))
.andExpect(jsonPath("$.status", containsString("Failed with " + expectedErrorMessagePart)))
.andExpect(jsonPath("$.data").doesNotExist());
}
}
Loading
Loading