test(docker): add conftest fixtures for docker harness

Task 0.1 of the s6-overlay supervision plan. Establishes the test
infrastructure for tests/docker/: skip-on-missing-Docker collection
hook, session-scoped image-build fixture (overridable via the
HERMES_TEST_IMAGE env var for faster local iteration), and a
container_name fixture that ensures cleanup on test exit.

Refs: docs/plans/2026-05-07-s6-overlay-dynamic-subagent-gateways.md
This commit is contained in:
Ben 2026-05-21 12:52:51 +10:00
parent ef536880a3
commit 705256aaa6
2 changed files with 79 additions and 0 deletions

0
tests/docker/__init__.py Normal file
View file

79
tests/docker/conftest.py Normal file
View file

@ -0,0 +1,79 @@
"""Shared fixtures for docker-image integration tests.
Tests in this directory build the image with the current ``Dockerfile``
and exercise it via ``docker run``. They skip when Docker is unavailable
(e.g. on developer laptops without a daemon).
Override the image with ``HERMES_TEST_IMAGE`` env var to point at a pre-built
image (faster local iteration); otherwise the ``built_image`` fixture builds
the repo's Dockerfile once per session.
"""
from __future__ import annotations
import os
import shutil
import subprocess
from collections.abc import Iterator
import pytest
IMAGE_TAG = os.environ.get("HERMES_TEST_IMAGE", "hermes-agent-harness:latest")
def _docker_available() -> bool:
"""Return True iff a docker CLI is on PATH and the daemon answers."""
if shutil.which("docker") is None:
return False
try:
r = subprocess.run(
["docker", "info"], capture_output=True, timeout=5,
)
return r.returncode == 0
except (subprocess.TimeoutExpired, OSError):
return False
def pytest_collection_modifyitems(config, items): # noqa: D401 - pytest hook
"""Skip every test under tests/docker/ when docker is unavailable."""
if _docker_available():
return
skip_docker = pytest.mark.skip(
reason="Docker not available or daemon not running",
)
for item in items:
if "tests/docker/" in str(item.fspath).replace(os.sep, "/"):
item.add_marker(skip_docker)
@pytest.fixture(scope="session")
def built_image() -> str:
"""Build the image once per test session.
Override with ``HERMES_TEST_IMAGE`` env var to point at a pre-built
image (faster local iteration).
"""
if os.environ.get("HERMES_TEST_IMAGE"):
return IMAGE_TAG
repo_root = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", ".."),
)
result = subprocess.run(
["docker", "build", "-t", IMAGE_TAG, repo_root],
capture_output=True, text=True, timeout=1200,
)
assert result.returncode == 0, (
f"docker build failed:\n{result.stderr[-2000:]}"
)
return IMAGE_TAG
@pytest.fixture
def container_name(request) -> Iterator[str]:
"""Generate a unique container name and ensure cleanup on test exit."""
safe = request.node.name.replace("[", "_").replace("]", "_")
name = f"hermes-test-{safe}"
yield name
subprocess.run(
["docker", "rm", "-f", name],
capture_output=True, timeout=10,
)