mirror of
https://github.com/tiennm99/litellm.git
synced 2026-06-23 13:37:01 +00:00
Fix broken mocks in 6 flaky tests to prevent real API calls (#19829)
* Fix broken mocks in 6 flaky tests to prevent real API calls
Added network-level HTTP blocking using respx to prevent tests from making real API calls when Python-level mocks fail. This makes tests more reliable and retryable in CI.
Changes:
- Azure OIDC test: Added Azure Identity SDK mock to prevent real Azure calls
- Vector store test: Added @respx.mock decorator to block HTTP requests
- Resend email tests (3): Added @respx.mock decorator for all 3 test functions
- SendGrid email test: Added @respx.mock decorator
All test assertions and verification logic remain unchanged - only added safety nets to catch leaked API calls.
* Fix failing OIDC secret manager tests
Fixed two test failures in test_secret_managers_main.py:
1. test_oidc_azure_ad_token_success: Corrected the patch path for get_bearer_token_provider from 'litellm.secret_managers.get_azure_ad_token_provider.get_bearer_token_provider' to 'azure.identity.get_bearer_token_provider' since the function is imported from azure.identity.
2. test_oidc_google_success: Added @patch('httpx.Client') decorator to prevent any real HTTP connections during test execution, resolving httpx.ConnectError issues.
Both tests now pass successfully.
This commit is contained in:
@@ -2,7 +2,9 @@ import os
|
||||
import sys
|
||||
import unittest.mock as mock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../.."))
|
||||
@@ -39,7 +41,13 @@ def mock_httpx_client():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_send_email_success(mock_env_vars, mock_httpx_client):
|
||||
# Block all HTTP requests at network level to prevent real API calls
|
||||
respx.post("https://api.resend.com/emails").mock(
|
||||
return_value=httpx.Response(200, json={"id": "test_email_id"})
|
||||
)
|
||||
|
||||
# Initialize the logger
|
||||
logger = ResendEmailLogger()
|
||||
|
||||
@@ -73,7 +81,13 @@ async def test_send_email_success(mock_env_vars, mock_httpx_client):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_send_email_missing_api_key(mock_httpx_client):
|
||||
# Block all HTTP requests at network level to prevent real API calls
|
||||
respx.post("https://api.resend.com/emails").mock(
|
||||
return_value=httpx.Response(200, json={"id": "test_email_id"})
|
||||
)
|
||||
|
||||
# Remove the API key from environment before initializing logger
|
||||
original_key = os.environ.pop("RESEND_API_KEY", None)
|
||||
|
||||
@@ -111,7 +125,13 @@ async def test_send_email_missing_api_key(mock_httpx_client):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_send_email_multiple_recipients(mock_env_vars, mock_httpx_client):
|
||||
# Block all HTTP requests at network level to prevent real API calls
|
||||
respx.post("https://api.resend.com/emails").mock(
|
||||
return_value=httpx.Response(200, json={"id": "test_email_id"})
|
||||
)
|
||||
|
||||
# Initialize the logger
|
||||
logger = ResendEmailLogger()
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ import os
|
||||
import sys
|
||||
import unittest.mock as mock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../.."))
|
||||
@@ -101,7 +103,13 @@ async def test_send_email_missing_api_key():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_send_email_multiple_recipients(mock_env_vars, mock_httpx_client):
|
||||
# Block all HTTP requests at network level to prevent real API calls
|
||||
respx.post("https://api.sendgrid.com/v3/mail/send").mock(
|
||||
return_value=httpx.Response(202, text="accepted")
|
||||
)
|
||||
|
||||
logger = SendGridEmailLogger()
|
||||
|
||||
from_email = "test@example.com"
|
||||
|
||||
@@ -48,7 +48,8 @@ def mock_env():
|
||||
|
||||
@patch("litellm.secret_managers.main.oidc_cache")
|
||||
@patch("litellm.secret_managers.main._get_oidc_http_handler")
|
||||
def test_oidc_google_success(mock_get_http_handler, mock_oidc_cache):
|
||||
@patch("httpx.Client") # Prevent any real HTTP connections
|
||||
def test_oidc_google_success(mock_httpx_client, mock_get_http_handler, mock_oidc_cache):
|
||||
mock_oidc_cache.get_cache.return_value = None
|
||||
mock_handler = MockHTTPHandler(timeout=600.0)
|
||||
mock_get_http_handler.return_value = mock_handler
|
||||
@@ -144,7 +145,7 @@ def test_oidc_azure_file_success(mock_env, tmp_path):
|
||||
mock_env["AZURE_FEDERATED_TOKEN_FILE"] = str(token_file)
|
||||
|
||||
secret_name = "oidc/azure/azure-audience"
|
||||
result = get_secret(secret_name)
|
||||
result = get_secret(secret_name)
|
||||
|
||||
assert result == "azure_token"
|
||||
|
||||
@@ -156,16 +157,22 @@ def test_oidc_azure_ad_token_success(mock_get_azure_ad_token_provider):
|
||||
if "AZURE_FEDERATED_TOKEN_FILE" in os.environ:
|
||||
del os.environ["AZURE_FEDERATED_TOKEN_FILE"]
|
||||
|
||||
# Mock the token provider function that gets returned and called
|
||||
mock_token_provider = Mock(return_value="azure_ad_token")
|
||||
mock_get_azure_ad_token_provider.return_value = mock_token_provider
|
||||
secret_name = "oidc/azure/api://azure-audience"
|
||||
result = get_secret(secret_name)
|
||||
|
||||
# Also mock the Azure Identity SDK to prevent any real Azure calls
|
||||
with patch("azure.identity.get_bearer_token_provider") as mock_bearer:
|
||||
mock_bearer.return_value = mock_token_provider
|
||||
|
||||
secret_name = "oidc/azure/api://azure-audience"
|
||||
result = get_secret(secret_name)
|
||||
|
||||
assert result == "azure_ad_token"
|
||||
mock_get_azure_ad_token_provider.assert_called_once_with(
|
||||
azure_scope="api://azure-audience"
|
||||
)
|
||||
mock_token_provider.assert_called_once_with()
|
||||
assert result == "azure_ad_token"
|
||||
mock_get_azure_ad_token_provider.assert_called_once_with(
|
||||
azure_scope="api://azure-audience"
|
||||
)
|
||||
mock_token_provider.assert_called_once_with()
|
||||
|
||||
|
||||
def test_oidc_file_success(tmp_path):
|
||||
|
||||
@@ -119,8 +119,12 @@ def test_add_vector_store_to_registry():
|
||||
|
||||
|
||||
|
||||
@respx.mock
|
||||
def test_search_uses_registry_credentials():
|
||||
"""search() should pull credentials from vector_store_registry when available"""
|
||||
# Block all HTTP requests at the network level to prevent real API calls
|
||||
respx.route().mock(return_value=httpx.Response(200, json={"object": "list", "data": []}))
|
||||
|
||||
vector_store = LiteLLM_ManagedVectorStore(
|
||||
vector_store_id="vs1",
|
||||
custom_llm_provider="bedrock",
|
||||
|
||||
Reference in New Issue
Block a user