mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
256 lines
18 KiB
TypeScript
256 lines
18 KiB
TypeScript
import { defineConfig } from 'vitest/config';
|
||
import swc from 'unplugin-swc';
|
||
|
||
export default defineConfig({
|
||
// SWC transform so NestJS decorator metadata is emitted in tests
|
||
// (vitest's default esbuild does not emit it -> type-based DI would break).
|
||
plugins: [
|
||
swc.vite({
|
||
// unplugin-swc's default filter is /\.m?[jt]sx?$/ — anchored at the end of the
|
||
// id, so anything carrying a query string skips the SWC transform. The istanbul
|
||
// provider's uncovered-file pass asks vite for every never-imported source as
|
||
// `<file>.ts?cache=<n>&vitest-uncovered-coverage=true`; with the default filter
|
||
// those ids come back as raw TypeScript and babel's instrumenter (no TS or
|
||
// decorator plugins) dies on the first `as const`/`interface`/`@Global()`.
|
||
// Match the same extensions with an optional query so those ids are transformed
|
||
// exactly like the imported ones.
|
||
include: /\.[cm]?[jt]sx?(\?.*)?$/,
|
||
jsc: {
|
||
parser: { syntax: 'typescript', decorators: true },
|
||
transform: { legacyDecorator: true, decoratorMetadata: true },
|
||
keepClassNames: true,
|
||
},
|
||
}),
|
||
],
|
||
test: {
|
||
root: '.',
|
||
include: ['tests/**/*.test.ts'],
|
||
globals: true,
|
||
// Migrates one database for the whole run and snapshots it; workers open a
|
||
// copy instead of replaying 242 migrations per file. See tests/global-setup.ts.
|
||
globalSetup: ['tests/global-setup.ts'],
|
||
setupFiles: ['tests/setup.ts', 'tests/setup.console-noise.ts'],
|
||
testTimeout: 15000,
|
||
hookTimeout: 15000,
|
||
pool: 'forks',
|
||
// Console output is kept for failing tests and dropped for passing ones: a
|
||
// green run used to print every e2e boot and every migrator line (~100k
|
||
// lines) and a red one buried its failure in them. `default` prints one line
|
||
// per file and the full detail only for failures; on CI the github-actions
|
||
// reporter adds inline annotations on the PR.
|
||
silent: 'passed-only',
|
||
reporters: process.env.CI ? ['default', 'github-actions'] : ['default'],
|
||
coverage: {
|
||
// Vite 8 + Vitest 4 made the sourcemap-based `v8` provider under-report branch
|
||
// coverage on the SWC/decorator-transformed output (it dropped to ~68% even
|
||
// though every test passes). `istanbul` instruments the source directly, so
|
||
// coverage is measured independently of the transform pipeline.
|
||
provider: 'istanbul',
|
||
// Serialises report processing: three identical runs otherwise gave 13/6/6
|
||
// threshold errors (files like platform/api-docs.ts flipping between 0% and
|
||
// 100%) because the default concurrency processes per-file coverage results
|
||
// out of order against istanbul's shared state.
|
||
processingConcurrency: 1,
|
||
// json-summary is what the per-domain ratchet below is derived from: the text
|
||
// reporter prints one row per DIRECTORY, not a recursive total, so reading the
|
||
// thresholds off it silently understates any domain with subdirectories.
|
||
reporter: ['lcov', 'text', 'json-summary'],
|
||
reportsDirectory: './coverage',
|
||
include: ['src/**/*.ts'],
|
||
// The plugin child bootstrap runs in a forked subprocess via tsx, so the
|
||
// parent's instrumentation can't measure it; it's exercised end-to-end by
|
||
// the supervisor integration test instead. Everything else in the plugin
|
||
// module runs in-process and is unit-tested.
|
||
exclude: ['src/nest/plugins/runtime/plugin-host-entry.ts'],
|
||
// Coverage gate scoped to the new NestJS code only — the legacy codebase is
|
||
// intentionally ungated.
|
||
//
|
||
// The bar is PER DOMAIN and works as a ratchet: each entry is that domain's
|
||
// measured coverage at the time it was set, minus one point of slack. Regenerate
|
||
// with `node scripts/coverage-thresholds.mjs` after a run that RAISED coverage. A single
|
||
// repo-wide 80% let a well-covered domain subsidise a thin one, so a domain could
|
||
// lose ten points and the build stayed green. Now it cannot: the only way past a
|
||
// number here is to raise it, which makes the ratchet visible in the diff.
|
||
//
|
||
// Raise an entry when you improve a domain. Never lower one to make a build pass —
|
||
// that is the whole point of the mechanism.
|
||
//
|
||
// Regenerate from a LINUX run if you can: CI runs on Linux, and a few branches are
|
||
// platform-specific (the drive-letter half of the backup zip-slip guard is only
|
||
// reachable on win32). Numbers taken on Windows can therefore sit a hair above what
|
||
// CI measures, which shows up as a threshold failure that reproduces nowhere local.
|
||
//
|
||
// A file matched by a glob below is EXCLUDED from the catch-all, so the last entry
|
||
// only covers what sits directly under src/nest (app.module.ts and the like).
|
||
thresholds: {
|
||
'src/nest/accommodations/**/*.ts': { statements: 92, branches: 87, functions: 99, lines: 97 },
|
||
'src/nest/addons/**/*.ts': { statements: 99, branches: 89, functions: 99, lines: 99 },
|
||
'src/nest/admin/**/*.ts': { statements: 86, branches: 70, functions: 89, lines: 88 },
|
||
'src/nest/airports/**/*.ts': { statements: 68, branches: 60, functions: 93, lines: 73 },
|
||
'src/nest/app-config/**/*.ts': { statements: 79, branches: 100, functions: 65, lines: 71 },
|
||
'src/nest/assignments/**/*.ts': { statements: 90, branches: 80, functions: 97, lines: 96 },
|
||
'src/nest/atlas/**/*.ts': { statements: 92, branches: 82, functions: 96, lines: 94 },
|
||
'src/nest/audit/**/*.ts': { statements: 93, branches: 80, functions: 99, lines: 96 },
|
||
'src/nest/auth/**/*.ts': { statements: 93, branches: 85, functions: 95, lines: 96 },
|
||
'src/nest/auth-core/**/*.ts': { statements: 89, branches: 83, functions: 94, lines: 89 },
|
||
'src/nest/backup/**/*.ts': { statements: 97, branches: 93, functions: 99, lines: 98 },
|
||
'src/nest/booking-import/**/*.ts': { statements: 50, branches: 28, functions: 64, lines: 53 },
|
||
'src/nest/budget/**/*.ts': { statements: 91, branches: 77, functions: 97, lines: 95 },
|
||
'src/nest/calendar/**/*.ts': { statements: 96, branches: 93, functions: 99, lines: 99 },
|
||
'src/nest/categories/**/*.ts': { statements: 99, branches: 80, functions: 99, lines: 99 },
|
||
'src/nest/collab/**/*.ts': { statements: 92, branches: 85, functions: 95, lines: 97 },
|
||
'src/nest/collections/**/*.ts': { statements: 86, branches: 75, functions: 95, lines: 95 },
|
||
'src/nest/common/**/*.ts': { statements: 95, branches: 89, functions: 99, lines: 97 },
|
||
'src/nest/config/**/*.ts': { statements: 99, branches: 100, functions: 99, lines: 99 },
|
||
'src/nest/database/**/*.ts': { statements: 99, branches: 100, functions: 99, lines: 99 },
|
||
'src/nest/day-notes/**/*.ts': { statements: 92, branches: 83, functions: 99, lines: 99 },
|
||
'src/nest/days/**/*.ts': { statements: 92, branches: 82, functions: 98, lines: 97 },
|
||
'src/nest/doc-sync/**/*.ts': { statements: 91, branches: 79, functions: 96, lines: 95 },
|
||
'src/nest/feeds/**/*.ts': { statements: 91, branches: 83, functions: 83, lines: 91 },
|
||
'src/nest/files/**/*.ts': { statements: 97, branches: 95, functions: 99, lines: 98 },
|
||
'src/nest/geo/**/*.ts': { statements: 99, branches: 95, functions: 99, lines: 99 },
|
||
// google-quota, managed, mcp-shared, mcp-transport, public-api,
|
||
// receipt-scan, route-usage, scheduling, school-holidays, tokens and tours
|
||
// sat on the 80 catch-all until 2026-10-08. Each is pinned one point under
|
||
// what the unit, integration and e2e suites that load it measure, as the
|
||
// script prints it: a subset a full run can only measure higher. managed holds an empty
|
||
// module and nothing to count. mcp-shared and route-usage measure below
|
||
// the 80 floor on branches and functions and are pinned where they are.
|
||
// tests/unit/coverage-thresholds.test.ts fails on a domain without an entry.
|
||
'src/nest/google-quota/**/*.ts': { statements: 99, branches: 99, functions: 99, lines: 99 },
|
||
'src/nest/health/**/*.ts': { statements: 99, branches: 65, functions: 99, lines: 99 },
|
||
'src/nest/help/**/*.ts': { statements: 81, branches: 70, functions: 99, lines: 86 },
|
||
'src/nest/integrations/**/*.ts': { statements: 78, branches: 68, functions: 78, lines: 80 },
|
||
'src/nest/journey/**/*.ts': { statements: 91, branches: 84, functions: 88, lines: 93 },
|
||
'src/nest/llm-parse/**/*.ts': { statements: 91, branches: 85, functions: 85, lines: 94 },
|
||
'src/nest/managed/**/*.ts': { statements: 100, branches: 100, functions: 100, lines: 100 },
|
||
'src/nest/maps/**/*.ts': { statements: 93, branches: 86, functions: 97, lines: 96 },
|
||
'src/nest/mcp-shared/**/*.ts': { statements: 84, branches: 59, functions: 99, lines: 88 },
|
||
'src/nest/mcp-transport/**/*.ts': { statements: 89, branches: 79, functions: 87, lines: 90 },
|
||
'src/nest/memories/**/*.ts': { statements: 92, branches: 83, functions: 97, lines: 94 },
|
||
'src/nest/notifications/**/*.ts': { statements: 83, branches: 72, functions: 85, lines: 87 },
|
||
'src/nest/oauth/**/*.ts': { statements: 96, branches: 95, functions: 97, lines: 97 },
|
||
'src/nest/oidc/**/*.ts': { statements: 88, branches: 83, functions: 91, lines: 92 },
|
||
'src/nest/packing/**/*.ts': { statements: 92, branches: 83, functions: 99, lines: 97 },
|
||
'src/nest/permissions/**/*.ts': { statements: 97, branches: 92, functions: 99, lines: 97 },
|
||
'src/nest/photos/**/*.ts': { statements: 96, branches: 92, functions: 92, lines: 96 },
|
||
// Landed on dev without an entry, so it sat on the 80 catch-all while
|
||
// measuring 99.5/99.0/97.4/100 — the gap the per-domain ratchet exists to
|
||
// close. Set with a few points of slack against the Linux/Windows drift.
|
||
'src/nest/place-enrichment/**/*.ts': { statements: 96, branches: 95, functions: 94, lines: 97 },
|
||
// New domain: the file and list readers that moved out of places/ and
|
||
// collections/. On the floor until a full CI run measures it; raise it
|
||
// with scripts/coverage-thresholds.mjs then, never lower it.
|
||
'src/nest/place-import/**/*.ts': { statements: 80, branches: 80, functions: 80, lines: 80 },
|
||
'src/nest/place-photos/**/*.ts': { statements: 87, branches: 79, functions: 72, lines: 89 },
|
||
// New domain in this change. Measured over its own suites at
|
||
// 98.3/89.8/96.2/100, and pinned well under that on purpose: the
|
||
// subset run does not load the controller the way the full run does,
|
||
// so the real figure is not this one. Regenerate with
|
||
// scripts/coverage-thresholds.mjs after a full run. What matters now is
|
||
// that it is off the 80 catch-all, where a new domain could shed twenty
|
||
// points without anything noticing.
|
||
'src/nest/place-shadow/**/*.ts': { statements: 90, branches: 80, functions: 90, lines: 95 },
|
||
'src/nest/places/**/*.ts': { statements: 91, branches: 82, functions: 96, lines: 94 },
|
||
'src/nest/platform/**/*.ts': { statements: 99, branches: 99, functions: 99, lines: 99 },
|
||
'src/nest/plugins/**/*.ts': { statements: 86, branches: 81, functions: 78, lines: 89 },
|
||
'src/nest/public-api/**/*.ts': { statements: 97, branches: 86, functions: 99, lines: 99 },
|
||
'src/nest/query-helpers/**/*.ts': { statements: 90, branches: 75, functions: 99, lines: 92 },
|
||
'src/nest/realtime/**/*.ts': { statements: 99, branches: 100, functions: 99, lines: 99 },
|
||
'src/nest/receipt-scan/**/*.ts': { statements: 99, branches: 99, functions: 99, lines: 99 },
|
||
'src/nest/reservation-import/**/*.ts': { statements: 61, branches: 55, functions: 41, lines: 61 },
|
||
'src/nest/reservations/**/*.ts': { statements: 92, branches: 83, functions: 96, lines: 96 },
|
||
// New domain in this change. Measured over its own suites at
|
||
// 94.0/81.1/100/99.1 and pinned under that: the subset run does not
|
||
// load the controller the way the full run does, so the real figure is
|
||
// not this one. Regenerate with scripts/coverage-thresholds.mjs after a
|
||
// full run. What matters now is that it carries its own floor instead of
|
||
// hiding in the 80 catch-all, where 557 new lines could shed twenty
|
||
// points against the headroom of the domains beside them.
|
||
'src/nest/roadtrip/**/*.ts': { statements: 88, branches: 80, functions: 95, lines: 95 },
|
||
'src/nest/route-usage/**/*.ts': { statements: 84, branches: 66, functions: 77, lines: 83 },
|
||
'src/nest/scheduling/**/*.ts': { statements: 99, branches: 92, functions: 99, lines: 99 },
|
||
'src/nest/school-holidays/**/*.ts': { statements: 97, branches: 95, functions: 99, lines: 97 },
|
||
// New domain in this change, unmeasured: pinned at the 80 floor so it
|
||
// has its own entry rather than hiding in the catch-all. Its suites
|
||
// cover every branch they can reach; regenerate with
|
||
// scripts/coverage-thresholds.mjs after the first full run.
|
||
'src/nest/sessions/**/*.ts': { statements: 80, branches: 80, functions: 80, lines: 80 },
|
||
'src/nest/settings/**/*.ts': { statements: 87, branches: 71, functions: 99, lines: 88 },
|
||
'src/nest/share/**/*.ts': { statements: 97, branches: 87, functions: 99, lines: 99 },
|
||
'src/nest/storage/**/*.ts': { statements: 94, branches: 84, functions: 97, lines: 94 },
|
||
'src/nest/system-notices/**/*.ts': { statements: 99, branches: 99, functions: 99, lines: 99 },
|
||
'src/nest/tags/**/*.ts': { statements: 97, branches: 89, functions: 99, lines: 99 },
|
||
'src/nest/todo/**/*.ts': { statements: 90, branches: 82, functions: 99, lines: 99 },
|
||
'src/nest/tokens/**/*.ts': { statements: 99, branches: 97, functions: 99, lines: 99 },
|
||
'src/nest/tours/**/*.ts': { statements: 95, branches: 92, functions: 99, lines: 97 },
|
||
'src/nest/transit/**/*.ts': { statements: 92, branches: 83, functions: 97, lines: 94 },
|
||
'src/nest/trip-invite/**/*.ts': { statements: 91, branches: 93, functions: 93, lines: 89 },
|
||
'src/nest/trip-membership/**/*.ts': { statements: 99, branches: 86, functions: 99, lines: 99 },
|
||
'src/nest/trip-members/**/*.ts': { statements: 95, branches: 91, functions: 99, lines: 95 },
|
||
'src/nest/trip-read-model/**/*.ts': { statements: 97, branches: 96, functions: 99, lines: 97 },
|
||
'src/nest/trips/**/*.ts': { statements: 95, branches: 86, functions: 95, lines: 96 },
|
||
'src/nest/unsplash/**/*.ts': { statements: 99, branches: 88, functions: 99, lines: 99 },
|
||
'src/nest/vacay/**/*.ts': { statements: 82, branches: 66, functions: 90, lines: 86 },
|
||
'src/nest/weather/**/*.ts': { statements: 93, branches: 78, functions: 91, lines: 97 },
|
||
'src/nest/**/*.ts': { statements: 80, branches: 80, functions: 80, lines: 80 },
|
||
|
||
// Outside the container. Coverage MEASURES src/**, but until now only
|
||
// src/nest/** was gated, so the boot path, the scheduler, the MCP
|
||
// handler and the migrations sat under no floor at all — which is
|
||
// exactly where the worst finding of the last audit lived (a swallowed
|
||
// EADDRINUSE that no test could have caught). These entries pin what is
|
||
// measured today so it cannot slide; they are floors to raise, not
|
||
// targets that have been met.
|
||
//
|
||
// src/demo/** is deliberately absent: it measures 0%, and a floor of
|
||
// zero asserts nothing. It needs tests before it needs a threshold.
|
||
'src/app-config/**/*.ts': { statements: 99, branches: 95, functions: 99, lines: 99 },
|
||
// Hand-maintained (scripts/coverage-thresholds.mjs only emits src/nest/* lines): floor(measured) - 1 after Plan 2.
|
||
'src/db/**/*.ts': { statements: 84, branches: 42, functions: 95, lines: 87 },
|
||
'src/db/dialect/**/*.ts': { statements: 99, branches: 99, functions: 99, lines: 99 },
|
||
// Branches re-pinned at floor(measured) - 1 once Plans 3a–4 had grown the
|
||
// folder from the Plan 2 handful to 136 repositories (89.83% measured,
|
||
// identical locally and on CI); the 99 was set before any of them existed.
|
||
'src/db/repositories/**/*.ts': { statements: 99, branches: 88, functions: 99, lines: 99 },
|
||
'src/db/types/**/*.ts': { statements: 93, branches: 99, functions: 99, lines: 93 },
|
||
'src/mcp/**/*.ts': { statements: 58, branches: 43, functions: 63, lines: 60 },
|
||
'src/middleware/**/*.ts': { statements: 91, branches: 89, functions: 87, lines: 94 },
|
||
// The folded-in nest-mcp decorator/registry layer keeps the 80% floor
|
||
// its own workspace gate enforced (tests/unit/nest-mcp/).
|
||
'src/nest-mcp/**/*.ts': { statements: 80, branches: 80, functions: 80, lines: 80 },
|
||
// The plugin RPC kit (rpc-kit, rpc-params, rpc-errors, PluginGuards), moved
|
||
// out of src/nest/plugins with its tests (tests/unit/nest-rpc/).
|
||
'src/nest-rpc/**/*.ts': { statements: 92, branches: 85, functions: 95, lines: 94 },
|
||
'src/systemNotices/**/*.ts': { statements: 84, branches: 67, functions: 93, lines: 85 },
|
||
'src/utils/**/*.ts': { statements: 92, branches: 87, functions: 89, lines: 96 },
|
||
// index.ts, bootstrap.ts, scheduler.ts, config.ts, websocket.ts and the
|
||
// small top-level modules beside them.
|
||
'src/*.ts': { statements: 39, branches: 36, functions: 36, lines: 42 },
|
||
},
|
||
},
|
||
},
|
||
resolve: {
|
||
alias: {
|
||
// MCP SDK's exports map uses extension-less wildcard targets that neither
|
||
// Node nor Vite can resolve. Point directly at the CJS dist files.
|
||
// Paths are relative to the monorepo root (packages are hoisted there).
|
||
'@modelcontextprotocol/sdk/server/mcp': new URL(
|
||
'../node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js',
|
||
import.meta.url
|
||
).pathname,
|
||
'@modelcontextprotocol/sdk/server/streamableHttp': new URL(
|
||
'../node_modules/@modelcontextprotocol/sdk/dist/cjs/server/streamableHttp.js',
|
||
import.meta.url
|
||
).pathname,
|
||
'@modelcontextprotocol/sdk/inMemory': new URL(
|
||
'../node_modules/@modelcontextprotocol/sdk/dist/cjs/inMemory.js',
|
||
import.meta.url
|
||
).pathname,
|
||
'@modelcontextprotocol/sdk/client/index': new URL(
|
||
'../node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js',
|
||
import.meta.url
|
||
).pathname,
|
||
},
|
||
},
|
||
}); |