diff --git a/src/auto-reply/reply/get-reply.imports.test.ts b/src/auto-reply/reply/get-reply.imports.test.ts index 1e3a362e6f8..ffe9271b5eb 100644 --- a/src/auto-reply/reply/get-reply.imports.test.ts +++ b/src/auto-reply/reply/get-reply.imports.test.ts @@ -1,10 +1,7 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../../test/helpers/import-fresh.ts"; describe("get-reply module imports", () => { - beforeEach(() => { - vi.resetModules(); - }); - it("does not load reset-model runtime on module import", async () => { const resetModelRuntimeLoads = vi.fn(); const sandboxMediaRuntimeLoads = vi.fn(); @@ -17,7 +14,10 @@ describe("get-reply module imports", () => { return await importOriginal(); }); - await import("./get-reply.js"); + await importFreshModule( + import.meta.url, + "./get-reply.js?scope=no-runtime-imports", + ); expect(resetModelRuntimeLoads).not.toHaveBeenCalled(); expect(sandboxMediaRuntimeLoads).not.toHaveBeenCalled(); diff --git a/src/auto-reply/reply/session.imports.test.ts b/src/auto-reply/reply/session.imports.test.ts index e29d7f47a63..9faddb31cc8 100644 --- a/src/auto-reply/reply/session.imports.test.ts +++ b/src/auto-reply/reply/session.imports.test.ts @@ -1,10 +1,7 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../../test/helpers/import-fresh.ts"; describe("reply session module imports", () => { - beforeEach(() => { - vi.resetModules(); - }); - it("does not load archive runtime on module import", async () => { const archiveRuntimeLoads = vi.fn(); vi.doMock("../../gateway/session-archive.runtime.js", async (importOriginal) => { @@ -12,7 +9,10 @@ describe("reply session module imports", () => { return await importOriginal(); }); - await import("./session.js"); + await importFreshModule( + import.meta.url, + "./session.js?scope=no-archive-runtime-on-import", + ); expect(archiveRuntimeLoads).not.toHaveBeenCalled(); vi.doUnmock("../../gateway/session-archive.runtime.js"); diff --git a/src/channels/plugins/bundled.shape-guard.test.ts b/src/channels/plugins/bundled.shape-guard.test.ts index fafd3731508..d36f91a070f 100644 --- a/src/channels/plugins/bundled.shape-guard.test.ts +++ b/src/channels/plugins/bundled.shape-guard.test.ts @@ -1,4 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../../test/helpers/import-fresh.ts"; afterEach(() => { vi.doUnmock("../../plugins/discovery.js"); @@ -7,7 +8,6 @@ afterEach(() => { describe("bundled channel entry shape guards", () => { it("treats missing bundled discovery results as empty", async () => { - vi.resetModules(); vi.doMock("../../plugins/discovery.js", () => ({ discoverOpenClawPlugins: () => ({ candidates: [], @@ -21,7 +21,10 @@ describe("bundled channel entry shape guards", () => { }), })); - const bundled = await import("./bundled.js"); + const bundled = await importFreshModule( + import.meta.url, + "./bundled.js?scope=missing-bundled-discovery", + ); expect(bundled.listBundledChannelPlugins()).toEqual([]); expect(bundled.listBundledChannelSetupPlugins()).toEqual([]); diff --git a/src/cli/deps.test.ts b/src/cli/deps.test.ts index 66ed9353950..ea11c13e5f1 100644 --- a/src/cli/deps.test.ts +++ b/src/cli/deps.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../test/helpers/import-fresh.ts"; const moduleLoads = vi.hoisted(() => ({ whatsapp: vi.fn(), @@ -56,8 +57,13 @@ vi.mock("./send-runtime/imessage.js", () => { }); describe("createDefaultDeps", () => { - async function loadCreateDefaultDeps() { - return (await import("./deps.js")).createDefaultDeps; + async function loadCreateDefaultDeps(scope: string) { + return ( + await importFreshModule( + import.meta.url, + `./deps.js?scope=${scope}`, + ) + ).createDefaultDeps; } function expectUnusedModulesNotLoaded(exclude: keyof typeof moduleLoads): void { @@ -72,11 +78,10 @@ describe("createDefaultDeps", () => { beforeEach(() => { vi.clearAllMocks(); - vi.resetModules(); }); it("does not load provider modules until a dependency is used", async () => { - const createDefaultDeps = await loadCreateDefaultDeps(); + const createDefaultDeps = await loadCreateDefaultDeps("lazy-load"); const deps = createDefaultDeps(); expect(moduleLoads.whatsapp).not.toHaveBeenCalled(); @@ -95,7 +100,7 @@ describe("createDefaultDeps", () => { }); it("reuses module cache after first dynamic import", async () => { - const createDefaultDeps = await loadCreateDefaultDeps(); + const createDefaultDeps = await loadCreateDefaultDeps("module-cache"); const deps = createDefaultDeps(); const sendDiscord = deps["discord"] as (...args: unknown[]) => Promise; @@ -107,7 +112,10 @@ describe("createDefaultDeps", () => { }); it("does not import the whatsapp runtime boundary on deps module load", async () => { - await import("./deps.js"); + await importFreshModule( + import.meta.url, + "./deps.js?scope=no-whatsapp-runtime-on-import", + ); expect(whatsappBoundaryLoads).not.toHaveBeenCalled(); }); diff --git a/src/config/bundled-channel-config-runtime.test.ts b/src/config/bundled-channel-config-runtime.test.ts index 9a7966917ca..e67e62cee59 100644 --- a/src/config/bundled-channel-config-runtime.test.ts +++ b/src/config/bundled-channel-config-runtime.test.ts @@ -1,8 +1,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../test/helpers/import-fresh.ts"; describe("bundled channel config runtime", () => { beforeEach(() => { - vi.resetModules(); vi.doUnmock("../channels/plugins/bundled.js"); }); @@ -11,7 +11,9 @@ describe("bundled channel config runtime", () => { listBundledChannelPlugins: () => undefined, })); - const runtimeModule = await import("./bundled-channel-config-runtime.js"); + const runtimeModule = await importFreshModule< + typeof import("./bundled-channel-config-runtime.js") + >(import.meta.url, "./bundled-channel-config-runtime.js?scope=missing-bundled-list"); expect(runtimeModule.getBundledChannelConfigSchemaMap().get("msteams")).toBeDefined(); expect(runtimeModule.getBundledChannelRuntimeMap().get("msteams")).toBeDefined(); @@ -26,7 +28,10 @@ describe("bundled channel config runtime", () => { }; }); - const runtime = await import("./bundled-channel-config-runtime.js"); + const runtime = await importFreshModule( + import.meta.url, + "./bundled-channel-config-runtime.js?scope=tdz-reference-error", + ); const configSchemaMap = runtime.getBundledChannelConfigSchemaMap(); expect(configSchemaMap.has("msteams")).toBe(true); diff --git a/src/config/load-channel-config-surface.test.ts b/src/config/load-channel-config-surface.test.ts index 20ea8e95ed8..b61d0825d9e 100644 --- a/src/config/load-channel-config-surface.test.ts +++ b/src/config/load-channel-config-surface.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { loadChannelConfigSurfaceModule } from "../../scripts/load-channel-config-surface.ts"; +import { importFreshModule } from "../../test/helpers/import-fresh.ts"; const tempDirs: string[] = []; @@ -13,7 +14,6 @@ function makeTempRoot(prefix: string): string { } async function importLoaderWithMissingBun() { - vi.resetModules(); const spawnSync = vi.fn(() => ({ error: Object.assign(new Error("bun not found"), { code: "ENOENT" }), status: null, @@ -23,11 +23,12 @@ async function importLoaderWithMissingBun() { vi.doMock("node:child_process", () => ({ spawnSync })); try { - const imported = await import("../../scripts/load-channel-config-surface.ts"); + const imported = await importFreshModule< + typeof import("../../scripts/load-channel-config-surface.ts") + >(import.meta.url, "../../scripts/load-channel-config-surface.ts?scope=missing-bun"); return { loadChannelConfigSurfaceModule: imported.loadChannelConfigSurfaceModule, spawnSync }; } finally { vi.doUnmock("node:child_process"); - vi.resetModules(); } } diff --git a/src/config/sessions.store.imports.test.ts b/src/config/sessions.store.imports.test.ts index f0c15e5a803..63c7bfcbe7b 100644 --- a/src/config/sessions.store.imports.test.ts +++ b/src/config/sessions.store.imports.test.ts @@ -1,15 +1,18 @@ import { describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../test/helpers/import-fresh.ts"; describe("session store module imports", () => { it("does not load archive runtime on module import", async () => { - vi.resetModules(); const archiveRuntimeLoads = vi.fn(); vi.doMock("../gateway/session-archive.runtime.js", async (importOriginal) => { archiveRuntimeLoads(); return await importOriginal(); }); - await import("./sessions/store.js"); + await importFreshModule( + import.meta.url, + "./sessions/store.js?scope=no-archive-runtime-on-import", + ); expect(archiveRuntimeLoads).not.toHaveBeenCalled(); vi.doUnmock("../gateway/session-archive.runtime.js"); diff --git a/src/infra/gateway-lock.test.ts b/src/infra/gateway-lock.test.ts index e3709a86509..b12e5ce16cf 100644 --- a/src/infra/gateway-lock.test.ts +++ b/src/infra/gateway-lock.test.ts @@ -177,7 +177,10 @@ describe("gateway lock", () => { const lock = await acquireForTest(env, { timeoutMs: 50 }); expect(lock).not.toBeNull(); - const pending = acquireForTest(env, { timeoutMs: 15 }); + const pending = acquireForTest(env, { + timeoutMs: 15, + readProcessCmdline: () => ["openclaw", "gateway", "run"], + }); await expect(pending).rejects.toBeInstanceOf(GatewayLockError); await lock?.release(); diff --git a/src/media/store.test.ts b/src/media/store.test.ts index ed9c33198a5..e58030e7721 100644 --- a/src/media/store.test.ts +++ b/src/media/store.test.ts @@ -3,6 +3,7 @@ import path from "node:path"; import JSZip from "jszip"; import sharp from "sharp"; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; +import { importFreshModule } from "../../test/helpers/import-fresh.ts"; import { isPathWithinBase } from "../../test/helpers/paths.js"; import { createTempHomeEnv, type TempHomeEnv } from "../test-utils/temp-home.js"; @@ -537,7 +538,6 @@ describe("media store", () => { it("prefers header mime extension when sniffed mime lacks mapping", async () => { await withTempStore(async (_store, home) => { - vi.resetModules(); vi.doMock("./mime.js", async () => { const actual = await vi.importActual("./mime.js"); return { @@ -547,7 +547,10 @@ describe("media store", () => { }); try { - const storeWithMock = await import("./store.js"); + const storeWithMock = await importFreshModule( + import.meta.url, + "./store.js?scope=sniffed-mime-header-extension", + ); const saved = await storeWithMock.saveMediaBuffer( Buffer.from("fake-audio"), "audio/ogg; codecs=opus",