import { afterEach, describe, expect, it, vi } from "vitest"; import { parseOperationalTask } from "./operational-task-client"; vi.mock("./project-client", () => ({ findOwnedWorkbenchProject: vi.fn(async () => ({ project: { project_id: "project" }, accessToken: "test" })), createWorkbenchHeaders: vi.fn(() => ({})), getServerError: vi.fn(() => "请求失败") })); const id = "324ff7ee-c2d4-5184-a84b-48fb14a2933e"; const time = "2026-09-23T01:00:00Z"; function task(generation: number | null | undefined = 2) { const snapshot = { revision: 1, source_session_id: "session", published_at: time, published_by: "agent", ...(generation === undefined ? {} : { execution_generation: generation }) }; return { project_id: id, task_id: id, task_type: "event_investigation", workflow: "water_quality_complaint", lifecycle_status: "waiting_human", active_run: null, status: "waiting_human", priority: "normal", revision: 1, execution_generation: 2, execution_request_id: id, title: "水质投诉", summary: "调查中", source: { kind: "external_system", external_id: null, external_revision: null, event_kind: null }, trigger: { kind: "event", occurred_at: time, detail: "投诉上报" }, scheduled_for: null, agent_session_id: "session", agent_lease_expires_at: null, current_plan_revision: 1, current_report_revision: 1, created_at: time, updated_at: time, latest_plan: { ...snapshot, plan: { schema: "execution-plan@1", title: "调查", reason: "投诉", nodes: [{ id: "check", title: "核查", owner: "agent", status: "completed", detail: "已核查", evidence_refs: [], action_refs: [] }], edges: [] } }, latest_report: { ...snapshot, report: { title: "调查报告", current_conclusion: "待采样", conclusion_boundary: "尚无现场证据", evidence_refs: [], recommendations: [], pending_items: ["采样"] } }, evidence: [], actions: [], permissions: { can_publish_artifacts: false, can_propose_actions: false, can_sync_external_feedback: false, can_decide_work_orders: false, can_execute_work_orders: false, can_decide_commands: false, can_execute_commands: false } }; } afterEach(() => vi.unstubAllGlobals()); describe("operational task contract", () => { it("accepts execution generation on both plan and report", () => { const parsed = parseOperationalTask(task()); expect(parsed.latest_plan).toHaveProperty("execution_generation", 2); expect(parsed.latest_report).toHaveProperty("execution_generation", 2); }); it("accepts legacy null or missing snapshot generation", () => { expect(() => parseOperationalTask(task(null))).not.toThrow(); const legacy = task(); Reflect.deleteProperty(legacy.latest_plan, "execution_generation"); Reflect.deleteProperty(legacy.latest_report, "execution_generation"); expect(() => parseOperationalTask(legacy)).not.toThrow(); }); it("accepts a task without any plan or report", () => { expect(parseOperationalTask({ ...task(), latest_plan: null, latest_report: null }).latest_report).toBeNull(); }); it("keeps unknown fields and invalid generations rejected", () => { expect(() => parseOperationalTask(task(0))).toThrow(); const value = task(); expect(() => parseOperationalTask({ ...value, latest_plan: { ...value.latest_plan, unexpected: true } })).toThrow(); expect(() => parseOperationalTask({ ...value, latest_report: { ...value.latest_report, unexpected: true } })).toThrow(); expect(() => parseOperationalTask({ ...value, latest_report: { ...value.latest_report, execution_generation: -1 } })).toThrow(); }); });