diff --git a/extensions/line/src/send.test.ts b/extensions/line/src/send.test.ts index 3c8db94ca5d..5acc642deec 100644 --- a/extensions/line/src/send.test.ts +++ b/extensions/line/src/send.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const { pushMessageMock, @@ -93,8 +93,11 @@ const LINE_TEST_CFG = { }; describe("LINE send helpers", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + sendModule = await import("./send.js"); + }); + + beforeEach(() => { pushMessageMock.mockReset(); replyMessageMock.mockReset(); showLoadingAnimationMock.mockReset(); @@ -125,7 +128,6 @@ describe("LINE send helpers", () => { pushMessageMock.mockResolvedValue({}); replyMessageMock.mockResolvedValue({}); showLoadingAnimationMock.mockResolvedValue({}); - sendModule = await import("./send.js"); }); afterEach(() => { diff --git a/extensions/line/src/setup-surface.test.ts b/extensions/line/src/setup-surface.test.ts index 943b487f1a0..442365778df 100644 --- a/extensions/line/src/setup-surface.test.ts +++ b/extensions/line/src/setup-surface.test.ts @@ -3,7 +3,6 @@ import path from "node:path"; import ts from "typescript"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { bundledPluginRoot } from "../../../test/helpers/bundled-plugin-paths.js"; -import { loadRuntimeApiExportTypesViaJiti } from "../../../test/helpers/plugins/jiti-runtime-api.ts"; import { createPluginSetupWizardConfigure, createTestWizardPrompter, @@ -375,30 +374,6 @@ describe("linePlugin status.probeAccount", () => { }); describe("line runtime api", () => { - it("loads through Jiti without duplicate export errors", () => { - const runtimeApiPath = path.join(process.cwd(), "extensions", "line", "runtime-api.ts"); - - expect( - loadRuntimeApiExportTypesViaJiti({ - modulePath: runtimeApiPath, - exportNames: [ - "buildTemplateMessageFromPayload", - "downloadLineMedia", - "isSenderAllowed", - "probeLineBot", - "pushMessageLine", - ], - realPluginSdkSpecifiers: ["openclaw/plugin-sdk/line-runtime"], - }), - ).toEqual({ - buildTemplateMessageFromPayload: "function", - downloadLineMedia: "function", - isSenderAllowed: "function", - probeLineBot: "function", - pushMessageLine: "function", - }); - }, 240_000); - it("keeps the LINE runtime barrel self-contained", () => { const runtimeApiPath = path.join(process.cwd(), "extensions", "line", "runtime-api.ts"); expect(collectRuntimeApiPreExports(runtimeApiPath)).toEqual([]); diff --git a/extensions/slack/src/monitor/events/interactions.test.ts b/extensions/slack/src/monitor/events/interactions.test.ts index a9b8236e852..c575e9ada5c 100644 --- a/extensions/slack/src/monitor/events/interactions.test.ts +++ b/extensions/slack/src/monitor/events/interactions.test.ts @@ -12,10 +12,92 @@ const resolvePluginConversationBindingApprovalMock = vi.hoisted(() => vi.fn()); const buildPluginBindingResolvedTextMock = vi.hoisted(() => vi.fn(() => "Binding updated.")); let registerSlackInteractionEvents: typeof import("./interactions.js").registerSlackInteractionEvents; -let enqueueSystemEventSpy: ReturnType; -let dispatchPluginInteractiveHandlerSpy: ReturnType; -let resolvePluginConversationBindingApprovalSpy: ReturnType; -let buildPluginBindingResolvedTextSpy: ReturnType; + +vi.mock("openclaw/plugin-sdk/infra-runtime", () => ({ + enqueueSystemEvent: (...args: unknown[]) => enqueueSystemEventMock(...args), +})); + +vi.mock("../../interactive-dispatch.js", () => ({ + dispatchSlackPluginInteractiveHandler: (params: { + data: string; + interactionId: string; + ctx: { + interaction?: Record; + } & Record; + respond: unknown; + }) => + (dispatchPluginInteractiveHandlerMock as (arg: unknown) => Promise)({ + channel: "slack", + data: params.data, + dedupeId: params.interactionId, + invoke: async ({ + registration, + namespace, + payload, + }: { + registration: { handler: (ctx: unknown) => unknown }; + namespace: string; + payload: string; + }) => + registration.handler({ + ...params.ctx, + channel: "slack", + interaction: { + ...params.ctx.interaction, + data: params.data, + namespace, + payload, + }, + respond: params.respond, + requestConversationBinding: vi.fn(), + detachConversationBinding: vi.fn(), + getCurrentConversationBinding: vi.fn(), + }), + }), +})); + +vi.mock("../conversation.runtime.js", () => { + const parsePluginBindingApprovalCustomId = (value: string) => { + const prefix = "pluginbind:"; + const trimmed = value.trim(); + if (!trimmed.startsWith(prefix)) { + return null; + } + const body = trimmed.slice(prefix.length); + const separator = body.lastIndexOf(":"); + if (separator <= 0 || separator === body.length - 1) { + return null; + } + const decisionCode = body.slice(separator + 1).trim(); + const decision = + decisionCode === "o" + ? "allow-once" + : decisionCode === "a" + ? "allow-always" + : decisionCode === "d" + ? "deny" + : null; + if (!decision) { + return null; + } + return { + approvalId: decodeURIComponent(body.slice(0, separator).trim()), + decision, + }; + }; + + return { + buildPluginBindingResolvedText: (...args: unknown[]) => + (buildPluginBindingResolvedTextMock as (...innerArgs: unknown[]) => string)(...args), + parsePluginBindingApprovalCustomId, + resolvePluginConversationBindingApproval: (...args: unknown[]) => + ( + resolvePluginConversationBindingApprovalMock as ( + ...innerArgs: unknown[] + ) => Promise + )(...args), + }; +}); type RegisteredHandler = (args: { ack: () => Promise; @@ -167,49 +249,10 @@ function createContext(overrides?: { describe("registerSlackInteractionEvents", () => { beforeAll(async () => { - const channelRuntime = await import("openclaw/plugin-sdk/infra-runtime"); - const pluginRuntime = await import("openclaw/plugin-sdk/plugin-runtime"); - const conversationBinding = await import("../../../../../src/plugins/conversation-binding.js"); - enqueueSystemEventSpy = vi - .spyOn(channelRuntime, "enqueueSystemEvent") - .mockImplementation(((...args: Parameters) => - (enqueueSystemEventMock as (...innerArgs: unknown[]) => boolean)( - ...args, - )) as typeof channelRuntime.enqueueSystemEvent); - dispatchPluginInteractiveHandlerSpy = vi - .spyOn(pluginRuntime, "dispatchPluginInteractiveHandler") - .mockImplementation((( - ...args: Parameters - ) => - (dispatchPluginInteractiveHandlerMock as (...innerArgs: unknown[]) => Promise)( - ...args, - )) as typeof pluginRuntime.dispatchPluginInteractiveHandler); - resolvePluginConversationBindingApprovalSpy = vi - .spyOn(conversationBinding, "resolvePluginConversationBindingApproval") - .mockImplementation((( - ...args: Parameters - ) => - ( - resolvePluginConversationBindingApprovalMock as ( - ...innerArgs: unknown[] - ) => Promise - )(...args)) as typeof conversationBinding.resolvePluginConversationBindingApproval); - buildPluginBindingResolvedTextSpy = vi - .spyOn(conversationBinding, "buildPluginBindingResolvedText") - .mockImplementation((( - ...args: Parameters - ) => - (buildPluginBindingResolvedTextMock as (...innerArgs: unknown[]) => string)( - ...args, - )) as typeof conversationBinding.buildPluginBindingResolvedText); ({ registerSlackInteractionEvents } = await import("./interactions.js")); }); beforeEach(() => { - enqueueSystemEventSpy.mockClear(); - dispatchPluginInteractiveHandlerSpy.mockClear(); - resolvePluginConversationBindingApprovalSpy.mockClear(); - buildPluginBindingResolvedTextSpy.mockClear(); enqueueSystemEventMock.mockClear(); dispatchPluginInteractiveHandlerMock.mockClear(); resolvePluginConversationBindingApprovalMock.mockClear(); diff --git a/extensions/slack/src/monitor/slash.test-harness.ts b/extensions/slack/src/monitor/slash.test-harness.ts index 172f6f516f2..cf58259efe3 100644 --- a/extensions/slack/src/monitor/slash.test-harness.ts +++ b/extensions/slack/src/monitor/slash.test-harness.ts @@ -11,16 +11,15 @@ const mocks = vi.hoisted(() => ({ resolveStorePathMock: vi.fn(), })); -vi.mock("./slash-dispatch.runtime.js", async () => { - const actual = await vi.importActual( - "./slash-dispatch.runtime.js", - ); +vi.mock("./slash-dispatch.runtime.js", () => { return { - ...actual, + deliverSlackSlashReplies: vi.fn(async () => {}), dispatchReplyWithDispatcher: (...args: unknown[]) => mocks.dispatchMock(...args), finalizeInboundContext: (...args: unknown[]) => mocks.finalizeInboundContextMock(...args), resolveAgentRoute: (...args: unknown[]) => mocks.resolveAgentRouteMock(...args), + resolveChunkMode: vi.fn(() => "auto"), resolveConversationLabel: (...args: unknown[]) => mocks.resolveConversationLabelMock(...args), + resolveMarkdownTableMode: vi.fn(() => "auto"), recordInboundSessionMetaSafe: (...args: unknown[]) => mocks.recordSessionMetaFromInboundMock(...args), };