Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ commands:
- wait_for_service:
url: tcp://localhost:5432
timeout: "60"
start_redis:
description: "Start a redis container on port 6379 and wait until it accepts connections. Use this to isolate a job from the shared remote Redis so concurrent CI pipelines don't contend for pod locks or buffer keys."
steps:
- run:
name: Start Redis
command: |
docker run -d \
--name redis-cache \
-p 6379:6379 \
redis:7-alpine@sha256:7aec734b2bb298a1d769fd8729f13b8514a41bf90fcdd1f38ec52267fbaa8ee6
- wait_for_service:
url: tcp://localhost:6379
timeout: "60"
setup_litellm_enterprise_pip:
steps:
- run:
Expand Down Expand Up @@ -1775,6 +1788,7 @@ jobs:
command: |
uv sync --frozen --all-groups --all-extras --python 3.12
- start_postgres
- start_redis
- attach_workspace:
at: ~/project
- run:
Expand All @@ -1784,15 +1798,18 @@ jobs:
docker images | grep litellm-docker-database
- run:
name: Run Docker container
# intentionally give bad redis credentials here
# the OTEL test - should get this as a trace
# Point the proxy at the job-local Redis (start_redis) instead of the
# shared remote Redis. The Redis transaction buffer uses a single
# global pod-lock key (cronjob_lock:db_spend_update_job) and a single
# global buffer list (litellm_spend_update_buffer); sharing those
# across concurrent CI pipelines causes spend flushes to stall or
# land in the wrong DB, which is what makes this test flaky.
command: |
docker run -d \
-p 4000:4000 \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e REDIS_HOST=host.docker.internal \
-e REDIS_PORT=6379 \
-e LITELLM_MASTER_KEY="sk-1234" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
Expand Down Expand Up @@ -1824,12 +1841,14 @@ jobs:
ls
uv run --no-sync python -m pytest -vv tests/spend_tracking_tests -x --junitxml=test-results/junit.xml --durations=5
no_output_timeout: 15m
# Clean up first container
- run:
name: Stop and remove first container
when: always
command: |
docker stop my-app
docker rm my-app
docker stop redis-cache
docker rm redis-cache
Comment thread
yuneng-berri marked this conversation as resolved.

proxy_multi_instance_tests:
machine:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,26 @@ def reset_router_callbacks():
litellm.logging_callback_manager._reset_all_callbacks()


@pytest.fixture(autouse=True)
def reset_proxy_auth_globals(monkeypatch):
"""
Pin proxy auth-related globals to a known baseline so tests don't inherit
leaked state (master_key, prisma_client, custom auth, cached tokens) from
earlier tests. Individual tests can still override via their own
monkeypatch calls — those run after this fixture and revert first.
"""
import litellm.proxy.proxy_server as ps

monkeypatch.setattr(ps, "prisma_client", None)
monkeypatch.setattr(ps, "master_key", None)
monkeypatch.setattr(ps, "user_custom_auth", None)
monkeypatch.setattr(ps, "general_settings", {})
try:
ps.user_api_key_cache.in_memory_cache.cache_dict.clear()
except AttributeError:
pass


@pytest.mark.asyncio
async def test_ui_view_spend_logs_with_user_id(client, monkeypatch):
mock_spend_logs = [
Expand Down Expand Up @@ -1150,14 +1170,14 @@ def filter_by_date(where):
async def test_ui_view_spend_logs_unauthorized(client):
# Test without authorization header
response = client.get("/spend/logs/ui")
assert response.status_code == 401 or response.status_code == 403
assert response.status_code in (401, 403), response.text

# Test with invalid authorization
response = client.get(
"/spend/logs/ui",
headers={"Authorization": "Bearer invalid-token"},
)
assert response.status_code == 401 or response.status_code == 403
assert response.status_code in (401, 403), response.text


@pytest.mark.asyncio
Expand Down
Loading