litellm/tests/e2e/quota_management/budgets
mubashir1osmani 67643606ab
test(e2e): add reproducers for passthrough and model budget gaps (#34657)
* test(e2e): add failing reproducers for two open gateway bugs

Both tests assert the behavior a customer expects and both are red today. They
are reproducers, not regressions: the product is wrong, not the tests.

Native passthrough returns almost none of the operational headers the managed
route does. A /gemini/ generateContent call comes back with three x-litellm-*
headers and no x-ratelimit-* at all, against sixteen and four on
/v1beta/models/{m}:generateContent for the same prompt, and critically it omits
x-litellm-response-cost. Customers front provider-native traffic through this
route and read those headers to reconcile spend and pace themselves, so native
traffic is currently invisible to the tooling that covers every other route.

/budget/update rejects any model_max_budget with a 500. The reported symptom was
model ids containing dots, and that reproduces (prisma raises "Unexpected
`-5.2[FloatValue]` Expected `:`" because the key is interpolated into a GraphQL
query unquoted, so glm-5.2 lexes as an identifier followed by a float), but the
plain name gpt4o fails too, on a separate "model_max_budget should be of any of
the following types: Json" type mismatch at budget_management_endpoints.py:173.
Omitting the field returns 200. The test drives both names so the failure says
whether per-model budgets are broken outright or only for punctuated ids; today
it stops on the plain name, which is the wider bug.

* test(e2e): add reproducer for unenforced end-user per-model rate limits

model_max_budget accepts an rpm_limit alongside the spend cap, and /budget/new
stores it: the create response echoes {"gemini-2.5-flash": {"rpm_limit": 1,
"max_budget": 100.0, "budget_duration": "1d"}}. Attach that budget to an end
user, drive three calls as that user, and all three return 200. The limit is
accepted, persisted, and then ignored.

The same shape already works when the budget hangs off a key, which is what
makes this quietly dangerous: the API gives every indication the cap is in
force. A customer using it to hold one end user to a slow rate on a shared key
gets no throttling at all.

Harness additions this needs: ModelBudgetEntry carries the rpm_limit/tpm_limit
the route already accepts, BudgetNewBody and create_budget carry
model_max_budget, and create_customer can attach an existing budget_id rather
than only an inline max_budget.

Red today, for the reason in the assertion message.

* test(e2e): tighten model_max_budget reproducers and drop in-loop closure

Trim the reproducer docstrings to the contract they assert, keeping the
failure messages that document each red-by-design bug. Replace the nested
per-model closure in the /budget/update test with a module-level predicate
and a per-model helper so nothing closes over a loop variable, and fix the
import order the merge left unsorted.

* test(e2e): skip the three reproducers while their gateway bugs stay open

The passthrough header contract, /budget/update model_max_budget, and
end-user per-model rpm enforcement reproducers all still fail against
staging by design. Skip each with the product gap named so the combined
suite can gate merges on green while the collector keeps reporting the
cells as uncovered.

* test(e2e): validate model budget response contracts

* refactor(e2e): unify model budget schema

* refactor(e2e): reuse shared model budget type
2026-08-11 18:15:34 -07:00
..
budget_client.py test(e2e): add reproducers for passthrough and model budget gaps (#34657) 2026-08-11 18:15:34 -07:00
BUDGET_CODE_MATRIX.md refactor(e2e): move budgets and spend_tracking suites under quota_management 2026-07-11 16:14:21 -07:00
BUDGET_TEST_COVERAGE_MATRIX.md refactor(e2e): move budgets and spend_tracking suites under quota_management 2026-07-11 16:14:21 -07:00
conftest.py test(e2e): rename Gateway to ProxyClient and expose it as a session-scoped fixture (#33750) 2026-07-18 18:41:18 +00:00
test_budget_crud_e2e.py test(e2e): rename Gateway to ProxyClient and expose it as a session-scoped fixture (#33750) 2026-07-18 18:41:18 +00:00
test_budget_enforcement_e2e.py test(e2e): align budget e2e with the team-key budget hierarchy (#35276) 2026-07-30 21:09:50 +00:00
test_budget_fallback_e2e.py test(e2e): rename Gateway to ProxyClient and expose it as a session-scoped fixture (#33750) 2026-07-18 18:41:18 +00:00
test_budget_reset_advances_e2e.py test(e2e): poll key spend to a deadline in budget reset advances tests 2026-08-01 19:29:56 -07:00
test_budget_reset_e2e.py test(e2e): align budget e2e with the team-key budget hierarchy (#35276) 2026-07-30 21:09:50 +00:00
test_model_max_budget_e2e.py test(e2e): add reproducers for passthrough and model budget gaps (#34657) 2026-08-11 18:15:34 -07:00
test_multi_window_budget_e2e.py test(e2e): assert the long budget window keeps blocking after the short window resets (#33832) 2026-07-18 18:00:15 -07:00
test_soft_budget_e2e.py refactor(e2e): move budgets and spend_tracking suites under quota_management 2026-07-11 16:14:21 -07:00
test_spend_counter_reseed_e2e.py fix(e2e): make spend-counter redis connection env-driven for non-cluster deployments (#35732) 2026-08-04 09:47:01 -07:00
test_tag_budget_e2e.py refactor(e2e): move budgets and spend_tracking suites under quota_management 2026-07-11 16:14:21 -07:00
test_team_member_budget_e2e.py test(e2e): rename Gateway to ProxyClient and expose it as a session-scoped fixture (#33750) 2026-07-18 18:41:18 +00:00
test_team_member_budget_isolation_e2e.py test(e2e): rename Gateway to ProxyClient and expose it as a session-scoped fixture (#33750) 2026-07-18 18:41:18 +00:00
test_team_member_budget_reset_e2e.py refactor(e2e): move budgets and spend_tracking suites under quota_management 2026-07-11 16:14:21 -07:00
test_team_multi_window_budget_e2e.py test(e2e): assert the long budget window keeps blocking after the short window resets (#33832) 2026-07-18 18:00:15 -07:00
test_user_budget_across_keys_e2e.py test(e2e): user budget across keys and team member budget isolation (#33745) 2026-07-17 14:22:32 -07:00