"""NIST CM-3: merged migrations never change (CONTRIBUTING rule 1). `migrations/IMMUTABLE.sha256` pins every revision file. Editing a merged migration means editing that manifest too — visible in review, and the reason belongs in the commit message. """ from __future__ import annotations import hashlib from pathlib import Path VERSIONS = Path(__file__).resolve().parents[1] / "migrations" / "versions" MANIFEST = VERSIONS.parent / "IMMUTABLE.sha256" def _manifest() -> dict[str, str]: out: dict[str, str] = {} for line in MANIFEST.read_text().splitlines(): if line.strip() and not line.startswith("#"): digest, name = line.split() out[name] = digest return out def test_every_revision_is_pinned() -> None: files = {p.name for p in VERSIONS.glob("*.py")} assert files - set(_manifest()) == set(), "add new migrations to IMMUTABLE.sha256" def test_pinned_revisions_unchanged() -> None: for name, digest in _manifest().items(): actual = hashlib.sha256((VERSIONS / name).read_bytes()).hexdigest() assert actual == digest, f"{name} changed after merge — add a new revision instead"