From b14eadef866c5ca45caf33f0dbd7d6b169348444 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Sat, 14 Mar 2026 17:40:33 -0700 Subject: [PATCH] Fix hanging CI tests in test_http_handler.py - Remove real HTTP call to example.com in test_force_ipv4_transport (hangs in CI when network is slow/unavailable) - Close leaked aiohttp.ClientSession in test_ssl_verification_with_aiohttp_transport - Add cleanup for transports in test_aiohttp_transport_trust_env_setting - Add cleanup for handler in test_ssl_security_level - Add cleanup for transport in test_ssl_context_transport Co-Authored-By: Claude Opus 4.6 --- .../llms/custom_httpx/test_http_handler.py | 121 ++++++++++-------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/tests/test_litellm/llms/custom_httpx/test_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_http_handler.py index f3b76ddbe8..893c8a34ec 100644 --- a/tests/test_litellm/llms/custom_httpx/test_http_handler.py +++ b/tests/test_litellm/llms/custom_httpx/test_http_handler.py @@ -32,24 +32,25 @@ async def test_ssl_security_level(monkeypatch): # Create async client with SSL verification disabled to isolate SSL context testing client = AsyncHTTPHandler() - # Get the transport (should be LiteLLMAiohttpTransport) - transport = client.client._transport - assert isinstance(transport, LiteLLMAiohttpTransport) + try: + # Get the transport (should be LiteLLMAiohttpTransport) + transport = client.client._transport + assert isinstance(transport, LiteLLMAiohttpTransport) - # Get the aiohttp ClientSession - client_session = transport._get_valid_client_session() + # Get the aiohttp ClientSession + client_session = transport._get_valid_client_session() - # Get the connector from the session - connector = client_session.connector - assert isinstance(connector, TCPConnector) + # Get the connector from the session + connector = client_session.connector + assert isinstance(connector, TCPConnector) - # Get the SSL context from the connector - ssl_context = connector._ssl + # Get the SSL context from the connector + ssl_context = connector._ssl - # Verify that the SSL context exists and has the correct cipher string - assert isinstance(ssl_context, ssl.SSLContext) - # Optionally, check the ciphers string if needed - # assert "DEFAULT@SECLEVEL=1" in ssl_context.get_ciphers() + # Verify that the SSL context exists and has the correct cipher string + assert isinstance(ssl_context, ssl.SSLContext) + finally: + await client.close() finally: # Restore original setting litellm.disable_aiohttp_transport = original_disable @@ -63,15 +64,8 @@ async def test_force_ipv4_transport(): transport = AsyncHTTPHandler._create_async_transport() - # Should get an AsyncHTTPTransport + # Should get an AsyncHTTPTransport (no real HTTP call — avoids CI hangs) assert isinstance(transport, httpx.AsyncHTTPTransport) - # Verify IPv4 configuration through a request - client = httpx.AsyncClient(transport=transport) - try: - response = await client.get("http://example.com") - assert response.status_code == 200 - finally: - await client.aclose() @pytest.mark.asyncio @@ -83,13 +77,17 @@ async def test_ssl_context_transport(): transport = AsyncHTTPHandler._create_async_transport(ssl_context=ssl_context) assert transport is not None - if isinstance(transport, LiteLLMAiohttpTransport): - # Get the client session and verify SSL context is passed through - client_session = transport._get_valid_client_session() - assert isinstance(client_session, ClientSession) - assert isinstance(client_session.connector, TCPConnector) - # Verify the connector has SSL context set by checking if it's using SSL - assert client_session.connector._ssl is not None + try: + if isinstance(transport, LiteLLMAiohttpTransport): + # Get the client session and verify SSL context is passed through + client_session = transport._get_valid_client_session() + assert isinstance(client_session, ClientSession) + assert isinstance(client_session.connector, TCPConnector) + # Verify the connector has SSL context set by checking if it's using SSL + assert client_session.connector._ssl is not None + finally: + if isinstance(transport, LiteLLMAiohttpTransport): + await transport.aclose() @pytest.mark.asyncio @@ -130,11 +128,14 @@ async def test_ssl_verification_with_aiohttp_transport(): aiohttp_session = aiohttp.ClientSession( connector=aiohttp.TCPConnector(ssl=False) ) - aiohttp_connector = aiohttp_session.connector - assert isinstance(aiohttp_connector, aiohttp.TCPConnector) + try: + aiohttp_connector = aiohttp_session.connector + assert isinstance(aiohttp_connector, aiohttp.TCPConnector) - # assert both litellm transport and aiohttp session have ssl_verify=False - assert transport_connector._ssl == aiohttp_connector._ssl + # assert both litellm transport and aiohttp session have ssl_verify=False + assert transport_connector._ssl == aiohttp_connector._ssl + finally: + await aiohttp_session.close() finally: # Restore original setting litellm.disable_aiohttp_transport = original_disable @@ -220,29 +221,37 @@ async def test_ssl_context_with_shared_session(): @pytest.mark.asyncio async def test_aiohttp_transport_trust_env_setting(monkeypatch): """Test that trust_env setting is properly configured in aiohttp transport""" - # Test 1: Default trust_env behavior - transport = AsyncHTTPHandler._create_aiohttp_transport() - client_session = transport._get_valid_client_session() - - # Default should be False (litellm.aiohttp_trust_env default) - default_trust_env = getattr(litellm, 'aiohttp_trust_env', False) - assert client_session._trust_env == default_trust_env - - # Test 2: Environment variable override - monkeypatch.setenv("AIOHTTP_TRUST_ENV", "True") - transport_with_env = AsyncHTTPHandler._create_aiohttp_transport() - client_session_with_env = transport_with_env._get_valid_client_session() - - # Should be True when environment variable is set - assert client_session_with_env._trust_env is True - - # Test 3: Verify environment variable with False value - monkeypatch.setenv("AIOHTTP_TRUST_ENV", "False") - transport_with_false_env = AsyncHTTPHandler._create_aiohttp_transport() - client_session_with_false_env = transport_with_false_env._get_valid_client_session() - - # Should respect the litellm.aiohttp_trust_env setting when env var is False - assert client_session_with_false_env._trust_env == default_trust_env + transports = [] + try: + # Test 1: Default trust_env behavior + transport = AsyncHTTPHandler._create_aiohttp_transport() + transports.append(transport) + client_session = transport._get_valid_client_session() + + # Default should be False (litellm.aiohttp_trust_env default) + default_trust_env = getattr(litellm, 'aiohttp_trust_env', False) + assert client_session._trust_env == default_trust_env + + # Test 2: Environment variable override + monkeypatch.setenv("AIOHTTP_TRUST_ENV", "True") + transport_with_env = AsyncHTTPHandler._create_aiohttp_transport() + transports.append(transport_with_env) + client_session_with_env = transport_with_env._get_valid_client_session() + + # Should be True when environment variable is set + assert client_session_with_env._trust_env is True + + # Test 3: Verify environment variable with False value + monkeypatch.setenv("AIOHTTP_TRUST_ENV", "False") + transport_with_false_env = AsyncHTTPHandler._create_aiohttp_transport() + transports.append(transport_with_false_env) + client_session_with_false_env = transport_with_false_env._get_valid_client_session() + + # Should respect the litellm.aiohttp_trust_env setting when env var is False + assert client_session_with_false_env._trust_env == default_trust_env + finally: + for t in transports: + await t.aclose() def test_get_ssl_configuration():