2026-06-10 13:57:42 +02:00
|
|
|
import importlib.util
|
|
|
|
|
import pathlib
|
|
|
|
|
|
|
|
|
|
_PATH = pathlib.Path(__file__).resolve().parent.parent / "scripts" / "repo-scan.py"
|
|
|
|
|
_spec = importlib.util.spec_from_file_location("repo_scan", _PATH)
|
|
|
|
|
rs = importlib.util.module_from_spec(_spec)
|
|
|
|
|
_spec.loader.exec_module(rs)
|
|
|
|
|
|
|
|
|
|
GOOD = [
|
|
|
|
|
"# ADR-099 — Example\n", "\n",
|
|
|
|
|
"## Status\n", "\n", "Accepted (2026-06-10)\n", "\n",
|
|
|
|
|
"## Context\n", "\n", "Why.\n", "\n",
|
|
|
|
|
"## Decision\n", "\n", "What.\n", "\n",
|
|
|
|
|
"## Consequences\n", "\n", "So what.\n",
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _checks(findings):
|
|
|
|
|
return [f for f in findings if f["check"] == "adr-structure"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_good_adr_has_no_findings():
|
|
|
|
|
out = rs.adr_structure_findings({"docs/decisions/099-example.md": GOOD})
|
|
|
|
|
assert _checks(out) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_missing_mandatory_section_is_flagged():
|
|
|
|
|
lines = [ln for ln in GOOD if not ln.startswith("## Consequences")]
|
|
|
|
|
out = _checks(rs.adr_structure_findings({"docs/decisions/099-example.md": lines}))
|
|
|
|
|
assert len(out) == 1
|
|
|
|
|
assert "Consequences" in out[0]["detail"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_unparseable_status_is_flagged():
|
|
|
|
|
lines = [("Designed, not built.\n" if ln == "Accepted (2026-06-10)\n" else ln)
|
|
|
|
|
for ln in GOOD]
|
|
|
|
|
out = _checks(rs.adr_structure_findings({"docs/decisions/099-example.md": lines}))
|
|
|
|
|
assert len(out) == 1
|
|
|
|
|
assert "Status not parseable" in out[0]["detail"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_superseded_status_is_accepted():
|
|
|
|
|
lines = [("Superseded by ADR-100 (2026-06-11)\n" if ln == "Accepted (2026-06-10)\n"
|
|
|
|
|
else ln) for ln in GOOD]
|
|
|
|
|
out = _checks(rs.adr_structure_findings({"docs/decisions/099-example.md": lines}))
|
|
|
|
|
assert out == []
|
|
|
|
|
|
|
|
|
|
|
2026-06-10 14:48:55 +02:00
|
|
|
def test_proposed_status_is_accepted():
|
|
|
|
|
lines = [("Proposed (2026-06-04)\n" if ln == "Accepted (2026-06-10)\n"
|
|
|
|
|
else ln) for ln in GOOD]
|
|
|
|
|
out = _checks(rs.adr_structure_findings({"docs/decisions/099-example.md": lines}))
|
|
|
|
|
assert out == []
|
|
|
|
|
|
|
|
|
|
|
2026-06-10 13:57:42 +02:00
|
|
|
def test_non_numbered_file_is_skipped():
|
|
|
|
|
bare = ["# ADR template\n", "\n", "## Status\n", "\n", "<!-- hint -->\n"]
|
|
|
|
|
out = _checks(rs.adr_structure_findings({"docs/decisions/adr-template.md": bare}))
|
|
|
|
|
assert out == []
|