test: reduce import wrapper reload churn

This commit is contained in:
Peter Steinberger 2026-04-03 07:14:31 +01:00
parent 9bba2ec0ad
commit b66197f932
No known key found for this signature in database
9 changed files with 57 additions and 31 deletions

View File

@ -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<typeof import("./stage-sandbox-media.runtime.js")>();
});
await import("./get-reply.js");
await importFreshModule<typeof import("./get-reply.js")>(
import.meta.url,
"./get-reply.js?scope=no-runtime-imports",
);
expect(resetModelRuntimeLoads).not.toHaveBeenCalled();
expect(sandboxMediaRuntimeLoads).not.toHaveBeenCalled();

View File

@ -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<typeof import("../../gateway/session-archive.runtime.js")>();
});
await import("./session.js");
await importFreshModule<typeof import("./session.js")>(
import.meta.url,
"./session.js?scope=no-archive-runtime-on-import",
);
expect(archiveRuntimeLoads).not.toHaveBeenCalled();
vi.doUnmock("../../gateway/session-archive.runtime.js");

View File

@ -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<typeof import("./bundled.js")>(
import.meta.url,
"./bundled.js?scope=missing-bundled-discovery",
);
expect(bundled.listBundledChannelPlugins()).toEqual([]);
expect(bundled.listBundledChannelSetupPlugins()).toEqual([]);

View File

@ -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<typeof import("./deps.js")>(
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<unknown>;
@ -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<typeof import("./deps.js")>(
import.meta.url,
"./deps.js?scope=no-whatsapp-runtime-on-import",
);
expect(whatsappBoundaryLoads).not.toHaveBeenCalled();
});

View File

@ -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<typeof import("./bundled-channel-config-runtime.js")>(
import.meta.url,
"./bundled-channel-config-runtime.js?scope=tdz-reference-error",
);
const configSchemaMap = runtime.getBundledChannelConfigSchemaMap();
expect(configSchemaMap.has("msteams")).toBe(true);

View File

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

View File

@ -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<typeof import("../gateway/session-archive.runtime.js")>();
});
await import("./sessions/store.js");
await importFreshModule<typeof import("./sessions/store.js")>(
import.meta.url,
"./sessions/store.js?scope=no-archive-runtime-on-import",
);
expect(archiveRuntimeLoads).not.toHaveBeenCalled();
vi.doUnmock("../gateway/session-archive.runtime.js");

View File

@ -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();

View File

@ -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<typeof import("./mime.js")>("./mime.js");
return {
@ -547,7 +547,10 @@ describe("media store", () => {
});
try {
const storeWithMock = await import("./store.js");
const storeWithMock = await importFreshModule<typeof import("./store.js")>(
import.meta.url,
"./store.js?scope=sniffed-mime-header-extension",
);
const saved = await storeWithMock.saveMediaBuffer(
Buffer.from("fake-audio"),
"audio/ogg; codecs=opus",