mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-12 03:48:56 +00:00
* test: move the unit half of 126 mixed legacy files into tests/unit * test: restore litellm globals that moved tests set * test: finalize migration test cleanup * test: restore original bodies of moved legacy tests The move into tests/unit had rewritten 612 test bodies, and some of the rewrites dropped assertions. Each moved test now carries its original body from the legacy file, with only the imports, helpers, fake provider credentials and monkeypatched env it needs to run under tests/unit test_timeout_streaming goes back to tests/local_testing because it needs the fake OpenAI endpoint server. The image payload fixture moves with its only user, and two tests that leaked global state (a registered model cost entry and queued logging tasks) are now isolated * test: drop module imports shadowed by restored local imports * test: assert on LiteLLM output in no-assertion moved tests and isolate leaks Twenty no-assertion candidates get one assertion on the value LiteLLM returns, with the original lines unchanged. Four tests go back to their legacy files because they only check types or imports, write into the working directory, or cannot assert without a body change Two moved tests leaked globals into later tests in the same worker, so monkeypatch fixtures now restore the retry-after header parser and the end user cost tracking flags * test: drain queued logging tasks before the Phoenix span test The moved Phoenix test counted spans from logging tasks that earlier tests had queued, so the drain fixture moves to tests/unit/conftest.py and both it and the Datadog batch test use it. test_factory_function goes back to its legacy file because its returned wrapper calls the real Assistants API and cannot be asserted on without a body change --------- Co-authored-by: yuneng <yuneng@berri.ai>
190 lines
7 KiB
Python
190 lines
7 KiB
Python
# 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
|