| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- package com.malk.service.workhours;
- import org.junit.jupiter.api.Test;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.LinkedHashMap;
- import java.util.List;
- import java.util.Map;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertSame;
- import static org.junit.jupiter.api.Assertions.assertThrows;
- import static org.junit.jupiter.api.Assertions.assertTrue;
- class TimeCardChangeApprovalWriteBackServiceTest {
- @Test
- void shouldSaveApprovalResultInSharedChangeSummary() {
- Map<String, Object> approved = billable("P-001", 8d, 6d, 8d, -2d);
- Map<String, Object> rejected = billable("P-002", 4d, 10d, 4d, 6d);
- Map<String, Object> detail = summary(approved, rejected);
- List<TimeCardChangeApprovalWriteBackService.ChangeRow> rows = Arrays.asList(
- row("P-001", 6d, -2d, true),
- row("P-002", 10d, 6d, false));
- Map<String, Object> update =
- TimeCardChangeApprovalWriteBackService.buildChangeSummaryUpdate(detail, rows);
- List<Map> actual = (List<Map>) update.get("tableField_mmczo634");
- assertEquals(6d, actual.get(0).get("numberField_mmczo636"));
- assertEquals(6d, actual.get(0).get("numberField_mmczo637"));
- assertEquals(-2d, actual.get(0).get("numberField_mmd80zae"));
- assertEquals(4d, actual.get(1).get("numberField_mmczo636"));
- assertEquals(4d, actual.get(1).get("numberField_mmczo637"));
- assertEquals(0d, actual.get(1).get("numberField_mmd80zae"));
- assertEquals(10d, update.get("numberField_mmeakgi4"));
- }
- @Test
- void shouldOnlyAccumulateChangeHoursInOriginalSummaryRows() {
- Map<String, Object> original = billable("P-001", 8d, 8d, 8d, 1d);
- List<TimeCardChangeApprovalWriteBackService.ChangeRow> rows = Collections.singletonList(
- row("P-001", 6d, -2d, true));
- List<Map> merged = TimeCardChangeApprovalWriteBackService.mergeDeltaRows(
- Collections.singletonList(original), rows,
- TimeCardChangeApprovalWriteBackService.BILLABLE);
- assertEquals(8d, merged.get(0).get("numberField_mmczo636"));
- assertEquals(8d, merged.get(0).get("numberField_mmczo637"));
- assertEquals(-1d, merged.get(0).get("numberField_mmd80zae"));
- }
- @Test
- void shouldGroupNonBillableDeltasByProjectWithoutChangingApprovedHours() {
- Map<String, Object> original = nonBillable("P-100", 4d, 4d, 0.5d);
- List<TimeCardChangeApprovalWriteBackService.ChangeRow> rows = Arrays.asList(
- row(TimeCardChangeApprovalWriteBackService.NON_BILLABLE,
- "P-100", 3d, -1d, true),
- row(TimeCardChangeApprovalWriteBackService.NON_BILLABLE,
- "P-100", 7d, 3d, true),
- row(TimeCardChangeApprovalWriteBackService.NON_BILLABLE,
- "P-100", 8d, 4d, false));
- List<Map> merged = TimeCardChangeApprovalWriteBackService.mergeDeltaRows(
- Collections.singletonList(original), rows,
- TimeCardChangeApprovalWriteBackService.NON_BILLABLE);
- assertEquals(1, merged.size());
- assertEquals("P-100", merged.get(0).get("textField_mmczo639"));
- assertEquals(4d, merged.get(0).get("numberField_mmczo63c"));
- assertEquals(4d, merged.get(0).get("numberField_mmczo63d"));
- assertEquals(2.5d, merged.get(0).get("numberField_mmeakgi9"));
- }
- @Test
- void shouldGroupOtherDeltasByActivityWithoutChangingApprovedHours() {
- Map<String, Object> original = other("Annual Leave", 8d, 8d, -1d);
- List<TimeCardChangeApprovalWriteBackService.ChangeRow> rows = Arrays.asList(
- row(TimeCardChangeApprovalWriteBackService.OTHER,
- "Annual Leave", 10d, 2d, true),
- row(TimeCardChangeApprovalWriteBackService.OTHER,
- "Annual Leave", 7.5d, -0.5d, true),
- row(TimeCardChangeApprovalWriteBackService.OTHER,
- "Annual Leave", 12d, 4d, false));
- List<Map> merged = TimeCardChangeApprovalWriteBackService.mergeDeltaRows(
- Collections.singletonList(original), rows,
- TimeCardChangeApprovalWriteBackService.OTHER);
- assertEquals(1, merged.size());
- assertEquals("Annual Leave", merged.get(0).get("selectField_mmeakgie"));
- assertEquals(8d, merged.get(0).get("numberField_mmeakgij"));
- assertEquals(8d, merged.get(0).get("numberField_mmeakgii"));
- assertEquals(0.5d, merged.get(0).get("numberField_mmeakgim"));
- }
- @Test
- void shouldMoveNegativeDeltaFromTargetDayToSubmitDay() {
- Map<String, Double> targetDeltas = new LinkedHashMap<>();
- targetDeltas.put("20260818", -2d);
- Map<String, Double> adjustments =
- TimeCardChangeApprovalWriteBackService.requiredAdjustments(
- targetDeltas, "20261007");
- assertEquals(2d, adjustments.get("20260818"));
- assertEquals(-2d, adjustments.get("20261007"));
- }
- @Test
- void shouldLeaveRequiredHoursUnchangedForSameDayNetZeroRedistribution() {
- Map<String, Double> targetDeltas = new LinkedHashMap<>();
- targetDeltas.put("20260818", 0d);
- Map<String, Double> adjustments =
- TimeCardChangeApprovalWriteBackService.requiredAdjustments(
- targetDeltas, "20261007");
- assertTrue(adjustments.isEmpty());
- }
- @Test
- void shouldKeepSignedLineDeltasForSubmissionDaySummary() {
- List<TimeCardChangeApprovalWriteBackService.ChangeRow> rows = Arrays.asList(
- row("P-001", 0d, -8d, true),
- row("P-002", 8d, 8d, true));
- List<Map> changes = TimeCardChangeApprovalWriteBackService.deltaRows(
- rows, TimeCardChangeApprovalWriteBackService.BILLABLE);
- assertEquals(2, changes.size());
- assertEquals(-8d, changes.get(0).get("numberField_mmd80zae"));
- assertEquals(8d, changes.get(1).get("numberField_mmd80zae"));
- }
- @Test
- void shouldNotRepeatBusinessWriteWhenCheckpointRetryIsNeeded() {
- String progressKey = "approval-retry-1";
- int[] actionCount = {0};
- int[] checkpointCount = {0};
- TimeCardChangeApprovalWriteBackService.clearLocalProgress(progressKey);
- try {
- assertThrows(IllegalStateException.class,
- () -> TimeCardChangeApprovalWriteBackService.runIdempotentStep(
- progressKey, 0, 0, () -> actionCount[0]++, completed -> {
- checkpointCount[0]++;
- throw new IllegalStateException("temporary checkpoint failure");
- }));
- int completed = TimeCardChangeApprovalWriteBackService.runIdempotentStep(
- progressKey, 0, 0, () -> actionCount[0]++, completedSteps -> checkpointCount[0]++);
- assertEquals(1, actionCount[0]);
- assertEquals(2, checkpointCount[0]);
- assertEquals(1, completed);
- } finally {
- TimeCardChangeApprovalWriteBackService.clearLocalProgress(progressKey);
- }
- }
- @Test
- void shouldSerializeChangeWriteBackForTheSameUser() {
- assertSame(TimeCardChangeApprovalWriteBackService.writeBackLock("user-1"),
- TimeCardChangeApprovalWriteBackService.writeBackLock("user-1"));
- }
- private static TimeCardChangeApprovalWriteBackService.ChangeRow row(
- String key, double target, double delta, boolean approved) {
- return row(TimeCardChangeApprovalWriteBackService.BILLABLE,
- key, target, delta, approved);
- }
- private static TimeCardChangeApprovalWriteBackService.ChangeRow row(
- TimeCardChangeApprovalWriteBackService.TableDef table, String key,
- double target, double delta, boolean approved) {
- return new TimeCardChangeApprovalWriteBackService.ChangeRow(
- table,
- "user-1", "20260818", key, "Project " + key, target, delta, approved);
- }
- private static Map<String, Object> billable(String key, double draft, double submitted,
- double approved, double change) {
- Map<String, Object> row = new HashMap<>();
- row.put("textField_mmacxew3", key);
- row.put("numberField_mmczo635", draft);
- row.put("numberField_mmczo636", submitted);
- row.put("numberField_mmczo637", approved);
- row.put("numberField_mmd80zae", change);
- return row;
- }
- private static Map<String, Object> nonBillable(String key, double submitted,
- double approved, double change) {
- Map<String, Object> row = new HashMap<>();
- row.put("textField_mmczo639", key);
- row.put("numberField_mmczo63c", submitted);
- row.put("numberField_mmczo63d", approved);
- row.put("numberField_mmeakgi9", change);
- return row;
- }
- private static Map<String, Object> other(String activity, double submitted,
- double approved, double change) {
- Map<String, Object> row = new HashMap<>();
- row.put("selectField_mmeakgie", activity);
- row.put("numberField_mmeakgij", submitted);
- row.put("numberField_mmeakgii", approved);
- row.put("numberField_mmeakgim", change);
- return row;
- }
- private static Map<String, Object> summary(Map<String, Object>... billableRows) {
- Map<String, Object> detail = new HashMap<>();
- detail.put("tableField_mmczo634", Arrays.asList(billableRows));
- detail.put("tableField_mmczo63h", Collections.emptyList());
- detail.put("tableField_mmeakgid", Collections.emptyList());
- return detail;
- }
- }
|