from pathlib import Path import pytest import scripts.decisions_lib as dl FIX = Path(__file__).parent / "fixtures" / "sample_decisions.md" def test_parses_migrated_record(): recs = dl.parse_file(FIX) migrated = [r for r in recs if r.key == "ci.runner-placement"] assert len(migrated) == 1 r = migrated[0] assert r.status == "active" assert r.since == "2026-07-17" assert r.supersedes == "none" assert r.superseded_by == "none" assert r.rule == "Every CI services container gets an explicit cap." assert r.signals is not None assert "issues: #390 #406" in r.signals assert r.mechanics is not None assert r.mechanics.startswith("docs/ci-cd.md") def test_legacy_record_is_unmigrated(): recs = dl.parse_file(FIX) legacy = [r for r in recs if r.heading.endswith("(#231)")] assert len(legacy) == 1 assert legacy[0].key is None assert legacy[0].status == "legacy-unmigrated" def test_index_section_parses_as_heading(): recs = dl.parse_file(FIX) assert any(r.heading == "Index" for r in recs) def test_parses_optional_stale_after_and_sources(): recs = dl.parse_file(FIX) r = next(r for r in recs if r.key == "ci.peak-anon-measurement") assert r.stale_after == "2027-01-15" assert r.sources is not None assert "gitea run 4471" in r.sources def test_optional_fields_default_to_none_when_absent(): recs = dl.parse_file(FIX) r = next(r for r in recs if r.key == "ci.runner-placement") assert r.stale_after is None assert r.sources is None def test_empty_stale_after_parses_as_empty_string_not_none(): """Absent vs present-but-empty must stay distinguishable for the validator.""" text = ( "## H\n" "`key: a.b` · `status: active` · `since: 2026-01-01` · `stale-after:` " "· `supersedes: none` · `superseded-by: none`\n" "**Rule:** r\n" ) r = dl.parse_text(text, Path("fake.md"))[0] assert r.stale_after == "" def test_frontmatter_reader_matches_pyyaml_on_every_real_record(): """The dependency-free reader must agree with PyYAML on the whole real corpus. The read path cannot import PyYAML — it runs in CI's `decisions lifecycle` job, the Husky pre-commit hook, and on every contributor's machine, none of which install it. (Requiring it made the validator crash with ModuleNotFoundError once the corpus was migrated.) A hand parser is only safe if it provably matches the library that WROTE the files, so this compares the two across every record rather than on a sample. Since #674 the comparison itself lives in `decisions_validate.pyyaml_frontmatter_faults`, which the VALIDATOR now runs too — before that it existed only here, so `decisions_validate.py` happily reported OK on a record PyYAML rejects. This test delegates to that one implementation rather than keeping a second copy of the comparison, so the suite and the validator cannot drift apart and agree on what "matches PyYAML" means. """ pytest.importorskip("yaml") import scripts.decisions_validate as dv files = [p for p in dl.RECORDS_DIR.rglob("*.md")] + [p for p in dl.ARCHIVE_DIR.rglob("*.md")] files = [f for f in files if dl.has_frontmatter(f.read_text(encoding="utf-8"))] assert len(files) > 100, f"only {len(files)} frontmatter files found — test would be near-vacuous" faults, ran = dv.pyyaml_frontmatter_faults(files) assert ran, "PyYAML is importable here, so the comparison must have actually run" assert not faults, f"{len(faults)} frontmatter fault(s) vs PyYAML:\n" + "\n".join(faults[:5])