94 lines
3.7 KiB
Python
94 lines
3.7 KiB
Python
"""Tests for friendly_error's handling of AI upstream error messages.
|
|
|
|
These cover the DifyChatService error_code → user-facing copy mapping that
|
|
the doctor workstation must apply when the server-side AI assistant returns
|
|
``ok=false`` with a Chinese ``error`` string. The legacy behaviour simply
|
|
echoed the raw text, which made incidents like ``UPSTREAM_REJECTED`` opaque
|
|
to clinicians.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from doctor_workstation.core.errors import (
|
|
ApiBusinessError,
|
|
ApiHttpError,
|
|
ApiProtocolError,
|
|
ApiTimeoutError,
|
|
ApiTransportError,
|
|
AuthenticationExpiredError,
|
|
)
|
|
from doctor_workstation.ui.widgets import _ai_upstream_hint, friendly_error
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("raw", "expected_fragment"),
|
|
[
|
|
# 当上游 Dify 服务 HTTP 4xx 时返回的"UPSTREAM_REJECTED"文案。
|
|
("模型未能处理本次请求", "稍后重试"),
|
|
("AI 服务凭据无效或无权限", "联系管理员"),
|
|
("AI 服务配置无效", "联系管理员"),
|
|
("AI 报告功能未启用", "联系管理员"),
|
|
("不支持的 AI 模型", "联系管理员"),
|
|
("该模型服务尚未完整配置", "联系管理员"),
|
|
("病例数据编码失败", "联系管理员"),
|
|
("无法初始化 AI 请求", "联系管理员"),
|
|
("暂时无法连接 AI 服务,请稍后重试", "网络"),
|
|
("模型服务繁忙,请稍后重试", "稍后重试"),
|
|
("模型响应超时,请稍后重试", "稍后重试"),
|
|
("AI 助手未返回内容,请重试", "稍后重试"),
|
|
],
|
|
)
|
|
def test_friendly_error_translates_ai_upstream_strings(raw: str, expected_fragment: str) -> None:
|
|
"""Upstream Dify messages should be replaced with actionable copy."""
|
|
|
|
rendered = friendly_error(raw)
|
|
assert expected_fragment in rendered
|
|
# 上游原文不应该再原样透传。
|
|
assert rendered != raw
|
|
|
|
|
|
def test_friendly_error_passes_through_unrelated_chinese_text() -> None:
|
|
"""中文业务文案不属于上游错误时,必须原样透传,避免误伤。"""
|
|
|
|
text = "AI 返回的处方草稿格式不符合要求,请重试"
|
|
assert friendly_error(text) == text
|
|
|
|
|
|
def test_friendly_error_handles_api_business_error_with_upstream_payload() -> None:
|
|
"""服务端通过 ApiBusinessError(code=0) 透传时仍要触发映射。"""
|
|
|
|
err = ApiBusinessError("模型未能处理本次请求", code=0)
|
|
rendered = friendly_error(err)
|
|
assert "稍后重试" in rendered
|
|
assert "联系管理员" in rendered
|
|
|
|
|
|
def test_friendly_error_keeps_existing_transport_mappings() -> None:
|
|
"""对网络/超时/未授权等既有规则的回归保护。"""
|
|
|
|
assert "证书" in friendly_error(Exception("certificate_verify_failed"))
|
|
assert "网络" in friendly_error(ApiTransportError("connection refused"))
|
|
assert "重新登录" in friendly_error(AuthenticationExpiredError("expired"))
|
|
timeout_render = friendly_error(ApiTimeoutError("timed out"))
|
|
assert "超时" in timeout_render
|
|
http_render = friendly_error(ApiHttpError("boom", status_code=503))
|
|
assert "503" in http_render
|
|
protocol_render = friendly_error(ApiProtocolError("api response envelope invalid"))
|
|
assert "数据格式" in protocol_render
|
|
|
|
|
|
def test_friendly_error_returns_default_for_empty_string() -> None:
|
|
"""Fallback 应当落到"操作未完成"而不是崩溃。"""
|
|
|
|
assert friendly_error(SimpleNamespace(__str__=lambda self: " ")) == "操作未完成,请稍后重试。"
|
|
|
|
|
|
def test_ai_upstream_hint_returns_none_for_unrelated_text() -> None:
|
|
assert _ai_upstream_hint("AI 返回的处方草稿格式不符合要求,请重试") is None
|
|
assert _ai_upstream_hint("connection refused") is None
|
|
assert _ai_upstream_hint("") is None
|