import { afterEach, describe, expect, test } from "vitest"; import { getSubagentSessionRegistry, type SubagentSessionInfo, SubagentSessionRegistry, } from "#src/authority/subagent-registry"; const REGISTRY_KEY = Symbol.for( "@gotgenes/pi-permission-system:subagent-registry", ); function makeInfo( overrides: Partial = {}, ): SubagentSessionInfo { return { ...overrides }; } describe("SubagentSessionRegistry", () => { test("has() returns false for an unregistered key", () => { const registry = new SubagentSessionRegistry(); expect(registry.has("session-abc")).toBe(false); }); test("get() returns undefined for an unregistered key", () => { const registry = new SubagentSessionRegistry(); expect(registry.get("session-abc")).toBeUndefined(); }); test("has() returns true after register()", () => { const registry = new SubagentSessionRegistry(); registry.register("session-abc", makeInfo()); expect(registry.has("session-abc")).toBe(true); }); test("get() returns the registered info after register()", () => { const registry = new SubagentSessionRegistry(); const info = makeInfo({ parentSessionId: "parent-123" }); registry.register("session-abc", info); expect(registry.get("session-abc")).toEqual(info); }); test("register() stores entry without parentSessionId", () => { const registry = new SubagentSessionRegistry(); registry.register("session-abc", makeInfo()); expect(registry.get("session-abc")).toEqual({}); }); test("has() returns false after unregister()", () => { const registry = new SubagentSessionRegistry(); registry.register("session-abc", makeInfo()); registry.unregister("session-abc"); expect(registry.has("session-abc")).toBe(false); }); test("get() returns undefined after unregister()", () => { const registry = new SubagentSessionRegistry(); registry.register("session-abc", makeInfo()); registry.unregister("session-abc"); expect(registry.get("session-abc")).toBeUndefined(); }); test("unregister() is a no-op for an unknown key", () => { const registry = new SubagentSessionRegistry(); expect(() => registry.unregister("session-nonexistent")).not.toThrow(); }); test("register() overwrites a previous entry for the same key", () => { const registry = new SubagentSessionRegistry(); registry.register("session-abc", makeInfo({ parentSessionId: "parent-1" })); registry.register("session-abc", makeInfo({ parentSessionId: "parent-2" })); expect(registry.get("session-abc")?.parentSessionId).toBe("parent-2"); }); // ── #298 regression: concurrent siblings must be independent ────────────── test("two sibling session ids are registered independently", () => { const registry = new SubagentSessionRegistry(); registry.register( "child-session-A", makeInfo({ parentSessionId: "parent-P" }), ); registry.register( "child-session-B", makeInfo({ parentSessionId: "parent-P" }), ); expect(registry.has("child-session-A")).toBe(true); expect(registry.has("child-session-B")).toBe(true); }); test("disposing one sibling does not evict the other (collision regression)", () => { const registry = new SubagentSessionRegistry(); registry.register( "child-session-A", makeInfo({ parentSessionId: "parent-P" }), ); registry.register( "child-session-B", makeInfo({ parentSessionId: "parent-P" }), ); // Sibling A finishes — should not affect B. registry.unregister("child-session-A"); expect(registry.has("child-session-A")).toBe(false); expect(registry.has("child-session-B")).toBe(true); expect(registry.get("child-session-B")?.parentSessionId).toBe("parent-P"); }); }); // ── process-global accessor ──────────────────────────────────────────────── describe("getSubagentSessionRegistry (process-global accessor)", () => { afterEach(() => { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Symbol-keyed global property; Map.delete() is not applicable delete (globalThis as Record)[REGISTRY_KEY]; }); test("returns a SubagentSessionRegistry instance", () => { const registry = getSubagentSessionRegistry(); expect(registry).toBeInstanceOf(SubagentSessionRegistry); }); test("returns the same instance on repeated calls", () => { const first = getSubagentSessionRegistry(); const second = getSubagentSessionRegistry(); expect(first).toBe(second); }); test("state registered through one call is visible through another call", () => { const writer = getSubagentSessionRegistry(); writer.register("child-session-xyz", { parentSessionId: "parent-abc", }); const reader = getSubagentSessionRegistry(); expect(reader.has("child-session-xyz")).toBe(true); expect(reader.get("child-session-xyz")?.parentSessionId).toBe("parent-abc"); }); test("starts empty on first call", () => { const registry = getSubagentSessionRegistry(); expect(registry.has("any-session-id")).toBe(false); }); });