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