"""SLICE-4 — write paths are sidecar-aware: a migrated (body-only `.md` + sidecar) entry never crashes `entry.parse` nor re-grows frontmatter.""" from __future__ import annotations import asyncio from app import gitea as gitea_mod, metadata from app.bot import Actor, Bot from app.config import load_config from test_propose_vertical import ( # noqa: F401 app_with_fake_gitea, provision_user_row, tmp_env, ) BODY_ONLY = "Alpha prose body.\n" SIDECAR = ("slug: alpha\ntitle: Alpha\nstate: active\n" "owners:\n- ben.stull\ntags:\n- one\npriority: P1\n") def _seed_migrated(fake, repo="meta"): fake.files[("wiggleverse", repo, "main", "rfcs/alpha.md")] = { "content": BODY_ONLY, "sha": "m1"} fake.files[("wiggleverse", repo, "main", "rfcs/alpha.meta.yaml")] = { "content": SIDECAR, "sha": "m2"} def _actor(): return Actor(user_id=1, gitea_login="ben.stull", display_name="Ben", email="ben@x.io") def test_mark_entry_reviewed_on_migrated_entry(app_with_fake_gitea): from fastapi.testclient import TestClient app, fake = app_with_fake_gitea _seed_migrated(fake) gitea = gitea_mod.Gitea(load_config()) bot = Bot(gitea) with TestClient(app): provision_user_row(user_id=1, login="ben.stull", role="owner") # Must not raise (legacy code parsed body-only .md → ValueError). asyncio.run(bot.mark_entry_reviewed( _actor(), org="wiggleverse", meta_repo="meta", slug="alpha", reviewed_by="ben.stull", reviewed_at="2026-06-07")) md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] sc = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"] assert "---" not in md # body stays clean (no re-grown FM) assert "reviewed_by: ben.stull" in sc # review stamp landed in the sidecar # ---- Task 3.2: body extract/wrap helpers (api_branches) ---- def test_extract_wrap_body_on_body_only_md(): from app import api_branches rfc = {"state": "super-draft", "repo": None, "slug": "alpha", "collection_id": "default"} body = api_branches._extract_body_pure(rfc, BODY_ONLY, "main", is_meta=True) assert body == BODY_ONLY wrapped = api_branches._wrap_body_pure(rfc, BODY_ONLY, "new body\n", "main", is_meta=True) assert wrapped == "new body\n" # stays clean — no re-grown frontmatter def test_wrap_body_preserves_legacy_frontmatter(): from app import api_branches legacy = "---\nslug: alpha\ntitle: Alpha\nstate: active\n---\n\nold body\n" rfc = {"state": "super-draft", "repo": None, "slug": "alpha", "collection_id": "default"} wrapped = api_branches._wrap_body_pure(rfc, legacy, "new body\n", "main", is_meta=True) assert wrapped.startswith("---") # legacy frontmatter preserved assert "new body" in wrapped # ---- Task 3.3: PR-replay body wrappers (api_prs) ---- def test_replay_wrappers_on_body_only(): from app import api_prs assert api_prs._extract_body_for_replay(True, BODY_ONLY) == BODY_ONLY out = api_prs._wrap_body_for_replay(True, BODY_ONLY, "new\n") assert out == "new\n" # clean, no re-grown frontmatter legacy = "---\nslug: a\ntitle: A\nstate: active\n---\n\nold\n" out2 = api_prs._wrap_body_for_replay(True, legacy, "new\n") assert out2.startswith("---") # legacy preserved # ---- Task 3.4: retire a fully-migrated (body-only + sidecar) entry ---- def test_retire_already_migrated_entry_does_not_crash(app_with_fake_gitea): from fastapi.testclient import TestClient from app import cache, db from app.config import load_config app, fake = app_with_fake_gitea _seed_migrated(fake) with TestClient(app) as client: provision_user_row(user_id=1, login="ben", role="owner") from test_propose_vertical import sign_in_as sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben", role="owner") # Ingest the migrated entry so the catalog/cache knows it. asyncio.run(cache.refresh_meta_repo(load_config(), gitea_mod.Gitea(load_config()))) assert db.conn().execute( "SELECT state FROM cached_rfcs WHERE slug='alpha'").fetchone()["state"] == "active" r = client.post("/api/rfcs/alpha/retire") assert r.status_code == 200, r.text assert r.json()["state"] == "retired" import yaml as _yaml sc = _yaml.safe_load( fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"]) assert sc["state"] == "retired" md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] assert "---" not in md # body stayed clean # ---- Task 3.5: graduate a fully-migrated super-draft entry ---- SUPER_SIDECAR = ("slug: alpha\ntitle: Alpha\nstate: super-draft\n" "owners:\n- ben\ntags:\n- one\npriority: P1\n") def test_graduate_already_migrated_super_draft(app_with_fake_gitea): from fastapi.testclient import TestClient from app import cache, db from app.config import load_config app, fake = app_with_fake_gitea fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")] = { "content": BODY_ONLY, "sha": "m1"} fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")] = { "content": SUPER_SIDECAR, "sha": "m2"} with TestClient(app) as client: from test_propose_vertical import sign_in_as provision_user_row(user_id=1, login="ben", role="owner") sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben", role="owner") asyncio.run(cache.refresh_meta_repo(load_config(), gitea_mod.Gitea(load_config()))) assert db.conn().execute( "SELECT state FROM cached_rfcs WHERE slug='alpha'").fetchone()["state"] == "super-draft" r = client.post("/api/rfcs/alpha/graduate?_sync=1", json={"rfc_id": "RFC-0007", "owners": ["ben"]}) assert r.status_code == 200, r.text import yaml as _yaml sc = _yaml.safe_load( fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"]) assert sc["state"] == "active" assert sc["id"] == "RFC-0007" assert sc.get("priority") == "P1" # INV-7 carried through graduation md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] assert "---" not in md # body stayed clean