test(hygiene): no tracked file may carry a retired-repository reference (red)

A node:test pin that scans every tracked text file and path for the terms
in a gitignored local list (tests/.excluded-terms.local.txt), so the terms
never live in tracked text. The scanner is validated against a planted
known-positive (content and path name), an empty scan fails closed, the
named EXEMPT list is empty and rot-checked, and without the local list the
scan is skipped with an explicit message.

Red on 0be152a with the list present: 12 hits in 7 files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 12:25:43 +02:00
commit e1637c2530
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q

View file

@ -0,0 +1,91 @@
// tests/lib/retired-references.test.mjs
// No tracked text file, and no tracked path, may contain a term from the
// local-only list in tests/.excluded-terms.local.txt (dead references to
// retired repositories). The list is gitignored on purpose: the terms never
// live in tracked text. Without the file the scan is skipped, loudly.
//
// When this test fails, rewrite the hit neutrally (describe the pattern, not
// the repository); do NOT widen EXEMPT to hide it.
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { execFileSync } from 'node:child_process';
import { existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = join(HERE, '..', '..');
const TERMS_FILE = join(ROOT, 'tests', '.excluded-terms.local.txt');
const MISSING = `${TERMS_FILE} not found; retired-reference scan not run`;
// Named exceptions: tracked path -> reason. Empty on purpose; an entry must
// still hit (a stale exemption fails below).
const EXEMPT = new Map([]);
function loadTerms() {
return readFileSync(TERMS_FILE, 'utf-8').split('\n')
.map(l => l.trim().toLowerCase())
.filter(l => l && !l.startsWith('#'));
}
function trackedFiles(root) {
const out = execFileSync('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf-8' });
return out.split('\0').filter(Boolean);
}
// Returns one "path:line" (or "path: <path name>") entry per hit. The matched
// text is not echoed, so a failure message never repeats a term.
function scan(root, files, terms) {
const hits = [];
const hit = s => { const l = s.toLowerCase(); return terms.some(t => l.includes(t)); };
for (const rel of files) {
if (hit(rel)) hits.push(`${rel}: <path name>`);
const buf = readFileSync(join(root, rel));
if (buf.includes(0)) continue; // binary
buf.toString('utf-8').split('\n').forEach((line, i) => {
if (hit(line)) hits.push(`${rel}:${i + 1}`);
});
}
return hits;
}
test('scan finds a planted term in content and in a path name (known-positive)', (t) => {
if (!existsSync(TERMS_FILE)) return t.skip(MISSING);
const terms = loadTerms();
assert.ok(terms.length > 0, `${TERMS_FILE} holds no terms`);
const dir = mkdtempSync(join(tmpdir(), 'retired-refs-'));
try {
const named = `${terms[0]}-notes.txt`;
writeFileSync(join(dir, 'clean.md'), 'nothing to see\n');
writeFileSync(join(dir, 'content.md'), `line one\nsee ${terms[0].toUpperCase()} here\n`);
writeFileSync(join(dir, named), 'clean body\n');
const hits = scan(dir, ['clean.md', 'content.md', named], terms);
assert.deepEqual(hits, ['content.md:2', `${named}: <path name>`]);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
test('the term list itself is not tracked', () => {
const tracked = trackedFiles(ROOT);
assert.ok(!tracked.includes('tests/.excluded-terms.local.txt'));
});
test('no tracked file or path contains a retired term', (t) => {
if (!existsSync(TERMS_FILE)) return t.skip(MISSING);
const files = trackedFiles(ROOT);
assert.ok(files.length > 100, `git ls-files returned ${files.length} files; refusing to pass on an empty scan`);
const hits = scan(ROOT, files.filter(f => !EXEMPT.has(f)), loadTerms());
assert.deepEqual(hits, [], `${hits.length} retired reference(s):\n${hits.join('\n')}`);
});
test('every EXEMPT entry is tracked and still hits', (t) => {
if (!existsSync(TERMS_FILE)) return t.skip(MISSING);
const files = new Set(trackedFiles(ROOT));
for (const rel of EXEMPT.keys()) {
assert.ok(files.has(rel), `EXEMPT names untracked ${rel}`);
assert.ok(scan(ROOT, [rel], loadTerms()).length > 0, `EXEMPT entry ${rel} no longer hits; remove it`);
}
});