Files
hermes-agent/tests/hermes_cli/test_update_gateway_launcher_refresh.py
T
ethernet 30da5d0a89 test: run os-specific tests on their real host, not a faked one
many tests patched sys.platform or a module's _IS_WINDOWS flag, then
ran on linux ci. the patch selects the branch under test, but the host
does not have the behavior the branch exists for. the test proves the
patch, not the platform. some gated assertions never ran on any host.

this commit adds three markers: linux_only, macos_only, windows_only.
a conftest hook skips a marked test on the other hosts, with a clear
reason. no test fakes a host now. two documented fakes remain
(android/termux, freebsd) because no ci runner exists for them.

each fake site got one of four treatments:
- gate it: the real host supplies the platform; mocks cover real
  dependencies only, never host identity
- patch the module's own probe when the subject is the probe's consumer
- assert against the real host when the fake stood in for any non-x host
- delete the patch when it set the value the host already has

bare skipif(sys.platform != ...) guards became markers too. the lane
model skips these on linux and never imports them on windows, so they
ran on no host. platform parametrize tables are now one marked test
per os.

running on real hosts found real errors: a chrome-sandbox failure in
test_gui_command that main hides, and two windows failures fixed here.
the agents.md testing section now documents the policy.
2026-08-09 22:09:49 -04:00

95 lines
3.4 KiB
Python

"""Legacy pythonw launcher normalization + post-update launcher refresh.
Covers the two halves of the "legacy pythonw gateways survive updates
forever" gap:
1. ``gateway_windows._resolve_detached_python`` — normalizes a legacy
``pythonw.exe`` interpreter (pre-aa2ae36c3f launchers / argv snapshots)
to the sibling console ``python.exe`` so respawns and regenerated
launchers use the hidden-console design (#54220/#56747) and don't die
with ``RuntimeError: sys.stderr is None`` (#71671).
2. ``hermes_cli.main._refresh_windows_gateway_launchers`` — ``hermes
update`` regenerates the installed Scheduled Task / Startup launcher
scripts instead of leaving install-time artifacts stale forever.
``_resolve_detached_python`` is a pure path helper and runs on any host.
``windowless_gateway_restart_spec`` returns its argv unchanged off Windows,
so the test that exercises the rewrite is ``windows_only`` rather than run
against a faked ``sys.platform``.
"""
from __future__ import annotations
from pathlib import Path
from unittest import mock
import pytest
import hermes_cli.gateway_windows as gateway_windows
import hermes_cli.main as cli_main
# ---------------------------------------------------------------------------
# _resolve_detached_python: legacy pythonw normalization
# ---------------------------------------------------------------------------
def _make_venv(tmp_path: Path, *, with_console_python: bool) -> tuple[Path, Path]:
scripts = tmp_path / "venv" / "Scripts"
scripts.mkdir(parents=True)
pythonw = scripts / "pythonw.exe"
pythonw.write_text("", encoding="utf-8")
python = scripts / "python.exe"
if with_console_python:
python.write_text("", encoding="utf-8")
return pythonw, python
def test_resolve_detached_python_swaps_legacy_pythonw_for_console_sibling(tmp_path):
pythonw, python = _make_venv(tmp_path, with_console_python=True)
exe, venv_dir, extra = gateway_windows._resolve_detached_python(str(pythonw))
assert exe == str(python)
assert venv_dir == tmp_path / "venv"
assert extra == []
@pytest.mark.windows_only
def test_restart_spec_normalizes_legacy_pythonw_argv(tmp_path):
"""A pre-rework Scheduled Task argv snapshot (leading pythonw.exe) must be
respawned through the console python + hidden-console launch, with every
argument after the interpreter preserved verbatim.
``windows_only``: ``windowless_gateway_restart_spec`` returns the argv
untouched off Windows, so the fake was the only thing making the rewrite
(and its ``Scripts/``-layout venv derivation) run at all.
"""
pythonw, python = _make_venv(tmp_path, with_console_python=True)
argv = [str(pythonw), "-m", "hermes_cli.main", "gateway", "run"]
with mock.patch.object(
gateway_windows, "_stable_gateway_working_dir", return_value=str(tmp_path)
), mock.patch("hermes_cli.config.get_hermes_home", return_value=str(tmp_path)):
new_argv, cwd, env = gateway_windows.windowless_gateway_restart_spec(list(argv))
assert new_argv[0] == str(python)
assert new_argv[1:] == argv[1:]
assert cwd == str(tmp_path)
assert env["VIRTUAL_ENV"] == str(tmp_path / "venv")
# ---------------------------------------------------------------------------
# _refresh_windows_gateway_launchers: hermes update regenerates launchers
# ---------------------------------------------------------------------------