package jnpf.dmsService.pdf; import jnpf.base.ActionResult; import jnpf.dms.DmsPersistentPdfApi; import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity; import jnpf.dmsEntity.storage.entity.DmsFileObjectEntity; import jnpf.dmsEntity.storage.pdf.DmsPdfGenerationTask; import jnpf.dmsEntity.storage.vo.DmsPersistentPdfGenerateVO; import jnpf.dmsMapper.DmsFileObjectMapper; import jnpf.dmsMapper.DmsFileVersionMapper; import jnpf.dmsMapper.DmsPdfGenerationTaskMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.UUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; 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 DmsPdfGenerationJobTest { private static final String TENANT = "tenant-1"; private static final UUID VERSION_ID = UUID.fromString("018f0000-0000-7000-8000-000000000702"); private static final String CHECKSUM = "a".repeat(64); private final DmsPdfGenerationTaskMapper tasks = mock(DmsPdfGenerationTaskMapper.class); private final DmsFileVersionMapper versions = mock(DmsFileVersionMapper.class); private final DmsFileObjectMapper objects = mock(DmsFileObjectMapper.class); private final DmsPersistentPdfApi fileApi = mock(DmsPersistentPdfApi.class); private final DmsPdfGenerationCompletion completion = mock(DmsPdfGenerationCompletion.class); private final DmsPdfGenerationJob job = new DmsPdfGenerationJob(tasks, versions, objects, fileApi, completion); @BeforeEach void configure() throws Exception { set("maxAttempts", 8); set("leaseSeconds", 300); } @Test void skipsTaskWhenAnotherWorkerOwnsTheLease() { DmsPdfGenerationTask task = task(0); when(tasks.claim(eq(TENANT), eq(VERSION_ID), anyString(), eq(8), eq(300))) .thenReturn(0); job.process(task); verify(versions, never()).selectByTenantAndId(anyString(), any()); verify(fileApi, never()).generate(anyString(), any()); } @Test void generatesAndCompletesClaimedTask() { DmsPdfGenerationTask task = task(0); DmsFileVersionEntity version = version(CHECKSUM); DmsFileObjectEntity object = object(); DmsPersistentPdfGenerateVO pdf = new DmsPersistentPdfGenerateVO(); pdf.setObjectKey("dms-derived/pdf/" + VERSION_ID + "/" + CHECKSUM + ".pdf"); pdf.setSizeBytes(128L); pdf.setChecksumSha256("b".repeat(64)); pdf.setSourceChecksumSha256(CHECKSUM); ActionResult response = new ActionResult<>(); response.setCode(200); response.setData(pdf); when(tasks.claim(eq(TENANT), eq(VERSION_ID), anyString(), eq(8), eq(300))) .thenReturn(1); when(versions.selectByTenantAndId(TENANT, VERSION_ID)).thenReturn(version); when(objects.selectByTenantAndFileVersionId(TENANT, VERSION_ID)).thenReturn(object); when(fileApi.generate(eq("jnpf-dms"), any())).thenReturn(response); job.process(task); verify(completion).succeed(eq(task), anyString(), eq(pdf)); verify(tasks, never()).fail(anyString(), any(), anyString(), anyString(), org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.anyBoolean()); } @Test void retriesWhenSourceChecksumChanged() { DmsPdfGenerationTask task = task(0); when(tasks.claim(eq(TENANT), eq(VERSION_ID), anyString(), eq(8), eq(300))) .thenReturn(1); when(versions.selectByTenantAndId(TENANT, VERSION_ID)) .thenReturn(version("c".repeat(64))); when(objects.selectByTenantAndFileVersionId(TENANT, VERSION_ID)).thenReturn(object()); job.process(task); verify(tasks).fail(eq(TENANT), eq(VERSION_ID), anyString(), eq("持久化 PDF 源文件不存在或已变化"), eq(60), eq(false)); verify(fileApi, never()).generate(anyString(), any()); } @Test void marksTaskFailedAfterLastAttempt() { DmsPdfGenerationTask task = task(7); when(tasks.claim(eq(TENANT), eq(VERSION_ID), anyString(), eq(8), eq(300))) .thenReturn(1); when(versions.selectByTenantAndId(TENANT, VERSION_ID)).thenReturn(version(CHECKSUM)); when(objects.selectByTenantAndFileVersionId(TENANT, VERSION_ID)).thenReturn(object()); when(fileApi.generate(eq("jnpf-dms"), any())).thenThrow(new IllegalStateException("timeout")); job.process(task); verify(tasks).fail(eq(TENANT), eq(VERSION_ID), anyString(), eq("timeout"), eq(21600), eq(true)); } private DmsPdfGenerationTask task(int attempts) { DmsPdfGenerationTask task = new DmsPdfGenerationTask(); task.setTenantId(TENANT); task.setFileVersionId(VERSION_ID); task.setSourceChecksumSha256(CHECKSUM); task.setAttempts(attempts); return task; } private DmsFileVersionEntity version(String checksum) { DmsFileVersionEntity version = new DmsFileVersionEntity(); version.setId(VERSION_ID); version.setTenantId(TENANT); version.setFileName("quality.docx"); version.setMimeType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); version.setSizeBytes(1024L); version.setChecksumSha256(checksum); return version; } private DmsFileObjectEntity object() { DmsFileObjectEntity object = new DmsFileObjectEntity(); object.setFileVersionId(VERSION_ID); object.setTenantId(TENANT); object.setStoragePlatform("seaweedfs-1"); object.setBucketName("jnpf-dms"); object.setBasePath(""); object.setObjectPath("dms/source/"); object.setObjectName("quality.docx"); return object; } private void set(String fieldName, Object value) throws Exception { Field field = DmsPdfGenerationJob.class.getDeclaredField(fieldName); field.setAccessible(true); field.set(job, value); } }