From 847faa3d04a76a2bcb3d7d71837f532445846479 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 3 Apr 2026 04:37:34 +0100 Subject: [PATCH] test: trim extension test import churn --- .../bluebubbles/src/channel.pairing.test.ts | 10 +++--- .../client-fetch.loopback-auth.test.ts | 1 - ...re.clamps-timeoutms-scrollintoview.test.ts | 1 - .../pw-tools-core.interactions.batch.test.ts | 1 - ...ls-core.last-file-chooser-arm-wins.test.ts | 1 - ...-core.screenshots-element-selector.test.ts | 1 - ...-core.waits-next-download-saves-it.test.ts | 1 - .../routes/agent.existing-session.test.ts | 1 - .../browser/server.auth-fail-closed.test.ts | 1 - ...te-disabled-does-not-block-storage.test.ts | 1 - extensions/diffs/src/browser.test.ts | 1 - .../diffs/src/tool-render-output.test.ts | 9 +++-- .../src/monitor/exec-approvals.test.ts | 1 - extensions/feishu/src/channel.test.ts | 20 ++++------- extensions/feishu/src/chat.test.ts | 11 +++---- extensions/feishu/src/client.test.ts | 1 - .../feishu/src/docx.account-selection.test.ts | 8 +++-- extensions/feishu/src/drive.test.ts | 10 +++--- extensions/feishu/src/media.test.ts | 8 +++-- extensions/feishu/src/probe.test.ts | 18 ++++------ extensions/feishu/src/send-target.test.ts | 8 +++-- .../feishu/src/send.reply-fallback.test.ts | 8 +++-- extensions/feishu/src/send.test.ts | 8 +++-- .../feishu/src/tool-account-routing.test.ts | 8 +++-- .../firecrawl/src/firecrawl-tools.test.ts | 2 -- extensions/irc/src/probe.test.ts | 10 +++--- extensions/line/src/bot-handlers.test.ts | 1 - extensions/line/src/download.test.ts | 12 ++++--- .../matrix/src/matrix/draft-stream.test.ts | 1 - extensions/mattermost/src/channel.test.ts | 8 +++-- .../src/mattermost/directory.test.ts | 10 +++--- .../src/mattermost/monitor-auth.test.ts | 33 +++++++++++-------- .../src/mattermost/monitor-resources.test.ts | 12 ++++--- .../src/mattermost/monitor-slash.test.ts | 14 ++++---- .../src/mattermost/target-resolution.test.ts | 33 +++++++++++-------- extensions/microsoft/tts.test.ts | 12 ++----- extensions/msteams/src/setup-surface.test.ts | 13 ++++---- extensions/nextcloud-talk/src/core.test.ts | 5 ++- .../openshell/src/openshell-core.test.ts | 1 - .../src/searxng-search-provider.test.ts | 1 - .../slack/src/actions.download-file.test.ts | 1 - extensions/slack/src/client.test.ts | 9 +++-- ...onitor.threading.missing-thread-ts.test.ts | 8 +++-- .../slack/src/monitor.tool-result.test.ts | 8 +++-- extensions/slack/src/monitor/auth.test.ts | 8 +++-- .../slack/src/monitor/events/channels.test.ts | 1 - .../slack/src/monitor/events/members.test.ts | 1 - .../slack/src/monitor/events/messages.test.ts | 1 - .../slack/src/monitor/events/pins.test.ts | 1 - .../src/monitor/events/reactions.test.ts | 1 - .../message-handler.app-mention-race.test.ts | 8 +++-- .../slack/src/monitor/message-handler.test.ts | 1 - .../message-handler/preview-finalize.test.ts | 10 +++--- extensions/slack/src/monitor/replies.test.ts | 1 - extensions/slack/src/outbound-hooks.test.ts | 10 +++--- extensions/slack/src/probe.test.ts | 10 +++--- .../src/channel.integration.test.ts | 11 ++++--- extensions/synology-chat/src/client.test.ts | 13 ++++---- extensions/tavily/src/tavily-client.test.ts | 11 ++++--- extensions/tavily/src/tavily-tools.test.ts | 1 - extensions/telegram/src/api-fetch.test.ts | 21 +++++++----- .../bot-message-context.acp-bindings.test.ts | 1 - extensions/telegram/src/bot-message.test.ts | 1 - .../src/bot-native-commands.registry.test.ts | 1 - .../bot-native-commands.session-meta.test.ts | 1 - .../telegram/src/bot-native-commands.test.ts | 1 - .../telegram/src/fetch.network-policy.test.ts | 1 - extensions/telegram/src/fetch.test.ts | 1 - .../telegram/src/polling-session.test.ts | 10 +++--- extensions/telegram/src/send.proxy.test.ts | 1 - extensions/telegram/src/webhook.test.ts | 1 - extensions/tlon/src/monitor/approval.test.ts | 10 +++--- .../src/auto-reply/deliver-reply.test.ts | 1 - .../src/auto-reply/heartbeat-runner.test.ts | 1 - .../src/inbound/access-control.test.ts | 1 - extensions/whatsapp/src/login.test.ts | 12 ++++--- .../whatsapp/src/resolve-target.test.ts | 1 - extensions/whatsapp/src/send.test.ts | 1 - .../zalouser/src/channel.sendpayload.test.ts | 6 ++-- extensions/zalouser/src/zca-client.test.ts | 8 ++--- .../src/host/batch-gemini.test.ts | 10 +++--- .../src/host/batch-http.test.ts | 11 ++++--- .../src/host/embeddings-gemini.test.ts | 1 - .../src/host/embeddings-remote-fetch.test.ts | 15 +++++---- .../src/host/embeddings-voyage.test.ts | 15 +++++---- .../src/host/embeddings.test.ts | 14 ++++---- .../src/host/post-json.test.ts | 11 ++++--- 87 files changed, 293 insertions(+), 286 deletions(-) diff --git a/extensions/bluebubbles/src/channel.pairing.test.ts b/extensions/bluebubbles/src/channel.pairing.test.ts index aaec7ce945f..d15c48f9ac5 100644 --- a/extensions/bluebubbles/src/channel.pairing.test.ts +++ b/extensions/bluebubbles/src/channel.pairing.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "./runtime-api.js"; const sendMessageBlueBubblesMock = vi.hoisted(() => vi.fn()); @@ -17,11 +17,13 @@ vi.mock("../../../src/channels/plugins/bundled.js", () => ({ let bluebubblesPlugin: typeof import("./channel.js").bluebubblesPlugin; describe("bluebubblesPlugin.pairing.notifyApproval", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ bluebubblesPlugin } = await import("./channel.js")); + }); + + beforeEach(() => { sendMessageBlueBubblesMock.mockReset(); sendMessageBlueBubblesMock.mockResolvedValue({ messageId: "bb-pairing" }); - ({ bluebubblesPlugin } = await import("./channel.js")); }); it("preserves accountId when sending pairing approvals", async () => { diff --git a/extensions/browser/src/browser/client-fetch.loopback-auth.test.ts b/extensions/browser/src/browser/client-fetch.loopback-auth.test.ts index 89dd13307ef..d5b702d9484 100644 --- a/extensions/browser/src/browser/client-fetch.loopback-auth.test.ts +++ b/extensions/browser/src/browser/client-fetch.loopback-auth.test.ts @@ -86,7 +86,6 @@ async function expectThrownBrowserFetchError( describe("fetchBrowserJson loopback auth", () => { beforeAll(async () => { - vi.resetModules(); ({ fetchBrowserJson } = await import("./client-fetch.js")); }); diff --git a/extensions/browser/src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts b/extensions/browser/src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts index 922a884faff..c1978912eea 100644 --- a/extensions/browser/src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts +++ b/extensions/browser/src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts @@ -10,7 +10,6 @@ let mod: typeof import("./pw-tools-core.js"); describe("pw-tools-core", () => { beforeAll(async () => { - vi.resetModules(); mod = await import("./pw-tools-core.js"); }); diff --git a/extensions/browser/src/browser/pw-tools-core.interactions.batch.test.ts b/extensions/browser/src/browser/pw-tools-core.interactions.batch.test.ts index 6ce48dddc5b..2801ebe8190 100644 --- a/extensions/browser/src/browser/pw-tools-core.interactions.batch.test.ts +++ b/extensions/browser/src/browser/pw-tools-core.interactions.batch.test.ts @@ -35,7 +35,6 @@ let batchViaPlaywright: typeof import("./pw-tools-core.interactions.js").batchVi describe("batchViaPlaywright", () => { beforeAll(async () => { - vi.resetModules(); ({ batchViaPlaywright } = await import("./pw-tools-core.interactions.js")); }); diff --git a/extensions/browser/src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts b/extensions/browser/src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts index 76724b3b569..c83091382c3 100644 --- a/extensions/browser/src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts +++ b/extensions/browser/src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts @@ -13,7 +13,6 @@ let mod: typeof import("./pw-tools-core.js"); describe("pw-tools-core", () => { beforeAll(async () => { - vi.resetModules(); mod = await import("./pw-tools-core.js"); }); diff --git a/extensions/browser/src/browser/pw-tools-core.screenshots-element-selector.test.ts b/extensions/browser/src/browser/pw-tools-core.screenshots-element-selector.test.ts index 3505a7f9ee0..7c6e924f61e 100644 --- a/extensions/browser/src/browser/pw-tools-core.screenshots-element-selector.test.ts +++ b/extensions/browser/src/browser/pw-tools-core.screenshots-element-selector.test.ts @@ -27,7 +27,6 @@ function createFileChooserPageMocks() { describe("pw-tools-core", () => { beforeAll(async () => { - vi.resetModules(); mod = await import("./pw-tools-core.js"); }); diff --git a/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts b/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts index 0dbb675c5a1..6778c1cd8db 100644 --- a/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts +++ b/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts @@ -57,7 +57,6 @@ let tmpDirModule: typeof import("../infra/tmp-openclaw-dir.js"); describe("pw-tools-core", () => { beforeAll(async () => { - vi.resetModules(); vi.doMock("./pw-session.js", () => sessionMocks); vi.doMock("./chrome.js", () => chromeMocks); tmpDirModule = await import("../infra/tmp-openclaw-dir.js"); diff --git a/extensions/browser/src/browser/routes/agent.existing-session.test.ts b/extensions/browser/src/browser/routes/agent.existing-session.test.ts index bf34fa5264a..a2742423f14 100644 --- a/extensions/browser/src/browser/routes/agent.existing-session.test.ts +++ b/extensions/browser/src/browser/routes/agent.existing-session.test.ts @@ -98,7 +98,6 @@ let registerBrowserAgentActRoutes: typeof import("./agent.act.js").registerBrows let registerBrowserAgentSnapshotRoutes: typeof import("./agent.snapshot.js").registerBrowserAgentSnapshotRoutes; beforeAll(async () => { - vi.resetModules(); ({ registerBrowserAgentActRoutes } = await import("./agent.act.js")); ({ registerBrowserAgentSnapshotRoutes } = await import("./agent.snapshot.js")); }); diff --git a/extensions/browser/src/browser/server.auth-fail-closed.test.ts b/extensions/browser/src/browser/server.auth-fail-closed.test.ts index ae105234775..4a0e16ac886 100644 --- a/extensions/browser/src/browser/server.auth-fail-closed.test.ts +++ b/extensions/browser/src/browser/server.auth-fail-closed.test.ts @@ -72,7 +72,6 @@ describe("browser control auth bootstrap failures", () => { afterEach(async () => { await stopBrowserControlServer(); - vi.resetModules(); }); it("fails closed when auth bootstrap throws and no auth is configured", async () => { diff --git a/extensions/browser/src/browser/server.evaluate-disabled-does-not-block-storage.test.ts b/extensions/browser/src/browser/server.evaluate-disabled-does-not-block-storage.test.ts index 4a46a79c2ea..b2722d2843a 100644 --- a/extensions/browser/src/browser/server.evaluate-disabled-does-not-block-storage.test.ts +++ b/extensions/browser/src/browser/server.evaluate-disabled-does-not-block-storage.test.ts @@ -70,7 +70,6 @@ let stopBrowserControlServer: typeof import("./server.js").stopBrowserControlSer describe("browser control evaluate gating", () => { beforeAll(async () => { - vi.resetModules(); ({ startBrowserControlServerFromConfig, stopBrowserControlServer } = await import("./server.js")); }); diff --git a/extensions/diffs/src/browser.test.ts b/extensions/diffs/src/browser.test.ts index 54b5f615c52..9053507442e 100644 --- a/extensions/diffs/src/browser.test.ts +++ b/extensions/diffs/src/browser.test.ts @@ -28,7 +28,6 @@ describe("PlaywrightDiffScreenshotter", () => { let cleanupRootDir: () => Promise; beforeAll(async () => { - vi.resetModules(); ({ PlaywrightDiffScreenshotter, resetSharedBrowserStateForTests } = await import("./browser.js")); }); diff --git a/extensions/diffs/src/tool-render-output.test.ts b/extensions/diffs/src/tool-render-output.test.ts index 97d838b1054..6b09d5efa84 100644 --- a/extensions/diffs/src/tool-render-output.test.ts +++ b/extensions/diffs/src/tool-render-output.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; import type { OpenClawPluginApi } from "../api.js"; import type { DiffScreenshotter } from "./browser.js"; @@ -16,11 +16,15 @@ vi.mock("./render.js", () => ({ })); describe("diffs tool rendered output guards", () => { + let createDiffsTool: typeof import("./tool.js").createDiffsTool; let cleanupRootDir: () => Promise; let store: Awaited>["store"]; + beforeAll(async () => { + ({ createDiffsTool } = await import("./tool.js")); + }); + beforeEach(async () => { - vi.resetModules(); renderDiffDocumentMock.mockReset(); ({ store, cleanup: cleanupRootDir } = await createDiffStoreHarness( "openclaw-diffs-tool-render-output-", @@ -39,7 +43,6 @@ describe("diffs tool rendered output guards", () => { imageHtml: "", }); - const { createDiffsTool } = await import("./tool.js"); const screenshotter = createPngScreenshotter({ assertHtml: (html) => { expect(html).toBe(""); diff --git a/extensions/discord/src/monitor/exec-approvals.test.ts b/extensions/discord/src/monitor/exec-approvals.test.ts index cc1493ce935..53d623e91bf 100644 --- a/extensions/discord/src/monitor/exec-approvals.test.ts +++ b/extensions/discord/src/monitor/exec-approvals.test.ts @@ -296,7 +296,6 @@ beforeEach(() => { }); beforeAll(async () => { - vi.resetModules(); ({ buildExecApprovalCustomId, extractDiscordChannelId, diff --git a/extensions/feishu/src/channel.test.ts b/extensions/feishu/src/channel.test.ts index 9e36dce3940..89f086b0eae 100644 --- a/extensions/feishu/src/channel.test.ts +++ b/extensions/feishu/src/channel.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../runtime-api.js"; import { createFeishuCardInteractionEnvelope } from "./card-interaction.js"; import { looksLikeFeishuId, normalizeFeishuTarget, resolveReceiveIdType } from "./targets.js"; @@ -101,12 +101,11 @@ async function expectLegacyFeishuCardPayloadRejected(cfg: OpenClawConfig, card: expect(sendCardFeishuMock).not.toHaveBeenCalled(); } -describe("feishuPlugin.status.probeAccount", () => { - beforeEach(async () => { - vi.resetModules(); - ({ feishuPlugin } = await import("./channel.js")); - }); +beforeAll(async () => { + ({ feishuPlugin } = await import("./channel.js")); +}); +describe("feishuPlugin.status.probeAccount", () => { it("uses current account credentials for multi-account config", async () => { const cfg = { channels: { @@ -145,9 +144,7 @@ describe("feishuPlugin.status.probeAccount", () => { }); describe("feishuPlugin.pairing.notifyApproval", () => { - beforeEach(async () => { - vi.resetModules(); - ({ feishuPlugin } = await import("./channel.js")); + beforeEach(() => { sendMessageFeishuMock.mockReset(); sendMessageFeishuMock.mockResolvedValue({ messageId: "pairing-msg", chatId: "ou_user" }); }); @@ -184,11 +181,6 @@ describe("feishuPlugin.pairing.notifyApproval", () => { }); describe("feishuPlugin messaging", () => { - beforeEach(async () => { - vi.resetModules(); - ({ feishuPlugin } = await import("./channel.js")); - }); - it("owns sender/topic session inheritance candidates", () => { expect( feishuPlugin.messaging?.resolveSessionConversation?.({ diff --git a/extensions/feishu/src/chat.test.ts b/extensions/feishu/src/chat.test.ts index d2d80e48110..6c80d826d48 100644 --- a/extensions/feishu/src/chat.test.ts +++ b/extensions/feishu/src/chat.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { OpenClawPluginApi } from "../runtime-api.js"; @@ -30,8 +30,11 @@ describe("registerFeishuChatTools", () => { }); } + beforeAll(async () => { + ({ registerFeishuChatTools } = await import("./chat.js")); + }); + beforeEach(() => { - vi.resetModules(); vi.clearAllMocks(); createFeishuClientMock.mockReturnValue({ im: { @@ -44,10 +47,6 @@ describe("registerFeishuChatTools", () => { }); }); - beforeEach(async () => { - ({ registerFeishuChatTools } = await import("./chat.js")); - }); - it("registers feishu_chat and handles info/members actions", async () => { const registerTool = vi.fn(); registerFeishuChatTools( diff --git a/extensions/feishu/src/client.test.ts b/extensions/feishu/src/client.test.ts index 3211f4b536a..316f4fbaf8c 100644 --- a/extensions/feishu/src/client.test.ts +++ b/extensions/feishu/src/client.test.ts @@ -133,7 +133,6 @@ function firstWsClientOptions(): { agent?: unknown } { } beforeAll(async () => { - vi.resetModules(); vi.doMock("@larksuiteoapi/node-sdk", () => ({ AppType: { SelfBuild: "self" }, Domain: { Feishu: "https://open.feishu.cn", Lark: "https://open.larksuite.com" }, diff --git a/extensions/feishu/src/docx.account-selection.test.ts b/extensions/feishu/src/docx.account-selection.test.ts index 3975e3fd65f..d4a2ff39470 100644 --- a/extensions/feishu/src/docx.account-selection.test.ts +++ b/extensions/feishu/src/docx.account-selection.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, test, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { createToolFactoryHarness } from "./tool-factory-test-harness.js"; @@ -22,9 +22,11 @@ vi.mock("@larksuiteoapi/node-sdk", () => { describe("feishu_doc account selection", () => { let registerFeishuDocTools: typeof import("./docx.js").registerFeishuDocTools; - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ registerFeishuDocTools } = await import("./docx.js")); + }); + + beforeEach(() => { vi.clearAllMocks(); }); diff --git a/extensions/feishu/src/drive.test.ts b/extensions/feishu/src/drive.test.ts index 6fbc1c23ff3..aee7a4c699c 100644 --- a/extensions/feishu/src/drive.test.ts +++ b/extensions/feishu/src/drive.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { OpenClawPluginApi } from "../runtime-api.js"; @@ -31,10 +31,12 @@ function createDriveToolApi(params: { describe("registerFeishuDriveTools", () => { const requestMock = vi.fn(); - beforeEach(async () => { - vi.resetModules(); - vi.clearAllMocks(); + beforeAll(async () => { ({ registerFeishuDriveTools } = await import("./drive.js")); + }); + + beforeEach(() => { + vi.clearAllMocks(); resolveAnyEnabledFeishuToolsConfigMock.mockReturnValue({ doc: false, chat: false, diff --git a/extensions/feishu/src/media.test.ts b/extensions/feishu/src/media.test.ts index 845beeb4e47..a6ddc834c67 100644 --- a/extensions/feishu/src/media.test.ts +++ b/extensions/feishu/src/media.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ClawdbotConfig } from "../runtime-api.js"; const createFeishuClientMock = vi.hoisted(() => vi.fn()); @@ -82,14 +82,16 @@ function mockResolvedFeishuAccount() { } describe("sendMediaFeishu msg_type routing", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ downloadImageFeishu, downloadMessageResourceFeishu, sanitizeFileNameForUpload, sendMediaFeishu, } = await import("./media.js")); + }); + + beforeEach(() => { vi.clearAllMocks(); mockResolvedFeishuAccount(); diff --git a/extensions/feishu/src/probe.test.ts b/extensions/feishu/src/probe.test.ts index f394aec8b3e..827d15aa134 100644 --- a/extensions/feishu/src/probe.test.ts +++ b/extensions/feishu/src/probe.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const createFeishuClientMock = vi.hoisted(() => vi.fn()); @@ -6,12 +6,6 @@ vi.mock("./client.js", () => ({ createFeishuClient: createFeishuClientMock, })); -async function importProbeModule(scope: string) { - void scope; - vi.resetModules(); - return await import("./probe.js"); -} - let FEISHU_PROBE_REQUEST_TIMEOUT_MS: typeof import("./probe.js").FEISHU_PROBE_REQUEST_TIMEOUT_MS; let probeFeishu: typeof import("./probe.js").probeFeishu; let clearProbeCache: typeof import("./probe.js").clearProbeCache; @@ -106,10 +100,12 @@ async function readSequentialDefaultProbePair() { } describe("probeFeishu", () => { - beforeEach(async () => { - ({ FEISHU_PROBE_REQUEST_TIMEOUT_MS, probeFeishu, clearProbeCache } = await importProbeModule( - `probe-${Date.now()}-${Math.random()}`, - )); + beforeAll(async () => { + ({ FEISHU_PROBE_REQUEST_TIMEOUT_MS, probeFeishu, clearProbeCache } = + await import("./probe.js")); + }); + + beforeEach(() => { clearProbeCache(); vi.restoreAllMocks(); }); diff --git a/extensions/feishu/src/send-target.test.ts b/extensions/feishu/src/send-target.test.ts index a1a2e17e245..c82ac22e006 100644 --- a/extensions/feishu/src/send-target.test.ts +++ b/extensions/feishu/src/send-target.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ClawdbotConfig } from "../runtime-api.js"; const resolveFeishuAccountMock = vi.hoisted(() => vi.fn()); @@ -19,9 +19,11 @@ describe("resolveFeishuSendTarget", () => { const cfg = {} as ClawdbotConfig; const client = { id: "client" }; - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ resolveFeishuSendTarget } = await import("./send-target.js")); + }); + + beforeEach(() => { resolveFeishuAccountMock.mockReset().mockReturnValue({ accountId: "default", enabled: true, diff --git a/extensions/feishu/src/send.reply-fallback.test.ts b/extensions/feishu/src/send.reply-fallback.test.ts index a612af2a8e6..7148b98284c 100644 --- a/extensions/feishu/src/send.reply-fallback.test.ts +++ b/extensions/feishu/src/send.reply-fallback.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const resolveFeishuSendTargetMock = vi.hoisted(() => vi.fn()); const resolveMarkdownTableModeMock = vi.hoisted(() => vi.fn(() => "preserve")); @@ -42,9 +42,11 @@ describe("Feishu reply fallback for withdrawn/deleted targets", () => { expect(result.messageId).toBe(expectedMessageId); } - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ sendCardFeishu, sendMessageFeishu } = await import("./send.js")); + }); + + beforeEach(() => { vi.clearAllMocks(); resolveFeishuSendTargetMock.mockReturnValue({ client: { diff --git a/extensions/feishu/src/send.test.ts b/extensions/feishu/src/send.test.ts index 69ecba127fe..d8e4a7c54dd 100644 --- a/extensions/feishu/src/send.test.ts +++ b/extensions/feishu/src/send.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ClawdbotConfig } from "../runtime-api.js"; const { @@ -59,8 +59,7 @@ let resolveFeishuCardTemplate: typeof import("./send.js").resolveFeishuCardTempl let sendMessageFeishu: typeof import("./send.js").sendMessageFeishu; describe("getMessageFeishu", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ buildStructuredCard, editMessageFeishu, @@ -69,6 +68,9 @@ describe("getMessageFeishu", () => { resolveFeishuCardTemplate, sendMessageFeishu, } = await import("./send.js")); + }); + + beforeEach(() => { vi.clearAllMocks(); mockResolveMarkdownTableMode.mockReturnValue("preserve"); mockConvertMarkdownTables.mockImplementation((text: string) => text); diff --git a/extensions/feishu/src/tool-account-routing.test.ts b/extensions/feishu/src/tool-account-routing.test.ts index b0f9ba8bf6b..2dcac5d9a36 100644 --- a/extensions/feishu/src/tool-account-routing.test.ts +++ b/extensions/feishu/src/tool-account-routing.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, test, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { createToolFactoryHarness } from "./tool-factory-test-harness.js"; @@ -51,8 +51,7 @@ function createConfig(params: { } describe("feishu tool account routing", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ registerFeishuBitableTools, registerFeishuDriveTools, registerFeishuPermTools } = await import("./bitable.js").then(async ({ registerFeishuBitableTools }) => ({ registerFeishuBitableTools, @@ -61,6 +60,9 @@ describe("feishu tool account routing", () => { ...(await import("./wiki.js")), }))); ({ registerFeishuWikiTools } = await import("./wiki.js")); + }); + + beforeEach(() => { vi.clearAllMocks(); }); diff --git a/extensions/firecrawl/src/firecrawl-tools.test.ts b/extensions/firecrawl/src/firecrawl-tools.test.ts index 865509f4bf4..9400bd4e788 100644 --- a/extensions/firecrawl/src/firecrawl-tools.test.ts +++ b/extensions/firecrawl/src/firecrawl-tools.test.ts @@ -37,7 +37,6 @@ describe("firecrawl tools", () => { let firecrawlClientTesting: typeof import("./firecrawl-client.js").__testing; beforeAll(async () => { - vi.resetModules(); ({ fetchFirecrawlContent } = await import("../api.js")); ({ createFirecrawlWebFetchProvider } = await import("./firecrawl-fetch-provider.js")); ({ createFirecrawlWebSearchProvider } = await import("./firecrawl-search-provider.js")); @@ -260,7 +259,6 @@ describe("firecrawl tools", () => { }); it("passes proxy and storeInCache through the fetch provider tool", async () => { - const { createFirecrawlWebFetchProvider } = await import("./firecrawl-fetch-provider.js"); const provider = createFirecrawlWebFetchProvider(); const tool = provider.createTool({ config: { test: true }, diff --git a/extensions/irc/src/probe.test.ts b/extensions/irc/src/probe.test.ts index 4a370b222b7..6ecf7c3f83f 100644 --- a/extensions/irc/src/probe.test.ts +++ b/extensions/irc/src/probe.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const resolveIrcAccountMock = vi.hoisted(() => vi.fn()); const buildIrcConnectOptionsMock = vi.hoisted(() => vi.fn()); @@ -19,12 +19,14 @@ vi.mock("./client.js", () => ({ let probeIrc: typeof import("./probe.js").probeIrc; describe("probeIrc", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ probeIrc } = await import("./probe.js")); + }); + + beforeEach(() => { resolveIrcAccountMock.mockReset(); buildIrcConnectOptionsMock.mockReset(); connectIrcClientMock.mockReset(); - ({ probeIrc } = await import("./probe.js")); }); afterEach(() => { diff --git a/extensions/line/src/bot-handlers.test.ts b/extensions/line/src/bot-handlers.test.ts index 74b0ad83751..df9533b36bf 100644 --- a/extensions/line/src/bot-handlers.test.ts +++ b/extensions/line/src/bot-handlers.test.ts @@ -214,7 +214,6 @@ async function startInflightReplayDuplicate(params: { describe("handleLineWebhookEvents", () => { beforeAll(async () => { - vi.resetModules(); ({ handleLineWebhookEvents, createLineWebhookReplayCache } = await import("./bot-handlers.js")); }); diff --git a/extensions/line/src/download.test.ts b/extensions/line/src/download.test.ts index 5423ff03434..701bce3f996 100644 --- a/extensions/line/src/download.test.ts +++ b/extensions/line/src/download.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const getMessageContentMock = vi.hoisted(() => vi.fn()); @@ -38,13 +38,15 @@ async function* chunks(parts: Buffer[]): AsyncGenerator { } describe("downloadLineMedia", () => { - beforeEach(async () => { - vi.restoreAllMocks(); - vi.resetModules(); - getMessageContentMock.mockReset(); + beforeAll(async () => { ({ downloadLineMedia } = await import("./download.js")); }); + beforeEach(() => { + vi.restoreAllMocks(); + getMessageContentMock.mockReset(); + }); + it("does not derive temp file path from external messageId", async () => { const messageId = "a/../../../../etc/passwd"; const jpeg = Buffer.from([0xff, 0xd8, 0xff, 0x00]); diff --git a/extensions/matrix/src/matrix/draft-stream.test.ts b/extensions/matrix/src/matrix/draft-stream.test.ts index ec5a0ab7039..8771d03935a 100644 --- a/extensions/matrix/src/matrix/draft-stream.test.ts +++ b/extensions/matrix/src/matrix/draft-stream.test.ts @@ -46,7 +46,6 @@ function createMockClient() { } beforeAll(async () => { - vi.resetModules(); const runtimeModule = await import("../runtime.js"); runtimeModule.setMatrixRuntime(runtimeStub); ({ createMatrixDraftStream } = await import("./draft-stream.js")); diff --git a/extensions/mattermost/src/channel.test.ts b/extensions/mattermost/src/channel.test.ts index 9292966183d..5443b04a12c 100644 --- a/extensions/mattermost/src/channel.test.ts +++ b/extensions/mattermost/src/channel.test.ts @@ -1,5 +1,5 @@ import { Type } from "@sinclair/typebox"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../runtime-api.js"; import { createChannelReplyPipeline } from "../runtime-api.js"; @@ -108,10 +108,12 @@ function createMattermostActionContext( } describe("mattermostPlugin", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ mattermostPlugin } = await import("./channel.js")); ({ resetMattermostReactionBotUserCacheForTests } = await import("./mattermost/reactions.js")); + }); + + beforeEach(() => { sendMessageMattermostMock.mockReset(); sendMessageMattermostMock.mockResolvedValue({ messageId: "post-1", diff --git a/extensions/mattermost/src/mattermost/directory.test.ts b/extensions/mattermost/src/mattermost/directory.test.ts index bb7723e22dd..98dd679db46 100644 --- a/extensions/mattermost/src/mattermost/directory.test.ts +++ b/extensions/mattermost/src/mattermost/directory.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const { listMattermostAccountIdsMock, @@ -32,13 +32,15 @@ let listMattermostDirectoryGroups: typeof import("./directory.js").listMattermos let listMattermostDirectoryPeers: typeof import("./directory.js").listMattermostDirectoryPeers; describe("mattermost directory", () => { - beforeEach(async () => { - vi.resetModules(); - vi.clearAllMocks(); + beforeAll(async () => { ({ listMattermostDirectoryGroups, listMattermostDirectoryPeers } = await import("./directory.js")); }); + beforeEach(() => { + vi.clearAllMocks(); + }); + it("deduplicates channels across enabled accounts and skips failing accounts", async () => { const clientA = { token: "token-a", diff --git a/extensions/mattermost/src/mattermost/monitor-auth.test.ts b/extensions/mattermost/src/mattermost/monitor-auth.test.ts index 8dc1e0eb6ea..471d5b42b27 100644 --- a/extensions/mattermost/src/mattermost/monitor-auth.test.ts +++ b/extensions/mattermost/src/mattermost/monitor-auth.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const evaluateSenderGroupAccessForPolicy = vi.hoisted(() => vi.fn()); const isDangerousNameMatchingEnabled = vi.hoisted(() => vi.fn()); @@ -15,8 +15,23 @@ vi.mock("./runtime-api.js", () => ({ })); describe("mattermost monitor auth", () => { + let authorizeMattermostCommandInvocation: typeof import("./monitor-auth.js").authorizeMattermostCommandInvocation; + let isMattermostSenderAllowed: typeof import("./monitor-auth.js").isMattermostSenderAllowed; + let normalizeMattermostAllowEntry: typeof import("./monitor-auth.js").normalizeMattermostAllowEntry; + let normalizeMattermostAllowList: typeof import("./monitor-auth.js").normalizeMattermostAllowList; + let resolveMattermostEffectiveAllowFromLists: typeof import("./monitor-auth.js").resolveMattermostEffectiveAllowFromLists; + + beforeAll(async () => { + ({ + authorizeMattermostCommandInvocation, + isMattermostSenderAllowed, + normalizeMattermostAllowEntry, + normalizeMattermostAllowList, + resolveMattermostEffectiveAllowFromLists, + } = await import("./monitor-auth.js")); + }); + beforeEach(() => { - vi.resetModules(); evaluateSenderGroupAccessForPolicy.mockReset(); isDangerousNameMatchingEnabled.mockReset(); resolveAllowlistMatchSimple.mockReset(); @@ -24,18 +39,12 @@ describe("mattermost monitor auth", () => { resolveEffectiveAllowFromLists.mockReset(); }); - it("normalizes allowlist entries and resolves effective lists", async () => { + it("normalizes allowlist entries and resolves effective lists", () => { resolveEffectiveAllowFromLists.mockReturnValue({ effectiveAllowFrom: ["alice"], effectiveGroupAllowFrom: ["team"], }); - const { - normalizeMattermostAllowEntry, - normalizeMattermostAllowList, - resolveMattermostEffectiveAllowFromLists, - } = await import("./monitor-auth.js"); - expect(normalizeMattermostAllowEntry(" @Alice ")).toBe("alice"); expect(normalizeMattermostAllowEntry("mattermost:Bob")).toBe("bob"); expect(normalizeMattermostAllowEntry("*")).toBe("*"); @@ -62,10 +71,8 @@ describe("mattermost monitor auth", () => { }); }); - it("checks sender allowlists against normalized ids and names", async () => { + it("checks sender allowlists against normalized ids and names", () => { resolveAllowlistMatchSimple.mockReturnValue({ allowed: true }); - - const { isMattermostSenderAllowed } = await import("./monitor-auth.js"); expect( isMattermostSenderAllowed({ senderId: "@Alice", @@ -98,8 +105,6 @@ describe("mattermost monitor auth", () => { }); resolveAllowlistMatchSimple.mockReturnValue({ allowed: false }); - const { authorizeMattermostCommandInvocation } = await import("./monitor-auth.js"); - expect( authorizeMattermostCommandInvocation({ account: { diff --git a/extensions/mattermost/src/mattermost/monitor-resources.test.ts b/extensions/mattermost/src/mattermost/monitor-resources.test.ts index 26034cca516..139e91f3450 100644 --- a/extensions/mattermost/src/mattermost/monitor-resources.test.ts +++ b/extensions/mattermost/src/mattermost/monitor-resources.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const fetchMattermostChannel = vi.hoisted(() => vi.fn()); const fetchMattermostUser = vi.hoisted(() => vi.fn()); @@ -18,8 +18,13 @@ vi.mock("./interactions.js", () => ({ })); describe("mattermost monitor resources", () => { + let createMattermostMonitorResources: typeof import("./monitor-resources.js").createMattermostMonitorResources; + + beforeAll(async () => { + ({ createMattermostMonitorResources } = await import("./monitor-resources.js")); + }); + beforeEach(() => { - vi.resetModules(); fetchMattermostChannel.mockReset(); fetchMattermostUser.mockReset(); sendMattermostTyping.mockReset(); @@ -36,7 +41,6 @@ describe("mattermost monitor resources", () => { path: "/tmp/file.png", contentType: "image/png", })); - const { createMattermostMonitorResources } = await import("./monitor-resources.js"); const resources = createMattermostMonitorResources({ accountId: "default", @@ -78,7 +82,6 @@ describe("mattermost monitor resources", () => { fetchMattermostChannel.mockResolvedValue({ id: "chan-1", name: "town-square" }); fetchMattermostUser.mockResolvedValue({ id: "user-1", username: "alice" }); buildButtonProps.mockReturnValue(undefined); - const { createMattermostMonitorResources } = await import("./monitor-resources.js"); const resources = createMattermostMonitorResources({ accountId: "default", @@ -128,7 +131,6 @@ describe("mattermost monitor resources", () => { }); it("proxies typing indicators to the mattermost client helper", async () => { - const { createMattermostMonitorResources } = await import("./monitor-resources.js"); const client = {} as never; const resources = createMattermostMonitorResources({ diff --git a/extensions/mattermost/src/mattermost/monitor-slash.test.ts b/extensions/mattermost/src/mattermost/monitor-slash.test.ts index 6bf373d44be..de32f16b136 100644 --- a/extensions/mattermost/src/mattermost/monitor-slash.test.ts +++ b/extensions/mattermost/src/mattermost/monitor-slash.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const listSkillCommandsForAgents = vi.hoisted(() => vi.fn()); const parseStrictPositiveInteger = vi.hoisted(() => vi.fn()); @@ -40,8 +40,13 @@ vi.mock("./slash-state.js", () => ({ })); describe("mattermost monitor slash", () => { + let registerMattermostMonitorSlashCommands: typeof import("./monitor-slash.js").registerMattermostMonitorSlashCommands; + + beforeAll(async () => { + ({ registerMattermostMonitorSlashCommands } = await import("./monitor-slash.js")); + }); + beforeEach(() => { - vi.resetModules(); listSkillCommandsForAgents.mockReset(); parseStrictPositiveInteger.mockReset(); fetchMattermostUserTeams.mockReset(); @@ -60,7 +65,6 @@ describe("mattermost monitor slash", () => { it("returns early when slash commands are disabled", async () => { resolveSlashCommandConfig.mockReturnValue({ enabled: false }); isSlashCommandsEnabled.mockReturnValue(false); - const { registerMattermostMonitorSlashCommands } = await import("./monitor-slash.js"); await registerMattermostMonitorSlashCommands({ client: {} as never, @@ -95,8 +99,6 @@ describe("mattermost monitor slash", () => { error: vi.fn(), }; - const { registerMattermostMonitorSlashCommands } = await import("./monitor-slash.js"); - await registerMattermostMonitorSlashCommands({ client: {} as never, cfg: { gateway: { port: 18789 } } as never, @@ -157,8 +159,6 @@ describe("mattermost monitor slash", () => { error: vi.fn(), }; - const { registerMattermostMonitorSlashCommands } = await import("./monitor-slash.js"); - await registerMattermostMonitorSlashCommands({ client: {} as never, cfg: { gateway: { customBindHost: "loopback" } } as never, diff --git a/extensions/mattermost/src/mattermost/target-resolution.test.ts b/extensions/mattermost/src/mattermost/target-resolution.test.ts index cc39e33c3ca..76ce9406d76 100644 --- a/extensions/mattermost/src/mattermost/target-resolution.test.ts +++ b/extensions/mattermost/src/mattermost/target-resolution.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const resolveMattermostAccount = vi.fn(); const createMattermostClient = vi.fn(); @@ -16,23 +16,34 @@ vi.mock("./client.js", () => ({ })); describe("mattermost target resolution", () => { + let isExplicitMattermostTarget: typeof import("./target-resolution.js").isExplicitMattermostTarget; + let isMattermostId: typeof import("./target-resolution.js").isMattermostId; + let parseMattermostApiStatus: typeof import("./target-resolution.js").parseMattermostApiStatus; + let resolveMattermostOpaqueTarget: typeof import("./target-resolution.js").resolveMattermostOpaqueTarget; + let resetMattermostOpaqueTargetCacheForTests: typeof import("./target-resolution.js").resetMattermostOpaqueTargetCacheForTests; + + beforeAll(async () => { + ({ + isExplicitMattermostTarget, + isMattermostId, + parseMattermostApiStatus, + resolveMattermostOpaqueTarget, + resetMattermostOpaqueTargetCacheForTests, + } = await import("./target-resolution.js")); + }); + beforeEach(() => { - vi.resetModules(); resolveMattermostAccount.mockReset(); createMattermostClient.mockReset(); fetchMattermostUser.mockReset(); normalizeMattermostBaseUrl.mockClear(); }); - afterEach(async () => { - const { resetMattermostOpaqueTargetCacheForTests } = await import("./target-resolution.js"); + afterEach(() => { resetMattermostOpaqueTargetCacheForTests(); }); - it("recognizes explicit targets and ID-shaped values", async () => { - const { isExplicitMattermostTarget, isMattermostId, parseMattermostApiStatus } = - await import("./target-resolution.js"); - + it("recognizes explicit targets and ID-shaped values", () => { expect(isExplicitMattermostTarget("@alice")).toBe(true); expect(isExplicitMattermostTarget("#town-square")).toBe(true); expect(isExplicitMattermostTarget("mattermost:chan")).toBe(true); @@ -46,8 +57,6 @@ describe("mattermost target resolution", () => { it("resolves opaque ids as users and caches the result", async () => { createMattermostClient.mockReturnValue({ client: true }); fetchMattermostUser.mockResolvedValue({ id: "abcd1234abcd1234abcd1234ab" }); - - const { resolveMattermostOpaqueTarget } = await import("./target-resolution.js"); const input = "abcd1234abcd1234abcd1234ab"; await expect( @@ -81,8 +90,6 @@ describe("mattermost target resolution", () => { it("falls back to channel targets on 404 lookups", async () => { createMattermostClient.mockReturnValue({ client: true }); fetchMattermostUser.mockRejectedValue(new Error("Mattermost API 404 Not Found")); - - const { resolveMattermostOpaqueTarget } = await import("./target-resolution.js"); const input = "bcde1234abcd1234abcd1234ab"; await expect( @@ -105,8 +112,6 @@ describe("mattermost target resolution", () => { }); createMattermostClient.mockReturnValue({ client: true }); fetchMattermostUser.mockResolvedValue({ id: "cdef1234abcd1234abcd1234ab" }); - - const { resolveMattermostOpaqueTarget } = await import("./target-resolution.js"); const input = "cdef1234abcd1234abcd1234ab"; await resolveMattermostOpaqueTarget({ diff --git a/extensions/microsoft/tts.test.ts b/extensions/microsoft/tts.test.ts index b6dd0db1474..f73c76a507d 100644 --- a/extensions/microsoft/tts.test.ts +++ b/extensions/microsoft/tts.test.ts @@ -1,7 +1,7 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; let edgeTTS: typeof import("./tts.js").edgeTTS; @@ -25,15 +25,7 @@ const baseEdgeConfig = { describe("edgeTTS empty audio validation", () => { let tempDir: string | undefined; - beforeEach(async () => { - vi.resetModules(); - vi.doMock("node-edge-tts", () => ({ - EdgeTTS: class { - ttsPromise(text: string, filePath: string) { - return mockTtsPromise(text, filePath); - } - }, - })); + beforeAll(async () => { ({ edgeTTS } = await import("./tts.js")); }); diff --git a/extensions/msteams/src/setup-surface.test.ts b/extensions/msteams/src/setup-surface.test.ts index 57ba6c388db..26cbeae63a4 100644 --- a/extensions/msteams/src/setup-surface.test.ts +++ b/extensions/msteams/src/setup-surface.test.ts @@ -1,5 +1,5 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { msteamsSetupAdapter } from "./setup-core.js"; const resolveMSTeamsUserAllowlist = vi.hoisted(() => vi.fn()); @@ -31,6 +31,12 @@ vi.mock("../../../src/channels/plugins/bundled.js", () => ({ })); describe("msteams setup surface", () => { + let msteamsSetupWizard: typeof import("./setup-surface.js").msteamsSetupWizard; + + beforeAll(async () => { + ({ msteamsSetupWizard } = await import("./setup-surface.js")); + }); + beforeEach(() => { resolveMSTeamsUserAllowlist.mockReset(); resolveMSTeamsChannelAllowlist.mockReset(); @@ -41,7 +47,6 @@ describe("msteams setup surface", () => { afterEach(() => { vi.unstubAllEnvs(); - vi.resetModules(); }); it("always resolves to the default account", () => { @@ -78,7 +83,6 @@ describe("msteams setup surface", () => { appId: "app", }); hasConfiguredMSTeamsCredentials.mockReturnValue(false); - const { msteamsSetupWizard } = await import("./setup-surface.js"); expect( msteamsSetupWizard.status.resolveConfigured({ @@ -90,7 +94,6 @@ describe("msteams setup surface", () => { it("reports configured status from configured credentials and renders status lines", async () => { resolveMSTeamsCredentials.mockReturnValue(null); hasConfiguredMSTeamsCredentials.mockReturnValue(true); - const { msteamsSetupWizard } = await import("./setup-surface.js"); expect( msteamsSetupWizard.status.resolveConfigured({ @@ -114,7 +117,6 @@ describe("msteams setup surface", () => { resolveMSTeamsCredentials.mockReturnValue(null); hasConfiguredMSTeamsCredentials.mockReturnValue(false); - const { msteamsSetupWizard } = await import("./setup-surface.js"); const result = await msteamsSetupWizard.finalize?.({ cfg: { channels: { msteams: { existing: true } } }, prompter: { @@ -149,7 +151,6 @@ describe("msteams setup surface", () => { throw new Error(`Unexpected prompt: ${message}`); }); - const { msteamsSetupWizard } = await import("./setup-surface.js"); const result = await msteamsSetupWizard.finalize?.({ cfg: { channels: { msteams: {} } }, prompter: { diff --git a/extensions/nextcloud-talk/src/core.test.ts b/extensions/nextcloud-talk/src/core.test.ts index 1602fa5f71d..2ed37164d5f 100644 --- a/extensions/nextcloud-talk/src/core.test.ts +++ b/extensions/nextcloud-talk/src/core.test.ts @@ -1,7 +1,7 @@ import { mkdtemp, rm } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import { escapeNextcloudTalkMarkdown, formatNextcloudTalkCodeBlock, @@ -67,8 +67,7 @@ const tempDirs: string[] = []; let nextcloudTalkPlugin: typeof import("./channel.js").nextcloudTalkPlugin; let NextcloudTalkConfigSchema: typeof import("./config-schema.js").NextcloudTalkConfigSchema; -beforeEach(async () => { - vi.resetModules(); +beforeAll(async () => { ({ nextcloudTalkPlugin } = await import("./channel.js")); ({ NextcloudTalkConfigSchema } = await import("./config-schema.js")); }); diff --git a/extensions/openshell/src/openshell-core.test.ts b/extensions/openshell/src/openshell-core.test.ts index a6c3626f812..aa7a63bc1b9 100644 --- a/extensions/openshell/src/openshell-core.test.ts +++ b/extensions/openshell/src/openshell-core.test.ts @@ -74,7 +74,6 @@ describe("openshell cli helpers", () => { describe("openshell backend manager", () => { beforeAll(async () => { - vi.resetModules(); vi.doMock("./cli.js", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/extensions/searxng/src/searxng-search-provider.test.ts b/extensions/searxng/src/searxng-search-provider.test.ts index 19236e76203..c7b1cc0c8ad 100644 --- a/extensions/searxng/src/searxng-search-provider.test.ts +++ b/extensions/searxng/src/searxng-search-provider.test.ts @@ -18,7 +18,6 @@ describe("searxng web search provider", () => { let plugin: typeof import("../index.js").default; beforeAll(async () => { - vi.resetModules(); ({ createSearxngWebSearchProvider } = await import("./searxng-search-provider.js")); ({ default: plugin } = await import("../index.js")); }); diff --git a/extensions/slack/src/actions.download-file.test.ts b/extensions/slack/src/actions.download-file.test.ts index 15fa5c921d5..0042ab07895 100644 --- a/extensions/slack/src/actions.download-file.test.ts +++ b/extensions/slack/src/actions.download-file.test.ts @@ -69,7 +69,6 @@ function mockSuccessfulMediaDownload(client: ReturnType) { describe("downloadSlackFile", () => { beforeAll(async () => { - vi.resetModules(); ({ downloadSlackFile } = await import("./actions.js")); }); diff --git a/extensions/slack/src/client.test.ts b/extensions/slack/src/client.test.ts index 249c8445616..1f2a0901d37 100644 --- a/extensions/slack/src/client.test.ts +++ b/extensions/slack/src/client.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("@slack/web-api", () => { const WebClient = vi.fn(function WebClientMock( @@ -20,8 +20,7 @@ let SLACK_DEFAULT_RETRY_OPTIONS: typeof import("./client.js").SLACK_DEFAULT_RETR let SLACK_WRITE_RETRY_OPTIONS: typeof import("./client.js").SLACK_WRITE_RETRY_OPTIONS; let WebClient: ReturnType; -beforeEach(async () => { - vi.resetModules(); +beforeAll(async () => { const slackWebApi = await import("@slack/web-api"); ({ createSlackWebClient, @@ -34,6 +33,10 @@ beforeEach(async () => { WebClient = slackWebApi.WebClient as unknown as ReturnType; }); +beforeEach(() => { + WebClient.mockClear(); +}); + describe("slack web client config", () => { it("applies the default retry config when none is provided", () => { const options = resolveSlackWebClientOptions(); diff --git a/extensions/slack/src/monitor.threading.missing-thread-ts.test.ts b/extensions/slack/src/monitor.threading.missing-thread-ts.test.ts index b6d2d4b35ff..50cb7e84e7d 100644 --- a/extensions/slack/src/monitor.threading.missing-thread-ts.test.ts +++ b/extensions/slack/src/monitor.threading.missing-thread-ts.test.ts @@ -1,5 +1,5 @@ import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { flush, getSlackClient, @@ -71,9 +71,11 @@ beforeEach(() => { resetInboundDedupe(); }); -beforeEach(async () => { - vi.resetModules(); +beforeAll(async () => { ({ monitorSlackProvider } = await import("./monitor.js")); +}); + +beforeEach(() => { resetInboundDedupe(); resetSlackTestState({ messages: { responsePrefix: "PFX" }, diff --git a/extensions/slack/src/monitor.tool-result.test.ts b/extensions/slack/src/monitor.tool-result.test.ts index 5d9000ed14b..cb1143e4841 100644 --- a/extensions/slack/src/monitor.tool-result.test.ts +++ b/extensions/slack/src/monitor.tool-result.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { expectPairingReplyText } from "../../../test/helpers/pairing-reply.js"; import { defaultSlackTestConfig, @@ -21,12 +21,14 @@ let monitorSlackProvider: typeof import("./monitor.js").monitorSlackProvider; const slackTestState = getSlackTestState(); const { sendMock, replyMock, reactMock, upsertPairingRequestMock } = slackTestState; -beforeEach(async () => { - vi.resetModules(); +beforeAll(async () => { ({ resetInboundDedupe } = await import("openclaw/plugin-sdk/reply-runtime")); ({ HISTORY_CONTEXT_MARKER } = await import("../../../src/auto-reply/reply/history.js")); ({ CURRENT_MESSAGE_MARKER } = await import("../../../src/auto-reply/reply/mentions.js")); ({ monitorSlackProvider } = await import("./monitor.js")); +}); + +beforeEach(() => { resetInboundDedupe(); resetSlackTestState(defaultSlackTestConfig()); }); diff --git a/extensions/slack/src/monitor/auth.test.ts b/extensions/slack/src/monitor/auth.test.ts index f0b204958bd..bd6d5cbf2bc 100644 --- a/extensions/slack/src/monitor/auth.test.ts +++ b/extensions/slack/src/monitor/auth.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { SlackMonitorContext } from "./context.js"; const readStoreAllowFromForDmPolicyMock = vi.hoisted(() => vi.fn()); @@ -25,10 +25,12 @@ function makeSlackCtx(allowFrom: string[]): SlackMonitorContext { describe("resolveSlackEffectiveAllowFrom", () => { const prevTtl = process.env.OPENCLAW_SLACK_PAIRING_ALLOWFROM_CACHE_TTL_MS; - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ clearSlackAllowFromCacheForTest, resolveSlackEffectiveAllowFrom } = await import("./auth.js")); + }); + + beforeEach(() => { readStoreAllowFromForDmPolicyMock.mockReset(); clearSlackAllowFromCacheForTest(); if (prevTtl === undefined) { diff --git a/extensions/slack/src/monitor/events/channels.test.ts b/extensions/slack/src/monitor/events/channels.test.ts index 3cc59b77145..2f3ddfeea22 100644 --- a/extensions/slack/src/monitor/events/channels.test.ts +++ b/extensions/slack/src/monitor/events/channels.test.ts @@ -38,7 +38,6 @@ function createChannelContext(params?: { describe("registerSlackChannelEvents", () => { beforeAll(async () => { - vi.resetModules(); ({ registerSlackChannelEvents } = await import("./channels.js")); ({ createSlackSystemEventTestHarness } = await import("./system-event-test-harness.js")); }); diff --git a/extensions/slack/src/monitor/events/members.test.ts b/extensions/slack/src/monitor/events/members.test.ts index b6dedc64bdb..4dd59d7b4da 100644 --- a/extensions/slack/src/monitor/events/members.test.ts +++ b/extensions/slack/src/monitor/events/members.test.ts @@ -71,7 +71,6 @@ async function runMemberCase(args: MemberCaseArgs = {}): Promise { describe("registerSlackMemberEvents", () => { beforeAll(async () => { - vi.resetModules(); ({ registerSlackMemberEvents } = await import("./members.js")); ({ createSlackSystemEventTestHarness: initSlackHarness } = await import("./system-event-test-harness.js")); diff --git a/extensions/slack/src/monitor/events/messages.test.ts b/extensions/slack/src/monitor/events/messages.test.ts index a1f858acab7..733bc891169 100644 --- a/extensions/slack/src/monitor/events/messages.test.ts +++ b/extensions/slack/src/monitor/events/messages.test.ts @@ -58,7 +58,6 @@ function resetMessageMocks(): void { } beforeAll(async () => { - vi.resetModules(); ({ registerSlackMessageEvents } = await import("./messages.js")); }); diff --git a/extensions/slack/src/monitor/events/pins.test.ts b/extensions/slack/src/monitor/events/pins.test.ts index 40597c0ab76..63c0907bfee 100644 --- a/extensions/slack/src/monitor/events/pins.test.ts +++ b/extensions/slack/src/monitor/events/pins.test.ts @@ -75,7 +75,6 @@ async function runPinCase(input: PinCase = {}): Promise { describe("registerSlackPinEvents", () => { beforeAll(async () => { - vi.resetModules(); ({ registerSlackPinEvents } = await import("./pins.js")); ({ createSlackSystemEventTestHarness: buildPinHarness } = await import("./system-event-test-harness.js")); diff --git a/extensions/slack/src/monitor/events/reactions.test.ts b/extensions/slack/src/monitor/events/reactions.test.ts index 4ff7a229012..585950de324 100644 --- a/extensions/slack/src/monitor/events/reactions.test.ts +++ b/extensions/slack/src/monitor/events/reactions.test.ts @@ -77,7 +77,6 @@ async function executeReactionCase(input: ReactionRunInput = {}) { describe("registerSlackReactionEvents", () => { beforeAll(async () => { - vi.resetModules(); ({ registerSlackReactionEvents } = await import("./reactions.js")); ({ createSlackSystemEventTestHarness } = await import("./system-event-test-harness.js")); }); diff --git a/extensions/slack/src/monitor/message-handler.app-mention-race.test.ts b/extensions/slack/src/monitor/message-handler.app-mention-race.test.ts index d4305ef0030..a132f2fddc1 100644 --- a/extensions/slack/src/monitor/message-handler.app-mention-race.test.ts +++ b/extensions/slack/src/monitor/message-handler.app-mention-race.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const prepareSlackMessageMock = vi.fn< @@ -117,9 +117,11 @@ async function createInFlightMessageScenario(ts: string) { } describe("createSlackMessageHandler app_mention race handling", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { ({ createSlackMessageHandler } = await import("./message-handler.js")); + }); + + beforeEach(() => { prepareSlackMessageMock.mockReset(); dispatchPreparedSlackMessageMock.mockReset(); }); diff --git a/extensions/slack/src/monitor/message-handler.test.ts b/extensions/slack/src/monitor/message-handler.test.ts index d0587cf6ee2..1c241e8b6bf 100644 --- a/extensions/slack/src/monitor/message-handler.test.ts +++ b/extensions/slack/src/monitor/message-handler.test.ts @@ -71,7 +71,6 @@ async function handleDirectMessage( describe("createSlackMessageHandler", () => { beforeAll(async () => { - vi.resetModules(); ({ createSlackMessageHandler } = await import("./message-handler.js")); }); diff --git a/extensions/slack/src/monitor/message-handler/preview-finalize.test.ts b/extensions/slack/src/monitor/message-handler/preview-finalize.test.ts index 7ffa983b5b3..3a55406ada6 100644 --- a/extensions/slack/src/monitor/message-handler/preview-finalize.test.ts +++ b/extensions/slack/src/monitor/message-handler/preview-finalize.test.ts @@ -1,5 +1,5 @@ import type { WebClient } from "@slack/web-api"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const editSlackMessageMock = vi.fn(); @@ -24,12 +24,14 @@ function createClient(overrides?: { } describe("finalizeSlackPreviewEdit", () => { - beforeEach(async () => { - vi.resetModules(); - editSlackMessageMock.mockReset(); + beforeAll(async () => { ({ finalizeSlackPreviewEdit, __testing } = await import("./preview-finalize.js")); }); + beforeEach(() => { + editSlackMessageMock.mockReset(); + }); + it("treats a thrown edit as success when history readback already matches", async () => { editSlackMessageMock.mockRejectedValueOnce(new Error("socket closed")); const client = createClient({ diff --git a/extensions/slack/src/monitor/replies.test.ts b/extensions/slack/src/monitor/replies.test.ts index d6367b34ac9..0abc56210a5 100644 --- a/extensions/slack/src/monitor/replies.test.ts +++ b/extensions/slack/src/monitor/replies.test.ts @@ -22,7 +22,6 @@ function baseParams(overrides?: Record) { describe("deliverReplies identity passthrough", () => { beforeAll(async () => { - vi.resetModules(); ({ deliverReplies } = await import("./replies.js")); }); diff --git a/extensions/slack/src/outbound-hooks.test.ts b/extensions/slack/src/outbound-hooks.test.ts index 31bcaf9504e..c7e81b7f8da 100644 --- a/extensions/slack/src/outbound-hooks.test.ts +++ b/extensions/slack/src/outbound-hooks.test.ts @@ -1,5 +1,5 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const sendMessageSlackMock = vi.hoisted(() => vi.fn()); const getGlobalHookRunnerMock = vi.hoisted(() => vi.fn()); @@ -73,11 +73,13 @@ const expectSlackSendCalledWith = ( }; describe("slack outbound hook wiring", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ slackOutbound } = await import("./outbound-adapter.js")); + }); + + beforeEach(() => { vi.clearAllMocks(); sendMessageSlackMock.mockResolvedValue({ messageId: "1234.5678", channelId: "C123" }); - ({ slackOutbound } = await import("./outbound-adapter.js")); }); afterEach(() => { diff --git a/extensions/slack/src/probe.test.ts b/extensions/slack/src/probe.test.ts index 9c55b106610..aff577e2849 100644 --- a/extensions/slack/src/probe.test.ts +++ b/extensions/slack/src/probe.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const authTestMock = vi.hoisted(() => vi.fn()); const createSlackWebClientMock = vi.hoisted(() => vi.fn()); @@ -15,8 +15,11 @@ vi.mock("openclaw/plugin-sdk/text-runtime", () => ({ let probeSlack: typeof import("./probe.js").probeSlack; describe("probeSlack", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ probeSlack } = await import("./probe.js")); + }); + + beforeEach(() => { authTestMock.mockReset(); createSlackWebClientMock.mockReset(); withTimeoutMock.mockReset(); @@ -27,7 +30,6 @@ describe("probeSlack", () => { }, }); withTimeoutMock.mockImplementation(async (promise: Promise) => await promise); - ({ probeSlack } = await import("./probe.js")); }); it("maps Slack auth metadata on success", async () => { diff --git a/extensions/synology-chat/src/channel.integration.test.ts b/extensions/synology-chat/src/channel.integration.test.ts index cde4b8ea7c6..4a34fc079d1 100644 --- a/extensions/synology-chat/src/channel.integration.test.ts +++ b/extensions/synology-chat/src/channel.integration.test.ts @@ -1,5 +1,5 @@ import type { IncomingMessage, ServerResponse } from "node:http"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { dispatchReplyWithBufferedBlockDispatcher, finalizeInboundContextMock, @@ -15,11 +15,12 @@ type RegisteredRoute = { }; let createSynologyChatPlugin: typeof import("./channel.js").createSynologyChatPlugin; -const freshChannelModulePath: string = "./channel.js?channel-integration-test"; describe("Synology channel wiring integration", () => { - beforeEach(async () => { - vi.resetModules(); - ({ createSynologyChatPlugin } = await import(freshChannelModulePath)); + beforeAll(async () => { + ({ createSynologyChatPlugin } = await import("./channel.js")); + }); + + beforeEach(() => { registerPluginHttpRouteMock.mockClear(); dispatchReplyWithBufferedBlockDispatcher.mockClear(); finalizeInboundContextMock.mockClear(); diff --git a/extensions/synology-chat/src/client.test.ts b/extensions/synology-chat/src/client.test.ts index fb9ee3085ba..524a5585ea0 100644 --- a/extensions/synology-chat/src/client.test.ts +++ b/extensions/synology-chat/src/client.test.ts @@ -1,5 +1,5 @@ import { EventEmitter } from "node:events"; -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from "vitest"; // Mock http and https modules before importing the client vi.mock("node:https", () => { @@ -54,9 +54,13 @@ function mockFailureResponse(statusCode = 500) { } function installFakeTimerHarness() { + beforeAll(async () => { + ({ sendMessage, sendFileUrl, fetchChatUsers, resolveLegacyWebhookNameToChatUserId } = + await import("./client.js")); + }); + beforeEach(() => { vi.clearAllMocks(); - vi.resetModules(); vi.useFakeTimers(); fakeNowMs += 10_000; vi.setSystemTime(fakeNowMs); @@ -65,11 +69,6 @@ function installFakeTimerHarness() { afterEach(() => { vi.useRealTimers(); }); - - beforeEach(async () => { - ({ sendMessage, sendFileUrl, fetchChatUsers, resolveLegacyWebhookNameToChatUserId } = - await import("./client.js")); - }); } describe("sendMessage", () => { diff --git a/extensions/tavily/src/tavily-client.test.ts b/extensions/tavily/src/tavily-client.test.ts index e4adf464909..810f765f34f 100644 --- a/extensions/tavily/src/tavily-client.test.ts +++ b/extensions/tavily/src/tavily-client.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; // Capture every call to postTrustedWebToolsJson so we can assert on extraHeaders. const postTrustedWebToolsJson = vi.fn(); @@ -29,15 +29,16 @@ describe("tavily client X-Client-Source header", () => { let runTavilySearch: typeof import("./tavily-client.js").runTavilySearch; let runTavilyExtract: typeof import("./tavily-client.js").runTavilyExtract; - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ runTavilySearch, runTavilyExtract } = await import("./tavily-client.js")); + }); + + beforeEach(() => { postTrustedWebToolsJson.mockReset(); postTrustedWebToolsJson.mockImplementation( async (_params: unknown, parse: (r: Response) => Promise) => parse(Response.json({ results: [] })), ); - - ({ runTavilySearch, runTavilyExtract } = await import("./tavily-client.js")); }); it("runTavilySearch sends X-Client-Source: openclaw", async () => { diff --git a/extensions/tavily/src/tavily-tools.test.ts b/extensions/tavily/src/tavily-tools.test.ts index d67c9a3f32f..aaefb4fd2ea 100644 --- a/extensions/tavily/src/tavily-tools.test.ts +++ b/extensions/tavily/src/tavily-tools.test.ts @@ -36,7 +36,6 @@ describe("tavily tools", () => { let tavilyClientTesting: typeof import("./tavily-client.js").__testing; beforeAll(async () => { - vi.resetModules(); ({ createTavilyWebSearchProvider } = await import("./tavily-search-provider.js")); ({ createTavilySearchTool } = await import("./tavily-search-tool.js")); ({ createTavilyExtractTool } = await import("./tavily-extract-tool.js")); diff --git a/extensions/telegram/src/api-fetch.test.ts b/extensions/telegram/src/api-fetch.test.ts index da90404ba9f..a18c33dd13d 100644 --- a/extensions/telegram/src/api-fetch.test.ts +++ b/extensions/telegram/src/api-fetch.test.ts @@ -1,5 +1,5 @@ import { createRequire } from "node:module"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { fetchTelegramChatId } from "./api-fetch.js"; const require = createRequire(import.meta.url); @@ -51,6 +51,12 @@ afterEach(() => { vi.unstubAllEnvs(); }); +vi.mock("undici", () => ({ + ProxyAgent: proxyMocks.ProxyAgent, + fetch: proxyMocks.undiciFetch, + setGlobalDispatcher: proxyMocks.setGlobalDispatcher, +})); + describe("fetchTelegramChatId", () => { const cases = [ { @@ -163,17 +169,14 @@ describe("undici env proxy semantics", () => { }); describe("makeProxyFetch", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ getProxyUrlFromFetch, makeProxyFetch } = await import("./proxy.js")); + }); + + beforeEach(() => { proxyMocks.undiciFetch.mockReset(); proxyMocks.proxyAgentSpy.mockClear(); proxyMocks.setGlobalDispatcher.mockClear(); - vi.doMock("undici", () => ({ - ProxyAgent: proxyMocks.ProxyAgent, - fetch: proxyMocks.undiciFetch, - setGlobalDispatcher: proxyMocks.setGlobalDispatcher, - })); - ({ getProxyUrlFromFetch, makeProxyFetch } = await import("./proxy.js")); }); it("attaches proxy metadata for resolver transport handling", () => { diff --git a/extensions/telegram/src/bot-message-context.acp-bindings.test.ts b/extensions/telegram/src/bot-message-context.acp-bindings.test.ts index 3ffab4d21f6..7e123d2d985 100644 --- a/extensions/telegram/src/bot-message-context.acp-bindings.test.ts +++ b/extensions/telegram/src/bot-message-context.acp-bindings.test.ts @@ -136,7 +136,6 @@ function createConfiguredTelegramRoute() { describe("buildTelegramMessageContext ACP configured bindings", () => { beforeAll(async () => { - vi.resetModules(); ({ buildTelegramMessageContextForTest } = await import("./bot-message-context.test-harness.js")); }); diff --git a/extensions/telegram/src/bot-message.test.ts b/extensions/telegram/src/bot-message.test.ts index f3028fa4e27..0ada6fa833f 100644 --- a/extensions/telegram/src/bot-message.test.ts +++ b/extensions/telegram/src/bot-message.test.ts @@ -19,7 +19,6 @@ let createTelegramMessageProcessor: typeof import("./bot-message.js").createTele describe("telegram bot message processor", () => { beforeAll(async () => { - vi.resetModules(); ({ createTelegramMessageProcessor } = await import("./bot-message.js")); }); diff --git a/extensions/telegram/src/bot-native-commands.registry.test.ts b/extensions/telegram/src/bot-native-commands.registry.test.ts index 96be818325c..837cb5cc8d5 100644 --- a/extensions/telegram/src/bot-native-commands.registry.test.ts +++ b/extensions/telegram/src/bot-native-commands.registry.test.ts @@ -54,7 +54,6 @@ async function registerPairMenu(params: { describe("registerTelegramNativeCommands real plugin registry", () => { beforeAll(async () => { - vi.resetModules(); ({ clearPluginCommands, registerPluginCommand } = await import("../../../src/plugins/commands.js")); ({ registerTelegramNativeCommands } = await import("./bot-native-commands.js")); 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 9bae151b468..7791a1299bb 100644 --- a/extensions/telegram/src/bot-native-commands.session-meta.test.ts +++ b/extensions/telegram/src/bot-native-commands.session-meta.test.ts @@ -387,7 +387,6 @@ function expectUnauthorizedNewCommandBlocked(sendMessage: ReturnType { beforeAll(async () => { - vi.resetModules(); ({ registerTelegramNativeCommands } = await import("./bot-native-commands.js")); }); diff --git a/extensions/telegram/src/bot-native-commands.test.ts b/extensions/telegram/src/bot-native-commands.test.ts index 1ccc35fa13e..182075c5271 100644 --- a/extensions/telegram/src/bot-native-commands.test.ts +++ b/extensions/telegram/src/bot-native-commands.test.ts @@ -22,7 +22,6 @@ import { describe("registerTelegramNativeCommands", () => { beforeAll(async () => { - vi.resetModules(); ({ registerTelegramNativeCommands, parseTelegramNativeCommandCallbackData } = await import("./bot-native-commands.js")); }); diff --git a/extensions/telegram/src/fetch.network-policy.test.ts b/extensions/telegram/src/fetch.network-policy.test.ts index 4721fa88668..4e5f4ff3b9f 100644 --- a/extensions/telegram/src/fetch.network-policy.test.ts +++ b/extensions/telegram/src/fetch.network-policy.test.ts @@ -31,7 +31,6 @@ describe("fetchRemoteMedia telegram network policy", () => { type LookupFn = NonNullable[0]["lookupFn"]>; beforeAll(async () => { - vi.resetModules(); ({ TEST_UNDICI_RUNTIME_DEPS_KEY } = await import("../../../src/infra/net/undici-runtime.js")); ({ fetchRemoteMedia } = await import("../../../src/media/fetch.js")); ({ resolveTelegramTransport, shouldRetryTelegramTransportFallback } = diff --git a/extensions/telegram/src/fetch.test.ts b/extensions/telegram/src/fetch.test.ts index 0f932c33136..0fb8d1e70ed 100644 --- a/extensions/telegram/src/fetch.test.ts +++ b/extensions/telegram/src/fetch.test.ts @@ -80,7 +80,6 @@ let resolveTelegramFetch: typeof import("./fetch.js").resolveTelegramFetch; let resolveTelegramTransport: typeof import("./fetch.js").resolveTelegramTransport; beforeAll(async () => { - vi.resetModules(); ({ resolveFetch } = await import("../../../src/infra/fetch.js")); ({ resolveTelegramFetch, resolveTelegramTransport } = await import("./fetch.js")); }); diff --git a/extensions/telegram/src/polling-session.test.ts b/extensions/telegram/src/polling-session.test.ts index e873a409f2f..cf6f3f22ada 100644 --- a/extensions/telegram/src/polling-session.test.ts +++ b/extensions/telegram/src/polling-session.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const runMock = vi.hoisted(() => vi.fn()); const createTelegramBotMock = vi.hoisted(() => vi.fn()); @@ -135,14 +135,16 @@ function createPollingSessionWithTransportRestart(params: { } describe("TelegramPollingSession", () => { - beforeEach(async () => { - vi.resetModules(); + beforeAll(async () => { + ({ TelegramPollingSession } = await import("./polling-session.js")); + }); + + beforeEach(() => { runMock.mockReset(); createTelegramBotMock.mockReset(); isRecoverableTelegramNetworkErrorMock.mockReset().mockReturnValue(true); computeBackoffMock.mockReset().mockReturnValue(0); sleepWithAbortMock.mockReset().mockResolvedValue(undefined); - ({ TelegramPollingSession } = await import("./polling-session.js")); }); it("uses backoff helpers for recoverable polling retries", async () => { diff --git a/extensions/telegram/src/send.proxy.test.ts b/extensions/telegram/src/send.proxy.test.ts index 5e29a0a21d0..afba01cedf5 100644 --- a/extensions/telegram/src/send.proxy.test.ts +++ b/extensions/telegram/src/send.proxy.test.ts @@ -90,7 +90,6 @@ describe("telegram proxy client", () => { }; beforeAll(async () => { - vi.resetModules(); ({ deleteMessageTelegram, reactMessageTelegram, diff --git a/extensions/telegram/src/webhook.test.ts b/extensions/telegram/src/webhook.test.ts index abd7bcbcabf..84495d80732 100644 --- a/extensions/telegram/src/webhook.test.ts +++ b/extensions/telegram/src/webhook.test.ts @@ -121,7 +121,6 @@ function resetTelegramWebhookMocks(): void { } beforeAll(async () => { - vi.resetModules(); ({ startTelegramWebhook } = await import("./webhook.js")); }); diff --git a/extensions/tlon/src/monitor/approval.test.ts b/extensions/tlon/src/monitor/approval.test.ts index 69599789d75..35522b87c80 100644 --- a/extensions/tlon/src/monitor/approval.test.ts +++ b/extensions/tlon/src/monitor/approval.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const cryptoMocks = vi.hoisted(() => ({ randomBytes: vi.fn(), @@ -10,12 +10,14 @@ vi.mock("node:crypto", () => ({ let generateApprovalId: typeof import("./approval.js").generateApprovalId; -beforeEach(async () => { - vi.resetModules(); - cryptoMocks.randomBytes.mockReset(); +beforeAll(async () => { ({ generateApprovalId } = await import("./approval.js")); }); +beforeEach(() => { + cryptoMocks.randomBytes.mockReset(); +}); + describe("generateApprovalId", () => { it("uses secure hex entropy while preserving the ID format", () => { cryptoMocks.randomBytes.mockReturnValueOnce(Buffer.from("a1b2c3", "hex")); diff --git a/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts b/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts index cd2b4e5d0c3..061669e6bd2 100644 --- a/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts +++ b/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts @@ -86,7 +86,6 @@ async function expectReplySuppressed(replyResult: { text: string; isReasoning?: describe("deliverWebReply", () => { beforeAll(async () => { - vi.resetModules(); ({ deliverWebReply } = await import("./deliver-reply.js")); }); diff --git a/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts b/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts index 583dca1ba1e..e75abfabb39 100644 --- a/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts +++ b/extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts @@ -162,7 +162,6 @@ describe("runWebHeartbeatOnce", () => { }); beforeAll(async () => { - vi.resetModules(); ({ runWebHeartbeatOnce } = await import("./heartbeat-runner.js")); }); diff --git a/extensions/whatsapp/src/inbound/access-control.test.ts b/extensions/whatsapp/src/inbound/access-control.test.ts index 80313fdef05..637283b546d 100644 --- a/extensions/whatsapp/src/inbound/access-control.test.ts +++ b/extensions/whatsapp/src/inbound/access-control.test.ts @@ -11,7 +11,6 @@ setupAccessControlTestHarness(); let checkInboundAccessControl: typeof import("./access-control.js").checkInboundAccessControl; beforeAll(async () => { - vi.resetModules(); ({ checkInboundAccessControl } = await import("./access-control.js")); }); diff --git a/extensions/whatsapp/src/login.test.ts b/extensions/whatsapp/src/login.test.ts index 652ce20cf86..df063e8adda 100644 --- a/extensions/whatsapp/src/login.test.ts +++ b/extensions/whatsapp/src/login.test.ts @@ -2,7 +2,7 @@ import { EventEmitter } from "node:events"; import { readFile } from "node:fs/promises"; import { resolve } from "node:path"; import { resetLogger, setLoggerOverride } from "openclaw/plugin-sdk/runtime-env"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { renderQrPngBase64 } from "./qr-image.js"; vi.mock("./session.js", async () => { @@ -26,14 +26,16 @@ let loginWeb: typeof import("./login.js").loginWeb; let createWaSocket: typeof import("./session.js").createWaSocket; describe("web login", () => { - beforeEach(async () => { - vi.resetModules(); - vi.useFakeTimers(); - vi.clearAllMocks(); + beforeAll(async () => { ({ loginWeb } = await import("./login.js")); ({ createWaSocket } = await import("./session.js")); }); + beforeEach(() => { + vi.useFakeTimers(); + vi.clearAllMocks(); + }); + afterEach(() => { vi.useRealTimers(); resetLogger(); diff --git a/extensions/whatsapp/src/resolve-target.test.ts b/extensions/whatsapp/src/resolve-target.test.ts index 9f315d6b82b..4d359e9eaff 100644 --- a/extensions/whatsapp/src/resolve-target.test.ts +++ b/extensions/whatsapp/src/resolve-target.test.ts @@ -77,7 +77,6 @@ let resolveTarget: NonNullable< describe("whatsapp resolveTarget", () => { beforeAll(async () => { - vi.resetModules(); const outbound = (await import("./channel.js")).whatsappPlugin.outbound; if (!outbound?.resolveTarget) { throw new Error("expected whatsapp outbound resolveTarget"); diff --git a/extensions/whatsapp/src/send.test.ts b/extensions/whatsapp/src/send.test.ts index a6f2fe8566b..3db1629fbe1 100644 --- a/extensions/whatsapp/src/send.test.ts +++ b/extensions/whatsapp/src/send.test.ts @@ -32,7 +32,6 @@ describe("web outbound", () => { const sendReaction = vi.fn(async () => {}); beforeAll(async () => { - vi.resetModules(); ({ sendMessageWhatsApp, sendPollWhatsApp, sendReactionWhatsApp } = await import("./send.js")); ({ setActiveWebListener } = await import("./active-listener.js")); ({ resetLogger, setLoggerOverride } = await import("openclaw/plugin-sdk/runtime-env")); diff --git a/extensions/zalouser/src/channel.sendpayload.test.ts b/extensions/zalouser/src/channel.sendpayload.test.ts index ae672194f3b..d39bc5b547d 100644 --- a/extensions/zalouser/src/channel.sendpayload.test.ts +++ b/extensions/zalouser/src/channel.sendpayload.test.ts @@ -5,6 +5,7 @@ import "./zalo-js.test-mocks.js"; import type { ReplyPayload } from "../runtime-api.js"; import { zalouserPlugin } from "./channel.js"; import { setZalouserRuntime } from "./runtime.js"; +import * as sendModule from "./send.js"; vi.mock("./send.js", () => ({ sendMessageZalouser: vi.fn().mockResolvedValue({ ok: true, messageId: "zlu-1" }), @@ -23,7 +24,7 @@ function baseCtx(payload: ReplyPayload) { describe("zalouserPlugin outbound sendPayload", () => { let mockedSend: ReturnType>; - beforeEach(async () => { + beforeEach(() => { setZalouserRuntime({ channel: { text: { @@ -32,8 +33,7 @@ describe("zalouserPlugin outbound sendPayload", () => { }, }, } as never); - const mod = await import("./send.js"); - mockedSend = vi.mocked(mod.sendMessageZalouser); + mockedSend = vi.mocked(sendModule.sendMessageZalouser); primeChannelOutboundSendMock(mockedSend, { ok: true, messageId: "zlu-1" }); }); diff --git a/extensions/zalouser/src/zca-client.test.ts b/extensions/zalouser/src/zca-client.test.ts index a1fc7e610e1..992c0520f77 100644 --- a/extensions/zalouser/src/zca-client.test.ts +++ b/extensions/zalouser/src/zca-client.test.ts @@ -1,12 +1,8 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; describe("zca-client runtime loading", () => { - beforeEach(() => { - vi.resetModules(); - vi.clearAllMocks(); - }); - it("does not import zca-js until a session is created", async () => { + vi.clearAllMocks(); const runtimeFactory = vi.fn(() => ({ Zalo: class MockZalo { constructor(public readonly options?: { logging?: boolean; selfListen?: boolean }) {} diff --git a/packages/memory-host-sdk/src/host/batch-gemini.test.ts b/packages/memory-host-sdk/src/host/batch-gemini.test.ts index cd8154b1057..095ebe008b9 100644 --- a/packages/memory-host-sdk/src/host/batch-gemini.test.ts +++ b/packages/memory-host-sdk/src/host/batch-gemini.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { GeminiEmbeddingClient } from "./embeddings-gemini.js"; vi.mock("./remote-http.js", () => ({ @@ -14,14 +14,16 @@ describe("runGeminiEmbeddingBatches", () => { let withRemoteHttpResponse: typeof import("./remote-http.js").withRemoteHttpResponse; let remoteHttpMock: ReturnType>; - beforeEach(async () => { - vi.resetModules(); - vi.clearAllMocks(); + beforeAll(async () => { ({ runGeminiEmbeddingBatches } = await import("./batch-gemini.js")); ({ withRemoteHttpResponse } = await import("./remote-http.js")); remoteHttpMock = vi.mocked(withRemoteHttpResponse); }); + beforeEach(() => { + vi.clearAllMocks(); + }); + afterEach(() => { vi.resetAllMocks(); vi.unstubAllGlobals(); diff --git a/packages/memory-host-sdk/src/host/batch-http.test.ts b/packages/memory-host-sdk/src/host/batch-http.test.ts index c2706048513..70ba31bd137 100644 --- a/packages/memory-host-sdk/src/host/batch-http.test.ts +++ b/packages/memory-host-sdk/src/host/batch-http.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("../../../../src/infra/retry.js", () => ({ retryAsync: vi.fn(async (run: () => Promise) => await run()), @@ -15,10 +15,7 @@ describe("postJsonWithRetry", () => { let postJsonMock: ReturnType>; let postJsonWithRetry: typeof import("./batch-http.js").postJsonWithRetry; - beforeEach(async () => { - vi.resetModules(); - vi.clearAllMocks(); - vi.resetModules(); + beforeAll(async () => { ({ postJsonWithRetry } = await import("./batch-http.js")); const retryModule = await import("../../../../src/infra/retry.js"); const postJsonModule = await import("./post-json.js"); @@ -26,6 +23,10 @@ describe("postJsonWithRetry", () => { postJsonMock = vi.mocked(postJsonModule.postJson); }); + beforeEach(() => { + vi.clearAllMocks(); + }); + it("posts JSON and returns parsed response payload", async () => { postJsonMock.mockImplementationOnce(async (params) => { return await params.parse({ ok: true, ids: [1, 2] }); diff --git a/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts b/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts index 9169ac6146b..472fe13242c 100644 --- a/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts @@ -71,7 +71,6 @@ let resolveGeminiOutputDimensionality: typeof import("./embeddings-gemini.js").r beforeAll(async () => { vi.doUnmock("undici"); - vi.resetModules(); ({ buildGeminiEmbeddingRequest, buildGeminiTextEmbeddingRequest, diff --git a/packages/memory-host-sdk/src/host/embeddings-remote-fetch.test.ts b/packages/memory-host-sdk/src/host/embeddings-remote-fetch.test.ts index 78fa48b2311..3ddddc708f5 100644 --- a/packages/memory-host-sdk/src/host/embeddings-remote-fetch.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings-remote-fetch.test.ts @@ -1,18 +1,21 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const postJsonMock = vi.hoisted(() => vi.fn()); +vi.mock("./post-json.js", () => ({ + postJson: postJsonMock, +})); + type EmbeddingsRemoteFetchModule = typeof import("./embeddings-remote-fetch.js"); let fetchRemoteEmbeddingVectors: EmbeddingsRemoteFetchModule["fetchRemoteEmbeddingVectors"]; describe("fetchRemoteEmbeddingVectors", () => { - beforeEach(async () => { - vi.resetModules(); - vi.doMock("./post-json.js", () => ({ - postJson: postJsonMock, - })); + beforeAll(async () => { ({ fetchRemoteEmbeddingVectors } = await import("./embeddings-remote-fetch.js")); + }); + + beforeEach(() => { postJsonMock.mockReset(); }); diff --git a/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts b/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts index 90effba8651..e3d7ef2782a 100644 --- a/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts @@ -1,4 +1,5 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import * as authModule from "../../../../src/agents/model-auth.js"; import { type FetchMock, withFetchPreconnect } from "../../../../src/test-utils/fetch-mock.js"; import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js"; @@ -42,19 +43,19 @@ function installFetchMock(fetchMock: typeof globalThis.fetch) { vi.stubGlobal("fetch", fetchMock); } -let authModule: typeof import("../../../../src/agents/model-auth.js"); let createVoyageEmbeddingProvider: typeof import("./embeddings-voyage.js").createVoyageEmbeddingProvider; let normalizeVoyageModel: typeof import("./embeddings-voyage.js").normalizeVoyageModel; -beforeEach(async () => { - vi.useRealTimers(); - vi.doUnmock("undici"); - vi.resetModules(); - authModule = await import("../../../../src/agents/model-auth.js"); +beforeAll(async () => { ({ createVoyageEmbeddingProvider, normalizeVoyageModel } = await import("./embeddings-voyage.js")); }); +beforeEach(() => { + vi.useRealTimers(); + vi.doUnmock("undici"); +}); + function mockVoyageApiKey() { vi.mocked(authModule.resolveApiKeyForProvider).mockResolvedValue({ apiKey: "voyage-key-123", diff --git a/packages/memory-host-sdk/src/host/embeddings.test.ts b/packages/memory-host-sdk/src/host/embeddings.test.ts index 56512919dbe..a185e0f6627 100644 --- a/packages/memory-host-sdk/src/host/embeddings.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings.test.ts @@ -1,5 +1,5 @@ import { setTimeout as sleep } from "node:timers/promises"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_GEMINI_EMBEDDING_MODEL } from "./embeddings-gemini.js"; import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js"; @@ -54,7 +54,7 @@ let nodeLlamaModule: typeof import("./node-llama.js"); let createEmbeddingProvider: EmbeddingsModule["createEmbeddingProvider"]; let DEFAULT_LOCAL_MODEL: EmbeddingsModule["DEFAULT_LOCAL_MODEL"]; -beforeEach(async () => { +beforeAll(async () => { vi.resetModules(); authModule = await import("../../../../src/agents/model-auth.js"); nodeLlamaModule = await import("./node-llama.js"); @@ -63,6 +63,10 @@ beforeEach(async () => { ({ createEmbeddingProvider, DEFAULT_LOCAL_MODEL } = await import("./embeddings.js")); }); +beforeEach(() => { + vi.useRealTimers(); +}); + afterEach(() => { vi.resetAllMocks(); vi.unstubAllGlobals(); @@ -575,11 +579,6 @@ describe("local embedding ensureContext concurrency", () => { vi.doUnmock("./node-llama.js"); }); - afterEach(() => { - vi.resetModules(); - vi.doUnmock("./node-llama.js"); - }); - async function setupLocalProviderWithMockedInit(params?: { initializationDelayMs?: number; failFirstGetLlama?: boolean; @@ -708,6 +707,7 @@ describe("FTS-only fallback when no provider available", () => { beforeEach(async () => { authModule = await import("../../../../src/agents/model-auth.js"); ({ createEmbeddingProvider, DEFAULT_LOCAL_MODEL } = await import("./embeddings.js")); + vi.spyOn(authModule, "resolveApiKeyForProvider"); }); it("returns null provider when all requested auth paths fail", async () => { diff --git a/packages/memory-host-sdk/src/host/post-json.test.ts b/packages/memory-host-sdk/src/host/post-json.test.ts index ebfe858fa4d..d09fa694782 100644 --- a/packages/memory-host-sdk/src/host/post-json.test.ts +++ b/packages/memory-host-sdk/src/host/post-json.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("./remote-http.js", () => ({ withRemoteHttpResponse: vi.fn(), @@ -10,15 +10,16 @@ let withRemoteHttpResponse: typeof import("./remote-http.js").withRemoteHttpResp describe("postJson", () => { let remoteHttpMock: ReturnType>; - beforeEach(async () => { - vi.resetModules(); - vi.clearAllMocks(); - vi.resetModules(); + beforeAll(async () => { ({ postJson } = await import("./post-json.js")); ({ withRemoteHttpResponse } = await import("./remote-http.js")); remoteHttpMock = vi.mocked(withRemoteHttpResponse); }); + beforeEach(() => { + vi.clearAllMocks(); + }); + it("parses JSON payload on successful response", async () => { remoteHttpMock.mockImplementationOnce(async (params) => { return await params.onResponse(