Files
hermes-agent/tests/hermes_cli/test_gateway_proc_fallback.py
ethernet 30da5d0a89 test: run os-specific tests on their real host, not a faked one
many tests patched sys.platform or a module's _IS_WINDOWS flag, then
ran on linux ci. the patch selects the branch under test, but the host
does not have the behavior the branch exists for. the test proves the
patch, not the platform. some gated assertions never ran on any host.

this commit adds three markers: linux_only, macos_only, windows_only.
a conftest hook skips a marked test on the other hosts, with a clear
reason. no test fakes a host now. two documented fakes remain
(android/termux, freebsd) because no ci runner exists for them.

each fake site got one of four treatments:
- gate it: the real host supplies the platform; mocks cover real
  dependencies only, never host identity
- patch the module's own probe when the subject is the probe's consumer
- assert against the real host when the fake stood in for any non-x host
- delete the patch when it set the value the host already has

bare skipif(sys.platform != ...) guards became markers too. the lane
model skips these on linux and never imports them on windows, so they
ran on no host. platform parametrize tables are now one marked test
per os.

running on real hosts found real errors: a chrome-sandbox failure in
test_gui_command that main hides, and two windows failures fixed here.
the agents.md testing section now documents the policy.
2026-08-09 22:09:49 -04:00

115 lines
3.8 KiB
Python

"""Tests for /proc-based gateway PID detection in Docker environments.
Verifies that _scan_gateway_pids() uses /proc/*/cmdline when available
(Docker without procps) and falls back to ps only when /proc is absent.
See: NousResearch/hermes-agent#7622
"""
import os
from unittest.mock import MagicMock, patch
import pytest
import hermes_cli.gateway as gateway_mod
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
_GATEWAY_CMD = "python -m hermes_cli.main gateway run"
_OTHER_CMD = "python -m some_other_thing"
def _fake_proc_dir(entries: dict):
"""Return side_effects that simulate /proc: isdir → True, listdir → pids,
open(cmdline) → null-delimited command bytes."""
def _isdir(path):
return str(path) == "/proc"
def _listdir(path):
if str(path) == "/proc":
return [str(pid) for pid in entries] + ["self", "version"]
raise FileNotFoundError(path)
def _open(path, mode="r", **kwargs):
path_str = str(path)
if "/cmdline" in path_str:
pid = int(path_str.split("/proc/")[1].split("/")[0])
raw = entries.get(pid, "").encode("utf-8").replace(b" ", b"\x00")
m = MagicMock()
m.read.return_value = raw
m.__enter__ = lambda s: s
m.__exit__ = MagicMock(return_value=False)
return m
raise FileNotFoundError(path)
return _isdir, _listdir, _open
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
@pytest.mark.linux_only
class TestProcFallback:
"""_scan_gateway_pids reads /proc when available, skips ps.
Linux-only: ``/proc/<pid>/cmdline`` is the subject. The non-Windows arm of
``_scan_gateway_pids`` is selected by the real host here, so the previous
``is_windows`` stub is gone — only the /proc filesystem itself is faked so
the scan sees deterministic PIDs.
"""
def test_detects_gateway_pid_via_proc(self):
my_pid = os.getpid()
entries = {
my_pid: "python -m hermes_cli.main", # own process — excluded
12345: _GATEWAY_CMD,
99999: _OTHER_CMD,
}
_isdir, _listdir, _open = _fake_proc_dir(entries)
with (
patch("os.path.isdir", side_effect=_isdir),
patch("os.listdir", side_effect=_listdir),
patch("builtins.open", side_effect=_open),
patch("hermes_cli.gateway._get_ancestor_pids", return_value=set()),
patch("subprocess.run") as mock_ps,
):
pids = gateway_mod._scan_gateway_pids(set(), all_profiles=True)
assert 12345 in pids
assert 99999 not in pids
mock_ps.assert_not_called() # ps must NOT be called when /proc worked
def test_proc_permission_error_skips_pid(self):
def _isdir(path):
return str(path) == "/proc"
def _listdir(path):
if str(path) == "/proc":
return ["12345", "self"]
raise FileNotFoundError
def _open(path, mode="r", **kwargs):
raise PermissionError("no access")
with (
patch("os.path.isdir", side_effect=_isdir),
patch("os.listdir", side_effect=_listdir),
patch("builtins.open", side_effect=_open),
patch("hermes_cli.gateway._get_ancestor_pids", return_value=set()),
patch("subprocess.run") as mock_ps,
):
pids = gateway_mod._scan_gateway_pids(set(), all_profiles=True)
# PermissionError swallowed — empty result, no crash
assert 12345 not in pids
mock_ps.assert_not_called() # /proc dir existed, so ps not called