"""Tests for FastGPT exception classes.""" import pytest from fastgpt_client.exceptions import ( FastGPTError, APIError, AuthenticationError, RateLimitError, ValidationError, StreamParseError, ) class TestFastGPTError: """Test suite for FastGPTError base exception.""" def test_init_with_message_only(self): """Test initialization with message only.""" error = FastGPTError("Test error message") assert error.message == "Test error message" assert error.status_code is None assert error.response_data == {} def test_init_with_message_and_status_code(self): """Test initialization with message and status code.""" error = FastGPTError("Test error", status_code=404) assert error.message == "Test error" assert error.status_code == 404 assert error.response_data == {} def test_init_with_all_parameters(self): """Test initialization with all parameters.""" response_data = {"code": "test_error", "details": "Something went wrong"} error = FastGPTError( "Test error", status_code=500, response_data=response_data ) assert error.message == "Test error" assert error.status_code == 500 assert error.response_data == response_data def test_str_representation(self): """Test string representation of exception.""" error = FastGPTError("Test error message") assert str(error) == "Test error message" def test_inheritance(self): """Test that FastGPTError inherits from Exception.""" error = FastGPTError("Test") assert isinstance(error, Exception) assert isinstance(error, FastGPTError) class TestAPIError: """Test suite for APIError exception.""" def test_api_error_inheritance(self): """Test that APIError inherits from FastGPTError.""" error = APIError("API error occurred") assert isinstance(error, FastGPTError) assert isinstance(error, APIError) assert isinstance(error, Exception) def test_api_error_basic(self): """Test basic APIError creation.""" error = APIError("API failed", status_code=500) assert error.message == "API failed" assert error.status_code == 500 def test_api_error_with_response_data(self): """Test APIError with response data.""" response_data = {"error": "Internal server error"} error = APIError("Server error", status_code=500, response_data=response_data) assert error.response_data == response_data class TestAuthenticationError: """Test suite for AuthenticationError exception.""" def test_auth_error_inheritance(self): """Test that AuthenticationError inherits from FastGPTError.""" error = AuthenticationError("Invalid credentials") assert isinstance(error, FastGPTError) assert isinstance(error, AuthenticationError) def test_auth_error_default(self): """Test AuthenticationError with default parameters.""" error = AuthenticationError("Invalid API key") assert error.message == "Invalid API key" assert error.status_code is None assert error.response_data == {} def test_auth_error_with_status_code(self): """Test AuthenticationError with status code.""" error = AuthenticationError( "Authentication failed", status_code=401 ) assert error.status_code == 401 def test_auth_error_full(self): """Test AuthenticationError with all parameters.""" response_data = { "code": "invalid_api_key", "message": "The provided API key is invalid" } error = AuthenticationError( "Auth failed", status_code=401, response_data=response_data ) assert error.message == "Auth failed" assert error.status_code == 401 assert error.response_data == response_data class TestRateLimitError: """Test suite for RateLimitError exception.""" def test_rate_limit_error_inheritance(self): """Test that RateLimitError inherits from FastGPTError.""" error = RateLimitError("Rate limit exceeded") assert isinstance(error, FastGPTError) assert isinstance(error, RateLimitError) def test_rate_limit_error_basic(self): """Test RateLimitError with basic parameters.""" error = RateLimitError("Too many requests") assert error.message == "Too many requests" assert error.retry_after is None assert error.status_code is None def test_rate_limit_error_with_retry_after(self): """Test RateLimitError with retry_after parameter.""" error = RateLimitError( "Rate limit exceeded", retry_after="60" ) assert error.message == "Rate limit exceeded" assert error.retry_after == "60" def test_rate_limit_error_full(self): """Test RateLimitError with all parameters.""" response_data = { "code": "rate_limit_exceeded", "limit": 100, "remaining": 0 } error = RateLimitError( "Rate limit hit", retry_after="120", status_code=429, response_data=response_data ) assert error.message == "Rate limit hit" assert error.retry_after == "120" assert error.status_code == 429 assert error.response_data == response_data def test_rate_limit_error_retry_after_integer(self): """Test RateLimitError with integer retry_after.""" error = RateLimitError( "Rate limited", retry_after=30 ) assert error.retry_after == 30 class TestValidationError: """Test suite for ValidationError exception.""" def test_validation_error_inheritance(self): """Test that ValidationError inherits from FastGPTError.""" error = ValidationError("Validation failed") assert isinstance(error, FastGPTError) assert isinstance(error, ValidationError) def test_validation_error_basic(self): """Test ValidationError with basic parameters.""" error = ValidationError("Invalid parameter") assert error.message == "Invalid parameter" def test_validation_error_with_status(self): """Test ValidationError with status code.""" error = ValidationError( "Invalid input", status_code=422 ) assert error.status_code == 422 def test_validation_error_with_response_data(self): """Test ValidationError with response data.""" response_data = { "code": "validation_error", "field": "email", "reason": "Invalid format" } error = ValidationError( "Validation failed", status_code=422, response_data=response_data ) assert error.response_data == response_data class TestStreamParseError: """Test suite for StreamParseError exception.""" def test_stream_parse_error_inheritance(self): """Test that StreamParseError inherits from FastGPTError.""" error = StreamParseError("Stream parsing failed") assert isinstance(error, FastGPTError) assert isinstance(error, StreamParseError) def test_stream_parse_error_basic(self): """Test StreamParseError with basic parameters.""" error = StreamParseError("Invalid stream format") assert error.message == "Invalid stream format" def test_stream_parse_error_with_status(self): """Test StreamParseError with status code.""" error = StreamParseError( "Parse error", status_code=500 ) assert error.status_code == 500 def test_stream_parse_error_with_response_data(self): """Test StreamParseError with response data.""" response_data = { "error": "stream_error", "details": "Unexpected EOF" } error = StreamParseError( "Failed to parse stream", status_code=502, response_data=response_data ) assert error.response_data == response_data class TestExceptionUsagePatterns: """Test suite for common exception usage patterns.""" def test_catching_base_exception(self): """Test catching exceptions using base FastGPTError.""" with pytest.raises(FastGPTError) as exc_info: raise APIError("API error") assert isinstance(exc_info.value, FastGPTError) assert str(exc_info.value) == "API error" def test_catching_specific_exception(self): """Test catching specific exception types.""" with pytest.raises(AuthenticationError) as exc_info: raise AuthenticationError("Auth failed", status_code=401) assert exc_info.value.status_code == 401 def test_exception_chain(self): """Test exception chaining.""" try: try: raise ValueError("Original error") except ValueError as e: raise APIError("Wrapped error") from e except APIError as caught: assert caught.__cause__ is not None assert isinstance(caught.__cause__, ValueError) def test_raising_from_http_response(self): """Test raising exception from HTTP response data.""" response_data = { "code": "invalid_request", "message": "The request was invalid" } with pytest.raises(APIError) as exc_info: raise APIError( "Invalid request", status_code=400, response_data=response_data ) assert exc_info.value.response_data == response_data def test_exception_with_none_response_data(self): """Test exception when response_data is None.""" error = APIError("Error", status_code=500, response_data=None) # None should default to empty dict assert error.response_data == {} def test_multiple_exception_types(self): """Test handling multiple exception types.""" def raise_specific_error(status_code): if status_code == 401: raise AuthenticationError("Unauthorized", status_code=status_code) elif status_code == 429: raise RateLimitError("Rate limited", status_code=status_code) elif status_code == 422: raise ValidationError("Invalid data", status_code=status_code) else: raise APIError("Generic error", status_code=status_code) # Test each type with pytest.raises(AuthenticationError): raise_specific_error(401) with pytest.raises(RateLimitError): raise_specific_error(429) with pytest.raises(ValidationError): raise_specific_error(422) with pytest.raises(APIError): raise_specific_error(500) def test_exception_message_formatting(self): """Test that exception messages are properly formatted.""" error1 = FastGPTError("Simple error") assert str(error1) == "Simple error" error2 = FastGPTError("Error with status", status_code=404) assert str(error2) == "Error with status" # Message should be accessible regardless of other parameters error3 = APIError("Complex error", status_code=500, response_data={"key": "value"}) assert error3.message == "Complex error" assert str(error3) == "Complex error"