diff --git a/extensions/discord/src/send.webhook-activity.test.ts b/extensions/discord/src/send.webhook-activity.test.ts index 04354936050..be6fec554c5 100644 --- a/extensions/discord/src/send.webhook-activity.test.ts +++ b/extensions/discord/src/send.webhook-activity.test.ts @@ -4,16 +4,16 @@ import { sendWebhookMessageDiscord } from "./send.js"; const recordChannelActivityMock = vi.hoisted(() => vi.fn()); const loadConfigMock = vi.hoisted(() => vi.fn(() => ({ channels: { discord: {} } }))); -vi.mock("../../../src/config/config.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, loadConfig: () => loadConfigMock(), }; }); -vi.mock("../../../src/infra/channel-activity.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, recordChannelActivity: (...args: unknown[]) => recordChannelActivityMock(...args), diff --git a/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts b/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts index d98bbec9e7c..3ab43677868 100644 --- a/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts @@ -64,13 +64,6 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock("../../../src/infra/outbound/session-binding-service.js", () => ({ - getSessionBindingService: () => ({ - resolveByConversation: resolveBoundConversationMock, - touch: touchBindingMock, - }), -})); - function createLifecycleConfig(): ClawdbotConfig { return { session: { mainKey: "main", scope: "per-sender" }, diff --git a/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts b/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts index e235af4d8ec..cd988862bdf 100644 --- a/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts @@ -75,13 +75,6 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock("../../../src/infra/outbound/session-binding-service.js", () => ({ - getSessionBindingService: () => ({ - resolveByConversation: resolveBoundConversationMock, - touch: touchBindingMock, - }), -})); - function createLifecycleConfig(): ClawdbotConfig { return { channels: { diff --git a/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts b/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts index 839ea934454..da29c4a6dbb 100644 --- a/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts @@ -71,13 +71,6 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock("../../../src/infra/outbound/session-binding-service.js", () => ({ - getSessionBindingService: () => ({ - resolveByConversation: resolveBoundConversationMock, - touch: touchBindingMock, - }), -})); - function createLifecycleConfig(): ClawdbotConfig { return { broadcast: { diff --git a/extensions/feishu/src/monitor.card-action.lifecycle.test.ts b/extensions/feishu/src/monitor.card-action.lifecycle.test.ts index c5908b29487..de34dafa29a 100644 --- a/extensions/feishu/src/monitor.card-action.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.card-action.lifecycle.test.ts @@ -77,13 +77,6 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock("../../../src/infra/outbound/session-binding-service.js", () => ({ - getSessionBindingService: () => ({ - resolveByConversation: resolveBoundConversationMock, - touch: touchBindingMock, - }), -})); - function createLifecycleConfig(): ClawdbotConfig { return { channels: { diff --git a/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts b/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts index 4a965110613..43ba7bdf3e6 100644 --- a/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts @@ -71,13 +71,6 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock("../../../src/infra/outbound/session-binding-service.js", () => ({ - getSessionBindingService: () => ({ - resolveByConversation: resolveBoundConversationMock, - touch: touchBindingMock, - }), -})); - function createLifecycleConfig(): ClawdbotConfig { return { messages: { diff --git a/extensions/slack/src/monitor/events/interactions.test.ts b/extensions/slack/src/monitor/events/interactions.test.ts index 5b71ebd11fd..0e2599f0747 100644 --- a/extensions/slack/src/monitor/events/interactions.test.ts +++ b/extensions/slack/src/monitor/events/interactions.test.ts @@ -10,20 +10,20 @@ const dispatchPluginInteractiveHandlerMock = vi.fn(async () => ({ const resolvePluginConversationBindingApprovalMock = vi.fn(); const buildPluginBindingResolvedTextMock = vi.fn(() => "Binding updated."); -vi.mock("../../../../../src/infra/system-events.js", () => ({ +vi.mock("openclaw/plugin-sdk/infra-runtime", () => ({ enqueueSystemEvent: (...args: unknown[]) => (enqueueSystemEventMock as (...innerArgs: unknown[]) => unknown)(...args), })); -vi.mock("../../../../../src/plugins/interactive.js", () => ({ +vi.mock("openclaw/plugin-sdk/plugin-runtime", () => ({ dispatchPluginInteractiveHandler: (...args: unknown[]) => (dispatchPluginInteractiveHandlerMock as (...innerArgs: unknown[]) => unknown)(...args), })); -vi.mock("../../../../../src/plugins/conversation-binding.js", async () => { - const actual = await vi.importActual< - typeof import("../../../../../src/plugins/conversation-binding.js") - >("../../../../../src/plugins/conversation-binding.js"); +vi.mock("openclaw/plugin-sdk/conversation-runtime", async () => { + const actual = await vi.importActual( + "openclaw/plugin-sdk/conversation-runtime", + ); return { ...actual, resolvePluginConversationBindingApproval: (...args: unknown[]) => diff --git a/extensions/telegram/src/bot-message-context.thread-binding.test.ts b/extensions/telegram/src/bot-message-context.thread-binding.test.ts index e635b6f4a11..70a5d4f8c1c 100644 --- a/extensions/telegram/src/bot-message-context.thread-binding.test.ts +++ b/extensions/telegram/src/bot-message-context.thread-binding.test.ts @@ -9,9 +9,8 @@ const hoisted = vi.hoisted(() => { }; }); -vi.mock("../../../src/infra/outbound/session-binding-service.js", async (importOriginal) => { - const actual = - await importOriginal(); +vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, getSessionBindingService: () => ({ diff --git a/extensions/telegram/src/bot-message-context.topic-agentid.test.ts b/extensions/telegram/src/bot-message-context.topic-agentid.test.ts index 57c0c8209a0..6a278b4c1a8 100644 --- a/extensions/telegram/src/bot-message-context.topic-agentid.test.ts +++ b/extensions/telegram/src/bot-message-context.topic-agentid.test.ts @@ -1,5 +1,5 @@ +import { loadConfig } from "openclaw/plugin-sdk/config-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { loadConfig } from "../../../src/config/config.js"; const { defaultRouteConfig } = vi.hoisted(() => ({ defaultRouteConfig: { @@ -11,8 +11,8 @@ const { defaultRouteConfig } = vi.hoisted(() => ({ }, })); -vi.mock("../../../src/config/config.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, loadConfig: vi.fn(() => defaultRouteConfig), diff --git a/extensions/telegram/src/bot-native-commands.session-meta.test.ts b/extensions/telegram/src/bot-native-commands.session-meta.test.ts index bfe314d4140..0ed7c389098 100644 --- a/extensions/telegram/src/bot-native-commands.session-meta.test.ts +++ b/extensions/telegram/src/bot-native-commands.session-meta.test.ts @@ -1,5 +1,5 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { OpenClawConfig } from "../../../src/config/config.js"; import type { ResolvedAgentRoute } from "../../../src/routing/resolve-route.js"; import type { TelegramBotDeps } from "./bot-deps.js"; import { @@ -56,6 +56,11 @@ const replyMocks = vi.hoisted(() => ({ const deliveryMocks = vi.hoisted(() => ({ deliverReplies: vi.fn(async () => ({ delivered: true })), })); +const pluginRuntimeMocks = vi.hoisted(() => ({ + getPluginCommandSpecs: vi.fn(() => []), + matchPluginCommand: vi.fn(() => null), + executePluginCommand: vi.fn(async () => ({ text: "ok" })), +})); const sessionBindingMocks = vi.hoisted(() => ({ resolveByConversation: vi.fn< (ref: unknown) => { bindingId: string; targetSessionKey: string } | null @@ -123,28 +128,19 @@ vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => { listSkillCommandsForAgents: vi.fn(() => []), }; }); +vi.mock("openclaw/plugin-sdk/plugin-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getPluginCommandSpecs: pluginRuntimeMocks.getPluginCommandSpecs, + matchPluginCommand: pluginRuntimeMocks.matchPluginCommand, + executePluginCommand: pluginRuntimeMocks.executePluginCommand, + }; +}); vi.mock("../../../src/config/sessions.js", () => ({ recordSessionMetaFromInbound: sessionMocks.recordSessionMetaFromInbound, resolveStorePath: sessionMocks.resolveStorePath, })); -vi.mock("../../../src/pairing/pairing-store.js", () => ({ - readChannelAllowFromStore: vi.fn(async () => []), -})); -vi.mock("../../../src/infra/outbound/session-binding-service.js", () => ({ - getSessionBindingService: () => ({ - bind: vi.fn(), - getCapabilities: vi.fn(), - listBySession: vi.fn(), - resolveByConversation: (ref: unknown) => sessionBindingMocks.resolveByConversation(ref), - touch: (bindingId: string, at?: number) => sessionBindingMocks.touch(bindingId, at), - unbind: vi.fn(), - }), -})); -vi.mock("../../../src/plugins/commands.js", () => ({ - getPluginCommandSpecs: vi.fn(() => []), - matchPluginCommand: vi.fn(() => null), - executePluginCommand: vi.fn(async () => ({ text: "ok" })), -})); vi.mock("./bot/delivery.js", () => ({ deliverReplies: deliveryMocks.deliverReplies, })); diff --git a/extensions/telegram/src/send.proxy.test.ts b/extensions/telegram/src/send.proxy.test.ts index 6c17b33fe38..be6a7b4faaf 100644 --- a/extensions/telegram/src/send.proxy.test.ts +++ b/extensions/telegram/src/send.proxy.test.ts @@ -21,8 +21,8 @@ const { resolveTelegramFetch } = vi.hoisted(() => ({ resolveTelegramFetch: vi.fn(), })); -vi.mock("../../../src/config/config.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, loadConfig, diff --git a/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts b/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts index 234b4dddfd5..fec4b4d72cb 100644 --- a/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts +++ b/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts @@ -1,7 +1,7 @@ +import type { getReplyFromConfig } from "openclaw/plugin-sdk/reply-runtime"; +import { HEARTBEAT_TOKEN } from "openclaw/plugin-sdk/reply-runtime"; +import { redactIdentifier } from "openclaw/plugin-sdk/text-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { getReplyFromConfig } from "../../../../src/auto-reply/reply.js"; -import { HEARTBEAT_TOKEN } from "../../../../src/auto-reply/tokens.js"; -import { redactIdentifier } from "../../../../src/logging/redact-identifier.js"; import type { sendMessageWhatsApp } from "../send.js"; const state = vi.hoisted(() => ({ @@ -22,78 +22,66 @@ const state = vi.hoisted(() => ({ heartbeatWarnLogs: [] as string[], })); -vi.mock("../../../../src/agents/current-time.js", () => ({ - appendCronStyleCurrentTimeLine: (body: string) => - `${body}\nCurrent time: 2026-02-15T00:00:00Z (mock)`, -})); +vi.mock("openclaw/plugin-sdk/agent-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + appendCronStyleCurrentTimeLine: (body: string) => + `${body}\nCurrent time: 2026-02-15T00:00:00Z (mock)`, + }; +}); // Perf: this module otherwise pulls a large dependency graph that we don't need // for these unit tests. -vi.mock("../../../../src/auto-reply/reply.js", () => ({ - getReplyFromConfig: vi.fn(async () => undefined), -})); +vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getReplyFromConfig: vi.fn(async () => undefined), + }; +}); -vi.mock("../../../../src/channels/plugins/whatsapp-heartbeat.js", () => ({ - resolveWhatsAppHeartbeatRecipients: () => [], -})); +vi.mock("openclaw/plugin-sdk/channel-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + resolveWhatsAppHeartbeatRecipients: () => [], + }; +}); -vi.mock("../../../../src/config/config.js", () => ({ - loadConfig: () => ({ agents: { defaults: {} }, session: {} }), -})); +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadConfig: () => ({ agents: { defaults: {} }, session: {} }), + loadSessionStore: () => state.store, + resolveSessionKey: () => "k", + resolveStorePath: () => "/tmp/store.json", + updateSessionStore: async (_path: string, updater: (store: typeof state.store) => void) => { + updater(state.store); + }, + }; +}); -vi.mock("../../../../src/routing/session-key.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("openclaw/plugin-sdk/routing", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, normalizeMainKey: () => null, }; }); -vi.mock("../../../../src/infra/heartbeat-visibility.js", () => ({ - resolveHeartbeatVisibility: () => state.visibility, -})); - -vi.mock("../../../../src/config/sessions.js", () => ({ - loadSessionStore: () => state.store, - resolveSessionKey: () => "k", - resolveStorePath: () => "/tmp/store.json", - updateSessionStore: async (_path: string, updater: (store: typeof state.store) => void) => { - updater(state.store); - }, -})); - vi.mock("./session-snapshot.js", () => ({ getSessionSnapshot: () => state.snapshot, })); -vi.mock("../../../../src/infra/heartbeat-events.js", () => ({ - emitHeartbeatEvent: (event: unknown) => state.events.push(event), - resolveIndicatorType: (status: string) => `indicator:${status}`, -})); - -vi.mock("../../../../src/logging.js", async (importOriginal) => { - const actual = await importOriginal(); - const createStubLogger = () => ({ - info: () => undefined, - warn: () => undefined, - error: () => undefined, - child: createStubLogger, - }); +vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, - getChildLogger: () => ({ - info: (...args: unknown[]) => state.loggerInfoCalls.push(args), - warn: (...args: unknown[]) => state.loggerWarnCalls.push(args), - }), - createSubsystemLogger: () => createStubLogger(), - }; -}); - -vi.mock("openclaw/plugin-sdk/state-paths", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - resolveOAuthDir: () => "/tmp/openclaw-oauth", + resolveHeartbeatVisibility: () => state.visibility, + emitHeartbeatEvent: (event: unknown) => state.events.push(event), + resolveIndicatorType: (status: string) => `indicator:${status}`, }; }); @@ -108,10 +96,22 @@ vi.mock("openclaw/plugin-sdk/runtime-env", async (importOriginal) => { }; return { ...actual, + getChildLogger: () => ({ + info: (...args: unknown[]) => state.loggerInfoCalls.push(args), + warn: (...args: unknown[]) => state.loggerWarnCalls.push(args), + }), createSubsystemLogger: () => logger, }; }); +vi.mock("openclaw/plugin-sdk/state-paths", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + resolveOAuthDir: () => "/tmp/openclaw-oauth", + }; +}); + vi.mock("../auth-store.js", () => ({ WA_WEB_AUTH_DIR: "/tmp/openclaw-oauth/whatsapp/default", resolveDefaultWebAuthDir: () => "/tmp/openclaw-oauth/whatsapp/default", diff --git a/extensions/whatsapp/src/inbound.media.test.ts b/extensions/whatsapp/src/inbound.media.test.ts index 7ed52cace45..196457c84ec 100644 --- a/extensions/whatsapp/src/inbound.media.test.ts +++ b/extensions/whatsapp/src/inbound.media.test.ts @@ -4,12 +4,10 @@ import os from "node:os"; import path from "node:path"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -const readAllowFromStoreMock = vi.fn().mockResolvedValue([]); -const upsertPairingRequestMock = vi.fn().mockResolvedValue({ code: "PAIRCODE", created: true }); const saveMediaBufferSpy = vi.fn(); -vi.mock("../../../src/config/config.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, loadConfig: vi.fn().mockReturnValue({ @@ -26,17 +24,6 @@ vi.mock("../../../src/config/config.js", async (importOriginal) => { }; }); -vi.mock("../../../src/pairing/pairing-store.js", () => { - return { - readChannelAllowFromStore(...args: unknown[]) { - return readAllowFromStoreMock(...args); - }, - upsertChannelPairingRequest(...args: unknown[]) { - return upsertPairingRequestMock(...args); - }, - }; -}); - vi.mock("../../../src/media/store.js", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/extensions/whatsapp/src/inbound/send-api.test.ts b/extensions/whatsapp/src/inbound/send-api.test.ts index e7bfcdce360..4e2d3127961 100644 --- a/extensions/whatsapp/src/inbound/send-api.test.ts +++ b/extensions/whatsapp/src/inbound/send-api.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; const recordChannelActivity = vi.fn(); -vi.mock("../../../../src/infra/channel-activity.js", () => ({ +vi.mock("openclaw/plugin-sdk/infra-runtime", () => ({ recordChannelActivity: (...args: unknown[]) => recordChannelActivity(...args), })); diff --git a/extensions/whatsapp/src/login.coverage.test.ts b/extensions/whatsapp/src/login.coverage.test.ts index dda665ccdce..89f0eb8a407 100644 --- a/extensions/whatsapp/src/login.coverage.test.ts +++ b/extensions/whatsapp/src/login.coverage.test.ts @@ -19,18 +19,22 @@ function resolveTestAuthDir() { const authDir = resolveTestAuthDir(); -vi.mock("../../../src/config/config.js", () => ({ - loadConfig: () => - ({ - channels: { - whatsapp: { - accounts: { - default: { enabled: true, authDir: resolveTestAuthDir() }, +vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadConfig: () => + ({ + channels: { + whatsapp: { + accounts: { + default: { enabled: true, authDir: resolveTestAuthDir() }, + }, }, }, - }, - }) as never, -})); + }) as never, + }; +}); vi.mock("./session.js", () => { const authDir = resolveTestAuthDir();