import { describe, expect, test, mock } from "bun:test"; import { createEventBus, onStateChanged, onExecutionLifecycle, emitStateChanged, emitExecutionLifecycle, } from "../lib/event-bus.ts"; import type { StateChangedEvent, ExecutionLifecycleEvent } from "../types.ts"; describe("createEventBus", () => { test("creates an event emitter", () => { const bus = createEventBus(); expect(bus).toBeDefined(); expect(typeof bus.on).toBe("function"); expect(typeof bus.emit).toBe("function"); }); test("allows many listeners", () => { const bus = createEventBus(); // Should not throw even with many listeners for (let i = 0; i < 100; i++) { bus.on("test", () => {}); } expect(bus.listenerCount("test")).toBe(100); }); }); describe("onStateChanged", () => { test("subscribes to state.changed events", () => { const bus = createEventBus(); const handler = mock(() => {}); onStateChanged(bus, handler); const event: StateChangedEvent = { slice: "test", before: { value: 1 }, after: { value: 2 }, causingAction: { type: "TEST_ACTION" }, }; emitStateChanged(bus, event); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(event); }); test("returns unsubscribe function", () => { const bus = createEventBus(); const handler = mock(() => {}); const unsubscribe = onStateChanged(bus, handler); // Emit once emitStateChanged(bus, { slice: "test", before: null, after: null, causingAction: { type: "TEST" }, }); expect(handler).toHaveBeenCalledTimes(1); // Unsubscribe unsubscribe(); // Emit again - handler should not be called emitStateChanged(bus, { slice: "test", before: null, after: null, causingAction: { type: "TEST" }, }); expect(handler).toHaveBeenCalledTimes(1); }); }); describe("onExecutionLifecycle", () => { test("subscribes to execution.lifecycle events", () => { const bus = createEventBus(); const handler = mock(() => {}); onExecutionLifecycle(bus, handler); const event: ExecutionLifecycleEvent = { status: "started", executionId: "exec-123", turnId: "turn-1", }; emitExecutionLifecycle(bus, event); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(event); }); test("returns unsubscribe function", () => { const bus = createEventBus(); const handler = mock(() => {}); const unsubscribe = onExecutionLifecycle(bus, handler); unsubscribe(); emitExecutionLifecycle(bus, { status: "completed", executionId: "exec-123", }); expect(handler).not.toHaveBeenCalled(); }); }); describe("emitStateChanged", () => { test("emits state.changed event", () => { const bus = createEventBus(); const handler = mock(() => {}); bus.on("state.changed", handler); const event: StateChangedEvent = { slice: "agent", before: { loop: 0 }, after: { loop: 1 }, causingAction: { type: "AGENT_LOOP" }, }; emitStateChanged(bus, event); expect(handler).toHaveBeenCalledWith(event); }); }); describe("emitExecutionLifecycle", () => { test("emits execution.lifecycle event", () => { const bus = createEventBus(); const handler = mock(() => {}); bus.on("execution.lifecycle", handler); const event: ExecutionLifecycleEvent = { status: "failed", executionId: "exec-456", error: new Error("Test error"), }; emitExecutionLifecycle(bus, event); expect(handler).toHaveBeenCalledWith(event); }); });