Files
hermes-agent/tests/gateway/test_systemd_stop_mark.py
ethernet 9f837d298b Merge remote-tracking branch 'origin/main' into ethie/pm-clean
Conflicts resolved toward the PM model: main's lazy_deps/update_cmd_deps/npm
stamp machinery stays deleted (PM + scripts/build/node-deps.mjs own it), the
systemd ExecStop stop-mark rides the installation launcher, legacy
linux_only/macos_only/windows_only markers are rewritten to platforms(), and
finalize_update_receipt carries pending manual-serve obligations forward
again (lost when the ContextVar receipt rewrite crossed c0aa3ce354).

Test harness: the real-home I/O guard exempts /proc/<pid>/fd metadata reads
(deleted-WAL holder scans) and run_tests.sh drops ~/.hermes PATH entries so
shutil.which() cannot trip the tripwire.
2026-09-20 10:07:50 -04:00

60 lines
2.2 KiB
Python

"""Supervisor-ordered stops exit cleanly instead of logging a failure.
Raw ``systemctl restart/stop`` sends SIGTERM with no marker, so the gateway
used to classify it as an unexpected kill (exit 1, ``Failed with result
exit-code``). The generated unit now marks the stop via ``ExecStop=`` first.
"""
from __future__ import annotations
import pytest
from gateway import systemd_stop_mark
def test_stop_mark_writes_planned_stop_for_supervised_pid(monkeypatch):
"""MAINPID (or an explicit argv PID) is marked; the helper owns identity."""
marked: list[int] = []
monkeypatch.setattr(
"gateway.status.write_planned_stop_marker",
lambda pid: marked.append(pid) or True,
)
monkeypatch.setenv("MAINPID", "4242")
assert systemd_stop_mark.main(["systemd_stop_mark"]) == 0
assert marked == [4242]
marked.clear()
monkeypatch.delenv("MAINPID")
assert systemd_stop_mark.main(["systemd_stop_mark", "7777"]) == 0
assert marked == [7777]
def test_stop_mark_never_blocks_restart_and_unit_wires_exec_stop(monkeypatch):
"""Unknown PIDs mark nothing but still exit 0; the unit runs the marker."""
marked: list[int] = []
monkeypatch.setattr(
"gateway.status.write_planned_stop_marker",
lambda pid: marked.append(pid) or True,
)
for argv, env in ((["m"], None), (["m", "0"], None), (["m", "abc"], None)):
if env is None:
monkeypatch.delenv("MAINPID", raising=False)
assert systemd_stop_mark.main(argv) == 0
assert marked == []
def _boom(_pid):
raise OSError("disk gone")
monkeypatch.setattr("gateway.status.write_planned_stop_marker", _boom)
monkeypatch.setenv("MAINPID", "4242")
assert systemd_stop_mark.main(["m"]) == 0
from hermes_cli import gateway as gateway_cli
unit = gateway_cli.generate_systemd_unit(system=False)
# Both modules run through the installation launcher (`hermes --run-module <mod>`).
assert "ExecStop=-" in unit and "gateway.systemd_stop_mark" in unit
# The cgroup reaper still runs after the main process exits.
assert "ExecStopPost=-" in unit and "gateway.cgroup_cleanup" in unit
assert unit.index("systemd_stop_mark") < unit.index("cgroup_cleanup")