import { describe, test, expect, beforeEach, mock } from "bun:test"; // Mock config before imports const mockConfig = { port: 3000, host: "0.0.0.0", apiKeys: ["test-api-key"], baseUrl: "http://localhost:3000", pollTimeout: 1, // Short timeout for tests heartbeatInterval: 20, jwtExpiresIn: 3600, disconnectTimeout: 300, webCorsOrigins: [], wsIdleTimeout: 30, wsKeepaliveInterval: 20, }; mock.module("../config", () => ({ config: mockConfig, getBaseUrl: () => "http://localhost:3000", })); import { storeReset, storeCreateEnvironment, storeCreateSession, storeGetWorkItem, storeGetPendingWorkItem } from "../store"; import { createWorkItem, pollWork, ackWork, stopWork, heartbeatWork, reconnectWorkForEnvironment, } from "../services/work-dispatch"; describe("Work Dispatch", () => { let envId: string; let sessionId: string; beforeEach(() => { storeReset(); const env = storeCreateEnvironment({ secret: "s" }); envId = env.id; const session = storeCreateSession({ environmentId: envId }); sessionId = session.id; }); describe("createWorkItem", () => { test("creates work item for active environment", async () => { const workId = await createWorkItem(envId, sessionId); expect(workId).toMatch(/^work_/); const item = storeGetWorkItem(workId); expect(item?.state).toBe("pending"); expect(item?.sessionId).toBe(sessionId); }); test("throws for non-existent environment", async () => { await expect(createWorkItem("env_no", sessionId)).rejects.toThrow("not found"); }); test("throws for inactive environment", async () => { const inactiveEnv = storeCreateEnvironment({ secret: "s2" }); // Manually set status to deregistered const { storeUpdateEnvironment } = await import("../store"); storeUpdateEnvironment(inactiveEnv.id, { status: "deregistered" }); await expect(createWorkItem(inactiveEnv.id, sessionId)).rejects.toThrow("not active"); }); test("encodes work secret as base64 JSON", async () => { const workId = await createWorkItem(envId, sessionId); const item = storeGetWorkItem(workId); const decoded = JSON.parse(Buffer.from(item!.secret, "base64url").toString()); expect(decoded.version).toBe(1); expect(decoded.session_ingress_token).toBe("test-api-key"); expect(decoded.api_base_url).toBe("http://localhost:3000"); }); }); describe("pollWork", () => { test("returns null when no work available (timeout)", async () => { const result = await pollWork(envId, 0.1); expect(result).toBeNull(); }); test("returns pending work and marks as dispatched", async () => { const workId = await createWorkItem(envId, sessionId); const result = await pollWork(envId, 1); expect(result).not.toBeNull(); expect(result!.id).toBe(workId); expect(result!.state).toBe("dispatched"); expect(result!.data.type).toBe("session"); expect(result!.data.id).toBe(sessionId); // Work should no longer be pending expect(storeGetPendingWorkItem(envId)).toBeUndefined(); }); test("does not return work for different environment", async () => { const env2 = storeCreateEnvironment({ secret: "s2" }); await createWorkItem(envId, sessionId); const result = await pollWork(env2.id, 0.1); expect(result).toBeNull(); }); }); describe("ackWork", () => { test("marks work as acked", async () => { const workId = await createWorkItem(envId, sessionId); ackWork(workId); expect(storeGetWorkItem(workId)?.state).toBe("acked"); }); }); describe("stopWork", () => { test("marks work as completed", async () => { const workId = await createWorkItem(envId, sessionId); stopWork(workId); expect(storeGetWorkItem(workId)?.state).toBe("completed"); }); }); describe("heartbeatWork", () => { test("extends lease and returns heartbeat info", async () => { const workId = await createWorkItem(envId, sessionId); const result = heartbeatWork(workId); expect(result.lease_extended).toBe(true); expect(result.ttl_seconds).toBe(40); // heartbeatInterval * 2 expect(result.last_heartbeat).toBeTruthy(); }); test("returns default state for non-existent work", async () => { const result = heartbeatWork("work_no"); expect(result.state).toBe("acked"); }); }); describe("reconnectWorkForEnvironment", () => { test("creates work items for idle sessions in environment", async () => { // Create another idle session storeCreateSession({ environmentId: envId }); const workIds = await reconnectWorkForEnvironment(envId); expect(workIds).toHaveLength(2); for (const id of workIds) { expect(storeGetWorkItem(id)?.state).toBe("pending"); } }); test("skips non-idle sessions", async () => { const activeSession = storeCreateSession({ environmentId: envId }); const { storeUpdateSession } = await import("../store"); storeUpdateSession(activeSession.id, { status: "active" }); const workIds = await reconnectWorkForEnvironment(envId); // Only the original idle session should get work expect(workIds).toHaveLength(1); }); test("returns empty for environment with no sessions", async () => { const emptyEnv = storeCreateEnvironment({ secret: "s_empty" }); const workIds = await reconnectWorkForEnvironment(emptyEnv.id); expect(workIds).toHaveLength(0); }); }); });