package com.malk.service.workhours; import com.alibaba.fastjson.JSON; import com.malk.server.aliwork.YDConf; import com.malk.server.aliwork.YDParam; import com.malk.server.dingtalk.DDR_New; import com.malk.server.workhours.TimeCardDuplicateCleanupRequest; import com.malk.server.workhours.TimeCardDuplicateCleanupResult; import com.malk.server.workhours.TimeCardSummaryUpsertRequest; import com.malk.server.workhours.WHConf; import com.malk.service.aliwork.YDClient; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.test.util.ReflectionTestUtils; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; 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.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class TimeCardSummaryServiceTest { @Test void shouldUpsertBySubmitterIdAndDayText() { YDClient ydClient = mock(YDClient.class); WHConf whConf = new WHConf(); whConf.setYidaAppType("APP"); whConf.setYidaSystemToken("TOKEN"); whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY"); TimeCardSummaryService service = new TimeCardSummaryService(); ReflectionTestUtils.setField(service, "ydClient", ydClient); ReflectionTestUtils.setField(service, "whConf", whConf); TimeCardSummaryUpsertRequest request = new TimeCardSummaryUpsertRequest(); request.setSubmitterUserId("user-1"); request.setDayText("20260716"); request.setFormData(Collections.singletonMap("numberField_hours", 8)); assertTrue(service.upsert(request)); ArgumentCaptor captor = ArgumentCaptor.forClass(YDParam.class); verify(ydClient).operateData(captor.capture(), eq(YDConf.FORM_OPERATION.upsert)); YDParam param = captor.getValue(); Map condition = JSON.parseObject(param.getSearchCondition(), Map.class); Map formData = JSON.parseObject(param.getFormDataJson(), Map.class); assertEquals("user-1", condition.get("textField_mmbffvd9")); assertEquals("20260716", condition.get("textField_mmbffvdb")); assertEquals("user-1", formData.get("textField_mmbffvd9")); assertEquals("20260716", formData.get("textField_mmbffvdb")); assertEquals(8, ((Number) formData.get("numberField_hours")).intValue()); } @Test void shouldValidateDuplicateCleanupWithoutDeletingInDryRun() { YDClient ydClient = mock(YDClient.class); WHConf whConf = new WHConf(); whConf.setYidaAppType("APP"); whConf.setYidaSystemToken("TOKEN"); whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY"); TimeCardSummaryService service = new TimeCardSummaryService(); ReflectionTestUtils.setField(service, "ydClient", ydClient); ReflectionTestUtils.setField(service, "whConf", whConf); DDR_New queryResult = mock(DDR_New.class); when(queryResult.getData()).thenReturn(Arrays.asList( summaryItem("DELETE-ID", "user-1", "20260507"), summaryItem("KEEP-ID", "user-1", "20260507"))); when(ydClient.queryData( any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form))) .thenReturn(queryResult); TimeCardDuplicateCleanupResult result = service.cleanupDuplicates(cleanupRequest(true)); assertEquals(1, result.getValidated()); assertEquals(0, result.getDeleted()); verify(ydClient, never()).operateData( any(YDParam.class), eq(YDConf.FORM_OPERATION.delete_batch)); } @Test void shouldRejectCleanupWhenCurrentDuplicateSetChanged() { TimeCardDuplicateCleanupRequest.Target target = cleanupRequest(true).getTargets().get(0); assertThrows( RuntimeException.class, () -> TimeCardSummaryService.validateCleanupTarget( target, Collections.singletonList("KEEP-ID"))); } @Test void shouldDeleteValidatedTargetAndVerifyKeeper() { YDClient ydClient = mock(YDClient.class); WHConf whConf = new WHConf(); whConf.setYidaAppType("APP"); whConf.setYidaSystemToken("TOKEN"); whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY"); TimeCardSummaryService service = new TimeCardSummaryService(); ReflectionTestUtils.setField(service, "ydClient", ydClient); ReflectionTestUtils.setField(service, "whConf", whConf); DDR_New duplicateResult = mock(DDR_New.class); when(duplicateResult.getData()).thenReturn(Arrays.asList( summaryItem("DELETE-ID", "user-1", "20260507"), summaryItem("KEEP-ID", "user-1", "20260507"))); DDR_New keeperResult = mock(DDR_New.class); when(keeperResult.getData()).thenReturn(Collections.singletonList( summaryItem("KEEP-ID", "user-1", "20260507"))); when(ydClient.queryData( any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form))) .thenReturn(duplicateResult, keeperResult); TimeCardDuplicateCleanupResult result = service.cleanupDuplicates(cleanupRequest(false)); ArgumentCaptor captor = ArgumentCaptor.forClass(YDParam.class); verify(ydClient).operateData( captor.capture(), eq(YDConf.FORM_OPERATION.delete_batch)); assertEquals(Collections.singletonList("DELETE-ID"), captor.getValue().getFormInstanceIdList()); assertEquals(1, result.getDeleted()); assertEquals(1, result.getVerified()); } private static TimeCardDuplicateCleanupRequest cleanupRequest(boolean dryRun) { TimeCardDuplicateCleanupRequest.Target target = new TimeCardDuplicateCleanupRequest.Target(); target.setUserId("user-1"); target.setDayText("20260507"); target.setDeleteInstanceId("DELETE-ID"); target.setKeepInstanceId("KEEP-ID"); TimeCardDuplicateCleanupRequest request = new TimeCardDuplicateCleanupRequest(); request.setDryRun(dryRun); request.setTargets(Collections.singletonList(target)); return request; } private static Map summaryItem(String instanceId, String userId, String dayText) { Map formData = new LinkedHashMap<>(); formData.put("textField_mmbffvd9", userId); formData.put("textField_mmbffvdb", dayText); Map item = new LinkedHashMap<>(); item.put("formInstanceId", instanceId); item.put("formData", formData); return item; } }