package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.permission.entity.DmsDocumentEntity; import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity; import jnpf.dmsEntity.permission.entity.DmsPermissionResourceEntity; import jnpf.dmsEntity.storage.entity.DmsFileObjectEntity; import jnpf.dmsEntity.storage.form.DmsFileUploadRegisterForm; import jnpf.dmsEntity.storage.vo.DmsFileUploadVO; import jnpf.dmsIntegration.audit.DmsAuditAdapter; import jnpf.dmsMapper.DmsDocumentMapper; import jnpf.dmsMapper.DmsFileObjectMapper; import jnpf.dmsMapper.DmsFileVersionMapper; import jnpf.dmsMapper.DmsPermissionResourceMapper; import jnpf.dmsMapper.DmsVersionStateMapper; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsPermission.DmsPermissionException; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import java.time.LocalDate; import java.util.UUID; 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.mockito.ArgumentMatchers.any; 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 DmsFileUploadServiceTest { @Test void registersDocumentVersionResourcesAndObjectInOneServiceCall() { Fixture fixture = new Fixture(); DmsFileUploadRegisterForm form = fixture.form(); DmsFileUploadVO created = fixture.view(form); when(fixture.fileObjectMapper.selectUploadBySource("tenant-1", "jnpf-file", "source-1")) .thenReturn(null, created); DmsFileUploadVO result = fixture.service.register(form, "jnpf-file"); assertSame(created, result); verify(fixture.decisionService).requireAllowed(any(), any(), any()); ArgumentCaptor documentCaptor = ArgumentCaptor.forClass(DmsDocumentEntity.class); verify(fixture.documentMapper).insert(documentCaptor.capture()); assertEquals("MM-R02", documentCaptor.getValue().getDocumentNo()); assertEquals("QUALITY_MANUAL", documentCaptor.getValue().getDocumentType()); assertEquals("dept-quality", documentCaptor.getValue().getResponsibleDeptId()); ArgumentCaptor versionCaptor = ArgumentCaptor.forClass(DmsFileVersionEntity.class); verify(fixture.fileVersionMapper).insert(versionCaptor.capture()); assertEquals("D(00)", versionCaptor.getValue().getVersionLabel()); assertEquals("author-1", versionCaptor.getValue().getPreparedBy()); assertEquals(LocalDate.of(2026, 8, 21), versionCaptor.getValue().getPreparedOn()); verify(fixture.resourceMapper, org.mockito.Mockito.times(2)).insert(any(DmsPermissionResourceEntity.class)); verify(fixture.versionStateMapper).insertDirectEffective("tenant-1", form.getDocumentId(), form.getFileVersionId(), "user-1"); verify(fixture.fileObjectMapper).insert(any(DmsFileObjectEntity.class)); } @Test void returnsExistingUploadWithoutDuplicateWrites() { Fixture fixture = new Fixture(); DmsFileUploadRegisterForm form = fixture.form(); DmsFileUploadVO existing = fixture.view(form); when(fixture.fileObjectMapper.selectUploadBySource("tenant-1", "jnpf-file", "source-1")) .thenReturn(existing); DmsFileUploadVO result = fixture.service.register(form, "jnpf-file"); assertSame(existing, result); verify(fixture.documentMapper, never()).insert(any(DmsDocumentEntity.class)); verify(fixture.fileVersionMapper, never()).insert(any(DmsFileVersionEntity.class)); verify(fixture.versionStateMapper, never()).insertDirectEffective(any(), any(), any(), any()); verify(fixture.fileObjectMapper, never()).insert(any(DmsFileObjectEntity.class)); } @Test void rejectsIdempotentRetryWhenDocumentNumberChanges() { Fixture fixture = new Fixture(); DmsFileUploadRegisterForm form = fixture.form(); DmsFileUploadVO existing = fixture.view(form); form.setDocumentNo("MM-R03"); when(fixture.fileObjectMapper.selectUploadBySource("tenant-1", "jnpf-file", "source-1")) .thenReturn(existing); assertThrows(DmsPermissionException.class, () -> fixture.service.register(form, "jnpf-file")); verify(fixture.documentMapper, never()).insert(any(DmsDocumentEntity.class)); verify(fixture.fileVersionMapper, never()).insert(any(DmsFileVersionEntity.class)); verify(fixture.versionStateMapper, never()).insertDirectEffective(any(), any(), any(), any()); verify(fixture.fileObjectMapper, never()).insert(any(DmsFileObjectEntity.class)); } private static class Fixture { private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class); private final DmsPermissionDecisionService decisionService = mock(DmsPermissionDecisionService.class); private final DmsDocumentMapper documentMapper = mock(DmsDocumentMapper.class); private final DmsFileVersionMapper fileVersionMapper = mock(DmsFileVersionMapper.class); private final DmsPermissionResourceMapper resourceMapper = mock(DmsPermissionResourceMapper.class); private final DmsFileObjectMapper fileObjectMapper = mock(DmsFileObjectMapper.class); private final DmsVersionStateMapper versionStateMapper = mock(DmsVersionStateMapper.class); private final DmsAuditAdapter auditAdapter = mock(DmsAuditAdapter.class); private final DmsFileUploadService service = new DmsFileUploadService(currentIdentity, decisionService, documentMapper, fileVersionMapper, resourceMapper, fileObjectMapper, versionStateMapper, auditAdapter); private Fixture() { UserInfo user = new UserInfo(); user.setUserId("user-1"); user.setTenantId("tenant-1"); when(currentIdentity.requireUser()).thenReturn(user); when(currentIdentity.tenantId(user)).thenReturn("tenant-1"); } private DmsFileUploadRegisterForm form() { DmsFileUploadRegisterForm form = new DmsFileUploadRegisterForm(); form.setDocumentId(UUID.fromString("018f0000-0000-7000-8000-000000000301")); form.setFileVersionId(UUID.fromString("018f0000-0000-7000-8000-000000000302")); form.setFileObjectId(UUID.fromString("018f0000-0000-7000-8000-000000000303")); form.setFolderId(UUID.fromString("018f0000-0000-7000-8000-000000000304")); form.setDocumentNo("MM-R02"); form.setDocumentName("质量手册"); form.setDocumentType("QUALITY_MANUAL"); form.setResponsibleDeptId("dept-quality"); form.setVersionLabel("D(00)"); form.setFileName("report.txt"); form.setMimeType("text/plain"); form.setSizeBytes(12L); form.setChecksumSha256("a".repeat(64)); form.setPreparedBy("author-1"); form.setPreparedOn(LocalDate.of(2026, 8, 21)); form.setStoragePlatform("seaweedfs-1"); form.setBucketName("jnpf-dms"); form.setBasePath("dms/"); form.setObjectPath("2026/08/17/document/"); form.setObjectName("version.txt"); form.setSourceId("source-1"); return form; } private DmsFileUploadVO view(DmsFileUploadRegisterForm form) { DmsFileUploadVO view = new DmsFileUploadVO(); view.setDocumentId(form.getDocumentId()); view.setFileVersionId(form.getFileVersionId()); view.setFileObjectId(form.getFileObjectId()); view.setFolderId(form.getFolderId()); view.setDocumentNo(form.getDocumentNo()); view.setDocumentName(form.getDocumentName()); view.setDocumentType(form.getDocumentType()); view.setResponsibleDeptId(form.getResponsibleDeptId()); view.setVersionNo(1L); view.setVersionLabel(form.getVersionLabel()); view.setFileName(form.getFileName()); view.setMimeType(form.getMimeType()); view.setSizeBytes(form.getSizeBytes()); view.setChecksumSha256(form.getChecksumSha256()); view.setPreparedBy(form.getPreparedBy()); view.setPreparedOn(form.getPreparedOn()); view.setStoragePlatform(form.getStoragePlatform()); view.setBucketName(form.getBucketName()); view.setBasePath(form.getBasePath()); view.setObjectPath(form.getObjectPath()); view.setObjectName(form.getObjectName()); view.setSourceId(form.getSourceId()); return view; } } }