chore(release): bump version to 1.9.1 and apply pending changes
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
异常体系单元测试
|
||||
================
|
||||
|
||||
验证 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"
|
||||
Reference in New Issue
Block a user