Files
AgentHub/client/src/lib/__tests__/eventBus.test.ts
T
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

97 lines
2.4 KiB
TypeScript

/**
* @file eventBus.test.ts
* @description Unit tests for the eventBus module to ensure correct subscription, publishing, and unsubscription behavior in the agent dashboard application.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
import { describe, it, expect, vi } from "vitest";
import { eventBus } from "../eventBus";
import type { WSMessage } from "../types";
function makeMsg(type: WSMessage["type"] = "new_event"): WSMessage {
return {
type,
data: {
id: 1,
session_id: "s1",
agent_id: null,
event_type: "PreToolUse",
tool_name: "Bash",
summary: "test",
data: null,
created_at: new Date().toISOString(),
},
timestamp: new Date().toISOString(),
};
}
describe("eventBus", () => {
it("should call subscriber when message is published", () => {
const handler = vi.fn();
const unsub = eventBus.subscribe(handler);
const msg = makeMsg();
eventBus.publish(msg);
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith(msg);
unsub();
});
it("should support multiple subscribers", () => {
const h1 = vi.fn();
const h2 = vi.fn();
const u1 = eventBus.subscribe(h1);
const u2 = eventBus.subscribe(h2);
eventBus.publish(makeMsg());
expect(h1).toHaveBeenCalledTimes(1);
expect(h2).toHaveBeenCalledTimes(1);
u1();
u2();
});
it("should stop calling handler after unsubscribe", () => {
const handler = vi.fn();
const unsub = eventBus.subscribe(handler);
eventBus.publish(makeMsg());
expect(handler).toHaveBeenCalledTimes(1);
unsub();
eventBus.publish(makeMsg());
expect(handler).toHaveBeenCalledTimes(1); // still 1
});
it("should not fail when publishing with no subscribers", () => {
expect(() => eventBus.publish(makeMsg())).not.toThrow();
});
it("should handle unsubscribe called multiple times", () => {
const handler = vi.fn();
const unsub = eventBus.subscribe(handler);
unsub();
unsub(); // second call should be harmless
eventBus.publish(makeMsg());
expect(handler).not.toHaveBeenCalled();
});
it("should not add duplicate handler references", () => {
const handler = vi.fn();
const u1 = eventBus.subscribe(handler);
const u2 = eventBus.subscribe(handler); // same ref, Set deduplicates
eventBus.publish(makeMsg());
expect(handler).toHaveBeenCalledTimes(1); // Set prevents double-add
u1();
u2();
});
});