151 lines
7.8 KiB
JavaScript
151 lines
7.8 KiB
JavaScript
// tests/kb-update/test-relabel-dato.test.mjs
|
|
// TDD for Enhet 2 (decision-b, path A, ⊥ R7) — value-preserving relabel of the third Norwegian
|
|
// header date label to its English equivalent, on the 16 ref files where **Dato:** is the SOLE
|
|
// date field (no pre-existing **Last updated:**):
|
|
// **Dato:** → **Last updated:** (16 files)
|
|
//
|
|
// Enhet 2 reuses the proven Enhet-1 primitive relabelHeaderDialect() (header-scoped, collision-
|
|
// guarded, byte-invariant) with a Dato-specific label map, plus ONE new piece of production logic:
|
|
// isRealDateValue() — a placeholder guard that rejects the body-template trap (`[YYYY-MM-DD]`,
|
|
// `YYYY-MM-DD`, `[Dato]`) while accepting every real date dialect actually present in the corpus
|
|
// (`2026-06-19`, `2026-04`, `5. februar 2026`).
|
|
//
|
|
// DELIBERATELY OUT of Enhet 2 (carved out after ground-truth audit 2026-07-06):
|
|
// - 5 DUAL-LABEL files that carry BOTH **Dato:** (creation date) AND **Last updated:** (modified
|
|
// date) with distinct values. Relabeling would duplicate the label / destroy provenance; the
|
|
// collision-guard aborts on them by design. Deferred to a dedup unit (Enhet 3-adjacent).
|
|
// - 8 body/list/placeholder occurrences (advisor YYYY-MM-DD templates, ros-report [YYYY-MM-DD],
|
|
// 3 body list-items incl. [Dato]) — excluded by header-scoping + isRealDateValue.
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { existsSync, readFileSync } from 'node:fs';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { relabelHeaderDialect } from '../../scripts/kb-update/relabel-dialect.mjs';
|
|
import {
|
|
DATO_LABEL_MAP,
|
|
isRealDateValue,
|
|
MANIFEST,
|
|
} from '../../scripts/kb-update/relabel-dato.mjs';
|
|
|
|
const PLUGIN_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
|
|
// The 5 dual-label files carved OUT of Enhet 2 (they already carry **Last updated:**).
|
|
const DUAL_LABEL_FILES = [
|
|
'skills/ms-ai-engineering/references/mlops-genaiops/feedback-loops-continuous-improvement.md',
|
|
'skills/ms-ai-engineering/references/mlops-genaiops/genaiops-llm-specific-practices.md',
|
|
'skills/ms-ai-engineering/references/mlops-genaiops/mlops-security-access-control.md',
|
|
'skills/ms-ai-engineering/references/mlops-genaiops/model-deployment-strategies-azure.md',
|
|
'skills/ms-ai-engineering/references/mlops-genaiops/prompt-flow-production-deployment.md',
|
|
];
|
|
|
|
// ── primitive reused with the Dato label map ───────────────────────────────
|
|
|
|
test('relabels **Dato:** → **Last updated:**, value byte-identical', () => {
|
|
const input = '# T\n\n**Category:** X\n**Dato:** 2026-06-19\n**Type:** reference\n\n---\n\n## Body\n';
|
|
const { content, applied } = relabelHeaderDialect(input, DATO_LABEL_MAP);
|
|
assert.equal(applied.length, 1);
|
|
assert.match(content, /\*\*Last updated:\*\* 2026-06-19/);
|
|
assert.doesNotMatch(content, /\*\*Dato:\*\*/);
|
|
// Only the label token changed — reversing the sub reconstructs the original exactly.
|
|
assert.equal(content.replace('**Last updated:**', '**Dato:**'), input);
|
|
});
|
|
|
|
test('header-scoping: a **Dato:** in the BODY (past first --- / ##) is NOT touched', () => {
|
|
// The body-template trap this whole unit exists to avoid.
|
|
const input =
|
|
'# T\n\n**Dato:** 2026-06-19\n\n---\n\n## Mal\n**Dato:** [YYYY-MM-DD]\n';
|
|
const { content, applied } = relabelHeaderDialect(input, DATO_LABEL_MAP);
|
|
assert.equal(applied.length, 1);
|
|
assert.match(content, /^\*\*Last updated:\*\* 2026-06-19/m);
|
|
// The body template line survives verbatim.
|
|
assert.match(content, /## Mal\n\*\*Dato:\*\* \[YYYY-MM-DD\]/);
|
|
});
|
|
|
|
test('collision-guard: throws when the header already carries **Last updated:** (dual-label file)', () => {
|
|
// This is the safety net for the 5 carved-out dual-label files: even if one were mis-added to
|
|
// the manifest, the relabel aborts rather than duplicating the label.
|
|
const input = '# T\n\n**Dato:** 2026-02-04\n**Last updated:** 2026-06-19\n\n## Body\n';
|
|
assert.throws(() => relabelHeaderDialect(input, DATO_LABEL_MAP), /already present/i);
|
|
});
|
|
|
|
test('idempotent: re-running on the relabeled output is a no-op', () => {
|
|
const input = '# T\n\n**Dato:** 2026-06-19\n**Category:** X\n\n## Body\n';
|
|
const once = relabelHeaderDialect(input, DATO_LABEL_MAP);
|
|
const twice = relabelHeaderDialect(once.content, DATO_LABEL_MAP);
|
|
assert.equal(twice.applied.length, 0);
|
|
assert.equal(twice.content, once.content);
|
|
});
|
|
|
|
// ── isRealDateValue: the placeholder guard ─────────────────────────────────
|
|
|
|
test('isRealDateValue accepts every real date dialect present in the corpus', () => {
|
|
for (const v of [' 2026-06-19', ' 2026-04', ' 2026-05', ' 5. februar 2026', ' 2026-02-03']) {
|
|
assert.ok(isRealDateValue(v), `should accept "${v}"`);
|
|
}
|
|
});
|
|
|
|
test('isRealDateValue rejects the template placeholders (the trap)', () => {
|
|
for (const v of [' [YYYY-MM-DD]', ' YYYY-MM-DD', ' [Dato]', '', ' ', ' TBD']) {
|
|
assert.ok(!isRealDateValue(v), `should reject "${v}"`);
|
|
}
|
|
});
|
|
|
|
// ── MANIFEST: structural well-formedness ───────────────────────────────────
|
|
|
|
test('MANIFEST: exactly 16 distinct, well-formed reference paths', () => {
|
|
assert.equal(MANIFEST.length, 16);
|
|
const set = new Set(MANIFEST);
|
|
assert.equal(set.size, 16, 'duplicate path in MANIFEST');
|
|
for (const p of MANIFEST) {
|
|
assert.match(p, /^skills\/ms-ai-[a-z]+\/references\/.+\.md$/, p);
|
|
}
|
|
});
|
|
|
|
test('MANIFEST: excludes the 5 dual-label files (carved out to a dedup unit)', () => {
|
|
for (const dual of DUAL_LABEL_FILES) {
|
|
assert.ok(!MANIFEST.includes(dual), `dual-label file must not be in manifest: ${dual}`);
|
|
}
|
|
});
|
|
|
|
// ── MANIFEST ↔ disk: committed-state ground-truth integrity (guards drift/reversion) ──
|
|
// These pin the DURABLE post-relabel invariant of the committed corpus — re-running the relabel
|
|
// is a no-op, and every manifest file now carries the English label with its date preserved.
|
|
|
|
function headerBlock(content) {
|
|
const lines = content.split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (/^---\s*$/.test(lines[i]) || /^##\s/.test(lines[i])) return lines.slice(0, i);
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
test('MANIFEST ↔ disk: committed state — English label present, no header **Dato:**, real date, idempotent', () => {
|
|
for (const rel of MANIFEST) {
|
|
const abs = join(PLUGIN_ROOT, rel);
|
|
assert.ok(existsSync(abs), `missing: ${rel}`);
|
|
const content = readFileSync(abs, 'utf8');
|
|
const header = headerBlock(content);
|
|
// Enhet 2 replaced the sole header date label: **Last updated:** present, **Dato:** gone.
|
|
const luLine = header.find((l) => l.startsWith('**Last updated:**'));
|
|
assert.ok(luLine, `expected header **Last updated:** in ${rel}`);
|
|
assert.ok(!header.some((l) => l.includes('**Dato:**')), `stray header **Dato:** remains in ${rel}`);
|
|
// The preserved value is still a real date, not a placeholder.
|
|
const value = luLine.split('**Last updated:**')[1] ?? '';
|
|
assert.ok(isRealDateValue(value), `Last-updated value not a real date in ${rel}: "${value.trim()}"`);
|
|
// Idempotent: the relabel is a no-op on the committed file.
|
|
const { applied } = relabelHeaderDialect(content, DATO_LABEL_MAP);
|
|
assert.equal(applied.length, 0, `relabel is not idempotent on committed ${rel}`);
|
|
}
|
|
});
|
|
|
|
test('carve-out ↔ disk: the 5 dual-label files still carry BOTH labels (untouched by Enhet 2)', () => {
|
|
for (const rel of DUAL_LABEL_FILES) {
|
|
const abs = join(PLUGIN_ROOT, rel);
|
|
assert.ok(existsSync(abs), `missing: ${rel}`);
|
|
const header = headerBlock(readFileSync(abs, 'utf8'));
|
|
assert.ok(header.some((l) => l.includes('**Dato:**')), `dual file lost its **Dato:** — ${rel}`);
|
|
assert.ok(header.some((l) => l.startsWith('**Last updated:**')), `dual file lost **Last updated:** — ${rel}`);
|
|
}
|
|
});
|