test(unit-fast): isolate fake-timer files (#88160)

This commit is contained in:
Dallin Romney
2026-05-29 17:11:05 -07:00
committed by GitHub
parent 4efc48a80d
commit 914f313740
16 changed files with 171 additions and 12 deletions

View File

@@ -250,7 +250,10 @@ const SPLIT_NODE_SHARDS = new Map([
[ [
{ {
shardName: "core-unit-fast", shardName: "core-unit-fast",
configs: ["test/vitest/vitest.unit-fast.config.ts"], configs: [
"test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
],
requiresDist: false, requiresDist: false,
}, },
], ],

View File

@@ -37,7 +37,10 @@ import {
} from "../test/vitest/vitest.plugin-sdk-paths.mjs"; } from "../test/vitest/vitest.plugin-sdk-paths.mjs";
import { fullSuiteVitestShards } from "../test/vitest/vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "../test/vitest/vitest.test-shards.mjs";
import { isUnitUiTestTarget } from "../test/vitest/vitest.ui-paths.mjs"; import { isUnitUiTestTarget } from "../test/vitest/vitest.ui-paths.mjs";
import { resolveUnitFastTestIncludePattern } from "../test/vitest/vitest.unit-fast-paths.mjs"; import {
resolveUnitFastTestIncludePattern,
resolveUnitFastTimerTestIncludePattern,
} from "../test/vitest/vitest.unit-fast-paths.mjs";
import { import {
isBoundaryTestFile, isBoundaryTestFile,
isBundledPluginDependentUnitTestFile, isBundledPluginDependentUnitTestFile,
@@ -127,6 +130,7 @@ const PLUGIN_SDK_LIGHT_VITEST_CONFIG = "test/vitest/vitest.plugin-sdk-light.conf
const PLUGIN_SDK_VITEST_CONFIG = "test/vitest/vitest.plugin-sdk.config.ts"; const PLUGIN_SDK_VITEST_CONFIG = "test/vitest/vitest.plugin-sdk.config.ts";
const PLUGINS_VITEST_CONFIG = "test/vitest/vitest.plugins.config.ts"; const PLUGINS_VITEST_CONFIG = "test/vitest/vitest.plugins.config.ts";
const UNIT_FAST_VITEST_CONFIG = "test/vitest/vitest.unit-fast.config.ts"; const UNIT_FAST_VITEST_CONFIG = "test/vitest/vitest.unit-fast.config.ts";
const UNIT_FAST_FAKE_TIMERS_VITEST_CONFIG = "test/vitest/vitest.unit-fast-fake-timers.config.ts";
const UNIT_SECURITY_VITEST_CONFIG = "test/vitest/vitest.unit-security.config.ts"; const UNIT_SECURITY_VITEST_CONFIG = "test/vitest/vitest.unit-security.config.ts";
const UNIT_SRC_VITEST_CONFIG = "test/vitest/vitest.unit-src.config.ts"; const UNIT_SRC_VITEST_CONFIG = "test/vitest/vitest.unit-src.config.ts";
const UNIT_SUPPORT_VITEST_CONFIG = "test/vitest/vitest.unit-support.config.ts"; const UNIT_SUPPORT_VITEST_CONFIG = "test/vitest/vitest.unit-support.config.ts";
@@ -320,6 +324,7 @@ const VITEST_CONFIG_BY_KIND = {
pluginSdkLight: PLUGIN_SDK_LIGHT_VITEST_CONFIG, pluginSdkLight: PLUGIN_SDK_LIGHT_VITEST_CONFIG,
process: PROCESS_VITEST_CONFIG, process: PROCESS_VITEST_CONFIG,
unitFast: UNIT_FAST_VITEST_CONFIG, unitFast: UNIT_FAST_VITEST_CONFIG,
unitFastFakeTimers: UNIT_FAST_FAKE_TIMERS_VITEST_CONFIG,
unitSecurity: UNIT_SECURITY_VITEST_CONFIG, unitSecurity: UNIT_SECURITY_VITEST_CONFIG,
unitSrc: UNIT_SRC_VITEST_CONFIG, unitSrc: UNIT_SRC_VITEST_CONFIG,
unitSupport: UNIT_SUPPORT_VITEST_CONFIG, unitSupport: UNIT_SUPPORT_VITEST_CONFIG,
@@ -1505,6 +1510,9 @@ function classifyTarget(arg, cwd) {
if (relative.startsWith("src/plugins/contracts/")) { if (relative.startsWith("src/plugins/contracts/")) {
return "contractsPlugin"; return "contractsPlugin";
} }
if (resolveUnitFastTimerTestIncludePattern(relative)) {
return "unitFastFakeTimers";
}
if (resolveUnitFastTestIncludePattern(relative)) { if (resolveUnitFastTestIncludePattern(relative)) {
return "unitFast"; return "unitFast";
} }
@@ -1684,6 +1692,10 @@ function resolveLightLaneIncludePatterns(kind, targetArg, cwd) {
const includePattern = resolveUnitFastTestIncludePattern(relative); const includePattern = resolveUnitFastTestIncludePattern(relative);
return includePattern ? [includePattern] : null; return includePattern ? [includePattern] : null;
} }
if (kind === "unitFastFakeTimers") {
const includePattern = resolveUnitFastTimerTestIncludePattern(relative);
return includePattern ? [includePattern] : null;
}
if (kind === "pluginSdkLight") { if (kind === "pluginSdkLight") {
const includePattern = resolvePluginSdkLightIncludePattern(relative); const includePattern = resolvePluginSdkLightIncludePattern(relative);
return includePattern ? [includePattern] : null; return includePattern ? [includePattern] : null;
@@ -1804,6 +1816,7 @@ export function buildVitestRunPlans(
const nonTargetArgs = activeForwardedArgs.filter((arg) => !activeTargetArgs.includes(arg)); const nonTargetArgs = activeForwardedArgs.filter((arg) => !activeTargetArgs.includes(arg));
const orderedKinds = [ const orderedKinds = [
"unitFast", "unitFast",
"unitFastFakeTimers",
"default", "default",
"boundary", "boundary",
"toolingIsolated", "toolingIsolated",

View File

@@ -113,9 +113,7 @@ const resolveExpectedVitestCliEntry = () => {
return path.join(path.dirname(vitestPackageJson), "vitest.mjs"); return path.join(path.dirname(vitestPackageJson), "vitest.mjs");
}; };
const resolveExpectedVitestNodeArgs = (env: NodeJS.ProcessEnv) => const resolveExpectedVitestNodeArgs = (env: NodeJS.ProcessEnv) =>
["1", "true", "yes", "on"].includes( ["1", "true", "yes", "on"].includes(env.OPENCLAW_VITEST_ENABLE_MAGLEV?.trim().toLowerCase() ?? "")
env.OPENCLAW_VITEST_ENABLE_MAGLEV?.trim().toLowerCase() ?? "",
)
? [] ? []
: ["--no-maglev"]; : ["--no-maglev"];
const VITEST_NODE_PREFIX = [ const VITEST_NODE_PREFIX = [
@@ -287,6 +285,17 @@ describe("test-projects args", () => {
]); ]);
}); });
it("routes fake-timer unit-fast targets to the serial fake-timer config", () => {
expect(buildVitestRunPlans(["src/acp/control-plane/manager.test.ts"])).toEqual([
{
config: "test/vitest/vitest.unit-fast-fake-timers.config.ts",
forwardedArgs: [],
includePatterns: ["src/acp/control-plane/manager.test.ts"],
watchMode: false,
},
]);
});
it("routes process targets to the process config", () => { it("routes process targets to the process config", () => {
expect(buildVitestRunPlans(["src/process/exec.test.ts"])).toEqual([ expect(buildVitestRunPlans(["src/process/exec.test.ts"])).toEqual([
{ {

3
test/AGENTS.md Normal file
View File

@@ -0,0 +1,3 @@
# Test Rules
- Fake-timer tests in `unit-fast` belong in `vitest.unit-fast-fake-timers`: `unit-fast` is `isolate: false` and parallel, so fake timers share worker globals and can hang unrelated real-timer tests.

1
test/CLAUDE.md Symbolic link
View File

@@ -0,0 +1 @@
AGENTS.md

View File

@@ -117,7 +117,10 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
expect(coreUnitShards).toEqual([ expect(coreUnitShards).toEqual([
{ {
configs: ["test/vitest/vitest.unit-fast.config.ts"], configs: [
"test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
],
requiresDist: false, requiresDist: false,
shardName: "core-unit-fast", shardName: "core-unit-fast",
}, },

View File

@@ -959,6 +959,19 @@ describe("scripts/test-projects changed-target routing", () => {
]); ]);
}); });
it("routes fake-timer unit-fast tests to the serial fake-timer lane", () => {
const plans = buildVitestRunPlans(["src/acp/control-plane/manager.test.ts"], process.cwd());
expect(plans).toEqual([
{
config: "test/vitest/vitest.unit-fast-fake-timers.config.ts",
forwardedArgs: [],
includePatterns: ["src/acp/control-plane/manager.test.ts"],
watchMode: false,
},
]);
});
it("routes changed commands source allowlist files to sibling light tests", () => { it("routes changed commands source allowlist files to sibling light tests", () => {
const plans = buildVitestRunPlans(["--changed", "origin/main"], process.cwd(), () => [ const plans = buildVitestRunPlans(["--changed", "origin/main"], process.cwd(), () => [
"src/commands/status-overview-values.ts", "src/commands/status-overview-values.ts",
@@ -1444,6 +1457,7 @@ describe("scripts/test-projects full-suite sharding", () => {
} }
expect(leafShardPlans.map((plan) => plan.config)).toEqual([ expect(leafShardPlans.map((plan) => plan.config)).toEqual([
"test/vitest/vitest.unit-fast.config.ts", "test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
"test/vitest/vitest.unit-src.config.ts", "test/vitest/vitest.unit-src.config.ts",
"test/vitest/vitest.unit-security.config.ts", "test/vitest/vitest.unit-security.config.ts",
"test/vitest/vitest.unit-ui.config.ts", "test/vitest/vitest.unit-ui.config.ts",

View File

@@ -28,6 +28,7 @@ import {
import { fullSuiteVitestShards } from "./vitest/vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest/vitest.test-shards.mjs";
import { unitUiIncludePatterns } from "./vitest/vitest.ui-paths.mjs"; import { unitUiIncludePatterns } from "./vitest/vitest.ui-paths.mjs";
import { createUiVitestConfig } from "./vitest/vitest.ui.config.ts"; import { createUiVitestConfig } from "./vitest/vitest.ui.config.ts";
import { createUnitFastFakeTimersVitestConfig } from "./vitest/vitest.unit-fast-fake-timers.config.ts";
import { createUnitFastVitestConfig } from "./vitest/vitest.unit-fast.config.ts"; import { createUnitFastVitestConfig } from "./vitest/vitest.unit-fast.config.ts";
import unitUiConfig from "./vitest/vitest.unit-ui.config.ts"; import unitUiConfig from "./vitest/vitest.unit-ui.config.ts";
import { createUnitVitestConfig } from "./vitest/vitest.unit.config.ts"; import { createUnitVitestConfig } from "./vitest/vitest.unit.config.ts";
@@ -225,6 +226,14 @@ describe("projects vitest config", () => {
expect(config.test.runner).toBeUndefined(); expect(config.test.runner).toBeUndefined();
}); });
it("keeps fake-timer unit-fast files serial with the non-isolated runner", () => {
const config = createUnitFastFakeTimersVitestConfig();
expect(config.test.isolate).toBe(false);
expect(normalizeConfigPath(config.test.runner)).toBe("test/non-isolated-runner.ts");
expect(config.test.fileParallelism).toBe(false);
expect(config.test.maxWorkers).toBe(1);
});
it("keeps the bundled lane on thread workers with the non-isolated runner", () => { it("keeps the bundled lane on thread workers with the non-isolated runner", () => {
const testConfig = requireTestConfig(bundledConfig); const testConfig = requireTestConfig(bundledConfig);
expect(testConfig.pool).toBe("threads"); expect(testConfig.pool).toBe("threads");

View File

@@ -2,6 +2,7 @@ import { beforeAll, describe, expect, it } from "vitest";
import { spawnNodeEvalSync } from "../src/test-utils/node-process.js"; import { spawnNodeEvalSync } from "../src/test-utils/node-process.js";
import { createCommandsLightVitestConfig } from "./vitest/vitest.commands-light.config.ts"; import { createCommandsLightVitestConfig } from "./vitest/vitest.commands-light.config.ts";
import { createPluginSdkLightVitestConfig } from "./vitest/vitest.plugin-sdk-light.config.ts"; import { createPluginSdkLightVitestConfig } from "./vitest/vitest.plugin-sdk-light.config.ts";
import { createUnitFastFakeTimersVitestConfig } from "./vitest/vitest.unit-fast-fake-timers.config.ts";
import { import {
classifyUnitFastTestFileContent, classifyUnitFastTestFileContent,
collectBroadUnitFastTestCandidates, collectBroadUnitFastTestCandidates,
@@ -9,8 +10,11 @@ import {
collectUnitFastTestFileAnalysis, collectUnitFastTestFileAnalysis,
forcedUnitFastTestFiles, forcedUnitFastTestFiles,
getUnitFastTestFiles, getUnitFastTestFiles,
getUnitFastTimerTestFiles,
isUnitFastTestFile, isUnitFastTestFile,
isUnitFastTimerTestFile,
resolveUnitFastTestIncludePattern, resolveUnitFastTestIncludePattern,
resolveUnitFastTimerTestIncludePattern,
} from "./vitest/vitest.unit-fast-paths.mjs"; } from "./vitest/vitest.unit-fast-paths.mjs";
import { createUnitFastVitestConfig } from "./vitest/vitest.unit-fast.config.ts"; import { createUnitFastVitestConfig } from "./vitest/vitest.unit-fast.config.ts";
@@ -53,6 +57,7 @@ describe("unit-fast vitest lane", () => {
let configProbeResult: ReturnType<typeof spawnNodeEvalSync>; let configProbeResult: ReturnType<typeof spawnNodeEvalSync>;
let unitFastConfig: ReturnType<typeof createUnitFastVitestConfig>; let unitFastConfig: ReturnType<typeof createUnitFastVitestConfig>;
let unitFastTestFiles: ReturnType<typeof getUnitFastTestFiles>; let unitFastTestFiles: ReturnType<typeof getUnitFastTestFiles>;
let unitFastTimerTestFiles: ReturnType<typeof getUnitFastTimerTestFiles>;
let unitFastAnalysis: ReturnType<typeof collectUnitFastTestFileAnalysis>; let unitFastAnalysis: ReturnType<typeof collectUnitFastTestFileAnalysis>;
let broadCandidates: ReturnType<typeof collectBroadUnitFastTestCandidates>; let broadCandidates: ReturnType<typeof collectBroadUnitFastTestCandidates>;
let broadAnalysis: ReturnType<typeof collectUnitFastTestFileAnalysis>; let broadAnalysis: ReturnType<typeof collectUnitFastTestFileAnalysis>;
@@ -77,6 +82,7 @@ describe("unit-fast vitest lane", () => {
}); });
unitFastConfig = createUnitFastVitestConfig({}); unitFastConfig = createUnitFastVitestConfig({});
unitFastTestFiles = getUnitFastTestFiles(); unitFastTestFiles = getUnitFastTestFiles();
unitFastTimerTestFiles = getUnitFastTimerTestFiles();
unitFastAnalysis = collectUnitFastTestFileAnalysis(); unitFastAnalysis = collectUnitFastTestFileAnalysis();
currentCandidates = collectUnitFastTestCandidates(); currentCandidates = collectUnitFastTestCandidates();
broadCandidates = collectBroadUnitFastTestCandidates(); broadCandidates = collectBroadUnitFastTestCandidates();
@@ -106,7 +112,6 @@ describe("unit-fast vitest lane", () => {
expect(testConfig.include).toContain("src/acp/control-plane/runtime-cache.test.ts"); expect(testConfig.include).toContain("src/acp/control-plane/runtime-cache.test.ts");
expect(testConfig.include).toContain("src/acp/runtime/registry.test.ts"); expect(testConfig.include).toContain("src/acp/runtime/registry.test.ts");
expect(testConfig.include).toContain("src/commands/status-overview-values.test.ts"); expect(testConfig.include).toContain("src/commands/status-overview-values.test.ts");
expect(testConfig.include).toContain("src/entry.respawn.test.ts");
expect(testConfig.include).toContain("src/entry.version-fast-path.test.ts"); expect(testConfig.include).toContain("src/entry.version-fast-path.test.ts");
expect(testConfig.include).toContain("src/flows/doctor-startup-channel-maintenance.test.ts"); expect(testConfig.include).toContain("src/flows/doctor-startup-channel-maintenance.test.ts");
expect(testConfig.include).toContain("src/crestodian/rescue-policy.test.ts"); expect(testConfig.include).toContain("src/crestodian/rescue-policy.test.ts");
@@ -127,7 +132,6 @@ describe("unit-fast vitest lane", () => {
expect(testConfig.include).toContain("src/security/audit-gateway-tools-http.test.ts"); expect(testConfig.include).toContain("src/security/audit-gateway-tools-http.test.ts");
expect(testConfig.include).toContain("src/security/audit-plugin-readonly-scope.test.ts"); expect(testConfig.include).toContain("src/security/audit-plugin-readonly-scope.test.ts");
expect(testConfig.include).toContain("src/security/audit-loopback-logging.test.ts"); expect(testConfig.include).toContain("src/security/audit-loopback-logging.test.ts");
expect(testConfig.include).toContain("src/security/audit-sandbox-browser.test.ts");
expect(testConfig.include).toContain("src/ui-app-settings.agents-files-refresh.test.ts"); expect(testConfig.include).toContain("src/ui-app-settings.agents-files-refresh.test.ts");
expect(testConfig.include).toContain("src/video-generation/provider-registry.test.ts"); expect(testConfig.include).toContain("src/video-generation/provider-registry.test.ts");
expect(testConfig.include).toContain("src/plugin-sdk/provider-entry.test.ts"); expect(testConfig.include).toContain("src/plugin-sdk/provider-entry.test.ts");
@@ -187,6 +191,26 @@ describe("unit-fast vitest lane", () => {
expect(unroutedForcedFiles).toStrictEqual([]); expect(unroutedForcedFiles).toStrictEqual([]);
}); });
it("routes fake-timer unit-fast tests through the serial fake-timer lane", () => {
const fakeTimerFiles = unitFastAnalysis
.filter((entry) => entry.unitFast && entry.reasons.includes("fake-timers"))
.map((entry) => entry.file);
expect(unitFastTimerTestFiles.length).toBeGreaterThan(0);
expect(unitFastTimerTestFiles).toEqual(fakeTimerFiles);
for (const file of unitFastTimerTestFiles) {
expect(isUnitFastTimerTestFile(file)).toBe(true);
expect(resolveUnitFastTestIncludePattern(file)).toBeNull();
expect(resolveUnitFastTimerTestIncludePattern(file)).toBe(file);
}
const fastConfig = requireTestConfig(unitFastConfig);
const timerConfig = requireTestConfig(createUnitFastFakeTimersVitestConfig({}));
expect(fastConfig.include).not.toEqual(expect.arrayContaining(unitFastTimerTestFiles));
expect(timerConfig.include).toEqual(unitFastTimerTestFiles);
expect(timerConfig.fileParallelism).toBe(false);
expect(timerConfig.maxWorkers).toBe(1);
});
it("keeps broad audit candidates separate from automatically routed unit-fast tests", () => { it("keeps broad audit candidates separate from automatically routed unit-fast tests", () => {
expect(currentCandidates.length).toBeGreaterThanOrEqual(unitFastTestFiles.length); expect(currentCandidates.length).toBeGreaterThanOrEqual(unitFastTestFiles.length);
expect(broadCandidates.length).toBeGreaterThan(currentCandidates.length); expect(broadCandidates.length).toBeGreaterThan(currentCandidates.length);

View File

@@ -39,6 +39,7 @@ export const rootVitestProjects = [
"test/vitest/vitest.daemon.config.ts", "test/vitest/vitest.daemon.config.ts",
"test/vitest/vitest.media.config.ts", "test/vitest/vitest.media.config.ts",
"test/vitest/vitest.unit-fast.config.ts", "test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
"test/vitest/vitest.plugin-sdk-light.config.ts", "test/vitest/vitest.plugin-sdk-light.config.ts",
"test/vitest/vitest.plugin-sdk.config.ts", "test/vitest/vitest.plugin-sdk.config.ts",
"test/vitest/vitest.plugins.config.ts", "test/vitest/vitest.plugins.config.ts",

View File

@@ -6,6 +6,9 @@ export default defineConfig({
test: { test: {
...sharedVitestConfig.test, ...sharedVitestConfig.test,
runner: undefined, runner: undefined,
projects: ["test/vitest/vitest.unit-fast.config.ts"], projects: [
"test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
],
}, },
}); });

View File

@@ -341,6 +341,7 @@ export const sharedVitestConfig = {
"test/vitest/vitest.media-understanding.config.ts", "test/vitest/vitest.media-understanding.config.ts",
"test/vitest/vitest.performance-config.ts", "test/vitest/vitest.performance-config.ts",
"test/vitest/vitest.unit-fast.config.ts", "test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
"test/vitest/vitest.unit-fast-paths.mjs", "test/vitest/vitest.unit-fast-paths.mjs",
"test/vitest/vitest.scoped-config.ts", "test/vitest/vitest.scoped-config.ts",
"test/vitest/vitest.shared-core.config.ts", "test/vitest/vitest.shared-core.config.ts",

View File

@@ -10,7 +10,10 @@ export const fullSuiteVitestShards = [
{ {
config: "test/vitest/vitest.full-core-unit-fast.config.ts", config: "test/vitest/vitest.full-core-unit-fast.config.ts",
name: "core-unit-fast", name: "core-unit-fast",
projects: ["test/vitest/vitest.unit-fast.config.ts"], projects: [
"test/vitest/vitest.unit-fast.config.ts",
"test/vitest/vitest.unit-fast-fake-timers.config.ts",
],
}, },
{ {
config: "test/vitest/vitest.full-core-unit-src.config.ts", config: "test/vitest/vitest.full-core-unit-src.config.ts",

View File

@@ -0,0 +1,32 @@
import { defineConfig } from "vitest/config";
import { loadPatternListFromEnv, narrowIncludePatternsForCli } from "./vitest.pattern-file.ts";
import { nonIsolatedRunnerPath, sharedVitestConfig } from "./vitest.shared.config.ts";
import { getUnitFastTimerTestFiles } from "./vitest.unit-fast-paths.mjs";
export function createUnitFastFakeTimersVitestConfig(
env: Record<string, string | undefined> = process.env,
options: { argv?: string[] } = {},
) {
const sharedTest = sharedVitestConfig.test ?? {};
const includeFromEnv = loadPatternListFromEnv("OPENCLAW_VITEST_INCLUDE_FILE", env);
const unitFastTimerTestFiles = getUnitFastTimerTestFiles();
const cliInclude = narrowIncludePatternsForCli(unitFastTimerTestFiles, options.argv);
return defineConfig({
...sharedVitestConfig,
test: {
...sharedTest,
name: "unit-fast-fake-timers",
isolate: false,
runner: nonIsolatedRunnerPath,
setupFiles: [],
include: includeFromEnv ?? cliInclude ?? unitFastTimerTestFiles,
exclude: sharedTest.exclude ?? [],
maxWorkers: 1,
fileParallelism: false,
passWithNoTests: true,
},
});
}
export default createUnitFastFakeTimersVitestConfig();

View File

@@ -425,6 +425,8 @@ export function collectUnitFastTestFileAnalysis(cwd = process.cwd(), options = {
let cachedUnitFastTestFiles = null; let cachedUnitFastTestFiles = null;
let cachedUnitFastTestFileSet = null; let cachedUnitFastTestFileSet = null;
let cachedUnitFastTimerTestFiles = null;
let cachedUnitFastTimerTestFileSet = null;
const cachedSingleUnitFastTestFileResults = new Map(); const cachedSingleUnitFastTestFileResults = new Map();
export function getUnitFastTestFiles() { export function getUnitFastTestFiles() {
@@ -437,6 +439,16 @@ export function getUnitFastTestFiles() {
return cachedUnitFastTestFiles; return cachedUnitFastTestFiles;
} }
export function getUnitFastTimerTestFiles() {
if (cachedUnitFastTimerTestFiles !== null) {
return cachedUnitFastTimerTestFiles;
}
cachedUnitFastTimerTestFiles = collectUnitFastTestFileAnalysis()
.filter((entry) => entry.unitFast && entry.reasons.includes("fake-timers"))
.map((entry) => entry.file);
return cachedUnitFastTimerTestFiles;
}
function getUnitFastTestFileSet() { function getUnitFastTestFileSet() {
if (cachedUnitFastTestFileSet !== null) { if (cachedUnitFastTestFileSet !== null) {
return cachedUnitFastTestFileSet; return cachedUnitFastTestFileSet;
@@ -445,6 +457,14 @@ function getUnitFastTestFileSet() {
return cachedUnitFastTestFileSet; return cachedUnitFastTestFileSet;
} }
function getUnitFastTimerTestFileSet() {
if (cachedUnitFastTimerTestFileSet !== null) {
return cachedUnitFastTimerTestFileSet;
}
cachedUnitFastTimerTestFileSet = new Set(getUnitFastTimerTestFiles());
return cachedUnitFastTimerTestFileSet;
}
function isUnitFastTestFileOnDemand(file, cwd = process.cwd()) { function isUnitFastTestFileOnDemand(file, cwd = process.cwd()) {
const normalized = normalizeRepoPath(file); const normalized = normalizeRepoPath(file);
const cacheKey = `${normalizeRepoPath(cwd)}\0${normalized}`; const cacheKey = `${normalizeRepoPath(cwd)}\0${normalized}`;
@@ -476,12 +496,22 @@ export function isUnitFastTestFile(file) {
return getUnitFastTestFileSet().has(normalizeRepoPath(file)); return getUnitFastTestFileSet().has(normalizeRepoPath(file));
} }
export function isUnitFastTimerTestFile(file) {
return getUnitFastTimerTestFileSet().has(normalizeRepoPath(file));
}
export function resolveUnitFastTestIncludePattern(file) { export function resolveUnitFastTestIncludePattern(file) {
const normalized = normalizeRepoPath(file); const normalized = normalizeRepoPath(file);
if (isUnitFastTimerTestFile(normalized)) {
return null;
}
if (isUnitFastTestFileOnDemand(normalized)) { if (isUnitFastTestFileOnDemand(normalized)) {
return normalized; return normalized;
} }
const siblingTestFile = normalized.replace(/\.ts$/u, ".test.ts"); const siblingTestFile = normalized.replace(/\.ts$/u, ".test.ts");
if (isUnitFastTimerTestFile(siblingTestFile)) {
return null;
}
if (isUnitFastTestFileOnDemand(siblingTestFile)) { if (isUnitFastTestFileOnDemand(siblingTestFile)) {
return siblingTestFile; return siblingTestFile;
} }
@@ -491,3 +521,12 @@ export function resolveUnitFastTestIncludePattern(file) {
} }
return null; return null;
} }
export function resolveUnitFastTimerTestIncludePattern(file) {
const normalized = normalizeRepoPath(file);
if (isUnitFastTimerTestFile(normalized)) {
return normalized;
}
const siblingTestFile = normalized.replace(/\.ts$/u, ".test.ts");
return isUnitFastTimerTestFile(siblingTestFile) ? siblingTestFile : null;
}

View File

@@ -1,7 +1,7 @@
import { defineConfig } from "vitest/config"; import { defineConfig } from "vitest/config";
import { loadPatternListFromEnv, narrowIncludePatternsForCli } from "./vitest.pattern-file.ts"; import { loadPatternListFromEnv, narrowIncludePatternsForCli } from "./vitest.pattern-file.ts";
import { sharedVitestConfig } from "./vitest.shared.config.ts"; import { sharedVitestConfig } from "./vitest.shared.config.ts";
import { getUnitFastTestFiles } from "./vitest.unit-fast-paths.mjs"; import { getUnitFastTestFiles, getUnitFastTimerTestFiles } from "./vitest.unit-fast-paths.mjs";
export function createUnitFastVitestConfig( export function createUnitFastVitestConfig(
env: Record<string, string | undefined> = process.env, env: Record<string, string | undefined> = process.env,
@@ -9,7 +9,8 @@ export function createUnitFastVitestConfig(
) { ) {
const sharedTest = sharedVitestConfig.test ?? {}; const sharedTest = sharedVitestConfig.test ?? {};
const includeFromEnv = loadPatternListFromEnv("OPENCLAW_VITEST_INCLUDE_FILE", env); const includeFromEnv = loadPatternListFromEnv("OPENCLAW_VITEST_INCLUDE_FILE", env);
const unitFastTestFiles = getUnitFastTestFiles(); const timerTestFiles = new Set(getUnitFastTimerTestFiles());
const unitFastTestFiles = getUnitFastTestFiles().filter((file) => !timerTestFiles.has(file));
const cliInclude = narrowIncludePatternsForCli(unitFastTestFiles, options.argv); const cliInclude = narrowIncludePatternsForCli(unitFastTestFiles, options.argv);
return defineConfig({ return defineConfig({