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 loadConfigMock = vi.hoisted(() => vi.fn(() => ({ channels: { discord: {} } })));
vi.mock("../../../src/config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/config/config.js")>();
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return {
...actual,
loadConfig: () => loadConfigMock(),
};
});
vi.mock("../../../src/infra/channel-activity.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/infra/channel-activity.js")>();
vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
return {
...actual,
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 {
return {
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 {
return {
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 {
return {
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 {
return {
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 {
return {
messages: {

View File

@ -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<typeof import("openclaw/plugin-sdk/conversation-runtime")>(
"openclaw/plugin-sdk/conversation-runtime",
);
return {
...actual,
resolvePluginConversationBindingApproval: (...args: unknown[]) =>

View File

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

View File

@ -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<typeof import("../../../src/config/config.js")>();
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return {
...actual,
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 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<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(() => ({
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<typeof import("openclaw/plugin-sdk/plugin-runtime")>();
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,
}));

View File

@ -21,8 +21,8 @@ const { resolveTelegramFetch } = vi.hoisted(() => ({
resolveTelegramFetch: vi.fn(),
}));
vi.mock("../../../src/config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../../src/config/config.js")>();
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
return {
...actual,
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 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<typeof import("openclaw/plugin-sdk/agent-runtime")>();
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<typeof import("openclaw/plugin-sdk/reply-runtime")>();
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<typeof import("openclaw/plugin-sdk/channel-runtime")>();
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<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) => {
const actual = await importOriginal<typeof import("../../../../src/routing/session-key.js")>();
vi.mock("openclaw/plugin-sdk/routing", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/routing")>();
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<typeof import("../../../../src/logging.js")>();
const createStubLogger = () => ({
info: () => undefined,
warn: () => undefined,
error: () => undefined,
child: createStubLogger,
});
vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
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<typeof import("openclaw/plugin-sdk/state-paths")>();
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<typeof import("openclaw/plugin-sdk/state-paths")>();
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",

View File

@ -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<typeof import("../../../src/config/config.js")>();
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
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<typeof import("../../../src/media/store.js")>();
return {

View File

@ -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),
}));

View File

@ -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<typeof import("openclaw/plugin-sdk/config-runtime")>();
return {
...actual,
loadConfig: () =>
({
channels: {
whatsapp: {
accounts: {
default: { enabled: true, authDir: resolveTestAuthDir() },
},
},
},
},
}) as never,
}));
}) as never,
};
});
vi.mock("./session.js", () => {
const authDir = resolveTestAuthDir();