""" 异常体系单元测试 ================ 验证 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"