task-141: similarasin file 进度合成单测(LLM/Python 上传/snapshot 三路径、fileReady 恒 100%、除零与状态边界)+ 8 条测试
This commit is contained in:
+212
@@ -0,0 +1,212 @@
|
||||
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.TaskProgressSnapshotEntity;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* task-141:similarasin file 进度合成逻辑单测(plan 08)。
|
||||
* attachFileProgress 三路径:LLM 阶段(completed/pending>0)、Python 上传阶段、
|
||||
* snapshot 百分比(上传完成无 LLM);fileReady=true 时恒 100%;边界安全。
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FileProgressSynthesisTest {
|
||||
|
||||
private static final Long TASK_ID = 2222L;
|
||||
private static final Long RESULT_ID = 2223L;
|
||||
|
||||
@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(Runnable configure, FileResultEntity row, FileTaskEntity task,
|
||||
TaskFileJobEntity job) {
|
||||
configure.run();
|
||||
return assembler.buildHistoryItems(List.of(row),
|
||||
task == null ? Map.of() : Map.of(TASK_ID, task),
|
||||
job == null ? Map.of() : Map.of(RESULT_ID, job)).getFirst();
|
||||
}
|
||||
|
||||
private FileResultEntity resultRow() {
|
||||
FileResultEntity row = new FileResultEntity();
|
||||
row.setId(RESULT_ID);
|
||||
row.setTaskId(TASK_ID);
|
||||
row.setSourceFilename("source.xlsx");
|
||||
row.setResultFileUrl(null);
|
||||
row.setSuccess(0);
|
||||
return row;
|
||||
}
|
||||
|
||||
private FileTaskEntity runningTask() {
|
||||
FileTaskEntity task = new FileTaskEntity();
|
||||
task.setId(TASK_ID);
|
||||
task.setModuleType("SIMILAR_ASIN");
|
||||
task.setStatus("RUNNING");
|
||||
return task;
|
||||
}
|
||||
|
||||
@Test
|
||||
void fileReadyPathIsAlwaysOneHundredPercent() {
|
||||
FileResultEntity row = resultRow();
|
||||
row.setResultFileUrl("oss://result/source-result.xlsx");
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, row, runningTask(), null);
|
||||
|
||||
assertEquals(100, vo.getFileProgressPercent());
|
||||
assertEquals(1, vo.getFileProgressCurrent());
|
||||
assertEquals(1, vo.getFileProgressTotal());
|
||||
assertEquals("结果文件已生成", vo.getFileProgressMessage());
|
||||
assertEquals(Boolean.TRUE, vo.getFileReady());
|
||||
}
|
||||
|
||||
@Test
|
||||
void llmProgressPathWhenCompletedExists() {
|
||||
when(taskScopeStateMapper.selectCount(any())).thenReturn(2L, 0L);
|
||||
lenient().when(taskChunkMapper.selectCount(any())).thenReturn(1L);
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), runningTask(), null);
|
||||
|
||||
assertNotNull(vo.getFileProgressPercent(), "LLM 阶段必须合成进度");
|
||||
assertEquals(2, vo.getFileProgressCurrent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pythonUploadPathWhenNoLlmStates() {
|
||||
lenient().when(taskChunkMapper.selectCount(any())).thenReturn(1L);
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), runningTask(), null);
|
||||
|
||||
assertNotNull(vo.getFileProgressPercent(), "Python 上传阶段必须合成进度");
|
||||
assertNotNull(vo.getFileProgressMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void snapshotPercentPathWhenUploadComplete() {
|
||||
TaskScopeStateEntity scope = new TaskScopeStateEntity();
|
||||
scope.setTaskId(TASK_ID);
|
||||
scope.setCompleted(1);
|
||||
when(taskScopeStateMapper.selectList(any())).thenReturn(List.of(scope));
|
||||
TaskProgressSnapshotEntity snapshot = new TaskProgressSnapshotEntity();
|
||||
snapshot.setTotalCount(10);
|
||||
snapshot.setSuccessCount(5);
|
||||
snapshot.setMessage("组装中");
|
||||
when(taskProgressSnapshotService.find(eq(TASK_ID), eq("SIMILAR_ASIN"))).thenReturn(snapshot);
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), runningTask(), null);
|
||||
|
||||
assertNotNull(vo.getFileProgressPercent(), "上传完成无 LLM 时走 snapshot 百分比");
|
||||
assertEquals(5, vo.getFileProgressCurrent());
|
||||
assertEquals(10, vo.getFileProgressTotal());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pendingOnlyStillUsesLlmPath() {
|
||||
when(taskScopeStateMapper.selectCount(any())).thenReturn(0L, 2L);
|
||||
lenient().when(taskChunkMapper.selectCount(any())).thenReturn(1L);
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), runningTask(), null);
|
||||
|
||||
assertNotNull(vo.getFileProgressPercent(), "pending>0 也走 LLM 路径");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uploadCompleteWithoutLlmOrSnapshotSetsNoProgress() {
|
||||
TaskScopeStateEntity scope = new TaskScopeStateEntity();
|
||||
scope.setTaskId(TASK_ID);
|
||||
scope.setCompleted(1);
|
||||
when(taskScopeStateMapper.selectList(any())).thenReturn(List.of(scope));
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), runningTask(), null);
|
||||
|
||||
assertNull(vo.getFileProgressPercent(), "无 LLM 无 snapshot 不合成进度");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonRunningTaskWithoutProgressSkipsSynthesis() {
|
||||
FileTaskEntity failed = runningTask();
|
||||
failed.setStatus("FAILED");
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), failed, null);
|
||||
|
||||
assertNull(vo.getFileProgressPercent(), "非 RUNNING 任务不合成上传进度");
|
||||
}
|
||||
|
||||
@Test
|
||||
void zeroTotalSnapshotIsDivisionSafe() {
|
||||
TaskScopeStateEntity scope = new TaskScopeStateEntity();
|
||||
scope.setTaskId(TASK_ID);
|
||||
scope.setCompleted(1);
|
||||
when(taskScopeStateMapper.selectList(any())).thenReturn(List.of(scope));
|
||||
TaskProgressSnapshotEntity snapshot = new TaskProgressSnapshotEntity();
|
||||
snapshot.setTotalCount(0);
|
||||
snapshot.setSuccessCount(0);
|
||||
when(taskProgressSnapshotService.find(eq(TASK_ID), eq("SIMILAR_ASIN"))).thenReturn(snapshot);
|
||||
|
||||
SimilarAsinHistoryItemVo vo = build(() -> {
|
||||
}, resultRow(), runningTask(), null);
|
||||
|
||||
assertNull(vo.getFileProgressPercent(), "total=0 时不除零不合成");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user