From 553cbccd4085e3a97185d2f7b84be876a7c0fcbb Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 15 Mar 2026 17:48:59 -0700 Subject: [PATCH] Tests: cover Slack shared interactive dispatcher --- src/plugins/interactive.test.ts | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/plugins/interactive.test.ts b/src/plugins/interactive.test.ts index f794cde4037..4e50ca28dda 100644 --- a/src/plugins/interactive.test.ts +++ b/src/plugins/interactive.test.ts @@ -147,6 +147,71 @@ describe("plugin interactive handlers", () => { ); }); + it("routes Slack interactions by namespace and dedupes interaction ids", async () => { + const handler = vi.fn(async () => ({ handled: true })); + expect( + registerPluginInteractiveHandler("codex-plugin", { + channel: "slack", + namespace: "codex", + handler, + }), + ).toEqual({ ok: true }); + + const baseParams = { + channel: "slack" as const, + data: "codex:approve:thread-1", + interactionId: "slack-ix-1", + ctx: { + accountId: "default", + interactionId: "slack-ix-1", + conversationId: "C123", + parentConversationId: "C123", + threadId: "1710000000.000100", + senderId: "U123", + senderUsername: "ada", + auth: { isAuthorizedSender: true }, + interaction: { + kind: "button" as const, + actionId: "codex", + blockId: "codex_actions", + messageTs: "1710000000.000200", + threadTs: "1710000000.000100", + value: "approve:thread-1", + selectedValues: ["approve:thread-1"], + selectedLabels: ["Approve"], + triggerId: "trigger-1", + responseUrl: "https://hooks.slack.test/response", + }, + }, + respond: { + acknowledge: vi.fn(async () => {}), + reply: vi.fn(async () => {}), + followUp: vi.fn(async () => {}), + editMessage: vi.fn(async () => {}), + }, + }; + + const first = await dispatchPluginInteractiveHandler(baseParams); + const duplicate = await dispatchPluginInteractiveHandler(baseParams); + + expect(first).toEqual({ matched: true, handled: true, duplicate: false }); + expect(duplicate).toEqual({ matched: true, handled: true, duplicate: true }); + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith( + expect.objectContaining({ + channel: "slack", + conversationId: "C123", + threadId: "1710000000.000100", + interaction: expect.objectContaining({ + namespace: "codex", + payload: "approve:thread-1", + actionId: "codex", + messageTs: "1710000000.000200", + }), + }), + ); + }); + it("does not consume dedupe keys when a handler throws", async () => { const handler = vi .fn(async () => ({ handled: true }))