test(e2e): budget refusals are 429 for bare keys and team caps block every team key - #33632
Conversation
Greptile SummaryThis PR adds two e2e test improvements for budget enforcement: it strengthens the existing
Confidence Score: 5/5Safe to merge — only test files are changed, no production code is touched. The change is entirely confined to the e2e test suite and its coverage registry. Both refusals in TeamBudgetCase now check status_code == 429 explicitly, the sibling assertion combines is_budget_block with the status-code guard, and the modification to _assert_budget_blocks is additive. No existing assertions are weakened. No files require special attention.
|
| Filename | Overview |
|---|---|
| tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py | Strengthens KeyBudgetCase to assert 429 on refusal; adds TeamBudgetCase proving team-wide cap blocks all member keys with 429 budget_exceeded. Both the primary key and the sibling key have full status-code assertions. |
| tests/e2e/coverage_registry/quota_management.yaml | New P0 registry row for quota_management.budget.team.blocks_over_limit inserted after the key row; rationale and source are accurate. |
| tests/e2e/CLAUDE.md | Grammar doc updated to include team in the budget variant vocab; consistent with the new registry row and test. |
Reviews (4): Last reviewed commit: "test(e2e): focus the team budget case on..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@greptileai re review |
|
@greptileai re review |
|
@greptileai re review |
e5a9f3f
into
litellm_internal_staging
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Both proofs run against a live proxy (the docker compose stack in tests/e2e) with real paid llama-3.3-70b-versatile calls through the
claude-haiku-4-5model group.Bare-key case, captured at 2962170. Mint a key with a tiny max_budget and no team_id or user_id, then drive /v1/chat/completions until refused. Call 1 succeeds, call 2 onward is a 429 budget_exceeded, never a 500:
Team case, captured at 3c74580. A team with max_budget 3e-06 and two keys under it, neither with a key-level budget. Key A's first call succeeds and burns through the team cap; key A's second call and key B's very first call are both refused with the same team 429 budget_exceeded:
All 6 enforcement cases pass against the same live proxy at d097d98 (6 passed in 15.33s),
python -m coverage_registry.collector --strictaccepts the new registry row, andbasedpyright tests/e2ereports 0 errorsType
✅ Test
Changes
Two strengthenings of
tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py, both about the blocking behavior a customer sees when a budget runs out.First, the bare-key case (
quota_management.budget.key.blocks_over_limit): the existingKeyBudgetCaseproved a block happens but never checked its shape. It now asserts the refusal is HTTP 429 with error typebudget_exceeded; anything else, including a 500, fails the test. To support that,_assert_budget_blocksreturns the blocked response instead of discarding it, so any entity case can assert on the refusal's shape; the sibling cases are behaviorally unchanged.Second, a new
TeamBudgetCasecovering the previously missing plain teammax_budget(new registry rowquota_management.budget.team.blocks_over_limit, plusteamadded to the variant vocab in tests/e2e/CLAUDE.md). Prior coverage existed only in tests/otel_tests/test_e2e_budgeting.py with a single key against a mock model; nothing anywhere asserted the team-wide property. The new case creates a team with a tiny budget and two keys with no key-level budgets, drives key A until the team cap blocks it, then asserts key B's very first call is also refused; both refusals must be 429 budget_exceeded. The cap provably sits on the team, not on the key that spent. The case is deliberately focused on blocking behavior; spend-ledger read-backs belong to the spend_tracking rows, not hereQA runbook
tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py::test_budget_enforcement[KeyBudgetCase] - a bare key (no team_id, no user_id) carrying its own tiny max_budget is refused with a 429 budget_exceeded once spend crosses the cap (needs ANTHROPIC_API_KEY for the claude-haiku-4-5 model in the compose config)
tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py::test_budget_enforcement[TeamBudgetCase] - a team-wide max_budget blocks every key on the team once one member burns through it (needs ANTHROPIC_API_KEY for the claude-haiku-4-5 model in the compose config)
Final Attestation