task-157: 任务存在结果缺失巡检(终态任务无 file_result 检出、运行中忽略、只读、可重复)+ 8 条测试

This commit is contained in:
2026-09-02 06:57:50 +08:00
parent d773748ae9
commit 9144bce33f
2 changed files with 235 additions and 0 deletions
@@ -0,0 +1,154 @@
package com.nanri.aiimage.modules.task.service;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* task-157:任务存在结果缺失巡检契约(plan 09)。
* 终态(SUCCESS/FAILED)任务无 file_result 行 → 检出;运行中忽略;只读;可重复。
*/
@ExtendWith(MockitoExtension.class)
class TaskResultMissingInspectorTest {
private static final Long MISSING_TASK_ID = 7001L;
private static final Long WITH_RESULT_TASK_ID = 7101L;
private static final Long RUNNING_TASK_ID = 7201L;
@Mock private FileTaskMapper fileTaskMapper;
@Mock private FileResultMapper fileResultMapper;
private TaskResultMissingInspector inspector;
@BeforeAll
static void initializeMybatisMetadata() {
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
TableInfoHelper.initTableInfo(assistant, FileTaskEntity.class);
TableInfoHelper.initTableInfo(assistant, FileResultEntity.class);
}
@BeforeEach
void setUp() {
inspector = new TaskResultMissingInspector(fileTaskMapper, fileResultMapper);
lenient().when(fileResultMapper.selectList(any())).thenReturn(List.of());
}
private FileTaskEntity task(Long id, String status) {
FileTaskEntity task = new FileTaskEntity();
task.setId(id);
task.setModuleType("SIMILAR_ASIN");
task.setStatus(status);
task.setUpdatedAt(LocalDateTime.now());
return task;
}
@Test
void missingResultDetected() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of(task(MISSING_TASK_ID, "SUCCESS")));
var report = inspector.inspectTasksMissingResult(50);
assertEquals(1, report.entries().size());
assertEquals(MISSING_TASK_ID, report.entries().getFirst().taskId());
}
@Test
void taskWithResultNotReported() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of(task(WITH_RESULT_TASK_ID, "SUCCESS")));
FileResultEntity result = new FileResultEntity();
result.setTaskId(WITH_RESULT_TASK_ID);
when(fileResultMapper.selectList(any())).thenReturn(List.of(result));
var report = inspector.inspectTasksMissingResult(50);
assertTrue(report.isEmpty());
}
@Test
void successWithoutResultIsReported() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of(task(MISSING_TASK_ID, "SUCCESS")));
var report = inspector.inspectTasksMissingResult(50);
assertEquals("SUCCESS", report.entries().getFirst().status());
}
@Test
void runningTasksAreIgnored() {
// 巡检只查终态任务:RUNNING 不在查询范围内(mapper 断言终态 IN 条件)
when(fileTaskMapper.selectList(any())).thenReturn(List.of());
inspector.inspectTasksMissingResult(50);
var wrapper = org.mockito.ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper.class);
verify(fileTaskMapper).selectList(wrapper.capture());
assertTrue(wrapper.getValue().getSqlSegment().contains("IN"), "只查终态任务: " + wrapper.getValue().getSqlSegment());
assertFalse(wrapper.getValue().getSqlSegment().contains("RUNNING"), "运行中任务不参与巡检");
}
@Test
void emptyReportWhenNoTasks() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of());
var report = inspector.inspectTasksMissingResult(50);
assertTrue(report.isEmpty());
}
@Test
void readOnlyDoesNotModifyAnything() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of(task(MISSING_TASK_ID, "FAILED")));
inspector.inspectTasksMissingResult(50);
verify(fileTaskMapper, never()).update(any(), any());
verify(fileResultMapper, never()).update(any(), any());
verify(fileTaskMapper, never()).delete(any());
}
@Test
void reportIncludesDetails() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of(task(MISSING_TASK_ID, "FAILED")));
var report = inspector.inspectTasksMissingResult(50);
TaskResultMissingInspector.MissingResultEntry entry = report.entries().getFirst();
assertEquals(MISSING_TASK_ID, entry.taskId());
assertEquals("SIMILAR_ASIN", entry.moduleType());
assertEquals("FAILED", entry.status());
assertNotNull(entry.updatedAt());
}
@Test
void rerunIsSafeAndStable() {
when(fileTaskMapper.selectList(any())).thenReturn(List.of(task(MISSING_TASK_ID, "SUCCESS")));
var first = inspector.inspectTasksMissingResult(50);
var second = inspector.inspectTasksMissingResult(50);
assertEquals(first.entries().size(), second.entries().size());
assertEquals(first.entries().getFirst().taskId(), second.entries().getFirst().taskId());
}
}