Skip to content

Conversation

@Zapper9982
Copy link
Contributor

@Zapper9982 Zapper9982 commented Aug 7, 2025

📋 Description

JIRA ID:

  • This PR adds and refines comprehensive unit tests to achieve 100% test coverage for key service classes in the MMU-API project.
  • All business logic, branches, and edge cases are now thoroughly tested, ensuring robust coverage and reliability.
  • No errors or regressions have been found in the API service as a result of these changes.

@drtechie @vanitha1822


✅ Type of Change

  • 🐞 Bug fix
  • New feature
  • 🔥 Breaking change
  • 🛠 Refactor
  • ⚙️ Config change
  • 📚 Documentation
  • 🧪 Tests (adding new or updating existing tests)
  • 🎨 UI/UX
  • 🚀 Performance
  • 🧹 Chore

Screenshots

image image

N/A – This PR only affects backend unit tests and does not introduce UI changes.


Additional Notes:

  • All tests pass successfully, and coverage reports confirm 100% coverage for the targeted service classes.
  • These improvements will help ensure future changes are safe

Summary by CodeRabbit

  • Tests
    • Enhanced test coverage for data synchronization functionality with comprehensive unit tests validating various data sync scenarios, edge cases, and error handling paths to ensure reliability of synchronization processes.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Aug 7, 2025

Walkthrough

Adds a comprehensive unit test suite for GetDataFromVanAndSyncToDBImpl that verifies data synchronization flows, query generation, routing logic, and edge cases through mocking of repository and digester dependencies across multiple scenarios and boundary conditions.

Changes

Cohort / File(s) Summary
Unit test suite for data sync service
src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java
Adds comprehensive test coverage for data synchronization including: beneficiary ID mapping updates with provisioned and empty sync data; syncDataToServer routing for multiple table types (m_beneficiaryregidmapping, t_indent, etc.); query generation for insert/update operations across various serverColumn configurations; edge cases such as column mismatches, null/special table names, boolean field handling, and facilityID null scenarios; and query structure validation via reflection for UPDATE operations.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

  • Verify mocking setup correctness and assertion logic for each test method
  • Ensure edge case coverage aligns with production code requirements
  • Validate reflection-based query structure test for accuracy

Poem

🐰 A test suite hops in with mighty care,
Covering syncs and queries everywhere,
Edge cases caught, mocks prepared just right,
Data flows tested from dawn to night! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references added unit tests for MMU-API, which aligns with the changeset that adds a comprehensive test suite for GetDataFromVanAndSyncToDBImpl. However, the title is overly broad and generic, using vague phrasing that doesn't specify what service or functionality is being tested.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Zapper9982
Copy link
Contributor Author

@vanitha1822 the merge conflicts occur because :

  • there was a security bug of not checking if dataToBesync is null .
  • changed private to protected for a JDBC template to make it accessible for test
  • updated long to Long since Long is an object and can be initialized as null , unlike long being a primitive datatype. (since duration initialized as null return can be expected to be null too )

Can you please review these changes . these are some few refactor I have done as that can be seen in the PR .

@Zapper9982 Zapper9982 marked this pull request as draft August 9, 2025 06:58
@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 9, 2025

@Zapper9982 Zapper9982 marked this pull request as ready for review August 11, 2025 19:30
@drtechie drtechie changed the base branch from release-3.4.0 to main October 9, 2025 07:57
@drtechie
Copy link
Member

drtechie commented Oct 9, 2025

@Zapper9982 can you please fix these conflicts?

@drtechie
Copy link
Member

drtechie commented Oct 9, 2025

cc: @vanitha1822

@Zapper9982
Copy link
Contributor Author

@vanitha1822
there seem to be new changes to the services , so its conflicting with some minor changes that i had fixed in service before , could u guide me in the latest code changes , so i can safely merge conflicts accordingly

@vanitha1822
Copy link
Contributor

@vanitha1822 there seem to be new changes to the services , so its conflicting with some minor changes that i had fixed in service before , could u guide me in the latest code changes , so i can safely merge conflicts accordingly

@Zapper9982 yes, we updated some changes in these 2 files. So please take the recent pull and accept the incoming change please.

@drtechie
Copy link
Member

drtechie commented Dec 1, 2025

@Zapper9982 can you resolve the conflicts?

@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 1, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java (5)

47-68: Strengthen test assertions with verification of mock interactions.

Most tests only assert that the result equals "data sync passed", but they don't verify that the mocked repository methods were called with the expected parameters. This reduces the tests' ability to catch regressions.

Consider adding verify() calls to ensure:

  • dataSyncRepositoryCentral.checkRecordIsAlreadyPresentOrNot() is called with correct parameters
  • dataSyncRepositoryCentral.syncDataToCentralDB() is called with expected query and data
  • The correct number of interactions occurred

For exception tests (lines 284-296), verify the specific exception type and message rather than just checking the exception is not null.

Example enhancement for a test:

     String result = spyService.syncDataToServer(json, "auth");
     assertEquals("data sync passed", result);
+    verify(dataSyncRepositoryCentral).checkRecordIsAlreadyPresentOrNot(
+        eq("schema"), eq("t_indent"), eq("FromFacilityID"), 
+        anyString(), eq("v1"), eq(1));
+    verify(dataSyncRepositoryCentral).syncDataToCentralDB(
+        anyString(), eq("schema"), any(), anyString(), anyList());

For exception tests:

-    Exception exception = assertThrows(Exception.class, () -> spyService.syncDataToServer(null, "auth"));
-    assertNotNull(exception);
+    Exception exception = assertThrows(NullPointerException.class, 
+        () -> spyService.syncDataToServer(null, "auth"));
+    assertTrue(exception.getMessage().contains("expected error context"));

Also applies to: 70-76, 78-90, 92-115, 117-141, 186-209, 212-236, 238-254, 256-270, 272-281, 284-288, 290-296, 299-322, 325-348, 351-375, 378-407, 410-441, 444-466, 469-496, 499-521, 524-545, 547-569


143-158: Use consistent service instance pattern.

These tests create new GetDataFromVanAndSyncToDBImpl() instances instead of using the @InjectMocks service field used in other tests. This inconsistency makes the test class harder to maintain.

Since these tests are testing methods that don't require mocked dependencies, the new instance creation is acceptable, but consider using the injected instance for consistency or clearly documenting why certain tests need fresh instances.

Also applies to: 160-183, 571-590


47-545: Consider extracting magic strings to constants.

The tests use many repeated string literals like "data sync passed", "schema", "table", "user", "auth", etc. Extracting these to private static final constants would improve maintainability and make it clearer which values are significant.

Example:

private static final String EXPECTED_SYNC_SUCCESS = "data sync passed";
private static final String TEST_SCHEMA = "schema";
private static final String TEST_TABLE = "table";
private static final String TEST_USER = "user";
private static final String TEST_AUTH = "auth";

592-594: Remove incomplete comment or implement the suggested utility.

The comment at the end of the file mentions a "JSON parsing stub" that "may need to add... for full testability," but no implementation is provided. Either:

  1. Remove the comment if the utility is not needed
  2. Implement the utility if it's required for test functionality
  3. Clarify what JSON parsing functionality is missing

The tests currently pass JSON strings to syncDataToServer() without showing how the actual JSON parsing is handled or mocked.


92-545: Consider parameterized tests to reduce duplication.

Many tests follow an identical structure but with different input data and expected behaviors. JUnit 5's @ParameterizedTest could reduce this duplication and make the test suite more maintainable.

For example, tests for different table names (lines 299-322) could be consolidated into a single parameterized test.

Example:

@ParameterizedTest
@ValueSource(strings = {"t_indent", "t_indentorder", "t_indentissue", "t_stocktransfer", "t_itemstockentry"})
void testSyncDataToServer_specialTableNames(String tableName) throws Exception {
    // Common setup with tableName parameter
    // Assertions
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94e4ddb and eca2316.

📒 Files selected for processing (1)
  • src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java (1)
src/main/java/com/iemr/mmu/data/syncActivity_syncLayer/SyncUploadDataDigester.java (1)
  • SyncUploadDataDigester (27-121)
🔇 Additional comments (3)
src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java (3)

36-45: LGTM!

The test class setup correctly uses Mockito annotations and properly initializes mocks in the @BeforeEach method.


547-569: Verify that null is the expected behavior for length mismatch.

This test expects a null return value when the number of sync records doesn't match the result array length. Please verify:

  1. Is returning null the intended behavior for this error condition?
  2. Should the method throw an exception or log an error instead?
  3. How should calling code handle a null result?

Silently returning null on errors can lead to null pointer exceptions in calling code. Consider whether an exception or explicit error result would be more appropriate.


47-590: Comprehensive test coverage.

The test suite provides excellent coverage of edge cases including:

  • Empty and null sync data
  • Insert and update flows
  • Length mismatches between expected and actual results
  • Special table name handling
  • Boolean value handling
  • Null facilityID scenarios
  • Invalid JSON inputs

This thorough testing approach will help catch regressions and ensure the synchronization logic works correctly across various scenarios.

Comment on lines +92 to +115
@Test
void testSyncDataToServer_otherTable_insertAndUpdate() throws Exception {
SyncUploadDataDigester digester = mock(SyncUploadDataDigester.class);
when(digester.getTableName()).thenReturn("t_indent");
when(digester.getSyncData()).thenReturn(Arrays.asList(
new HashMap<String, Object>() {{
put("FromFacilityID", 1.0);
put("SyncFacilityID", 1);
put("VanID", "v1");
put("date_format(SyncedDate,'%Y-%m-%d %H:%i:%s')", "");
}}
));
when(digester.getVanAutoIncColumnName()).thenReturn("FromFacilityID");
when(digester.getFacilityID()).thenReturn(1);
when(digester.getSchemaName()).thenReturn("schema");
when(digester.getServerColumns()).thenReturn("col1,col2");
when(dataSyncRepositoryCentral.checkRecordIsAlreadyPresentOrNot(anyString(), anyString(), anyString(), anyString(), anyString(), anyInt())).thenReturn(0);
when(dataSyncRepositoryCentral.syncDataToCentralDB(anyString(), anyString(), any(), anyString(), anyList())).thenReturn(new int[]{1});
GetDataFromVanAndSyncToDBImpl spyService = spy(service);
doReturn("t_indent").when(digester).getTableName();
String json = "{\"tableName\":\"t_indent\"}";
String result = spyService.syncDataToServer(json, "auth");
assertEquals("data sync passed", result);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove redundant mock configuration.

Throughout these tests, the digester mock is configured with when().thenReturn() statements, and then the same values are set again using doReturn().when() (e.g., lines 111, 137, 205, 232, 317, 344, 371, 403, 437, 462, 492, 517, 541). This is redundant because the mock is already properly configured with the when() statements.

The doReturn() calls should only be used on the spyService, not on the already-mocked digester.

Example fix for lines 110-111:

     when(dataSyncRepositoryCentral.syncDataToCentralDB(anyString(), anyString(), any(), anyString(), anyList())).thenReturn(new int[]{1});
     GetDataFromVanAndSyncToDBImpl spyService = spy(service);
-    doReturn("t_indent").when(digester).getTableName();
     String json = "{\"tableName\":\"t_indent\"}";

Apply similar changes to all affected tests.

Also applies to: 117-141, 186-209, 212-236, 299-322, 325-348, 351-375, 378-407, 410-441, 444-466, 469-496, 499-521, 524-545

🤖 Prompt for AI Agents
In
src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java
around lines 92 to 115, the test configures the mocked digester with
when(...).thenReturn(...) and then redundantly calls
doReturn(...).when(digester) later; remove the redundant doReturn() calls that
target the digester (keep the when().thenReturn() stubs) and only use doReturn()
on the spyService where needed; update all similar occurrences listed (117-141,
186-209, 212-236, 299-322, 325-348, 351-375, 378-407, 410-441, 444-466, 469-496,
499-521, 524-545) by deleting the doReturn(...).when(digester) lines and leaving
the original when(...).thenReturn(...) mocks intact so the tests rely on the
mocked digester and the spyService stubbing remains via doReturn().

Comment on lines +571 to +590
@Test
void testGetqueryFor_M_BeneficiaryRegIdMapping() {
GetDataFromVanAndSyncToDBImpl service = new GetDataFromVanAndSyncToDBImpl();
// Using reflection to test the private method
try {
java.lang.reflect.Method method = GetDataFromVanAndSyncToDBImpl.class.getDeclaredMethod("getqueryFor_M_BeneficiaryRegIdMapping", String.class, String.class);
method.setAccessible(true);
String query = (String) method.invoke(service, "testSchema", "testTable");
assertTrue(query.contains("UPDATE testSchema.testTable"));
String normalizedQuery = query.replaceAll("\\s+", " ").trim();
if (!normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")) {
System.out.println("Actual query: " + normalizedQuery);
}
assertTrue(normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")); // actual query uses lowercase 'syncedBy'
assertTrue(normalizedQuery.contains("AND BeneficiaryID = ?"));
assertTrue(normalizedQuery.contains("AND VanID = ?"));
} catch (Exception e) {
fail("Failed to test getqueryFor_M_BeneficiaryRegIdMapping: " + e.getMessage());
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid testing private methods via reflection.

Testing private methods directly using reflection is a code smell. Private methods should be tested indirectly through the public API. If this method requires direct testing, consider whether it should be package-private or if the class design needs refactoring.

Additionally, remove the debug System.out.println statement at line 582.

Apply this diff to remove the debug statement:

         String query = (String) method.invoke(service, "testSchema", "testTable");
         assertTrue(query.contains("UPDATE  testSchema.testTable"));
         String normalizedQuery = query.replaceAll("\\s+", " ").trim();
-        if (!normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")) {
-            System.out.println("Actual query: " + normalizedQuery);
-        }
         assertTrue(normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")); // actual query uses lowercase 'syncedBy'
         assertTrue(normalizedQuery.contains("AND BeneficiaryID = ?"));
         assertTrue(normalizedQuery.contains("AND VanID = ?"));
📝 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
@Test
void testGetqueryFor_M_BeneficiaryRegIdMapping() {
GetDataFromVanAndSyncToDBImpl service = new GetDataFromVanAndSyncToDBImpl();
// Using reflection to test the private method
try {
java.lang.reflect.Method method = GetDataFromVanAndSyncToDBImpl.class.getDeclaredMethod("getqueryFor_M_BeneficiaryRegIdMapping", String.class, String.class);
method.setAccessible(true);
String query = (String) method.invoke(service, "testSchema", "testTable");
assertTrue(query.contains("UPDATE testSchema.testTable"));
String normalizedQuery = query.replaceAll("\\s+", " ").trim();
if (!normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")) {
System.out.println("Actual query: " + normalizedQuery);
}
assertTrue(normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")); // actual query uses lowercase 'syncedBy'
assertTrue(normalizedQuery.contains("AND BeneficiaryID = ?"));
assertTrue(normalizedQuery.contains("AND VanID = ?"));
} catch (Exception e) {
fail("Failed to test getqueryFor_M_BeneficiaryRegIdMapping: " + e.getMessage());
}
}
@Test
void testGetqueryFor_M_BeneficiaryRegIdMapping() {
GetDataFromVanAndSyncToDBImpl service = new GetDataFromVanAndSyncToDBImpl();
// Using reflection to test the private method
try {
java.lang.reflect.Method method = GetDataFromVanAndSyncToDBImpl.class.getDeclaredMethod("getqueryFor_M_BeneficiaryRegIdMapping", String.class, String.class);
method.setAccessible(true);
String query = (String) method.invoke(service, "testSchema", "testTable");
assertTrue(query.contains("UPDATE testSchema.testTable"));
String normalizedQuery = query.replaceAll("\\s+", " ").trim();
assertTrue(normalizedQuery.contains("SET Provisioned = true, SyncedDate = now(), syncedBy = ?")); // actual query uses lowercase 'syncedBy'
assertTrue(normalizedQuery.contains("AND BeneficiaryID = ?"));
assertTrue(normalizedQuery.contains("AND VanID = ?"));
} catch (Exception e) {
fail("Failed to test getqueryFor_M_BeneficiaryRegIdMapping: " + e.getMessage());
}
}
🤖 Prompt for AI Agents
In
src/test/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java
around lines 571 to 590, the test uses reflection to call a private method
(getqueryFor_M_BeneficiaryRegIdMapping) and contains a debug System.out.println;
remove the println at line 582 and stop testing the private method via
reflection — instead test the behavior through the class's public API (call the
public method(s) that exercise getqueryFor_M_BeneficiaryRegIdMapping and assert
on their outcomes) or, if direct testing is essential, change the method
visibility to package-private and add a package-scoped test, then update the
test to call the method directly without reflection.

@Zapper9982 Zapper9982 closed this Dec 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants