Spaces:
Paused
Paused
| import os | |
| import sys | |
| from unittest.mock import MagicMock, patch | |
| import pytest | |
| sys.path.insert( | |
| 0, os.path.abspath("../../..") | |
| ) # Adds the parent directory to the system-path | |
| from litellm.proxy.proxy_cli import ProxyInitializationHelpers | |
| class TestProxyInitializationHelpers: | |
| def test_echo_litellm_version(self, mock_echo, mock_version): | |
| # Setup | |
| mock_version.return_value = "1.0.0" | |
| # Execute | |
| ProxyInitializationHelpers._echo_litellm_version() | |
| # Assert | |
| mock_version.assert_called_once_with("litellm") | |
| mock_echo.assert_called_once_with("\nLiteLLM: Current Version = 1.0.0\n") | |
| def test_run_health_check(self, mock_dumps, mock_print, mock_get): | |
| # Setup | |
| mock_response = MagicMock() | |
| mock_response.json.return_value = {"status": "healthy"} | |
| mock_get.return_value = mock_response | |
| mock_dumps.return_value = '{"status": "healthy"}' | |
| # Execute | |
| ProxyInitializationHelpers._run_health_check("localhost", 8000) | |
| # Assert | |
| mock_get.assert_called_once_with(url="http://localhost:8000/health") | |
| mock_response.json.assert_called_once() | |
| mock_dumps.assert_called_once_with({"status": "healthy"}, indent=4) | |
| def test_run_test_chat_completion(self, mock_print, mock_echo, mock_openai): | |
| # Setup | |
| mock_client = MagicMock() | |
| mock_openai.return_value = mock_client | |
| mock_response = MagicMock() | |
| mock_client.chat.completions.create.return_value = mock_response | |
| mock_stream_response = MagicMock() | |
| mock_stream_response.__iter__.return_value = [MagicMock(), MagicMock()] | |
| mock_client.chat.completions.create.side_effect = [ | |
| mock_response, | |
| mock_stream_response, | |
| ] | |
| # Execute | |
| with pytest.raises(ValueError, match="Invalid test value"): | |
| ProxyInitializationHelpers._run_test_chat_completion( | |
| "localhost", 8000, "gpt-3.5-turbo", True | |
| ) | |
| # Test with valid string test value | |
| ProxyInitializationHelpers._run_test_chat_completion( | |
| "localhost", 8000, "gpt-3.5-turbo", "http://test-url" | |
| ) | |
| # Assert | |
| mock_openai.assert_called_once_with( | |
| api_key="My API Key", base_url="http://test-url" | |
| ) | |
| mock_client.chat.completions.create.assert_called() | |
| def test_get_default_unvicorn_init_args(self): | |
| # Test without log_config | |
| args = ProxyInitializationHelpers._get_default_unvicorn_init_args( | |
| "localhost", 8000 | |
| ) | |
| assert args["app"] == "litellm.proxy.proxy_server:app" | |
| assert args["host"] == "localhost" | |
| assert args["port"] == 8000 | |
| # Test with log_config | |
| args = ProxyInitializationHelpers._get_default_unvicorn_init_args( | |
| "localhost", 8000, "log_config.json" | |
| ) | |
| assert args["log_config"] == "log_config.json" | |
| # Test with json_logs=True | |
| with patch("litellm.json_logs", True): | |
| args = ProxyInitializationHelpers._get_default_unvicorn_init_args( | |
| "localhost", 8000 | |
| ) | |
| assert args["log_config"] is None | |
| # Test with keepalive_timeout | |
| args = ProxyInitializationHelpers._get_default_unvicorn_init_args( | |
| "localhost", 8000, None, 60 | |
| ) | |
| assert args["timeout_keep_alive"] == 60 | |
| # Test with both log_config and keepalive_timeout | |
| args = ProxyInitializationHelpers._get_default_unvicorn_init_args( | |
| "localhost", 8000, "log_config.json", 120 | |
| ) | |
| assert args["log_config"] == "log_config.json" | |
| assert args["timeout_keep_alive"] == 120 | |
| def test_init_hypercorn_server(self, mock_print, mock_asyncio_run): | |
| # Setup | |
| mock_app = MagicMock() | |
| # Execute | |
| ProxyInitializationHelpers._init_hypercorn_server( | |
| mock_app, "localhost", 8000, None, None | |
| ) | |
| # Assert | |
| mock_asyncio_run.assert_called_once() | |
| # Test with SSL | |
| ProxyInitializationHelpers._init_hypercorn_server( | |
| mock_app, "localhost", 8000, "cert.pem", "key.pem" | |
| ) | |
| def test_run_ollama_serve(self, mock_popen): | |
| # Execute | |
| ProxyInitializationHelpers._run_ollama_serve() | |
| # Assert | |
| mock_popen.assert_called_once() | |
| # Test exception handling | |
| mock_popen.side_effect = Exception("Test exception") | |
| ProxyInitializationHelpers._run_ollama_serve() # Should not raise | |
| def test_is_port_in_use(self, mock_socket): | |
| # Setup for port in use | |
| mock_socket_instance = MagicMock() | |
| mock_socket_instance.connect_ex.return_value = 0 | |
| mock_socket.return_value.__enter__.return_value = mock_socket_instance | |
| # Execute and Assert | |
| assert ProxyInitializationHelpers._is_port_in_use(8000) is True | |
| # Setup for port not in use | |
| mock_socket_instance.connect_ex.return_value = 1 | |
| # Execute and Assert | |
| assert ProxyInitializationHelpers._is_port_in_use(8000) is False | |
| def test_get_loop_type(self): | |
| # Test on Windows | |
| with patch("sys.platform", "win32"): | |
| assert ProxyInitializationHelpers._get_loop_type() is None | |
| # Test on Linux | |
| with patch("sys.platform", "linux"): | |
| assert ProxyInitializationHelpers._get_loop_type() == "uvloop" | |
| def test_database_url_construction_with_special_characters(self): | |
| # Setup environment variables with special characters that need escaping | |
| test_env = { | |
| "DATABASE_HOST": "localhost:5432", | |
| "DATABASE_USERNAME": "user@with+special", | |
| "DATABASE_PASSWORD": "pass&word!@#$%", | |
| "DATABASE_NAME": "db_name/test", | |
| } | |
| with patch.dict(os.environ, test_env): | |
| # Call the relevant function - we'll need to extract the database URL construction logic | |
| # This is simulating what happens in the run_server function when database_url is None | |
| import urllib.parse | |
| from litellm.proxy.proxy_cli import append_query_params | |
| database_host = os.environ["DATABASE_HOST"] | |
| database_username = os.environ["DATABASE_USERNAME"] | |
| database_password = os.environ["DATABASE_PASSWORD"] | |
| database_name = os.environ["DATABASE_NAME"] | |
| # Test the URL encoding part | |
| database_username_enc = urllib.parse.quote_plus(database_username) | |
| database_password_enc = urllib.parse.quote_plus(database_password) | |
| database_name_enc = urllib.parse.quote_plus(database_name) | |
| # Construct DATABASE_URL from the provided variables | |
| database_url = f"postgresql://{database_username_enc}:{database_password_enc}@{database_host}/{database_name_enc}" | |
| # Assert the correct URL was constructed with properly escaped characters | |
| expected_url = "postgresql://user%40with%2Bspecial:pass%26word%21%40%23%24%25@localhost:5432/db_name%2Ftest" | |
| assert database_url == expected_url | |
| # Test appending query parameters | |
| params = {"connection_limit": 10, "pool_timeout": 60} | |
| modified_url = append_query_params(database_url, params) | |
| assert "connection_limit=10" in modified_url | |
| assert "pool_timeout=60" in modified_url | |
| def test_skip_server_startup(self, mock_print, mock_uvicorn_run): | |
| """Test that the skip_server_startup flag prevents server startup when True""" | |
| from click.testing import CliRunner | |
| from litellm.proxy.proxy_cli import run_server | |
| runner = CliRunner() | |
| mock_app = MagicMock() | |
| mock_proxy_config = MagicMock() | |
| mock_key_mgmt = MagicMock() | |
| mock_save_worker_config = MagicMock() | |
| with patch.dict( | |
| "sys.modules", | |
| { | |
| "proxy_server": MagicMock( | |
| app=mock_app, | |
| ProxyConfig=mock_proxy_config, | |
| KeyManagementSettings=mock_key_mgmt, | |
| save_worker_config=mock_save_worker_config, | |
| ) | |
| }, | |
| ), patch( | |
| "litellm.proxy.proxy_cli.ProxyInitializationHelpers._get_default_unvicorn_init_args" | |
| ) as mock_get_args: | |
| mock_get_args.return_value = { | |
| "app": "litellm.proxy.proxy_server:app", | |
| "host": "localhost", | |
| "port": 8000, | |
| } | |
| result = runner.invoke(run_server, ["--local", "--skip_server_startup"]) | |
| assert result.exit_code == 0 | |
| mock_uvicorn_run.assert_not_called() | |
| mock_print.assert_any_call( | |
| "LiteLLM: Setup complete. Skipping server startup as requested." | |
| ) | |
| mock_uvicorn_run.reset_mock() | |
| mock_print.reset_mock() | |
| result = runner.invoke(run_server, ["--local"]) | |
| assert result.exit_code == 0 | |
| mock_uvicorn_run.assert_called_once() | |
| def test_keepalive_timeout_flag(self, mock_print, mock_uvicorn_run): | |
| """Test that the keepalive_timeout flag is properly passed to uvicorn""" | |
| from click.testing import CliRunner | |
| from litellm.proxy.proxy_cli import run_server | |
| runner = CliRunner() | |
| mock_app = MagicMock() | |
| mock_proxy_config = MagicMock() | |
| mock_key_mgmt = MagicMock() | |
| mock_save_worker_config = MagicMock() | |
| with patch.dict( | |
| "sys.modules", | |
| { | |
| "proxy_server": MagicMock( | |
| app=mock_app, | |
| ProxyConfig=mock_proxy_config, | |
| KeyManagementSettings=mock_key_mgmt, | |
| save_worker_config=mock_save_worker_config, | |
| ) | |
| }, | |
| ), patch( | |
| "litellm.proxy.proxy_cli.ProxyInitializationHelpers._get_default_unvicorn_init_args" | |
| ) as mock_get_args: | |
| mock_get_args.return_value = { | |
| "app": "litellm.proxy.proxy_server:app", | |
| "host": "localhost", | |
| "port": 8000, | |
| "timeout_keep_alive": 30, | |
| } | |
| result = runner.invoke(run_server, ["--local", "--keepalive_timeout", "30"]) | |
| assert result.exit_code == 0 | |
| mock_get_args.assert_called_once_with( | |
| host="0.0.0.0", | |
| port=4000, | |
| log_config=None, | |
| keepalive_timeout=30, | |
| ) | |
| mock_uvicorn_run.assert_called_once() | |
| # Check that the uvicorn.run was called with the timeout_keep_alive parameter | |
| call_args = mock_uvicorn_run.call_args | |
| assert call_args[1]["timeout_keep_alive"] == 30 | |