fix(gateway): implement platform-aware PID termination

This commit is contained in:
H-5-Isminiz 2026-04-09 23:48:46 +03:00 committed by Teknium
parent 9bb8cb8d83
commit 00dd5cc491
6 changed files with 138 additions and 25 deletions

View file

@ -1,6 +1,5 @@
"""Tests for hermes_cli.gateway."""
import signal
from types import SimpleNamespace
from unittest.mock import patch, call
@ -211,8 +210,7 @@ class TestWaitForGatewayExit:
assert poll_count == 3
def test_force_kills_after_grace_period(self, monkeypatch):
"""When the process doesn't exit, SIGKILL the saved PID."""
import time as _time
"""When the process doesn't exit, force-kill the saved PID."""
# Simulate monotonic time advancing past force_after
call_num = 0
@ -224,8 +222,8 @@ class TestWaitForGatewayExit:
return call_num * 2.0 # 2, 4, 6, 8, ...
kills = []
def mock_kill(pid, sig):
kills.append((pid, sig))
def mock_terminate(pid, force=False):
kills.append((pid, force))
# get_running_pid returns the PID until kill is sent, then None
def mock_get_running_pid():
@ -234,14 +232,13 @@ class TestWaitForGatewayExit:
monkeypatch.setattr("time.monotonic", fake_monotonic)
monkeypatch.setattr("time.sleep", lambda _: None)
monkeypatch.setattr("gateway.status.get_running_pid", mock_get_running_pid)
monkeypatch.setattr("os.kill", mock_kill)
monkeypatch.setattr(gateway, "terminate_pid", mock_terminate)
gateway._wait_for_gateway_exit(timeout=10.0, force_after=5.0)
assert (42, signal.SIGKILL) in kills
assert (42, True) in kills
def test_handles_process_already_gone_on_kill(self, monkeypatch):
"""ProcessLookupError during SIGKILL is not fatal."""
import time as _time
"""ProcessLookupError during force-kill is not fatal."""
call_num = 0
def fake_monotonic():
@ -249,13 +246,24 @@ class TestWaitForGatewayExit:
call_num += 1
return call_num * 3.0 # Jump past force_after quickly
def mock_kill(pid, sig):
def mock_terminate(pid, force=False):
raise ProcessLookupError
monkeypatch.setattr("time.monotonic", fake_monotonic)
monkeypatch.setattr("time.sleep", lambda _: None)
monkeypatch.setattr("gateway.status.get_running_pid", lambda: 99)
monkeypatch.setattr("os.kill", mock_kill)
monkeypatch.setattr(gateway, "terminate_pid", mock_terminate)
# Should not raise — ProcessLookupError means it's already gone.
gateway._wait_for_gateway_exit(timeout=10.0, force_after=2.0)
def test_kill_gateway_processes_force_uses_helper(self, monkeypatch):
calls = []
monkeypatch.setattr(gateway, "find_gateway_pids", lambda exclude_pids=None: [11, 22])
monkeypatch.setattr(gateway, "terminate_pid", lambda pid, force=False: calls.append((pid, force)))
killed = gateway.kill_gateway_processes(force=True)
assert killed == 2
assert calls == [(11, True), (22, True)]