// frontmatter-compat.test.mjs // Karakteriserings-/kompatibilitetstest (SC2): beviser at ruting av // inject-okr-context.mjs gjennom den delte lib/frontmatter.mjs er // ATFERDS-BEVARENDE for de 9 leste noeklene, med EN tilsiktet endring: // comment-leak paa usitert `okr_frikoblet_fra_loenn`-linje forsvinner. // Spawner hooken som subprosess (full 9-felts frontmatter, rikere enn // inject-okr-context.test.mjs sin makeProjectConfig som kun skriver navn). // Zero npm deps. Moenster: tests/inject-okr-context.test.mjs. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { execFileSync } from 'node:child_process'; import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const HOOK = join( dirname(fileURLToPath(import.meta.url)), '..', 'hooks', 'scripts', 'inject-okr-context.mjs', ); // Bygger en full 9-felts okr.local.md. okr_frikoblet_fra_loenn skrives USITERT // MED en trailing "# ..."-kommentar — det er denne lekkasjen den delte // parseren skal fjerne. function makeFullConfig(workDir, fields = {}) { const f = { navn: 'TestOrg', kortform: 'TO', gjeldende: 'T2-2026', sektor: 'transport', modenhetsnivaa: 'nivaa-2', fase: 'utforsker', okr_frikoblet_fra_loenn: 'true', psykologisk_trygghet: 'hoy', linear: true, ...fields, }; const body = [ '---', `navn: "${f.navn}"`, `kortform: "${f.kortform}"`, `gjeldende: "${f.gjeldende}"`, `sektor: "${f.sektor}"`, `modenhetsnivaa: "${f.modenhetsnivaa}"`, `fase: "${f.fase}"`, `okr_frikoblet_fra_loenn: ${f.okr_frikoblet_fra_loenn} # frikoblet fra lonn`, `psykologisk_trygghet: "${f.psykologisk_trygghet}"`, 'linear:', ` aktivert: ${f.linear}`, '---', '', ].join('\n'); const p = join(workDir, '.claude'); mkdirSync(p, { recursive: true }); writeFileSync(join(p, 'okr.local.md'), body); } const OKR_PROMPT = JSON.stringify({ prompt: 'hjelp meg skrive OKR' }); function runHook(cwd, home, input = OKR_PROMPT) { return execFileSync('node', [HOOK], { cwd, env: { ...process.env, HOME: home }, input, encoding: 'utf8', }); } function withDirs(fn) { const home = mkdtempSync(join(tmpdir(), 'okrhome-')); const work = mkdtempSync(join(tmpdir(), 'okrwork-')); try { fn(home, work); } finally { rmSync(home, { recursive: true, force: true }); rmSync(work, { recursive: true, force: true }); } } test('full config: alle 9 noekler resolver likt som foer', () => { withDirs((home, work) => { makeFullConfig(work); const out = runHook(work, home); assert.match(out, /Organisasjon: TestOrg \(TO\)/, 'navn + kortform'); assert.match(out, /Syklus: T2-2026 \[utforsker\]/, 'gjeldende + fase'); assert.match(out, /Sektor: transport/, 'sektor'); assert.match(out, /Modenhet: nivaa-2/, 'modenhetsnivaa'); assert.match(out, /OKR frikoblet fra lonn: true/, 'frikoblet-verdi'); assert.match(out, /Psykologisk trygghet: hoy/, 'psykologisk_trygghet'); assert.match(out, /Linear: aktivert/, 'linear aktivert: true'); }); }); test('comment-leak fjernet: ingen "#" i injisert melding', () => { withDirs((home, work) => { makeFullConfig(work); const out = runHook(work, home); assert.doesNotMatch( out, /#/, 'usitert frikoblet-linjes "# ..."-kommentar skal ikke lekke inn i meldingen', ); }); });