Type-aware create-guard for reference-filer (Spor 3 Port 1/Port 2): - kb-headers.mjs: parseTypeHeader/parseVerifiedHeader/parseVerifiedByHeader (samme top-500-byte bold-label-skann som parseSourceHeader). - transform.mjs: buildKbHeader er type-aware (reference krever source+verified+verified_by; non-reference kaster pa MS-Learn-source) + emitterer Type/Verified/Verified by; validateKbFile type-aware; stampVerifiedMeta = fodt-verifisert-gate (stempler KUN ved bestatt judge-verdikt, ellers kaster). - validate-kb-file.mjs (ny): kjorbar gate generatorer kaller for commit (exit != 0 ved kontraktbrudd). - generate-skills.md + kb-update.md + transform-prompt.md: wiret til fodt-verifisert kontrakt (header-felt + judge-steg + create-guard-gate). Suite 586/586 (33 nye + 1 invariant). Plugin-validering 239/0/0. Utenfor scope (flagget): generate-skills.sh legacy (sonnet/Cosmo/no-source), Cosmo-persona i generatorene (S-Cosmo), korpus-migrering av 306 filer (Spor 1).
116 lines
4.6 KiB
JavaScript
116 lines
4.6 KiB
JavaScript
// tests/kb-update/test-kb-headers.test.mjs
|
|
// Unit tests for scripts/kb-update/lib/kb-headers.mjs — parses the optional
|
|
// **Source:** / **Primary source:** authority header from a KB reference file.
|
|
// Coverage is 0% today (no file declares one yet); the parser is wired into
|
|
// build-registry so authority_source can populate as files adopt the header.
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
parseSourceHeader,
|
|
parseTypeHeader,
|
|
parseVerifiedHeader,
|
|
parseVerifiedByHeader,
|
|
} from '../../scripts/kb-update/lib/kb-headers.mjs';
|
|
|
|
test('parseSourceHeader — **Source:** header', () => {
|
|
const content = '# Title\n\n**Source:** https://learn.microsoft.com/azure/foundry/concepts/x\n**Status:** GA\n';
|
|
assert.equal(
|
|
parseSourceHeader(content),
|
|
'https://learn.microsoft.com/azure/foundry/concepts/x'
|
|
);
|
|
});
|
|
|
|
test('parseSourceHeader — **Primary source:** alias', () => {
|
|
const content = '# Title\n\n**Primary source:** https://learn.microsoft.com/azure/ai-services/y\n';
|
|
assert.equal(
|
|
parseSourceHeader(content),
|
|
'https://learn.microsoft.com/azure/ai-services/y'
|
|
);
|
|
});
|
|
|
|
test('parseSourceHeader — case-insensitive label', () => {
|
|
const content = '**source:** https://learn.microsoft.com/azure/z\n';
|
|
assert.equal(parseSourceHeader(content), 'https://learn.microsoft.com/azure/z');
|
|
});
|
|
|
|
test('parseSourceHeader — no header returns null', () => {
|
|
const content = '# Title\n\n**Last updated:** 2026-04\n**Status:** GA\n**Category:** RAG\n';
|
|
assert.equal(parseSourceHeader(content), null);
|
|
});
|
|
|
|
test('parseSourceHeader — empty/garbage input returns null', () => {
|
|
assert.equal(parseSourceHeader(''), null);
|
|
assert.equal(parseSourceHeader(null), null);
|
|
assert.equal(parseSourceHeader(undefined), null);
|
|
});
|
|
|
|
test('parseSourceHeader — only scans the top of the file (header region)', () => {
|
|
// A Source-like line buried far below the header region must NOT be picked up.
|
|
const buried = '# Title\n' + 'x'.repeat(800) + '\n**Source:** https://learn.microsoft.com/late\n';
|
|
assert.equal(parseSourceHeader(buried), null);
|
|
});
|
|
|
|
test('parseSourceHeader — trims trailing whitespace from URL', () => {
|
|
const content = '**Source:** https://learn.microsoft.com/azure/trim \n';
|
|
assert.equal(parseSourceHeader(content), 'https://learn.microsoft.com/azure/trim');
|
|
});
|
|
|
|
// --- parseTypeHeader: the Port 1 frontmatter-contract `type` field --------------
|
|
// Spor 3 Port 1: every file carries type (reference|template|methodology|regulatory).
|
|
// Same top-500-byte bold-label scan as parseSourceHeader (one source of truth).
|
|
|
|
test('parseTypeHeader — **Type:** header', () => {
|
|
const content = '# T\n\n**Status:** GA\n**Type:** reference\n**Source:** https://learn.microsoft.com/x\n';
|
|
assert.equal(parseTypeHeader(content), 'reference');
|
|
});
|
|
|
|
test('parseTypeHeader — case-insensitive label, lowercased value', () => {
|
|
assert.equal(parseTypeHeader('**type:** Methodology\n'), 'methodology');
|
|
});
|
|
|
|
test('parseTypeHeader — no header returns null', () => {
|
|
assert.equal(parseTypeHeader('# T\n\n**Status:** GA\n'), null);
|
|
assert.equal(parseTypeHeader(''), null);
|
|
assert.equal(parseTypeHeader(null), null);
|
|
});
|
|
|
|
test('parseTypeHeader — only scans the top of the file (header region)', () => {
|
|
const buried = '# T\n' + 'x'.repeat(800) + '\n**Type:** template\n';
|
|
assert.equal(parseTypeHeader(buried), null);
|
|
});
|
|
|
|
// --- parseVerifiedHeader: the Port 1 `verified` date (born-verified stamp) ------
|
|
|
|
test('parseVerifiedHeader — full date', () => {
|
|
assert.equal(parseVerifiedHeader('**Verified:** 2026-06-29\n'), '2026-06-29');
|
|
});
|
|
|
|
test('parseVerifiedHeader — YYYY-MM date', () => {
|
|
assert.equal(parseVerifiedHeader('**Verified:** 2026-06\n'), '2026-06');
|
|
});
|
|
|
|
test('parseVerifiedHeader — does NOT match the "**Verified by:**" line', () => {
|
|
// "Verified by:" is a distinct label; the date parser must not swallow it.
|
|
assert.equal(parseVerifiedHeader('**Verified by:** judge-v2\n'), null);
|
|
});
|
|
|
|
test('parseVerifiedHeader — no header / garbage returns null', () => {
|
|
assert.equal(parseVerifiedHeader('**Status:** GA\n'), null);
|
|
assert.equal(parseVerifiedHeader(null), null);
|
|
});
|
|
|
|
// --- parseVerifiedByHeader: the Port 1 `verified_by` field (judge-vN | human) ---
|
|
|
|
test('parseVerifiedByHeader — judge version', () => {
|
|
assert.equal(parseVerifiedByHeader('**Verified by:** judge-v2\n'), 'judge-v2');
|
|
});
|
|
|
|
test('parseVerifiedByHeader — human', () => {
|
|
assert.equal(parseVerifiedByHeader('**Verified by:** human\n'), 'human');
|
|
});
|
|
|
|
test('parseVerifiedByHeader — no header returns null', () => {
|
|
assert.equal(parseVerifiedByHeader('**Verified:** 2026-06-29\n'), null);
|
|
assert.equal(parseVerifiedByHeader(''), null);
|
|
});
|