Files
crawler-plugin/backend-java/src/test/java/com/nanri/aiimage/config/PythonContractFixtureTest.java
T
huangzd1997 92bd114e71 fix(module_type): 对齐 module_type 规范大写并加防回归守卫(防 similarasin/SIMILAR_ASIN 之类漂移)
- heartbeat 夹具 moduleType 改规范大写 SIMILAR_ASIN 并加断言锁定
- ModuleTypeAlignmentTest:扫描 src/main——MODULE_TYPE 常量须大写下划线、setModuleType("小写") 禁止(仅允许 legacy collectdata)——守卫写入/常量/夹具三者一致
- 全量扫描结果:生产代码写入均用常量大写,无存量漂移
2026-09-05 08:54:06 +08:00

118 lines
5.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.nanri.aiimage.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* task-202Python-contract 夹具准备契约。
* 校验 python-contract/*.json 存在、为合法 JSON、顶层字段与真实 DTO 对齐、已脱敏(无真实凭据)、
* 可复现;字段来源与脱敏口径见同目录 README.md。
*/
class PythonContractFixtureTest {
private static final Path DIR = Path.of(System.getProperty("user.dir"),
"src/test/resources/python-contract");
private static final ObjectMapper MAPPER = new ObjectMapper();
private JsonNode json(String name) throws IOException {
return MAPPER.readTree(Files.readAllBytes(DIR.resolve(name)));
}
private static Set<String> keys(JsonNode node) {
List<String> names = new ArrayList<>();
node.fieldNames().forEachRemaining(names::add);
return Set.copyOf(names);
}
@Test
void fixtureResultRequestExists() throws IOException {
JsonNode n = json("result.request.json");
assertEquals("sim-20260905-0001", n.path("submissionId").asText());
}
@Test
void fixtureItemsExists() throws IOException {
JsonNode n = json("items.response.json");
assertTrue(n.path("items").isArray() && n.path("items").size() >= 1);
}
@Test
void fixtureParsedPayloadExists() throws IOException {
JsonNode n = json("parsed_payload.response.json");
assertTrue(n.has("allItems") && n.has("groups") && n.has("items"));
}
@Test
void fixtureHeartbeatExists() throws IOException {
JsonNode req = json("heartbeat.request.json");
JsonNode resp = json("heartbeat.response.json");
assertTrue(req.has("moduleType") && req.has("phase"));
assertTrue(resp.has("alive") && resp.has("status"));
assertEquals("SIMILAR_ASIN", req.path("moduleType").asText(),
"夹具 moduleType 须用规范大写(与服务 MODULE_TYPE 常量一致,防小写漂移 bug");
}
@Test
void fixtureSanitizedNoRealCredentials() throws IOException {
String all = String.join("\n", List.of(
Files.readString(DIR.resolve("parsed_payload.response.json")),
Files.readString(DIR.resolve("result.request.json"))));
assertFalse(all.contains("WTFrb"), "不得出现真实 DB/内部凭据");
assertFalse(all.toLowerCase().contains("bearer "), "不得出现真实 token");
JsonNode parsed = json("parsed_payload.response.json");
assertEquals("<redacted>", parsed.path("apiKey").asText(), "apiKey 必须占位");
assertFalse(parsed.path("aiPrompt").asText().toLowerCase().contains("真实"),
"aiPrompt 不得含真实内容");
}
@Test
void fixtureShapeValidJson() throws IOException {
for (String name : List.of("heartbeat.request.json", "heartbeat.response.json",
"items.response.json", "result.request.json", "parsed_payload.response.json")) {
JsonNode n = json(name);
assertTrue(n.isContainerNode(), name + " 应为 JSON 对象/数组");
}
}
@Test
void fixtureFieldsDocumentedByReadme() throws IOException {
String readme = Files.readString(DIR.resolve("README.md"));
assertTrue(readme.contains("TaskHeartbeatRequest") && readme.contains("CollectDataItemsPageVo")
&& readme.contains("SimilarAsinSubmitResultRequest")
&& readme.contains("SimilarAsinParsedPayloadDto"), "README 应说明字段来源");
assertTrue(readme.contains("脱敏"), "README 应说明脱敏");
}
@Test
void fixtureHeartbeatTopKeysMatchDto() throws IOException {
assertEquals(Set.of("moduleType", "phase", "current", "total", "collectStage",
"currentKeyword", "searchCurrentPage", "searchTotalPages",
"detailProcessedAsins", "detailTotalAsins"),
keys(json("heartbeat.request.json")), "心跳请求顶层字段应与 TaskHeartbeatRequest 对齐");
assertEquals(Set.of("alive", "status", "moduleType", "message"),
keys(json("heartbeat.response.json")), "心跳响应应与 TaskHeartbeatVo 对齐");
}
@Test
void fixtureReproducibleStable() throws IOException {
for (String name : List.of("heartbeat.request.json", "items.response.json",
"result.request.json", "parsed_payload.response.json")) {
String a = Files.readString(DIR.resolve(name));
String b = Files.readString(DIR.resolve(name));
assertEquals(a, b, name + " 应可复现");
}
}
}