diff --git a/tests/e2e/budgets/test_team_member_budget_e2e.py b/tests/e2e/budgets/test_team_member_budget_e2e.py index 32e8a0d2513..301617bfdca 100644 --- a/tests/e2e/budgets/test_team_member_budget_e2e.py +++ b/tests/e2e/budgets/test_team_member_budget_e2e.py @@ -21,6 +21,7 @@ import pytest from budget_client import BudgetClient, is_budget_block from e2e_config import unique_marker from e2e_http import Success, require_successful_call +from lifecycle import ResourceManager from models import ChatBody, ChatMessage pytestmark = pytest.mark.e2e @@ -41,16 +42,23 @@ class _Member: @pytest.fixture(scope="class") def member(client: BudgetClient) -> Iterator[_Member]: """A team with a large budget plus one member capped at a tiny per-team budget, - and that member's key. Shared across the class; torn down when it finishes.""" - marker = unique_marker() - team_id = client.create_team(alias=f"e2e-team-member-{marker}", max_budget=TEAM_BUDGET) - user_id = client.create_user(max_budget=TEAM_BUDGET) - client.add_team_member(team_id, user_id, max_budget_in_team=MEMBER_BUDGET) - key = client.generate_key(team_id=team_id, user_id=user_id) - yield _Member(team_id=team_id, user_id=user_id, key=key) - client.delete_key(key) - client.delete_user(user_id) - client.delete_team(team_id) + and that member's key. Shared across the class; torn down when it finishes. + Cleanups register progressively and run LIFO best-effort through ResourceManager, + so a partial-setup failure still releases what came before and one failed delete + never strands the rest on the shared proxy.""" + resources = ResourceManager(client=client.gateway) + try: + marker = unique_marker() + team_id = client.create_team(alias=f"e2e-team-member-{marker}", max_budget=TEAM_BUDGET) + resources.defer(lambda: client.delete_team(team_id)) + user_id = client.create_user(max_budget=TEAM_BUDGET) + resources.defer(lambda: client.delete_user(user_id)) + client.add_team_member(team_id, user_id, max_budget_in_team=MEMBER_BUDGET) + key = client.generate_key(team_id=team_id, user_id=user_id) + resources.defer(lambda: client.delete_key(key)) + yield _Member(team_id=team_id, user_id=user_id, key=key) + finally: + resources.teardown() def _send(client: BudgetClient, key: str) -> str | None: diff --git a/tests/e2e/budgets/test_team_multi_window_budget_e2e.py b/tests/e2e/budgets/test_team_multi_window_budget_e2e.py index 2d4fb860045..da580e18629 100644 --- a/tests/e2e/budgets/test_team_multi_window_budget_e2e.py +++ b/tests/e2e/budgets/test_team_multi_window_budget_e2e.py @@ -7,10 +7,11 @@ the tight window's cap is exceeded, then - once the 30s elapses and the reset jo and calls flow again. This exercises the reset_budget_windows TEAM branch (raw SQL over LiteLLM_TeamTable.budget_limits, the literal #25109 path), which had no live coverage. -Currently fails at team creation: /team/new writes the raw window list straight to the +Fails at team creation today: /team/new writes the raw window list straight to the Json? column, where Prisma rejects it (500), unlike the key path and /team/update which -json.dumps it first. Left failing rather than weakened - it passes once that write is -fixed. +json.dumps it first. Marked xfail(strict=True) so the suite stays green while the bug +persists and flips to a failure the moment the write is fixed and the marker should be +removed. """ import time @@ -32,6 +33,12 @@ def _call(client: BudgetClient, key: str): return client.chat(key, "claude-haiku-4-5", f"team-window {unique_marker()}", max_tokens=16) +@pytest.mark.xfail( + strict=True, + reason="known proxy bug: /team/new writes budget_limits straight to the Json? " + "column and Prisma rejects it (500), unlike the key path and /team/update which " + "json.dumps first; remove this marker once that write is fixed", +) def test_team_short_window_blocks_then_resets(client: BudgetClient, resources: ResourceManager) -> None: team_id = client.create_team( alias=f"e2e-team-window-{unique_marker()}",