Keep investigation graphs stable during updates, defer expensive chart rendering, and prevent stale results when switching tasks. Adopt reference annotations and consolidate frontend documentation.
105 lines
4.3 KiB
TypeScript
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();
|
|
});
|