Problem class: vitest-test-cwd-relative-path-resolution-enoent
The verified solution is written to /workspace/solution.md. Full content:
vitest-test-cwd-relative-path-resolution-enoentProblem class: vitest-test-cwd-relative-path-resolution-enoent
Affected file: apps/api/src/modules/safety/__tests__/safety-eval.test.ts (QA‑EDUOS‑29)
Symptom: ENOENT when the suite resolves its baseline/fixture via path.resolve(process.cwd(), ...), but only when Vitest is launched from a directory other than the canonical checkout.
The test built its fixture path from the process current working directory:
const reportPath = path.resolve(process.cwd(), '../../specs/baselines/...');
process.cwd() is whatever directory the runner was launched from — it is not the test file's location and it is not changed by Vitest's root/--root option.
--root only tells the runner where the project/config is. The worker's process.cwd() still points at the shell's cwd.apps/api, a foreign cwd with --root, CI shards, or pnpm exec from another package. Each yields a different reportPath.apps/api, so ../../specs lands on the repo root) and masks the bug; every other cwd resolves outside the repo and readFileSync throws ENOENT.The path is a function of cwd, when it should be a function of the test file.
process.cwd()-relative ../../specs/baselines/safety-baseline.json:
| Launch cwd | Resolved path | Result |
|---|---|---|
repo root (/…/eduos) |
/tmp/specs/baselines/… |
❌ ENOENT |
apps/api |
/…/eduos/specs/baselines/… |
✅ (accidental pass) |
foreign cwd + --root /…/eduos |
/specs/baselines/… |
❌ ENOENT |
The single pass is coincidental — exactly the "works on my invocation" trap.
Anchor the path to the test module using ESM's import.meta.url, then climb the fixed number of levels to the repo root.
apps/api/src/modules/safety/__tests__/safety-eval.test.ts:
// BEFORE (buggy — depends on the launch cwd)
const reportPath = path.resolve(process.cwd(), '../../specs/baselines/...');
// AFTER (cwd-independent)
import path from 'node:path';
import { fileURLToPath } from 'node:url';
// __dirname does not exist in ESM; derive it from import.meta.url.
const testDir = path.dirname(fileURLToPath(import.meta.url));
// apps/api/src/modules/safety/__tests__ -> repo root = six levels up
// 1 .. safety 2 .. modules 3 .. src
// 4 .. api 5 .. apps 6 .. repo root
const repoRoot = path.resolve(testDir, '../../../../../..');
const reportPath = path.resolve(repoRoot, 'specs/baselines/safety-baseline.json');
If the file already imports path, keep it and only add the node:url import.
apps/api/src/modules/safety/__tests__/safety-eval.test.ts
└── testDir = …/apps/api/src/modules/safety/__tests__
../../../.. #1–4 → …/apps/api/src
../.. #5–6 → …/apps → repo root
The literal with six .. is '../../../../../..'. Sanity check:
node -e "const p=require('path');console.log(p.resolve('apps/api/src/modules/safety/__tests__','../../../../../..'))"
# prints the absolute repo root
A fixed climb breaks if the file moves. Walk up to a repo marker instead:
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const testDir = path.dirname(fileURLToPath(import.meta.url));
function findRepoRoot(start: string): string {
let dir = start;
while (dir !== path.dirname(dir)) {
if (
fs.existsSync(path.join(dir, 'pnpm-workspace.yaml')) ||
fs.existsSync(path.join(dir, '.git'))
) {
return dir;
}
dir = path.dirname(dir);
}
throw new Error(`Could not locate repo root above ${start}`);
}
const reportPath = path.resolve(
findRepoRoot(testDir),
'specs/baselines/safety-baseline.json',
);
Use the markers your repo actually contains. Prefer the six-level climb for a minimal diff; prefer the marker walk if the test is likely to move.
process.cwd() in a test.path.dirname(fileURLToPath(import.meta.url)).import.meta.url in the test file (or a module colocated at the same depth); don't push it into a util at a different depth.path.resolve/path.join handle separators — no string concatenation.REPO=/path/to/eduos.dexdat.com.co
TESTDIR="$REPO/apps/api/src/modules/safety/__tests__"
node -e "
const p = require('path'), fs = require('fs');
const repoRoot = p.resolve('$TESTDIR', '../../../../../..');
const reportPath = p.resolve(repoRoot, 'specs/baselines/safety-baseline.json');
console.log({ repoRoot, reportPath, exists: fs.existsSync(reportPath) });
"
repoRoot must be the absolute checkout root and exists: true.
# 1) repo root
cd /path/to/eduos.dexdat.com.co
pnpm --filter @eduos/api exec vitest run src/modules/safety/__tests__/safety-eval.test.ts
# 2) apps/api
cd /path/to/eduos.dexdat.com.co/apps/api
pnpm exec vitest run src/modules/safety/__tests__/safety-eval.test.ts
# 3) foreign cwd with an explicit --root (worst case)
cd /tmp/foreign
pnpm --dir /path/to/eduos.dexdat.com.co exec vitest run \
--root /path/to/eduos.dexdat.com.co \
apps/api/src/modules/safety/__tests__/safety-eval.test.ts
All three must report Tests 1 passed (1) (or the full suite passing) with no ENOENT.
Fixture layout and suite were reproduced locally (Vitest 3.2.7, Node 22). Observed output:
===== FIXED from repo root =====
[fixed] cwd=/tmp/repro/eduos repoRoot=/tmp/repro/eduos resolved=/tmp/repro/eduos/specs/baselines/safety-baseline.json
Test Files 1 passed (1)
Tests 1 passed (1)
===== FIXED from apps/api =====
[fixed] cwd=/tmp/repro/eduos/apps/api repoRoot=/tmp/repro/eduos resolved=/tmp/repro/eduos/specs/baselines/safety-baseline.json
Test Files 1 passed (1)
Tests 1 passed (1)
===== FIXED from foreign cwd + --root =====
[fixed] cwd=/tmp/foreign repoRoot=/tmp/repro/eduos resolved=/tmp/repro/eduos/specs/baselines/safety-baseline.json
Test Files 1 passed (1)
Tests 1 passed (1)
And the broken process.cwd() version for contrast:
===== BROKEN from repo root =====
[broken] cwd=/tmp/repro/eduos resolved=/tmp/specs/baselines/safety-baseline.json exists=false
Test Files 1 failed (1)
===== BROKEN from apps/api =====
[broken] cwd=/tmp/repro/eduos/apps/api resolved=/tmp/repro/eduos/specs/baselines/safety-baseline.json exists=true
Test Files 1 passed (1)
===== BROKEN from foreign cwd + --root =====
[broken] cwd=/tmp/foreign resolved=/specs/baselines/safety-baseline.json exists=false
Test Files 1 failed (1)
safety-eval.test.ts contains no process.cwd() for path resolution.import.meta.url.apps/api, and from a foreign cwd with --root pointing at the repo.cd into package directories remain green.process.cwd() (grep the module and fix them together).# Evidence - Problem class: vitest-test-cwd-relative-path-resolution-enoent - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-21T00:15:47.176Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Test suites that resolve fixture/baseline files via path.resolve(process.cwd(), relative-climb) fail with ENOENT when the suite is invoked from any cwd other than the canonical checkout (e.g. vitest run from apps/api with a ../../ climb, or pnpm exec from a different package dir). Fix pattern: anchor to the test file itself via path.dirname(fileURLToPath(import.meta.url)) (ESM/vitest: import.meta.url) and compute the repo-relative climb from there. Repo: dexdat/eduos.dexdat.com.co, apps/api/src/modules/safety/__tests__/safety-eval.test.ts (QA-EDUOS-29): reportPath was process.cwd()-relative \"../../specs/baselines/...\"; fixed with import.meta.url + six-level climb; suite now passes from apps/api AND repo root AND a foreign /tmp-rooted --root invocation.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "vitest-test-cwd-relative-path-resolution-enoent", "provider": "openrouter", "solved_at": "2026-09-21T00:15:47.176Z", "version": ""}