test(api): 补齐天猫恢复审计与指标回归

This commit is contained in:
renzhiye 2026-04-03 14:07:16 +08:00
parent f8085888c1
commit 8c4d057202
2 changed files with 112 additions and 3 deletions

View File

@ -73,7 +73,7 @@
- [ ] `S3-03` 阻塞恢复与 `L3 Browser Recovery` 落地(进行中)
- [ ] `S3-04` 双平台候选确认与执行控制台落地(进行中:页面与状态展示已具备,真实并发执行待补)
- [x] `S3-05` `PartialCompleted``Blocked``Failed` 汇总规则落地
- [ ] `S3-06` 双平台主回归包落地(进行中:已新增 JD + 天猫 live 搜索、确认、执行、报告的主链 API 回归,并覆盖 `tmall SearchBlocked + jd Completed``tmall NoResult + jd Completed``tmall Blocked + jd Completed``tmall Blocked -> retry success -> report v2``tmall Blocked -> retry blocked -> report unchanged`,以及 `tmall SearchBlocked` 恢复后二次确认仅补跑新恢复平台的回归,待继续补更多失败/恢复组合)
- [ ] `S3-06` 双平台主回归包落地(进行中:已新增 JD + 天猫 live 搜索、确认、执行、报告的主链 API 回归,并覆盖 `tmall SearchBlocked + jd Completed``tmall NoResult + jd Completed``tmall Blocked + jd Completed``tmall Blocked -> retry success -> report v2``tmall Blocked -> retry blocked -> report unchanged``tmall SearchBlocked -> retry success -> audit/metrics`,以及 `tmall SearchBlocked` 恢复后二次确认仅补跑新恢复平台的回归,待继续补更多失败/恢复组合)
## `S4`
@ -86,7 +86,7 @@
## `S5`
- [ ] `S5-01` 平台级定向重试稳定化(进行中)
- [ ] `S5-01` 平台级定向重试稳定化(进行中:已补天猫 `SearchBlocked` 恢复后的审计与 `retryCount/recoveryCount` 回归,待继续扩展更多失败来源与版本差异检测
- [ ] `S5-02` 性能与成本优化(未开始)
- [ ] `S5-03` UAT 与试运行任务集执行(未开始)
- [ ] `S5-04` 部署、值守、排障与热修手册落地(未开始)
@ -96,6 +96,6 @@
- [ ] `X-01` 上下游文档变更同步(进行中)
- [ ] `X-02` 安全与合规检查(未开始)
- [ ] `X-03` 测试资产维护(进行中:已补天猫搜索解析/服务回归,以及双平台 live 主链、`SearchBlocked``NoResult``Blocked``Blocked -> retry success``Blocked -> retry blocked -> report unchanged``SearchBlocked` 恢复后二次确认不重复执行已完成平台的回归,真实 fixture/HAR 待补)
- [ ] `X-03` 测试资产维护(进行中:已补天猫搜索解析/服务回归,以及双平台 live 主链、`SearchBlocked``NoResult``Blocked``Blocked -> retry success``Blocked -> retry blocked -> report unchanged``SearchBlocked -> retry success -> audit/metrics``SearchBlocked` 恢复后二次确认不重复执行已完成平台的回归,真实 fixture/HAR 待补)
- [ ] `X-04` 设计一致性与可访问性检查(进行中)
- [ ] `X-05` 观测指标复盘(未开始)

View File

@ -903,6 +903,115 @@ describe("Dual-platform live loop", () => {
await app.close();
});
it("records recovery audit entries and retry metrics when a SearchBlocked Tmall platform recovers", async () => {
let allowTmallSearch = false;
const tmallLiveService = createTmallLiveServiceStub({
async previewSearch(query) {
if (!allowTmallSearch) {
const error = new Error("Tmall search session appears invalid.") as Error & {
statusCode: number;
};
error.statusCode = 409;
throw error;
}
return {
query,
source: "html",
candidateCount: 1,
candidates: [
{
candidateId: "tmall-934454505228",
platform: "tmall",
title: "Apple iPhone 15",
price: 4399,
priceLabel: "CNY 4399",
storeName: "Apple 官方旗舰店",
productUrl: "https://detail.tmall.com/item.htm?id=934454505228",
imageUrl: "https://img.alicdn.com/example.jpg",
salesHint: "已售 70万+",
specLabel: "128GB",
highlights: ["天猫", "官方旗舰店"]
}
]
};
}
});
const app = createServer({
jdLiveService: createJdLiveServiceStub(),
tmallLiveService
});
await app.ready();
await importDualLiveSessions(app);
const createdTask = await createTask(app, "iPhone 15");
expect(
createdTask.platformRuns.find((run: { platform: string }) => run.platform === "tmall")
).toMatchObject({
platform: "tmall",
status: "SearchBlocked"
});
allowTmallSearch = true;
const retryResponse = await app.inject({
method: "POST",
url: `/api/tasks/${createdTask.taskId}/platforms/tmall/retry`
});
expect(retryResponse.statusCode).toBe(200);
expect(
retryResponse
.json()
.task.platformRuns.find((run: { platform: string }) => run.platform === "tmall")
).toMatchObject({
platform: "tmall",
status: "AwaitingSelection"
});
const auditResponse = await app.inject({
method: "GET",
url: `/api/tasks/${createdTask.taskId}/audit`
});
expect(auditResponse.statusCode).toBe(200);
expect(auditResponse.json().audit).toEqual(
expect.arrayContaining([
expect.objectContaining({
action: "platform.retry_started",
platform: "tmall"
}),
expect.objectContaining({
action: "task.recovery_completed",
platform: "tmall"
}),
expect.objectContaining({
action: "platform.retry_completed",
platform: "tmall"
})
])
);
const overviewResponse = await app.inject({
method: "GET",
url: "/api/observability/overview"
});
expect(overviewResponse.statusCode).toBe(200);
expect(
overviewResponse
.json()
.overview.platformRuns.find((metric: { platform: string }) => metric.platform === "tmall")
).toMatchObject({
platform: "tmall",
retryCount: 1,
recoveryCount: 1
});
expect(overviewResponse.json().overview.audits.recoveryActions).toBe(3);
await app.close();
});
it("keeps the current report version when a blocked Tmall retry does not recover", async () => {
const tmallLiveService = createTmallLiveServiceStub({
async previewProduct() {