package jnpf.fileStorage.dms; import jnpf.base.ActionResult; import jnpf.config.ConfigValueUtil; import jnpf.dms.DmsResourceApi; import jnpf.dmsEntity.storage.form.DmsFileRevisionRegisterForm; import jnpf.dmsEntity.storage.form.DmsFileUploadRegisterForm; import jnpf.dmsEntity.storage.vo.DmsFileUploadVO; import jnpf.exception.DataException; import org.dromara.x.file.storage.core.FileInfo; import org.dromara.x.file.storage.core.FileStorageService; import org.dromara.x.file.storage.core.hash.HashInfo; import org.dromara.x.file.storage.core.platform.MinioFileStorage; import org.dromara.x.file.storage.core.upload.UploadPretreatment; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; public class DmsFileStorageServiceTest { @Test public void approvedContentReturnsStorageFactsWithoutCallingBackIntoDms() { Fixture f = new Fixture(); DmsFileUploadRegisterForm result = f.service.storeWorkflowContent(f.documentId, f.baseVersionId, f.file); assertEquals(f.documentId, result.getDocumentId()); org.junit.Assert.assertNotNull(result.getFileVersionId()); org.junit.Assert.assertNotNull(result.getFileObjectId()); assertEquals("a".repeat(64), result.getChecksumSha256()); assertEquals(Long.valueOf(12L), result.getSizeBytes()); assertEquals("jnpf-dms", result.getBucketName()); verify(f.upload).setSaveFilename(result.getFileObjectId() + ".txt"); org.mockito.Mockito.verifyNoInteractions(f.dmsResourceApi); } @Test public void approvedContentRejectsMissingChecksum() { Fixture f = new Fixture(); f.stored.setHashInfo(null); assertThrows(DataException.class, () -> f.service.storeWorkflowContent(f.documentId, f.baseVersionId, f.file)); verify(f.fileStorageService).delete(f.stored); org.mockito.Mockito.verifyNoInteractions(f.dmsResourceApi); } @Test public void serverErrorRetainsObjectsForReconciliation() { Fixture fixture = new Fixture(); ActionResult response = new ActionResult<>(); response.setCode(500); when(fixture.dmsResourceApi.registerUpload(anyString(), anyString(), fixture.formCaptor.capture())) .thenReturn(response); assertThrows(DataException.class, fixture::upload); verify(fixture.fileStorageService, never()).delete(fixture.stored); } @Test public void timeoutAfterRegistrationDoesNotDeletePossiblyCommittedObjects() { Fixture fixture = new Fixture(); when(fixture.dmsResourceApi.registerUpload(anyString(), anyString(), fixture.formCaptor.capture())) .thenThrow(new IllegalStateException("response timed out after commit")); when(fixture.dmsResourceApi.registerRevision(anyString(), anyString(), fixture.revisionCaptor.capture())) .thenThrow(new IllegalStateException("response timed out after commit")); assertThrows(IllegalStateException.class, fixture::upload); assertThrows(IllegalStateException.class, fixture::revise); verify(fixture.fileStorageService, never()).delete(fixture.stored); } @Test public void missingRegistrationResponseRetainsUploadedObjects() { Fixture fixture = new Fixture(); assertThrows(DataException.class, fixture::upload); assertThrows(DataException.class, fixture::revise); verify(fixture.fileStorageService, never()).delete(fixture.stored); } @Test public void successWithoutDataRetainsUploadedObjects() { Fixture fixture = new Fixture(); ActionResult response = new ActionResult<>(); response.setCode(200); when(fixture.dmsResourceApi.registerUpload(anyString(), anyString(), fixture.formCaptor.capture())) .thenReturn(response); when(fixture.dmsResourceApi.registerRevision(anyString(), anyString(), fixture.revisionCaptor.capture())) .thenReturn(response); assertThrows(DataException.class, fixture::upload); assertThrows(DataException.class, fixture::revise); verify(fixture.fileStorageService, never()).delete(fixture.stored); } @Test public void uploadsToDedicatedSeaweedFsPlatformAndRegistersDmsResources() { Fixture fixture = new Fixture(); DmsFileUploadVO registered = fixture.registeredViewForStoredObject(); ActionResult response = mock(ActionResult.class); when(response.getCode()).thenReturn(200); when(response.getData()).thenReturn(registered); when(fixture.dmsResourceApi.registerUpload(anyString(), anyString(), fixture.formCaptor.capture())) .thenReturn(response); DmsFileUploadVO result = fixture.upload(); assertEquals(registered, result); verify(fixture.upload).setPlatform("seaweedfs-1"); verify(fixture.upload).setHashCalculatorSha256(); DmsFileUploadRegisterForm form = fixture.formCaptor.getValue(); assertEquals(fixture.folderId, form.getFolderId()); assertEquals("MM-R02", form.getDocumentNo()); assertEquals("质量手册", form.getDocumentName()); assertEquals("QUALITY_MANUAL", form.getDocumentType()); assertEquals("dept-quality", form.getResponsibleDeptId()); assertEquals("D(00)", form.getVersionLabel()); assertEquals("author-1", form.getPreparedBy()); assertEquals(LocalDate.of(2026, 8, 21), form.getPreparedOn()); assertEquals("seaweedfs-1", form.getStoragePlatform()); assertEquals("jnpf-dms", form.getBucketName()); assertEquals("a".repeat(64), form.getChecksumSha256()); assertEquals("sign-1", form.getAuditSignParameters().get("biz_sign")); verify(fixture.dmsResourceApi).registerUpload(anyString(), org.mockito.ArgumentMatchers.eq("audit-sign-1"), org.mockito.ArgumentMatchers.any()); assertTrue(form.getDocumentId().version() == 7); } @Test public void deletesStoredObjectWhenDmsRegistrationFails() { Fixture fixture = new Fixture(); ActionResult response = mock(ActionResult.class); when(response.getCode()).thenReturn(400); when(response.getMsg()).thenReturn("DMS rejected"); when(fixture.dmsResourceApi.registerUpload(anyString(), anyString(), fixture.formCaptor.capture())) .thenReturn(response); when(fixture.fileStorageService.delete(fixture.stored)).thenReturn(true); assertThrows(DataException.class, fixture::upload); verify(fixture.fileStorageService).delete(fixture.stored); } @Test public void uploadsRevisionToSameDocumentAndRegistersNewVersion() { Fixture fixture = new Fixture(); DmsFileUploadVO registered = fixture.registeredViewForStoredObject(); ActionResult response = mock(ActionResult.class); when(response.getCode()).thenReturn(200); when(response.getData()).thenReturn(registered); when(fixture.dmsResourceApi.registerRevision(anyString(), anyString(), fixture.revisionCaptor.capture())) .thenReturn(response); DmsFileUploadVO result = fixture.revise(); assertEquals(registered, result); verify(fixture.upload).setPlatform("seaweedfs-1"); DmsFileRevisionRegisterForm form = fixture.revisionCaptor.getValue(); assertEquals(fixture.documentId, form.getDocumentId()); assertEquals(fixture.baseVersionId, form.getBaseFileVersionId()); assertEquals("D(01)", form.getVersionLabel()); assertEquals("更新质量职责", form.getRevisionReason()); assertEquals("author-2", form.getPreparedBy()); assertEquals(LocalDate.of(2026, 8, 22), form.getPreparedOn()); assertEquals("sign-1", form.getAuditSignParameters().get("biz_sign")); verify(fixture.dmsResourceApi).registerRevision(anyString(), org.mockito.ArgumentMatchers.eq("audit-sign-1"), org.mockito.ArgumentMatchers.any()); assertTrue(form.getFileVersionId().version() == 7); assertTrue(form.getFileObjectId().version() == 7); } @Test public void deletesRevisionObjectWhenDmsRegistrationFails() { Fixture fixture = new Fixture(); ActionResult response = mock(ActionResult.class); when(response.getCode()).thenReturn(400); when(response.getMsg()).thenReturn("current version changed"); when(fixture.dmsResourceApi.registerRevision(anyString(), anyString(), fixture.revisionCaptor.capture())) .thenReturn(response); when(fixture.fileStorageService.delete(fixture.stored)).thenReturn(true); assertThrows(DataException.class, fixture::revise); verify(fixture.fileStorageService).delete(fixture.stored); } private static class Fixture { private final UUID folderId = UUID.fromString("018f0000-0000-7000-8000-000000000401"); private final UUID documentId = UUID.fromString("018f0000-0000-7000-8000-000000000402"); private final UUID baseVersionId = UUID.fromString("018f0000-0000-7000-8000-000000000403"); private final FileStorageService fileStorageService = mock(FileStorageService.class); private final DmsResourceApi dmsResourceApi = mock(DmsResourceApi.class); private final ConfigValueUtil configValueUtil = mock(ConfigValueUtil.class); private final MultipartFile file = mock(MultipartFile.class); private final UploadPretreatment upload = mock(UploadPretreatment.class); private final MinioFileStorage minio = mock(MinioFileStorage.class); private final FileInfo stored = storedFile(); private final ArgumentCaptor formCaptor = ArgumentCaptor.forClass(DmsFileUploadRegisterForm.class); private final ArgumentCaptor revisionCaptor = ArgumentCaptor.forClass(DmsFileRevisionRegisterForm.class); private final DmsFileStorageService service = new DmsFileStorageService(fileStorageService, dmsResourceApi, configValueUtil); private Fixture() { when(file.isEmpty()).thenReturn(false); when(file.getOriginalFilename()).thenReturn("report.txt"); when(configValueUtil.getAllowUploadFileType()).thenReturn("txt,pdf,doc,docx"); when(fileStorageService.getFileStorageVerify("seaweedfs-1")).thenReturn(minio); when(fileStorageService.getFileStorage("seaweedfs-1")).thenReturn(minio); when(minio.getBucketName()).thenReturn("jnpf-dms"); when(fileStorageService.of(file)).thenReturn(upload); when(upload.setPlatform("seaweedfs-1")).thenReturn(upload); when(upload.setPath(anyString())).thenReturn(upload); when(upload.setSaveFilename(anyString())).thenReturn(upload); when(upload.setOriginalFilename("report.txt")).thenReturn(upload); when(upload.setHashCalculatorSha256()).thenReturn(upload); when(upload.upload()).thenReturn(stored); } private DmsFileUploadVO registeredViewForStoredObject() { DmsFileUploadVO view = new DmsFileUploadVO(); view.setStoragePlatform(stored.getPlatform()); view.setBasePath(stored.getBasePath()); view.setObjectPath(stored.getPath()); view.setObjectName(stored.getFilename()); return view; } private DmsFileUploadVO upload() { Map signs = new LinkedHashMap<>(); signs.put("biz_sign", "sign-1"); return service.upload(folderId, "source-1", "MM-R02", "质量手册", "QUALITY_MANUAL", "dept-quality", "D(00)", "author-1", LocalDate.of(2026, 8, 21), signs, "audit-sign-1", file); } private DmsFileUploadVO revise() { Map signs = new LinkedHashMap<>(); signs.put("biz_sign", "sign-1"); return service.revise(documentId, baseVersionId, "revision-1", "D(01)", "author-2", LocalDate.of(2026, 8, 22), "更新质量职责", signs, "audit-sign-1", file); } private static FileInfo storedFile() { HashInfo hashInfo = new HashInfo(); hashInfo.put("SHA-256", "a".repeat(64)); FileInfo info = new FileInfo(); info.setPlatform("seaweedfs-1"); info.setBasePath("dms/"); info.setPath("2026/08/17/document/"); info.setFilename("version.txt"); info.setOriginalFilename("report.txt"); info.setContentType("text/plain"); info.setSize(12L); info.setHashInfo(hashInfo); return info; } } }