/*
RFC-0500: snapshot test for the ratgeber topics→articles migrator —
verifies that topics are moved to articles, sections are converted to prose,
frontmatter fields are renamed, and FAQ-only entries are removed.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-0500: initial snapshot test for topics→articles migrator.
*/
import { test, expect } from "vitest";
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { rfc0500Migrator } from "./rfc-0500.ts";
import type { SternsystemData, MigrationContext } from "./types.ts";
const ctx: MigrationContext = {
systemId: "test",
missionId: "test-mission",
logger: { info: () => {} },
};
async function createWorkpiece(): Promise {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "rfc-0500-snap-"));
const topicsDir = path.join(dir, "src", "content", "surface", "topics", "de");
await fs.mkdir(topicsDir, { recursive: true });
await fs.writeFile(
path.join(topicsDir, "website-kosten.md"),
"---\nname: Website Kosten\nslug: website-kosten\nintro: Was kostet eine Website?\nsections:\n - heading: Überblick\n body: Die Kosten hängen von verschiedenen Faktoren ab.\n---\n",
);
await fs.writeFile(
path.join(topicsDir, "faq-only.md"),
"---\nname: FAQ Only\nslug: faq-only\nfaqs:\n - question: Q\n answer: A\n---\n",
);
return dir;
}
test("rfc-0500 snapshot: moves topics to articles and transforms frontmatter", async () => {
const dir = await createWorkpiece();
try {
const data: SternsystemData = { rootPath: dir, dataPaths: [] };
await rfc0500Migrator.transform(data, ctx);
const articlesDir = path.join(dir, "src", "content", "surface", "articles");
const topicsDir = path.join(dir, "src", "content", "surface", "topics");
expect(
await fs
.stat(articlesDir)
.then(() => true)
.catch(() => false),
).toBe(true);
expect(
await fs
.stat(topicsDir)
.then(() => true)
.catch(() => false),
).toBe(false);
const articlePath = path.join(articlesDir, "de", "website-kosten.md");
const content = await fs.readFile(articlePath, "utf8");
expect(content).toContain("title: Website Kosten");
expect(content).toContain("summary: Was kostet eine Website?");
expect(content).toContain("status: published");
expect(content).toContain("articleType: grundlagenartikel");
expect(content).not.toContain("sections:");
const faqOnlyPath = path.join(articlesDir, "de", "faq-only.md");
expect(
await fs
.stat(faqOnlyPath)
.then(() => true)
.catch(() => false),
).toBe(false);
const prosePath = path.join(dir, "src", "content", "prose", "ratgeber-de-website-kosten.md");
const proseContent = await fs.readFile(prosePath, "utf8");
expect(proseContent).toContain("## Überblick");
expect(proseContent).toContain("Die Kosten");
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test("rfc-0500 snapshot: migrator is idempotent on already-migrated content", async () => {
const dir = await createWorkpiece();
try {
const data: SternsystemData = { rootPath: dir, dataPaths: [] };
const once = await rfc0500Migrator.transform(data, ctx);
const articlesDir = path.join(dir, "src", "content", "surface", "articles");
const articlePath = path.join(articlesDir, "de", "website-kosten.md");
const content1 = await fs.readFile(articlePath, "utf8");
const twice = await rfc0500Migrator.transform(once, ctx);
const content2 = await fs.readFile(articlePath, "utf8");
expect(content2).toEqual(content1);
expect(twice).toEqual(once);
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});