mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(test): replace live httpbin.org call with mocked transport in test_pass_through_with_httpbin_redirect
Root cause of OOM: the test made a real HTTP request to https://httpbin.org inside a pytest-xdist worker. Under memory pressure the worker's httpx client and redirect-following logic allocated enough virtual memory to trip the OOM killer (confirmed by ulimit -v 16GB reproducing the crash with 'node down: Not properly terminated' on this exact test). Fix: replace the real network call with a custom httpx.AsyncBaseTransport that returns a pre-built 302 -> 200 response sequence in-memory. The test now runs hermetically with no network dependency and no excess memory allocation. ulimit -v 16GB: 24,284 passed (0 crashes) after this fix.
This commit is contained in:
parent
3cdb964e21
commit
add0ff19d3
1 changed files with 40 additions and 23 deletions
|
|
@ -2198,10 +2198,11 @@ async def test_pass_through_request_query_params_forwarding():
|
|||
@pytest.mark.asyncio
|
||||
async def test_pass_through_with_httpbin_redirect():
|
||||
"""
|
||||
Integration test using httpbin.org redirect endpoint to test real redirect handling.
|
||||
This tests the actual redirect handling capability end-to-end using the full pass_through_request function.
|
||||
Tests redirect handling in pass_through_request using a mocked HTTP transport.
|
||||
The mock simulates: GET /redirect/1 -> 302 Location: /get -> 200 {url: ...}
|
||||
No real network calls are made.
|
||||
"""
|
||||
from unittest.mock import MagicMock
|
||||
import json
|
||||
|
||||
from fastapi import Request
|
||||
from starlette.datastructures import Headers, QueryParams
|
||||
|
|
@ -2210,24 +2211,50 @@ async def test_pass_through_with_httpbin_redirect():
|
|||
pass_through_request,
|
||||
)
|
||||
|
||||
# Create mock request
|
||||
# Build the two responses the mock transport will return in order:
|
||||
# 1. 302 redirect from /redirect/1 -> /get
|
||||
redirect_response = httpx.Response(
|
||||
status_code=302,
|
||||
headers={"location": "https://httpbin.org/get"},
|
||||
content=b"",
|
||||
request=httpx.Request("GET", "https://httpbin.org/redirect/1"),
|
||||
)
|
||||
# 2. 200 final response from /get
|
||||
final_body = json.dumps({"url": "https://httpbin.org/get"}).encode()
|
||||
final_response = httpx.Response(
|
||||
status_code=200,
|
||||
headers={"content-type": "application/json"},
|
||||
content=final_body,
|
||||
request=httpx.Request("GET", "https://httpbin.org/get"),
|
||||
)
|
||||
|
||||
responses = iter([redirect_response, final_response])
|
||||
|
||||
class _MockTransport(httpx.AsyncBaseTransport):
|
||||
async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
|
||||
return next(responses)
|
||||
|
||||
# pass_through_request accesses get_async_httpx_client(...).client, so the
|
||||
# mock must expose a .client attribute wrapping our transport.
|
||||
mock_handler = MagicMock()
|
||||
mock_handler.client = httpx.AsyncClient(transport=_MockTransport(), follow_redirects=True)
|
||||
|
||||
# Create mock FastAPI request
|
||||
mock_request = MagicMock(spec=Request)
|
||||
mock_request.method = "GET"
|
||||
mock_request.headers = Headers({})
|
||||
mock_request.query_params = QueryParams("")
|
||||
|
||||
# Mock the body method to return empty bytes for GET request
|
||||
async def mock_body():
|
||||
return b""
|
||||
|
||||
mock_request.body = mock_body
|
||||
|
||||
# Mock user API key dict
|
||||
mock_user_api_key_dict = MagicMock()
|
||||
|
||||
try:
|
||||
# Test with httpbin.org redirect endpoint
|
||||
# This will redirect to httpbin.org/get
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.pass_through_endpoints.get_async_httpx_client",
|
||||
return_value=mock_handler,
|
||||
):
|
||||
response = await pass_through_request(
|
||||
request=mock_request,
|
||||
target="https://httpbin.org/redirect/1",
|
||||
|
|
@ -2235,19 +2262,9 @@ async def test_pass_through_with_httpbin_redirect():
|
|||
user_api_key_dict=mock_user_api_key_dict,
|
||||
)
|
||||
|
||||
# Should get the final response (200) from /get endpoint, not the redirect (302)
|
||||
assert response.status_code == 200
|
||||
|
||||
# The response should be from the /get endpoint
|
||||
response_content = bytes(response.body).decode("utf-8")
|
||||
|
||||
# httpbin.org/get returns JSON with info about the request
|
||||
assert '"url": "https://httpbin.org/get"' in response_content
|
||||
except Exception as e:
|
||||
# If httpbin.org is not accessible, skip the test
|
||||
import pytest
|
||||
|
||||
pytest.skip(f"Could not reach httpbin.org for integration test: {e}")
|
||||
assert response.status_code == 200
|
||||
response_content = bytes(response.body).decode("utf-8")
|
||||
assert '"url": "https://httpbin.org/get"' in response_content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue