Centralize session scope, chat projections and typed task bridges. Keep historical interaction replies authoritative and publish terminal state to subscribers. Validate Server contracts before replacing generated artifacts.
46 lines
3.5 KiB
TypeScript
46 lines
3.5 KiB
TypeScript
import { expect, it } from "bun:test";
|
|
import express from "express";
|
|
import type { OperationalTaskClient } from "../../src/product/operational-task-client.js";
|
|
import { registerTaskCommandBridge } from "../../src/routes/task-command-bridge.js";
|
|
import { removeRuntimeSessionContext, setRuntimeSessionContext } from "../../src/runtime/sessionContext.js";
|
|
|
|
it("preserves command-specific session purposes and trusted bridge authentication", async () => {
|
|
const calls: unknown[][] = [];
|
|
const client = {
|
|
requestDataAccess: async (...args: unknown[]) => { calls.push(args); return {}; },
|
|
proposeAction: async (...args: unknown[]) => { calls.push(args); throw Object.assign(new Error("conflict"), { status: 409 }); },
|
|
addHumanEvidence: async (...args: unknown[]) => { calls.push(args); return {}; },
|
|
} as unknown as Pick<OperationalTaskClient, "requestDataAccess" | "proposeAction" | "addHumanEvidence">;
|
|
for (const purpose of ["task_execution", "task_collaboration"] as const) setRuntimeSessionContext({
|
|
sessionId: purpose, clientSessionId: `client-${purpose}`, taskId: "task", purpose,
|
|
actorKey: "actor", projectKey: "project", traceId: "test",
|
|
});
|
|
const app = express(); app.use(express.json());
|
|
registerTaskCommandBridge(app, { internalToken: "test-token", client });
|
|
const server = app.listen(0, "127.0.0.1");
|
|
await new Promise<void>((resolve) => server.once("listening", resolve));
|
|
const post = (kind: string, session: string, data: object, headers = {}) => fetch(`http://127.0.0.1:${(server.address() as { port: number }).port}/internal/operational-task/${kind}`, {
|
|
method: "POST", headers: { "Content-Type": "application/json", "x-agent-internal-token": "test-token", ...headers }, body: JSON.stringify({ session_id: session, ...data }),
|
|
});
|
|
const requestId = "60000000-0000-4000-8000-000000000001";
|
|
const access = { request: { requestId, nodeId: "read", scopes: ["scada_devices"], reason: "查询" } };
|
|
const evidence = { command: { request_id: requestId, evidence: { evidence_id: "e1", kind: "human_input", summary: "已复核", source_label: "用户", observed_at: "2026-11-09T00:00:00Z" } } };
|
|
try {
|
|
expect((await post("access-requests", "task_execution", access, { Origin: "http://browser.test" })).status).toBe(403);
|
|
expect((await post("access-requests", "task_execution", access, { "x-agent-internal-token": "wrong" })).status).toBe(403);
|
|
expect((await post("access-requests", "task_collaboration", access)).status).toBe(403);
|
|
expect((await post("human-evidence", "task_execution", evidence)).status).toBe(403);
|
|
expect(calls).toHaveLength(0);
|
|
expect((await post("access-requests", "task_execution", access)).status).toBe(200);
|
|
expect(calls[0]).toEqual(["task_execution", "task", { ...access.request, sourceSessionId: "client-task_execution" }]);
|
|
expect((await post("human-evidence", "task_collaboration", evidence)).status).toBe(200);
|
|
const action = { action: { request_id: requestId, reason: "核验", kind: "create_work_order", payload: { title: "采样", description: "核验", work_order_type: "general" } } };
|
|
expect((await post("actions", "task_execution", action)).status).toBe(409);
|
|
expect((await post("actions", "task_collaboration", action)).status).toBe(409);
|
|
expect((await post("actions", "unknown", action)).status).toBe(403);
|
|
} finally {
|
|
removeRuntimeSessionContext("task_execution"); removeRuntimeSessionContext("task_collaboration");
|
|
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
}
|
|
});
|