mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
The unit workflows trigger on pull_request only, so a commit that actually lands on a gated branch ends up with no unit-test check runs at all. The commit status API reports success for those commits, which a release gate reads as "nothing failed" rather than "never tested". PR checks also only ever ran against the merge preview, not the commit that landed, so two branches that are each green can still land broken together Add a push trigger on the two gated branches to the twelve unit workflows and to the code-quality workflow, so every landed commit gets check runs addressable by its SHA test-linting.yml is deliberately left on pull_request only; six of its steps gate on a diff against github.event.pull_request.base.sha, which is empty outside a pull request, and a "what did this branch add" check has no meaning on a merge commit Also key the concurrency group on github.sha and restrict cancel-in-progress to pull_request. The previous group was stable across pushes to a branch, so consecutive merges would cancel the in-flight run for the earlier commit and leave that SHA without a result, which is the same blind spot this change is meant to close
234 lines
10 KiB
YAML
234 lines
10 KiB
YAML
name: "Unit Tests: Proxy DB Operations"
|
|
|
|
on:
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- litellm_internal_staging
|
|
- litellm_oss_staging
|
|
- "litellm_**"
|
|
push:
|
|
branches:
|
|
- main
|
|
- litellm_internal_staging
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
# Semantic matrix: each shard groups tests by concern (auth, server, logging, …)
|
|
# rather than alphabetical letter ranges. Adding a new test file means adding it
|
|
# to whichever group it belongs to, not reshuffling slices.
|
|
#
|
|
# Design targets:
|
|
# * Every shard runs in <= 7 minutes of wall-clock on the default runner.
|
|
# Most of a shard's time is pytest plugin load + xdist worker imports +
|
|
# pytest-cov instrumentation, not the tests themselves. Keeping per-shard
|
|
# work low and matching worker count to runner cores is what controls it.
|
|
# * workers: 4 matches the 4-core ubuntu-latest runner. -n 8 on 4 cores
|
|
# oversubscribes 2x and workers fight for CPU during their cold-start
|
|
# imports (measured ~441% CPU for -n 8 locally, i.e. ~55% effective).
|
|
# * test_key_generate_prisma.py stays serial (workers=0) — it has event-loop
|
|
# conflicts with the logging worker when run in parallel.
|
|
# * test_proxy_utils.py runs as a single shard with --dist=worksteal so
|
|
# xdist balances its 188 parametrized cases across workers instead of
|
|
# pinning the whole file to one worker (the default --dist=loadscope
|
|
# behavior for single-file targets).
|
|
jobs:
|
|
# Fast guard — fails the workflow if a test_*.py file under
|
|
# tests/proxy_unit_tests/ is not referenced by any matrix entry below.
|
|
# The semantic-shard design (no catch-all "remaining" bucket) relies on
|
|
# every test file being explicitly assigned; this guard prevents a new
|
|
# file from silently dropping out of CI.
|
|
assert-shard-coverage:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 2
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
- name: Assert every test_*.py is in a matrix shard
|
|
run: |
|
|
python3 - <<'PY'
|
|
import pathlib, sys, yaml
|
|
wf = yaml.safe_load(open(".github/workflows/test-unit-proxy-db.yml"))
|
|
matrix = wf["jobs"]["proxy-db"]["strategy"]["matrix"]["include"]
|
|
referenced = set()
|
|
for entry in matrix:
|
|
for token in entry["test-path"].split():
|
|
if token.startswith("tests/proxy_unit_tests/"):
|
|
referenced.add(pathlib.PurePosixPath(token).name)
|
|
actual = {p.name for p in pathlib.Path("tests/proxy_unit_tests").iterdir()
|
|
if p.name.startswith("test_") and (p.suffix == ".py" or p.is_dir())
|
|
and p.name != "test_configs"}
|
|
orphans = sorted(actual - referenced)
|
|
if orphans:
|
|
print("ERROR: the following files/dirs under tests/proxy_unit_tests/")
|
|
print(" are not assigned to any shard in test-unit-proxy-db.yml:")
|
|
for o in orphans:
|
|
print(f" - {o}")
|
|
print()
|
|
print("Add each to whichever semantic shard it belongs to.")
|
|
sys.exit(1)
|
|
print(f"OK: all {len(actual)} files assigned to a shard.")
|
|
PY
|
|
|
|
proxy-db:
|
|
needs: assert-shard-coverage
|
|
# Display only the semantic shard name in the checks UI instead of GHA's
|
|
# default "proxy-db (key-generation, tests/proxy_unit_tests/…, 0, loadscope, 20)"
|
|
# which includes every matrix field and gets truncated past the test-path.
|
|
name: ${{ matrix.test-group }}
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
pull-requests: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# Must run serially — event-loop conflict with the logging worker.
|
|
- test-group: key-generation
|
|
test-path: "tests/proxy_unit_tests/test_key_generate_prisma.py"
|
|
workers: 0
|
|
dist: loadscope
|
|
timeout: 20
|
|
|
|
# ---- auth: split into 2 shards ----
|
|
- test-group: auth-checks
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_auth_checks.py
|
|
tests/proxy_unit_tests/test_user_api_key_auth.py
|
|
tests/proxy_unit_tests/test_deprecated_key_grace_period.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
- test-group: jwt-and-keys
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_jwt.py
|
|
tests/proxy_unit_tests/test_jwt_key_mapping.py
|
|
tests/proxy_unit_tests/test_proxy_custom_auth.py
|
|
tests/proxy_unit_tests/test_key_generate_dynamodb.py
|
|
tests/proxy_unit_tests/test_deployed_proxy_keygen.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
|
|
# ---- test_proxy_utils.py, single shard, worksteal distribution ----
|
|
- test-group: proxy-utils
|
|
test-path: "tests/proxy_unit_tests/test_proxy_utils.py"
|
|
workers: 4
|
|
dist: worksteal
|
|
timeout: 15
|
|
|
|
# ---- proxy server: split into 2 shards ----
|
|
- test-group: proxy-server-core
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_proxy_server.py
|
|
tests/proxy_unit_tests/test_proxy_server_keys.py
|
|
tests/proxy_unit_tests/test_proxy_server_caching.py
|
|
tests/proxy_unit_tests/test_proxy_server_langfuse.py
|
|
tests/proxy_unit_tests/test_proxy_server_spend.py
|
|
tests/proxy_unit_tests/test_aproxy_startup.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
- test-group: proxy-runtime
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_proxy_config_unit_test.py
|
|
tests/proxy_unit_tests/test_proxy_routes.py
|
|
tests/proxy_unit_tests/test_proxy_gunicorn.py
|
|
tests/proxy_unit_tests/test_server_root_path.py
|
|
tests/proxy_unit_tests/test_proxy_pass_user_config.py
|
|
tests/proxy_unit_tests/test_proxy_token_counter.py
|
|
tests/proxy_unit_tests/test_request_size_limit_middleware.py
|
|
tests/proxy_unit_tests/test_multipart_bypass_repro.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
|
|
# ---- logging: split into 2 shards ----
|
|
- test-group: custom-logging
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_custom_callback_input.py
|
|
tests/proxy_unit_tests/test_custom_logger_s3_gcs.py
|
|
tests/proxy_unit_tests/test_proxy_custom_logger.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
- test-group: logging-misc
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_proxy_reject_logging.py
|
|
tests/proxy_unit_tests/test_audit_logs_proxy.py
|
|
tests/proxy_unit_tests/test_search_api_logging.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
|
|
- test-group: db-and-spend
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_prisma_client_backoff_retry.py
|
|
tests/proxy_unit_tests/test_db_schema_changes.py
|
|
tests/proxy_unit_tests/test_e2e_pod_lock_manager.py
|
|
tests/proxy_unit_tests/test_skills_db.py
|
|
tests/proxy_unit_tests/test_update_daily_tag_spend.py
|
|
tests/proxy_unit_tests/test_update_spend.py
|
|
tests/proxy_unit_tests/test_proxy_encrypt_decrypt.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
|
|
# ---- guardrails + budget + hooks: split into 2 ----
|
|
- test-group: guardrails-hooks
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_proxy_setting_guardrails.py
|
|
tests/proxy_unit_tests/test_banned_keyword_list.py
|
|
tests/proxy_unit_tests/test_unit_test_proxy_hooks.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
- test-group: budgets
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_default_end_user_budget_simple.py
|
|
tests/proxy_unit_tests/test_unit_test_max_model_budget_limiter.py
|
|
tests/proxy_unit_tests/test_zero_cost_model_budget_bypass.py
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
|
|
- test-group: endpoints-and-responses
|
|
test-path: >-
|
|
tests/proxy_unit_tests/test_blog_posts_endpoint.py
|
|
tests/proxy_unit_tests/test_models_fallback_endpoint.py
|
|
tests/proxy_unit_tests/test_google_endpoint_routing.py
|
|
tests/proxy_unit_tests/test_google_gemini_proxy_request.py
|
|
tests/proxy_unit_tests/test_gemini_agents_endpoints.py
|
|
tests/proxy_unit_tests/test_get_favicon.py
|
|
tests/proxy_unit_tests/test_get_image.py
|
|
tests/proxy_unit_tests/test_reducto_ocr_route.py
|
|
tests/proxy_unit_tests/test_ui_path_detection.py
|
|
tests/proxy_unit_tests/test_prompt_test_endpoint.py
|
|
tests/proxy_unit_tests/test_check_batch_cost.py
|
|
tests/proxy_unit_tests/test_check_responses_cost.py
|
|
tests/proxy_unit_tests/test_response_polling_handler.py
|
|
tests/proxy_unit_tests/test_response_polling_pre_call_checks.py
|
|
tests/proxy_unit_tests/test_realtime_cache.py
|
|
tests/proxy_unit_tests/test_proxy_exception_mapping.py
|
|
tests/proxy_unit_tests/test_custom_tokenizer_bug.py
|
|
tests/proxy_unit_tests/test_model_response_typing
|
|
workers: 4
|
|
dist: loadscope
|
|
timeout: 15
|
|
uses: ./.github/workflows/_test-unit-base.yml
|
|
with:
|
|
test-path: ${{ matrix.test-path }}
|
|
workers: ${{ matrix.workers }}
|
|
reruns: 2
|
|
timeout-minutes: ${{ matrix.timeout }}
|
|
dist: ${{ matrix.dist }}
|
|
artifact-name: proxy-db-${{ matrix.test-group }}
|