"""US09-03: the architecture overview, checked against the architecture. An architecture document is the one that rots most quietly: nothing breaks when it describes a module that was renamed two epics ago, it just quietly misleads the next person. So the parts of it that the code also knows — module names, state machines, table names, the modules an invariant is claimed to live in — are compared with the code, and a missing one fails the suite. The diagrams' rendering is proven in ``tests/e2e/test_docs_ui.py``. """ from __future__ import annotations import re from pathlib import Path import photo_pipeline.models # noqa: F401 (registers every table on Base.metadata) from photo_pipeline.db import Base from photo_pipeline.services import jobs, rename_journal, upload_batches REPO = Path(__file__).resolve().parents[2] PACKAGE = REPO / "photo_pipeline" OVERVIEW = REPO / "docs" / "architecture.md" TEXT = OVERVIEW.read_text() # Names the map does not owe the reader individually: the package markers, the CLI # entry point, and the two modules that exist to be small and obvious. UNMAPPED = {"__init__", "__main__", "logging"} def modules() -> set[str]: """Every module and package under photo_pipeline, by the name it is imported as.""" names = set() for path in PACKAGE.rglob("*.py"): relative = path.relative_to(PACKAGE) names.add(relative.parts[0] if len(relative.parts) > 1 else relative.stem) return {name.removesuffix(".py") for name in names} - UNMAPPED def states(container: type) -> set[str]: return { value for name, value in vars(container).items() if name.isupper() and isinstance(value, str) } # ── the module map ─────────────────────────────────────────────────────────── def test_every_module_appears_in_the_map(): """A service nobody documented is a service the next person re-implements.""" missing = sorted(name for name in modules() if name not in TEXT) assert missing == [], f"absent from the architecture overview: {missing}" def test_every_service_is_named_individually(): """The package table says what `services/` is for; this is the list that actually drifts, because a new service is added roughly every story.""" services = {path.stem for path in (PACKAGE / "services").glob("*.py")} - UNMAPPED missing = sorted(name for name in services if not re.search(rf"\b{name}\b", TEXT)) assert missing == [], f"services missing from the overview: {missing}" def test_the_map_names_no_module_that_does_not_exist(): """Every `services/` or backticked module path in the document resolves.""" referenced = set(re.findall(r"`(?:photo_pipeline/)?([a-z_]+)/([a-z_]+)\.py`", TEXT)) referenced |= {("", name) for name in re.findall(r"`([a-z_]+)\.py`", TEXT)} missing = [ f"{package}/{module}.py" if package else f"{module}.py" for package, module in referenced if not (PACKAGE / package / f"{module}.py").is_file() ] assert missing == [], f"documented but absent from the source: {missing}" # ── the state machines ─────────────────────────────────────────────────────── def test_every_job_state_is_documented(): missing = sorted(state for state in states(jobs.JobState) if state not in TEXT) assert missing == [], f"job states missing from the overview: {missing}" def test_every_rename_journal_state_is_documented(): missing = sorted(state for state in states(rename_journal.JournalState) if state not in TEXT) assert missing == [], f"journal states missing from the overview: {missing}" def test_every_upload_batch_state_is_documented(): missing = sorted(state for state in states(upload_batches.BatchState) if state not in TEXT) assert missing == [], f"batch states missing from the overview: {missing}" def test_the_unsafe_journal_states_are_named_as_the_ones_that_block(): """The reason an unrelated mutation is refused has to be findable.""" for state in rename_journal.UNSAFE_STATES: assert state in TEXT assert "rename_recovery_required" in TEXT def test_an_uncertain_upload_is_documented_as_not_retryable(): assert upload_batches.BatchState.UNKNOWN not in upload_batches.RUNNABLE_STATES assert "not** restartable" in TEXT or "not restartable" in TEXT # ── the data model ─────────────────────────────────────────────────────────── def test_every_table_is_listed(): missing = sorted(name for name in Base.metadata.tables if name not in TEXT) assert missing == [], f"tables missing from the overview: {missing}" def test_the_document_lists_no_table_that_does_not_exist(): listed = set(re.findall(r"`([a-z_]+)`", TEXT)) plausible = {name for name in listed if name.endswith("s") and "_" in name} invented = sorted( name for name in plausible if name not in Base.metadata.tables and not (PACKAGE / "services" / f"{name}.py").is_file() and name not in {"library_roots", "trusted_proxies", "allowed_hosts", "asset_paths"} ) assert invented == [], f"looks like a table but is not one: {invented}" # ── the invariants ─────────────────────────────────────────────────────────── def test_each_invariant_points_at_a_module_that_enforces_it(): """The point of the table is to answer 'where do I look'. A wrong answer there costs more than no answer.""" claims = { "path_policy.is_excluded": PACKAGE / "path_policy.py", "path_policy.resolve_in_roots": PACKAGE / "path_policy.py", "services/app_lock.py": PACKAGE / "services" / "app_lock.py", "services/exif_checkpoint.py": PACKAGE / "services" / "exif_checkpoint.py", "services/archive_transfer.py": PACKAGE / "services" / "archive_transfer.py", "api/security.py": PACKAGE / "api" / "security.py", "imaging.py": PACKAGE / "imaging.py", } for claim, path in claims.items(): assert claim in TEXT, f"the invariant table does not mention {claim}" assert path.is_file(), f"{claim} does not exist" function = claim.rpartition(".")[2] if "/" not in claim else "" if function and function != "py": assert f"def {function}" in path.read_text(), f"{claim} is not defined there" def test_the_lock_order_matches_the_ranks_in_the_code(): from photo_pipeline.jobs import locks order = [name for name, _ in sorted(locks.LOCK_RANK.items(), key=lambda item: item[1])] assert order[0].startswith("library"), "the broadest lock is no longer the library" assert "library → stage/job → album/folder → asset" in TEXT def test_the_frozen_donor_archive_is_described_as_provenance_not_a_dependency(): """That production never imports the frozen sources is proven by ``tests/unit/test_legacy_archive.py`` — which also forbids any other test from naming that directory, so this one checks the claim by its consequence.""" assert "must not import" in TEXT assert "provenance and rollback evidence" in TEXT