"""SLICE-1 integration — frontmatter→sidecar migration tool (PUC-5). Per docs/design/2026-06-06-configurable-collection-metadata.md §6.5 / §7.2: a tool walks a collection; for each entry with legacy frontmatter it writes `.meta.yaml` and rewrites `.md` to the body only — one commit per collection, idempotent, preserving unknown keys (INV-7). """ from __future__ import annotations import asyncio import yaml from app import gitea as gitea_mod, metadata from app.config import load_config from test_propose_vertical import ( # noqa: F401 (fixtures) app_with_fake_gitea, tmp_env, ) ALPHA_MD = """--- slug: alpha title: Alpha state: active owners: - ben.stull tags: - one priority: P1 --- Alpha body prose. """ BETA_MD = """--- slug: beta title: Beta state: super-draft --- Beta body prose. """ def _seed_entries(fake): fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")] = { "content": ALPHA_MD, "sha": "a0001"} fake.files[("wiggleverse", "meta", "main", "rfcs/beta.md")] = { "content": BETA_MD, "sha": "b0001"} def _run_migration(subfolder=""): cfg = load_config() gitea = gitea_mod.Gitea(cfg) return asyncio.run( metadata.migrate_collection( gitea, org="wiggleverse", repo="meta", subfolder=subfolder ) ) def test_migration_writes_sidecars_and_strips_bodies(app_with_fake_gitea): _app, fake = app_with_fake_gitea _seed_entries(fake) summary = _run_migration() assert sorted(summary["migrated"]) == ["alpha", "beta"] # Sidecars now exist. alpha_sc = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"] beta_sc = fake.files[("wiggleverse", "meta", "main", "rfcs/beta.meta.yaml")]["content"] alpha_vals = yaml.safe_load(alpha_sc) assert alpha_vals["slug"] == "alpha" assert alpha_vals["title"] == "Alpha" assert alpha_vals["tags"] == ["one"] # INV-7: the unknown key rides along into the sidecar. assert alpha_vals["priority"] == "P1" # .md bodies are stripped of frontmatter (INV-2). alpha_md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] assert "---" not in alpha_md assert "priority:" not in alpha_md assert alpha_md.strip() == "Alpha body prose." assert beta_sc # beta got a sidecar too def test_migration_is_idempotent(app_with_fake_gitea): _app, fake = app_with_fake_gitea _seed_entries(fake) first = _run_migration() assert sorted(first["migrated"]) == ["alpha", "beta"] assert first["committed"] is True commits_after_first = fake._commit_counter alpha_md_after_first = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] second = _run_migration() assert second["migrated"] == [] assert second["committed"] is False # No new commit; files untouched. assert fake._commit_counter == commits_after_first assert fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] == alpha_md_after_first def test_migration_one_commit_for_whole_collection(app_with_fake_gitea): _app, fake = app_with_fake_gitea _seed_entries(fake) before = fake._commit_counter _run_migration() # Two entries migrated in exactly one commit (ChangeFiles batch). assert fake._commit_counter == before + 1 def test_migration_dual_read_equivalence_after_migrate(app_with_fake_gitea): """An entry reads identically before and after migration (INV-6).""" _app, fake = app_with_fake_gitea _seed_entries(fake) before, _ = metadata.read_entry(ALPHA_MD, None) _run_migration() md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] sc = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"] after, malformed = metadata.read_entry(md, sc) assert malformed is False assert after.slug == before.slug assert after.title == before.title assert after.state == before.state assert after.tags == before.tags assert after.extra == before.extra assert after.body.strip() == before.body.strip()