/** * Coverage gate for CI. * * Reads coverage-summary.json (Vitest --coverage output) and fails * if overall line coverage drops below the configured threshold. * * Usage: * pnpm test:coverage * node scripts/ci/check-coverage-delta.mjs * * Environment variable overrides: * COVERAGE_THRESHOLD – minimum line coverage % (default: 65) * COVERAGE_FILE – path to coverage-summary.json */ import { readFileSync } from "node:fs"; const THRESHOLD = Number(process.env.COVERAGE_THRESHOLD ?? 65); const COVERAGE_FILE = process.env.COVERAGE_FILE ?? "coverage/coverage-summary.json"; let summary; try { summary = JSON.parse(readFileSync(COVERAGE_FILE, "utf8")); } catch (err) { console.error(`Could not read ${COVERAGE_FILE}: ${err.message}`); console.error("Run `pnpm test:coverage` first."); process.exit(1); } const total = summary.total; if (!total) { console.error(`No "total" key in ${COVERAGE_FILE}`); process.exit(1); } const linePct = total.lines?.pct ?? 0; const branchPct = total.branches?.pct ?? 0; const fnPct = total.functions?.pct ?? 0; const stmtPct = total.statements?.pct ?? 0; console.log("\n--- Coverage Report ---\n"); console.log(` Lines: ${linePct.toFixed(2)}%`); console.log(` Branches: ${branchPct.toFixed(2)}%`); console.log(` Functions: ${fnPct.toFixed(2)}%`); console.log(` Statements: ${stmtPct.toFixed(2)}%`); console.log(`\n Threshold: ${THRESHOLD}% (lines)\n`); if (linePct < THRESHOLD) { console.error( `FAILED: Line coverage ${linePct.toFixed(2)}% is below the ${THRESHOLD}% threshold` ); process.exit(1); } else { console.log( `PASSED: Line coverage ${linePct.toFixed(2)}% meets the ${THRESHOLD}% threshold` ); }