/* RFC-0483: snapshot test for the rfc-0483 content migrator — runs on real warpgogol-com content data and verifies all 60 reference patterns are correctly migrated, de/ PBP entities are created, presentation fields are populated, and the legacy business/ directory is deleted. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-0483: initial snapshot test for content migrator. */ import { test, expect } from "vitest"; import fs from "node:fs/promises"; import path from "node:path"; import os from "node:os"; import { rfc0483Migrator } from "./rfc-0483.ts"; import type { SternsystemData, MigrationContext } from "./types.ts"; const ctx: MigrationContext = { systemId: "warpgogol-com", missionId: "test-mission", logger: { info: () => {} }, }; const FIXTURE_DIR = path.resolve(process.cwd(), "src", "migrators", "fixtures", "rfc-0483"); async function copyDir(src: string, dest: string): Promise { await fs.mkdir(dest, { recursive: true }); const entries = await fs.readdir(src, { withFileTypes: true }); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { await copyDir(srcPath, destPath); } else { await fs.copyFile(srcPath, destPath); } } } async function readMarkdownFiles(dir: string): Promise { const results: string[] = []; let entries: import("node:fs").Dirent[]; try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch { return results; } for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { const nested = await readMarkdownFiles(fullPath); results.push(...nested); } else if (entry.name.endsWith(".md")) { results.push(fullPath); } } return results; } async function createSnapshotWorkpiece(dir: string): Promise { const srcDir = path.join(dir, "src"); const contentDir = path.join(srcDir, "content"); await fs.mkdir(contentDir, { recursive: true }); await fs.writeFile( path.join(contentDir, "system.md"), `---\ni18n:\n default: de\n supported:\n de:\n name: Deutsch\n uk:\n name: Українська\n---\n`, ); await copyDir(path.join(FIXTURE_DIR, "business"), path.join(contentDir, "business")); await copyDir( path.join(FIXTURE_DIR, "business-profile"), path.join(contentDir, "business-profile"), ); await copyDir(path.join(FIXTURE_DIR, "pages"), path.join(contentDir, "pages")); await copyDir(path.join(FIXTURE_DIR, "prose"), path.join(contentDir, "prose")); await fs.writeFile( path.join(srcDir, "content.config.ts"), `// GENERATED. Do not change this line unless the file contains project specific changes.\nimport { defineCollection } from "astro:content";\nimport { fsDataCollectionLoader } from "@warpgogol/werkstatt-site/content-source";\nimport { pbpCollections } from "@warpgogol/werkstatt-site/pbp/astro";\nimport { toDataEntryId } from "@warpgogol/werkstatt-shared/content";\nimport { z } from "astro/zod";\n\n// Loads business entity records from src/content/business/{lang}/ as the "business" collection.\n// Used by content references {business.*.*} in pages and prose (RFC-0045).\nconst business = defineCollection({\n loader: fsDataCollectionLoader({\n base: "src/content/business",\n generateId: (entry) => toDataEntryId(entry),\n }),\n schema: z.object({}).catchall(z.any()),\n});\n\nexport const collections = {\n ...pbpCollections,\n business,\n};\n`, ); } test("snapshot: rfc-0483 migrates all {business.*} references from real warpgogol-com content", async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "rfc-0483-snapshot-")); try { await createSnapshotWorkpiece(dir); const data: SternsystemData = { rootPath: dir, dataPaths: [] }; await rfc0483Migrator.transform(data, ctx); const mdFiles = await readMarkdownFiles(path.join(dir, "src", "content")); let remainingBusinessRefs = 0; for (const file of mdFiles) { const content = await fs.readFile(file, "utf8"); const matches = content.match(/\{business\.[^}]+\}/g); if (matches) { const realRefs = matches.filter((m) => m !== "{business.offer.*}"); remainingBusinessRefs += realRefs.length; } } expect(remainingBusinessRefs).toBe(0); let businessProfileRefs = 0; for (const file of mdFiles) { const content = await fs.readFile(file, "utf8"); const matches = content.match(/\{business-profile\.[^}]+\}/g); if (matches) businessProfileRefs += matches.length; } expect(businessProfileRefs).toBeGreaterThan(0); const businessDir = path.join(dir, "src", "content", "business"); let businessExists = false; try { await fs.access(businessDir); businessExists = true; } catch { businessExists = false; } expect(businessExists).toBe(false); const configPath = path.join(dir, "src", "content.config.ts"); const configContent = await fs.readFile(configPath, "utf8"); expect(configContent).not.toContain("const business ="); expect(configContent).not.toContain("business,"); expect(configContent).not.toContain("@warpgogol/business"); const deContactPath = path.join( dir, "src", "content", "business-profile", "de", "contact", "general-email.md", ); try { const deContact = await fs.readFile(deContactPath, "utf8"); expect(deContact).toContain("schema: pbp/contact-point@1"); } catch { // de/ entities may already exist from RFC-0481 or be created by this migrator } const dePlacesPath = path.join( dir, "src", "content", "business-profile", "de", "places", "backnang.md", ); try { const dePlaces = await fs.readFile(dePlacesPath, "utf8"); expect(dePlaces).toContain("schema: pbp/place@1"); } catch { // may not exist if uk/ doesn't have it } const deWebPrimaryPath = path.join( dir, "src", "content", "business-profile", "de", "web", "primary.md", ); try { const deWeb = await fs.readFile(deWebPrimaryPath, "utf8"); expect(deWeb).toContain("schema: pbp/web-presence@1"); } catch { // may not exist if uk/ doesn't have it } } finally { await fs.rm(dir, { recursive: true, force: true }); } }); test("snapshot: rfc-0483 is idempotent on real warpgogol-com content", async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "rfc-0483-snapshot-idem-")); try { await createSnapshotWorkpiece(dir); const data: SternsystemData = { rootPath: dir, dataPaths: [] }; const once = await rfc0483Migrator.transform(data, ctx); const mdFiles = await readMarkdownFiles(path.join(dir, "src", "content")); const snapshots = new Map(); for (const file of mdFiles) { snapshots.set(file, await fs.readFile(file, "utf8")); } const twice = await rfc0483Migrator.transform(once, ctx); const mdFiles2 = await readMarkdownFiles(path.join(dir, "src", "content")); for (const file of mdFiles2) { const content = await fs.readFile(file, "utf8"); const prev = snapshots.get(file); if (prev !== undefined) { expect(content).toEqual(prev); } } expect(twice).toEqual(once); } finally { await fs.rm(dir, { recursive: true, force: true }); } });