Files
next-tjwater-frontend/src/features/workbench/api/investigation-report.test.ts
T
jiang a617aa95e9 fix(workbench): stabilize investigation presentation rendering
Keep investigation graphs stable during updates, defer expensive chart rendering, and prevent stale results when switching tasks. Adopt reference annotations and consolidate frontend documentation.
2026-11-11 19:10:41 +08:00

105 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { investigationReportSchema } from "./investigation-report";
const base = {
title: "调查报告",
current_conclusion: "无法确定原因",
conclusion_boundary: "仅有上游监测",
evidence_refs: ["query-1"],
recommendations: [],
pending_items: []
};
const result = {
...base,
stage: "final",
outcome: "insufficient_data",
stop_reason: "缺少采样",
data_gaps: [
{
gap_id: "g1",
node_id: "read",
missing_data: "末梢采样",
scope: "投诉小区,投诉时段",
impact: "无法确定异常来源",
required_data: "该时段采样结果",
evidence_refs: ["query-1"],
blocking: true
}
]
};
describe("investigation report read contract", () => {
it("preserves unknown historical facts instead of inventing a result or empty gap list", () => {
const legacy = investigationReportSchema.parse(base);
expect(legacy.outcome).toBeUndefined();
expect(legacy.data_gaps).toBeUndefined();
expect(
investigationReportSchema.parse({
...base,
stage: null,
outcome: null,
data_gaps: null,
stop_reason: null
}).data_gaps
).toBeNull();
});
it("accepts an evidenced final insufficient-data result", () => {
expect(investigationReportSchema.parse(result).outcome).toBe("insufficient_data");
});
it("reads a narrative diagram without requiring a legacy execution plan", () => {
const value = investigationReportSchema.parse({
...result,
data_gaps: [{ ...result.data_gaps[0], node_id: null }],
diagram: {
title: "调查脉络",
mermaid: 'flowchart TD\nA["查询"] --> B["判断"]',
node_refs: [{ node_id: "A", label: "查询", evidence_refs: ["query-1"] }]
}
});
expect(value.diagram?.node_refs[0]?.action_refs).toEqual([]);
expect(value.data_gaps?.[0]?.node_id).toBeNull();
});
it("reads legacy waits as saved input conditions without inferring live waiting", () => {
const value = investigationReportSchema.parse({ ...base, diagram: {
title: "等待结果", mermaid: "flowchart LR\nA[外部回执] --> B[人工复核] --> C[下一步]",
node_refs: [{ node_id: "A", label: "外部回执", status: "waiting" },
{ node_id: "B", label: "人工复核", status: "waiting_human" },
{ node_id: "C", label: "下一步" }]
} });
expect(value.diagram?.node_refs.map((node) => node.status)).toEqual(["needs_input", "needs_input", undefined]);
});
it("reads host-generated cumulative graphs with failed attempts and failure reasons", () => {
const value = investigationReportSchema.parse({ ...base, diagram: {
title: "累计调查脉络", mermaid: 'flowchart TD\nold["首次读取(失败)"] --> retry["重新读取"]',
node_refs: [{ node_id: "old", label: "首次读取", status: "failed", detail: "接口超时" },
{ node_id: "retry", label: "重新读取", status: "completed" }],
edges: [{ source: "old", target: "retry", label: "重试" }]
} });
expect(value.diagram?.node_refs[0]).toMatchObject({ status: "failed", detail: "接口超时" });
expect(value.diagram?.edges).toEqual([{ source: "old", target: "retry", label: "重试" }]);
});
it.each([
{ outcome: "other" },
{ stage: "interim" },
{ stop_reason: null },
{ data_gaps: [] },
{ recommendations: ["自动派单"] },
{ pending_items: ["等人工"] },
{ outcome: "concluded" },
{ data_gaps: [{ ...result.data_gaps[0], evidence_refs: [] }] },
{ data_gaps: [result.data_gaps[0], result.data_gaps[0]] },
{ schema: "invented" }
])("rejects inconsistent business facts %j", (changes) => {
expect(() => investigationReportSchema.parse({ ...result, ...changes })).toThrow();
});
});
it("retains fallback text from a future component while reading the fixed report facts", () => {
const value = investigationReportSchema.parse({ ...base, ui_blocks: [{ evidence_refs: ["query-1"], block: {
version: "answer-ui@future", block_id: "future", fallback_text: "旧版客户端仍可阅读。", spec: { future: true },
} }] });
expect(value.current_conclusion).toBe(base.current_conclusion);
expect(value.ui_blocks?.[0].block.fallback_text).toBe("旧版客户端仍可阅读。");
expect(investigationReportSchema.parse(base).ui_blocks).toBeUndefined();
});