"""AC-3: docker-compose.yml and docker-compose.test.yml are valid. YAML syntactic validity always runs. The `docker compose ... config --quiet` shape validation requires the Docker daemon and the v2 plugin; when those are not present, that test skips with the prerequisite reason. """ from __future__ import annotations import shutil import subprocess from pathlib import Path import pytest import yaml REPO_ROOT = Path(__file__).resolve().parents[2] COMPOSE_FILES = ( REPO_ROOT / "docker-compose.yml", REPO_ROOT / "docker-compose.test.yml", ) @pytest.mark.parametrize("compose_path", COMPOSE_FILES) def test_compose_yaml_parses(compose_path: Path) -> None: # Act parsed = yaml.safe_load(compose_path.read_text()) # Assert assert isinstance(parsed, dict), f"{compose_path.name} must parse to a mapping" assert "services" in parsed, f"{compose_path.name} must declare a services block" def test_compose_yml_declares_required_services() -> None: # Arrange data = yaml.safe_load((REPO_ROOT / "docker-compose.yml").read_text()) services = data["services"] # Assert for required in ("companion", "operator-tooling", "mock-sat", "db"): assert required in services, f"docker-compose.yml missing service: {required}" def test_compose_test_yml_extends_base() -> None: # Arrange data = yaml.safe_load((REPO_ROOT / "docker-compose.test.yml").read_text()) # Assert assert "services" in data, "docker-compose.test.yml must declare services" assert "e2e-runner" in data["services"], ( "docker-compose.test.yml must declare the e2e-runner sidecar" ) @pytest.mark.parametrize("compose_path", COMPOSE_FILES) def test_compose_config_quiet(compose_path: Path) -> None: # Arrange docker = shutil.which("docker") if docker is None: pytest.skip("docker CLI not on PATH; Tier-1 CI image installs Docker") plugin_check = subprocess.run( [docker, "compose", "version"], capture_output=True, text=True, check=False ) if plugin_check.returncode != 0: pytest.skip("docker compose v2 plugin unavailable; Tier-1 CI image installs it") # Act result = subprocess.run( [docker, "compose", "-f", str(compose_path), "config", "--quiet"], cwd=REPO_ROOT, capture_output=True, text=True, check=False, ) # Assert assert result.returncode == 0, ( f"docker compose config --quiet failed for {compose_path.name}:\n" f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}" )