70 lines
3.7 KiB
TypeScript
70 lines
3.7 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
|
|
import type { WorkbenchApplication, WorkbenchTemporalGateway } from "./contracts.ts";
|
|
import { createWorkbenchDispatcher } from "./dispatcher.ts";
|
|
import { createCloudWorkbenchApplication } from "./cloud-application.ts";
|
|
|
|
describe("Workbench dispatcher", () => {
|
|
test("submits through Temporal without synthesizing terminal state", async () => {
|
|
const calls: string[] = [];
|
|
const application: WorkbenchApplication = {
|
|
async health() { return { status: "ready" }; },
|
|
async createSession() { return { session: { sessionId: "ses_test" } }; },
|
|
async admitTurn(input) { calls.push("admit"); return input; },
|
|
async dispatchTurn() { throw new Error("API dispatcher must not dispatch directly"); },
|
|
async cancelTurn() { throw new Error("API dispatcher must not cancel directly"); }
|
|
};
|
|
const temporal: WorkbenchTemporalGateway = {
|
|
async submit(input) { calls.push("temporal"); return { workflowId: `workbench-turn-${input.traceId}`, workflowRunId: "run-1", reused: false }; },
|
|
async cancel() { return { workflowId: "cancel", workflowRunId: "run-2", reused: false }; },
|
|
async close() {}
|
|
};
|
|
const dispatch = createWorkbenchDispatcher({ application, temporal, mode: "temporal" });
|
|
const result = await dispatch({ operation: "turn.submit", actor: { id: "usr_test" }, traceId: "trc_test", params: { sessionId: "ses_test", message: "hi" } });
|
|
expect(calls).toEqual(["admit", "temporal"]);
|
|
expect(result).toMatchObject({ ok: true, mode: "temporal", data: { status: "running", traceId: "trc_test", terminalAuthority: "hwlab.event.v1", resultSynthesized: false } });
|
|
});
|
|
|
|
test("cancel is a durable Temporal command", async () => {
|
|
const application = stubApplication();
|
|
const temporal: WorkbenchTemporalGateway = {
|
|
async submit() { throw new Error("not used"); },
|
|
async cancel(input) { return { workflowId: `workbench-cancel-${input.traceId}`, workflowRunId: "run-cancel", reused: false }; },
|
|
async close() {}
|
|
};
|
|
const result = await createWorkbenchDispatcher({ application, temporal, mode: "temporal" })({ operation: "turn.cancel", actor: { id: "usr_test" }, traceId: "trc_test" });
|
|
expect(result).toMatchObject({ ok: true, data: { status: "cancel_requested", resultSynthesized: false } });
|
|
});
|
|
});
|
|
|
|
describe("Workbench Cloud application adapter", () => {
|
|
test("persists admission before dispatch through distinct internal routes", async () => {
|
|
const calls: Array<{ path: string; authorization: string | null }> = [];
|
|
const application = createCloudWorkbenchApplication({
|
|
baseUrl: "http://cloud.internal",
|
|
authorization: "unified-internal-key",
|
|
fetchImpl: (async (url: string | URL | Request, init?: RequestInit) => {
|
|
calls.push({ path: new URL(String(url)).pathname, authorization: new Headers(init?.headers).get("authorization") });
|
|
return Response.json({ ok: true, accepted: true, status: "admitted" });
|
|
}) as typeof fetch
|
|
});
|
|
const input = { actor: { id: "usr_test" }, traceId: "trc_test", sessionId: "ses_test", params: { sessionId: "ses_test", message: "hi" } };
|
|
await application.admitTurn(input);
|
|
await application.dispatchTurn(input);
|
|
expect(calls).toEqual([
|
|
{ path: "/v1/internal/workbench/admit", authorization: "unified-internal-key" },
|
|
{ path: "/v1/agent/chat", authorization: "unified-internal-key" }
|
|
]);
|
|
});
|
|
});
|
|
|
|
function stubApplication(): WorkbenchApplication {
|
|
return {
|
|
async health() { return {}; },
|
|
async createSession() { return {}; },
|
|
async admitTurn(input) { return input; },
|
|
async dispatchTurn() { return {}; },
|
|
async cancelTurn() { return {}; }
|
|
};
|
|
}
|