diff --git a/tests/gateway/test_update_command.py b/tests/gateway/test_update_command.py index fa223a42f..5cc7f206e 100644 --- a/tests/gateway/test_update_command.py +++ b/tests/gateway/test_update_command.py @@ -51,10 +51,16 @@ class TestHandleUpdateCommand: event = _make_event() monkeypatch.setenv("HERMES_MANAGED", "homebrew") - result = await runner._handle_update_command(event) + # Guard: prevent any accidental fall-through from spawning a real + # `hermes update --gateway` against the CI checkout. The managed-install + # guard should return before Popen is ever reached, but mock it as + # belt-and-suspenders so a premature return doesn't corrupt the repo. + with patch("subprocess.Popen") as mock_popen: + result = await runner._handle_update_command(event) assert "managed by Homebrew" in result assert "brew upgrade hermes-agent" in result + mock_popen.assert_not_called() # must return before reaching Popen @pytest.mark.asyncio async def test_no_git_directory(self, tmp_path): @@ -388,16 +394,16 @@ class TestUpdateCommandPlatformGate: blocked by the allowlist gate before any side effects fire.""" runner = _make_runner() event = _make_event(platform=Platform.WEBHOOK) - # Stop _handle_update_command from progressing further if the gate - # somehow lets the event through — the assertion on the returned - # string is the real test. monkeypatch.setenv("HERMES_MANAGED", "") - result = await runner._handle_update_command(event) + # Guard: platform gate must fire before any real subprocess spawn. + with patch("subprocess.Popen") as mock_popen: + result = await runner._handle_update_command(event) # The exact rejection message comes from # ``gateway.update.platform_not_messaging`` translation key. assert "only available from messaging platforms" in result + mock_popen.assert_not_called() @pytest.mark.asyncio async def test_blocks_api_server_platform(self, monkeypatch): @@ -408,9 +414,11 @@ class TestUpdateCommandPlatformGate: event = _make_event(platform=Platform.API_SERVER) monkeypatch.setenv("HERMES_MANAGED", "") - result = await runner._handle_update_command(event) + with patch("subprocess.Popen") as mock_popen: + result = await runner._handle_update_command(event) assert "only available from messaging platforms" in result + mock_popen.assert_not_called() @pytest.mark.asyncio async def test_allows_plugin_platform_via_registry_fallback(self, monkeypatch): @@ -439,7 +447,8 @@ class TestUpdateCommandPlatformGate: event = _make_event(platform=Platform.DISCORD) monkeypatch.setenv("HERMES_MANAGED", "") - result = await runner._handle_update_command(event) + with patch("subprocess.Popen"): + result = await runner._handle_update_command(event) # The gate must NOT have rejected us — anything other than the # ``platform_not_messaging`` rejection string is acceptable here. @@ -467,7 +476,8 @@ class TestUpdateCommandPlatformGate: event = _make_event(platform=Platform.MATTERMOST) monkeypatch.setenv("HERMES_MANAGED", "") - result = await runner._handle_update_command(event) + with patch("subprocess.Popen"): + result = await runner._handle_update_command(event) assert "only available from messaging platforms" not in result @@ -492,7 +502,8 @@ class TestUpdateCommandPlatformGate: event = _make_event(platform=Platform.HOMEASSISTANT) monkeypatch.setenv("HERMES_MANAGED", "") - result = await runner._handle_update_command(event) + with patch("subprocess.Popen"): + result = await runner._handle_update_command(event) assert "only available from messaging platforms" not in result @@ -509,7 +520,8 @@ class TestUpdateCommandPlatformGate: event = _make_event(platform=Platform.TELEGRAM) monkeypatch.setenv("HERMES_MANAGED", "") - result = await runner._handle_update_command(event) + with patch("subprocess.Popen"): + result = await runner._handle_update_command(event) assert "only available from messaging platforms" not in result