diff --git a/AGENTS.md b/AGENTS.md index 4c9b504b33b..9ed9ce7afc6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,7 +118,7 @@ - Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat. - For targeted/local debugging, keep using the wrapper: `pnpm test -- [vitest args...]` (for example `pnpm test -- src/commands/onboard-search.test.ts -t "shows registered plugin providers"`); do not default to raw `pnpm vitest run ...` because it bypasses wrapper config/profile/pool routing. - Do not set test workers above 16; tried already. -- Do not switch CI `pnpm test` lanes back to Vitest `vmForks` by default without fresh green evidence on current `main`; keep CI on `forks` unless explicitly re-validated. +- Do not reintroduce Vitest VM pools by default without fresh green evidence on current `main`; keep CI on `forks`. - If local Vitest runs cause memory pressure (common on non-Mac-Studio hosts), use `OPENCLAW_TEST_PROFILE=low OPENCLAW_TEST_SERIAL_GATEWAY=1 pnpm test` for land/gate runs. - Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`. - Full kit + what’s covered: `docs/help/testing.md`. diff --git a/docs/help/testing.md b/docs/help/testing.md index 430af3869ae..17d97c3ddd1 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -71,12 +71,11 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost): sufficient substitute for those integration paths. - Pool note: - Base Vitest config still defaults to `forks`. - - Unit wrapper lanes default to `threads`, with explicit manifest fork/vmFork exceptions. + - Unit wrapper lanes default to `threads`, with explicit manifest fork-only exceptions. - Extension scoped config defaults to `threads`. - `pnpm test` also defaults to `--isolate=false` at the wrapper level for faster file startup. - Opt back into Vitest file isolation with `OPENCLAW_TEST_ISOLATE=1 pnpm test`. - `OPENCLAW_TEST_NO_ISOLATE=0` or `OPENCLAW_TEST_NO_ISOLATE=false` also force isolated runs. - - `OPENCLAW_TEST_VM_FORKS=1` remains an opt-in experiment on Node 22, 23, and 24 only. ### E2E (gateway smoke) diff --git a/docs/reference/test.md b/docs/reference/test.md index e864f9eca67..8c724f99580 100644 --- a/docs/reference/test.md +++ b/docs/reference/test.md @@ -11,16 +11,14 @@ title: "Tests" - `pnpm test:force`: Kills any lingering gateway process holding the default control port, then runs the full Vitest suite with an isolated gateway port so server tests don’t collide with a running instance. Use this when a prior gateway run left port 18789 occupied. - `pnpm test:coverage`: Runs the unit suite with V8 coverage (via `vitest.unit.config.ts`). Global thresholds are 70% lines/branches/functions/statements. Coverage excludes integration-heavy entrypoints (CLI wiring, gateway/telegram bridges, webchat static server) to keep the target focused on unit-testable logic. -- `pnpm test` on Node 22, 23, and 24 uses Vitest `vmForks` by default for local runs with enough memory. CI stays on `forks` unless explicitly overridden. Node 25+ falls back to `forks` until re-validated. You can force behavior with `OPENCLAW_TEST_VM_FORKS=0|1`. - `pnpm test`: runs the full wrapper. It keeps only a small behavioral override manifest in git, then uses a checked-in timing snapshot to peel the heaviest measured unit files into dedicated lanes. -- Unit files default to `threads` in the wrapper; keep fork-only or `forkBatched` exceptions documented in `test/fixtures/test-parallel.behavior.json`. +- Unit files default to `threads` in the wrapper; keep fork-only exceptions documented in `test/fixtures/test-parallel.behavior.json`. - `pnpm test:extensions` now defaults to `threads` via `vitest.extensions.config.ts`; the March 22, 2026 direct full-suite control run passed clean without extension-specific fork exceptions. -- Files marked `forkBatched` stay on `forks`, but the wrapper batches them into low-concurrency `forks` lanes with `maxWorkers=1` instead of spawning one fresh Vitest process per file. Tune lane count with `OPENCLAW_TEST_UNIT_FORK_BATCH_LANES=`. - `pnpm test:channels`: runs channel-heavy suites. - `pnpm test:extensions`: runs extension/plugin suites. - `pnpm test:perf:update-timings`: refreshes the checked-in slow-file timing snapshot used by `scripts/test-parallel.mjs`. - Gateway integration: opt-in via `OPENCLAW_TEST_INCLUDE_GATEWAY=1 pnpm test` or `pnpm test:gateway`. -- `pnpm test:e2e`: Runs gateway end-to-end smoke tests (multi-instance WS/HTTP/node pairing). Defaults to `vmForks` + adaptive workers in `vitest.e2e.config.ts`; tune with `OPENCLAW_E2E_WORKERS=` and set `OPENCLAW_E2E_VERBOSE=1` for verbose logs. +- `pnpm test:e2e`: Runs gateway end-to-end smoke tests (multi-instance WS/HTTP/node pairing). Defaults to `forks` + adaptive workers in `vitest.e2e.config.ts`; tune with `OPENCLAW_E2E_WORKERS=` and set `OPENCLAW_E2E_VERBOSE=1` for verbose logs. - `pnpm test:live`: Runs provider live tests (minimax/zai). Requires API keys and `LIVE=1` (or provider-specific `*_LIVE_TEST=1`) to unskip. ## Local PR gate diff --git a/package.json b/package.json index 6b8d2a00c3b..4cdda19ba33 100644 --- a/package.json +++ b/package.json @@ -699,7 +699,7 @@ "test:install:e2e:openai": "OPENCLAW_E2E_MODELS=openai CLAWDBOT_E2E_MODELS=openai bash scripts/test-install-sh-e2e-docker.sh", "test:install:smoke": "bash scripts/test-install-sh-docker.sh", "test:live": "OPENCLAW_LIVE_TEST=1 CLAWDBOT_LIVE_TEST=1 vitest run --config vitest.live.config.ts", - "test:macmini": "OPENCLAW_TEST_VM_FORKS=0 OPENCLAW_TEST_PROFILE=macmini node scripts/test-parallel.mjs", + "test:macmini": "OPENCLAW_TEST_PROFILE=macmini node scripts/test-parallel.mjs", "test:parallels:linux": "bash scripts/e2e/parallels-linux-smoke.sh", "test:parallels:macos": "bash scripts/e2e/parallels-macos-smoke.sh", "test:parallels:windows": "bash scripts/e2e/parallels-windows-smoke.sh", diff --git a/scripts/test-find-thread-candidates.mjs b/scripts/test-find-thread-candidates.mjs index 285e4f40d00..d1d63e56c26 100644 --- a/scripts/test-find-thread-candidates.mjs +++ b/scripts/test-find-thread-candidates.mjs @@ -83,12 +83,8 @@ export function getExistingThreadCandidateExclusions(behavior) { ...(behavior.base?.threadPinned ?? []).map((entry) => entry.file), ...(behavior.base?.threadSingleton ?? []).map((entry) => entry.file), ...(behavior.unit?.isolated ?? []).map((entry) => entry.file), - ...(behavior.unit?.forkBatched ?? []).map((entry) => entry.file), - ...(behavior.unit?.singletonIsolated ?? []).map((entry) => entry.file), ...(behavior.unit?.threadPinned ?? []).map((entry) => entry.file), ...(behavior.unit?.threadSingleton ?? []).map((entry) => entry.file), - ...(behavior.unit?.vmForkPinned ?? []).map((entry) => entry.file), - ...(behavior.unit?.vmForkSingleton ?? []).map((entry) => entry.file), ]); } diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index a8656b269f3..83b76aae099 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -51,16 +51,8 @@ const cleanupTempArtifacts = () => { const existingUnitConfigFiles = (entries) => existingFiles(entries).filter(isUnitConfigTestFile); const baseThreadPinnedFiles = existingFiles(behaviorManifest.base?.threadPinned ?? []); const unitForkIsolatedFiles = existingUnitConfigFiles(behaviorManifest.unit.isolated); -const unitForkBatchedConfiguredFiles = existingUnitConfigFiles(behaviorManifest.unit.forkBatched); const unitThreadPinnedFiles = existingUnitConfigFiles(behaviorManifest.unit.threadPinned); -const unitVmForkPinnedFiles = existingUnitConfigFiles(behaviorManifest.unit.vmForkPinned); -const extensionIsolatedFiles = existingFiles(behaviorManifest.extensions.isolated); -const unitBehaviorOverrideSet = new Set([ - ...unitForkIsolatedFiles, - ...unitForkBatchedConfiguredFiles, - ...unitThreadPinnedFiles, - ...unitVmForkPinnedFiles, -]); +const unitBehaviorOverrideSet = new Set([...unitForkIsolatedFiles, ...unitThreadPinnedFiles]); const channelSingletonFiles = []; const children = new Set(); @@ -87,14 +79,10 @@ const isMacMiniProfile = testProfile === "macmini"; // Vitest executes Node tests through Vite's SSR/module-runner pipeline, so the // shared unit lane still retains transformed ESM/module state even when the // tests themselves are not "server rendering" a website. We previously kept -// forks as the default after vmFork OOM regressions on constrained hosts. On +// forks as the default after VM-pool regressions on constrained hosts. On // 2026-03-22, a direct full-unit threads run finished 1109/1110 green; the sole // correctness exception stayed on the manifest fork lane, so the wrapper now -// defaults unit runs to threads while preserving explicit fork/vmFork escapes. -// Preserve OPENCLAW_TEST_UNIT_DEFAULT_POOL=forks or OPENCLAW_TEST_VM_FORKS=1 as -// explicit debug escape hatches. -const supportsVmForks = Number.isFinite(nodeMajor) ? nodeMajor <= 24 : true; -const useVmForks = process.env.OPENCLAW_TEST_VM_FORKS === "1" && supportsVmForks; +// defaults unit runs to threads while preserving explicit fork escapes. const forceIsolation = process.env.OPENCLAW_TEST_ISOLATE === "1" || process.env.OPENCLAW_TEST_ISOLATE === "true"; const disableIsolation = @@ -107,9 +95,6 @@ const parsePoolOverride = (value, fallback) => { if (value === "threads" || value === "forks") { return value; } - if (value === "vmForks") { - return supportsVmForks ? value : "forks"; - } return fallback; }; // Even on low-memory hosts, keep the isolated lane split so files like @@ -275,7 +260,6 @@ const allKnownTestFiles = [ const defaultUnitPool = parsePoolOverride(process.env.OPENCLAW_TEST_UNIT_DEFAULT_POOL, "threads"); const isTargetedIsolatedUnitFile = (fileFilter) => unitForkIsolatedFiles.includes(fileFilter) || - unitForkBatchFiles.includes(fileFilter) || unitMemoryIsolatedFiles.includes(fileFilter); const inferTarget = (fileFilter) => { const isolated = isTargetedIsolatedUnitFile(fileFilter); @@ -369,40 +353,14 @@ const { memoryHeavyFiles: memoryHeavyUnitFiles, timedHeavyFiles: timedHeavyUnitF memoryHeavyFiles: [], timedHeavyFiles: [], }; -const unitForkBatchFiles = dedupeFilesPreserveOrder( - unitForkBatchedConfiguredFiles, - new Set(unitForkIsolatedFiles), -); const unitMemoryIsolatedFiles = dedupeFilesPreserveOrder( memoryHeavyUnitFiles, - new Set([...unitBehaviorOverrideSet, ...unitForkBatchFiles]), + unitBehaviorOverrideSet, ); const unitSchedulingOverrideSet = new Set([...unitBehaviorOverrideSet, ...memoryHeavyUnitFiles]); const unitFastExcludedFiles = [ ...new Set([...unitSchedulingOverrideSet, ...timedHeavyUnitFiles, ...channelSingletonFiles]), ]; -const defaultForkBatchLaneCount = - testProfile === "serial" - ? 0 - : unitForkBatchFiles.length === 0 - ? 0 - : isCI - ? Math.ceil(unitForkBatchFiles.length / 6) - : testProfile === "low" && highMemLocalHost - ? Math.ceil(unitForkBatchFiles.length / 8) + 1 - : highMemLocalHost - ? Math.ceil(unitForkBatchFiles.length / 8) - : lowMemLocalHost - ? Math.ceil(unitForkBatchFiles.length / 12) - : Math.ceil(unitForkBatchFiles.length / 10); -const configuredForkBatchLaneCount = parseEnvNumber( - "OPENCLAW_TEST_UNIT_FORK_BATCH_LANES", - defaultForkBatchLaneCount, -); -const forkBatchLaneCount = - unitForkBatchFiles.length === 0 - ? 0 - : Math.min(unitForkBatchFiles.length, Math.max(1, configuredForkBatchLaneCount)); const estimateUnitDurationMs = (file) => unitTimingManifest.files[file]?.durationMs ?? unitTimingManifest.defaultDurationMs; const splitFilesByDurationBudget = (files, targetDurationMs, estimateDurationMs) => { @@ -431,18 +389,11 @@ const splitFilesByDurationBudget = (files, targetDurationMs, estimateDurationMs) return batches; }; -const unitForkBatchBuckets = - forkBatchLaneCount > 0 - ? packFilesByDuration(unitForkBatchFiles, forkBatchLaneCount, estimateUnitDurationMs) - : []; const unitFastExcludedFileSet = new Set(unitFastExcludedFiles); const unitFastCandidateFiles = allKnownUnitFiles.filter( (file) => !unitFastExcludedFileSet.has(file), ); -const extensionIsolatedExcludedFileSet = new Set(extensionIsolatedFiles); -const extensionSharedCandidateFiles = allKnownTestFiles.filter( - (file) => file.startsWith("extensions/") && !extensionIsolatedExcludedFileSet.has(file), -); +const extensionSharedCandidateFiles = allKnownTestFiles.filter((file) => file.startsWith("extensions/")); const defaultUnitFastLaneCount = isCI && !isWindows ? 3 : 1; const unitFastLaneCount = Math.max( 1, @@ -498,11 +449,6 @@ const unitHeavyEntries = heavyUnitBuckets.map((files, index) => ({ name: `unit-heavy-${String(index + 1)}`, args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", ...files], })); -const unitForkBatchEntries = unitForkBatchBuckets.map((files, index) => ({ - name: - unitForkBatchBuckets.length === 1 ? "unit-fork-batch" : `unit-fork-batch-${String(index + 1)}`, - args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", ...files], -})); const unitThreadEntries = unitThreadPinnedFiles.length > 0 ? [ @@ -523,17 +469,12 @@ const unitIsolatedEntries = unitForkIsolatedFiles.map((file) => ({ name: `unit-${path.basename(file, ".test.ts")}-isolated`, args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", file], })); -const extensionIsolatedEntries = extensionIsolatedFiles.map((file) => ({ - name: `${path.basename(file, ".test.ts")}-extensions-isolated`, - args: ["vitest", "run", "--config", "vitest.extensions.config.ts", "--pool=forks", file], -})); const baseRuns = [ ...(shouldSplitUnitRuns ? [ ...unitFastEntries, ...unitIsolatedEntries, ...unitHeavyEntries, - ...unitForkBatchEntries, ...unitMemoryIsolatedFiles.map((file) => ({ name: `unit-${path.basename(file, ".test.ts")}-memory-isolated`, args: [ @@ -541,23 +482,11 @@ const baseRuns = [ "run", "--config", "vitest.unit.config.ts", - `--pool=${useVmForks ? "vmForks" : "forks"}`, + "--pool=forks", file, ], })), ...unitThreadEntries, - ...unitVmForkPinnedFiles.map((file) => ({ - name: `${path.basename(file, ".test.ts")}-vmforks`, - args: [ - "vitest", - "run", - "--config", - "vitest.unit.config.ts", - `--pool=${useVmForks ? "vmForks" : "forks"}`, - ...(disableIsolation ? ["--isolate=false"] : []), - file, - ], - })), ...channelSingletonFiles.map((file) => ({ name: `${path.basename(file, ".test.ts")}-channels-isolated`, args: ["vitest", "run", "--config", "vitest.channels.config.ts", "--pool=forks", file], @@ -571,7 +500,7 @@ const baseRuns = [ "run", "--config", "vitest.unit.config.ts", - `--pool=${useVmForks ? "vmForks" : "forks"}`, + "--pool=forks", ...(disableIsolation ? ["--isolate=false"] : []), ], }, @@ -594,10 +523,8 @@ const baseRuns = [ "run", "--config", "vitest.extensions.config.ts", - ...(useVmForks ? ["--pool=vmForks"] : []), ], }, - ...extensionIsolatedEntries, ] : []), ...(includeGatewaySuite @@ -633,26 +560,11 @@ const resolveFilterMatches = (fileFilter) => { } return allKnownTestFiles.filter((file) => file.includes(normalizedFilter)); }; -const isVmForkPinnedUnitFile = (fileFilter) => unitVmForkPinnedFiles.includes(fileFilter); const isThreadPinnedUnitFile = (fileFilter) => unitThreadPinnedFiles.includes(fileFilter); const isBaseThreadPinnedFile = (fileFilter) => baseThreadPinnedFiles.includes(fileFilter); const createTargetedEntry = (owner, isolated, filters) => { const name = isolated ? `${owner}-isolated` : owner; const forceForks = isolated; - if (owner === "unit-vmforks") { - return { - name, - args: [ - "vitest", - "run", - "--config", - "vitest.unit.config.ts", - `--pool=${useVmForks ? "vmForks" : "forks"}`, - ...(disableIsolation ? ["--isolate=false"] : []), - ...filters, - ], - }; - } if (owner === "unit") { return { name, @@ -682,14 +594,7 @@ const createTargetedEntry = (owner, isolated, filters) => { if (owner === "extensions") { return { name, - args: [ - "vitest", - "run", - "--config", - "vitest.extensions.config.ts", - ...(forceForks ? ["--pool=forks"] : useVmForks ? ["--pool=vmForks"] : []), - ...filters, - ], + args: ["vitest", "run", "--config", "vitest.extensions.config.ts", ...filters], }; } if (owner === "gateway") { @@ -701,14 +606,7 @@ const createTargetedEntry = (owner, isolated, filters) => { if (owner === "channels") { return { name, - args: [ - "vitest", - "run", - "--config", - "vitest.channels.config.ts", - ...(forceForks ? ["--pool=forks"] : useVmForks ? ["--pool=vmForks"] : []), - ...filters, - ], + args: ["vitest", "run", "--config", "vitest.channels.config.ts", ...filters], }; } if (owner === "live") { @@ -747,9 +645,7 @@ const createPerFileTargetedEntry = (file) => { const target = inferTarget(file); const owner = isThreadPinnedUnitFile(file) ? "unit-threads" - : isVmForkPinnedUnitFile(file) - ? "unit-vmforks" - : isBaseThreadPinnedFile(file) + : isBaseThreadPinnedFile(file) ? "base-threads" : target.owner; return { @@ -768,9 +664,7 @@ const targetedEntries = (() => { const target = inferTarget(normalizedFile); const owner = isThreadPinnedUnitFile(normalizedFile) ? "unit-threads" - : isVmForkPinnedUnitFile(normalizedFile) - ? "unit-vmforks" - : isBaseThreadPinnedFile(normalizedFile) + : isBaseThreadPinnedFile(normalizedFile) ? "base-threads" : target.owner; const key = `${owner}:${target.isolated ? "isolated" : "default"}`; @@ -783,9 +677,7 @@ const targetedEntries = (() => { const target = inferTarget(matchedFile); const owner = isThreadPinnedUnitFile(matchedFile) ? "unit-threads" - : isVmForkPinnedUnitFile(matchedFile) - ? "unit-vmforks" - : isBaseThreadPinnedFile(matchedFile) + : isBaseThreadPinnedFile(matchedFile) ? "base-threads" : target.owner; const key = `${owner}:${target.isolated ? "isolated" : "default"}`; @@ -925,16 +817,13 @@ const maxWorkersForRun = (name) => { if (resolvedOverride) { return resolvedOverride; } - if (name === "unit-fork-batch" || name.startsWith("unit-fork-batch-")) { - return 1; - } if (isCI && !isMacOS) { return null; } if (isCI && isMacOS) { return 1; } - if (name.endsWith("-threads") || name.endsWith("-vmforks")) { + if (name.endsWith("-threads")) { return 1; } if (name.endsWith("-isolated")) { @@ -1012,12 +901,7 @@ const runOnce = (entry, extraArgs = []) => new Promise((resolve) => { const startedAt = Date.now(); const maxWorkers = maxWorkersForRun(entry.name); - // vmForks with a single worker has shown cross-file leakage in extension suites. - // Fall back to process forks when we intentionally clamp that lane to one worker. - const entryArgs = - entry.name === "extensions" && maxWorkers === 1 && entry.args.includes("--pool=vmForks") - ? entry.args.map((arg) => (arg === "--pool=vmForks" ? "--pool=forks" : arg)) - : entry.args; + const entryArgs = entry.args; const explicitEntryFilters = getExplicitEntryFilters(entryArgs); const args = maxWorkers ? [ diff --git a/scripts/test-runner-manifest.mjs b/scripts/test-runner-manifest.mjs index 2561f398d48..96189975b53 100644 --- a/scripts/test-runner-manifest.mjs +++ b/scripts/test-runner-manifest.mjs @@ -47,19 +47,13 @@ export function loadTestRunnerBehavior() { const raw = tryReadJsonFile(behaviorManifestPath, {}); const unit = raw.unit ?? {}; const base = raw.base ?? {}; - const extensions = raw.extensions ?? {}; return { base: { threadPinned: mergeManifestEntries(base, ["threadPinned", "threadSingleton"]), }, unit: { isolated: mergeManifestEntries(unit, ["isolated"]), - forkBatched: mergeManifestEntries(unit, ["forkBatched", "singletonIsolated"]), threadPinned: mergeManifestEntries(unit, ["threadPinned", "threadSingleton"]), - vmForkPinned: mergeManifestEntries(unit, ["vmForkPinned", "vmForkSingleton"]), - }, - extensions: { - isolated: mergeManifestEntries(extensions, ["isolated", "singletonIsolated"]), }, }; } diff --git a/test/fixtures/test-parallel.behavior.json b/test/fixtures/test-parallel.behavior.json index 2f1e4278da4..19cfc1a868a 100644 --- a/test/fixtures/test-parallel.behavior.json +++ b/test/fixtures/test-parallel.behavior.json @@ -22,7 +22,6 @@ "reason": "Touches process-level unhandledRejection listeners." } ], - "forkBatched": [], "threadPinned": [ { "file": "src/cli/secrets-cli.test.ts", @@ -184,10 +183,6 @@ "file": "src/media-understanding/runner.video.test.ts", "reason": "Measured ~25% faster under threads than forks on this host while keeping the file green." } - ], - "vmForkPinned": [] - }, - "extensions": { - "isolated": [] + ] } } diff --git a/test/scripts/test-find-thread-candidates.test.ts b/test/scripts/test-find-thread-candidates.test.ts index 8cf761c75e8..30d58bdd4ce 100644 --- a/test/scripts/test-find-thread-candidates.test.ts +++ b/test/scripts/test-find-thread-candidates.test.ts @@ -49,20 +49,10 @@ describe("scripts/test-find-thread-candidates exclusions", () => { }, unit: { isolated: [{ file: "src/a.test.ts" }], - forkBatched: [{ file: "src/b.test.ts" }], threadPinned: [{ file: "src/c.test.ts" }], - vmForkPinned: [{ file: "src/d.test.ts" }], }, }), - ).toEqual( - new Set([ - "src/base-a.test.ts", - "src/a.test.ts", - "src/b.test.ts", - "src/c.test.ts", - "src/d.test.ts", - ]), - ); + ).toEqual(new Set(["src/base-a.test.ts", "src/a.test.ts", "src/c.test.ts"])); }); it("keeps backward-compatible aliases readable", () => { @@ -73,20 +63,10 @@ describe("scripts/test-find-thread-candidates exclusions", () => { }, unit: { isolated: [{ file: "src/a.test.ts" }], - singletonIsolated: [{ file: "src/b.test.ts" }], threadSingleton: [{ file: "src/c.test.ts" }], - vmForkSingleton: [{ file: "src/d.test.ts" }], }, }), - ).toEqual( - new Set([ - "src/base-a.test.ts", - "src/a.test.ts", - "src/b.test.ts", - "src/c.test.ts", - "src/d.test.ts", - ]), - ); + ).toEqual(new Set(["src/base-a.test.ts", "src/a.test.ts", "src/c.test.ts"])); }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 0f554aff281..2341006a3df 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -31,9 +31,9 @@ export default defineConfig({ testTimeout: 120_000, hookTimeout: isWindows ? 180_000 : 120_000, // Many suites rely on `vi.stubEnv(...)` and expect it to be scoped to the test. - // This is especially important under `pool=vmForks` where env leaks cross-file. + // Keep env restoration automatic so shared-worker runs do not leak state. unstubEnvs: true, - // Same rationale as unstubEnvs: avoid cross-test pollution under vmForks. + // Same rationale as unstubEnvs: avoid cross-test pollution from shared globals. unstubGlobals: true, pool: "forks", maxWorkers: isCI ? ciWorkers : localWorkers, diff --git a/vitest.e2e.config.ts b/vitest.e2e.config.ts index 67e7cada10e..c9656cc2936 100644 --- a/vitest.e2e.config.ts +++ b/vitest.e2e.config.ts @@ -21,8 +21,7 @@ export default defineConfig({ ...base, test: { ...baseTest, - // vmForks reuses VM contexts in ways that can leak module state/mocks across - // files for our e2e harnesses. Use process forks for deterministic isolation. + // Keep e2e in process forks for deterministic cross-file isolation. pool: "forks", maxWorkers: e2eWorkers, silent: !verboseE2E, diff --git a/vitest.scoped-config.ts b/vitest.scoped-config.ts index 30eb2c87e4b..4f0ade86afd 100644 --- a/vitest.scoped-config.ts +++ b/vitest.scoped-config.ts @@ -3,12 +3,11 @@ import baseConfig from "./vitest.config.ts"; export function createScopedVitestConfig( include: string[], - options?: { exclude?: string[]; pool?: "threads" | "forks" | "vmForks" }, + options?: { exclude?: string[]; pool?: "threads" | "forks" }, ) { const base = baseConfig as unknown as Record; - const baseTest = - (baseConfig as { test?: { exclude?: string[]; pool?: "threads" | "forks" | "vmForks" } }) - .test ?? {}; + const baseTest = (baseConfig as { test?: { exclude?: string[]; pool?: "threads" | "forks" } }) + .test ?? {}; const exclude = [...(baseTest.exclude ?? []), ...(options?.exclude ?? [])]; return defineConfig({