package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.fenfa.*; import jnpf.dmsEntity.fenfa.DmsWenjianDayinFasongEntity.Batch; import jnpf.dmsEntity.fenfa.form.DmsWenjianDayinFasongForm; import jnpf.dmsMapper.*; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.exception.DataException; import jnpf.util.JsonUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class DmsWenjianDayinFasongServiceTest { final DmsWenjianDayinHaomaService numbers = mock(DmsWenjianDayinHaomaService.class); final DmsCurrentIdentity identity = mock(DmsCurrentIdentity.class); final DmsWenjianFenfaMapper tasks = mock(DmsWenjianFenfaMapper.class); final DmsWenjianDayinQuerenMapper mapper = mock(DmsWenjianDayinQuerenMapper.class); final DmsWenjianDayinTaizhangService ledger = mock(DmsWenjianDayinTaizhangService.class); final DmsWenjianFenfaService distribution = mock(DmsWenjianFenfaService.class); final DmsWenjianDayinQuerenService service = new DmsWenjianDayinQuerenService(identity, tasks, mapper, ledger, distribution, numbers); final DmsWenjianDayinFasongForm form = new DmsWenjianDayinFasongForm(); DmsWenjianDayinFasongEntity saved; @BeforeEach void setup() throws Exception { when(numbers.ensureTaskNumbers(anyString(), any(), anyInt())).thenAnswer(c -> java.util.stream.IntStream.rangeClosed(1, 7).mapToObj(i -> { jnpf.dmsEntity.fenfa.DmsWenjianDayinHaoma n = new jnpf.dmsEntity.fenfa.DmsWenjianDayinHaoma(); n.setTaskSequence(i); n.setSerialNumber("S"+i); n.setCountSerialNumber("2026"+String.format("%04d",i)); return n; }).toList()); UserInfo user = new UserInfo(); user.setUserId("user"); when(identity.requireUser()).thenReturn(user); when(identity.tenantId(user)).thenReturn("tenant"); form.setRequestId(UUID.randomUUID().toString()); form.setTaskId("task"); form.setFileVersionId("version"); form.setCopies(1); DmsWenjianDayinRenwuEntity task = new DmsWenjianDayinRenwuEntity(); task.setDayinFenshu(7); task.setId("task"); when(tasks.selectPrintTaskForUpdate("tenant", "task")).thenReturn(task); when(ledger.list(any(), isNull(), eq("f_id"), eq("task"))).thenReturn(List.of(Map.of("f_id", "task"))); DmsWenjianDayinQuerenEntity version = new DmsWenjianDayinQuerenEntity(); version.setFileVersionId("version"); when(mapper.selectTaskVersion("tenant", "task")).thenReturn(version); when(mapper.insertAttempt(any())).thenAnswer(c -> { saved = c.getArgument(0); return 1; }); when(mapper.selectAttempt(eq("tenant"), anyString())).thenAnswer(c -> saved != null && saved.getId().equals(c.getArgument(1)) ? saved : null); when(mapper.selectActiveAttempt("tenant", "task")).thenAnswer(c -> saved != null && !"CLOSED".equals(saved.getState()) ? saved : null); when(mapper.updateAttempt(any())).thenAnswer(c -> { saved = c.getArgument(0); return 1; }); } jnpf.dmsEntity.fenfa.form.DmsWenjianDayinYulanForm previewForm() { var preview = new jnpf.dmsEntity.fenfa.form.DmsWenjianDayinYulanForm(); preview.setTaskId("task"); preview.setFileVersionId("version"); return preview; } @Test void previewUsesNextFixedNumberAndDoesNotCreateSendOrPrintRecords() { when(tasks.maxPrintSequence("tenant", "task")).thenReturn(3); var first = service.previewParameters(previewForm()); assertEquals("S4", first.getSerialNumber()); assertEquals("20260004", first.getCountSerialNumber()); assertEquals(first, service.previewParameters(previewForm())); verify(mapper, never()).insertAttempt(any()); verify(mapper, never()).insertConfirmation(any()); verifyNoInteractions(distribution); } @Test void previewRejectsUnauthorizedOrChangedVersionBeforeAllocating() { when(ledger.list(any(), isNull(), eq("f_id"), eq("task"))).thenReturn(List.of()); assertThrows(DataException.class, () -> service.previewParameters(previewForm())); when(ledger.list(any(), isNull(), eq("f_id"), eq("task"))).thenReturn(List.of(Map.of("f_id", "task"))); var preview = previewForm(); preview.setFileVersionId("other-version"); assertThrows(DataException.class, () -> service.previewParameters(preview)); verifyNoInteractions(numbers, distribution); } @Test void previewPreservesActiveAndConfirmedAttemptNumberWithoutAllocating() { service.prepare(form); clearInvocations(numbers); when(tasks.maxPrintSequence("tenant", "task")).thenReturn(1); assertEquals("S1", service.previewParameters(previewForm()).getSerialNumber()); saved.setState("CONFIRMED"); assertEquals("S1", service.previewParameters(previewForm()).getSerialNumber()); saved.setUserId("other"); assertThrows(DataException.class, () -> service.previewParameters(previewForm())); verifyNoInteractions(numbers, distribution); } @Test void previewRejectsCompletedMissingAndDamagedTasks() { when(tasks.countConsumedPrints("tenant", "task")).thenReturn(7); assertThrows(DataException.class, () -> service.previewParameters(previewForm())); verifyNoInteractions(numbers); when(tasks.countConsumedPrints("tenant", "task")).thenReturn(0); when(numbers.ensureTaskNumbers(anyString(), any(), anyInt())).thenReturn(List.of()); assertThrows(DataException.class, () -> service.previewParameters(previewForm())); when(tasks.selectPrintTaskForUpdate("tenant", "task")).thenReturn(null); assertThrows(DataException.class, () -> service.previewParameters(previewForm())); assertThrows(DataException.class, () -> service.previewParameters(null)); verify(mapper, never()).insertAttempt(any()); verifyNoInteractions(distribution); } Batch batch(int... pages) { Batch b = new Batch(); b.setId(UUID.randomUUID().toString()); b.setItems(List.of(0)); b.setPages(Arrays.stream(pages).boxed().toList()); return b; } void planned() throws Exception { service.prepare(form); form.setBatches(List.of(batch(1), batch(2))); service.plan(form); form.setBatchId(form.getBatches().get(0).getId()); form.setToken(UUID.randomUUID().toString()); } @Test void refreshRestoresFrozenCopiesWithoutNewAttempt() throws Exception { service.prepare(form); String original = saved.getId(); form.setRequestId(UUID.randomUUID().toString()); form.setCopies(7); assertEquals(original, service.prepare(form).getId()); assertEquals(1, saved.getCopies()); verify(mapper, times(1)).insertAttempt(any()); verifyNoInteractions(distribution); } @Test void anotherActorCannotTakeOver() throws Exception { service.prepare(form); saved.setUserId("other"); assertThrows(DataException.class, () -> service.prepare(form)); assertThrows(DataException.class, () -> service.begin(form)); } @Test void twoCopiesRequireFullSeparatePlansAndFreezeNextTaskNumbers() throws Exception { form.setCopies(2); when(tasks.maxPrintSequence("tenant", "task")).thenReturn(3); service.prepare(form); var frozen = JsonUtil.getJsonToList(saved.getNumbersJson(), DmsWenjianDayinHaoma.class); assertEquals(List.of(4,5), frozen.stream().map(DmsWenjianDayinHaoma::getTaskSequence).toList()); Batch first = batch(1,2), shortSecond = batch(1); shortSecond.setItems(List.of(1)); form.setBatches(List.of(first,shortSecond)); assertThrows(DataException.class, () -> service.plan(form)); Batch second = batch(1,2); second.setItems(List.of(1)); form.setBatches(List.of(first,second)); service.plan(form); assertEquals(2, JsonUtil.getJsonToList(saved.getBatchesJson(), Batch.class).size()); } @Test void sameBatchCannotBeClaimedTwiceEvenWithSameToken() throws Exception { planned(); service.begin(form); assertThrows(DataException.class, () -> service.begin(form)); form.setToken(UUID.randomUUID().toString()); assertThrows(DataException.class, () -> service.begin(form)); verifyNoInteractions(distribution); } @Test void lateReceiptMustMatchTokenAndIsIdempotent() throws Exception { planned(); service.begin(form); String token = form.getToken(); form.setToken(UUID.randomUUID().toString()); assertThrows(DataException.class, () -> service.submitted(form)); form.setToken(token); service.submitted(form); service.submitted(form); assertEquals("SUBMITTED", JsonUtil.getJsonToList(saved.getBatchesJson(), Batch.class).get(0).getState()); assertThrows(DataException.class, () -> service.finish(form)); } @Test void cannotSkipBatchOrChangeFrozenCopies() throws Exception { planned(); form.setBatchId(form.getBatches().get(1).getId()); assertThrows(DataException.class, () -> service.begin(form)); form.setCopies(3); assertThrows(DataException.class, () -> service.plan(form)); } @Test void planCannotSkipPagesAndSentBatchCannotSplit() throws Exception { service.prepare(form); form.setBatches(List.of(batch(1, 3))); assertThrows(DataException.class, () -> service.plan(form)); form.setBatches(List.of(batch(1, 2))); service.plan(form); form.setBatchId(form.getBatches().get(0).getId()); form.setToken(UUID.randomUUID().toString()); service.begin(form); form.setBatches(List.of(batch(1), batch(2))); assertThrows(DataException.class, () -> service.split(form)); } @Test void splitPreservesPreviousSubmittedProgress() throws Exception { service.prepare(form); form.setBatches(List.of(batch(1), batch(2, 3))); service.plan(form); form.setBatchId(form.getBatches().get(0).getId()); form.setToken(UUID.randomUUID().toString()); service.begin(form); service.submitted(form); form.setBatchId(form.getBatches().get(1).getId()); form.setBatches(List.of(batch(2), batch(3))); service.split(form); List restored = JsonUtil.getJsonToList(saved.getBatchesJson(), Batch.class); assertEquals(3, restored.size()); assertEquals("SUBMITTED", restored.get(0).getState()); assertEquals(List.of(2), restored.get(1).getPages()); } @Test void permissionChangeAfterPreparationPreventsDispatch() throws Exception { planned(); when(ledger.list(any(), isNull(), eq("f_id"), eq("task"))).thenReturn(List.of()); assertThrows(DataException.class, () -> service.begin(form)); assertEquals("READY", JsonUtil.getJsonToList(saved.getBatchesJson(), Batch.class).get(0).getState()); } }