The gate reads `tests/.excluded-terms.local.json` (gitignored via `*.local.json`): a pattern plus known positives and negatives. With the list present it fails on any tracked text line that matches; without it both tests are skipped with a message, so a fresh clone stays green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
2.6 KiB
JavaScript
65 lines
2.6 KiB
JavaScript
// tests/kb-update/test-excluded-terms.test.mjs
|
|
// Preventive gate: no tracked text file may contain a term from the
|
|
// maintainer's excluded-terms list. Examples, fixtures and placeholders use
|
|
// generic, fictitious material instead.
|
|
//
|
|
// The list lives in `tests/.excluded-terms.local.json` (gitignored via
|
|
// `*.local.json`) and holds `pattern`, `flags`, `positives` and `negatives`.
|
|
// Without that file the gate is skipped with a message, so a fresh clone runs
|
|
// green; with it, the known positives prove the pattern can find before the
|
|
// empty result over the tree is trusted.
|
|
//
|
|
// Population: `git ls-files` (tracked files only). Binary files (NUL byte in
|
|
// the first 8000 bytes, as git decides) are skipped: text in a screenshot is
|
|
// rendered pixels, so a byte match there says nothing about what it shows.
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { existsSync, readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const pluginRoot = fileURLToPath(new URL('../../', import.meta.url));
|
|
const listPath = fileURLToPath(new URL('../.excluded-terms.local.json', import.meta.url));
|
|
|
|
const skip = existsSync(listPath)
|
|
? false
|
|
: 'tests/.excluded-terms.local.json not present (local-only list) — gate skipped';
|
|
|
|
function loadList() {
|
|
const list = JSON.parse(readFileSync(listPath, 'utf8'));
|
|
return { ...list, regex: new RegExp(list.pattern, list.flags ?? '') };
|
|
}
|
|
|
|
function trackedFiles() {
|
|
return execFileSync('git', ['ls-files', '-z'], { cwd: pluginRoot, encoding: 'utf8' })
|
|
.split('\0')
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function isBinary(buf) {
|
|
return buf.subarray(0, 8000).includes(0);
|
|
}
|
|
|
|
test('pattern matches every known positive and no known negative', { skip }, () => {
|
|
const { regex, positives = [], negatives = [] } = loadList();
|
|
assert.ok(positives.length > 0, 'the list must carry known positives');
|
|
for (const s of positives) assert.match(s, regex, s);
|
|
for (const s of negatives) assert.doesNotMatch(s, regex, s);
|
|
});
|
|
|
|
test('no tracked text file contains an excluded term', { skip }, () => {
|
|
const { regex } = loadList();
|
|
const files = trackedFiles();
|
|
assert.ok(files.length > 500, `git ls-files returned ${files.length} files — population too small to trust`);
|
|
const hits = [];
|
|
for (const rel of files) {
|
|
let buf;
|
|
try { buf = readFileSync(pluginRoot + rel); } catch { continue; }
|
|
if (isBinary(buf)) continue;
|
|
buf.toString('utf8').split('\n').forEach((line, i) => {
|
|
if (regex.test(line)) hits.push(`${rel}:${i + 1}`);
|
|
});
|
|
}
|
|
assert.deepEqual(hits, [], `excluded terms in tracked files:\n ${hits.join('\n ')}`);
|
|
});
|