"""kilostar.utils.error 类型层级与状态码测试。 异常体系两条主轴: - ``BusinessError``:4xx; - ``InfraError``:5xx,下分 ``RetryableError`` / ``NonRetryableError``。 """ import pytest from kilostar.utils import error def test_two_main_branches_under_kilostar_error(): assert issubclass(error.BusinessError, error.KiloStarError) assert issubclass(error.InfraError, error.KiloStarError) assert not issubclass(error.BusinessError, error.InfraError) assert not issubclass(error.InfraError, error.BusinessError) def test_retryable_and_nonretryable_under_infra(): assert issubclass(error.RetryableError, error.InfraError) assert issubclass(error.NonRetryableError, error.InfraError) assert not issubclass(error.RetryableError, error.NonRetryableError) assert not issubclass(error.NonRetryableError, error.RetryableError) def test_demand_error_is_business_4xx(): assert issubclass(error.DemandError, error.BusinessError) assert error.DemandError.http_status == 400 def test_workflow_exit_is_business_not_workflow_error(): """WorkflowExit 在新体系里是预期退出(4xx),与 WorkflowError(5xx) 完全分家。""" assert issubclass(error.WorkflowExit, error.BusinessError) assert not issubclass(error.WorkflowExit, error.WorkflowError) assert error.WorkflowExit.http_status == 400 @pytest.mark.parametrize( "child,parent", [ (error.UserNotExistError, error.UserError), (error.UserPasswordError, error.UserError), (error.ProviderNotExistError, error.ProviderError), (error.UserError, error.BusinessError), (error.ProviderError, error.BusinessError), (error.ModelNotExistError, error.BusinessError), (error.WorkflowError, error.InfraError), ], ) def test_subclass_hierarchy(child, parent): assert issubclass(child, parent) @pytest.mark.parametrize( "exc_cls,expected_status", [ (error.UserNotExistError, 404), (error.UserPasswordError, 401), (error.ProviderNotExistError, 404), (error.ModelNotExistError, 404), (error.WorkflowExit, 400), (error.RetryableError, 503), (error.NonRetryableError, 500), (error.WorkflowError, 500), ], ) def test_http_status_mapping(exc_cls, expected_status): assert exc_cls.http_status == expected_status def test_each_error_has_code_attr(): """所有自定义异常都应带可读的 ``code``,用于 API 响应中 ``{"code": ...}``。""" for cls in [ error.KiloStarError, error.BusinessError, error.InfraError, error.UserNotExistError, error.RetryableError, error.WorkflowError, ]: assert isinstance(cls.code, str) and cls.code def test_errors_can_be_raised_and_caught(): with pytest.raises(error.UserNotExistError) as exc: raise error.UserNotExistError("missing") assert "missing" in str(exc.value)