ktg-plugin-marketplace/scripts/check-nav-golden.test.mjs
Kjell Tore Guttormsen 325727523d test(okf): consume nav-golden fixtures + thin STEG 0 corpus gate
Consume commons' nav-golden fixture class byte-exact from
portfolio-optimiser-commons @ b641741 (nav-golden-hierarchy positive +
nav-golden-escape boundary) into test/nav-golden-corpus/ and wire a thin
gate scripts/check-nav-golden.mjs (+ .test.mjs, 10 tests).

Scope (operator decision, thin): the catalog is the convention owner and
holds no consumer (null konsument), and the shared retrieval skill is
deferred (spec.md §10 Stage 3). So the gate does NOT run a read-context
navigator and does NOT assert the goldens byte-exact. It asserts only what
the catalog owns: each fixture bundle/ is a conformant OKF bundle under the
existing §3 checkBundle (every concept typed + root okf_version), and the
committed expected-read-context.md golden is present and non-empty. Byte-exact
navigator conformance stays a consumer concern (okr STEG 4 / Stage 3 skill).

Fixtures pinned byte-exact (.gitattributes -text); manifest carries source
repo+SHA per the vendoring-provenance rule. Naming: the stale "§5 pts 1-5"
read-context label maps to the settled method-spec §3 Step 1 (commons 9801d35);
no open §5 dependency. commons confirmed b641741 complete + goldens derived
from §3 Step 1 (via coord).

Verified: check-nav-golden 10/10; gate CLI exit 0 (2 fixtures PASS); full
suite 73/73 across the six test files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeK9hkxrU9wFPBYGYnSV1V
2026-07-24 20:35:44 +02:00

194 lines
7.5 KiB
JavaScript

// Tests for the STEG 0 nav-golden corpus gate (check-nav-golden.mjs).
// Style mirrors check-okf-parity.test.mjs: node:test, temp fixtures, a subprocess for
// the CLI exit contract, zero npm deps. Two layers:
// 1. Over the COMMITTED corpus — both consumed fixtures conform + their goldens are present.
// 2. Runtime-materialized meta-tests that drive each FAIL axis (untyped concept, missing
// okf_version, absent bundle/, absent golden, empty golden) + the orphan-dir report.
// SCOPE mirror: this gate is THIN (conformance + golden presence). It does NOT run a
// navigator, so there is no byte-exact golden assertion here — that is a consumer concern.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { checkFixture, runCorpus, GOLDEN_FILE } from './check-nav-golden.mjs';
const HERE = dirname(fileURLToPath(import.meta.url));
const RUNNER = join(HERE, 'check-nav-golden.mjs');
const CORPUS = join(HERE, '..', 'test', 'nav-golden-corpus');
function tmpRoot() {
return mkdtempSync(join(tmpdir(), 'nav-golden-'));
}
// Materialize a minimal fixture directory: <base>/<name>/{bundle/{index.md,concept.md},golden}.
// Options flip each FAIL axis independently.
function makeFixture(base, name, opts = {}) {
const {
withBundle = true,
okfVersion = true,
conceptTyped = true,
golden = 'read-context body\n',
} = opts;
const fdir = join(base, name);
mkdirSync(fdir, { recursive: true });
if (withBundle) {
const bundle = join(fdir, 'bundle');
mkdirSync(bundle, { recursive: true });
writeFileSync(
join(bundle, 'index.md'),
okfVersion ? '---\ntype: index\nokf_version: 0.1\n---\n# root\n' : '---\ntype: index\n---\n# root\n',
);
writeFileSync(
join(bundle, 'concept.md'),
conceptTyped ? '---\ntype: project\ntitle: C\n---\n# c\n' : '---\ntitle: C\n---\n# c\n',
);
}
if (golden !== null) writeFileSync(join(fdir, GOLDEN_FILE), golden);
return fdir;
}
// --- Layer 1: the committed corpus (the acceptance binding for the consumed fixtures) ---
test('committed corpus: both nav-golden fixtures conform and their goldens are present', () => {
const { results, failed, orphans, exitCode } = runCorpus(CORPUS);
assert.equal(exitCode, 0, `committed corpus must be green; failed: ${JSON.stringify(failed.map((f) => f.fixture))}`);
assert.deepEqual(failed, []);
assert.deepEqual(orphans, [], `no unmanifested corpus dirs; orphans: ${orphans.join(', ')}`);
const byName = Object.fromEntries(results.map((r) => [r.fixture, r]));
for (const name of ['nav-golden-hierarchy', 'nav-golden-escape']) {
const r = byName[name];
assert.ok(r, `${name} must be in the manifest`);
assert.equal(r.status, 'PASS', `${name} must PASS`);
assert.equal(r.okf.okfVersion, '0.1', `${name} bundle root must echo okf_version 0.1`);
assert.equal(r.goldenPresent, true);
assert.equal(r.goldenNonEmpty, true);
assert.deepEqual(r.problems, []);
}
});
test('CLI over committed corpus: exit 0 + "OK: nav-golden" line', () => {
const stdout = execFileSync('node', [RUNNER], { encoding: 'utf8' });
assert.match(stdout, /OK: nav-golden fixtures conform/);
});
// --- Layer 2: materialized FAIL axes (checkFixture is pure over a fixture dir) ---
test('PASS: a well-formed fixture (typed concept + okf_version + non-empty golden)', () => {
const base = tmpRoot();
try {
const fdir = makeFixture(base, 'ok');
const r = checkFixture(fdir, 'ok');
assert.equal(r.status, 'PASS');
assert.deepEqual(r.problems, []);
assert.equal(r.okf.okfVersion, '0.1');
assert.equal(r.goldenNonEmpty, true);
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test('FAIL: a concept file without type', () => {
const base = tmpRoot();
try {
const fdir = makeFixture(base, 'untyped', { conceptTyped: false });
const r = checkFixture(fdir, 'untyped');
assert.equal(r.status, 'FAIL');
assert.ok(r.problems.some((p) => /without type/.test(p)), `problems: ${r.problems.join('; ')}`);
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test('FAIL: root index.md without okf_version', () => {
const base = tmpRoot();
try {
const fdir = makeFixture(base, 'nover', { okfVersion: false });
const r = checkFixture(fdir, 'nover');
assert.equal(r.status, 'FAIL');
assert.ok(r.problems.some((p) => /okf_version/.test(p)), `problems: ${r.problems.join('; ')}`);
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test('FAIL: missing bundle/ directory', () => {
const base = tmpRoot();
try {
const fdir = makeFixture(base, 'nobundle', { withBundle: false });
const r = checkFixture(fdir, 'nobundle');
assert.equal(r.status, 'FAIL');
assert.ok(r.problems.some((p) => /bundle\//.test(p)), `problems: ${r.problems.join('; ')}`);
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test('FAIL: the committed golden is absent', () => {
const base = tmpRoot();
try {
const fdir = makeFixture(base, 'nogolden', { golden: null });
const r = checkFixture(fdir, 'nogolden');
assert.equal(r.status, 'FAIL');
assert.equal(r.goldenPresent, false);
assert.ok(r.problems.some((p) => new RegExp(GOLDEN_FILE).test(p)), `problems: ${r.problems.join('; ')}`);
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test('FAIL: the golden is present but empty (whitespace only)', () => {
const base = tmpRoot();
try {
const fdir = makeFixture(base, 'emptygolden', { golden: ' \n\n' });
const r = checkFixture(fdir, 'emptygolden');
assert.equal(r.status, 'FAIL');
assert.equal(r.goldenPresent, true);
assert.equal(r.goldenNonEmpty, false);
} finally {
rmSync(base, { recursive: true, force: true });
}
});
// --- Layer 2b: runCorpus over a materialized corpus (manifest + orphan report + exit code) ---
test('runCorpus: a FAIL fixture drives exit 1; an unmanifested dir is reported as an orphan', () => {
const base = tmpRoot();
try {
makeFixture(base, 'good');
makeFixture(base, 'bad', { conceptTyped: false });
makeFixture(base, 'stowaway'); // on disk but NOT in the manifest -> orphan
writeFileSync(
join(base, 'manifest.json'),
JSON.stringify({
fixtures: {
good: { axis: 'a' },
bad: { axis: 'b' },
},
}),
);
const { results, orphans, failed, exitCode } = runCorpus(base);
assert.equal(exitCode, 1, 'a FAIL fixture must make the corpus red');
assert.deepEqual(failed.map((f) => f.fixture).sort(), ['bad']);
assert.deepEqual(orphans, ['stowaway']);
const good = results.find((r) => r.fixture === 'good');
assert.equal(good.status, 'PASS');
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test('runCorpus: a manifested fixture absent on disk is reported MISSING (exit 1)', () => {
const base = tmpRoot();
try {
makeFixture(base, 'present');
writeFileSync(
join(base, 'manifest.json'),
JSON.stringify({ fixtures: { present: { axis: 'a' }, ghost: { axis: 'b' } } }),
);
const { failed, exitCode } = runCorpus(base);
assert.equal(exitCode, 1);
assert.ok(failed.some((f) => f.fixture === 'ghost' && f.status === 'MISSING'));
} finally {
rmSync(base, { recursive: true, force: true });
}
});