""" Unit tests for per-service SSL support in LiteLLM. These tests verify that ssl_verify parameters are correctly propagated through the call stack without requiring live API credentials. """ import sys from pathlib import Path from unittest.mock import Mock, patch import pytest # Add litellm to path sys.path.insert(0, str(Path(__file__).parent)) import litellm.proxy.guardrails.guardrail_hooks.aim.aim as _aim_module import litellm.proxy.guardrails.guardrail_hooks.cato_networks.cato_networks as _cato_networks_module from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM from litellm.proxy.guardrails.guardrail_hooks.aim.aim import AimGuardrail from litellm.proxy.guardrails.guardrail_hooks.cato_networks.cato_networks import CatoNetworksGuardrail class TestBaseAWSLLMSSLVerify: """Test SSL verification parameter handling in BaseAWSLLM.""" def test_get_ssl_verify_with_parameter(self): """Test that _get_ssl_verify accepts and uses the ssl_verify parameter.""" base_llm = BaseAWSLLM() # Test with True result = base_llm._get_ssl_verify(ssl_verify=True) assert result is True # Test with False result = base_llm._get_ssl_verify(ssl_verify=False) assert result is False # Test with cert path cert_path = "/path/to/cert.pem" result = base_llm._get_ssl_verify(ssl_verify=cert_path) assert result == cert_path def test_get_ssl_verify_without_parameter(self): """Test that _get_ssl_verify falls back to environment/global when no parameter.""" base_llm = BaseAWSLLM() # Should fall back to environment or global litellm.ssl_verify result = base_llm._get_ssl_verify() # Result depends on environment, just verify it doesn't crash assert result is not None or result is None # Can be None, True, False, or path class TestAimGuardrailSSLVerify: """Test SSL verification parameter handling in AimGuardrail.""" def test_init_accepts_ssl_verify(self): """Test that AimGuardrail.__init__ accepts and uses ssl_verify parameter.""" mock_handler = Mock() # Use patch.object on the actual module reference for reliable patching # across different import orders / CI environments with patch.object( _aim_module, "get_async_httpx_client", return_value=mock_handler ) as mock_get_client: # Initialize with ssl_verify cert_path = "/path/to/aim_cert.pem" AimGuardrail( api_key="test_key", api_base="https://test.aim.api", ssl_verify=cert_path, ) # Verify get_async_httpx_client was called with ssl_verify in params assert mock_get_client.called call_kwargs = mock_get_client.call_args[1] assert "params" in call_kwargs assert call_kwargs["params"] is not None assert call_kwargs["params"]["ssl_verify"] == cert_path def test_init_without_ssl_verify(self): """Test that AimGuardrail works without ssl_verify parameter.""" mock_handler = Mock() # Use patch.object on the actual module reference for reliable patching with patch.object( _aim_module, "get_async_httpx_client", return_value=mock_handler ) as mock_get_client: # Initialize without ssl_verify AimGuardrail(api_key="test_key", api_base="https://test.aim.api") # Should still work, just without custom SSL assert mock_get_client.called class TestCatoNetworksGuardrailSSLVerify: """Test SSL verification parameter handling in CatoNetworksGuardrail.""" def test_init_accepts_ssl_verify(self): """Test that CatoNetworksGuardrail.__init__ accepts and uses ssl_verify parameter.""" mock_handler = Mock() # Use patch.object on the actual module reference for reliable patching # across different import orders / CI environments with patch.object( _cato_networks_module, "get_async_httpx_client", return_value=mock_handler ) as mock_get_client: # Initialize with ssl_verify cert_path = "/path/to/cato_cert.pem" CatoNetworksGuardrail( api_key="test_key", api_base="https://test.catonetworks.api", ssl_verify=cert_path, ) # Verify get_async_httpx_client was called with ssl_verify in params assert mock_get_client.called call_kwargs = mock_get_client.call_args[1] assert "params" in call_kwargs assert call_kwargs["params"] is not None assert call_kwargs["params"]["ssl_verify"] == cert_path def test_init_without_ssl_verify(self): """Test that CatoNetworksGuardrail works without ssl_verify parameter.""" mock_handler = Mock() # Use patch.object on the actual module reference for reliable patching with patch.object( _cato_networks_module, "get_async_httpx_client", return_value=mock_handler ) as mock_get_client: # Initialize without ssl_verify CatoNetworksGuardrail(api_key="test_key", api_base="https://test.catonetworks.api") # Should still work, just without custom SSL assert mock_get_client.called class TestHTTPHandlerSSLVerify: """Test SSL verification parameter handling in HTTP handlers.""" def test_get_async_httpx_client_accepts_ssl_verify_in_params(self): """Test that get_async_httpx_client accepts ssl_verify in params dict.""" from litellm.llms.custom_httpx.http_handler import get_async_httpx_client from litellm.types.llms.custom_http import httpxSpecialProvider # Call with ssl_verify in params cert_path = "/path/to/cert.pem" client = get_async_httpx_client( llm_provider=httpxSpecialProvider.GuardrailCallback, params={"ssl_verify": cert_path}, ) # Verify client was created (actual SSL config is tested in integration tests) assert client is not None def test_ssl_verify_parameter_types(): """Test that various ssl_verify parameter types are handled correctly.""" base_llm = BaseAWSLLM() # Test boolean True result = base_llm._get_ssl_verify(ssl_verify=True) assert result is True # Test boolean False result = base_llm._get_ssl_verify(ssl_verify=False) assert result is False # Test string path cert_path = "/path/to/cert.pem" result = base_llm._get_ssl_verify(ssl_verify=cert_path) assert result == cert_path # Test None (should fall back to environment/global) result = base_llm._get_ssl_verify(ssl_verify=None) # Result depends on environment assert result is not None or result is None if __name__ == "__main__": # Run tests pytest.main([__file__, "-v", "--tb=short"])