hermes-agent/tests/docker/test_immutable_install.py
ethernet 3e89edf830 perf(docker-tests): share containers across read-only tests
Add a module-scoped `shared_container` fixture to tests/docker/conftest.py
that boots one `sleep infinity` container per test module and tears it
down at module exit. Convert read-only tests that previously used
`docker run --rm --entrypoint sh/cat/test/su` (bypassing s6 to check
static image properties) or `docker run -d` + `docker exec` (starting
identical containers per test) to use `docker exec` on the shared
container instead.

Converted files:
  test_immutable_install_permissions.py — 2 throwaway runs → 2 execs
  test_license_file_present.py          — 1 throwaway run → 1 exec
  test_tini_compat_shim.py              — 1 throwaway run → 1 exec
  test_tui_prebuilt_bundle.py           — 2 throwaway runs → 2 execs
  test_dump_build_sha.py                — 2 throwaway runs → 2 execs
  test_immutable_install.py             — 3 detached runs → 1 shared + 1 isolated
  test_dashboard.py                     — 2 detached runs → 0 (use shared)

Local profiling shows docker run calls in these 7 files dropped from
~25 to 7 (the 7 are shared_container boots per module + the one test
that needs a restart). Each eliminated `docker run` was paying 1-9s
of s6 cont-init startup; the replacement `docker exec` calls average
0.10s — an ~50x speedup per operation.

Tests that mutate state (restarts, config changes, gateway starts)
still use their own containers via `container_name` + `start_container`.
2026-07-14 02:10:00 -04:00

138 lines
4.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Runtime smoke tests for Docker immutable install tree and install-method stamp.
Build the real image and verify at runtime:
1. /opt/hermes is not writable by the hermes user (immutable install tree)
2. PYTHONDONTWRITEBYTECODE and HERMES_DISABLE_LAZY_INSTALLS are set
3. /opt/hermes/.install_method contains "docker" (code-scoped stamp)
4. $HERMES_HOME/.install_method is NOT stamped as "docker" by stage2
5. A stale "docker" stamp in $HERMES_HOME is healed (removed) on boot
Tests 14 are read-only checks against the default container state and
share the module-scoped ``shared_container`` fixture. Test 5 mutates
state and triggers a restart, so it uses its own container.
"""
from __future__ import annotations
from tests.docker.conftest import (
docker_exec,
docker_exec_sh,
restart_container,
start_container,
)
def test_install_tree_not_writable_by_hermes(
shared_container: str,
) -> None:
"""The hermes user must not be able to modify /opt/hermes.
The install tree (source, venv, TUI bundle, node_modules) must remain
root-owned and non-writable so an agent session cannot self-modify
the installation and brick the gateway.
"""
r = docker_exec_sh(
shared_container,
# Try to create a file under /opt/hermes as the hermes user
"touch /opt/hermes/test_write 2>&1 && "
"echo WRITE_SUCCEEDED || echo WRITE_FAILED",
timeout=10,
)
assert "WRITE_FAILED" in r.stdout, (
f"hermes user can write to /opt/hermes (install tree not immutable): "
f"{r.stdout}"
)
# Also check a key subdirectory
r = docker_exec_sh(
shared_container,
"touch /opt/hermes/.venv/test_write 2>&1 && "
"echo WRITE_SUCCEEDED || echo WRITE_FAILED",
timeout=10,
)
assert "WRITE_FAILED" in r.stdout, (
f"hermes user can write to /opt/hermes/.venv: {r.stdout}"
)
def test_hermes_disable_lazy_installs_and_dont_write_bytecode(
shared_container: str,
) -> None:
"""The container must set PYTHONDONTWRITEBYTECODE and
HERMES_DISABLE_LAZY_INSTALLS=1 so no .pyc files are written to the
immutable install tree and no lazy installs attempt to modify it."""
r = docker_exec_sh(
shared_container,
'test "$PYTHONDONTWRITEBYTECODE" = "1" && '
'test "$HERMES_DISABLE_LAZY_INSTALLS" = "1" && '
'echo ENV_OK || echo ENV_MISSING',
timeout=10,
)
assert "ENV_OK" in r.stdout, (
f"expected PYTHONDONTWRITEBYTECODE=1 and "
f"HERMES_DISABLE_LAZY_INSTALLS=1, got: {r.stdout} stderr={r.stderr}"
)
def test_install_method_stamp_is_code_scoped(
shared_container: str,
) -> None:
"""The 'docker' install-method stamp must be baked at
/opt/hermes/.install_method (code-scoped), NOT in $HERMES_HOME."""
# Code-scoped stamp must exist and say "docker"
r = docker_exec_sh(
shared_container,
"cat /opt/hermes/.install_method",
timeout=10,
)
assert r.returncode == 0, (
f"/opt/hermes/.install_method not found: {r.stderr}"
)
assert r.stdout.strip() == "docker", (
f"expected 'docker' stamp, got: {r.stdout.strip()!r}"
)
# $HERMES_HOME must NOT have a 'docker' stamp
r = docker_exec_sh(
shared_container,
"cat /opt/data/.install_method 2>/dev/null || echo NONE",
timeout=10,
)
assert r.stdout.strip() != "docker", (
"$HERMES_HOME/.install_method is stamped 'docker' - stage2 must "
"not stamp the data volume (shared with host installs)"
)
def test_stale_docker_stamp_in_home_is_healed_on_boot(
built_image: str, container_name: str,
) -> None:
"""A stale 'docker' stamp left in $HERMES_HOME by an older image
must be removed on boot so shared homes self-heal."""
# Start container, write a stale stamp
start_container(built_image, container_name)
# Write a stale 'docker' stamp as root
docker_exec(
container_name, "sh", "-c",
"printf 'docker\\n' > /opt/data/.install_method",
user="root", timeout=5,
)
# Verify it exists
r = docker_exec_sh(container_name, "cat /opt/data/.install_method", timeout=5)
assert r.stdout.strip() == "docker"
# Restart - stage2 should heal it
restart_container(container_name)
# The stale stamp must be gone
r = docker_exec_sh(
container_name,
"test -f /opt/data/.install_method && "
"cat /opt/data/.install_method || echo HEALED",
timeout=10,
)
assert "HEALED" in r.stdout or r.stdout.strip() != "docker", (
f"stale 'docker' stamp in $HERMES_HOME was not healed on boot: "
f"{r.stdout}"
)