Spaces:
Sleeping
Sleeping
| """Tests for providers/nvidia_nim/errors.py error mapping.""" | |
| from unittest.mock import MagicMock, patch | |
| import openai | |
| import pytest | |
| from httpx import ReadTimeout, Request, Response | |
| from providers.common import append_request_id, get_user_facing_error_message, map_error | |
| from providers.exceptions import ( | |
| APIError, | |
| AuthenticationError, | |
| InvalidRequestError, | |
| OverloadedError, | |
| RateLimitError, | |
| ) | |
| def _make_openai_error(cls, message="test error", status_code=None): | |
| """Helper to create openai exceptions with required httpx objects.""" | |
| response = Response( | |
| status_code=status_code or 500, request=Request("POST", "http://test") | |
| ) | |
| body = {"error": {"message": message}} | |
| # openai.APIError base class has a different constructor signature | |
| if cls is openai.APIError: | |
| return cls(message, request=Request("POST", "http://test"), body=body) | |
| return cls(message, response=response, body=body) | |
| class TestMapError: | |
| """Tests for map_error function.""" | |
| def test_authentication_error(self): | |
| """openai.AuthenticationError -> AuthenticationError.""" | |
| exc = _make_openai_error(openai.AuthenticationError, status_code=401) | |
| result = map_error(exc) | |
| assert isinstance(result, AuthenticationError) | |
| assert result.status_code == 401 | |
| def test_rate_limit_error(self): | |
| """openai.RateLimitError -> RateLimitError and triggers global block.""" | |
| exc = _make_openai_error(openai.RateLimitError, status_code=429) | |
| with patch("providers.common.error_mapping.GlobalRateLimiter") as mock_rl: | |
| mock_instance = MagicMock() | |
| mock_rl.get_instance.return_value = mock_instance | |
| result = map_error(exc) | |
| assert isinstance(result, RateLimitError) | |
| assert result.status_code == 429 | |
| mock_instance.set_blocked.assert_called_once_with(60) | |
| def test_bad_request_error(self): | |
| """openai.BadRequestError -> InvalidRequestError.""" | |
| exc = _make_openai_error(openai.BadRequestError, status_code=400) | |
| result = map_error(exc) | |
| assert isinstance(result, InvalidRequestError) | |
| assert result.status_code == 400 | |
| def test_internal_server_error_overloaded(self, message): | |
| """InternalServerError with overloaded/capacity keywords -> OverloadedError.""" | |
| exc = _make_openai_error( | |
| openai.InternalServerError, message=message, status_code=500 | |
| ) | |
| result = map_error(exc) | |
| assert isinstance(result, OverloadedError) | |
| assert result.status_code == 529 | |
| def test_internal_server_error_generic(self): | |
| """InternalServerError without keywords -> APIError(500).""" | |
| exc = _make_openai_error( | |
| openai.InternalServerError, message="Unknown error", status_code=500 | |
| ) | |
| result = map_error(exc) | |
| assert isinstance(result, APIError) | |
| assert result.status_code == 500 | |
| def test_generic_api_error(self): | |
| """openai.APIError -> APIError with original status_code.""" | |
| exc = _make_openai_error( | |
| openai.APIError, message="Bad gateway", status_code=502 | |
| ) | |
| result = map_error(exc) | |
| assert isinstance(result, APIError) | |
| def test_unmapped_exception_passthrough(self): | |
| """Non-openai exceptions are returned as-is.""" | |
| exc = RuntimeError("unexpected") | |
| result = map_error(exc) | |
| assert result is exc | |
| assert isinstance(result, RuntimeError) | |
| def test_value_error_passthrough(self): | |
| """ValueError passes through unchanged.""" | |
| exc = ValueError("bad value") | |
| result = map_error(exc) | |
| assert result is exc | |
| def test_mapping_parametrized(self, exc_cls, expected_cls): | |
| """Parametrized check of openai -> provider error mapping.""" | |
| status_map = { | |
| openai.AuthenticationError: 401, | |
| openai.RateLimitError: 429, | |
| openai.BadRequestError: 400, | |
| } | |
| exc = _make_openai_error(exc_cls, status_code=status_map[exc_cls]) | |
| with patch("providers.common.error_mapping.GlobalRateLimiter"): | |
| result = map_error(exc) | |
| assert isinstance(result, expected_cls) | |
| def test_user_facing_message_read_timeout_empty_string(): | |
| """ReadTimeout wrapping TimeoutError should still produce readable text.""" | |
| timeout_exc = ReadTimeout("") | |
| message = get_user_facing_error_message(timeout_exc, read_timeout_s=60) | |
| assert message == "Provider request timed out after 60s." | |
| def test_append_request_id_suffix(): | |
| """Request id suffix should be appended deterministically.""" | |
| message = append_request_id("Provider request failed.", "req_abc123") | |
| assert message == "Provider request failed. (request_id=req_abc123)" | |