import { describe, test } from "node:test"; import assert from "node:assert/strict"; import { mintContentId } from "../src/id.js"; import { parsePublishedRecord, serializePublishedRecord, type PublishedRecord, } from "../src/ingest.js"; const baseRec = (body: string): PublishedRecord => ({ id: mintContentId(body), provenance: "published", published_date: "2026-05-26", captured_at: "2026-06-23", source: "manual", body, }); /** Round-trip helper: parse∘serialize must equal the original record. */ function roundTrip(rec: PublishedRecord): PublishedRecord { return parsePublishedRecord(serializePublishedRecord(rec)); } describe("mintContentId — verbatim-body content hash (SC3)", () => { test("deterministic: same body → same id", () => { const body = "Jeg lærte noe om dømmekraft i dag.\n\nDel 1 av serien."; assert.equal(mintContentId(body), mintContentId(body)); }); test("returns a 12-hex id", () => { assert.match(mintContentId("any body"), /^[0-9a-f]{12}$/); }); test("different bodies → different ids", () => { assert.notEqual(mintContentId("post A"), mintContentId("post B")); }); test("whitespace/case differences mint DIFFERENT ids (no normalize-collision, B2 guard)", () => { // Under specifics-bank normalizeContent these would collapse to the SAME id — // proving the post-body hash is byte-identity, not normalized. assert.notEqual(mintContentId("Hello World"), mintContentId("hello world")); assert.notEqual( mintContentId("Line one\nLine two"), mintContentId("Line one Line two"), ); }); }); describe("PublishedRecord grammar — parse∘serialize identity (SC2, B1 edge battery)", () => { const bodies: Record = { "plain multi-paragraph": "Hook line.\n\nBody paragraph one.\n\nBody paragraph two.", "empty body": "", "single newline": "\n", "trailing newlines": "post text\n\n", "body starting with ---": "---\nlooks like a separator but is body", "body containing a \\n---\\n sentinel mid-text": "before\n---\nafter the fake separator", "header-shaped first line": "id: 0123456789ab\nprovenance: published", "special chars ] | quotes": 'value with ] and | and "quotes" inside', }; for (const [label, body] of Object.entries(bodies)) { test(`round-trips: ${label}`, () => { const rec = baseRec(body); assert.deepEqual(roundTrip(rec), rec); }); } test("serialized form has the fixed 5-line header then the --- sentinel", () => { const text = serializePublishedRecord(baseRec("hi")); const [header, ...rest] = text.split("\n---\n"); assert.equal(header.split("\n").length, 5, "exactly 5 header lines before the sentinel"); assert.equal(rest.join("\n---\n"), "hi", "body is everything after the FIRST sentinel"); assert.match(text, /^id: [0-9a-f]{12}\nprovenance: published\n/); }); test("a published/ record with non-published provenance is rejected (corruption signal)", () => { const corrupt = serializePublishedRecord(baseRec("x")).replace( "provenance: published", "provenance: ai-draft", ); assert.throws(() => parsePublishedRecord(corrupt), /provenance/i); }); });