121 lines
4.3 KiB
Python
121 lines
4.3 KiB
Python
"""Regression coverage for #88654.
|
|
|
|
``hermes update`` relaunches manually-run profile gateways through
|
|
``_prepare_profile_gateway_update_restart``. When the profile-derived
|
|
relaunch could not be armed the helper returned ``None``, and the update
|
|
path's response to ``None`` was a bare ``continue`` -- so the gateway was
|
|
neither relaunched, nor stopped, nor mentioned. It kept serving from
|
|
pre-update modules while the new code sat on disk, and every lazy import
|
|
from that point mixed versions.
|
|
|
|
The helper now falls back to replaying the process's own captured command
|
|
line via ``launch_detached_gateway_restart_by_cmdline`` -- the companion
|
|
that already exists for gateways with no profile mapping, and that the
|
|
Windows post-update path already uses for exactly this case.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
import hermes_cli.gateway as gateway
|
|
|
|
|
|
_ARGV = ["python", "-m", "hermes_cli.main", "gateway", "run"]
|
|
|
|
|
|
def _stub_argv(monkeypatch, argv):
|
|
monkeypatch.setattr(gateway, "_capture_gateway_argv", lambda _pid: argv)
|
|
|
|
|
|
def test_profile_relaunch_wins_and_skips_the_cmdline_replay(monkeypatch):
|
|
"""The existing path is unchanged: a profile relaunch short-circuits."""
|
|
_stub_argv(monkeypatch, list(_ARGV))
|
|
monkeypatch.setattr(
|
|
gateway, "launch_detached_profile_gateway_restart", lambda *_a: True
|
|
)
|
|
monkeypatch.setattr(
|
|
gateway,
|
|
"launch_detached_gateway_restart_by_cmdline",
|
|
lambda *_a: pytest.fail("cmdline replay must not run when the profile path works"),
|
|
)
|
|
|
|
assert gateway._prepare_profile_gateway_update_restart("fitness", 4242) == "detached"
|
|
|
|
|
|
def test_falls_back_to_cmdline_replay_when_profile_relaunch_fails(monkeypatch):
|
|
"""The #88654 fix: an unarmable profile relaunch still gets the gateway back."""
|
|
_stub_argv(monkeypatch, list(_ARGV))
|
|
monkeypatch.setattr(
|
|
gateway, "launch_detached_profile_gateway_restart", lambda *_a: False
|
|
)
|
|
seen = []
|
|
|
|
def _by_cmdline(pid, argv):
|
|
seen.append((pid, argv))
|
|
return True
|
|
|
|
monkeypatch.setattr(
|
|
gateway, "launch_detached_gateway_restart_by_cmdline", _by_cmdline
|
|
)
|
|
|
|
assert (
|
|
gateway._prepare_profile_gateway_update_restart("fitness", 4242)
|
|
== "detached-cmdline"
|
|
)
|
|
# Replays the process's OWN argv, which is the whole point: the profile
|
|
# could not be mapped back to a run argv, so the captured one is the only
|
|
# faithful description of how to restart it.
|
|
assert seen == [(4242, _ARGV)]
|
|
|
|
|
|
def test_returns_none_when_there_is_no_argv_to_replay(monkeypatch):
|
|
"""No captured argv means no honest way to relaunch; caller must be told."""
|
|
_stub_argv(monkeypatch, [])
|
|
monkeypatch.setattr(
|
|
gateway, "launch_detached_profile_gateway_restart", lambda *_a: False
|
|
)
|
|
monkeypatch.setattr(
|
|
gateway,
|
|
"launch_detached_gateway_restart_by_cmdline",
|
|
lambda *_a: pytest.fail("must not replay an empty argv"),
|
|
)
|
|
|
|
assert gateway._prepare_profile_gateway_update_restart("fitness", 4242) is None
|
|
|
|
|
|
def test_returns_none_when_both_relaunch_paths_fail(monkeypatch):
|
|
"""Both mechanisms failing is still reported as None, not a false success."""
|
|
_stub_argv(monkeypatch, list(_ARGV))
|
|
monkeypatch.setattr(
|
|
gateway, "launch_detached_profile_gateway_restart", lambda *_a: False
|
|
)
|
|
monkeypatch.setattr(
|
|
gateway, "launch_detached_gateway_restart_by_cmdline", lambda *_a: False
|
|
)
|
|
|
|
assert gateway._prepare_profile_gateway_update_restart("fitness", 4242) is None
|
|
|
|
|
|
def test_external_supervisor_still_short_circuits_before_any_replay(monkeypatch):
|
|
"""Guardrail: the supervisor hand-back must not gain a replay behind it.
|
|
|
|
Replaying the argv for an externally supervised gateway would escape the
|
|
manager and race its replacement process, which is the exact hazard the
|
|
supervisor branch exists to avoid.
|
|
"""
|
|
_stub_argv(monkeypatch, _ARGV + ["--external-supervisor"])
|
|
monkeypatch.setattr(
|
|
gateway,
|
|
"launch_detached_profile_gateway_restart",
|
|
lambda *_a: pytest.fail("detached watcher must not be launched"),
|
|
)
|
|
monkeypatch.setattr(
|
|
gateway,
|
|
"launch_detached_gateway_restart_by_cmdline",
|
|
lambda *_a: pytest.fail("cmdline replay must not be launched"),
|
|
)
|
|
|
|
assert (
|
|
gateway._prepare_profile_gateway_update_restart("fitness", 4242)
|
|
== "external-supervisor"
|
|
)
|