task-143: publish 文件级 file 状态语义契约(ready 由 result.success+url 推导、job 字段独立、多文件混合、无结果 null)+ 8 条测试
This commit is contained in:
+170
@@ -0,0 +1,170 @@
|
||||
package com.nanri.aiimage.modules.task.contract;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.config.InstanceMetadata;
|
||||
import com.nanri.aiimage.modules.file.service.LocalFileStorageService;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import com.nanri.aiimage.modules.publish.mapper.PublishFileMapper;
|
||||
import com.nanri.aiimage.modules.publish.mapper.PublishItemMapper;
|
||||
import com.nanri.aiimage.modules.publish.model.vo.PublishResultVo;
|
||||
import com.nanri.aiimage.modules.publish.service.PublishTaskService;
|
||||
import com.nanri.aiimage.modules.publish.service.PublishWorkbookService;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
|
||||
import com.nanri.aiimage.modules.task.mapper.TaskChunkMapper;
|
||||
import com.nanri.aiimage.modules.task.mapper.TaskScopeStateMapper;
|
||||
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskFileJobEntity;
|
||||
import com.nanri.aiimage.modules.task.service.TaskDistributedLockService;
|
||||
import com.nanri.aiimage.modules.task.service.TaskFileJobService;
|
||||
import com.nanri.aiimage.modules.task.service.TaskProgressLightAssembler;
|
||||
import com.nanri.aiimage.modules.task.service.TransientPayloadStorageService;
|
||||
import com.nanri.aiimage.modules.ziniao.service.ZiniaoShopSwitchService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* task-143:publish 文件级 file 状态语义契约(plan 08)。
|
||||
* publish 的 file 状态为文件级(PublishResultVo.fileReady 由 result.success+url
|
||||
* 推导,PublishItemsPageVo.fileStatus = file.status),与其余模块 job 级不同;
|
||||
* 语义固化。
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PublishFileLevelStatusTest {
|
||||
|
||||
private static final Long RESULT_ID = 9201L;
|
||||
|
||||
@Mock private LocalFileStorageService localFileStorageService;
|
||||
@Mock private ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
@Mock private PublishWorkbookService workbookService;
|
||||
@Mock private PublishFileMapper publishFileMapper;
|
||||
@Mock private PublishItemMapper publishItemMapper;
|
||||
@Mock private FileTaskMapper fileTaskMapper;
|
||||
@Mock private FileResultMapper fileResultMapper;
|
||||
@Mock private TaskChunkMapper taskChunkMapper;
|
||||
@Mock private TaskScopeStateMapper taskScopeStateMapper;
|
||||
@Mock private TaskFileJobService taskFileJobService;
|
||||
@Mock private TaskDistributedLockService taskDistributedLockService;
|
||||
@Mock private TransientPayloadStorageService transientPayloadStorageService;
|
||||
@Mock private OssStorageService ossStorageService;
|
||||
@Mock private ObjectMapper objectMapper;
|
||||
@Mock private TransactionTemplate transactionTemplate;
|
||||
@Mock private InstanceMetadata instanceMetadata;
|
||||
@Mock private TaskProgressLightAssembler taskProgressLightAssembler;
|
||||
|
||||
private PublishTaskService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new PublishTaskService(
|
||||
localFileStorageService, ziniaoShopSwitchService, workbookService, publishFileMapper,
|
||||
publishItemMapper, fileTaskMapper, fileResultMapper, taskChunkMapper, taskScopeStateMapper,
|
||||
taskFileJobService, taskDistributedLockService, transientPayloadStorageService, ossStorageService,
|
||||
objectMapper, transactionTemplate, instanceMetadata, taskProgressLightAssembler);
|
||||
lenient().when(ossStorageService.generateFreshDownloadUrl(any()))
|
||||
.thenReturn("https://dl.example/publish-result.zip");
|
||||
}
|
||||
|
||||
private PublishResultVo toVo(FileResultEntity result, TaskFileJobEntity job) {
|
||||
return (PublishResultVo) ReflectionTestUtils.invokeMethod(service, "toResultVo", result, job);
|
||||
}
|
||||
|
||||
private FileResultEntity result(int success, String url) {
|
||||
FileResultEntity result = new FileResultEntity();
|
||||
result.setId(RESULT_ID);
|
||||
result.setSuccess(success);
|
||||
result.setResultFileUrl(url);
|
||||
result.setResultFilename("PUBLISH-1_上架结果.zip");
|
||||
return result;
|
||||
}
|
||||
|
||||
private TaskFileJobEntity job() {
|
||||
TaskFileJobEntity job = new TaskFileJobEntity();
|
||||
job.setId(9301L);
|
||||
job.setStatus("RUNNING");
|
||||
job.setRetryCount(1);
|
||||
job.setErrorMessage("写入慢");
|
||||
return job;
|
||||
}
|
||||
|
||||
@Test
|
||||
void readyTrueWhenSuccessAndUrlPresent() {
|
||||
PublishResultVo vo = toVo(result(1, "oss://result/p.zip"), null);
|
||||
|
||||
assertTrue(vo.getFileReady());
|
||||
assertEquals("https://dl.example/publish-result.zip", vo.getDownloadUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readyFalseWhenUrlMissing() {
|
||||
PublishResultVo vo = toVo(result(1, null), null);
|
||||
|
||||
assertFalse(vo.getFileReady());
|
||||
assertNull(vo.getDownloadUrl(), "未 ready 不生成下载直链");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedFileNeverReady() {
|
||||
PublishResultVo vo = toVo(result(0, "oss://result/p.zip"), null);
|
||||
|
||||
assertFalse(vo.getFileReady(), "success=0 的文件不算 ready");
|
||||
assertNull(vo.getDownloadUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
void perFileReadyIsIndependent() {
|
||||
PublishResultVo ready = toVo(result(1, "oss://a.zip"), null);
|
||||
PublishResultVo pending = toVo(result(1, null), null);
|
||||
|
||||
assertTrue(ready.getFileReady());
|
||||
assertFalse(pending.getFileReady());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mixedFilesShowMixedReadyState() {
|
||||
PublishResultVo failed = toVo(result(0, "oss://f.zip"), null);
|
||||
PublishResultVo ready = toVo(result(1, "oss://r.zip"), null);
|
||||
|
||||
assertFalse(failed.getFileReady());
|
||||
assertTrue(ready.getFileReady());
|
||||
}
|
||||
|
||||
@Test
|
||||
void noResultReturnsNull() {
|
||||
assertNull(toVo(null, null), "无结果记录不返回 VO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jobFieldsMappedWhenJobPresent() {
|
||||
PublishResultVo vo = toVo(result(1, "oss://p.zip"), job());
|
||||
|
||||
assertEquals(9301L, vo.getFileJobId());
|
||||
assertEquals("RUNNING", vo.getFileJobStatus());
|
||||
assertEquals(1, vo.getFileJobRetryCount());
|
||||
assertEquals("写入慢", vo.getFileJobError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void semanticsFrozen() {
|
||||
PublishResultVo vo = toVo(result(1, "oss://p.zip"), job());
|
||||
|
||||
// 快照:ready 由 result 行推导(非 job),job 字段独立携带(文件级语义)
|
||||
assertTrue(vo.getFileReady());
|
||||
assertEquals("PUBLISH-1_上架结果.zip", vo.getResultFilename());
|
||||
assertEquals(RESULT_ID, vo.getResultId());
|
||||
assertEquals("RUNNING", vo.getFileJobStatus());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user