mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-25 23:47:20 +00:00
test: harden vitest no-isolate coverage
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { setTimeout as nativeSetTimeout } from "node:timers";
|
||||
import { vi } from "vitest";
|
||||
|
||||
export function useFastShortTimeouts(maxDelayMs = 2000): () => void {
|
||||
const realSetTimeout = setTimeout;
|
||||
const spy = vi.spyOn(global, "setTimeout").mockImplementation(((
|
||||
handler: TimerHandler,
|
||||
timeout?: number,
|
||||
@@ -9,9 +9,9 @@ export function useFastShortTimeouts(maxDelayMs = 2000): () => void {
|
||||
) => {
|
||||
const delay = typeof timeout === "number" ? timeout : 0;
|
||||
if (delay > 0 && delay <= maxDelayMs) {
|
||||
return realSetTimeout(handler, 0, ...args);
|
||||
return nativeSetTimeout(handler, 0, ...args);
|
||||
}
|
||||
return realSetTimeout(handler, delay, ...args);
|
||||
return nativeSetTimeout(handler, delay, ...args);
|
||||
}) as typeof setTimeout);
|
||||
return () => spy.mockRestore();
|
||||
}
|
||||
|
||||
68
test/non-isolated-runner.ts
Normal file
68
test/non-isolated-runner.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import fs from "node:fs";
|
||||
import { TestRunner, type RunnerTestSuite, vi } from "vitest";
|
||||
|
||||
type EvaluatedModuleNode = {
|
||||
promise?: unknown;
|
||||
exports?: unknown;
|
||||
evaluated?: boolean;
|
||||
importers: Set<string>;
|
||||
};
|
||||
|
||||
type EvaluatedModules = {
|
||||
idToModuleMap: Map<string, EvaluatedModuleNode>;
|
||||
};
|
||||
|
||||
function resetEvaluatedModules(modules: EvaluatedModules, resetMocks: boolean) {
|
||||
const skipPaths = [
|
||||
/\/vitest\/dist\//,
|
||||
/vitest-virtual-\w+\/dist/u,
|
||||
/@vitest\/dist/u,
|
||||
...(resetMocks ? [] : [/^mock:/u]),
|
||||
];
|
||||
|
||||
modules.idToModuleMap.forEach((node, modulePath) => {
|
||||
if (skipPaths.some((pattern) => pattern.test(modulePath))) {
|
||||
return;
|
||||
}
|
||||
node.promise = undefined;
|
||||
node.exports = undefined;
|
||||
node.evaluated = false;
|
||||
node.importers.clear();
|
||||
});
|
||||
}
|
||||
|
||||
export default class OpenClawNonIsolatedRunner extends TestRunner {
|
||||
override onCollectStart(file: { filepath: string }) {
|
||||
super.onCollectStart(file);
|
||||
const orderLogPath = process.env.OPENCLAW_VITEST_FILE_ORDER_LOG?.trim();
|
||||
if (orderLogPath) {
|
||||
fs.appendFileSync(orderLogPath, `START ${file.filepath}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
override async onAfterRunSuite(suite: RunnerTestSuite) {
|
||||
await super.onAfterRunSuite(suite);
|
||||
if (this.config.isolate || !("filepath" in suite) || typeof suite.filepath !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const orderLogPath = process.env.OPENCLAW_VITEST_FILE_ORDER_LOG?.trim();
|
||||
if (orderLogPath) {
|
||||
fs.appendFileSync(orderLogPath, `END ${suite.filepath}\n`);
|
||||
}
|
||||
|
||||
// Mirror the missing cleanup from Vitest isolate mode so shared workers do
|
||||
// not carry file-scoped timers, stubs, spies, or stale module state
|
||||
// forward into the next file.
|
||||
if (vi.isFakeTimers()) {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
vi.unstubAllEnvs();
|
||||
vi.clearAllMocks();
|
||||
vi.resetModules();
|
||||
this.moduleRunner?.mocker?.reset?.();
|
||||
resetEvaluatedModules(this.workerState.evaluatedModules as EvaluatedModules, true);
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,60 @@ const pickSendFn = (id: ChannelId, deps?: OutboundSendDeps) => {
|
||||
return deps?.[id] as ((...args: unknown[]) => Promise<unknown>) | undefined;
|
||||
};
|
||||
|
||||
type VitestEvaluatedModuleNode = {
|
||||
promise?: unknown;
|
||||
exports?: unknown;
|
||||
evaluated?: boolean;
|
||||
importers: Set<string>;
|
||||
};
|
||||
|
||||
type VitestEvaluatedModules = {
|
||||
idToModuleMap: Map<string, VitestEvaluatedModuleNode>;
|
||||
};
|
||||
|
||||
const resetVitestWorkerModules = (resetMocks: boolean) => {
|
||||
const workerState = (
|
||||
globalThis as typeof globalThis & {
|
||||
__vitest_worker__?: {
|
||||
evaluatedModules?: VitestEvaluatedModules;
|
||||
};
|
||||
}
|
||||
).__vitest_worker__;
|
||||
const modules = workerState?.evaluatedModules;
|
||||
if (!modules) {
|
||||
return;
|
||||
}
|
||||
|
||||
const skipPaths = [
|
||||
/\/vitest\/dist\//,
|
||||
/vitest-virtual-\w+\/dist/u,
|
||||
/@vitest\/dist/u,
|
||||
...(resetMocks ? [] : [/^mock:/u]),
|
||||
];
|
||||
|
||||
modules.idToModuleMap.forEach((node, modulePath) => {
|
||||
if (skipPaths.some((pattern) => pattern.test(modulePath))) {
|
||||
return;
|
||||
}
|
||||
node.promise = undefined;
|
||||
node.exports = undefined;
|
||||
node.evaluated = false;
|
||||
node.importers.clear();
|
||||
});
|
||||
};
|
||||
|
||||
const resetVitestWorkerFileState = () => {
|
||||
const mocker = (
|
||||
globalThis as typeof globalThis & {
|
||||
__vitest_mocker__?: {
|
||||
reset?: () => void;
|
||||
};
|
||||
}
|
||||
).__vitest_mocker__;
|
||||
mocker?.reset?.();
|
||||
resetVitestWorkerModules(true);
|
||||
};
|
||||
|
||||
const createStubOutbound = (
|
||||
id: ChannelId,
|
||||
deliveryMode: ChannelOutboundAdapter["deliveryMode"] = "direct",
|
||||
@@ -274,8 +328,17 @@ afterEach(() => {
|
||||
globalRegistryState.key = null;
|
||||
globalRegistryState.version += 1;
|
||||
}
|
||||
// Guard against leaked fake timers across test files/workers.
|
||||
if (vi.isFakeTimers()) {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
// Always normalize timer/date state. Some suites call `vi.setSystemTime()`
|
||||
// without leaving fake timers enabled, which still leaks mocked time into
|
||||
// later files under `--isolate=false`.
|
||||
vi.useRealTimers();
|
||||
// Non-isolated runs reuse the same module graph across files. Clear it so
|
||||
// hoisted per-file mocks still apply when later files import the same modules.
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// Mirror Vitest's isolate-mode file cleanup so `--isolate=false` does not
|
||||
// carry hoisted mocks or stale module graphs into the next test file.
|
||||
resetVitestWorkerFileState();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user