import { describe, expect, test, mock } from "bun:test"; import { createStateRoute } from "../routes/state.ts"; import { createAbortRoute } from "../routes/abort.ts"; import { safeSerialize } from "../lib/serialization.ts"; import type { Runtime, Action } from "@gizmo-ai/runtime"; // Mock runtime factory function createMockRuntime(state: Record): Runtime, Action> { return { getState: () => state, dispatch: mock(() => {}), execute: mock(() => ({ included: Promise.resolve(), completed: Promise.resolve({ status: "completed" }), cancel: () => {}, })), subscribe: () => () => {}, subscribeToActions: () => () => {}, dispatchToReducer: mock(() => {}), resetState: mock(() => {}), getPluginGraph: () => ({ nodes: [], edges: [] }), getPlugins: () => [], } as Runtime, Action>; } describe("createStateRoute", () => { test("GET /state returns full state", async () => { const state = { execution: { id: "exec-1", state: "idle" }, agent: { loop: 0 }, }; const runtime = createMockRuntime(state); const app = createStateRoute(runtime, safeSerialize); const response = await app.request("/"); expect(response.status).toBe(200); const body = await response.json(); expect(body).toEqual(state); }); test("GET /state/:slice returns specific slice", async () => { const state = { execution: { id: "exec-1", state: "pending" }, agent: { loop: 5 }, }; const runtime = createMockRuntime(state); const app = createStateRoute(runtime, safeSerialize); const response = await app.request("/agent"); expect(response.status).toBe(200); const body = await response.json(); expect(body).toEqual({ loop: 5 }); }); test("GET /state/:slice returns 404 for unknown slice", async () => { const state = { execution: { id: "exec-1" } }; const runtime = createMockRuntime(state); const app = createStateRoute(runtime, safeSerialize); const response = await app.request("/unknown"); expect(response.status).toBe(404); const body = await response.json(); expect(body.error).toContain("not found"); }); }); describe("createAbortRoute", () => { test("POST /abort aborts pending execution", async () => { const state = { execution: { id: "exec-123", state: "pending" }, }; const runtime = createMockRuntime(state); const app = createAbortRoute(runtime); const response = await app.request("/", { method: "POST" }); expect(response.status).toBe(200); const body = await response.json(); expect(body.status).toBe("aborted"); expect(body.executionId).toBe("exec-123"); expect(runtime.dispatch).toHaveBeenCalledWith( expect.objectContaining({ type: "RUNTIME_EXECUTION_ABORTED", payload: { executionId: "exec-123" }, }) ); }); test("POST /abort returns 400 when no active execution", async () => { const state = { execution: { id: null, state: "idle" }, }; const runtime = createMockRuntime(state); const app = createAbortRoute(runtime); const response = await app.request("/", { method: "POST" }); expect(response.status).toBe(400); const body = await response.json(); expect(body.error).toContain("No active execution"); }); });