mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-26 06:03:19 +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>
130 lines
4.2 KiB
Python
130 lines
4.2 KiB
Python
"""The opt-in REPLICA IDENTITY FULL step, without a database.
|
|
|
|
The behavior against real Postgres is covered by
|
|
tests/integration/database/migrations/test_replica_identity_full.py; these pin the two
|
|
things that hold with no database at all: the statement handed to the Prisma
|
|
CLI, and the promise that no failure of this optional step escapes into a
|
|
migration run that already succeeded.
|
|
"""
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from litellm_proxy_extras.replica_identity import (
|
|
REPLICA_IDENTITY_FULL_ENV_VAR,
|
|
apply_replica_identity_full,
|
|
)
|
|
from litellm_proxy_extras.utils import ProxyExtrasDBManager
|
|
|
|
|
|
def test_hands_the_alter_statement_to_the_prisma_cli():
|
|
captured = {}
|
|
|
|
def capture(cmd, **kwargs):
|
|
captured["cmd"] = cmd
|
|
captured["sql"] = Path(cmd[cmd.index("--file") + 1]).read_text()
|
|
return subprocess.CompletedProcess(cmd, 0)
|
|
|
|
with patch(
|
|
"litellm_proxy_extras.replica_identity.run_prisma", side_effect=capture
|
|
):
|
|
applied = apply_replica_identity_full(
|
|
schema_path="/somewhere/schema.prisma",
|
|
prisma_command="prisma",
|
|
prisma_env={"DATABASE_URL": "postgresql://x/y"},
|
|
)
|
|
|
|
assert applied is True
|
|
assert captured["cmd"][:3] == ["prisma", "db", "execute"]
|
|
assert captured["cmd"][-2:] == ["--schema", "/somewhere/schema.prisma"]
|
|
|
|
sql = captured["sql"]
|
|
assert "ALTER TABLE %s REPLICA IDENTITY FULL" in sql
|
|
assert r"c.relname LIKE 'LiteLLM\_%'" in sql
|
|
assert "c.relreplident <> 'f'" in sql
|
|
assert "lock_timeout" in sql
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"failure",
|
|
[
|
|
subprocess.CalledProcessError(1, "prisma", stderr="must be owner of table"),
|
|
subprocess.TimeoutExpired("prisma", 60),
|
|
OSError(2, "No such file or directory"),
|
|
PermissionError(13, "Read-only file system"),
|
|
],
|
|
ids=["rejected", "timed-out", "cli-missing", "read-only-fs"],
|
|
)
|
|
def test_every_failure_is_reported_instead_of_raised(failure):
|
|
with patch(
|
|
"litellm_proxy_extras.replica_identity.run_prisma", side_effect=failure
|
|
):
|
|
assert (
|
|
apply_replica_identity_full(
|
|
schema_path="/somewhere/schema.prisma",
|
|
prisma_command="prisma",
|
|
prisma_env={},
|
|
)
|
|
is False
|
|
)
|
|
|
|
|
|
def test_an_unusable_migrations_dir_skips_the_step_instead_of_killing_the_run(
|
|
tmp_path, monkeypatch
|
|
):
|
|
"""LITELLM_MIGRATION_DIR makes the step copy the migrations tree before it
|
|
can run, and that copy is filesystem work that can fail on its own."""
|
|
blocker = tmp_path / "blocker"
|
|
blocker.write_text("not a directory")
|
|
monkeypatch.setenv(REPLICA_IDENTITY_FULL_ENV_VAR, "true")
|
|
monkeypatch.setenv("LITELLM_MIGRATION_DIR", str(blocker / "migrations"))
|
|
|
|
assert ProxyExtrasDBManager.apply_replica_identity_full_if_requested() is False
|
|
|
|
|
|
def test_reports_an_unrunnable_prisma_cli_without_raising(tmp_path):
|
|
"""A deployment without the Prisma CLI on PATH must still finish its
|
|
migration run instead of dying on the optional replication step."""
|
|
assert (
|
|
apply_replica_identity_full(
|
|
schema_path=str(tmp_path / "schema.prisma"),
|
|
prisma_command=str(tmp_path / "no-such-prisma"),
|
|
prisma_env={},
|
|
)
|
|
is False
|
|
)
|
|
|
|
|
|
def test_setup_database_applies_after_a_successful_migration_run(monkeypatch):
|
|
applied = []
|
|
monkeypatch.setattr(
|
|
ProxyExtrasDBManager, "_run_migrations", staticmethod(lambda **kwargs: True)
|
|
)
|
|
monkeypatch.setattr(
|
|
ProxyExtrasDBManager,
|
|
"apply_replica_identity_full_if_requested",
|
|
staticmethod(lambda: applied.append(True)),
|
|
)
|
|
|
|
assert ProxyExtrasDBManager.setup_database(use_migrate=True) is True
|
|
assert applied == [True]
|
|
|
|
|
|
def test_setup_database_skips_replica_identity_when_migrations_fail(monkeypatch):
|
|
applied = []
|
|
monkeypatch.setattr(
|
|
ProxyExtrasDBManager, "_run_migrations", staticmethod(lambda **kwargs: False)
|
|
)
|
|
monkeypatch.setattr(
|
|
ProxyExtrasDBManager,
|
|
"apply_replica_identity_full_if_requested",
|
|
staticmethod(lambda: applied.append(True)),
|
|
)
|
|
|
|
assert ProxyExtrasDBManager.setup_database(use_migrate=True) is False
|
|
assert applied == []
|
|
|
|
|