mirror of
https://github.com/usestrix/strix.git
synced 2026-10-11 03:37:54 +00:00
fix: show scan error as TUI toast before exit
Scan errors were stored in _scan_error and re-raised only after the user closed the TUI, making the app appear stuck while producing a confusing post-exit traceback. Extract _notify_scan_error and call it from all three error branches in _start_scan_thread so a persistent Textual toast is shown immediately when the scan thread fails.
This commit is contained in:
parent
c91e4ca1b9
commit
4bc1430d23
4 changed files with 60 additions and 0 deletions
|
|
@ -1378,12 +1378,15 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||
except (ConnectionError, TimeoutError) as e:
|
||||
logging.exception("Network error during scan")
|
||||
self._scan_error = e
|
||||
self._notify_scan_error(e)
|
||||
except RuntimeError as e:
|
||||
logging.exception("Runtime error during scan")
|
||||
self._scan_error = e
|
||||
self._notify_scan_error(e)
|
||||
except Exception as e:
|
||||
logging.exception("Unexpected error during scan")
|
||||
self._scan_error = e
|
||||
self._notify_scan_error(e)
|
||||
finally:
|
||||
with contextlib.suppress(Exception):
|
||||
loop.run_until_complete(
|
||||
|
|
@ -1399,6 +1402,16 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||
self._scan_thread = threading.Thread(target=scan_target, daemon=True)
|
||||
self._scan_thread.start()
|
||||
|
||||
def _notify_scan_error(self, exc: Exception) -> None:
|
||||
"""Dispatch a persistent error toast from any thread."""
|
||||
with contextlib.suppress(Exception):
|
||||
self.call_from_thread(
|
||||
self.notify,
|
||||
f"Scan failed: {type(exc).__name__}: {exc}",
|
||||
severity="error",
|
||||
timeout=0,
|
||||
)
|
||||
|
||||
def _capture_sdk_event(self, agent_id: str, event: Any) -> None:
|
||||
try:
|
||||
self.call_from_thread(self._record_sdk_event, agent_id, event)
|
||||
|
|
|
|||
0
tests/interface/__init__.py
Normal file
0
tests/interface/__init__.py
Normal file
0
tests/interface/tui/__init__.py
Normal file
0
tests/interface/tui/__init__.py
Normal file
47
tests/interface/tui/test_app.py
Normal file
47
tests/interface/tui/test_app.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"""Tests for StrixTUIApp._notify_scan_error — Fix 2: in-TUI error display."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from strix.interface.tui.app import StrixTUIApp
|
||||
|
||||
|
||||
def _mock_app() -> MagicMock:
|
||||
return MagicMock(spec=StrixTUIApp)
|
||||
|
||||
|
||||
def test_notify_scan_error_dispatches_toast() -> None:
|
||||
"""_notify_scan_error must call notify via call_from_thread with error text."""
|
||||
app = _mock_app()
|
||||
exc = RuntimeError("something went wrong")
|
||||
|
||||
StrixTUIApp._notify_scan_error(app, exc)
|
||||
|
||||
app.call_from_thread.assert_called_once_with(
|
||||
app.notify,
|
||||
"Scan failed: RuntimeError: something went wrong",
|
||||
severity="error",
|
||||
timeout=0,
|
||||
)
|
||||
|
||||
|
||||
def test_notify_scan_error_suppresses_call_from_thread_failure() -> None:
|
||||
"""If call_from_thread itself raises, the exception must be swallowed."""
|
||||
app = _mock_app()
|
||||
app.call_from_thread.side_effect = RuntimeError("TUI not ready")
|
||||
|
||||
# Must not propagate the exception.
|
||||
StrixTUIApp._notify_scan_error(app, ValueError("boom"))
|
||||
|
||||
|
||||
def test_notify_scan_error_formats_exception_type() -> None:
|
||||
"""Toast text must include the exception's class name and message."""
|
||||
app = _mock_app()
|
||||
exc = ConnectionError("host unreachable")
|
||||
|
||||
StrixTUIApp._notify_scan_error(app, exc)
|
||||
|
||||
args = app.call_from_thread.call_args.args
|
||||
assert "ConnectionError" in args[1]
|
||||
assert "host unreachable" in args[1]
|
||||
Loading…
Add table
Reference in a new issue