From be0329eda2fc5ac482b6b5cd9ade3d9ac5f7b8e2 Mon Sep 17 00:00:00 2001 From: joshavant <830519+joshavant@users.noreply.github.com> Date: Wed, 18 Mar 2026 22:06:19 -0500 Subject: [PATCH] Slack tests: align slash harness runtime mocks --- extensions/slack/src/blocks.test-helpers.ts | 46 +++++++----- extensions/slack/src/monitor.test-helpers.ts | 75 ++++++++----------- .../slack/src/monitor/slash.test-harness.ts | 19 +---- 3 files changed, 64 insertions(+), 76 deletions(-) diff --git a/extensions/slack/src/blocks.test-helpers.ts b/extensions/slack/src/blocks.test-helpers.ts index ce628d73449..ae5c92818d1 100644 --- a/extensions/slack/src/blocks.test-helpers.ts +++ b/extensions/slack/src/blocks.test-helpers.ts @@ -1,23 +1,6 @@ import type { WebClient } from "@slack/web-api"; import { vi } from "vitest"; -vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - loadConfig: () => ({}), - }; -}); - -vi.mock("./accounts.js", () => ({ - resolveSlackAccount: () => ({ - accountId: "default", - botToken: "xoxb-test", - botTokenSource: "config", - config: {}, - }), -})); - export type SlackEditTestClient = WebClient & { chat: { update: ReturnType; @@ -33,8 +16,35 @@ export type SlackSendTestClient = WebClient & { }; }; +const slackBlockTestState = vi.hoisted(() => ({ + account: { + accountId: "default", + botToken: "xoxb-test", + botTokenSource: "config", + config: {}, + }, + config: {}, +})); + +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadConfig: () => slackBlockTestState.config, + }; +}); + +vi.mock("./accounts.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + resolveSlackAccount: () => slackBlockTestState.account, + }; +}); + +// Kept for compatibility with existing tests; mocks install at module evaluation. export function installSlackBlockTestMocks() { - // Backward compatible no-op. Mocks are hoisted at module scope. + return; } export function createSlackEditTestClient(): SlackEditTestClient { diff --git a/extensions/slack/src/monitor.test-helpers.ts b/extensions/slack/src/monitor.test-helpers.ts index 87443e5332c..9980c34e29b 100644 --- a/extensions/slack/src/monitor.test-helpers.ts +++ b/extensions/slack/src/monitor.test-helpers.ts @@ -202,37 +202,30 @@ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => { const actual = await importOriginal(); + const replyResolver: typeof actual.getReplyFromConfig = (...args) => + slackTestState.replyMock(...args) as ReturnType; return { ...actual, - dispatchInboundMessage: async (params: { - ctx: unknown; - replyOptions?: { - onReplyStart?: () => Promise | void; - onAssistantMessageStart?: () => Promise | void; - }; - dispatcher: { - sendFinalReply: (payload: unknown) => boolean; - waitForIdle: () => Promise; - markComplete: () => void; - }; - }) => { - const reply = await slackTestState.replyMock(params.ctx, { - ...params.replyOptions, - onReplyStart: - params.replyOptions?.onReplyStart ?? params.replyOptions?.onAssistantMessageStart, - }); - const queuedFinal = reply ? params.dispatcher.sendFinalReply(reply) : false; - params.dispatcher.markComplete(); - await params.dispatcher.waitForIdle(); - return { - queuedFinal, - counts: { - tool: 0, - block: 0, - final: queuedFinal ? 1 : 0, - }, - }; - }, + getReplyFromConfig: replyResolver, + dispatchInboundMessage: (params: Parameters[0]) => + actual.dispatchInboundMessage({ + ...params, + replyResolver, + }), + dispatchInboundMessageWithBufferedDispatcher: ( + params: Parameters[0], + ) => + actual.dispatchInboundMessageWithBufferedDispatcher({ + ...params, + replyResolver, + }), + dispatchInboundMessageWithDispatcher: ( + params: Parameters[0], + ) => + actual.dispatchInboundMessageWithDispatcher({ + ...params, + replyResolver, + }), }; }); @@ -246,9 +239,13 @@ vi.mock("./resolve-users.js", () => ({ entries.map((input) => ({ input, resolved: false })), })); -vi.mock("./send.js", () => ({ - sendMessageSlack: (...args: unknown[]) => slackTestState.sendMock(...args), -})); +vi.mock("./send.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + sendMessageSlack: (...args: unknown[]) => slackTestState.sendMock(...args), + }; +}); vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { const actual = await importOriginal(); @@ -265,20 +262,12 @@ vi.mock("@slack/bolt", () => { const { handlers, client: slackClient } = ensureSlackTestRuntime(); class App { client = slackClient; - receiver = { - client: { - on: vi.fn(), - off: vi.fn(), - }, - }; event(name: string, handler: SlackHandler) { handlers.set(name, handler); } - command = vi.fn(); - action = vi.fn(); - options = vi.fn(); - view = vi.fn(); - shortcut = vi.fn(); + command() { + /* no-op */ + } start = vi.fn().mockResolvedValue(undefined); stop = vi.fn().mockResolvedValue(undefined); } diff --git a/extensions/slack/src/monitor/slash.test-harness.ts b/extensions/slack/src/monitor/slash.test-harness.ts index d8f09d74cda..48a11cf3460 100644 --- a/extensions/slack/src/monitor/slash.test-harness.ts +++ b/extensions/slack/src/monitor/slash.test-harness.ts @@ -7,7 +7,7 @@ const mocks = vi.hoisted(() => ({ resolveAgentRouteMock: vi.fn(), finalizeInboundContextMock: vi.fn(), resolveConversationLabelMock: vi.fn(), - createChannelReplyPipelineMock: vi.fn(), + createReplyPrefixOptionsMock: vi.fn(), recordSessionMetaFromInboundMock: vi.fn(), resolveStorePathMock: vi.fn(), })); @@ -43,27 +43,16 @@ vi.mock("openclaw/plugin-sdk/channel-runtime", async (importOriginal) => { return { ...actual, resolveConversationLabel: (...args: unknown[]) => mocks.resolveConversationLabelMock(...args), + createReplyPrefixOptions: (...args: unknown[]) => mocks.createReplyPrefixOptionsMock(...args), recordInboundSessionMetaSafe: (...args: unknown[]) => mocks.recordSessionMetaFromInboundMock(...args), }; }); -vi.mock("openclaw/plugin-sdk/channel-reply-pipeline", async (importOriginal) => { - const actual = - await importOriginal(); - return { - ...actual, - createChannelReplyPipeline: (...args: unknown[]) => - mocks.createChannelReplyPipelineMock(...args), - }; -}); - vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - recordSessionMetaFromInbound: (...args: unknown[]) => - mocks.recordSessionMetaFromInboundMock(...args), resolveStorePath: (...args: unknown[]) => mocks.resolveStorePathMock(...args), }; }); @@ -75,7 +64,7 @@ type SlashHarnessMocks = { resolveAgentRouteMock: ReturnType; finalizeInboundContextMock: ReturnType; resolveConversationLabelMock: ReturnType; - createChannelReplyPipelineMock: ReturnType; + createReplyPrefixOptionsMock: ReturnType; recordSessionMetaFromInboundMock: ReturnType; resolveStorePathMock: ReturnType; }; @@ -95,7 +84,7 @@ export function resetSlackSlashMocks() { }); mocks.finalizeInboundContextMock.mockReset().mockImplementation((ctx: unknown) => ctx); mocks.resolveConversationLabelMock.mockReset().mockReturnValue(undefined); - mocks.createChannelReplyPipelineMock.mockReset().mockReturnValue({ onModelSelected: () => {} }); + mocks.createReplyPrefixOptionsMock.mockReset().mockReturnValue({ onModelSelected: () => {} }); mocks.recordSessionMetaFromInboundMock.mockReset().mockResolvedValue(undefined); mocks.resolveStorePathMock.mockReset().mockReturnValue("/tmp/openclaw-sessions.json"); }