"""Issue #45 (B), review round 2 R2: resume closes a restored pane only when herdr's answer proves the shell is in the foreground. Odd pid values - missing, null, 0, negative, bool, string - must leave the pane alone. herdr is a stub here; the real-server behaviour is tests/test_issue45_restore.py.""" from __future__ import annotations import subprocess import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent / ".agents" / "skills")) from lib_py import herdr_restore as hr # noqa: E402 SID = "probe-id" def _stub(monkeypatch, process_info): calls = [] def fake(herdr, session, *args): calls.append(args) if args[:2] == ("pane", "list"): return {"panes": [{"pane_id": "probe-pane", "agent": None, "agent_session": {"value": SID}}]} if args[:2] == ("pane", "process-info"): return {"process_info": process_info} return {} monkeypatch.setattr(hr, "_herdr", fake) return calls @pytest.mark.parametrize("shell,fg", [(0, 0), (False, False), ("", ""), (-1, -1), (None, None), ("123", "123"), (True, True), (1.0, 1.0)], ids=["zero", "false", "empty", "negative", "null", "string", "true", "float"]) def test_an_odd_pid_answer_leaves_the_pane_alone(monkeypatch, shell, fg): calls = _stub(monkeypatch, {"shell_pid": shell, "foreground_process_group_id": fg}) assert hr.close_restored_shells(SID, session="m45-review-probe") == [] assert ("pane", "close", "probe-pane") not in calls def test_a_missing_process_info_leaves_the_pane_alone(monkeypatch): calls = _stub(monkeypatch, {}) assert hr.close_restored_shells(SID, session="m45-review-probe") == [] assert ("pane", "close", "probe-pane") not in calls def test_a_real_shell_in_the_foreground_is_closed(monkeypatch): calls = _stub(monkeypatch, {"shell_pid": 4242, "foreground_process_group_id": 4242}) assert hr.close_restored_shells(SID, session="m45-review-probe") == ["probe-pane"] assert ("pane", "close", "probe-pane") in calls def test_a_busy_pane_is_left_alone(monkeypatch): calls = _stub(monkeypatch, {"shell_pid": 4242, "foreground_process_group_id": 4300}) assert hr.close_restored_shells(SID, session="m45-review-probe") == [] assert ("pane", "close", "probe-pane") not in calls # herdr 0.9.3: a pane whose saved directory is gone is restored as a placeholder # with no process; process-info answers pane_not_found (real-server check: # test_issue45_restore.py test_b_only_the_resumed_sessions_restored_shell_is_closed). PLACEHOLDER_ERR = '{"error":{"code":"pane_not_found","message":"pane not found"},"id":"cli:pane:process_info"}' def _stub_failing(monkeypatch, restore_error, stderr): calls = [] def fake(herdr, session, *args): calls.append(args) if args[:2] == ("pane", "list"): pane = {"pane_id": "probe-pane", "agent": None, "agent_session": {"value": SID}} if restore_error: pane["restore_error"] = restore_error return {"panes": [pane]} if args[:2] == ("pane", "process-info"): raise subprocess.CalledProcessError(1, ["herdr", *args], output="", stderr=stderr) return {} monkeypatch.setattr(hr, "_herdr", fake) return calls def test_a_placeholder_for_a_missing_directory_is_closed(monkeypatch): calls = _stub_failing(monkeypatch, "Saved directory is unavailable. Restore the directory and restart " "this session.", PLACEHOLDER_ERR) assert hr.close_restored_shells(SID, session="m45-review-probe") == ["probe-pane"] assert ("pane", "close", "probe-pane") in calls @pytest.mark.parametrize("restore_error,stderr", [ (None, PLACEHOLDER_ERR), # pane_not_found without herdr's marker ("Saved directory is unavailable.", '{"error":{"code":"server_error"}}'), # another failure ], ids=["no-restore-error", "other-error"]) def test_any_other_process_info_failure_still_stops_the_run(monkeypatch, restore_error, stderr): calls = _stub_failing(monkeypatch, restore_error, stderr) with pytest.raises(subprocess.CalledProcessError): hr.close_restored_shells(SID, session="m45-review-probe") assert ("pane", "close", "probe-pane") not in calls