mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-10 08:28:25 +00:00
Some checks are pending
CodSpeed Benchmarks / benchmarks (push) Waiting to run
Deploy main to gateway-dev / dispatch (push) Waiting to run
Publish lint base counts / publish (basedpyright, scripts/type_check_gate.py) (push) Waiting to run
Publish lint base counts / publish (openapi-docs, scripts/openapi_docs_gate.py) (push) Waiting to run
Publish lint base counts / publish (ruff-strict, scripts/ruff_strict_gate.py) (push) Waiting to run
Publish lint base counts / publish (test-quality, scripts/test_quality_gate.py) (push) Waiting to run
Publish lint base counts / publish (type-discipline, scripts/type_discipline_gate.py) (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
unit / proxy-server-core (push) Blocked by required conditions
unit / root (push) Blocked by required conditions
unit / proxy-utils (push) Blocked by required conditions
unit / mcp-elicitation (push) Blocked by required conditions
unit / proxy-auth (push) Blocked by required conditions
unit / proxy-endpoints (push) Blocked by required conditions
unit / proxy-extras (push) Blocked by required conditions
unit / proxy-feature-endpoints (push) Blocked by required conditions
unit / proxy-hooks-client (push) Blocked by required conditions
unit / proxy-infra (push) Blocked by required conditions
unit / proxy-infra-root (push) Blocked by required conditions
unit / responses-caching-types (push) Blocked by required conditions
unit / router (push) Blocked by required conditions
unit / rust-bridge-harness (push) Blocked by required conditions
unit / auth-checks (push) Blocked by required conditions
unit / budgets (push) Blocked by required conditions
unit / custom-logging (push) Blocked by required conditions
unit / enterprise-routing (push) Blocked by required conditions
unit / guardrails-hooks (push) Blocked by required conditions
unit / key-generation (push) Blocked by required conditions
unit / logging-misc (push) Blocked by required conditions
unit / assert-shard-coverage (push) Waiting to run
unit / llms-openai-meta (push) Blocked by required conditions
unit / llms-providers (push) Blocked by required conditions
unit / llms-vertex-ai (push) Blocked by required conditions
unit / ui-unit (push) Waiting to run
unit / docs (push) Waiting to run
unit / OpenAI 3.25.0 compatibility (push) Waiting to run
unit / coverage (push) Blocked by required conditions
unit / db-and-spend (push) Blocked by required conditions
unit / endpoints-and-responses (push) Blocked by required conditions
unit / jwt-and-keys (push) Blocked by required conditions
unit / mcp-oauth (push) Blocked by required conditions
unit / proxy-runtime (push) Blocked by required conditions
unit / rust-bridge (push) Waiting to run
unit / enterprise-managed-files (push) Blocked by required conditions
unit / proxy-server (push) Blocked by required conditions
unit / lens-python-310 (push) Blocked by required conditions
unit / caching-local (push) Blocked by required conditions
unit / core-utils (push) Blocked by required conditions
unit / endpoints (push) Blocked by required conditions
unit / enterprise-package (push) Blocked by required conditions
unit / enterprise-repositories-secrets (push) Blocked by required conditions
unit / integrations (push) Blocked by required conditions
unit / llms-anthropic (push) Blocked by required conditions
unit / llms-bedrock (push) Blocked by required conditions
unit / helm (push) Waiting to run
unit / OpenAI 2.20.0 compatibility (push) Waiting to run
unit / OpenAI 3.0.0 compatibility (push) Waiting to run
unit / unit passed (push) Blocked by required conditions
GitHub Actions Security Analysis / zizmor (push) Waiting to run
* test: migrate proxy security, schema migration and MCP legacy tests * test: run the MCP proxy e2e upstream under the SDK1 peer in integration-mcp * test: warm the baseline store before timing the locked projection * test(e2e): hold the stack exclusively while the semantic filter flag is flipped * test(mcp): scope the in-process proxy e2e fixtures to their module The session-scoped autouse fixture deleted DATABASE_URL and overrode LITELLM_MASTER_KEY for the rest of the worker, which broke every later test in the integration mcp suite * test(e2e): assert the semantic filter keeps listed tools instead of a ranking Which Datadog tool an OpenAI embedding ranks first is not something litellm owns, so the test now checks that the kept tools are a non-empty subset of the key's listing within top_k * test(mcp): keep the in-process proxy e2e tests on one worker loadgroup spread the file's tests across workers and interleaved them with other files, so the module-scoped in-process proxy was torn down and restarted between tests and requests timed out * test(mcp): name the upstream requests that carried the wrong token The bare all() assertion hid which request broke the forwarded token, which made an intermittent CircleCI failure undiagnosable --------- Co-authored-by: yuneng <yuneng@berri.ai>
121 lines
4.2 KiB
Python
121 lines
4.2 KiB
Python
"""Coverage for the opt-in REPLICA IDENTITY FULL post-migration step.
|
|
|
|
The DB-backed tests run against the same Postgres the migration suite uses, in
|
|
a throwaway schema so they cannot disturb the migrated tables.
|
|
"""
|
|
|
|
import os
|
|
import uuid
|
|
|
|
import pytest
|
|
|
|
from litellm_proxy_extras.replica_identity import (
|
|
REPLICA_IDENTITY_FULL_ENV_VAR,
|
|
)
|
|
from litellm_proxy_extras.utils import ProxyExtrasDBManager
|
|
|
|
psycopg = pytest.importorskip("psycopg")
|
|
|
|
requires_db = pytest.mark.skipif(
|
|
"DATABASE_URL" not in os.environ,
|
|
reason="requires a postgres database (DATABASE_URL)",
|
|
)
|
|
|
|
|
|
def _base_url() -> str:
|
|
return os.environ["DATABASE_URL"].split("?")[0]
|
|
|
|
|
|
def _replica_identities(schema: str) -> dict:
|
|
with psycopg.connect(_base_url(), autocommit=True) as conn:
|
|
rows = conn.execute(
|
|
"SELECT c.relname, c.relreplident FROM pg_class c "
|
|
"JOIN pg_namespace n ON n.oid = c.relnamespace "
|
|
"WHERE n.nspname = %s AND c.relkind = 'r'",
|
|
(schema,),
|
|
).fetchall()
|
|
return dict(rows)
|
|
|
|
|
|
@pytest.fixture
|
|
def scratch_schema(monkeypatch):
|
|
"""A schema holding two LiteLLM tables and one foreign table, all at the default."""
|
|
schema = f"replica_identity_{uuid.uuid4().hex[:8]}"
|
|
with psycopg.connect(_base_url(), autocommit=True) as conn:
|
|
conn.execute(f'CREATE SCHEMA "{schema}"')
|
|
conn.execute(
|
|
f'CREATE TABLE "{schema}"."LiteLLM_ScratchTable" (id TEXT PRIMARY KEY, note TEXT)'
|
|
)
|
|
conn.execute(f'CREATE TABLE "{schema}"."LiteLLM_ScratchSibling" (id TEXT PRIMARY KEY)')
|
|
conn.execute(f'CREATE TABLE "{schema}"."ScratchForeignTable" (id TEXT PRIMARY KEY)')
|
|
|
|
monkeypatch.setenv("DATABASE_URL", f"{_base_url()}?schema={schema}")
|
|
yield schema
|
|
|
|
with psycopg.connect(_base_url(), autocommit=True) as conn:
|
|
conn.execute(f'DROP SCHEMA "{schema}" CASCADE')
|
|
|
|
|
|
@requires_db
|
|
def test_applies_full_to_litellm_tables_only(scratch_schema, monkeypatch):
|
|
monkeypatch.setenv(REPLICA_IDENTITY_FULL_ENV_VAR, "true")
|
|
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is True
|
|
|
|
identities = _replica_identities(scratch_schema)
|
|
assert identities["LiteLLM_ScratchTable"] == "f"
|
|
assert identities["LiteLLM_ScratchSibling"] == "f"
|
|
assert identities["ScratchForeignTable"] == "d"
|
|
|
|
|
|
@requires_db
|
|
def test_a_locked_table_does_not_block_the_others(scratch_schema, monkeypatch):
|
|
"""ALTER TABLE needs an exclusive lock, so a table busy with a long read has
|
|
to be skipped for the next run instead of stalling every other table behind it."""
|
|
monkeypatch.setenv(REPLICA_IDENTITY_FULL_ENV_VAR, "true")
|
|
|
|
with psycopg.connect(_base_url()) as holder:
|
|
holder.execute(f'SELECT * FROM "{scratch_schema}"."LiteLLM_ScratchTable"')
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is True
|
|
|
|
identities = _replica_identities(scratch_schema)
|
|
assert identities["LiteLLM_ScratchTable"] == "d"
|
|
assert identities["LiteLLM_ScratchSibling"] == "f"
|
|
|
|
|
|
@requires_db
|
|
def test_leaves_tables_alone_when_not_requested(scratch_schema, monkeypatch):
|
|
monkeypatch.delenv(REPLICA_IDENTITY_FULL_ENV_VAR, raising=False)
|
|
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is False
|
|
assert _replica_identities(scratch_schema)["LiteLLM_ScratchTable"] == "d"
|
|
|
|
|
|
@requires_db
|
|
def test_is_idempotent_across_runs(scratch_schema, monkeypatch):
|
|
monkeypatch.setenv(REPLICA_IDENTITY_FULL_ENV_VAR, "true")
|
|
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is True
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is True
|
|
|
|
assert _replica_identities(scratch_schema)["LiteLLM_ScratchTable"] == "f"
|
|
|
|
|
|
@requires_db
|
|
def test_reports_failure_without_raising(scratch_schema, monkeypatch):
|
|
"""A run that cannot execute the statement must not take the migration down."""
|
|
monkeypatch.setenv(REPLICA_IDENTITY_FULL_ENV_VAR, "true")
|
|
monkeypatch.setattr(
|
|
ProxyExtrasDBManager,
|
|
"_get_prisma_dir",
|
|
staticmethod(lambda: "/nonexistent/prisma/dir"),
|
|
)
|
|
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is False
|
|
assert _replica_identities(scratch_schema)["LiteLLM_ScratchTable"] == "d"
|
|
|
|
|
|
|
|
|
|
|
|
|