task-140: 13 模块 file 字段契约快照(fileStatus/fileError/fileReady 类型与白名单冻结、旧字段仍在、collectdata 自洽)+ 8 条守门测试
This commit is contained in:
+129
@@ -0,0 +1,129 @@
|
||||
package com.nanri.aiimage.modules.task.contract;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.modules.similarasin.model.vo.SimilarAsinHistoryItemVo;
|
||||
import com.nanri.aiimage.modules.similarasin.model.vo.SimilarAsinTaskLightVo;
|
||||
import com.nanri.aiimage.modules.task.model.vo.TaskProgressLightBatchVo;
|
||||
import com.nanri.aiimage.modules.task.model.vo.TaskProgressLightVo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* task-140:13 模块 file 阶段字段契约快照(基线固化,plan 08)。
|
||||
* 共享 TaskProgressLightVo(11 模块)+ SimilarAsinTaskLightVo 的 file 字段
|
||||
* (fileStatus/fileError/fileReady)存在且类型正确;旧字段
|
||||
* (taskId/resultId/downloadUrl/status)仍在;JSON 序列化快照冻结白名单。
|
||||
*/
|
||||
class ResultFilePhaseContractTest {
|
||||
|
||||
/** 13 个业务模块的共享 light VO(collectdata 走同一共享 VO,自洽语义见 test 5)。 */
|
||||
private static final Set<String> SHARED_LIGHT_MODULES = Set.of(
|
||||
"APPEARANCE_PATENT", "SHOP_DATA_CRAWL", "QUERY_ASIN", "WITHDRAW",
|
||||
"PATROL_DELETE", "SHOP_MATCH", "PRODUCT_RISK_RESOLVE", "PRICE_TRACK",
|
||||
"DELETE_BRAND", "PUBLISH", "COLLECT_DATA", "BRAND");
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private static Map<String, Class<?>> fieldsOf(Class<?> type) {
|
||||
return Arrays.stream(type.getDeclaredFields())
|
||||
.filter(field -> !java.lang.reflect.Modifier.isStatic(field.getModifiers()))
|
||||
.collect(Collectors.toMap(Field::getName, Field::getType));
|
||||
}
|
||||
|
||||
private static void assertFileFields(Class<?> voType) {
|
||||
Map<String, Class<?>> fields = fieldsOf(voType);
|
||||
assertEquals(String.class, fields.get("fileStatus"), voType.getSimpleName() + ".fileStatus 必须为 String");
|
||||
assertEquals(String.class, fields.get("fileError"), voType.getSimpleName() + ".fileError 必须为 String");
|
||||
assertEquals(Boolean.class, fields.get("fileReady"), voType.getSimpleName() + ".fileReady 必须为 Boolean");
|
||||
}
|
||||
|
||||
@Test
|
||||
void similarAsinHasFileFields() {
|
||||
assertFileFields(SimilarAsinTaskLightVo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appearancePatentHasFileFields() {
|
||||
assertFileFields(TaskProgressLightVo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shopDataCrawlHasFileFields() {
|
||||
assertFileFields(TaskProgressLightVo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishHasFileFields() {
|
||||
// publish 文件级:共享 VO 的 file 字段由 Job 推导(TaskProgressLightAssembler 语义)
|
||||
assertFileFields(TaskProgressLightVo.class);
|
||||
assertTrue(SHARED_LIGHT_MODULES.contains("PUBLISH"), "publish 必须接入共享 light VO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectDataIsSelfConsistent() {
|
||||
// collectdata 与 11 模块共享同一 TaskProgressLightVo(file 字段齐全、语义自洽)
|
||||
assertFileFields(TaskProgressLightVo.class);
|
||||
Map<String, Class<?>> fields = fieldsOf(TaskProgressLightVo.class);
|
||||
assertEquals(Long.class, fields.get("taskId"));
|
||||
assertEquals(String.class, fields.get("status"));
|
||||
assertNotNull(fields.get("updatedAt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void oldFieldsStillPresent() {
|
||||
Map<String, Class<?>> fields = fieldsOf(SimilarAsinHistoryItemVo.class);
|
||||
assertEquals(Long.class, fields.get("taskId"), "旧字段 taskId 仍在");
|
||||
assertEquals(Long.class, fields.get("resultId"), "旧字段 resultId 仍在");
|
||||
assertEquals(String.class, fields.get("downloadUrl"), "旧字段 downloadUrl 仍在");
|
||||
assertEquals(String.class, fields.get("taskStatus"), "旧字段 taskStatus 仍在(状态语义不变)");
|
||||
assertEquals(String.class, fields.get("fileStatus"), "history 文件级字段仍在");
|
||||
assertEquals(Boolean.class, fields.get("fileReady"), "history fileReady 仍在");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fileFieldTypesAreCorrect() {
|
||||
assertFileFields(TaskProgressLightVo.class);
|
||||
Map<String, Class<?>> similar = fieldsOf(SimilarAsinTaskLightVo.class);
|
||||
assertEquals(String.class, similar.get("fileStatus"));
|
||||
assertEquals(Boolean.class, similar.get("fileReady"));
|
||||
assertEquals(String.class, similar.get("fileError"));
|
||||
assertEquals(String.class, similar.get("status"));
|
||||
assertEquals(Long.class, similar.get("taskId"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void contractFrozenAsJsonSnapshot() throws Exception {
|
||||
TaskProgressLightVo item = new TaskProgressLightVo();
|
||||
item.setTaskId(3938L);
|
||||
item.setStatus("SUCCESS");
|
||||
item.setFileStatus("SUCCESS");
|
||||
item.setFileError(null);
|
||||
item.setFileReady(true);
|
||||
item.setUpdatedAt("2026-09-02T05:00:00");
|
||||
|
||||
TaskProgressLightBatchVo batch = new TaskProgressLightBatchVo();
|
||||
batch.setItems(java.util.List.of(item));
|
||||
batch.setMissingTaskIds(java.util.List.of());
|
||||
|
||||
String json = objectMapper.writeValueAsString(batch);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> root = objectMapper.readValue(json, Map.class);
|
||||
assertTrue(root.containsKey("items"), "响应必须含 items");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> first = ((java.util.List<Map<String, Object>>) root.get("items")).get(0);
|
||||
assertEquals(Set.of("taskId", "status", "statusCode", "fileStatus", "fileError", "fileReady", "updatedAt"),
|
||||
first.keySet(), "light 响应键集必须精确匹配白名单(无明细/payload/result 内容)");
|
||||
assertEquals(Boolean.TRUE, first.get("fileReady"));
|
||||
assertEquals("SUCCESS", first.get("fileStatus"));
|
||||
assertEquals("SUCCESS", first.get("status"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user