Closes devils-advocate audit §Top changes #8 (MINOR×MED). Reports the behavior-test count separately from the doc-consistency prose-pin count so the cited total no longer oversells behavior coverage, and removes the clearest reword-fragile redundancies. Conservative scope (operator-chosen): no genuine regression guard dropped. 1. Suite census (report separately). lib/util/test-census.mjs + tests/lib/test-census.test.mjs walk tests/**/*.test.mjs and bucket top-level test() declarations into behavior vs doc-consistency-pins (bucket per file, regex PIN_FILE_RE), asserting the two sum to the total so neither can drift silently. Split emitted as a t.diagnostic: behavior=601 doc-consistency-pins=69. Metric = top-level declarations; node:test's runtime total counts subtests too and is therefore >= it. 2. Conservative prune (3 tests, each provably subsumed): - "trekexecute.md still parses v1.7 plan schema" — tautological OR-chain; real coverage = the plan_version:1.7 template pin + plan-validator / plan-schema behavior tests. - "CLAUDE.md mentions all six pipeline commands" — hardcoded six-string list subsumed by the filesystem-driven "commands table mentions every commands/*.md file" structural pin. - "CLAUDE.md mentions /trekcontinue command" — same subsumption. Each removal leaves an in-place note (why + where coverage lives). 3. doc-consistency.test.mjs header now documents the structural-invariant vs prose/existence-pin distinction so new pins land in the right kind. TDD: census test written failing-first (ERR_MODULE_NOT_FOUND before lib/util/test-census.mjs existed). Suite 699->698 (696 pass / 2 skip / 0 fail): -3 prune +2 census. plugin validate passes (1 accepted warning). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
49 lines
1.9 KiB
JavaScript
49 lines
1.9 KiB
JavaScript
// lib/util/test-census.mjs
|
|
// Census of the test suite: split top-level test() declarations into
|
|
// "behavior" tests vs "doc-consistency pins" (string/existence assertions that
|
|
// pin documentation against a source-of-truth). Makes the cited test count
|
|
// honest — a prose-pin is not the same coverage as a behavior test, so a single
|
|
// conflated total oversells behavior coverage.
|
|
// Devil's-advocate audit §Top changes #8 (S19).
|
|
//
|
|
// Metric: top-level `test(` declarations (static, deterministic, in-process).
|
|
// This is distinct from node:test's runtime total, which additionally counts
|
|
// subtests; the runtime total is therefore ≥ this declaration count.
|
|
|
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
// Files whose tests are documentation pins, not behavior coverage. Kept as a
|
|
// regex (not a single filename) so a future split-out (e.g. prose-pins.test.mjs)
|
|
// is bucketed correctly without editing this module.
|
|
export const PIN_FILE_RE = /(doc-consistency|prose-pins)\.test\.mjs$/;
|
|
|
|
function walk(dir) {
|
|
const out = [];
|
|
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
const p = join(dir, e.name);
|
|
if (e.isDirectory()) out.push(...walk(p));
|
|
else if (e.isFile() && e.name.endsWith('.test.mjs')) out.push(p);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function countTests(file) {
|
|
return (readFileSync(file, 'utf-8').match(/^\s*test\(/gm) || []).length;
|
|
}
|
|
|
|
// Returns { behavior, docPins, total, byFile } for all *.test.mjs under
|
|
// testsRoot. behavior + docPins === total by construction.
|
|
export function censusTests(testsRoot) {
|
|
const files = walk(testsRoot).sort();
|
|
const byFile = {};
|
|
let behavior = 0;
|
|
let docPins = 0;
|
|
for (const f of files) {
|
|
const n = countTests(f);
|
|
byFile[f] = n;
|
|
if (PIN_FILE_RE.test(f)) docPins += n;
|
|
else behavior += n;
|
|
}
|
|
return { behavior, docPins, total: behavior + docPins, byFile };
|
|
}
|