Files
pigway-frame-flow/tests/test_ci_compose.py

87 lines
3.2 KiB
Python

"""CI orchestration must isolate host resources and clean up on failure."""
import importlib.util
import subprocess
from pathlib import Path
from types import SimpleNamespace
import pytest
@pytest.fixture
def ci(monkeypatch, tmp_path):
spec = importlib.util.spec_from_file_location(
"ci_compose", Path(__file__).parents[1] / "scripts/ci-compose-verify.py"
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
monkeypatch.setenv("GITHUB_ENV", str(tmp_path / "job-env"))
return module
def test_ci_config_preserves_runtime_and_isolates_resources(ci):
config = ci.isolated_compose("ffci-fixture")
production = ci.yaml.safe_load((ci.ROOT / "docker-compose.yml").read_text())
service = config["services"]["pigway-frame-flow"]
assert not any(key in service for key in ("ports", "container_name", "env_file"))
assert service["volumes"] == ["data:/data", "config:/config"]
assert config["volumes"] == {"data": {}, "config": {}}
assert config["name"] == "ffci-fixture"
assert service["image"] != "pigway-frame-flow:latest"
assert service["environment"]["KOKONNA_MOCK"] == "true"
assert service["environment"]["KOKONNA_WRITE_ENABLED"] == "false"
for key in (
"user",
"read_only",
"healthcheck",
"restart",
"cap_drop",
"security_opt",
):
assert service[key] == production["services"]["pigway-frame-flow"][key]
assert (
production["services"]["pigway-frame-flow"]["container_name"]
== "pigway-frame-flow"
)
@pytest.mark.parametrize("fail_smoke", [False, True])
def test_verification_cleanup_and_container_side_smoke(ci, monkeypatch, fail_smoke):
calls = []
def run(command, **kwargs):
calls.append((command, kwargs))
if command[-2:] == ["python", "-"] and fail_smoke:
raise subprocess.CalledProcessError(1, command)
return SimpleNamespace(returncode=0, stdout="fixture-container\n")
monkeypatch.setattr(ci.subprocess, "run", run)
if fail_smoke:
with pytest.raises(subprocess.CalledProcessError):
ci.verify()
else:
ci.verify()
assert any("down" in cmd and "--volumes" in cmd for cmd, _ in calls)
assert any(cmd[1:3] == ["image", "rm"] for cmd, _ in calls)
smoke = [(cmd, kw) for cmd, kw in calls if cmd[-2:] == ["python", "-"]]
assert len(smoke) == (1 if fail_smoke else 2)
assert all("exec" in cmd and "-T" in cmd and kw["input"] for cmd, kw in smoke)
if not fail_smoke:
assert any("restart" in cmd for cmd, _ in calls)
build_env = calls[0][1]["env"]
assert build_env["FRAME_FLOW_IMAGE"].startswith("pigway-frame-flow-ci:ffci-")
assert not Path(build_env["COMPOSE_FILE"]).exists()
def test_cleanup_failure_keeps_manifest_for_always_step(ci, monkeypatch, tmp_path):
path = tmp_path / "compose.yml"
path.write_text(ci.yaml.safe_dump(ci.isolated_compose("ffci-fixture")))
def fail(*args, **kwargs):
raise subprocess.CalledProcessError(1, args[0])
monkeypatch.setattr(ci.subprocess, "run", fail)
with pytest.raises(subprocess.CalledProcessError):
ci.cleanup(path)
assert path.exists()