diff --git a/src/slack/monitor.test-helpers.ts b/src/slack/monitor.test-helpers.ts index b50f871a23..e00005ea0b 100644 --- a/src/slack/monitor.test-helpers.ts +++ b/src/slack/monitor.test-helpers.ts @@ -1,6 +1,11 @@ import { Mock, vi } from "vitest"; type SlackHandler = (args: unknown) => Promise; +type SlackProviderMonitor = (params: { + botToken: string; + appToken: string; + abortSignal: AbortSignal; +}) => Promise; const slackTestState: { config: Record; @@ -43,6 +48,57 @@ export async function waitForSlackEvent(name: string) { } } +export function startSlackMonitor( + monitorSlackProvider: SlackProviderMonitor, + opts?: { botToken?: string; appToken?: string }, +) { + const controller = new AbortController(); + const run = monitorSlackProvider({ + botToken: opts?.botToken ?? "bot-token", + appToken: opts?.appToken ?? "app-token", + abortSignal: controller.signal, + }); + return { controller, run }; +} + +export async function getSlackHandlerOrThrow(name: string) { + await waitForSlackEvent(name); + const handler = getSlackHandlers()?.get(name); + if (!handler) { + throw new Error(`Slack ${name} handler not registered`); + } + return handler; +} + +export async function stopSlackMonitor(params: { + controller: AbortController; + run: Promise; +}) { + await flush(); + params.controller.abort(); + await params.run; +} + +export async function runSlackEventOnce( + monitorSlackProvider: SlackProviderMonitor, + name: string, + args: unknown, + opts?: { botToken?: string; appToken?: string }, +) { + const { controller, run } = startSlackMonitor(monitorSlackProvider, opts); + const handler = await getSlackHandlerOrThrow(name); + await handler(args); + await stopSlackMonitor({ controller, run }); +} + +export async function runSlackMessageOnce( + monitorSlackProvider: SlackProviderMonitor, + args: unknown, + opts?: { botToken?: string; appToken?: string }, +) { + await runSlackEventOnce(monitorSlackProvider, "message", args, opts); +} + export const defaultSlackTestConfig = () => ({ messages: { responsePrefix: "PFX", diff --git a/src/slack/monitor.tool-result.forces-thread-replies-replytoid-is-set.test.ts b/src/slack/monitor.tool-result.forces-thread-replies-replytoid-is-set.test.ts index 4871ffaadc..4743cd8518 100644 --- a/src/slack/monitor.tool-result.forces-thread-replies-replytoid-is-set.test.ts +++ b/src/slack/monitor.tool-result.forces-thread-replies-replytoid-is-set.test.ts @@ -2,12 +2,13 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import { defaultSlackTestConfig, - flush, getSlackClient, - getSlackHandlers, + getSlackHandlerOrThrow, getSlackTestState, resetSlackTestState, - waitForSlackEvent, + runSlackMessageOnce, + startSlackMonitor, + stopSlackMonitor, } from "./monitor.test-helpers.js"; const { monitorSlackProvider } = await import("./monitor.js"); @@ -39,20 +40,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -63,10 +51,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: "555" }); }); @@ -84,20 +68,7 @@ describe("monitorSlackProvider tool results", () => { channel: { name: "general", is_channel: true }, }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -108,10 +79,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(reactMock).toHaveBeenCalledWith({ channel: "C1", timestamp: "456", @@ -131,20 +98,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -155,10 +109,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).not.toHaveBeenCalled(); expect(upsertPairingRequestMock).toHaveBeenCalled(); expect(sendMock).toHaveBeenCalledTimes(1); @@ -181,18 +131,8 @@ describe("monitorSlackProvider tool results", () => { .mockResolvedValueOnce({ code: "PAIRCODE", created: true }) .mockResolvedValueOnce({ code: "PAIRCODE", created: false }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } + const { controller, run } = startSlackMonitor(monitorSlackProvider); + const handler = await getSlackHandlerOrThrow("message"); const baseEvent = { type: "message", @@ -206,9 +146,7 @@ describe("monitorSlackProvider tool results", () => { await handler({ event: baseEvent }); await handler({ event: { ...baseEvent, ts: "124", text: "hello again" } }); - await flush(); - controller.abort(); - await run; + await stopSlackMonitor({ controller, run }); expect(sendMock).toHaveBeenCalledTimes(1); }); diff --git a/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts b/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts index 4e6169ba29..f9fc21705c 100644 --- a/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts +++ b/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts @@ -4,12 +4,13 @@ import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import { CURRENT_MESSAGE_MARKER } from "../auto-reply/reply/mentions.js"; import { defaultSlackTestConfig, - flush, getSlackTestState, getSlackClient, - getSlackHandlers, + getSlackHandlerOrThrow, resetSlackTestState, - waitForSlackEvent, + runSlackMessageOnce, + startSlackMonitor, + stopSlackMonitor, } from "./monitor.test-helpers.js"; const { monitorSlackProvider } = await import("./monitor.js"); @@ -26,20 +27,7 @@ describe("monitorSlackProvider tool results", () => { it("skips tool summaries with responsePrefix", async () => { replyMock.mockResolvedValue({ text: "final reply" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -50,10 +38,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0][1]).toBe("PFX final reply"); }); @@ -69,34 +53,21 @@ describe("monitorSlackProvider tool results", () => { api_app_id: "A1", }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "xapp-1-A1-abc", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ - body: { api_app_id: "A2", team_id: "T1" }, - event: { - type: "message", - user: "U1", - text: "hello", - ts: "123", - channel: "C1", - channel_type: "im", + await runSlackMessageOnce( + monitorSlackProvider, + { + body: { api_app_id: "A2", team_id: "T1" }, + event: { + type: "message", + user: "U1", + text: "hello", + ts: "123", + channel: "C1", + channel_type: "im", + }, }, - }); - - await flush(); - controller.abort(); - await run; + { appToken: "xapp-1-A1-abc" }, + ); expect(sendMock).not.toHaveBeenCalled(); expect(replyMock).not.toHaveBeenCalled(); @@ -134,20 +105,7 @@ describe("monitorSlackProvider tool results", () => { replyMock.mockResolvedValue({ text: "final reply" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -158,10 +116,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0][1]).toBe("final reply"); }); @@ -184,18 +138,8 @@ describe("monitorSlackProvider tool results", () => { return undefined; }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } + const { controller, run } = startSlackMonitor(monitorSlackProvider); + const handler = await getSlackHandlerOrThrow("message"); await handler({ event: { @@ -219,9 +163,7 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; + await stopSlackMonitor({ controller, run }); expect(replyMock).toHaveBeenCalledTimes(2); expect(capturedCtx.Body).not.toContain(HISTORY_CONTEXT_MARKER); @@ -249,18 +191,8 @@ describe("monitorSlackProvider tool results", () => { return undefined; }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } + const { controller, run } = startSlackMonitor(monitorSlackProvider); + const handler = await getSlackHandlerOrThrow("message"); await handler({ event: { @@ -298,9 +230,7 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; + await stopSlackMonitor({ controller, run }); expect(replyMock).toHaveBeenCalledTimes(2); expect(capturedCtx[0]?.Body).toContain("thread-a-one"); @@ -314,20 +244,7 @@ describe("monitorSlackProvider tool results", () => { return { text: "final reply" }; }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -338,10 +255,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - const client = getSlackClient() as { assistant?: { threads?: { setStatus?: ReturnType } }; }; @@ -376,20 +289,7 @@ describe("monitorSlackProvider tool results", () => { }; replyMock.mockResolvedValue({ text: "hi" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -400,10 +300,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true); }); @@ -423,20 +319,7 @@ describe("monitorSlackProvider tool results", () => { }; replyMock.mockResolvedValue({ text: "hi" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -447,10 +330,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true); }); @@ -466,20 +345,7 @@ describe("monitorSlackProvider tool results", () => { }; replyMock.mockResolvedValue({ text: "hi" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -492,10 +358,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true); }); @@ -512,20 +374,7 @@ describe("monitorSlackProvider tool results", () => { }; replyMock.mockResolvedValue({ text: "hi" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -536,10 +385,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); expect(replyMock.mock.calls[0][0].WasMentioned).toBe(false); expect(sendMock).toHaveBeenCalledTimes(1); @@ -548,20 +393,7 @@ describe("monitorSlackProvider tool results", () => { it("treats control commands as mentions for group bypass", async () => { replyMock.mockResolvedValue({ text: "ok" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -572,10 +404,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true); }); @@ -596,20 +424,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -621,10 +436,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: "456" }); }); diff --git a/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts b/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts index 15a570ec4a..7e2574d696 100644 --- a/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts +++ b/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts @@ -2,12 +2,10 @@ import { beforeEach, describe, expect, it } from "vitest"; import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import { defaultSlackTestConfig, - flush, getSlackClient, - getSlackHandlers, getSlackTestState, resetSlackTestState, - waitForSlackEvent, + runSlackMessageOnce, } from "./monitor.test-helpers.js"; const { monitorSlackProvider } = await import("./monitor.js"); @@ -37,20 +35,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -61,10 +46,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: "123" }); }); @@ -72,20 +53,7 @@ describe("monitorSlackProvider tool results", () => { it("treats parent_user_id as a thread reply even when thread_ts matches ts", async () => { replyMock.mockResolvedValue({ text: "thread reply" }); - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -98,10 +66,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); const ctx = replyMock.mock.calls[0]?.[0] as { SessionKey?: string; @@ -125,20 +89,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -150,10 +101,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); const ctx = replyMock.mock.calls[0]?.[0] as { SessionKey?: string; @@ -188,20 +135,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -213,10 +147,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); const ctx = replyMock.mock.calls[0]?.[0] as { SessionKey?: string; @@ -256,20 +186,7 @@ describe("monitorSlackProvider tool results", () => { }); } - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -281,10 +198,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(replyMock).toHaveBeenCalledTimes(1); const ctx = replyMock.mock.calls[0]?.[0] as { SessionKey?: string; @@ -310,20 +223,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -334,10 +234,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: undefined }); }); @@ -358,20 +254,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - const controller = new AbortController(); - const run = monitorSlackProvider({ - botToken: "bot-token", - appToken: "app-token", - abortSignal: controller.signal, - }); - - await waitForSlackEvent("message"); - const handler = getSlackHandlers()?.get("message"); - if (!handler) { - throw new Error("Slack message handler not registered"); - } - - await handler({ + await runSlackMessageOnce(monitorSlackProvider, { event: { type: "message", user: "U1", @@ -382,10 +265,6 @@ describe("monitorSlackProvider tool results", () => { }, }); - await flush(); - controller.abort(); - await run; - expect(sendMock).toHaveBeenCalledTimes(1); // First reply starts a thread under the incoming message expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: "789" });