"""Browser cleanup fallback contracts, retained after retiring the npx warmer.""" import os import signal from unittest.mock import MagicMock, patch import pytest from tools.browser_tool_lifecycle import _legacy_kill_process_tree @pytest.mark.platforms("posix") def test_posix_kills_process_group_term_then_kill(monkeypatch): proc = MagicMock(pid=999) monkeypatch.setattr(os, "getpgid", lambda pid: 999) calls = [] monkeypatch.setattr(os, "killpg", lambda pgid, sig: calls.append((pgid, sig))) _legacy_kill_process_tree(proc) assert calls == [(999, signal.SIGTERM), (999, signal.SIGKILL)] @pytest.mark.platforms("posix") def test_posix_missing_process_returns_silently(monkeypatch): def missing(pid): raise ProcessLookupError() monkeypatch.setattr(os, "getpgid", missing) _legacy_kill_process_tree(MagicMock(pid=999)) @pytest.mark.platforms("posix") @pytest.mark.parametrize("kill_error", [None, OSError("already reaped")]) def test_missing_killpg_falls_back_to_proc_kill(monkeypatch, kill_error): proc = MagicMock(pid=999) proc.kill.side_effect = kill_error monkeypatch.delattr(os, "killpg", raising=False) _legacy_kill_process_tree(proc) proc.kill.assert_called_once() @pytest.mark.platforms("posix") def test_permission_denied_does_not_attempt_sigkill(monkeypatch): monkeypatch.setattr(os, "getpgid", lambda pid: 999) calls = [] def denied(pgid, sig): calls.append((pgid, sig)) raise PermissionError() monkeypatch.setattr(os, "killpg", denied) _legacy_kill_process_tree(MagicMock(pid=999)) assert calls == [(999, signal.SIGTERM)] @pytest.mark.platforms("windows") @pytest.mark.parametrize("error", [None, OSError("taskkill missing")]) def test_windows_taskkill_targets_tree_and_is_best_effort(error): with patch("subprocess.run", side_effect=error) as run: _legacy_kill_process_tree(MagicMock(pid=4321)) assert run.call_args.args[0] == ["taskkill", "/PID", "4321", "/T", "/F"]