Files
hermes-agent/tests/gateway/test_process_cmdline_source.py
teknium1 b93d68e424 test: trim cmdline-source tests to the two invariants
Keep the "psutil answers without a fork" and "ps stays the fallback when psutil is denied" cases; the dead-PID and agreement cases are change-detectors.
2026-09-20 15:25:46 -07:00

71 lines
2.2 KiB
Python

"""``_read_process_cmdline`` asks the cheapest source that can answer, and still answers.
The gateway-identity check reads a live PID's command line, and `list_profiles` runs it per profile
with a live gateway — on every 5s roster poll, per connection. On macOS there is no ``/proc``, so
this used to fork ``ps`` every time (measured 4.2ms) when psutil answers the same string in 0.02ms.
psutil cannot always answer (macOS raises ``AccessDenied`` across users), so ``ps`` must remain the
fallback, not be replaced.
"""
from __future__ import annotations
import os
import subprocess
import sys
import time
import pytest
from gateway import status
@pytest.fixture
def no_proc(monkeypatch):
"""Force the non-Linux shape: /proc unreadable, so the source choice is psutil vs ps."""
real_read_bytes = status.Path.read_bytes
def _read_bytes(self, *a, **kw):
if str(self).startswith("/proc/"):
raise OSError("no /proc")
return real_read_bytes(self, *a, **kw)
monkeypatch.setattr(status.Path, "read_bytes", _read_bytes)
def _no_fork(monkeypatch):
"""Fail loudly if anything forks a subprocess."""
def _boom(*a, **kw):
raise AssertionError(f"forked a subprocess: {a[0] if a else kw}")
monkeypatch.setattr(status.subprocess, "run", _boom)
def test_psutil_answers_without_forking_ps(no_proc, monkeypatch):
_no_fork(monkeypatch)
cmdline = status._read_process_cmdline(os.getpid())
assert cmdline and sys.executable.split("/")[-1] in cmdline
def test_ps_still_answers_when_psutil_cannot(no_proc, monkeypatch):
"""macOS raises AccessDenied across users; ps still reports those, so it stays the fallback."""
import psutil
def _denied(_pid):
raise psutil.AccessDenied(_pid)
monkeypatch.setattr(psutil, "Process", _denied)
forked: list = []
real_run = status.subprocess.run
def _watching(cmd, *a, **kw):
forked.append(cmd)
return real_run(cmd, *a, **kw)
monkeypatch.setattr(status.subprocess, "run", _watching)
cmdline = status._read_process_cmdline(os.getpid())
assert cmdline, "the ps fallback must still answer when psutil refuses"
assert forked and forked[0][:2] == ["ps", "-p"]