// syklus-data.test.mjs // D5 (fase D, steg 7-8): datamodellen for styringssloeyfa. Testene deles i to lag // som landet i hver sin commit: // (A) FIXTURE-KONTRAKTEN (steg 7) -- asserterer paa fixture-INNHOLD, ikke paa // lib-kode. Disse er roede foer fixturen utvides og groenne etterpaa, slik at // steg 8 har data aa bygge mot (RED-baseline-disiplin, jf. innboks-ingest). // (B) LESER + SCOREBEREGNING (steg 8) -- lib/syklus-data.mjs. // // Beslutning B-1: flate `krN_`-noekler i okr-*.md sin frontmatter. Verifisert kjoert, // ikke bare lest: `^\s*type:` i lib/frontmatter.mjs:29 matcher ikke `kr1_type:`, saa // OKF-noekkelen `type` og KR-noekkelen `krN_type` kolliderer ikke. // Beslutning B-2: score BEREGNES, lagres aldri. target == baseline gir undefined. // // Offline, zero-dep, node:test. Test-kilden holdes ASCII-ren (bash 3.2 set -u). import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); const SYKLUS = 'tests/fixtures/okf-realistic/syklus/T1-2026'; const OKR_FILER = ['okr-trafikksikkerhet.md', 'okr-digitalisering.md']; const lesFixture = (navn) => readFileSync(join(ROOT, SYKLUS, navn), 'utf8'); // --- (A) Fixture-kontrakten (steg 7) --- test('(A1) steg 7: begge OKR-fixturene baerer kr1_baseline', () => { const missing = OKR_FILER.filter((f) => !/^\s*kr1_baseline\s*:/m.test(lesFixture(f))); assert.deepEqual( missing, [], `fixture-OKR uten kr1_baseline: ${missing.join(', ')}`, ); }); test('(A2) steg 7: begge KR-typene forekommer i fixturen', () => { const alle = OKR_FILER.map(lesFixture).join('\n'); assert.match(alle, /^\s*kr\d+_type\s*:\s*committed\s*$/m, 'ingen committed KR i fixturen'); assert.match( alle, /^\s*kr\d+_type\s*:\s*aspirational\s*$/m, 'ingen aspirational KR i fixturen', ); }); // B-2-kanten skal vaere dekket av DATA, ikke bare av enhetstest -- ellers moeter // generatoren i steg 9-10 den foerste gang i produksjon. test('(A3) steg 7: fixturen inneholder ett KR-par der target == baseline', () => { const funn = []; for (const f of OKR_FILER) { const doc = lesFixture(f); for (const m of doc.matchAll(/^\s*kr(\d+)_baseline\s*:\s*(\S+)\s*$/gm)) { const t = new RegExp(`^\\s*kr${m[1]}_target\\s*:\\s*(\\S+)\\s*$`, 'm').exec(doc); if (t && t[1] === m[2]) funn.push(`${f}:kr${m[1]}`); } } assert.ok( funn.length >= 1, 'ingen KR med target == baseline -- udefinert-score-kanten (B-2) er udekket av data', ); }); // OKF-laget skal vaere uroert av KR-utvidelsen: `type:` er fortsatt OKR-typen, ikke // en KR-type. Dette er antakelse 2 i planen, her bundet til faktisk fixture-innhold. test('(A4) steg 7: krN_type forstyrrer ikke OKF-noekkelen type', async () => { const { parseFrontmatter } = await import('../lib/frontmatter.mjs'); for (const f of OKR_FILER) { const fm = parseFrontmatter(lesFixture(f)); assert.equal(fm.get('type'), 'OKR', `${f}: OKF-type forskjovet av krN_type`); } });