test(proxy): cover fail-closed team budget without membership

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Devin AI 2026-09-29 17:50:05 +00:00
parent 0fe4028cd9
commit 200224e5d6
3 changed files with 37 additions and 0 deletions

View file

@ -21,6 +21,7 @@
- {id: quota_management.budget.organization.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: organization, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "An organization's max_budget blocks keys under its teams"}
- {id: quota_management.budget.team_member.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: team_member, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "A member's per-team budget blocks independently of the team budget"}
- {id: quota_management.budget.team_member.isolates_per_member, module: quota_management, tier: P1, behavior: budget, variant: team_member, assertions: [isolates_per_member], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "One team member's exhausted per-team budget does not block a different member on the same team"}
- {id: quota_management.budget.team_member.missing_membership_counts_as_verified_zero_spend, module: quota_management, tier: P1, behavior: budget, variant: team_member, assertions: [missing_membership_counts_as_verified_zero_spend], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py + proxy_server.py:get_current_spend", rationale: "A key scoped to a team with a default member budget but no membership row treats the absent row as verified zero spend and admits the request"}
- {id: quota_management.budget.model_access_group.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: model_access_group, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "A model access group's shared max_budget blocks further calls to deployments in the group once the pool is spent"}
- {id: quota_management.budget.model_access_group.enforced_across_keys, module: quota_management, tier: P1, behavior: budget, variant: model_access_group, assertions: [enforced_across_keys], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "The pool is shared, so a key that spent nothing of its own is blocked once another key granted the same group drained it"}
- {id: quota_management.budget.model_access_group.isolates_per_group, module: quota_management, tier: P1, behavior: budget, variant: model_access_group, assertions: [isolates_per_group], exercised_on: [chat_completions], source: "proxy/db/db_spend_update_writer.py", rationale: "A request is charged only to the granted groups that serve the model it called, so an exhausted group never blocks a sibling group"}

View file

@ -97,6 +97,7 @@ class TeamNewBody(BaseModel):
max_budget: float | None = None
budget_duration: str | None = None
organization_id: str | None = None
team_member_budget: float | None = None
budget_limits: list[BudgetWindow] | None = None
@ -416,6 +417,7 @@ class BudgetClient:
max_budget: float | None = None,
budget_duration: str | None = None,
organization_id: str | None = None,
team_member_budget: float | None = None,
budget_limits: list[BudgetWindow] | None = None,
) -> str:
team_id = unwrap(
@ -427,6 +429,7 @@ class BudgetClient:
max_budget=max_budget,
budget_duration=budget_duration,
organization_id=organization_id,
team_member_budget=team_member_budget,
budget_limits=budget_limits,
),
response_type=TeamNewResponse,

View file

@ -61,6 +61,25 @@ def member(client: BudgetClient) -> Iterator[_Member]:
resources.teardown()
@pytest.fixture(scope="class")
def member_without_membership(client: BudgetClient) -> Iterator[_Member]:
resources = ResourceManager(client=client.proxy)
try:
marker = unique_marker()
team_id = client.create_team(
alias=f"e2e-team-member-no-membership-{marker}",
team_member_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))
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:
"""One member call; its response id (== the spend-log request_id) if it went
through, else None."""
@ -106,3 +125,17 @@ class TestTeamMemberBudget:
require_successful_call(result)
time.sleep(2)
pytest.fail("per-member budget never enforced within the call budget")
class TestFailClosedTeamMemberBudgetWithoutMembership:
@pytest.mark.covers("quota_management.budget.team_member.missing_membership_counts_as_verified_zero_spend")
def test_missing_membership_counts_as_verified_zero_spend(
self, client: BudgetClient, member_without_membership: _Member
) -> None:
result = client.chat(
member_without_membership.key,
MODEL,
f"missing membership {unique_marker()}",
max_tokens=16,
)
assert result.status_code == 200, result.body