# What is this? ## Tests if proxy/auth/auth_utils.py works as expected import sys, os, asyncio, time, random, uuid import traceback from dotenv import load_dotenv load_dotenv() import pytest import litellm def test_get_end_user_id_from_request_body_always_returns_str(): from litellm.proxy.auth.auth_utils import get_end_user_id_from_request_body from fastapi import Request from unittest.mock import MagicMock # Create a mock Request object mock_request = MagicMock(spec=Request) mock_request.headers = {} request_body = {"user": 123} end_user_id = get_end_user_id_from_request_body( request_body, dict(mock_request.headers) ) assert end_user_id == "123" assert isinstance(end_user_id, str) @pytest.mark.parametrize( "headers, general_settings_config, request_body, expected_user_id", [ # Test 1: user_header_name configured and header present ( {"X-User-ID": "header-user-123"}, {"user_header_name": "X-User-ID"}, {"user": "body-user-456"}, "header-user-123", # Header should take precedence ), # Test 2: user_header_name configured but header not present, fallback to body ( {}, {"user_header_name": "X-User-ID"}, {"user": "body-user-456"}, "body-user-456", # Should fall back to body ), # Test 3: user_header_name not configured, should use body ( {"X-User-ID": "header-user-123"}, {}, {"user": "body-user-456"}, "body-user-456", # Should ignore header when not configured ), # Test 4: user_header_name configured, header present, but no body user ( {"X-Custom-User": "header-only-user"}, {"user_header_name": "X-Custom-User"}, {"model": "gpt-4"}, "header-only-user", # Should use header ), # Test 5: user_header_name configured but header is empty string ( {"X-User-ID": ""}, {"user_header_name": "X-User-ID"}, {"user": "body-user-456"}, "body-user-456", # Should fall back to body when header is empty ), # Test 6: user_header_name configured with case-insensitive header ( {"x-user-id": "lowercase-header-user"}, {"user_header_name": "x-user-id"}, {"user": "body-user-456"}, "lowercase-header-user", ), # Test 7: user_header_name configured but set to None ( {"X-User-ID": "header-user-123"}, {"user_header_name": None}, {"user": "body-user-456"}, "body-user-456", # Should fall back to body when header name is None ), # Test 8: user_header_name is not a string ( {"X-User-ID": "header-user-123"}, {"user_header_name": 123}, {"user": "body-user-456"}, "body-user-456", # Should fall back to body when header name is not a string ), # Test 9: Multiple fallback sources - litellm_metadata ( {}, {"user_header_name": "X-User-ID"}, {"litellm_metadata": {"user": "litellm-user-789"}}, "litellm-user-789", ), # Test 10: Multiple fallback sources - metadata.user_id ( {}, {"user_header_name": "X-User-ID"}, {"metadata": {"user_id": "metadata-user-999"}}, "metadata-user-999", ), # Test 11: Header takes precedence over all body sources ( {"X-User-ID": "header-priority"}, {"user_header_name": "X-User-ID"}, { "user": "body-user", "litellm_metadata": {"user": "litellm-user"}, "metadata": {"user_id": "metadata-user"}, }, "header-priority", ), # Test 12: user_header_name is matched case-insensitively ( {"x-user-id": "lowercase-header-user"}, {"user_header_name": "X-User-ID"}, {"user": "body-user-456"}, "lowercase-header-user", ), ], ) def test_get_end_user_id_from_request_body_with_user_header_name( headers, general_settings_config, request_body, expected_user_id ): """Test that get_end_user_id_from_request_body respects user_header_name property""" from litellm.proxy.auth.auth_utils import get_end_user_id_from_request_body from fastapi import Request from unittest.mock import MagicMock, patch # Create a mock Request object with headers mock_request = MagicMock(spec=Request) mock_request.headers = headers # Mock general_settings at the proxy_server module level with patch("litellm.proxy.proxy_server.general_settings", general_settings_config): end_user_id = get_end_user_id_from_request_body( request_body, dict(mock_request.headers) ) assert end_user_id == expected_user_id def test_get_end_user_id_from_request_body_no_user_found(): """Test that function returns None when no user ID is found anywhere""" from litellm.proxy.auth.auth_utils import get_end_user_id_from_request_body from fastapi import Request from unittest.mock import MagicMock, patch # Create a mock Request object with no relevant headers mock_request = MagicMock(spec=Request) mock_request.headers = {"X-Other-Header": "some-value"} # Mock general_settings with user_header_name that doesn't match headers general_settings_config = {"user_header_name": "X-User-ID"} # Request body with no user identifiers request_body = { "model": "gpt-4", "messages": [{"role": "user", "content": "hello"}], } with patch("litellm.proxy.proxy_server.general_settings", general_settings_config): end_user_id = get_end_user_id_from_request_body( request_body, dict(mock_request.headers) ) assert end_user_id is None def test_get_end_user_id_from_request_body_backwards_compatibility(): """Test that function works with just request_body parameter (backwards compatibility)""" from litellm.proxy.auth.auth_utils import get_end_user_id_from_request_body # Test with just request_body - should work like before request_body = {"user": "test-user-123"} end_user_id = get_end_user_id_from_request_body(request_body) assert end_user_id == "test-user-123" # Test with litellm_metadata request_body = {"litellm_metadata": {"user": "litellm-user-456"}} end_user_id = get_end_user_id_from_request_body(request_body) assert end_user_id == "litellm-user-456" # Test with metadata.user_id request_body = {"metadata": {"user_id": "metadata-user-789"}} end_user_id = get_end_user_id_from_request_body(request_body) assert end_user_id == "metadata-user-789" # Test with no user - should return None request_body = {"model": "gpt-4"} end_user_id = get_end_user_id_from_request_body(request_body) assert end_user_id is None