mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
Add tests/e2e/router/test_redis_timeout_e2e.py against a proxy booted from tests/e2e/gateway/redis_timeout_ci_config.yml: a real Redis with socket_timeout 0.001, so every command times out and the circuit breaker opens, plus a primary deployment that always fails and falls back to a healthy one, so every request carries retry breadcrumbs into cost tracking. The test drives twenty chat requests through the proxy and asserts each answers within ten seconds, the last third is no slower than the first, /health/liveliness stays fast, and every request still reaches the spend log. Gate it behind the redis_timeout marker and E2E_REDIS_TIMEOUT, exclude it from the per-PR e2e-changed selector, register the reliability.circuit_breaker.redis_timeout.stays_responsive cell, and run it as its own job in the weekly load anomaly workflow with a Postgres and Valkey service. Against a v1.100.0 proxy the run wedges the worker: requests time out and liveliness stops answering (LIT-6780). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ZDULyJPp17ZFiJenRxs2T
45 lines
1.7 KiB
Python
45 lines
1.7 KiB
Python
import re
|
|
import sys
|
|
from typing import Final
|
|
|
|
SELECTABLE: Final = re.compile(r"^tests/e2e/([A-Za-z0-9_.-]+/)*test_[A-Za-z0-9_.-]+\.py$")
|
|
UNSUPPORTED: Final = re.compile(
|
|
r"^tests/e2e/(ui|claude_code|load)/"
|
|
r"|^tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e\.py$"
|
|
r"|^tests/e2e/batches/test_managed_files_enforcement_e2e\.py$"
|
|
r"|^tests/e2e/guardrails/test_presidio_masking_e2e\.py$"
|
|
r"|^tests/e2e/router/test_redis_timeout_e2e\.py$"
|
|
)
|
|
HARNESS: Final = re.compile(
|
|
r"^tests/e2e/[A-Za-z0-9_.-]+\.(py|ini)$"
|
|
r"|^tests/e2e/gateway/"
|
|
r"|^\.github/e2e-stack/"
|
|
r"|^\.github/workflows/test-e2e-changed\.yml$"
|
|
)
|
|
UNEXPANDED: Final = re.compile(r"[*?\[]")
|
|
|
|
|
|
def is_selectable(path: str) -> bool:
|
|
return SELECTABLE.match(path) is not None and UNSUPPORTED.match(path) is None
|
|
|
|
|
|
def select(changed: tuple[str, ...], canary: tuple[str, ...]) -> tuple[str, ...]:
|
|
direct: Final = frozenset(path for path in changed if is_selectable(path))
|
|
harness_changed: Final = any(HARNESS.match(path) for path in changed)
|
|
canary_tests: Final = frozenset(path for path in canary if harness_changed and is_selectable(path))
|
|
return tuple(sorted(direct | canary_tests))
|
|
|
|
|
|
def main() -> int:
|
|
canary: Final = tuple(sys.argv[1:])
|
|
unexpanded: Final = tuple(path for path in canary if UNEXPANDED.search(path))
|
|
if unexpanded:
|
|
_ = sys.stderr.write(f"the canary paths reached the selector unexpanded: {' '.join(unexpanded)}\n")
|
|
return 1
|
|
changed: Final = tuple(line.strip() for line in sys.stdin if line.strip())
|
|
_ = sys.stdout.write(" ".join(select(changed, canary)) + "\n")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|