128 lines
5.6 KiB
JavaScript
128 lines
5.6 KiB
JavaScript
// test-apply-verified-stamp.test.mjs — R7.1: the surgical born-verified stamp driver.
|
|
// Applies the judge-pass ledger's `pass` verdicts to disk via insertVerifiedFields:
|
|
// ONLY the **Verified:** / **Verified by:** header lines are added/updated inside the
|
|
// 500-byte window; the body below the header is byte-identical; writes are atomic and
|
|
// idempotent; a `flagged` record is never touched; an unstampable file is collected as
|
|
// an error (R7 flips it to flagged «header-slanking kreves») without aborting the rest.
|
|
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { mkdtempSync, rmSync, writeFileSync, readFileSync, mkdirSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { planVerifiedStamp } from '../../scripts/kb-update/apply-verified-stamp.mjs';
|
|
import { parseVerifiedHeader, parseVerifiedByHeader } from '../../scripts/kb-update/lib/kb-headers.mjs';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const CLI = join(__dirname, '..', '..', 'scripts', 'kb-update', 'apply-verified-stamp.mjs');
|
|
|
|
const FILE_A = `# Testfil A
|
|
|
|
**Last updated:** 2026-06
|
|
**Status:** GA
|
|
**Category:** Test
|
|
**Type:** reference
|
|
**Source:** https://learn.microsoft.com/test-a
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
Brødtekst som aldri skal endres.
|
|
`;
|
|
|
|
// Header so fat the stamp insertion point lands at ~byte 488 — the two stamp lines then land
|
|
// past the 500-byte window and insertVerifiedFields refuses (header-slanking required).
|
|
const FILE_FAT = `# Testfil ${'x'.repeat(49)}
|
|
|
|
**Last updated:** 2026-06 ${'y'.repeat(400)}
|
|
|
|
---
|
|
|
|
## Body
|
|
`;
|
|
|
|
function withTmp(fn) {
|
|
const dir = mkdtempSync(join(tmpdir(), 'avs-'));
|
|
try { return fn(dir); } finally { rmSync(dir, { recursive: true, force: true }); }
|
|
}
|
|
|
|
function bodyOf(content) {
|
|
const lines = content.split('\n');
|
|
const i = lines.findIndex((l) => /^##\s/.test(l) || /^---\s*$/.test(l));
|
|
return lines.slice(i).join('\n');
|
|
}
|
|
|
|
test('planVerifiedStamp stamps both fields in-window, body byte-identical, idempotent re-plan', () => {
|
|
const { content: stamped, changed } = planVerifiedStamp(FILE_A, { verified: '2026-07-18', verified_by: 'judge-v3.1' });
|
|
assert.equal(changed, true);
|
|
assert.equal(parseVerifiedHeader(stamped), '2026-07-18');
|
|
assert.equal(parseVerifiedByHeader(stamped), 'judge-v3.1');
|
|
assert.equal(bodyOf(stamped), bodyOf(FILE_A));
|
|
const again = planVerifiedStamp(stamped, { verified: '2026-07-18', verified_by: 'judge-v3.1' });
|
|
assert.equal(again.changed, false);
|
|
assert.equal(again.content, stamped);
|
|
});
|
|
|
|
function ledgerWith(files) {
|
|
return {
|
|
_meta: { purpose: 'test', derived_from: 'test', cadence: 'test', batch: 1, count: files.length, generated: '2026-07-18' },
|
|
files,
|
|
};
|
|
}
|
|
|
|
function passRec(file) {
|
|
return { file, batch: 1, judged_at: '2026-07-18', per_file_verdict: 'pass', claim_count: 1, verified: '2026-07-18', verified_by: 'judge-v3.1', flags: [] };
|
|
}
|
|
|
|
test('CLI --write stamps pass records, skips flagged, is idempotent on re-run', () => {
|
|
withTmp((dir) => {
|
|
mkdirSync(join(dir, 'skills'), { recursive: true });
|
|
writeFileSync(join(dir, 'skills', 'a.md'), FILE_A);
|
|
writeFileSync(join(dir, 'skills', 'b.md'), FILE_A);
|
|
const ledger = ledgerWith([
|
|
passRec('skills/a.md'),
|
|
{ file: 'skills/b.md', batch: 1, judged_at: '2026-07-18', per_file_verdict: 'flagged', claim_count: 1, verified: null, verified_by: null,
|
|
flags: [{ id: 'b.md#1', judge_verdict: 'not_grounded', rule: 'R1', evidence_url: '', evidence_quote: '', reason: 'x', file: 'skills/b.md', line: 3, claim: 'c', disposition: 'outdated' }] },
|
|
]);
|
|
const mPath = join(dir, 'ledger.json');
|
|
writeFileSync(mPath, JSON.stringify(ledger));
|
|
|
|
// Dry-run: nothing written.
|
|
execFileSync('node', [CLI, '--manifest', mPath, '--root', dir], { encoding: 'utf8' });
|
|
assert.equal(readFileSync(join(dir, 'skills', 'a.md'), 'utf8'), FILE_A);
|
|
|
|
const out1 = execFileSync('node', [CLI, '--manifest', mPath, '--root', dir, '--write'], { encoding: 'utf8' });
|
|
assert.match(out1, /stamped: 1/);
|
|
const stamped = readFileSync(join(dir, 'skills', 'a.md'), 'utf8');
|
|
assert.equal(parseVerifiedHeader(stamped), '2026-07-18');
|
|
assert.equal(bodyOf(stamped), bodyOf(FILE_A));
|
|
// Flagged never touched.
|
|
assert.equal(readFileSync(join(dir, 'skills', 'b.md'), 'utf8'), FILE_A);
|
|
// Idempotent re-run: no change.
|
|
const out2 = execFileSync('node', [CLI, '--manifest', mPath, '--root', dir, '--write'], { encoding: 'utf8' });
|
|
assert.match(out2, /stamped: 0/);
|
|
assert.equal(readFileSync(join(dir, 'skills', 'a.md'), 'utf8'), stamped);
|
|
});
|
|
});
|
|
|
|
test('CLI collects an unstampable pass record as error (exit 1) but still stamps the rest', () => {
|
|
withTmp((dir) => {
|
|
mkdirSync(join(dir, 'skills'), { recursive: true });
|
|
writeFileSync(join(dir, 'skills', 'ok.md'), FILE_A);
|
|
writeFileSync(join(dir, 'skills', 'fat.md'), FILE_FAT);
|
|
const mPath = join(dir, 'ledger.json');
|
|
writeFileSync(mPath, JSON.stringify(ledgerWith([passRec('skills/ok.md'), passRec('skills/fat.md')])));
|
|
let failed = null;
|
|
try {
|
|
execFileSync('node', [CLI, '--manifest', mPath, '--root', dir, '--write'], { encoding: 'utf8', stdio: 'pipe' });
|
|
} catch (e) { failed = e; }
|
|
assert.ok(failed, 'expected exit 1');
|
|
assert.equal(failed.status, 1);
|
|
assert.match(String(failed.stdout) + String(failed.stderr), /fat\.md/);
|
|
// The stampable sibling was still stamped.
|
|
assert.equal(parseVerifiedHeader(readFileSync(join(dir, 'skills', 'ok.md'), 'utf8')), '2026-07-18');
|
|
});
|
|
});
|