From 9e6125ea2f61a29c712651174c76e5cabb5660d1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Feb 2026 11:28:00 +0000 Subject: [PATCH] test(discord): stabilize subagent hook coverage --- extensions/discord/src/subagent-hooks.test.ts | 298 ++++++++---------- 1 file changed, 128 insertions(+), 170 deletions(-) diff --git a/extensions/discord/src/subagent-hooks.test.ts b/extensions/discord/src/subagent-hooks.test.ts index 8e2514b3b77..f8a139cd56d 100644 --- a/extensions/discord/src/subagent-hooks.test.ts +++ b/extensions/discord/src/subagent-hooks.test.ts @@ -64,6 +64,95 @@ function registerHandlersForTest( return handlers; } +function getRequiredHandler( + handlers: Map unknown>, + hookName: string, +): (event: unknown, ctx: unknown) => unknown { + const handler = handlers.get(hookName); + if (!handler) { + throw new Error(`expected ${hookName} hook handler`); + } + return handler; +} + +function createSpawnEvent(overrides?: { + childSessionKey?: string; + agentId?: string; + label?: string; + mode?: string; + requester?: { + channel?: string; + accountId?: string; + to?: string; + threadId?: string; + }; + threadRequested?: boolean; +}): { + childSessionKey: string; + agentId: string; + label: string; + mode: string; + requester: { + channel: string; + accountId: string; + to: string; + threadId?: string; + }; + threadRequested: boolean; +} { + const base = { + childSessionKey: "agent:main:subagent:child", + agentId: "main", + label: "banana", + mode: "session", + requester: { + channel: "discord", + accountId: "work", + to: "channel:123", + threadId: "456", + }, + threadRequested: true, + }; + return { + ...base, + ...overrides, + requester: { + ...base.requester, + ...(overrides?.requester ?? {}), + }, + }; +} + +function createSpawnEventWithoutThread() { + return createSpawnEvent({ + label: "", + requester: { threadId: undefined }, + }); +} + +async function runSubagentSpawning( + config?: Record, + event = createSpawnEventWithoutThread(), +) { + const handlers = registerHandlersForTest(config); + const handler = getRequiredHandler(handlers, "subagent_spawning"); + return await handler(event, {}); +} + +async function expectSubagentSpawningError(params?: { + config?: Record; + errorContains?: string; + event?: ReturnType; +}) { + const result = await runSubagentSpawning(params?.config, params?.event); + expect(hookMocks.autoBindSpawnedDiscordSubagent).not.toHaveBeenCalled(); + expect(result).toMatchObject({ status: "error" }); + if (params?.errorContains) { + const errorText = (result as { error?: string }).error ?? ""; + expect(errorText).toContain(params.errorContains); + } +} + describe("discord subagent hook handlers", () => { beforeEach(() => { hookMocks.resolveDiscordAccount.mockClear(); @@ -90,27 +179,9 @@ describe("discord subagent hook handlers", () => { it("binds thread routing on subagent_spawning", async () => { const handlers = registerHandlersForTest(); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } + const handler = getRequiredHandler(handlers, "subagent_spawning"); - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - label: "banana", - mode: "session", - requester: { - channel: "discord", - accountId: "work", - to: "channel:123", - threadId: "456", - }, - threadRequested: true, - }, - {}, - ); + const result = await handler(createSpawnEvent(), {}); expect(hookMocks.autoBindSpawnedDiscordSubagent).toHaveBeenCalledTimes(1); expect(hookMocks.autoBindSpawnedDiscordSubagent).toHaveBeenCalledWith({ @@ -127,82 +198,42 @@ describe("discord subagent hook handlers", () => { }); it("returns error when thread-bound subagent spawn is disabled", async () => { - const handlers = registerHandlersForTest({ - channels: { - discord: { - threadBindings: { - spawnSubagentSessions: false, + await expectSubagentSpawningError({ + config: { + channels: { + discord: { + threadBindings: { + spawnSubagentSessions: false, + }, }, }, }, + errorContains: "spawnSubagentSessions=true", }); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } - - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - requester: { - channel: "discord", - accountId: "work", - to: "channel:123", - }, - threadRequested: true, - }, - {}, - ); - - expect(hookMocks.autoBindSpawnedDiscordSubagent).not.toHaveBeenCalled(); - expect(result).toMatchObject({ status: "error" }); - const errorText = (result as { error?: string }).error ?? ""; - expect(errorText).toContain("spawnSubagentSessions=true"); }); it("returns error when global thread bindings are disabled", async () => { - const handlers = registerHandlersForTest({ - session: { - threadBindings: { - enabled: false, - }, - }, - channels: { - discord: { + await expectSubagentSpawningError({ + config: { + session: { threadBindings: { - spawnSubagentSessions: true, + enabled: false, + }, + }, + channels: { + discord: { + threadBindings: { + spawnSubagentSessions: true, + }, }, }, }, + errorContains: "threadBindings.enabled=true", }); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } - - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - requester: { - channel: "discord", - accountId: "work", - to: "channel:123", - }, - threadRequested: true, - }, - {}, - ); - - expect(hookMocks.autoBindSpawnedDiscordSubagent).not.toHaveBeenCalled(); - expect(result).toMatchObject({ status: "error" }); - const errorText = (result as { error?: string }).error ?? ""; - expect(errorText).toContain("threadBindings.enabled=true"); }); it("allows account-level threadBindings.enabled to override global disable", async () => { - const handlers = registerHandlersForTest({ + const result = await runSubagentSpawning({ session: { threadBindings: { enabled: false, @@ -221,79 +252,34 @@ describe("discord subagent hook handlers", () => { }, }, }); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } - - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - requester: { - channel: "discord", - accountId: "work", - to: "channel:123", - }, - threadRequested: true, - }, - {}, - ); expect(hookMocks.autoBindSpawnedDiscordSubagent).toHaveBeenCalledTimes(1); expect(result).toMatchObject({ status: "ok", threadBindingReady: true }); }); it("defaults thread-bound subagent spawn to disabled when unset", async () => { - const handlers = registerHandlersForTest({ - channels: { - discord: { - threadBindings: {}, + await expectSubagentSpawningError({ + config: { + channels: { + discord: { + threadBindings: {}, + }, }, }, }); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } - - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - requester: { - channel: "discord", - accountId: "work", - to: "channel:123", - }, - threadRequested: true, - }, - {}, - ); - - expect(hookMocks.autoBindSpawnedDiscordSubagent).not.toHaveBeenCalled(); - expect(result).toMatchObject({ status: "error" }); }); it("no-ops when thread binding is requested on non-discord channel", async () => { - const handlers = registerHandlersForTest(); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } - - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - mode: "session", + const result = await runSubagentSpawning( + undefined, + createSpawnEvent({ requester: { channel: "signal", + accountId: "", to: "+123", + threadId: undefined, }, - threadRequested: true, - }, - {}, + }), ); expect(hookMocks.autoBindSpawnedDiscordSubagent).not.toHaveBeenCalled(); @@ -302,26 +288,7 @@ describe("discord subagent hook handlers", () => { it("returns error when thread bind fails", async () => { hookMocks.autoBindSpawnedDiscordSubagent.mockResolvedValueOnce(null); - const handlers = registerHandlersForTest(); - const handler = handlers.get("subagent_spawning"); - if (!handler) { - throw new Error("expected subagent_spawning hook handler"); - } - - const result = await handler( - { - childSessionKey: "agent:main:subagent:child", - agentId: "main", - mode: "session", - requester: { - channel: "discord", - accountId: "work", - to: "channel:123", - }, - threadRequested: true, - }, - {}, - ); + const result = await runSubagentSpawning(); expect(result).toMatchObject({ status: "error" }); const errorText = (result as { error?: string }).error ?? ""; @@ -330,10 +297,7 @@ describe("discord subagent hook handlers", () => { it("unbinds thread routing on subagent_ended", () => { const handlers = registerHandlersForTest(); - const handler = handlers.get("subagent_ended"); - if (!handler) { - throw new Error("expected subagent_ended hook handler"); - } + const handler = getRequiredHandler(handlers, "subagent_ended"); handler( { @@ -361,10 +325,7 @@ describe("discord subagent hook handlers", () => { { accountId: "work", threadId: "777" }, ]); const handlers = registerHandlersForTest(); - const handler = handlers.get("subagent_delivery_target"); - if (!handler) { - throw new Error("expected subagent_delivery_target hook handler"); - } + const handler = getRequiredHandler(handlers, "subagent_delivery_target"); const result = handler( { @@ -404,10 +365,7 @@ describe("discord subagent hook handlers", () => { { accountId: "work", threadId: "888" }, ]); const handlers = registerHandlersForTest(); - const handler = handlers.get("subagent_delivery_target"); - if (!handler) { - throw new Error("expected subagent_delivery_target hook handler"); - } + const handler = getRequiredHandler(handlers, "subagent_delivery_target"); const result = handler( {