"""WP-170 release-contract regression tests.""" from __future__ import annotations import json from pathlib import Path import yaml from scripts.promotion_preflight import _RESET, _ComposeMergeLoader ROOT = Path(__file__).resolve().parents[1] COMPOSE = ROOT / "infrastructure/compose/docker-compose.yml" PROD_COMPOSE = ROOT / "infrastructure/compose/docker-compose.prod.yml" INVENTORY = ROOT / "evidence/WP-170/release-inventory.json" TRACEABILITY = ROOT / "evidence/WP-170/traceability.json" def test_release_compose_overlay_requires_immutable_images_and_never_builds() -> None: # _ComposeMergeLoader subclasses yaml.SafeLoader and only adds the !reset tag. document = yaml.load(PROD_COMPOSE.read_text(encoding="utf-8"), Loader=_ComposeMergeLoader) # noqa: S506 base = yaml.safe_load(COMPOSE.read_text(encoding="utf-8")) services = document["services"] assert services for name, service in services.items(): if base["services"][name].get("profiles"): # Optional services are absent from the baseline promotion. Their # activation overlay pins the image and is tested separately. assert service.get("build") is _RESET continue if "image" not in service: # Production may override runtime configuration without repeating an # image only when the inherited base service is third-party, # build-free, and already immutable by digest. base_service = base["services"][name] assert "build" not in base_service assert "@sha256:" in str(base_service["image"]) continue assert service.get("build") is _RESET image = str(service["image"]) if image.startswith("${"): assert "immutable" in image and ":?" in image else: # The recovered ContextForge baseline is one deliberately bounded # exception until this exact compatibility image is promoted to an # immutable registry digest. It must stay on the reviewed local tag # and must never be pulled implicitly. if name == "contextforge": assert image == "itworx-mcp-hub/contextforge-schema:12d4a0c7789c" assert service.get("pull_policy") == "never" continue # Other literal production images remain immutable by digest. assert "@sha256:" in image def test_base_compose_preserves_socket_and_edge_boundaries() -> None: document = yaml.safe_load(COMPOSE.read_text(encoding="utf-8")) assert any( "/var/run/docker.sock" in str(value) for value in document["services"]["docker-read-broker"].get("volumes", []) ) assert not document["services"]["docker-read-broker"].get("ports") def test_release_inventory_records_all_rc_images_and_external_boundaries() -> None: inventory = json.loads(INVENTORY.read_text(encoding="utf-8")) assert inventory["sourceCommit"] assert inventory["releaseDecision"]["v1Tag"] is None assert inventory["releaseDecision"]["reason"] == "external-input-required" assert len(inventory["images"]) == 8 assert all(image["digest"].startswith("sha256:") for image in inventory["images"]) assert inventory["externalInputs"] def test_traceability_covers_every_acceptance_matrix_capability() -> None: matrix = (ROOT / "ACCEPTANCE_MATRIX.md").read_text(encoding="utf-8") traceability = json.loads(TRACEABILITY.read_text(encoding="utf-8")) capabilities = [ line.split("|", 2)[1].strip() for line in matrix.splitlines() if line.startswith("|") and not line.startswith("| Capability") and not line.startswith("|---") ] assert {item["capability"] for item in traceability["requirements"]} == set(capabilities) assert all(item["evidence"] for item in traceability["requirements"])