task-205: parsed-payload 契约回放(env-gated,真实服务:造 SIMILAR_ASIN 任务+内联载荷 → success/顶层字段 items/allItems/groups/headers/脱敏 apiKey/行数据;未知任务 success:false)+ 已本地真跑

This commit is contained in:
2026-09-05 09:00:50 +08:00
parent 0b83c9a3c1
commit f0f9ba29f0
@@ -0,0 +1,79 @@
package com.nanri.aiimage.modules.similarasin.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.time.Duration;
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-205parsed-payload 契约回放(真实服务,env-gated)。
* CONTRACT_BASE_URL + CONTRACT_DB_* 设则跑、否则跳过(CI 绿)。
* 造 SIMILAR_ASIN 任务 + 内联解析载荷 → GET parsed-payloadsuccess、顶层字段(items/allItems/groups/headers)、
* 行数据、submissionId 关联字段就绪;未知任务返回 success:false(错误语义)。
*/
class SimilarAsinParsedPayloadContractReplayTest {
private static final long TASK_ID = 9000003L;
private static final ObjectMapper MAPPER = new ObjectMapper();
@Test
void parsedPayloadContract() throws Exception {
String base = System.getenv("CONTRACT_BASE_URL");
String dbUrl = System.getenv("CONTRACT_DB_URL");
String dbUser = System.getenv("CONTRACT_DB_USER");
String dbPass = System.getenv("CONTRACT_DB_PASSWORD");
Assumptions.assumeTrue(base != null && dbUrl != null && dbUser != null, "未设 CONTRACT_* 跳过");
seed(dbUrl, dbUser, dbPass);
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
HttpResponse<String> ok = get(client, base + "/api/similar-asin/tasks/" + TASK_ID + "/parsed-payload?user_id=1");
assertEquals(200, ok.statusCode());
JsonNode body = MAPPER.readTree(ok.body());
assertTrue(body.path("success").asBoolean(), ok.body());
JsonNode data = body.path("data");
assertTrue(data.has("aiPrompt") && data.has("apiKey") && data.has("items") && data.has("allItems")
&& data.has("groups") && data.has("headers"), "parsed-payload 顶层字段缺失: " + data);
assertEquals("<redacted>", data.path("apiKey").asText(), "apiKey 已脱敏");
assertTrue(data.path("items").isArray() && data.path("items").size() >= 1, "应有解析行");
assertEquals("B0FAKETEST123", data.path("items").get(0).path("asin").asText());
HttpResponse<String> unknown = get(client, base + "/api/similar-asin/tasks/999999999/parsed-payload?user_id=1");
assertEquals(200, unknown.statusCode());
JsonNode unknownBody = MAPPER.readTree(unknown.body());
assertFalse(unknownBody.path("success").asBoolean(), "未知任务应 success:false");
}
private static void seed(String dbUrl, String user, String pass) throws Exception {
String payload = "{\"aiPrompt\":\"<redacted>\",\"apiKey\":\"<redacted>\",\"imgSwitch\":false,"
+ "\"categorySwitch\":false,\"sourceFiles\":[],\"headers\":[\"asin\",\"keyword\",\"title\"],"
+ "\"items\":[{\"rowIndex\":1,\"asin\":\"B0FAKETEST123\",\"keyword\":\"ring\"}],"
+ "\"groups\":[],\"allItems\":[{\"rowIndex\":1,\"asin\":\"B0FAKETEST123\",\"keyword\":\"ring\"}]}";
try (Connection c = DriverManager.getConnection(dbUrl, user, pass);
Statement s = c.createStatement()) {
s.executeUpdate("INSERT INTO biz_file_task (id,task_no,module_type,task_mode,status,created_at,updated_at,user_id,owner_instance_id,result_json) "
+ "VALUES (" + TASK_ID + ",'SA-REPLAY-" + TASK_ID + "','SIMILAR_ASIN','workbook','RUNNING',NOW(),NOW(),1,'local-smoke','"
+ payload.replace("'", "''") + "') ON DUPLICATE KEY UPDATE module_type='SIMILAR_ASIN',status='RUNNING',user_id=1,result_json=VALUES(result_json)");
}
}
private static HttpResponse<String> get(HttpClient client, String url) throws Exception {
return client.send(HttpRequest.newBuilder(URI.create(url)).GET()
.timeout(Duration.ofSeconds(15)).build(),
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
}
}