import { defineConfig } from "vitest/config"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.dirname(fileURLToPath(import.meta.url)); export default defineConfig({ resolve: { alias: { // ORDER MATTERS: Vite tries these in insertion order, so the more specific // prefix has to come first. With "@" first, `@/features/os-shell` resolved to // /features/os-shell — a path that does not exist — and any test whose // module graph touched a slice barrel failed with "Cannot find package". // tsconfig had it right; only the test runner disagreed. "@/features": path.join(root, "frontend", "slices"), "@": root, }, }, test: { // Shell/systemd lifecycle fixtures can exceed Vitest's 5s default under the full parallel gate. testTimeout: 15_000, // Cache transformed modules INSIDE THIS CHECKOUT, not in the shared // `node_modules/.vitest-cache`. // // Agent worktrees symlink `node_modules` to this canonical checkout (so every // worktree reuses one install). Vitest's default cache path resolves through that // symlink, so two gates running at once — a worktree's pre-push and this repo's — // share one cache directory and race on rmdir: // // Error: ENOTEMPTY: directory not empty, rmdir '.../node_modules/.vitest-cache' // // That failed a push three times with a message that looked like a code problem. // Keying the cache by checkout root makes concurrent gates independent; the cache // is still reused within one checkout. fsModuleCachePath: path.join(root, "node_modules", ".vitest-cache", path.basename(root)), // `zz-*` is the agreed scratch prefix: agent/debug harnesses that must never // run in CI, never be committed, and never break a push. Also excluded from // tsconfig and gitignored — three places, because a leftover has broken this // repo three separate ways (a CI typecheck, a would-be commit, and a file that // minted a real session cookie to disk). exclude: ["**/node_modules/**", "**/.next/**", "**/zz-*"], include: [ "app/**/*.test.{ts,tsx}", "frontend/slices/**/*.test.{ts,tsx}", "lib/**/*.test.{ts,tsx}", "bin/**/*.test.{ts,tsx}", "scripts/**/*.test.{ts,tsx}", "instrumentation.test.ts", // Root-level modules Next requires by name (middleware is `proxy.ts` in 16), // so their tests are colocated at the root and named one by one. NAMED, not // globbed — a root test file that is not listed here is silently never run, // which is worse than having no test at all, because the suite still says green. "proxy.test.ts", "proxy-websocket.test.ts", "proxy-machine-protocol.test.ts", ], environment: "node", // `bun run coverage`, and it IS in `verify`, so these are gates. // // The numbers are the CURRENT floor minus a hair, not a target. They used to // read 50/40/50/50 with a comment calling them "real gates" while real coverage // was 19% and nothing ran them — an aspiration written as a fact, which is the // worse of the two failure modes: it reads as covered. Raise these when you add // tests; never lower them to make a push go through. coverage: { provider: "v8", reporter: ["text", "json-summary"], include: ["lib/**/*.{ts,tsx}", "frontend/slices/**/*.{ts,tsx}"], exclude: [ "**/*.test.{ts,tsx}", "**/*.d.ts", "**/node_modules/**", "**/.next/**", ], thresholds: { // Ratcheted from the September audit baseline: 42.29 / 40.04 / 32.76 / 44.00. statements: 42, branches: 40, functions: 32, lines: 43.5, }, }, }, });