import { test } from "node:test"; import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { ARCHIVE_TREES, CONTRACT, PER_CHANNEL_TREES, ROOT_FILES, archiveUrl, corpusUrl, isFlatTree, manifestUrl, pageFileName, pageUrl, rootFileUrl, shipsPwa, } from "./contract"; import { buildSiteCorpus } from "../corpus"; import type { PublicSiteDescriptor } from "../siteDescriptor"; const HERE = path.dirname(fileURLToPath(import.meta.url)); // A minimal descriptor with two channels, one of which ships posts and digests, // so every conditional manifest pointer buildSiteCorpus can emit is exercised. function descriptor(siteUrl?: string): PublicSiteDescriptor { return { contract: CONTRACT.siteDescriptor, siteId: "testsite", siteTitle: "Test Site", siteDescription: "fixture", generatedAt: "2026-09-12T00:00:00.000Z", ...(siteUrl ? { siteUrl } : {}), channels: [ { slug: "alpha", name: "Alpha", count: 3 }, { slug: "beta", name: "Beta", count: 2, groupId: "g1" }, ], groups: [], defaultGroupId: "default", socialLinks: [], pwa: false, } as unknown as PublicSiteDescriptor; } test("page shards are zero-padded to four — page-0.json is a 404", () => { assert.equal(pageFileName(0), "page-0000.json"); assert.equal(pageFileName(7), "page-0007.json"); assert.equal(pageFileName(1234), "page-1234.json"); assert.equal(pageFileName(12345), "page-12345.json"); assert.equal(CONTRACT.pagePad, 4); }); test("archiveUrl: absolute with a base, root-relative without", () => { assert.equal(archiveUrl(undefined, "/corpus.json"), "/corpus.json"); assert.equal(archiveUrl("https://x.example", "/corpus.json"), "https://x.example/corpus.json"); // A trailing slash on the base must not double up. assert.equal(archiveUrl("https://x.example/", "/corpus.json"), "https://x.example/corpus.json"); assert.equal(archiveUrl("https://x.example///", "/a/b.json"), "https://x.example/a/b.json"); }); test("per-channel trees take a slug, flat trees do not", () => { assert.equal(isFlatTree("summaries"), true); assert.equal(isFlatTree("stats"), true); assert.equal(isFlatTree("transcripts"), false); assert.equal(manifestUrl("transcripts", "alpha"), "/transcripts/alpha/manifest.json"); assert.equal(manifestUrl("summaries"), "/summaries/manifest.json"); assert.equal(manifestUrl("stats"), "/stats/manifest.json"); assert.equal(pageUrl("subs", "alpha", 2), "/subs/alpha/page-0002.json"); assert.equal(pageUrl("summaries", undefined, 2), "/summaries/page-0002.json"); assert.equal(pageUrl("stats", undefined, 0), "/stats/page-0000.json"); // A per-channel tree with no slug is a bug at the call site, not a URL that // 404s quietly against a real site. assert.throws(() => manifestUrl("transcripts"), /needs a channel slug/); assert.throws(() => pageUrl("posts", undefined, 1), /needs a channel slug/); }); test("root files: the five JSON documents a reader fetches by name", () => { assert.deepEqual([...ROOT_FILES], [ "corpus.json", "site.json", "search-aliases.json", "duplicates.json", // Curated tags. Legitimately absent (like duplicates.json) — but unlike it, // DECLARED in corpus.json when present, which is what corpusSpec 4 says. "tags.json", ]); assert.equal(corpusUrl(), "/corpus.json"); assert.equal(corpusUrl("https://x.example"), "https://x.example/corpus.json"); assert.equal(rootFileUrl("duplicates.json"), "/duplicates.json"); assert.equal( rootFileUrl("search-aliases.json", "https://x.example"), "https://x.example/search-aliases.json", ); }); // THE reason these functions exist: what a reader builds must be byte-for-byte // what the writer emitted. Compare against buildSiteCorpus's actual output // rather than against a second hand-written string. test("manifestUrl reproduces what buildSiteCorpus emits — absolute, with a siteUrl", () => { const base = "https://testsite.example"; const corpus = buildSiteCorpus(descriptor(base), { hasArchives: false, postCounts: { beta: 4 }, digestCounts: { beta: 1 }, }); const alpha = corpus.channels[0]; const beta = corpus.channels[1]; assert.equal(alpha.manifests.transcripts, manifestUrl("transcripts", "alpha", base)); assert.equal(alpha.manifests.subs, manifestUrl("subs", "alpha", base)); assert.equal(beta.manifests.posts, manifestUrl("posts", "beta", base)); assert.equal(beta.manifests.digests, manifestUrl("digests", "beta", base)); assert.equal(alpha.manifests.transcripts, `${base}/transcripts/alpha/manifest.json`); // A channel with no posts/digests advertises neither pointer — the layer is // absent from the wire, not present-and-empty. assert.equal(alpha.manifests.posts, undefined); assert.equal(alpha.manifests.digests, undefined); }); test("manifestUrl reproduces what buildSiteCorpus emits — root-relative, no siteUrl", () => { const corpus = buildSiteCorpus(descriptor(), { hasArchives: false }); const alpha = corpus.channels[0]; assert.equal(alpha.manifests.transcripts, manifestUrl("transcripts", "alpha")); assert.equal(alpha.manifests.transcripts, "/transcripts/alpha/manifest.json"); assert.equal(alpha.manifests.subs, "/subs/alpha/manifest.json"); assert.equal(corpus.site.url, undefined); }); test("the hub corpus's per-member pointers go through the same builders", () => { const corpus = buildSiteCorpus(descriptor("https://a.example/"), { hasArchives: false, }); // buildSiteCorpus strips the trailing slash exactly as archiveUrl does. assert.equal( corpus.channels[0].manifests.transcripts, "https://a.example/transcripts/alpha/manifest.json", ); }); test("shipsPwa: the site flag, or hub mode", () => { const prev = process.env.INSTANCE_MODE; try { delete process.env.INSTANCE_MODE; assert.equal(shipsPwa({}), false); assert.equal(shipsPwa({ pwa: false }), false); assert.equal(shipsPwa({ pwa: true }), true); process.env.INSTANCE_MODE = "hub"; // Hub mode always ships the PWA, whatever the site says. assert.equal(shipsPwa({}), true); assert.equal(shipsPwa({ pwa: false }), true); process.env.INSTANCE_MODE = "site"; assert.equal(shipsPwa({}), false); } finally { if (prev === undefined) delete process.env.INSTANCE_MODE; else process.env.INSTANCE_MODE = prev; } }); test("CONTRACT is frozen where it is published", () => { // These are on the wire. A change here is a change to every deployed archive's // machine contract, so it belongs in a slice that says so, not in a refactor. assert.equal(CONTRACT.corpusSpec, 5); assert.equal(CONTRACT.siteDescriptor, 1); assert.equal(CONTRACT.manifest, 3); assert.equal(CONTRACT.transcriptsManifest, 1); assert.equal(CONTRACT.subsManifest, 4); assert.equal(CONTRACT.subsChannelManifest, 1); assert.equal(CONTRACT.summariesPageSize, 1000); assert.deepEqual( [...CONTRACT.layers], ["transcripts", "subs", "posts", "digests", "summaries"], ); }); // ─── The three hand-written copies of the contract, pinned ─── // // Three files legitimately cannot import this module and re-spell part of it by // hand: the two service workers (a service worker has no module graph to import // through — it is fetched and evaluated standalone) and the search index worker // (a classic Worker bundle). Each copy is silent when it drifts: a layer the SW // does not match is simply never cached, and a page file name without the // padding 404s against a real site while passing every unit test that made up // its own fixture names. // // So the copies are read off disk and compared with the contract. This is the // guard that lets CONTRACT.layers grow: add a layer, and these fail by name. const REPO = path.resolve(HERE, "..", "..", ".."); function readSource(rel: string): string { return readFileSync(path.join(REPO, rel), "utf8"); } // Pull `const = /…/;` out of a service worker and return the alternatives // of its first (…) group, un-escaped. Throws rather than returning [] when the // constant is missing, so a rename cannot make this test vacuously pass. function alternatives(src: string, name: string): string[] { const decl = new RegExp(`const ${name} = /(.+)/;`).exec(src); assert.ok(decl, `${name} not found — did it move or get renamed?`); const group = /\(([^)]+)\)/.exec(decl[1]); assert.ok(group, `${name} has no alternation group`); return group[1].split("|").map((a) => a.replace(/\\(.)/g, "$1")); } // The `//${slug}/` entries of an eviction prefix list. function evictedTrees(src: string): string[] { const block = /const prefixes = \[([\s\S]*?)\]/.exec(src); assert.ok(block, "eviction prefix list not found"); return [...block[1].matchAll(/`\/([^/]+)\/\$\{slug\}\/`/g)].map((m) => m[1]); } for (const sw of ["export/service-worker/site-sw.js", "export/service-worker/sw-hub.js"]) { test(`${sw} matches exactly the contract's trees and root files`, () => { const src = readSource(sw); // Per-channel and flat, split the way the URL shapes are split — and // together exactly ARCHIVE_TREES, so a layer cannot be quietly dropped from // one family and "found" in the other. assert.deepEqual( alternatives(src, "SHARD_RE").sort(), [...PER_CHANNEL_TREES].sort(), ); assert.deepEqual( alternatives(src, "FLAT_RE").sort(), ARCHIVE_TREES.filter(isFlatTree).sort(), ); assert.deepEqual( [...alternatives(src, "SHARD_RE"), ...alternatives(src, "FLAT_RE")].sort(), [...ARCHIVE_TREES].sort(), ); // The root documents, by name. ROOT_RE is a full-path match, so these are // the file names with no extra path. assert.deepEqual(alternatives(src, "ROOT_RE").sort(), [...ROOT_FILES].sort()); // Eviction is per channel, so it sweeps the per-channel trees and nothing // else. A flat tree here would be a prefix that can never match. assert.deepEqual(evictedTrees(src).sort(), [...PER_CHANNEL_TREES].sort()); }); } test("the search index worker's private pageFileName matches CONTRACT.pagePad", () => { // components/searchIndex.worker.ts keeps its own copy on purpose — a worker // bundle cannot import this module. Extract the literal padding it uses and // run the copy, so both the constant AND the produced name are pinned. const src = readSource("common/components/searchIndex.worker.ts"); const fn = /function pageFileName\(index: number\): string \{\s*return `page-\$\{String\(index\)\.padStart\((\d+), "0"\)\}\.json`;\s*\}/.exec( src, ); assert.ok(fn, "searchIndex.worker.ts's pageFileName is not the shape this test pins"); assert.equal(Number(fn[1]), CONTRACT.pagePad); // And the URLs it builds are the contract's, for the one tree it walks. assert.ok(src.includes("`/transcripts/${slug}/manifest.json`")); assert.equal(manifestUrl("transcripts", "alpha"), "/transcripts/alpha/manifest.json"); assert.ok(src.includes("`/transcripts/${slug}/${pageFileName(p)}`")); assert.equal(pageUrl("transcripts", "alpha", 12), "/transcripts/alpha/page-0012.json"); }); // shipsPwa is the one function in this module that reads the ambient // environment, and the guard in front of that read is not observable from its // return value — `typeof process` is true in every runtime the test suite has. // So the assertion is on the SOURCE, the same way the service-worker checks // above are: what is being pinned is that the read cannot throw at import time // in a browser, not what it answers. test("shipsPwa guards its process.env read", () => { const src = readSource("common/lib/archive/contract.ts"); const fn = /export function shipsPwa\([\s\S]*?\n}/.exec(src); assert.ok(fn, "shipsPwa not found — did it move or get renamed?"); assert.match(fn[0], /typeof process !== "undefined"/); // The guard must come BEFORE the dereference, which is the only arrangement // that stops `ReferenceError: process is not defined`. assert.ok( fn[0].indexOf('typeof process !== "undefined"') < fn[0].indexOf("process.env.INSTANCE_MODE"), "the guard must precede the read", ); // What it ANSWERS is unchanged, and is pinned by the behaviour test above. });