mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Second, deeper pass over tools/gateway/hermes_cli plus first pass over the trees wave 1 missed (acp, acp_adapter, skills, computer_use, docker, dashboard, conformance, monitoring, secret_sources, hermes_state, providers). Same rubric as wave 1 (AGENTS.md test policy); security, alternation/caching invariants, issue-number regressions, and E2E kept. Real test-quality fixes found and rooted out along the way: - tests/tools/test_command_guards.py made real auxiliary-LLM HTTPS calls (DEFAULT_CONFIG smart-approval leaked in) — pinned approval mode=manual via autouse fixture: 17.4s → 0.4s. - test_model_switch_custom_providers.py / test_user_providers_model_switch.py silently probed live provider catalogs (~2s/test) — stubbed cached_provider_model_ids/provider_model_ids/fetch_api_models. - test_telegram_noise_filter.py: 15-platform copy-paste matrix over shared gateway.run logic → 3 representative platforms (55s → 3.9s). - test_gateway_shutdown.py: stop()'s 5s interrupt-deadline loop spun on MagicMock agents — interrupt.side_effect now clears _running_agents (22s → 1.0s). - test_gateway_inactivity_timeout.py poll-harness timings shrunk 3-5x (24s → 1.1s); test_mcp_stability.py backoff/SIGTERM-grace sleeps patched (15.4s → 2.5s); test_async_delegation.py negative-drain wait 5s → 0.5s. - test_telegram_init_deadline.py: loop-block margin restored to 1.0s with rationale comment — the watchdog-dump assertion needs the loop blocked well past deadline+grace under parallel load (flaked once in the 40-worker verification run at a 0.2s margin). Verification: full hermetic suite via scripts/run_tests.sh — 2,438 files, 21,718 tests passed, 0 failed, 293.9s wall. Suite totals vs original baseline: 46,820 → 19,757 test functions (−57.8%), wall 583.5s → 293.9s (−50%), subprocess CPU 13,564s → 11,623s.
110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
"""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
|
|
"""
|
|
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(
|
|
built_image: str, container_name: 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.
|
|
"""
|
|
start_container(built_image, container_name)
|
|
|
|
r = docker_exec_sh(
|
|
container_name,
|
|
# 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(
|
|
container_name,
|
|
"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(
|
|
built_image: str, container_name: 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."""
|
|
start_container(built_image, container_name)
|
|
|
|
r = docker_exec_sh(
|
|
container_name,
|
|
'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_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}"
|
|
)
|