backblaze-proofframe / tests /test_docker_smoke.py
ADJCJH's picture
Retry Docker health connection resets
b0593e2 verified
Raw
History Blame Contribute Delete
6.43 kB
import importlib.util
from pathlib import Path
SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "docker_smoke.py"
SPEC = importlib.util.spec_from_file_location("docker_smoke", SCRIPT_PATH)
docker_smoke = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
SPEC.loader.exec_module(docker_smoke)
def write_dockerignore(root: Path) -> None:
(root / ".dockerignore").write_text(
"\n".join(
[
".git/",
".env",
".env.*",
"!.env.example",
"!.env.final.example",
".env.final.local",
".venv/",
"var/",
"output/",
]
)
+ "\n",
encoding="utf-8",
)
def fake_runner(args, cwd, timeout, env=None):
if args[:2] == ["docker", "info"]:
return {"ok": True, "returncode": 0, "stdout_tail": '"29.3.0"', "stderr_tail": ""}
if args[:2] == ["docker", "build"]:
return {"ok": True, "returncode": 0, "stdout_tail": "Successfully tagged", "stderr_tail": ""}
if args[:2] == ["docker", "run"]:
return {"ok": True, "returncode": 0, "stdout_tail": "container-id", "stderr_tail": ""}
if args[:2] == ["docker", "logs"]:
return {"ok": True, "returncode": 0, "stdout_tail": "Uvicorn running", "stderr_tail": ""}
if args[:3] == ["docker", "rm", "-f"]:
return {"ok": True, "returncode": 0, "stdout_tail": "removed", "stderr_tail": ""}
if args[-1].startswith("http://127.0.0.1:"):
assert env and env["PYTHONPATH"] == "src"
return {"ok": True, "returncode": 0, "stdout_tail": '{"ok": true}', "stderr_tail": ""}
raise AssertionError(f"unexpected command: {args}")
def recording_runner(calls, overrides=None):
overrides = overrides or {}
def runner(args, cwd, timeout, env=None):
calls.append(args)
command_id = "_".join(args[:2]) if args[:2] != ["docker", "rm"] else "docker_rm"
if command_id in overrides:
return overrides[command_id]
return fake_runner(args, cwd, timeout, env)
return runner
def healthy_fetcher(url, timeout):
return {
"ok": True,
"status": 200,
"json": {
"ready": True,
"storage_backend": "local",
"generation_backend": "mock",
"b2_configured": False,
"genblaze_configured": False,
},
"error": None,
}
def test_fetch_health_treats_connection_reset_as_transient(monkeypatch):
def broken_urlopen(request, timeout):
raise ConnectionResetError("reset by peer")
monkeypatch.setattr(docker_smoke, "urlopen", broken_urlopen)
result = docker_smoke.fetch_health("http://127.0.0.1:18088/api/health", 1)
assert result["ok"] is False
assert "reset by peer" in result["error"]
def test_docker_smoke_report_passes_with_fake_runner(tmp_path):
write_dockerignore(tmp_path)
report = docker_smoke.build_report(
root=tmp_path,
command_runner=fake_runner,
health_fetcher=healthy_fetcher,
sleep_fn=lambda seconds: None,
)
assert report["schema"] == "proofframe.docker_smoke.v1"
assert report["ok"] is True
assert report["mode"] == "docker_smoke_ready"
assert report["dockerignore"]["missing_required_patterns"] == []
assert report["dockerignore"]["missing_allow_patterns"] == []
assert {check["id"] for check in report["checks"]} >= {
"dockerignore_secret_exclusions",
"docker_build",
"docker_health",
"api_smoke",
}
assert "does not read .env.final.local" in report["secret_policy"]
def test_docker_smoke_cleans_up_after_health_exception(tmp_path):
write_dockerignore(tmp_path)
calls = []
def broken_fetcher(url, timeout):
raise RuntimeError("health exploded")
report = docker_smoke.build_report(
root=tmp_path,
command_runner=recording_runner(calls),
health_fetcher=broken_fetcher,
sleep_fn=lambda seconds: None,
)
failed = {check["id"] for check in report["checks"] if not check["ok"]}
assert report["ok"] is False
assert "docker_health" in failed
assert ["docker", "rm", "-f", "proofframe-submission-smoke"] in calls
cleanup = next(command for command in report["commands"] if command["id"] == "docker_cleanup")
assert cleanup["ok"] is True
logs = next(command for command in report["commands"] if command["id"] == "docker_logs")
assert "health exploded" in logs["stderr_tail"]
def test_docker_smoke_cleans_up_when_api_smoke_fails(tmp_path):
write_dockerignore(tmp_path)
calls = []
report = docker_smoke.build_report(
root=tmp_path,
command_runner=recording_runner(
calls,
overrides={
f"{docker_smoke.sys.executable}_scripts/api_smoke.py": {
"ok": False,
"returncode": 1,
"stdout_tail": "",
"stderr_tail": "api failed",
}
},
),
health_fetcher=healthy_fetcher,
sleep_fn=lambda seconds: None,
)
failed = {check["id"] for check in report["checks"] if not check["ok"]}
assert report["ok"] is False
assert "api_smoke" in failed
assert ["docker", "rm", "-f", "proofframe-submission-smoke"] in calls
def test_docker_smoke_fails_closed_without_dockerignore(tmp_path):
report = docker_smoke.build_report(
root=tmp_path,
command_runner=fake_runner,
health_fetcher=healthy_fetcher,
sleep_fn=lambda seconds: None,
)
failed = {check["id"] for check in report["checks"] if not check["ok"]}
assert report["ok"] is False
assert "dockerignore_secret_exclusions" in failed
def test_docker_smoke_writes_json_and_markdown(tmp_path):
write_dockerignore(tmp_path)
report = docker_smoke.build_report(
root=tmp_path,
command_runner=fake_runner,
health_fetcher=healthy_fetcher,
sleep_fn=lambda seconds: None,
)
json_path = tmp_path / "docker-smoke.json"
markdown_path = tmp_path / "docker-smoke.md"
docker_smoke.write_outputs(report, json_path, markdown_path)
assert json_path.read_text(encoding="utf-8").startswith("{")
assert "# ProofFrame Docker Smoke Report" in markdown_path.read_text(encoding="utf-8")