From 017c25b07593a63d07ec687fe686df3a8fd99bd6 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 01:15:59 +0100 Subject: [PATCH] test(runtime): fix stale harness and registry mocks --- .../slack/src/outbound-payload-harness.ts | 20 +++---------------- src/channels/plugins/acp-bindings.test.ts | 4 ++++ src/cli/qr-dashboard.integration.test.ts | 14 ++++++++----- src/config/validation.ts | 5 ++++- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/extensions/slack/src/outbound-payload-harness.ts b/extensions/slack/src/outbound-payload-harness.ts index 39ad5202459..157bdcd9346 100644 --- a/extensions/slack/src/outbound-payload-harness.ts +++ b/extensions/slack/src/outbound-payload-harness.ts @@ -1,10 +1,7 @@ -import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk/channel-contract"; import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; -import { - loadBundledPluginTestApiSync, - primeChannelOutboundSendMock, -} from "openclaw/plugin-sdk/testing"; +import { primeChannelOutboundSendMock } from "openclaw/plugin-sdk/testing"; import { vi, type Mock } from "vitest"; +import { slackOutbound } from "./outbound-adapter.js"; type OutboundSendMock = Mock<(...args: unknown[]) => Promise>>; @@ -14,17 +11,6 @@ type SlackOutboundPayloadHarness = { to: string; }; -let slackOutboundCache: ChannelOutboundAdapter | undefined; - -function getSlackOutbound(): ChannelOutboundAdapter { - if (!slackOutboundCache) { - ({ slackOutbound: slackOutboundCache } = loadBundledPluginTestApiSync<{ - slackOutbound: ChannelOutboundAdapter; - }>("slack")); - } - return slackOutboundCache; -} - export function createSlackOutboundPayloadHarness(params: { payload: ReplyPayload; sendResults?: Array<{ messageId: string }>; @@ -45,7 +31,7 @@ export function createSlackOutboundPayloadHarness(params: { }, }; return { - run: async () => await getSlackOutbound().sendPayload!(ctx), + run: async () => await slackOutbound.sendPayload!(ctx), sendMock: sendSlack, to: ctx.to, }; diff --git a/src/channels/plugins/acp-bindings.test.ts b/src/channels/plugins/acp-bindings.test.ts index fd9f86e708f..12097f3cb7b 100644 --- a/src/channels/plugins/acp-bindings.test.ts +++ b/src/channels/plugins/acp-bindings.test.ts @@ -6,6 +6,7 @@ const resolveDefaultAgentIdMock = vi.hoisted(() => vi.fn()); const resolveAgentWorkspaceDirMock = vi.hoisted(() => vi.fn()); const getChannelPluginMock = vi.hoisted(() => vi.fn()); const getActivePluginChannelRegistryVersionMock = vi.hoisted(() => vi.fn()); +const requireActivePluginChannelRegistryMock = vi.hoisted(() => vi.fn(() => ({}))); vi.mock("../../agents/agent-scope.js", () => ({ resolveAgentConfig: (...args: unknown[]) => resolveAgentConfigMock(...args), @@ -20,6 +21,8 @@ vi.mock("./index.js", () => ({ vi.mock("../../plugins/runtime.js", () => ({ getActivePluginChannelRegistryVersion: (...args: unknown[]) => getActivePluginChannelRegistryVersionMock(...args), + requireActivePluginChannelRegistry: (...args: unknown[]) => + requireActivePluginChannelRegistryMock(...args), })); async function importConfiguredBindings() { @@ -100,6 +103,7 @@ describe("configured binding registry", () => { resolveAgentWorkspaceDirMock.mockReset().mockReturnValue("/tmp/workspace"); getChannelPluginMock.mockReset(); getActivePluginChannelRegistryVersionMock.mockReset().mockReturnValue(1); + requireActivePluginChannelRegistryMock.mockReset().mockReturnValue({}); }); it("resolves configured ACP bindings from an already loaded channel plugin", async () => { diff --git a/src/cli/qr-dashboard.integration.test.ts b/src/cli/qr-dashboard.integration.test.ts index 1b2281ae947..8490bcefa35 100644 --- a/src/cli/qr-dashboard.integration.test.ts +++ b/src/cli/qr-dashboard.integration.test.ts @@ -15,11 +15,15 @@ const { } = createCliRuntimeCapture(); const runtimeExit = runtime.exit; -vi.mock("../config/config.js", () => ({ - loadConfig: loadConfigMock, - readConfigFileSnapshot: readConfigFileSnapshotMock, - resolveGatewayPort: resolveGatewayPortMock, -})); +vi.mock("../config/config.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadConfig: loadConfigMock, + readConfigFileSnapshot: readConfigFileSnapshotMock, + resolveGatewayPort: resolveGatewayPortMock, + }; +}); vi.mock("../infra/clipboard.js", () => ({ copyToClipboard: copyToClipboardMock, diff --git a/src/config/validation.ts b/src/config/validation.ts index 5baa1020331..00f6ec4607a 100644 --- a/src/config/validation.ts +++ b/src/config/validation.ts @@ -969,6 +969,9 @@ function validateConfigObjectWithPluginsBase( const entry = normalizedPlugins.entries[pluginId]; const entryExists = entry !== undefined; const entryHasConfig = Boolean(entry?.config); + const shouldReplacePluginConfig = opts.applyDefaults + ? entryExists || entryHasConfig + : entryHasConfig; const activationState = resolveEffectivePluginActivationState({ id: pluginId, @@ -1014,7 +1017,7 @@ function validateConfigObjectWithPluginsBase( allowedValuesHiddenCount: error.allowedValuesHiddenCount, }); } - } else if (entryExists || entryHasConfig) { + } else if (shouldReplacePluginConfig) { replacePluginEntryConfig(pluginId, res.value as Record); } } else if (record.format === "bundle") {