package jnpf.dmsService;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import jnpf.base.ActionResult;
|
import jnpf.dmsEntity.storage.approval.*;
|
import jnpf.dmsMapper.DmsApprovedBatchMapper;
|
import jnpf.exception.DataException;
|
import jnpf.file.FileContentApi;
|
import jnpf.model.filecontent.*;
|
import org.junit.jupiter.api.Test;
|
import java.util.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.Mockito.*;
|
|
class DmsApprovedContentServiceTest {
|
final FileContentApi files = mock(FileContentApi.class);
|
final DmsApprovedBatchMapper progress = mock(DmsApprovedBatchMapper.class);
|
final DmsApprovedFileAcceptanceService acceptance = mock(DmsApprovedFileAcceptanceService.class);
|
final ObjectMapper json = new ObjectMapper();
|
final DmsApprovedContentService service = new DmsApprovedContentService(files, progress, acceptance, json);
|
final DmsApprovedBatch batch = new DmsApprovedBatch();
|
final DmsApprovedItem item = new DmsApprovedItem();
|
final FileContentReceipt receipt = new FileContentReceipt();
|
DmsApprovedContentServiceTest() {
|
batch.setId(UUID.randomUUID()); batch.setTenantId("tenant"); batch.setLeaseToken(UUID.randomUUID().toString());
|
batch.setSourceDigest("digest"); batch.setItems(List.of(item));
|
item.setSourceKey("audit:zhuwenjian"); item.setChangeType("CREATE");
|
item.setDocumentId(UUID.randomUUID()); item.setFileVersionId(UUID.randomUUID());
|
item.setFileJson("[{\"url\":\"/api/file/Image/annex/main.txt\"}]"); item.setDocumentName("main.txt");
|
receipt.setFileObjectId(UUID.randomUUID()); receipt.setChecksumSha256("a".repeat(64)); receipt.setSizeBytes(12L);
|
receipt.setFileName("main.txt"); receipt.setStoragePlatform("seaweedfs-1"); receipt.setBucketName("jnpf-dms");
|
receipt.setObjectPath("2026/09/18/object/"); receipt.setObjectName("object.txt");
|
}
|
void physicalResponse() {
|
ActionResult<FileContentReceipt> response = new ActionResult<>(); response.setCode(200); response.setData(receipt);
|
when(files.importContent(eq("jnpf-dms"), any())).thenReturn(response);
|
}
|
@Test void retryAfterCenterFailureReusesDurablyStoredPhysicalResult() throws Exception {
|
physicalResponse();
|
when(progress.storePhysicalReceipt(eq("tenant"), eq(batch.getId()), eq(item.getSourceKey()), eq(batch.getLeaseToken()), anyString())).thenReturn(1);
|
when(acceptance.acceptStoredContent(any())).thenThrow(new DataException("center failed")).thenReturn(batch);
|
assertThrows(DataException.class, () -> service.register(batch));
|
assertNotNull(item.getStorageReceipt());
|
// A recovered batch reads this serialized receipt from the unified progress row.
|
item.setStorageReceipt(json.writeValueAsString(receipt));
|
assertSame(batch, service.register(batch));
|
verify(files, times(1)).importContent(eq("jnpf-dms"), any());
|
org.mockito.InOrder order = inOrder(progress, acceptance);
|
order.verify(progress).storePhysicalReceipt(eq("tenant"), eq(batch.getId()), eq(item.getSourceKey()), eq(batch.getLeaseToken()), anyString());
|
order.verify(acceptance, times(2)).acceptStoredContent(any());
|
}
|
@Test void onlyNewAndRevisionAuditsRequestCommentRemoval() {
|
physicalResponse();
|
when(progress.storePhysicalReceipt(anyString(), any(), anyString(), anyString(), anyString())).thenReturn(1);
|
when(acceptance.acceptStoredContent(any())).thenReturn(batch);
|
for (String type : new String[]{"xinzeng_shenhe", "xiuding_shenhe", "other", null}) {
|
batch.setAuditType(type); item.setStorageReceipt(null);
|
service.register(batch);
|
}
|
org.mockito.ArgumentCaptor<FileContentImportForm> request = org.mockito.ArgumentCaptor.forClass(FileContentImportForm.class);
|
verify(files, times(4)).importContent(eq("jnpf-dms"), request.capture());
|
assertEquals(List.of(true, true, false, false), request.getAllValues().stream()
|
.map(FileContentImportForm::isRemoveDocxComments).toList());
|
}
|
|
@Test void leaseLossAfterPhysicalCopyStopsCenterRegistration() {
|
physicalResponse();
|
assertThrows(DataException.class, () -> service.register(batch));
|
assertNull(item.getStorageReceipt());
|
verifyNoInteractions(acceptance);
|
}
|
@Test void malformedReceiptCannotAdvanceProgressOrCenter() {
|
physicalResponse(); receipt.setChecksumSha256(null);
|
assertThrows(DataException.class, () -> service.register(batch));
|
verifyNoInteractions(progress, acceptance);
|
}
|
@Test void removedAndUnchangedRecordsNeverCopyContent() {
|
DmsApprovedItem unchanged = new DmsApprovedItem(); unchanged.setChangeType("REUSE");
|
item.setChangeType("RETIRE"); batch.setItems(List.of(item, unchanged));
|
when(acceptance.acceptStoredContent(any())).thenReturn(batch);
|
assertSame(batch, service.register(batch));
|
verifyNoInteractions(files, progress);
|
}
|
|
@Test void sendsReservedIdsAndSignatureMetadataToPhysicalImport() throws Exception {
|
physicalResponse();
|
item.setQianpiyeId("signature-page"); item.setQianpiyeFiles("[{\"url\":\"signature-url\"}]");
|
item.setApprovalValuesByTag(Map.of("dms.qianpiye.flow.approve-1.approver", "审核人"));
|
item.setApprovalFlowVersionId("flow-version");
|
when(progress.storePhysicalReceipt(anyString(), any(), anyString(), anyString(), anyString())).thenReturn(1);
|
when(acceptance.acceptStoredContent(any())).thenReturn(batch);
|
assertSame(batch, service.register(batch));
|
org.mockito.ArgumentCaptor<FileContentImportForm> request = org.mockito.ArgumentCaptor.forClass(FileContentImportForm.class);
|
verify(files).importContent(eq("jnpf-dms"), request.capture());
|
com.fasterxml.jackson.databind.JsonNode wire = json.readTree(json.writeValueAsString(request.getValue()));
|
assertEquals(item.getDocumentId().toString(), wire.path("documentId").asText());
|
assertEquals(item.getFileVersionId().toString(), wire.path("fileVersionId").asText());
|
assertEquals(item.getFileJson(), wire.path("fileJson").asText());
|
assertEquals(item.getDocumentName(), wire.path("fileName").asText());
|
assertEquals(item.getQianpiyeId(), wire.path("signaturePageId").asText());
|
assertEquals(item.getQianpiyeFiles(), wire.path("signaturePageFiles").asText());
|
assertEquals("flow-version", wire.path("approvalFlowVersionId").asText());
|
assertEquals("审核人", wire.path("signaturePageValues")
|
.path("dms.qianpiye.flow.approve-1.approver").asText());
|
assertFalse(wire.has("taskId")); assertFalse(wire.has("changeType"));
|
}
|
|
@Test void revisionApprovalPageFallsBackToTheValidatedLegacySource() {
|
physicalResponse();
|
item.setChangeType("REVISE");
|
item.setApprovalPageFileJson("[{\"url\":\"revision-approval.docx\"}]");
|
item.setApprovalValuesByTag(Map.of("dms.qianpiye.flow.approve-1.approver", "修订审核人"));
|
when(progress.storePhysicalReceipt(anyString(), any(), anyString(), anyString(), anyString())).thenReturn(1);
|
when(acceptance.acceptStoredContent(any())).thenReturn(batch);
|
|
assertSame(batch, service.register(batch));
|
|
org.mockito.ArgumentCaptor<FileContentImportForm> request = org.mockito.ArgumentCaptor.forClass(FileContentImportForm.class);
|
verify(files).importContent(eq("jnpf-dms"), request.capture());
|
assertEquals(item.getApprovalPageFileJson(), request.getValue().getSignaturePageFiles());
|
assertEquals(item.getApprovalValuesByTag(), request.getValue().getSignaturePageValues());
|
}
|
|
@Test void secondCopyFailureKeepsFirstReceiptForTheNextAttempt() throws Exception {
|
DmsApprovedItem record = new DmsApprovedItem();
|
record.setSourceKey("audit:jilu:1"); record.setChangeType("REVISE");
|
record.setDocumentId(UUID.randomUUID()); record.setFileVersionId(UUID.randomUUID());
|
record.setFileJson("[{\"url\":\"/api/file/Image/annex/record.txt\"}]"); record.setDocumentName("record.txt");
|
batch.setItems(List.of(item, record));
|
when(progress.storePhysicalReceipt(anyString(), any(), anyString(), anyString(), anyString())).thenReturn(1);
|
ActionResult<FileContentReceipt> first = new ActionResult<>(); first.setCode(200); first.setData(receipt);
|
FileContentReceipt recordReceipt = json.readValue(json.writeValueAsString(receipt), FileContentReceipt.class);
|
recordReceipt.setFileObjectId(UUID.randomUUID()); recordReceipt.setObjectName("record.txt");
|
ActionResult<FileContentReceipt> second = new ActionResult<>(); second.setCode(200); second.setData(recordReceipt);
|
when(files.importContent(eq("jnpf-dms"), argThat(r -> r != null && item.getDocumentId().equals(r.getDocumentId())))).thenReturn(first);
|
when(files.importContent(eq("jnpf-dms"), argThat(r -> r != null && record.getDocumentId().equals(r.getDocumentId()))))
|
.thenThrow(new DataException("copy failed")).thenReturn(second);
|
assertThrows(DataException.class, () -> service.register(batch));
|
assertNotNull(item.getStorageReceipt()); assertNull(record.getStorageReceipt());
|
verifyNoInteractions(acceptance);
|
when(acceptance.acceptStoredContent(any())).thenReturn(batch);
|
assertSame(batch, service.register(batch));
|
verify(files, times(1)).importContent(eq("jnpf-dms"), argThat(r -> r != null && item.getDocumentId().equals(r.getDocumentId())));
|
verify(files, times(2)).importContent(eq("jnpf-dms"), argThat(r -> r != null && record.getDocumentId().equals(r.getDocumentId())));
|
org.mockito.ArgumentCaptor<jnpf.dmsEntity.storage.form.DmsApprovedRegistrationCallback> callback =
|
org.mockito.ArgumentCaptor.forClass(jnpf.dmsEntity.storage.form.DmsApprovedRegistrationCallback.class);
|
verify(acceptance).acceptStoredContent(callback.capture());
|
assertEquals(2, callback.getValue().getFiles().size());
|
assertEquals(receipt.getFileObjectId(), callback.getValue().getFiles().get(0).getContent().getFileObjectId());
|
assertEquals(recordReceipt.getFileObjectId(), callback.getValue().getFiles().get(1).getContent().getFileObjectId());
|
}
|
}
|