litellm/tests/e2e/coverage_registry
yuneng-jiang a00d8a6ee1
fix(proxy): backport the 1.107.0 rc triage fixes to rc/1.107.0 (#47043)
* fix(proxy): give /team/delete transactions a 30s timeout (#46979)

Both interactive transactions in /team/delete used Prisma's 5s default.
A lock wait or a short database stall past 5s expired the transaction
after the keys were already deleted, so the call returned 503 and left
the team row, memberships and team models behind

(cherry picked from commit 6530b329f4)

* fix(sso): concurrent first sign-ins land on the winner's account, worker-kill test stops waiting on the reap (#46855)

* fix(sso): concurrent first sign-ins land on the account the winner created, worker-kill test pins half its streams to each worker

* test(integration): a worker kill returns once the worker is dead, the reap waits at block exit

uvicorn can take a full worker healthcheck to reap a worker that died mid-ping, so kill() held
the caller that long and peers held on the surviving worker hit their client timeout

* fix(sso): a sign-in that loses the first-insert race joins the default teams

The losing sign-in could read the winner's row before new_user added the default teams, so its
CLI key was minted with no team. The race tests now run the real new_user against a fake user table

(cherry picked from commit 29cd11bec6)

* test(e2e): fix MCP tool search hit matching and the retired Bedrock image model (#46998)

* test(e2e): compare parsed MCP tool search hit names instead of raw text

* test(e2e): move Bedrock image generation off the retired Nova Canvas model

(cherry picked from commit e9a8581a02)

* test(integration): fix seven CircleCI integration tests that race or assume a worker spread (#47001)

* test(integration): expect the tool search call replayed beside the function call for Anthropic targets

* test(integration): wait for the team spend flush before killing a worker

* test(integration): poll for the S3 attempt the batch sinks write concurrently

* test(integration): queue the terminal object and its sibling into the same s3_v2 flush

* test(integration): time the long Bedrock version against an equally long unversioned name

* test(integration): pin each half of a burst to one worker instead of hoping the kernel spreads it

* test(integration): assert parsed SSE events for live pings instead of transport chunk boundaries

(cherry picked from commit 910c30381b)

* test(ui): wait for the logout navigation before checking the session is gone (#47008)

(cherry picked from commit 53ef63145b)

* fix(ui): preserve key settings drafts during background refreshes (#41350)

* fix(ui): preserve key settings drafts during background refreshes

* fix(ui): refresh untouched key settings while preserving edits

* fix(ui): keep team-change MCP clears across key refreshes

clearMcpGrants now marks the MCP fields dirty, so a background refresh with
keepDirtyValues no longer restores the grants a team change just cleared.
Also adapts the draft tests to the onSubmit(values, dirtyFields) signature
on main and asserts a refresh does not mark untouched fields dirty, since the
team admin member-key path rejects saves with non-budget dirty fields.
KeyEditView becomes a one-line arrow to stay under the max-lines limit.

* fix(ui): keep MCP tool defaults out of a key's edited fields

MCPToolPermissions writes non-delete tool defaults for unrestricted servers
on mount. Marking that write dirty made a team admin's budget-only save on a
member key get rejected for editing mcp_tool_permissions. onChange now says
whether a write is a default or a user edit, and the key editor dirties only
edits.

The edit session is now keyed by token or token_id, matching the form's own
fallback, so switching between keys that only carry token_id starts fresh.

* fix(ui): keep a new MCP server's default tool limits across refreshes

Default tool writes for a server the user just added in this edit session
are part of that edit, so they are marked dirty and survive a background
refresh. Defaults written for an already saved server stay clean, so a team
admin's budget-only save on a member key is still accepted.

(cherry picked from commit 37b3a697c7)

* fix(logging): stop the spend metadata merge from walking a dict the logging thread writes to (#46044)

(cherry picked from commit aa32e4cc88)

* test(integration): expect a pre-token invalid prompt on a bridged stream to fall back (#47020)

#46493 reads the first stream chunk inside the router's retry and fallback
chain, so an invalid prompt rejected before any token now falls back to the
configured fallback deployment instead of answering 400. The author
confirmed this is intended

The fallback test now asserts the spare serves the stream. The old 400
without a fallback attempt still holds when fallback_on_unsupported_params
is false, so that contract moves to a second proxy with that setting

(cherry picked from commit 856a3f62f3)

* test(e2e): repair fallback management test for config-owned fallbacks (#47023)

* test(e2e): repair fallback round trip for config-owned fallbacks

The e2e stack config sets router_settings.fallbacks, and since #45860 a
/fallback write to a key the config file owns is refused with a 400. The
round trip now runs on context_window fallbacks, which the stack config
leaves to the database, and also deletes the rule and checks it is gone.

A second test locks the new contract: a /fallback write to the
config-owned fallbacks key returns 400 naming router_settings.fallbacks,
and the config rule keeps being served unchanged.

* test(e2e): assert the refused fallback rule is never served

(cherry picked from commit 59fa785de0)

* test(e2e): assert the Vertex cache-creation billing contract in the prompt caching test (#47027)

The cache-creating call now reports cache_creation_input_tokens on top of
Vertex's own prompt count and bills the write at the input rate, as Google
does. The old assertions assumed neither, so the test went red on every run.
Check the cache write, the discounted read, and that a second identical call
reuses the cache instead.

(cherry picked from commit 81a3802a92)

---------

Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-16 19:03:48 -07:00
..
__init__.py chore: consolidate CLAUDE.md into AGENTS.md 2026-09-19 02:30:35 +00:00
collector.py fix(e2e-changed): keep the gate off suites the stack cannot run 2026-09-05 21:03:50 -07:00
guardrail.yaml fix(guardrails): run unified guardrails on /v1/images/edits (#44195) 2026-10-02 21:14:35 -07:00
llm_claude_code_compat.yaml test(e2e): replay a real tool-search assistant turn back to Bedrock Invoke (#36856) 2026-08-17 11:59:26 -07:00
llm_conversational.yaml test: migrate local_testing SDK and provider tests out of build_and_test (#45760) 2026-10-10 23:14:25 +00:00
llm_nonconversational.yaml fix(bedrock): forward Nova Sonic realtime tool calls with their arguments (#46214) 2026-10-16 01:10:53 +00:00
logging.yaml fix(langfuse): log Responses API token usage instead of zeros (#42212) 2026-10-10 16:42:59 -07:00
management_cases.py test: enforce isolated actors and stop OIDC process groups 2026-09-12 13:49:49 -07:00
mcp.yaml fix(mcp): leave out tool texts too long to embed instead of failing tool search (#43554) 2026-10-14 14:53:00 -07:00
mgmt.yaml fix(proxy): backport the 1.107.0 rc triage fixes to rc/1.107.0 (#47043) 2026-10-16 19:03:48 -07:00
other.yaml fix(proxy): enforce the personal max_budget on proxy-admin JWTs (#43070) 2026-10-12 16:04:12 -07:00
quota_management.yaml test(e2e): cover persisted pricing spend (#41566) 2026-10-10 16:06:48 -07:00
README.md chore: consolidate CLAUDE.md into AGENTS.md 2026-09-19 02:30:35 +00:00
registry.py ci: gate tests/e2e on zero basedpyright errors in pre-commit and lint CI 2026-07-11 10:25:22 -07:00
reliability.yaml test(e2e): assert the sibling-replica cooldown through the router (#44706) 2026-10-06 02:12:57 +00:00
schema.py test: migrate local_testing SDK and provider tests out of build_and_test (#45760) 2026-10-10 23:14:25 +00:00

e2e coverage registry

This directory is the denominator for e2e test coverage: the set of behaviors we want covered, one row per behavior, checked into the repo so coverage is a number we can track instead of a guess. It implements the plan in the "E2E Coverage Tracking" note; the naming grammar lives in tests/e2e/AGENTS.md.

The model

A cell is one customer-noticeable behavior a single e2e test can assert pass/fail on, for example llm.chat_completions.bedrock_converse.tool_use.stream.works. Cells are grouped module > feature > test, with LLM cells split into Core LLMs and Non-Core LLMs for dashboarding. Each cell carries a tier (P0/P1/P2), a source, and a fail_before_fix flag.

The rows live in per-prefix YAML files (llm_*.yaml, mgmt.yaml, mcp.yaml, reliability.yaml, quota_management.yaml, logging.yaml, guardrail.yaml, other.yaml) and validate against the discriminated union in schema.py, so an LLM row cannot carry a guardrail field and vice versa. llm rows with subject_endpoint of chat_completions, messages, or responses roll up to Core LLMs; all other LLM endpoints roll up to Non-Core LLMs. LLM endpoint, route, and capability values are typed in schema.py, so new taxonomy values require an explicit schema change. logging and guardrail are two id-prefixes that roll up into the single Logging & Guardrails dashboard module.

A test declares what it covers with a marker:

@pytest.mark.covers("llm.chat_completions.openai.tool_use.stream.works")
def test_openai_streaming_tool_calls(self) -> None:
    ...

The number

collector.py diffs the registry against those markers and reports coverage per module. It is static: a collect-only pass reads the markers, so it runs no test and needs no live proxy. Whether a covered cell currently passes or fails is a separate, live concern.

A skipped test asserts nothing, so its markers do not count. A cell is covered only when at least one test pytest would actually run declares it; a cell claimed by both a live test and a skipped one stays covered. Skip state comes from pytest's own evaluator, so skip and skipif resolve exactly as they do in the e2e run, which also means a skipif on an absent credential makes that cell uncovered in the environments where the test cannot run. Cells left uncovered this way are listed under the headline (and counted by litellm_e2e_coverage_skipped_markers) so an unskipped-pending gap is visible rather than inflating the number. The one skip the collector cannot see is pytest.skip() called from inside a test body, since it does not exist until the test runs.

cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector

Use --format loki after the e2e pytest run in the same Kubernetes job/pod to print structured stdout lines for Loki:

cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector --format loki --strict

This emits exactly one COVERAGE_TOTAL line and one COVERAGE_MODULE line per module in MODULE_ORDER, in that order. Loki uses log-safe module= labels from LOKI_MODULE_LABELS (core_llms, management_ui, etc.) so existing JSON and Prometheus consumers keep their human-readable module names unchanged.

The headline is overall coverage. The collector also lists markers that point at ids not in the registry, so a typo or an unenumerated behavior surfaces instead of being silently dropped.

Use strict mode in CI once existing draft markers are reconciled:

cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector --strict

Strict mode exits non-zero on @pytest.mark.covers(...) ids that are not checked into the registry. Add --fail-on-collection-errors when the job should also fail on pytest collection errors.

Provider x feature matrix: customer-run Bedrock combinations

The provider and feature combinations customers actually run get explicit cells, expanded here as incidents surface new ones. The current Bedrock set, seeded from a customer's production shape (regional us.anthropic.* inference-profile ids over both chat routes, provider response headers for AWS-side correlation, and the Test Connection probe for a responses-mode Bedrock Mantle deployment):

Cell Feature Covering test
llm.chat_completions.bedrock_converse.basic.nonstream.works regional us. id, Converse llm_translation/test_chat_completions_regression_e2e.py
llm.chat_completions.bedrock_converse.basic.stream.works regional us. id, Converse stream llm_translation/test_chat_completions_regression_e2e.py
llm.chat_completions.bedrock_invoke.basic.nonstream.works regional us. id, Invoke llm_translation/test_bedrock_provider_matrix_e2e.py
llm.chat_completions.bedrock_invoke.basic.stream.works regional us. id, Invoke stream llm_translation/test_bedrock_provider_matrix_e2e.py
llm.chat_completions.bedrock_converse.response_headers.nonstream.works llm_provider-* headers llm_translation/test_bedrock_provider_matrix_e2e.py
llm.chat_completions.bedrock_converse.response_headers.stream.works llm_provider-* headers, stream llm_translation/test_bedrock_provider_matrix_e2e.py
mgmt.model.test_connection.happy_path Test Connection, Bedrock Mantle management/test_model_test_connection_e2e.py

Status: this is a draft for review

The cells were enumerated from the codebase and the tiers are a first proposal. Known things to settle before treating the set as final:

  • tiers are proposed, not signed off; 125 P0 is a lot to prove fail-before-fix, so P0 may want tightening
  • a few cells need a support check or a prune (for example llm.embeddings.anthropic.* and reliability.perf.throughput.under_slo)
  • auth is covered in two places (other.auth.* and the mgmt authz assertions); the boundary needs a decision, and the auth cluster may deserve promotion to its own module
  • the P2 "niche" cells each stand in for a large tail of integrations/providers by design, so the denominator is deliberately P0-weighted rather than a full inventory