Files
msd-core/tests/edge-probe-docs-fixtures.test.cjs
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

123 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// allow-test-rule: source-text-is-the-product
// the rendered reference/SPEC/ADR vocab surfaces are the runtime contract; this pins their bijection to the code (docs-parity)
// Asserts the portable reference doc (msd-core/references/edge-probe.md) keeps its
// worked-example JSON blocks in sync with the source-of-truth fixture files under
// msd-core/references/edge-probe-fixtures/. The fixtures are the canonical data; the
// doc embeds copies. Per the CONTRIBUTING.md exception matrix this is `docs-parity`: a
// reference doc must mirror source-defined data and there is no runtime enumeration API.
// The comparison is PARSED JSON (deepEqual of JSON.parse on both sides), never a raw-text
// substring match — so a reformat that preserves the data does not fail, and any semantic
// drift between doc and fixture does.
'use strict';
process.env.MSD_TEST_MODE = '1';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { scanFencedBlocks } = require('../msd-core/bin/lib/markdown-sectionizer.cjs');
const docPath = path.join(__dirname, '..', 'msd-core', 'references', 'edge-probe.md');
const fixturesRoot = path.join(__dirname, '..', 'msd-core', 'references', 'edge-probe-fixtures');
const adrPath = path.join(__dirname, '..', 'docs', 'adr', '550-spec-phase-probe-contract.md');
const specTemplatePath = path.join(__dirname, '..', 'msd-core', 'templates', 'spec.md');
// Extract fenced blocks tagged ```json edge-probe:<dir>/<file> from the doc, keyed by ref.
// The \n? before the closing fence allows blocks whose closing fence has no preceding newline
// (fixes the silent-skip bug where a trailing-fence-with-no-newline was not matched).
function taggedJsonBlocks(md) {
const lines = md.split(/\r?\n/);
const out = {};
for (const block of scanFencedBlocks(lines)) {
if (block.closeLineIdx === -1) continue;
const info = block.infoString || '';
const tagMatch = /^json edge-probe:([^\r\n]+)$/.exec(info);
if (!tagMatch) continue;
out[tagMatch[1].trim()] = lines.slice(block.openLineIdx + 1, block.closeLineIdx).join('\n');
}
return out;
}
describe('edge-probe doc/fixture sync', () => {
test('reference doc exists', () => {
assert.ok(fs.existsSync(docPath), `${docPath} must exist`);
});
test('doc embeds tagged fixture blocks for every expected-coverage.json fixture (count-equality)', () => {
const md = fs.readFileSync(docPath, 'utf8');
const blocks = taggedJsonBlocks(md);
// Count the expected-coverage.json files under the fixtures root (one per fixture dir).
const expectedCount = fs.readdirSync(fixturesRoot, { withFileTypes: true })
.filter(e => e.isDirectory())
.filter(dir => fs.existsSync(path.join(fixturesRoot, dir.name, 'expected-coverage.json')))
.length;
assert.strictEqual(
Object.keys(blocks).length,
expectedCount,
`edge-probe.md must embed exactly ${expectedCount} tagged blocks (one per fixture expected-coverage.json)`
);
});
test('every tagged doc block parses and deepEquals its fixture file', () => {
const md = fs.readFileSync(docPath, 'utf8');
const blocks = taggedJsonBlocks(md);
for (const [ref, body] of Object.entries(blocks)) {
const fixtureFile = path.join(fixturesRoot, ref);
const onDisk = fs.readFileSync(fixtureFile, 'utf8');
assert.deepEqual(JSON.parse(body), JSON.parse(onDisk),
`doc block edge-probe:${ref} must deepEqual ${fixtureFile}`);
}
});
});
// m2: lock the machine↔SPEC vocabulary mapping so the two layers cannot silently drift. The
// machine contract uses orthogonal `status` × `verification`; the SPEC table and planner prose
// render a flat `covered/dismissed/backstop/unresolved`. The migration is documented in ADR-550
// Decision 7a and the reference's "Generic mapping" table, but the SPEC table is rendered by the
// LLM workflow (no JS renderer to round-trip against). So this pins the canonical map as code AND
// grounds it in every doc surface that renders the vocabulary — a renderer/parser drift fails here.
describe('edge-probe machine↔SPEC vocabulary mapping (m2 drift lock)', () => {
// The canonical migration map (ADR-550 Decision 7a): machine state → SPEC display label.
// `verification` is null for the lifecycle-only states (dismissed/unresolved).
const MACHINE_TO_DISPLAY = [
{ status: 'resolved', verification: 'explicit', display: 'covered' },
{ status: 'resolved', verification: 'backstop', display: 'backstop' },
{ status: 'dismissed', verification: null, display: 'dismissed' },
{ status: 'unresolved', verification: null, display: 'unresolved' },
];
const machineKey = (s, v) => `${s}|${v ?? '∅'}`;
test('the mapping is a bijection — machine→display→machine is identity, no shared labels', () => {
const toDisplay = new Map(MACHINE_TO_DISPLAY.map((m) => [machineKey(m.status, m.verification), m.display]));
const fromDisplay = new Map(MACHINE_TO_DISPLAY.map((m) => [m.display, machineKey(m.status, m.verification)]));
// No two machine states collapse onto the same display label (the silent-drift failure mode).
assert.equal(toDisplay.size, MACHINE_TO_DISPLAY.length, 'each machine state must have a distinct key');
assert.equal(fromDisplay.size, MACHINE_TO_DISPLAY.length, 'each display label must map back to exactly one machine state');
// Round-trip identity.
for (const m of MACHINE_TO_DISPLAY) {
const key = machineKey(m.status, m.verification);
assert.equal(fromDisplay.get(toDisplay.get(key)), key, `${key} must round-trip through its display label`);
}
});
test('ADR-550 Decision 7a documents the resolved/explicit↔covered and resolved/backstop↔backstop migration', () => {
const adr = fs.readFileSync(adrPath, 'utf8');
assert.match(adr, /covered\b[^.]*resolved[^.]*explicit/i, 'ADR must document covered → {resolved, explicit}');
assert.match(adr, /backstop\b[^.]*resolved[^.]*backstop/i, 'ADR must document backstop → {resolved, backstop}');
assert.match(adr, /count-for-count|count-preserved/i, 'ADR must state coverage.resolved is count-preserved across the migration');
});
test('the SPEC template legend renders exactly the four canonical display labels', () => {
const spec = fs.readFileSync(specTemplatePath, 'utf8');
for (const { display } of MACHINE_TO_DISPLAY) {
assert.match(spec, new RegExp(`\\b${display}\\b`, 'i'), `spec.md Edge Coverage legend must render the "${display}" label`);
}
});
test('the reference Generic-mapping table distinguishes the resolved/explicit and resolved/backstop tiers', () => {
const md = fs.readFileSync(docPath, 'utf8');
assert.match(md, /resolved`?\/`?explicit/i, 'reference must name the resolved/explicit tier in the mapping table');
assert.match(md, /resolved`?\/`?backstop/i, 'reference must name the resolved/backstop tier in the mapping table');
});
});