# Extension test tiers Tests are routed to a Vitest project by the **folder** they live in, not by the extension name. Pick the folder by how the test behaves, so a new extension needs no change to `vitest.config.ts`. | Folder | Project | Isolation | Put a test here when it… | | --- | --- | --- | --- | | `__tests__/` | `unit` | shared process, parallel | is pure and deterministic: no module mocks, no global/env mutation left behind. Most tests. | | `__tests__/contract.test.ts` | `contract` | per-file process.env sandbox | is the required owned-fixture contract test (see `AGENTS.md`). One per extension. | | `__serial__/` | `isolated-serial` | fresh module state, one worker | calls `vi.mock(...)`, or needs a single worker for real subprocesses / fake-command `PATH` / `process.chdir`. | | `__e2e__/` | Playwright | browser | drives rendered browser assets. | | `__fuzz__/` | campaign | — | hostile-input fuzz targets (`.fuzz.ts`) plus their `regressions/`. | | `__bench__/` | bench | — | benchmarks. | ## The rule that matters The `unit` project runs **non-isolated** across files in a shared worker, so a `vi.mock(...)` persists in the module registry and leaks into the next file. `//:check` fails any `__tests__/*.test.ts` (except `contract.test.ts`) that calls `vi.mock`, with a pointer to `__serial__/`. Global or env stubs (`vi.stubGlobal`, `vi.stubEnv`) are fine in `__tests__/` **only if** restored in cleanup; otherwise move them to `__serial__/` too. Spawning real subprocesses is allowed in `__tests__/` (scope `cwd`/`env` per call); move to `__serial__/` if contention under parallelism is a problem. ## Coverage, mutation, fuzz - `mise run //extensions/:coverage` — V8 coverage over the deterministic projects, scoped to the extension. - `mise run //extensions/:mutate ` — Stryker, per-extension cache. - `mise run //extensions/:fuzz ` — fuzz campaign; minimized findings become replay tests next to the target.