117 lines
3.7 KiB
Python
117 lines
3.7 KiB
Python
"""
|
|
异常体系单元测试
|
|
================
|
|
|
|
验证 AppException / InsufficientPointsException 的结构化字段,
|
|
确保前端可以通过 error_code 识别错误类型。
|
|
"""
|
|
|
|
from fastapi import HTTPException
|
|
|
|
from app.core.exceptions import (
|
|
AIEmptyResponseException,
|
|
AIParseErrorException,
|
|
AITimeoutException,
|
|
AppException,
|
|
BusinessException,
|
|
InsufficientPointsException,
|
|
NotFoundException,
|
|
PromptNotFoundException,
|
|
ValidationException,
|
|
)
|
|
|
|
|
|
class TestAppException:
|
|
"""业务异常基类"""
|
|
|
|
def test_app_exception_has_error_code(self) -> None:
|
|
exc = AppException(
|
|
status_code=400,
|
|
message="参数错误",
|
|
error_code="validation_error",
|
|
)
|
|
assert exc.status_code == 400
|
|
assert exc.message == "参数错误"
|
|
assert exc.error_code == "validation_error"
|
|
assert exc.detail == {"message": "参数错误", "error_code": "validation_error"}
|
|
|
|
def test_app_exception_detail_can_be_dict(self) -> None:
|
|
exc = AppException(
|
|
status_code=422,
|
|
message="字段校验失败",
|
|
detail={"fields": {"name": "required"}},
|
|
error_code="validation_error",
|
|
)
|
|
assert exc.detail == {
|
|
"fields": {"name": "required"},
|
|
"message": "字段校验失败",
|
|
"error_code": "validation_error",
|
|
}
|
|
|
|
def test_subclasses_without_error_code(self) -> None:
|
|
exc = NotFoundException("资源不存在")
|
|
assert exc.status_code == 404
|
|
assert exc.message == "资源不存在"
|
|
assert exc.error_code is None
|
|
|
|
|
|
class TestInsufficientPointsException:
|
|
"""积分不足异常"""
|
|
|
|
def test_default_fields(self) -> None:
|
|
exc = InsufficientPointsException()
|
|
assert exc.status_code == 402
|
|
assert exc.message == "积分不足"
|
|
assert exc.error_code == "insufficient_points"
|
|
assert isinstance(exc, HTTPException)
|
|
|
|
def test_custom_message(self) -> None:
|
|
exc = InsufficientPointsException("余额不足,请先充值")
|
|
assert exc.message == "余额不足,请先充值"
|
|
assert exc.error_code == "insufficient_points"
|
|
|
|
def test_detail_structure(self) -> None:
|
|
exc = InsufficientPointsException("需要 10 积分")
|
|
assert exc.detail == {
|
|
"message": "需要 10 积分",
|
|
"error_code": "insufficient_points",
|
|
}
|
|
|
|
|
|
class TestOtherSubclasses:
|
|
"""其他常用子类"""
|
|
|
|
def test_validation_exception(self) -> None:
|
|
exc = ValidationException("字段缺失")
|
|
assert exc.status_code == 422
|
|
assert exc.message == "字段缺失"
|
|
|
|
def test_business_exception(self) -> None:
|
|
exc = BusinessException("业务状态不允许")
|
|
assert exc.status_code == 400
|
|
assert exc.message == "业务状态不允许"
|
|
|
|
|
|
class TestAIStructuredExceptions:
|
|
"""AI 相关结构化异常"""
|
|
|
|
def test_prompt_not_found_exception(self) -> None:
|
|
exc = PromptNotFoundException("未找到提示词")
|
|
assert exc.status_code == 404
|
|
assert exc.error_code == "prompt_not_found"
|
|
|
|
def test_ai_empty_response_exception(self) -> None:
|
|
exc = AIEmptyResponseException("AI 返回为空")
|
|
assert exc.status_code == 500
|
|
assert exc.error_code == "empty_result"
|
|
|
|
def test_ai_parse_error_exception(self) -> None:
|
|
exc = AIParseErrorException("解析失败")
|
|
assert exc.status_code == 500
|
|
assert exc.error_code == "parse_error"
|
|
|
|
def test_ai_timeout_exception(self) -> None:
|
|
exc = AITimeoutException("请求超时")
|
|
assert exc.status_code == 504
|
|
assert exc.error_code == "timeout"
|