"""Regression coverage for stdio inline-handler failures (#112816). ``entry.main()`` used to call ``dispatch(req)`` bare, so an exception from an inline (non-pool) handler unwound the read loop and killed the gateway child, losing the in-flight reply and wedging the TUI until a restart. """ from __future__ import annotations import io import json from tui_gateway import entry def test_main_reports_inline_handler_error_and_keeps_reading(monkeypatch): """A failed inline request must not prevent the next request from replying.""" replies: list[dict] = [] breadcrumbs: list[str] = [] stdin_text = "".join(json.dumps(req) + "\n" for req in ( {"jsonrpc": "2.0", "id": "broken", "method": "clipboard.save"}, {"jsonrpc": "2.0", "id": "next", "method": "ping"}, )) monkeypatch.setattr(entry, "_install_sidecar_publisher", lambda: None) monkeypatch.setattr(entry, "ensure_mcp_discovery_started", lambda: None) monkeypatch.setattr(entry, "resolve_skin", lambda: "default") monkeypatch.setattr(entry.server, "_start_backend_heartbeat_refresher", lambda: None) monkeypatch.setattr(entry.server, "_schedule_startup_orphan_sweep", lambda: None) monkeypatch.setattr(entry.server, "_ensure_skin_watcher", lambda: None) monkeypatch.setattr(entry, "handle_spurious_eof", lambda *_args: False) monkeypatch.setattr(entry, "write_json", lambda payload: replies.append(payload) or True) monkeypatch.setattr(entry, "_append_crash_log", lambda header, dump=None: breadcrumbs.append(header)) monkeypatch.setattr(entry.sys, "stdin", io.StringIO(stdin_text)) def dispatch(req): if req["id"] == "broken": raise NameError("clipboard backend is unavailable") return {"jsonrpc": "2.0", "id": req["id"], "result": {"ok": True}} monkeypatch.setattr(entry, "dispatch", dispatch) entry.main() assert replies[1]["id"] == "broken" assert replies[1]["error"]["code"] == -32000 assert "clipboard backend is unavailable" in replies[1]["error"]["message"] assert replies[2] == {"jsonrpc": "2.0", "id": "next", "result": {"ok": True}} # Forensics: the survived crash leaves the same crash-log trail a fatal one would. assert len(breadcrumbs) == 1 and "clipboard.save" in breadcrumbs[0]