import pytest from transcription.errors import AppError from transcription.errors import ErrorCategory from transcription.ui.components.error_presenter import display_error_category from transcription.ui.components.error_presenter import display_failure_detail from transcription.ui.components.error_presenter import run_ui_action def test_display_error_category_uses_canonical_taxonomy(): assert display_error_category(AppError("x", category=ErrorCategory.USER_INPUT)) == "validation" assert display_error_category(AppError("x", category=ErrorCategory.NOT_FOUND)) == "not_found" assert display_error_category(AppError("x", category=ErrorCategory.CONFLICT)) == "conflict" assert display_error_category(AppError("x", category=ErrorCategory.EXTERNAL_PROVIDER)) == "external" assert display_error_category(AppError("x", category=ErrorCategory.EXTERNAL_TIMEOUT)) == "timeout" assert display_error_category(AppError("x", category=ErrorCategory.INFRA_TRANSIENT)) == "timeout" assert display_error_category(AppError("x", category=ErrorCategory.PROCESSING)) == "internal" assert display_error_category(AppError("x", category=ErrorCategory.INFRA_PERSISTENT)) == "internal" def test_display_failure_detail_returns_none_for_missing_detail(): assert display_failure_detail(None) is None @pytest.mark.parametrize( ("raw_detail", "expected_basename"), [ ( ( "[internal_unexpected_error] Unexpected error during worker.process_job. | " "detail=PermissionError: [Errno 13] Permission denied: '/app/uploads/documents/abc/page-1.jpg' | " "suggestion=Retry once. | error_id=deadbeef" ), "page-1.jpg", ), ( ( "[infrastructure_persistent_error] Prompt file not found: transcribe_document.md | " "detail=FileNotFoundError: [Errno 2] No such file or directory: " r"'C:\app\prompts\transcribe_document.md' | " "suggestion=Verify PROMPT_DIR. | error_id=feedface" ), "transcribe_document.md", ), ], ) def test_display_failure_detail_strips_absolute_paths_from_detail_segment(raw_detail, expected_basename): rendered = display_failure_detail(raw_detail) assert rendered is not None assert rendered.startswith("[") assert "suggestion=" in rendered assert "error_id=" in rendered assert expected_basename in rendered assert "/app/uploads/documents/abc/page-1.jpg" not in rendered assert r"C:\app\prompts\transcribe_document.md" not in rendered @pytest.mark.asyncio async def test_run_ui_action_returns_success_value(): outcome = await run_ui_action( operation="ui.test.success", title="Should not show", action=lambda: _async_value(123), ) assert outcome.ok is True assert outcome.value == 123 @pytest.mark.asyncio async def test_run_ui_action_shows_error_and_returns_failed(monkeypatch): captured: dict[str, object] = {} def _capture(exc: Exception, *, title: str, operation: str) -> None: captured["exc"] = exc captured["title"] = title captured["operation"] = operation monkeypatch.setattr("transcription.ui.components.error_presenter.show_error", _capture) outcome = await run_ui_action( operation="ui.test.failure", title="Load failed", action=lambda: _async_raises(RuntimeError("boom")), ) assert outcome.ok is False assert outcome.value is None assert isinstance(captured["exc"], RuntimeError) assert str(captured["exc"]) == "boom" assert captured["title"] == "Load failed" assert captured["operation"] == "ui.test.failure" async def _async_value(value: int) -> int: return value async def _async_raises(exc: Exception) -> int: raise exc