Files
gstack/test/testing-internals-anchors.test.ts
T
Garry Tanandcapy-ai[bot] 6417abf7a6 Move the tools only Capy crews use into a crew/ folder (GST-1833) (#4145)
* Load the Capy crew skills only on Capy machines (GST-1833)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Add changelog entry for Capy-only crew skills (GST-1833)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Move the Capy-only crew tooling into crew/ and run its tests only when needed (GST-1833)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Test that the Capy launch file puts crew/bin on PATH

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Count any ~/.capy as a Capy machine, so a Mac running Capy keeps /captain

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Fix the tests and fixtures that broke when crew tooling moved into crew/

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Plan every crew test when a pull request's merge-base can't be read

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Add a test that checks each crew-only module left the root tree for crew/

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Run crew tests on pull requests that change bin/ commands or workflows they drive

Review-Finding: p1/specialist-batched#1 reader=testing
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Keep running every crew test on every pull request; the skip comes in a later change

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Keep copied installs and older release bisects working after the crew/ move

Setup now copies crew/lib into every host's runtime root beside bin and lib, so Windows installs (which copy instead of linking) can still start gstack-ship-receipt, gstack-pregate and the other commands that import it. The release bisect runs the train-gate script from wherever the checked-out commit has it, so a bisect over commits from before the move still gets a verdict.

Review-Finding: p1/outside-voice#1 reader=outside-voice
Review-Finding: p1/outside-voice#2 reader=outside-voice
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Say in the changelog that Windows installs keep working after the crew/ move

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Point the harness guide's merge-queue rows at crew/lib/ where those modules moved

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Point the plain-title test at the wave module's new home in crew/ (GST-1833)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-08 14:26:14 +00:00

42 lines
1.9 KiB
TypeScript

/**
* Tripwire and fix messages point readers at docs/TESTING_INTERNALS.md#<anchor>.
* Every such anchor in code must match a heading there, or the fix text sends
* the reader to the top of a 900-line page.
*/
import { expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as path from 'node:path';
const ROOT = path.resolve(import.meta.dir, '..');
const DOC = 'docs/TESTING_INTERNALS.md';
const SCAN = ['scripts', 'test', 'lib', 'bin', 'crew', '.github'];
function githubSlug(heading: string): string {
return heading.trim().toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu, '').replace(/\s/g, '-');
}
function walk(dir: string): string[] {
const out: string[] = [];
for (const entry of fs.readdirSync(path.join(ROOT, dir), { withFileTypes: true })) {
const rel = `${dir}/${entry.name}`;
if (entry.isDirectory()) {
if (entry.name === 'node_modules' || entry.name === 'fixtures' || entry.name === 'dist') continue;
out.push(...walk(rel));
} else if (/\.(ts|sh|ya?ml|mjs)$/.test(entry.name) || !entry.name.includes('.')) out.push(rel);
}
return out;
}
test('every TESTING_INTERNALS.md anchor named in code matches a heading', () => {
const anchors = new Set(
[...fs.readFileSync(path.join(ROOT, DOC), 'utf8').matchAll(/^#{1,6} (.+)$/gm)].map(match => githubSlug(match[1]!)),
);
const references = SCAN.flatMap(walk).flatMap(file =>
[...fs.readFileSync(path.join(ROOT, file), 'utf8').matchAll(/TESTING_INTERNALS\.md#([a-z0-9-]+)/g)]
.map(match => ({ file, anchor: match[1]! })));
expect(references.length, 'scan found no TESTING_INTERNALS.md anchors; the regex or scan roots rotted').toBeGreaterThan(0);
const missing = references.filter(ref => !anchors.has(ref.anchor) && ref.file !== 'test/testing-internals-anchors.test.ts');
expect(missing.map(ref => `${ref.file} points at ${DOC}#${ref.anchor}, which has no heading; fix: add a "### " heading with that slug to ${DOC} or correct the link`))
.toEqual([]);
});