124 lines
5.8 KiB
JavaScript
124 lines
5.8 KiB
JavaScript
// innboks-frontmatter.test.mjs
|
|
// Step 5 (SC idempotens): deterministisk frontmatter-projeksjon. projectFrontmatter
|
|
// utleder type/tags regel-basert (INGEN LLM) og snapper mot lukket vokab; resource =
|
|
// original relativ sti; description = foerste ikke-tomme avsnitt; timestamp = ISO av
|
|
// original-mtime (IKKE veggklokke); kilde:innboks; og fester concept.destRel =
|
|
// routeLevel(type)/slug.md FOER relasjons-steget. Asserterer paa parseFrontmatter().
|
|
// get() (aldri substring for verdier). Moenster: tests/oppsett-okf-write.test.mjs:93.
|
|
|
|
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';
|
|
import { parseFrontmatter } from '../lib/frontmatter.mjs';
|
|
import { TYPE_VOCAB, routeLevel } from '../lib/okf-vocab.mjs';
|
|
import { splitConcepts } from '../lib/innboks-split.mjs';
|
|
import { projectFrontmatter } from '../lib/innboks-frontmatter.mjs';
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
const FIX = join(HERE, 'fixtures', 'inbox-converted');
|
|
const dokA = readFileSync(join(FIX, 'dok-a.md'), 'utf8');
|
|
|
|
const MTIME = new Date('2026-03-15T08:30:00.000Z');
|
|
const OPTS = { sourcePath: 'innboks/dok-a.txt', sourceMtime: MTIME };
|
|
|
|
// Frontmatter-noekler i rekkefolge (kun linjestart-noekler; list-elementer hoppes over).
|
|
function fmKeyOrder(fm) {
|
|
const { raw } = parseFrontmatter(fm);
|
|
return (raw || '')
|
|
.split('\n')
|
|
.map((l) => {
|
|
const m = l.match(/^([a-z_]+):/);
|
|
return m ? m[1] : null;
|
|
})
|
|
.filter(Boolean);
|
|
}
|
|
|
|
test('projectFrontmatter: type snappes til lukket vokab; keyword i title -> kanonisk type', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
const { get } = parseFrontmatter(e.frontmatter);
|
|
assert.ok(TYPE_VOCAB.includes(get('type')), 'type i lukket vokab');
|
|
assert.equal(get('type'), 'Tildelingsbrev', 'keyword "Tildelingsbrev" i title -> kanonisk type');
|
|
});
|
|
|
|
test('projectFrontmatter: ukjent emne -> safe default Dokument (negativ vokab-sjekk)', () => {
|
|
const concept = {
|
|
sourceSlug: 'x',
|
|
title: 'Helt annerledes emne',
|
|
slug: 'helt-annerledes-emne',
|
|
level: 1,
|
|
body: 'Innhold uten vokab-noekkelord.',
|
|
};
|
|
const e = projectFrontmatter(concept, { sourcePath: 'innboks/x.txt', sourceMtime: MTIME });
|
|
assert.equal(parseFrontmatter(e.frontmatter).get('type'), 'Dokument');
|
|
assert.doesNotMatch(
|
|
e.frontmatter,
|
|
/^type: (Tildelingsbrev|Virksomhetsplan|OKR)$/m,
|
|
'ikke feil-snappet til en foeringstype',
|
|
);
|
|
});
|
|
|
|
test('projectFrontmatter: resource = original relativ sti (kanonisk navn, ikke "source")', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
const { get } = parseFrontmatter(e.frontmatter);
|
|
assert.equal(get('resource'), 'innboks/dok-a.txt');
|
|
assert.equal(get('source'), null, 'noekkelen heter resource, ikke source');
|
|
});
|
|
|
|
test('projectFrontmatter: description deterministisk (2 kjoeringer byte-identiske, foerste avsnitt)', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e1 = projectFrontmatter(c1, OPTS);
|
|
const e2 = projectFrontmatter(c1, OPTS);
|
|
assert.equal(e1.frontmatter, e2.frontmatter, 'byte-identisk projeksjon');
|
|
assert.match(parseFrontmatter(e1.frontmatter).get('description'), /Tildelingsbrevet gir overordnede/);
|
|
});
|
|
|
|
test('projectFrontmatter: lang body -> description trunkeres deterministisk', () => {
|
|
const longBody = 'A'.repeat(500);
|
|
const concept = { sourceSlug: 'lang', title: 'Lang', slug: 'lang', level: 1, body: longBody };
|
|
const e = projectFrontmatter(concept, { sourcePath: 'innboks/lang.txt', sourceMtime: MTIME });
|
|
const desc = parseFrontmatter(e.frontmatter).get('description');
|
|
assert.ok(desc.length <= 244, 'trunkert til <= 240 + ellipsis');
|
|
assert.match(desc, /\.\.\.$/, 'ellipsis-markoer ved trunkering');
|
|
});
|
|
|
|
test('projectFrontmatter: timestamp = ISO av sourceMtime (idempotent paa tvers av veggklokke)', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
assert.equal(parseFrontmatter(e.frontmatter).get('timestamp'), MTIME.toISOString());
|
|
});
|
|
|
|
test('projectFrontmatter: kilde:innboks provenans-markoer alltid satt', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
assert.equal(parseFrontmatter(e.frontmatter).get('kilde'), 'innboks');
|
|
assert.equal(e.kilde, 'innboks', 'kilde ogsaa paa konsept-objektet');
|
|
});
|
|
|
|
test('projectFrontmatter: concept.destRel satt + konsistent med routeLevel(type)', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
assert.ok(e.destRel, 'destRel satt FOER relasjons-steget');
|
|
assert.equal(e.destRel, `${routeLevel(e.type)}/${c1.slug}.md`);
|
|
assert.equal(e.destRel, 'strategisk-kontekst/tildelingsbrev-2026.md', 'Tildelingsbrev -> strategisk-kontekst');
|
|
});
|
|
|
|
test('projectFrontmatter: kanonisk noekkel-rekkefolge (type/resource/title/desc/tags/timestamp/kilde)', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
const order = fmKeyOrder(e.frontmatter);
|
|
const canon = ['type', 'resource', 'title', 'description', 'tags', 'timestamp', 'kilde'];
|
|
assert.deepEqual(order, canon.filter((k) => order.includes(k)), 'present keys i kanonisk rekkefolge');
|
|
assert.equal(order[0], 'type', 'type foerst');
|
|
assert.equal(order[order.length - 1], 'kilde', 'kilde sist');
|
|
});
|
|
|
|
test('projectFrontmatter: tags er multi-linje liste (writeFrontmatter array-gren) + i vokab', () => {
|
|
const [c1] = splitConcepts(dokA, { sourceSlug: 'dok-a' });
|
|
const e = projectFrontmatter(c1, OPTS);
|
|
assert.match(e.frontmatter, /^tags:$/m, 'multi-linje tags-noekkel');
|
|
assert.match(e.frontmatter, /^ {2}- Tildelingsbrev$/m, 'tag-element i vokab');
|
|
});
|