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.
60 lines
2.2 KiB
Python
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")
|