diff --git a/extensions/slack/inbound-contract-test-api.ts b/extensions/slack/inbound-contract-test-api.ts new file mode 100644 index 00000000000..18d6d74fd45 --- /dev/null +++ b/extensions/slack/inbound-contract-test-api.ts @@ -0,0 +1,2 @@ +export { prepareSlackMessage } from "./src/monitor/message-handler/prepare.js"; +export { createInboundSlackTestContext } from "./src/monitor/message-handler/prepare.test-helpers.js"; diff --git a/extensions/slack/outbound-payload-test-api.ts b/extensions/slack/outbound-payload-test-api.ts new file mode 100644 index 00000000000..ef7148e1997 --- /dev/null +++ b/extensions/slack/outbound-payload-test-api.ts @@ -0,0 +1 @@ +export { createSlackOutboundPayloadHarness } from "./src/outbound-payload.test-harness.js"; diff --git a/extensions/whatsapp/outbound-payload-test-api.ts b/extensions/whatsapp/outbound-payload-test-api.ts new file mode 100644 index 00000000000..49ea8ff67b4 --- /dev/null +++ b/extensions/whatsapp/outbound-payload-test-api.ts @@ -0,0 +1 @@ +export { whatsappOutbound } from "./src/outbound-adapter.js"; diff --git a/extensions/whatsapp/src/outbound-adapter.ts b/extensions/whatsapp/src/outbound-adapter.ts index ffb1f1b6e7e..dd4ed99e86b 100644 --- a/extensions/whatsapp/src/outbound-adapter.ts +++ b/extensions/whatsapp/src/outbound-adapter.ts @@ -11,8 +11,7 @@ import { import { chunkText } from "openclaw/plugin-sdk/reply-runtime"; import { shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env"; import { WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps.js"; -import { resolveWhatsAppOutboundTarget } from "./runtime-api.js"; -import { sendPollWhatsApp } from "./send.js"; +import { resolveWhatsAppOutboundTarget } from "./resolve-outbound-target.js"; function trimLeadingWhitespace(text: string | undefined): string { return text?.trimStart() ?? ""; @@ -90,7 +89,9 @@ export const whatsappOutbound: ChannelOutboundAdapter = { }); }, sendPoll: async ({ cfg, to, poll, accountId }) => - await sendPollWhatsApp(to, poll, { + await ( + await import("./send.js") + ).sendPollWhatsApp(to, poll, { verbose: shouldLogVerbose(), accountId: accountId ?? undefined, cfg, diff --git a/src/channels/plugins/contracts/channel-import-guardrails.test.ts b/src/channels/plugins/contracts/channel-import-guardrails.test.ts index 455724bfd29..3bac99b3b6b 100644 --- a/src/channels/plugins/contracts/channel-import-guardrails.test.ts +++ b/src/channels/plugins/contracts/channel-import-guardrails.test.ts @@ -248,6 +248,14 @@ const sourceAnalysisCache = new Map(); let extensionSourceFilesCache: string[] | null = null; let coreSourceFilesCache: string[] | null = null; const extensionFilesCache = new Map(); +const STATIC_FROM_IMPORT_RE = + /^\s*import(?:\s+type)?\s+(?!["'])(?:[\s\S]*?)\s+from\s*["']([^"']+)["']/gmu; +const STATIC_SIDE_EFFECT_IMPORT_RE = /^\s*import\s*["']([^"']+)["']/gmu; +const RE_EXPORT_STAR_RE = + /^\s*export\s+(?:type\s+)?\*\s*(?:as\s+\w+\s+)?from\s*["']([^"']+)["']/gmu; +const RE_EXPORT_NAMED_RE = /^\s*export\s+(?:type\s+)?\{[^}]*\}\s+from\s*["']([^"']+)["']/gmu; +const DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/gmu; +const REQUIRE_RE = /\brequire\s*\(\s*["']([^"']+)["']\s*\)/gmu; type SourceFileCollectorOptions = { rootDir: string; @@ -388,16 +396,18 @@ function collectExtensionFiles(extensionId: string): string[] { function collectModuleSpecifiers(text: string): string[] { const patterns = [ - /\bimport\s*\(\s*["']([^"']+\.(?:[cm]?[jt]sx?))["']\s*\)/g, - /\brequire\s*\(\s*["']([^"']+\.(?:[cm]?[jt]sx?))["']\s*\)/g, - /\b(?:import|export)\b[\s\S]*?\bfrom\s*["']([^"']+\.(?:[cm]?[jt]sx?))["']/g, - /\bimport\s*["']([^"']+\.(?:[cm]?[jt]sx?))["']/g, + DYNAMIC_IMPORT_RE, + REQUIRE_RE, + STATIC_FROM_IMPORT_RE, + STATIC_SIDE_EFFECT_IMPORT_RE, + RE_EXPORT_STAR_RE, + RE_EXPORT_NAMED_RE, ] as const; const specifiers = new Set(); for (const pattern of patterns) { for (const match of text.matchAll(pattern)) { const specifier = match[1]?.trim(); - if (specifier) { + if (specifier && /\.(?:[cm]?[jt]sx?)$/u.test(specifier)) { specifiers.add(specifier); } } diff --git a/src/channels/plugins/contracts/inbound.contract.test.ts b/src/channels/plugins/contracts/inbound.contract.test.ts new file mode 100644 index 00000000000..aa06edf2570 --- /dev/null +++ b/src/channels/plugins/contracts/inbound.contract.test.ts @@ -0,0 +1,28 @@ +import { describe } from "vitest"; +import { installDiscordInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.discord.js"; +import { installSignalInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.signal.js"; +import { installSlackInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.slack.js"; +import { installTelegramInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.telegram.js"; +import { installWhatsAppInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.whatsapp.js"; + +describe("inbound channel contracts", () => { + describe("discord", () => { + installDiscordInboundContractSuite(); + }); + + describe("signal", () => { + installSignalInboundContractSuite(); + }); + + describe("slack", () => { + installSlackInboundContractSuite(); + }); + + describe("telegram", () => { + installTelegramInboundContractSuite(); + }); + + describe("whatsapp", () => { + installWhatsAppInboundContractSuite(); + }); +}); diff --git a/src/channels/plugins/contracts/inbound.discord.contract.test.ts b/src/channels/plugins/contracts/inbound.discord.contract.test.ts deleted file mode 100644 index c1861b87b4e..00000000000 --- a/src/channels/plugins/contracts/inbound.discord.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installDiscordInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.discord.js"; - -describe("discord inbound contract", () => { - installDiscordInboundContractSuite(); -}); diff --git a/src/channels/plugins/contracts/inbound.signal.contract.test.ts b/src/channels/plugins/contracts/inbound.signal.contract.test.ts deleted file mode 100644 index eada8a6e6e0..00000000000 --- a/src/channels/plugins/contracts/inbound.signal.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installSignalInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.signal.js"; - -describe("signal inbound contract", () => { - installSignalInboundContractSuite(); -}); diff --git a/src/channels/plugins/contracts/inbound.slack.contract.test.ts b/src/channels/plugins/contracts/inbound.slack.contract.test.ts deleted file mode 100644 index 34910741c8b..00000000000 --- a/src/channels/plugins/contracts/inbound.slack.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installSlackInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.slack.js"; - -describe("slack inbound contract", () => { - installSlackInboundContractSuite(); -}); diff --git a/src/channels/plugins/contracts/inbound.telegram.contract.test.ts b/src/channels/plugins/contracts/inbound.telegram.contract.test.ts deleted file mode 100644 index 2b4cae5eb76..00000000000 --- a/src/channels/plugins/contracts/inbound.telegram.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installTelegramInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.telegram.js"; - -describe("telegram inbound contract", () => { - installTelegramInboundContractSuite(); -}); diff --git a/src/channels/plugins/contracts/inbound.whatsapp.contract.test.ts b/src/channels/plugins/contracts/inbound.whatsapp.contract.test.ts deleted file mode 100644 index 2c5f6169b6d..00000000000 --- a/src/channels/plugins/contracts/inbound.whatsapp.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installWhatsAppInboundContractSuite } from "../../../../test/helpers/channels/inbound-contract.whatsapp.js"; - -describe("whatsapp inbound contract", () => { - installWhatsAppInboundContractSuite(); -}); diff --git a/src/channels/plugins/contracts/outbound-payload.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.contract.test.ts new file mode 100644 index 00000000000..1a0f1de8e5d --- /dev/null +++ b/src/channels/plugins/contracts/outbound-payload.contract.test.ts @@ -0,0 +1,35 @@ +import { describe } from "vitest"; +import { + installDirectTextMediaOutboundPayloadContractSuite, + installDiscordOutboundPayloadContractSuite, + installSlackOutboundPayloadContractSuite, + installWhatsAppOutboundPayloadContractSuite, + installZaloOutboundPayloadContractSuite, + installZalouserOutboundPayloadContractSuite, +} from "../../../../test/helpers/channels/outbound-payload-contract.js"; + +describe("outbound payload contracts", () => { + describe("discord", () => { + installDiscordOutboundPayloadContractSuite(); + }); + + describe("imessage", () => { + installDirectTextMediaOutboundPayloadContractSuite(); + }); + + describe("slack", () => { + installSlackOutboundPayloadContractSuite(); + }); + + describe("whatsapp", () => { + installWhatsAppOutboundPayloadContractSuite(); + }); + + describe("zalo", () => { + installZaloOutboundPayloadContractSuite(); + }); + + describe("zalouser", () => { + installZalouserOutboundPayloadContractSuite(); + }); +}); diff --git a/src/channels/plugins/contracts/outbound-payload.discord.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.discord.contract.test.ts deleted file mode 100644 index f3e478897cb..00000000000 --- a/src/channels/plugins/contracts/outbound-payload.discord.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installDiscordOutboundPayloadContractSuite } from "../../../../test/helpers/channels/outbound-payload-contract.js"; - -describe("discord outbound payload contract", () => { - installDiscordOutboundPayloadContractSuite(); -}); diff --git a/src/channels/plugins/contracts/outbound-payload.imessage.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.imessage.contract.test.ts deleted file mode 100644 index fa82608026f..00000000000 --- a/src/channels/plugins/contracts/outbound-payload.imessage.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installDirectTextMediaOutboundPayloadContractSuite } from "../../../../test/helpers/channels/outbound-payload-contract.js"; - -describe("imessage outbound payload contract", () => { - installDirectTextMediaOutboundPayloadContractSuite(); -}); diff --git a/src/channels/plugins/contracts/outbound-payload.slack.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.slack.contract.test.ts deleted file mode 100644 index e1c347313ca..00000000000 --- a/src/channels/plugins/contracts/outbound-payload.slack.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installSlackOutboundPayloadContractSuite } from "../../../../test/helpers/channels/outbound-payload-contract.js"; - -describe("slack outbound payload contract", () => { - installSlackOutboundPayloadContractSuite(); -}); diff --git a/src/channels/plugins/contracts/outbound-payload.whatsapp.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.whatsapp.contract.test.ts deleted file mode 100644 index c8bd1f9a72f..00000000000 --- a/src/channels/plugins/contracts/outbound-payload.whatsapp.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installWhatsAppOutboundPayloadContractSuite } from "../../../../test/helpers/channels/outbound-payload-contract.js"; - -describe("whatsapp outbound payload contract", () => { - installWhatsAppOutboundPayloadContractSuite(); -}); diff --git a/src/channels/plugins/contracts/outbound-payload.zalo.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.zalo.contract.test.ts deleted file mode 100644 index 5d395bbf661..00000000000 --- a/src/channels/plugins/contracts/outbound-payload.zalo.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installZaloOutboundPayloadContractSuite } from "../../../../test/helpers/channels/outbound-payload-contract.js"; - -describe("zalo outbound payload contract", () => { - installZaloOutboundPayloadContractSuite(); -}); diff --git a/src/channels/plugins/contracts/outbound-payload.zalouser.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.zalouser.contract.test.ts deleted file mode 100644 index 0e1a79b6fc4..00000000000 --- a/src/channels/plugins/contracts/outbound-payload.zalouser.contract.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe } from "vitest"; -import { installZalouserOutboundPayloadContractSuite } from "../../../../test/helpers/channels/outbound-payload-contract.js"; - -describe("zalouser outbound payload contract", () => { - installZalouserOutboundPayloadContractSuite(); -}); diff --git a/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts b/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts index a9e0712d6d5..35815290d53 100644 --- a/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts +++ b/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts @@ -1,12 +1,9 @@ -import { spawnSync } from "node:child_process"; -import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs"; +import { readdirSync, readFileSync } from "node:fs"; import { createRequire } from "node:module"; import { dirname, join, relative, resolve } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; -import * as tar from "tar"; -import { afterEach, describe, expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { pluginSdkEntrypoints } from "../../plugin-sdk/entrypoints.js"; -import { cleanupTrackedTempDirs, makeTrackedTempDir } from "../test-helpers/fs-fixtures.js"; const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); const REPO_ROOT = resolve(ROOT_DIR, ".."); @@ -15,9 +12,6 @@ const PUBLIC_CONTRACT_REFERENCE_FILES = [ "src/plugins/contracts/plugin-sdk-subpaths.test.ts", ] as const; const PLUGIN_SDK_SUBPATH_PATTERN = /openclaw\/plugin-sdk\/([a-z0-9][a-z0-9-]*)\b/g; -const NPM_PACK_MAX_BUFFER_BYTES = 64 * 1024 * 1024; -const WINDOWS_UNSAFE_CMD_CHARS_RE = /[&|<>^%\r\n]/; -const tempDirs: string[] = []; function collectPluginSdkPackageExports(): string[] { const packageJson = JSON.parse(readFileSync(resolve(REPO_ROOT, "package.json"), "utf8")) as { @@ -87,124 +81,6 @@ function createRootPackageRequire() { return createRequire(pathToFileURL(resolve(REPO_ROOT, "package.json")).href); } -function isNpmExecPath(value: string): boolean { - return /^npm(?:-cli)?(?:\.(?:c?js|cmd|exe))?$/.test( - value.split(/[\\/]/).at(-1)?.toLowerCase() ?? "", - ); -} - -function escapeForCmdExe(arg: string): string { - if (WINDOWS_UNSAFE_CMD_CHARS_RE.test(arg)) { - throw new Error(`unsafe Windows cmd.exe argument detected: ${JSON.stringify(arg)}`); - } - if (!arg.includes(" ") && !arg.includes('"')) { - return arg; - } - return `"${arg.replace(/"/g, '""')}"`; -} - -function buildCmdExeCommandLine(command: string, args: string[]): string { - return [escapeForCmdExe(command), ...args.map(escapeForCmdExe)].join(" "); -} - -type NpmCommandInvocation = { - command: string; - args: string[]; - env?: NodeJS.ProcessEnv; - windowsVerbatimArguments?: boolean; -}; - -function resolveNpmCommandInvocation(npmArgs: string[]): NpmCommandInvocation { - const npmExecPath = process.env.npm_execpath; - if (typeof npmExecPath === "string" && npmExecPath.length > 0 && isNpmExecPath(npmExecPath)) { - return { command: process.execPath, args: [npmExecPath, ...npmArgs] }; - } - - if (process.platform !== "win32") { - return { command: "npm", args: npmArgs }; - } - - const nodeDir = dirname(process.execPath); - const npmCliCandidates = [ - resolve(nodeDir, "../lib/node_modules/npm/bin/npm-cli.js"), - resolve(nodeDir, "node_modules/npm/bin/npm-cli.js"), - ]; - const npmCliPath = npmCliCandidates.find((candidate) => existsSync(candidate)); - if (npmCliPath) { - return { command: process.execPath, args: [npmCliPath, ...npmArgs] }; - } - - const npmExePath = resolve(nodeDir, "npm.exe"); - if (existsSync(npmExePath)) { - return { command: npmExePath, args: npmArgs }; - } - - const npmCmdPath = resolve(nodeDir, "npm.cmd"); - if (existsSync(npmCmdPath)) { - return { - command: process.env.ComSpec ?? "cmd.exe", - args: ["/d", "/s", "/c", buildCmdExeCommandLine(npmCmdPath, npmArgs)], - windowsVerbatimArguments: true, - }; - } - - return { - command: process.env.ComSpec ?? "cmd.exe", - args: ["/d", "/s", "/c", buildCmdExeCommandLine("npm.cmd", npmArgs)], - windowsVerbatimArguments: true, - }; -} - -function packOpenClawToTempDir(packDir: string): string { - const invocation = resolveNpmCommandInvocation([ - "pack", - "--ignore-scripts", - "--json", - "--pack-destination", - packDir, - ]); - const result = spawnSync(invocation.command, invocation.args, { - cwd: REPO_ROOT, - encoding: "utf8", - env: { - ...process.env, - ...invocation.env, - COREPACK_ENABLE_DOWNLOAD_PROMPT: "0", - }, - maxBuffer: NPM_PACK_MAX_BUFFER_BYTES, - stdio: ["ignore", "pipe", "pipe"], - windowsVerbatimArguments: invocation.windowsVerbatimArguments, - }); - if (result.error) { - throw result.error; - } - if (result.status !== 0) { - throw new Error((result.stderr || result.stdout || "npm pack failed").trim()); - } - const raw = result.stdout; - const parsed = JSON.parse(raw) as Array<{ filename?: string }>; - const filename = parsed[0]?.filename?.trim(); - if (!filename) { - throw new Error(`npm pack did not return a filename: ${raw}`); - } - return join(packDir, filename); -} - -async function readPackedRootPackageJson(archivePath: string): Promise<{ - dependencies?: Record; -}> { - const extractDir = makeTrackedTempDir("openclaw-packed-root-package-json", tempDirs); - await tar.x({ - file: archivePath, - cwd: extractDir, - filter: (entryPath) => entryPath === "package/package.json", - strict: true, - }); - return JSON.parse(readFileSync(join(extractDir, "package", "package.json"), "utf8")) as { - dependencies?: Record; - }; -} - function collectExtensionFiles(dir: string): string[] { const entries = readdirSync(dir, { withFileTypes: true }); const files: string[] = []; @@ -259,10 +135,6 @@ function collectExtensionCoreImportLeaks(): Array<{ file: string; specifier: str } describe("plugin-sdk package contract guardrails", () => { - afterEach(() => { - cleanupTrackedTempDirs(tempDirs); - }); - it("keeps package.json exports aligned with built plugin-sdk entrypoints", () => { expect(collectPluginSdkPackageExports()).toEqual([...pluginSdkEntrypoints].toSorted()); }); @@ -291,7 +163,7 @@ describe("plugin-sdk package contract guardrails", () => { expect(failures).toEqual([]); }); - it("mirrors matrix runtime deps needed by the bundled host graph", () => { + it("mirrors package runtime deps needed by bundled host graphs", () => { const rootRuntimeDeps = collectRuntimeDependencySpecs(readRootPackageJson()); const matrixPackageJson = readMatrixPackageJson(); const matrixRuntimeDeps = collectRuntimeDependencySpecs(matrixPackageJson); @@ -304,6 +176,7 @@ describe("plugin-sdk package contract guardrails", () => { ]) { expect(rootRuntimeDeps.get(dep)).toBe(matrixRuntimeDeps.get(dep)); } + expect(rootRuntimeDeps.has("@openclaw/plugin-package-contract")).toBe(false); }); it("resolves matrix crypto WASM from the root runtime surface", () => { @@ -316,20 +189,6 @@ describe("plugin-sdk package contract guardrails", () => { expect(resolvedPath).toContain("@matrix-org/matrix-sdk-crypto-wasm"); }); - it("keeps matrix crypto WASM in the packed artifact manifest", async () => { - const tempRoot = makeTrackedTempDir("openclaw-matrix-wasm-pack", tempDirs); - const packDir = join(tempRoot, "pack"); - mkdirSync(packDir, { recursive: true }); - - const archivePath = packOpenClawToTempDir(packDir); - const packedPackageJson = await readPackedRootPackageJson(archivePath); - const matrixPackageJson = readMatrixPackageJson(); - expect(packedPackageJson.dependencies?.["@matrix-org/matrix-sdk-crypto-wasm"]).toBe( - matrixPackageJson.dependencies?.["@matrix-org/matrix-sdk-crypto-wasm"], - ); - expect(packedPackageJson.dependencies?.["@openclaw/plugin-package-contract"]).toBeUndefined(); - }); - it("keeps extension sources on public sdk or local package seams", () => { expect(collectExtensionCoreImportLeaks()).toEqual([]); }); diff --git a/test/helpers/channels/inbound-contract.slack.ts b/test/helpers/channels/inbound-contract.slack.ts index 4dbd9cb6e40..a6a6ce72bfe 100644 --- a/test/helpers/channels/inbound-contract.slack.ts +++ b/test/helpers/channels/inbound-contract.slack.ts @@ -34,7 +34,7 @@ type SlackTestApi = { const slackPrepareTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({ fromModuleUrl: import.meta.url, pluginId: "slack", - artifactBasename: "test-api.js", + artifactBasename: "inbound-contract-test-api.js", }); let slackTestApiPromise: Promise | undefined; diff --git a/test/helpers/channels/outbound-payload-contract.ts b/test/helpers/channels/outbound-payload-contract.ts index 751d7e3468c..ffe676709b3 100644 --- a/test/helpers/channels/outbound-payload-contract.ts +++ b/test/helpers/channels/outbound-payload-contract.ts @@ -21,12 +21,12 @@ const discordOutboundAdapterModuleId = resolveRelativeBundledPluginPublicModuleI const slackTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({ fromModuleUrl: import.meta.url, pluginId: "slack", - artifactBasename: "test-api.js", + artifactBasename: "outbound-payload-test-api.js", }); const whatsappTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({ fromModuleUrl: import.meta.url, pluginId: "whatsapp", - artifactBasename: "test-api.js", + artifactBasename: "outbound-payload-test-api.js", }); let discordOutboundCache: Promise | undefined; diff --git a/test/helpers/plugins/provider-discovery-contract.ts b/test/helpers/plugins/provider-discovery-contract.ts index 7a40b92e3a9..c581129203c 100644 --- a/test/helpers/plugins/provider-discovery-contract.ts +++ b/test/helpers/plugins/provider-discovery-contract.ts @@ -1,4 +1,4 @@ -import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.js"; import type { OpenClawConfig } from "../../../src/config/config.js"; import type { ModelDefinitionConfig } from "../../../src/config/types.models.js"; @@ -179,6 +179,7 @@ function installDiscoveryHooks( providerIds: readonly BundledProviderUnderTest[], ) { beforeAll(async () => { + vi.resetModules(); vi.doMock("openclaw/plugin-sdk/agent-runtime", () => { return { ensureAuthProfileStore: ensureAuthProfileStoreMock, @@ -311,10 +312,13 @@ function installDiscoveryHooks( "cloudflare-ai-gateway", ); } - setRuntimeAuthStore(); }); beforeEach(() => { + setRuntimeAuthStore(); + }); + + afterEach(() => { vi.restoreAllMocks(); resolveCopilotApiTokenMock.mockReset(); buildOllamaProviderMock.mockReset();