import { describe, expect, it } from "bun:test"; import express from "express"; import type { OperationalTaskClient } from "../../src/product/operational-task-client.js"; import { registerTaskPublicationBridge } from "../../src/routes/task-publication-bridge.js"; import { removeRuntimeSessionContext, setRuntimeSessionContext } from "../../src/runtime/sessionContext.js"; describe("task publication bridge", () => { it("requires a trusted execution context and injects task identity without browser capabilities", async () => { const taskId = "50000000-0000-4000-8000-000000000001"; const requestId = "60000000-0000-4000-8000-000000000001"; const contexts = ["task-publication-execution", "task-publication-collaboration", "task-publication-ordinary"]; const published: Array<{ kind: string; args: unknown[] }> = []; contexts.forEach((sessionId, index) => setRuntimeSessionContext({ sessionId, clientSessionId: `client-${index}`, actorKey: "actor-1", projectKey: "project-1", traceId: "test", taskId, purpose: ["task_execution", "task_collaboration", "ordinary_chat"][index] as "task_execution", })); const app = express(); app.use(express.json()); registerTaskPublicationBridge(app, { internalToken: "internal-test", client: { publishArtifacts: async (...args: unknown[]) => { published.push({ kind: "artifacts", args }); return { progress_revision: 1 }; }, publishPresentation: async (...args: unknown[]) => { published.push({ kind: "presentation", args }); return { recorded: true, event_seq: 8, run_id: "55000000-0000-4000-8000-000000000001", execution_generation: 1 }; }, } as unknown as Pick }); const server = app.listen(0, "127.0.0.1"); await new Promise((resolve) => server.once("listening", resolve)); const base = `http://127.0.0.1:${(server.address() as { port: number }).port}`; const post = (kind: string, body: unknown, extraHeaders: Record = {}) => fetch(`${base}/internal/operational-task/${kind}`, { method: "POST", headers: { "Content-Type": "application/json", "x-agent-internal-token": "internal-test", ...extraHeaders }, body: JSON.stringify(body), }); const progress = { summary: "核对实际资料", diagram: null }; const command = { action: "highlight_features", layer: "pipes", feature_ids: ["P-1"], effect: "pulse" }; try { const body = { session_id: contexts[0], request_id: requestId, command }; expect((await post("presentation", body, { "x-agent-internal-token": "wrong" })).status).toBe(403); expect((await post("presentation", body, { Origin: "http://browser.test" })).status).toBe(403); expect((await post("presentation", { ...body, session_id: "unknown" })).status).toBe(403); for (const sessionId of contexts.slice(1)) { expect((await post("presentation", { ...body, session_id: sessionId })).status).toBe(403); expect((await post("artifacts", { session_id: sessionId, request_id: requestId, progress })).status).toBe(403); } expect((await post("presentation", { ...body, run_id: "model-supplied-run" })).status).toBe(400); expect((await post("presentation", { ...body, command: { action: "set_overlay", overlay_id: "o1", title: "位置", mode: "geometry", evidence_id: "e1", data: { coordinates: [1, 2] }, } })).status).toBe(400); expect(published).toHaveLength(0); const result = await post("presentation", body); expect(result.status).toBe(200); expect(await result.json()).toMatchObject({ recorded: true, event_seq: 8 }); expect(published[0]).toEqual({ kind: "presentation", args: [contexts[0], taskId, { requestId, sourceSessionId: "client-0", command, }] }); expect((await post("artifacts", { session_id: contexts[0], request_id: requestId, progress })).status).toBe(200); expect(published[1]?.args).toEqual([contexts[0], taskId, { requestId, sourceSessionId: "client-0", evidence: undefined, executionPlan: undefined, report: undefined, progress, }]); const report = { title: "报告", current_conclusion: "核验结束", conclusion_boundary: "授权数据范围", stage: "final", outcome: "concluded", stop_reason: null, data_gaps: [] }; expect((await post("artifacts", { session_id: contexts[1], request_id: requestId, report })).status).toBe(200); const interactive = { ...report, evidence_refs: ["e1"], ui_blocks: [{ evidence_refs: ["e1"], block: { version: "answer-ui@1", block_id: "summary", fallback_text: "已保存的演练记录。", spec: { root: "text", elements: { text: { type: "Text", children: [], props: { text: "已保存的演练记录。", tone: "neutral" } } }, }, } }] }; expect((await post("artifacts", { session_id: contexts[0], request_id: requestId, report: interactive })).status).toBe(200); expect(published.at(-1)?.args[2]).toMatchObject({ report: { ui_blocks: interactive.ui_blocks }, sourceSessionId: "client-0" }); const count = published.length; expect((await post("artifacts", { session_id: contexts[0], request_id: requestId, report: { ...interactive, evidence_refs: [] } })).status).toBe(400); expect(published).toHaveLength(count); } finally { contexts.forEach(removeRuntimeSessionContext); await new Promise((resolve) => server.close(() => resolve())); } }); });