mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-28 01:32:17 +00:00
test(proxy_behavior): pin /key/list default-visibility matrix (8 scenarios)
Slice 9 of the management-endpoints behavior-pinning effort. For /key/list
the response IS the matrix: each of the 8 seeded actors calls the endpoint
with default filters and the test asserts set-equality between the returned
visible-token set (filtered to seeded tokens only, so unrelated rows can't
flap the assertion) and a pinned expected actor-set.
Pinned default visibility:
* PROXY_ADMIN sees all 8 actors' keys.
* Every other actor sees only their own key — including ORG_ADMIN
(which had broader expectations going in but currently behaves
same-as-internal-user for /key/list defaults) and TEAM_ADMIN (no
team-aggregation without include_team_keys=true).
Future changes that broaden or narrow any single actor's default
visibility will turn this matrix red — exactly the regression signal we
want. Parameter-driven views (include_team_keys, filters) are deferred to
Slice 13 / PR2 follow-up.
Plan: https://www.notion.so/36643b8acdab8128a581ced0f6a4744d
This commit is contained in:
parent
f5bd477292
commit
1afe43810b
1 changed files with 84 additions and 0 deletions
84
tests/proxy_behavior/management/test_key_list.py
Normal file
84
tests/proxy_behavior/management/test_key_list.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"""Slice 9 — actor visibility matrix for ``GET /key/list``.
|
||||
|
||||
For ``/key/list`` the *response itself* is the matrix: each actor calls the
|
||||
endpoint with default filters and we assert which seeded actor keys end up in
|
||||
the returned set. The set-equality assertion is filtered to seeded tokens
|
||||
only, so unrelated rows in the DB (other tests, leftover dev data) can't flap
|
||||
the matrix.
|
||||
|
||||
8 scenarios — one per actor. Expected visibility is pinned against the
|
||||
current handler so future changes to the filter logic surface red.
|
||||
"""
|
||||
|
||||
from typing import FrozenSet
|
||||
|
||||
import pytest
|
||||
|
||||
from .actors import Actor
|
||||
|
||||
pytestmark = pytest.mark.asyncio(loop_scope="session")
|
||||
|
||||
|
||||
# Maps each actor → the set of seeded actors whose keys it is permitted to see
|
||||
# under default ``/key/list`` (no filter params, page=1, size=10).
|
||||
_VISIBILITY = {
|
||||
Actor.PROXY_ADMIN: frozenset(Actor),
|
||||
Actor.ORG_ADMIN: frozenset({Actor.ORG_ADMIN}),
|
||||
Actor.TEAM_ADMIN: frozenset({Actor.TEAM_ADMIN}),
|
||||
Actor.INTERNAL_USER: frozenset({Actor.INTERNAL_USER}),
|
||||
Actor.OWNER: frozenset({Actor.OWNER}),
|
||||
Actor.UNRELATED_SAME_ORG: frozenset({Actor.UNRELATED_SAME_ORG}),
|
||||
Actor.CROSS_ORG_USER: frozenset({Actor.CROSS_ORG_USER}),
|
||||
Actor.SERVICE_ACCOUNT: frozenset({Actor.SERVICE_ACCOUNT}),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"actor,expected_visible",
|
||||
list(_VISIBILITY.items()),
|
||||
ids=[a.value for a in _VISIBILITY],
|
||||
)
|
||||
async def test_key_list_visibility(
|
||||
actor: Actor,
|
||||
expected_visible: FrozenSet[Actor],
|
||||
proxy_client,
|
||||
world,
|
||||
):
|
||||
caller = world.keys[actor]
|
||||
seeded_hashes = {a: world.keys[a].hashed for a in Actor}
|
||||
hashed_to_actor = {h: a for a, h in seeded_hashes.items()}
|
||||
|
||||
# Use size=100 to ensure we see all 8 seeded keys for proxy_admin.
|
||||
resp = await proxy_client.get(
|
||||
"/key/list?size=100",
|
||||
headers={"Authorization": f"Bearer {caller.cleartext}"},
|
||||
)
|
||||
assert (
|
||||
resp.status_code == 200
|
||||
), f"{actor.value} GET /key/list → {resp.status_code}: {resp.text}"
|
||||
|
||||
body = resp.json()
|
||||
returned = body.get("keys", [])
|
||||
|
||||
# /key/list can return either token strings (default) or full objects.
|
||||
# Default: list of dicts with ``token`` key. Reduce to hashes.
|
||||
returned_hashes = set()
|
||||
for entry in returned:
|
||||
if isinstance(entry, dict):
|
||||
tok = entry.get("token")
|
||||
else:
|
||||
tok = entry
|
||||
if tok:
|
||||
returned_hashes.add(tok)
|
||||
|
||||
visible_seeded = {
|
||||
hashed_to_actor[h] for h in returned_hashes if h in hashed_to_actor
|
||||
}
|
||||
expected = set(expected_visible)
|
||||
assert visible_seeded == expected, (
|
||||
f"{actor.value} /key/list visibility differs:\n"
|
||||
f" expected: {sorted(a.value for a in expected)}\n"
|
||||
f" actual: {sorted(a.value for a in visible_seeded)}\n"
|
||||
f" diff (missing): {sorted(a.value for a in (expected - visible_seeded))}\n"
|
||||
f" diff (extra): {sorted(a.value for a in (visible_seeded - expected))}"
|
||||
)
|
||||
Loading…
Add table
Reference in a new issue