diff --git a/extensions/discord/src/monitor/message-handler.process.test.ts b/extensions/discord/src/monitor/message-handler.process.test.ts index e656631b156..79eb5915c2d 100644 --- a/extensions/discord/src/monitor/message-handler.process.test.ts +++ b/extensions/discord/src/monitor/message-handler.process.test.ts @@ -36,11 +36,48 @@ const deliveryMocks = vi.hoisted(() => ({ ) => Promise >(async () => ({ id: "m1" }) as import("discord-api-types/v10").APIMessage), deliverDiscordReply: vi.fn<(params: unknown) => Promise>(async () => {}), - createDiscordDraftStream: vi.fn(() => createMockDraftStream()), + createDiscordDraftStream: vi.fn<(params: unknown) => ReturnType>( + () => createMockDraftStream(), + ), })); const editMessageDiscord = deliveryMocks.editMessageDiscord; const deliverDiscordReply = deliveryMocks.deliverDiscordReply; const createDiscordDraftStream = deliveryMocks.createDiscordDraftStream; + +vi.mock("../send.js", () => ({ + reactMessageDiscord: async ( + channelId: string, + messageId: string, + emoji: string, + opts?: unknown, + ) => { + await sendMocks.reactMessageDiscord(channelId, messageId, emoji, opts); + return { ok: true }; + }, + removeReactionDiscord: async ( + channelId: string, + messageId: string, + emoji: string, + opts?: unknown, + ) => { + await sendMocks.removeReactionDiscord(channelId, messageId, emoji, opts); + return { ok: true }; + }, +})); + +vi.mock("../send.messages.js", () => ({ + editMessageDiscord: (channelId: string, messageId: string, payload: unknown, opts?: unknown) => + deliveryMocks.editMessageDiscord(channelId, messageId, payload, opts), +})); + +vi.mock("../draft-stream.js", () => ({ + createDiscordDraftStream: (params: unknown) => deliveryMocks.createDiscordDraftStream(params), +})); + +vi.mock("./reply-delivery.js", () => ({ + deliverDiscordReply: (params: unknown) => deliveryMocks.deliverDiscordReply(params), +})); + type DispatchInboundParams = { dispatcher: { sendBlockReply: (payload: ReplyPayload) => boolean | Promise; @@ -101,94 +138,70 @@ const configSessionsMocks = vi.hoisted(() => ({ })); const readSessionUpdatedAt = configSessionsMocks.readSessionUpdatedAt; const resolveStorePath = configSessionsMocks.resolveStorePath; +const createDiscordRestClientSpy = vi.hoisted(() => + vi.fn< + (params: unknown) => { + token: string; + rest: object; + account: { accountId: string; config: object }; + } + >(() => ({ + token: "token", + rest: {}, + account: { accountId: "default", config: {} }, + })), +); let createBaseDiscordMessageContext: typeof import("./message-handler.test-harness.js").createBaseDiscordMessageContext; let createDiscordDirectMessageContextOverrides: typeof import("./message-handler.test-harness.js").createDiscordDirectMessageContextOverrides; let threadBindingTesting: typeof import("./thread-bindings.js").__testing; let createThreadBindingManager: typeof import("./thread-bindings.js").createThreadBindingManager; let processDiscordMessage: typeof import("./message-handler.process.js").processDiscordMessage; -const sendModule = await import("../send.js"); -vi.spyOn(sendModule, "reactMessageDiscord").mockImplementation( - async (channelId, messageId, emoji, opts) => { - await sendMocks.reactMessageDiscord(channelId, messageId, emoji, opts); - return { ok: true }; - }, -); -vi.spyOn(sendModule, "removeReactionDiscord").mockImplementation( - async (channelId, messageId, emoji, opts) => { - await sendMocks.removeReactionDiscord(channelId, messageId, emoji, opts); - return { ok: true }; - }, -); +vi.mock("openclaw/plugin-sdk/reply-runtime", () => ({ + dispatchInboundMessage: (params: DispatchInboundParams) => dispatchInboundMessage(params), + createReplyDispatcherWithTyping: (opts: { + deliver: (payload: unknown, info: { kind: string }) => Promise | void; + }) => ({ + dispatcher: { + sendToolResult: vi.fn(() => true), + sendBlockReply: vi.fn((payload: unknown) => { + void opts.deliver(payload, { kind: "block" }); + return true; + }), + sendFinalReply: vi.fn((payload: unknown) => { + void opts.deliver(payload, { kind: "final" }); + return true; + }), + waitForIdle: vi.fn(async () => {}), + getQueuedCounts: vi.fn(() => ({ tool: 0, block: 0, final: 0 })), + markComplete: vi.fn(), + }, + replyOptions: {}, + markDispatchIdle: vi.fn(), + markRunComplete: vi.fn(), + }), +})); -const sendMessagesModule = await import("../send.messages.js"); -vi.spyOn(sendMessagesModule, "editMessageDiscord").mockImplementation( - (( - channelId: Parameters[0], - messageId: Parameters[1], - payload: Parameters[2], - opts: Parameters[3], - ) => deliveryMocks.editMessageDiscord(channelId, messageId, payload, opts) as never) as never, -); +vi.mock("openclaw/plugin-sdk/conversation-runtime", () => ({ + recordInboundSession: (...args: unknown[]) => recordInboundSession(...args), + registerSessionBindingAdapter: vi.fn(), + unregisterSessionBindingAdapter: vi.fn(), + resolveThreadBindingConversationIdFromBindingId: (bindingId: string) => + bindingId.split(":").at(-1) ?? bindingId, +})); -const draftStreamModule = await import("../draft-stream.js"); -vi.spyOn(draftStreamModule, "createDiscordDraftStream").mockImplementation( - deliveryMocks.createDiscordDraftStream, -); +vi.mock("openclaw/plugin-sdk/session-store-runtime", () => ({ + readSessionUpdatedAt: (...args: unknown[]) => configSessionsMocks.readSessionUpdatedAt(...args), + resolveStorePath: (...args: unknown[]) => configSessionsMocks.resolveStorePath(...args), +})); -const replyDeliveryModule = await import("./reply-delivery.js"); -vi.spyOn(replyDeliveryModule, "deliverDiscordReply").mockImplementation( - ((params: Parameters[0]) => - deliveryMocks.deliverDiscordReply(params) as never) as never, -); - -const replyRuntimeModule = await import("openclaw/plugin-sdk/reply-runtime"); -vi.spyOn(replyRuntimeModule, "dispatchInboundMessage").mockImplementation( - ((params: Parameters[0]) => - dispatchInboundMessage(params as DispatchInboundParams) as never) as never, -); -vi.spyOn(replyRuntimeModule, "createReplyDispatcherWithTyping").mockImplementation(((opts: { - deliver: (payload: unknown, info: { kind: string }) => Promise | void; -}) => ({ - dispatcher: { - sendToolResult: vi.fn(() => true), - sendBlockReply: vi.fn((payload: unknown) => { - void opts.deliver(payload as never, { kind: "block" }); - return true; - }), - sendFinalReply: vi.fn((payload: unknown) => { - void opts.deliver(payload as never, { kind: "final" }); - return true; - }), - waitForIdle: vi.fn(async () => {}), - getQueuedCounts: vi.fn(() => ({ tool: 0, block: 0, final: 0 })), - markComplete: vi.fn(), - }, - replyOptions: {}, - markDispatchIdle: vi.fn(), - markRunComplete: vi.fn(), -})) as never); - -const conversationRuntimeModule = await import("openclaw/plugin-sdk/conversation-runtime"); -vi.spyOn(conversationRuntimeModule, "recordInboundSession").mockImplementation( - ((params: Parameters[0]) => - recordInboundSession(params) as never) as never, -); - -const sessionStoreRuntimeModule = await import("openclaw/plugin-sdk/session-store-runtime"); -vi.spyOn(sessionStoreRuntimeModule, "readSessionUpdatedAt").mockImplementation( - ((params: Parameters[0]) => - configSessionsMocks.readSessionUpdatedAt(params) as never) as never, -); -vi.spyOn(sessionStoreRuntimeModule, "resolveStorePath").mockImplementation( - (( - path: Parameters[0], - opts: Parameters[1], - ) => configSessionsMocks.resolveStorePath(path, opts) as never) as never, -); - -const clientModule = await import("../client.js"); -const createDiscordRestClientSpy = vi.spyOn(clientModule, "createDiscordRestClient"); +vi.mock("../client.js", () => ({ + createDiscordRuntimeAccountContext: (params: { cfg: unknown; accountId: string }) => ({ + cfg: params.cfg, + accountId: params.accountId, + }), + createDiscordRestClient: (params: unknown) => createDiscordRestClientSpy(params), +})); const BASE_CHANNEL_ROUTE = { agentId: "main",