From f0fc44c244681b37aa68ed3ec00431bd21d8f518 Mon Sep 17 00:00:00 2001 From: Julio Quinteros Pro Date: Tue, 17 Feb 2026 20:52:25 -0300 Subject: [PATCH 1/2] fix(tests): use class-level AsyncHTTPHandler mock in vertex GPT-OSS tests Replace instance-level patch.object(client, "post", side_effect=...) with class-level patch of AsyncHTTPHandler and AsyncMock to reliably intercept HTTP calls in CI where real Google credentials are available. The old approach patched a specific instance's post method and passed client=client to acompletion(). In CI, the mock wasn't intercepting actual HTTP calls, causing 401 ACCESS_TOKEN_TYPE_UNSUPPORTED errors. The new approach patches AsyncHTTPHandler at the class level so any instance created internally by get_async_httpx_client() is also mocked. Co-Authored-By: Claude Sonnet 4.6 --- .../test_vertex_ai_gpt_oss_transformation.py | 78 +++++++------------ 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py b/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py index 71069b8750..fb5529d475 100644 --- a/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py @@ -1,9 +1,8 @@ import json import os import sys -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, patch -import httpx import pytest sys.path.insert( @@ -76,11 +75,6 @@ async def test_vertex_ai_gpt_oss_simple_request(): Test that a simple request to vertex_ai/openai/gpt-oss-20b-maas lands at the correct URL with the correct request body. """ - from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler - from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( - VertexLLM, - ) - # Mock response mock_response = MagicMock() mock_response.status_code = 200 @@ -107,21 +101,17 @@ async def test_vertex_ai_gpt_oss_simple_request(): } } - client = AsyncHTTPHandler() - - async def mock_post_func(*args, **kwargs): - return mock_response - - # Mock vertexai module to prevent import from triggering authentication mock_vertexai = MagicMock() mock_vertexai.preview = MagicMock() mock_vertexai.preview.language_models = MagicMock() - with patch.object(client, "post", side_effect=mock_post_func) as mock_post, \ - patch.object(VertexLLM, "_ensure_access_token", return_value=("fake-token", "pathrise-convert-1606954137718")), \ - patch('litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini.VertexAIError', Exception), \ - patch.dict('sys.modules', {'vertexai': mock_vertexai, 'vertexai.preview': mock_vertexai.preview}), \ + with patch("litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler") as mock_http_handler, \ + patch("litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini.VertexLLM._ensure_access_token", + return_value=("fake-token", "pathrise-convert-1606954137718")), \ + patch.dict("sys.modules", {"vertexai": mock_vertexai, "vertexai.preview": mock_vertexai.preview}), \ patch.dict(os.environ, {"VERTEXAI_PROJECT": "pathrise-convert-1606954137718"}): + mock_http_handler.return_value.post = AsyncMock(return_value=mock_response) + response = await litellm.acompletion( model="vertex_ai/openai/gpt-oss-20b-maas", messages=[ @@ -136,22 +126,20 @@ async def test_vertex_ai_gpt_oss_simple_request(): ], vertex_ai_location="us-central1", vertex_ai_project="pathrise-convert-1606954137718", - client=client ) - + # Verify the mock was called - mock_post.assert_called_once() - + mock_http_handler.return_value.post.assert_called_once() + # Get the call arguments - call_args = mock_post.call_args - # For side_effect, the URL is passed as kwargs['url'] + call_args = mock_http_handler.return_value.post.call_args called_url = call_args.kwargs["url"] request_body = json.loads(call_args.kwargs["data"]) - + # Verify the URL expected_url = "https://us-central1-aiplatform.googleapis.com/v1/projects/pathrise-convert-1606954137718/locations/us-central1/endpoints/openapi/chat/completions" assert called_url == expected_url - + # Verify the request body expected_request_body = { 'model': 'openai/gpt-oss-20b-maas', @@ -168,7 +156,7 @@ async def test_vertex_ai_gpt_oss_simple_request(): 'stream': False } assert request_body == expected_request_body - + # Verify response structure assert response.model == "openai/gpt-oss-20b-maas" assert len(response.choices) == 1 @@ -181,11 +169,6 @@ async def test_vertex_ai_gpt_oss_reasoning_effort(): Test that reasoning_effort parameter is correctly passed in the request body for GPT-OSS models. """ - from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler - from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( - VertexLLM, - ) - # Mock response mock_response = MagicMock() mock_response.status_code = 200 @@ -212,21 +195,17 @@ async def test_vertex_ai_gpt_oss_reasoning_effort(): } } - client = AsyncHTTPHandler() - - async def mock_post_func(*args, **kwargs): - return mock_response - - # Mock vertexai module to prevent import from triggering authentication mock_vertexai = MagicMock() mock_vertexai.preview = MagicMock() mock_vertexai.preview.language_models = MagicMock() - with patch.object(client, "post", side_effect=mock_post_func) as mock_post, \ - patch.object(VertexLLM, "_ensure_access_token", return_value=("fake-token", "pathrise-convert-1606954137718")), \ - patch('litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini.VertexAIError', Exception), \ - patch.dict('sys.modules', {'vertexai': mock_vertexai, 'vertexai.preview': mock_vertexai.preview}), \ + with patch("litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler") as mock_http_handler, \ + patch("litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini.VertexLLM._ensure_access_token", + return_value=("fake-token", "pathrise-convert-1606954137718")), \ + patch.dict("sys.modules", {"vertexai": mock_vertexai, "vertexai.preview": mock_vertexai.preview}), \ patch.dict(os.environ, {"VERTEXAI_PROJECT": "pathrise-convert-1606954137718"}): + mock_http_handler.return_value.post = AsyncMock(return_value=mock_response) + response = await litellm.acompletion( model="vertex_ai/openai/gpt-oss-20b-maas", messages=[ @@ -242,20 +221,19 @@ async def test_vertex_ai_gpt_oss_reasoning_effort(): reasoning_effort="low", vertex_ai_location="us-central1", vertex_ai_project="pathrise-convert-1606954137718", - client=client ) - + # Verify the mock was called - mock_post.assert_called_once() - - # Get the call arguments - call_args = mock_post.call_args + mock_http_handler.return_value.post.assert_called_once() + + # Get the call arguments + call_args = mock_http_handler.return_value.post.call_args request_body = json.loads(call_args.kwargs["data"]) - + # Verify reasoning_effort is in the request body assert "reasoning_effort" in request_body assert request_body["reasoning_effort"] == "low" - + # Verify other expected fields expected_request_body = { 'model': 'openai/gpt-oss-20b-maas', @@ -273,7 +251,7 @@ async def test_vertex_ai_gpt_oss_reasoning_effort(): 'stream': False } assert request_body == expected_request_body - + # Verify response structure assert response.model == "openai/gpt-oss-20b-maas" assert len(response.choices) == 1 From ea0cfac9957b7a122211f5b11a85083554d53e3f Mon Sep 17 00:00:00 2001 From: Julio Quinteros Pro Date: Tue, 17 Feb 2026 21:02:56 -0300 Subject: [PATCH 2/2] fix(tests): add cache flush fixture for reliable HTTP client isolation Add _reset_litellm_http_client_cache autouse fixture (matching test_vertex_gemma_transformation.py) to flush in_memory_llm_clients_cache before each test. Without this, a cached real AsyncHTTPHandler from an earlier test could bypass the class-level mock and cause real HTTP calls. Co-Authored-By: Claude Sonnet 4.6 --- .../gpt_oss/test_vertex_ai_gpt_oss_transformation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py b/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py index fb5529d475..b6d6402bcd 100644 --- a/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/gpt_oss/test_vertex_ai_gpt_oss_transformation.py @@ -15,6 +15,14 @@ from litellm.llms.vertex_ai.vertex_ai_partner_models.gpt_oss.transformation impo ) +@pytest.fixture(autouse=True) +def _reset_litellm_http_client_cache(): + """Ensure each test gets a fresh async HTTP client mock.""" + from litellm import in_memory_llm_clients_cache + + in_memory_llm_clients_cache.flush_cache() + + @pytest.fixture(autouse=True) def clean_vertex_env(): """Clear Google/Vertex AI environment variables before each test to prevent test isolation issues."""