task-142: fileReady/fileStatus 语义契约(URL 判定、无 job SUCCESS/null、job 状态与错误映射、空白边界)+ 8 条测试
This commit is contained in:
+168
@@ -0,0 +1,168 @@
|
|||||||
|
package com.nanri.aiimage.modules.similarasin.service.support;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||||
|
import com.nanri.aiimage.modules.similarasin.model.vo.SimilarAsinHistoryItemVo;
|
||||||
|
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.FileTaskEntity;
|
||||||
|
import com.nanri.aiimage.modules.task.model.entity.TaskFileJobEntity;
|
||||||
|
import com.nanri.aiimage.modules.task.model.entity.TaskScopeStateEntity;
|
||||||
|
import com.nanri.aiimage.modules.task.service.TaskProgressSnapshotService;
|
||||||
|
import com.nanri.aiimage.modules.task.service.TransientPayloadStorageService;
|
||||||
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
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 java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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-142:fileReady/fileStatus 语义契约(plan 08)。
|
||||||
|
* fileReady = resultFileUrl 非空;无 job 时 fileStatus = ready?SUCCESS:null;
|
||||||
|
* 有 job 时 fileStatus = job.status;fileError = job.errorMessage。
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class FilePhaseSemanticsTest {
|
||||||
|
|
||||||
|
private static final Long TASK_ID = 1212L;
|
||||||
|
private static final Long RESULT_ID = 1213L;
|
||||||
|
|
||||||
|
@Mock private TaskScopeStateMapper taskScopeStateMapper;
|
||||||
|
@Mock private TaskChunkMapper taskChunkMapper;
|
||||||
|
@Mock private FileTaskMapper fileTaskMapper;
|
||||||
|
@Mock private TaskProgressSnapshotService taskProgressSnapshotService;
|
||||||
|
@Mock private OssStorageService ossStorageService;
|
||||||
|
@Mock private TransientPayloadStorageService transientPayloadStorageService;
|
||||||
|
@Mock private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private SimilarAsinHistoryAssembler assembler;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void initializeMybatisMetadata() {
|
||||||
|
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
||||||
|
TableInfoHelper.initTableInfo(assistant, FileResultEntity.class);
|
||||||
|
TableInfoHelper.initTableInfo(assistant, TaskScopeStateEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
assembler = new SimilarAsinHistoryAssembler(taskScopeStateMapper, taskChunkMapper, fileTaskMapper,
|
||||||
|
taskProgressSnapshotService, ossStorageService, transientPayloadStorageService, objectMapper);
|
||||||
|
lenient().when(taskScopeStateMapper.selectCount(any())).thenReturn(0L);
|
||||||
|
lenient().when(taskScopeStateMapper.selectList(any())).thenReturn(List.of());
|
||||||
|
lenient().when(taskProgressSnapshotService.find(any(), any())).thenReturn(null);
|
||||||
|
lenient().when(taskChunkMapper.selectCount(any())).thenReturn(0L);
|
||||||
|
lenient().when(ossStorageService.generateFreshDownloadUrl(any())).thenReturn("https://dl.example/x.xlsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimilarAsinHistoryItemVo build(FileResultEntity row, TaskFileJobEntity job) {
|
||||||
|
FileTaskEntity task = new FileTaskEntity();
|
||||||
|
task.setId(TASK_ID);
|
||||||
|
task.setModuleType("SIMILAR_ASIN");
|
||||||
|
task.setStatus("RUNNING");
|
||||||
|
return assembler.buildHistoryItems(List.of(row), Map.of(TASK_ID, task),
|
||||||
|
job == null ? Map.of() : Map.of(RESULT_ID, job)).getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileResultEntity row(String url) {
|
||||||
|
FileResultEntity row = new FileResultEntity();
|
||||||
|
row.setId(RESULT_ID);
|
||||||
|
row.setTaskId(TASK_ID);
|
||||||
|
row.setSourceFilename("source.xlsx");
|
||||||
|
row.setResultFileUrl(url);
|
||||||
|
row.setSuccess(0);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskFileJobEntity job(String status, String error) {
|
||||||
|
TaskFileJobEntity job = new TaskFileJobEntity();
|
||||||
|
job.setId(901L);
|
||||||
|
job.setTaskId(TASK_ID);
|
||||||
|
job.setResultId(RESULT_ID);
|
||||||
|
job.setStatus(status);
|
||||||
|
job.setErrorMessage(error);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readyTrueWhenUrlPresent() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row("oss://result/a.xlsx"), null);
|
||||||
|
|
||||||
|
assertEquals(Boolean.TRUE, vo.getFileReady());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readyFalseWhenUrlBlank() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row(null), null);
|
||||||
|
|
||||||
|
assertFalse(Boolean.TRUE.equals(vo.getFileReady()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void noJobWithReadyMapsToSuccessStatus() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row("oss://result/a.xlsx"), null);
|
||||||
|
|
||||||
|
assertEquals("SUCCESS", vo.getFileStatus(), "无 job 且 ready 时 fileStatus=SUCCESS");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void noJobWithoutReadyMapsToNullStatus() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row(null), null);
|
||||||
|
|
||||||
|
assertNull(vo.getFileStatus(), "无 job 且未 ready 时 fileStatus=null");
|
||||||
|
assertNull(vo.getFileJobId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jobStatusMappedDirectly() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row("oss://result/a.xlsx"), job("RUNNING", null));
|
||||||
|
|
||||||
|
assertEquals("RUNNING", vo.getFileStatus(), "有 job 时 fileStatus=job.status");
|
||||||
|
assertEquals(901L, vo.getFileJobId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jobErrorMappedDirectly() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row(null), job("FAILED", "组装失败: 超时"));
|
||||||
|
|
||||||
|
assertEquals("组装失败: 超时", vo.getFileError(), "fileError=job.errorMessage");
|
||||||
|
assertEquals("FAILED", vo.getFileStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void blankWhitespaceUrlIsNotReady() {
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row(" "), null);
|
||||||
|
|
||||||
|
assertFalse(Boolean.TRUE.equals(vo.getFileReady()), "空白字符串不算 ready");
|
||||||
|
assertNull(vo.getFileStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void semanticsFrozenCombined() {
|
||||||
|
// 组合快照:URL + SUCCESS job → ready=true、status=SUCCESS、error 映射、jobId 携带
|
||||||
|
SimilarAsinHistoryItemVo vo = build(row("oss://result/a.xlsx"), job("SUCCESS", null));
|
||||||
|
|
||||||
|
assertEquals(Boolean.TRUE, vo.getFileReady());
|
||||||
|
assertEquals("SUCCESS", vo.getFileStatus());
|
||||||
|
assertNull(vo.getFileError());
|
||||||
|
assertEquals(901L, vo.getFileJobId());
|
||||||
|
assertTrue(vo.getFileReady());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user