test: align extension seam mocks

Regeneration-Prompt: |
  Clean up extension tests that still mocked old src/** internals after the plugin-sdk/runtime seam refactors. Inspect the production imports first, then update only the tests whose mocked dependency no longer matches the live seam. Keep the change test-only, prefer mocking openclaw/plugin-sdk/* entry points over core internals, and remove redundant dead mocks where the seam mock already covers the behavior. Validate with targeted vitest runs for the touched Telegram, Slack, Discord, WhatsApp, and Feishu tests.
This commit is contained in:
Josh Lehman 2026-03-20 20:40:07 -07:00
parent 5408a3d1a4
commit c6433f8de4
No known key found for this signature in database
GPG Key ID: D141B425AC7F876B
15 changed files with 106 additions and 155 deletions

View File

@ -4,16 +4,16 @@ import { sendWebhookMessageDiscord } from "./send.js";
const recordChannelActivityMock = vi.hoisted(() => vi.fn()); const recordChannelActivityMock = vi.hoisted(() => vi.fn());
const loadConfigMock = vi.hoisted(() => vi.fn(() => ({ channels: { discord: {} } }))); const loadConfigMock = vi.hoisted(() => vi.fn(() => ({ channels: { discord: {} } })));
vi.mock("../../../src/config/config.js", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/config/config.js")>(); const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return { return {
...actual, ...actual,
loadConfig: () => loadConfigMock(), loadConfig: () => loadConfigMock(),
}; };
}); });
vi.mock("../../../src/infra/channel-activity.js", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/infra/channel-activity.js")>(); const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
return { return {
...actual, ...actual,
recordChannelActivity: (...args: unknown[]) => recordChannelActivityMock(...args), recordChannelActivity: (...args: unknown[]) => recordChannelActivityMock(...args),

View File

@ -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 { function createLifecycleConfig(): ClawdbotConfig {
return { return {
session: { mainKey: "main", scope: "per-sender" }, session: { mainKey: "main", scope: "per-sender" },

View File

@ -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 { function createLifecycleConfig(): ClawdbotConfig {
return { return {
channels: { channels: {

View File

@ -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 { function createLifecycleConfig(): ClawdbotConfig {
return { return {
broadcast: { broadcast: {

View File

@ -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 { function createLifecycleConfig(): ClawdbotConfig {
return { return {
channels: { channels: {

View File

@ -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 { function createLifecycleConfig(): ClawdbotConfig {
return { return {
messages: { messages: {

View File

@ -10,20 +10,20 @@ const dispatchPluginInteractiveHandlerMock = vi.fn(async () => ({
const resolvePluginConversationBindingApprovalMock = vi.fn(); const resolvePluginConversationBindingApprovalMock = vi.fn();
const buildPluginBindingResolvedTextMock = vi.fn(() => "Binding updated."); const buildPluginBindingResolvedTextMock = vi.fn(() => "Binding updated.");
vi.mock("../../../../../src/infra/system-events.js", () => ({ vi.mock("openclaw/plugin-sdk/infra-runtime", () => ({
enqueueSystemEvent: (...args: unknown[]) => enqueueSystemEvent: (...args: unknown[]) =>
(enqueueSystemEventMock as (...innerArgs: unknown[]) => unknown)(...args), (enqueueSystemEventMock as (...innerArgs: unknown[]) => unknown)(...args),
})); }));
vi.mock("../../../../../src/plugins/interactive.js", () => ({ vi.mock("openclaw/plugin-sdk/plugin-runtime", () => ({
dispatchPluginInteractiveHandler: (...args: unknown[]) => dispatchPluginInteractiveHandler: (...args: unknown[]) =>
(dispatchPluginInteractiveHandlerMock as (...innerArgs: unknown[]) => unknown)(...args), (dispatchPluginInteractiveHandlerMock as (...innerArgs: unknown[]) => unknown)(...args),
})); }));
vi.mock("../../../../../src/plugins/conversation-binding.js", async () => { vi.mock("openclaw/plugin-sdk/conversation-runtime", async () => {
const actual = await vi.importActual< const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/conversation-runtime")>(
typeof import("../../../../../src/plugins/conversation-binding.js") "openclaw/plugin-sdk/conversation-runtime",
>("../../../../../src/plugins/conversation-binding.js"); );
return { return {
...actual, ...actual,
resolvePluginConversationBindingApproval: (...args: unknown[]) => resolvePluginConversationBindingApproval: (...args: unknown[]) =>

View File

@ -9,9 +9,8 @@ const hoisted = vi.hoisted(() => {
}; };
}); });
vi.mock("../../../src/infra/outbound/session-binding-service.js", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => {
const actual = const actual = await importOriginal<typeof import("openclaw/plugin-sdk/conversation-runtime")>();
await importOriginal<typeof import("../../../src/infra/outbound/session-binding-service.js")>();
return { return {
...actual, ...actual,
getSessionBindingService: () => ({ getSessionBindingService: () => ({

View File

@ -1,5 +1,5 @@
import { loadConfig } from "openclaw/plugin-sdk/config-runtime";
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
import { loadConfig } from "../../../src/config/config.js";
const { defaultRouteConfig } = vi.hoisted(() => ({ const { defaultRouteConfig } = vi.hoisted(() => ({
defaultRouteConfig: { defaultRouteConfig: {
@ -11,8 +11,8 @@ const { defaultRouteConfig } = vi.hoisted(() => ({
}, },
})); }));
vi.mock("../../../src/config/config.js", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/config/config.js")>(); const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return { return {
...actual, ...actual,
loadConfig: vi.fn(() => defaultRouteConfig), loadConfig: vi.fn(() => defaultRouteConfig),

View File

@ -1,5 +1,5 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { beforeEach, describe, expect, it, vi } from "vitest"; 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 { ResolvedAgentRoute } from "../../../src/routing/resolve-route.js";
import type { TelegramBotDeps } from "./bot-deps.js"; import type { TelegramBotDeps } from "./bot-deps.js";
import { import {
@ -56,6 +56,11 @@ const replyMocks = vi.hoisted(() => ({
const deliveryMocks = vi.hoisted(() => ({ const deliveryMocks = vi.hoisted(() => ({
deliverReplies: vi.fn<DeliverRepliesFn>(async () => ({ delivered: true })), deliverReplies: vi.fn<DeliverRepliesFn>(async () => ({ delivered: true })),
})); }));
const pluginRuntimeMocks = vi.hoisted(() => ({
getPluginCommandSpecs: vi.fn(() => []),
matchPluginCommand: vi.fn(() => null),
executePluginCommand: vi.fn(async () => ({ text: "ok" })),
}));
const sessionBindingMocks = vi.hoisted(() => ({ const sessionBindingMocks = vi.hoisted(() => ({
resolveByConversation: vi.fn< resolveByConversation: vi.fn<
(ref: unknown) => { bindingId: string; targetSessionKey: string } | null (ref: unknown) => { bindingId: string; targetSessionKey: string } | null
@ -123,28 +128,19 @@ vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => {
listSkillCommandsForAgents: vi.fn(() => []), listSkillCommandsForAgents: vi.fn(() => []),
}; };
}); });
vi.mock("openclaw/plugin-sdk/plugin-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/plugin-runtime")>();
return {
...actual,
getPluginCommandSpecs: pluginRuntimeMocks.getPluginCommandSpecs,
matchPluginCommand: pluginRuntimeMocks.matchPluginCommand,
executePluginCommand: pluginRuntimeMocks.executePluginCommand,
};
});
vi.mock("../../../src/config/sessions.js", () => ({ vi.mock("../../../src/config/sessions.js", () => ({
recordSessionMetaFromInbound: sessionMocks.recordSessionMetaFromInbound, recordSessionMetaFromInbound: sessionMocks.recordSessionMetaFromInbound,
resolveStorePath: sessionMocks.resolveStorePath, 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", () => ({ vi.mock("./bot/delivery.js", () => ({
deliverReplies: deliveryMocks.deliverReplies, deliverReplies: deliveryMocks.deliverReplies,
})); }));

View File

@ -21,8 +21,8 @@ const { resolveTelegramFetch } = vi.hoisted(() => ({
resolveTelegramFetch: vi.fn(), resolveTelegramFetch: vi.fn(),
})); }));
vi.mock("../../../src/config/config.js", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/config/config.js")>(); const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return { return {
...actual, ...actual,
loadConfig, loadConfig,

View File

@ -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 { 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"; import type { sendMessageWhatsApp } from "../send.js";
const state = vi.hoisted(() => ({ const state = vi.hoisted(() => ({
@ -22,78 +22,66 @@ const state = vi.hoisted(() => ({
heartbeatWarnLogs: [] as string[], heartbeatWarnLogs: [] as string[],
})); }));
vi.mock("../../../../src/agents/current-time.js", () => ({ vi.mock("openclaw/plugin-sdk/agent-runtime", async (importOriginal) => {
appendCronStyleCurrentTimeLine: (body: string) => const actual = await importOriginal<typeof import("openclaw/plugin-sdk/agent-runtime")>();
`${body}\nCurrent time: 2026-02-15T00:00:00Z (mock)`, 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 // Perf: this module otherwise pulls a large dependency graph that we don't need
// for these unit tests. // for these unit tests.
vi.mock("../../../../src/auto-reply/reply.js", () => ({ vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => {
getReplyFromConfig: vi.fn(async () => undefined), const actual = await importOriginal<typeof import("openclaw/plugin-sdk/reply-runtime")>();
})); return {
...actual,
getReplyFromConfig: vi.fn(async () => undefined),
};
});
vi.mock("../../../../src/channels/plugins/whatsapp-heartbeat.js", () => ({ vi.mock("openclaw/plugin-sdk/channel-runtime", async (importOriginal) => {
resolveWhatsAppHeartbeatRecipients: () => [], const actual = await importOriginal<typeof import("openclaw/plugin-sdk/channel-runtime")>();
})); return {
...actual,
resolveWhatsAppHeartbeatRecipients: () => [],
};
});
vi.mock("../../../../src/config/config.js", () => ({ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
loadConfig: () => ({ agents: { defaults: {} }, session: {} }), const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
})); 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) => { vi.mock("openclaw/plugin-sdk/routing", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../../src/routing/session-key.js")>(); const actual = await importOriginal<typeof import("openclaw/plugin-sdk/routing")>();
return { return {
...actual, ...actual,
normalizeMainKey: () => null, 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", () => ({ vi.mock("./session-snapshot.js", () => ({
getSessionSnapshot: () => state.snapshot, getSessionSnapshot: () => state.snapshot,
})); }));
vi.mock("../../../../src/infra/heartbeat-events.js", () => ({ vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
emitHeartbeatEvent: (event: unknown) => state.events.push(event), const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
resolveIndicatorType: (status: string) => `indicator:${status}`,
}));
vi.mock("../../../../src/logging.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../../src/logging.js")>();
const createStubLogger = () => ({
info: () => undefined,
warn: () => undefined,
error: () => undefined,
child: createStubLogger,
});
return { return {
...actual, ...actual,
getChildLogger: () => ({ resolveHeartbeatVisibility: () => state.visibility,
info: (...args: unknown[]) => state.loggerInfoCalls.push(args), emitHeartbeatEvent: (event: unknown) => state.events.push(event),
warn: (...args: unknown[]) => state.loggerWarnCalls.push(args), resolveIndicatorType: (status: string) => `indicator:${status}`,
}),
createSubsystemLogger: () => createStubLogger(),
};
});
vi.mock("openclaw/plugin-sdk/state-paths", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/state-paths")>();
return {
...actual,
resolveOAuthDir: () => "/tmp/openclaw-oauth",
}; };
}); });
@ -108,10 +96,22 @@ vi.mock("openclaw/plugin-sdk/runtime-env", async (importOriginal) => {
}; };
return { return {
...actual, ...actual,
getChildLogger: () => ({
info: (...args: unknown[]) => state.loggerInfoCalls.push(args),
warn: (...args: unknown[]) => state.loggerWarnCalls.push(args),
}),
createSubsystemLogger: () => logger, createSubsystemLogger: () => logger,
}; };
}); });
vi.mock("openclaw/plugin-sdk/state-paths", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/state-paths")>();
return {
...actual,
resolveOAuthDir: () => "/tmp/openclaw-oauth",
};
});
vi.mock("../auth-store.js", () => ({ vi.mock("../auth-store.js", () => ({
WA_WEB_AUTH_DIR: "/tmp/openclaw-oauth/whatsapp/default", WA_WEB_AUTH_DIR: "/tmp/openclaw-oauth/whatsapp/default",
resolveDefaultWebAuthDir: () => "/tmp/openclaw-oauth/whatsapp/default", resolveDefaultWebAuthDir: () => "/tmp/openclaw-oauth/whatsapp/default",

View File

@ -4,12 +4,10 @@ import os from "node:os";
import path from "node:path"; import path from "node:path";
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; 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(); const saveMediaBufferSpy = vi.fn();
vi.mock("../../../src/config/config.js", async (importOriginal) => { vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/config/config.js")>(); const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return { return {
...actual, ...actual,
loadConfig: vi.fn().mockReturnValue({ 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) => { vi.mock("../../../src/media/store.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/media/store.js")>(); const actual = await importOriginal<typeof import("../../../src/media/store.js")>();
return { return {

View File

@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
const recordChannelActivity = vi.fn(); const recordChannelActivity = vi.fn();
vi.mock("../../../../src/infra/channel-activity.js", () => ({ vi.mock("openclaw/plugin-sdk/infra-runtime", () => ({
recordChannelActivity: (...args: unknown[]) => recordChannelActivity(...args), recordChannelActivity: (...args: unknown[]) => recordChannelActivity(...args),
})); }));

View File

@ -19,18 +19,22 @@ function resolveTestAuthDir() {
const authDir = resolveTestAuthDir(); const authDir = resolveTestAuthDir();
vi.mock("../../../src/config/config.js", () => ({ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
loadConfig: () => const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
({ return {
channels: { ...actual,
whatsapp: { loadConfig: () =>
accounts: { ({
default: { enabled: true, authDir: resolveTestAuthDir() }, channels: {
whatsapp: {
accounts: {
default: { enabled: true, authDir: resolveTestAuthDir() },
},
}, },
}, },
}, }) as never,
}) as never, };
})); });
vi.mock("./session.js", () => { vi.mock("./session.js", () => {
const authDir = resolveTestAuthDir(); const authDir = resolveTestAuthDir();