mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
test(auth_v2): cover DbIdentityStore group CRUD; sync dashboard API types
Regression tests for upsert_group/get_group/list_groups/delete_group against a fake prisma that mirrors the one behavior they depend on (a Json-wrapped write round-trips as its plain value), so a future drop of the prisma Json wrapper or the typed read fails here rather than only against a live database. Regenerate ui/litellm-dashboard/src/lib/http/schema.d.ts from the proxy OpenAPI spec now that the /admin/teams routes are mounted.
This commit is contained in:
parent
8f401a8aaf
commit
86ac48701c
2 changed files with 274 additions and 0 deletions
|
|
@ -3,6 +3,9 @@ from __future__ import annotations
|
|||
from typing import Dict, Optional
|
||||
|
||||
import pytest
|
||||
from prisma import Json
|
||||
from scim2_models import Group as ScimGroup
|
||||
from scim2_models import GroupMember as ScimGroupMember
|
||||
|
||||
from litellm.proxy._types import LiteLLM_UserTable, UserAPIKeyAuth, hash_token
|
||||
from litellm.proxy.auth_v2.authorization import Role
|
||||
|
|
@ -153,3 +156,73 @@ async def test_mtls_credential_resolves_to_service_account():
|
|||
assert principal.principal_type == PrincipalType.SERVICE_ACCOUNT
|
||||
assert principal.user is None
|
||||
assert principal.subject == "CN=svc-a,O=Co"
|
||||
|
||||
|
||||
class _FakeTeamTable:
|
||||
"""Minimal stand-in for the prisma team table.
|
||||
|
||||
Mirrors the one prisma behavior the group methods depend on: a Json-wrapped
|
||||
write is stored as its plain Python value and read back the same way. Storing
|
||||
the raw value would let a regression that forgets the Json wrapper pass here
|
||||
while failing against a real database.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.rows: Dict[str, dict] = {}
|
||||
|
||||
@staticmethod
|
||||
def _norm(data: dict) -> dict:
|
||||
return {k: (v.data if isinstance(v, Json) else v) for k, v in data.items()}
|
||||
|
||||
async def find_unique(self, where):
|
||||
return self.rows.get(where["team_id"])
|
||||
|
||||
async def create(self, data):
|
||||
row = self._norm(data)
|
||||
self.rows[row["team_id"]] = row
|
||||
return row
|
||||
|
||||
async def update(self, where, data):
|
||||
self.rows[where["team_id"]].update(self._norm(data))
|
||||
return self.rows[where["team_id"]]
|
||||
|
||||
async def find_many(self, **kwargs):
|
||||
return list(self.rows.values())
|
||||
|
||||
async def delete(self, where):
|
||||
return self.rows.pop(where["team_id"], None)
|
||||
|
||||
|
||||
class _FakeTeamPrisma:
|
||||
def __init__(self) -> None:
|
||||
self.db = type("_Db", (), {"litellm_teamtable": _FakeTeamTable()})()
|
||||
|
||||
|
||||
async def test_group_upsert_round_trips_members_through_json():
|
||||
store = DbIdentityStore(_FakeTeamPrisma(), _FakeCache())
|
||||
created = await store.upsert_group(
|
||||
ScimGroup(
|
||||
display_name="eng",
|
||||
members=[ScimGroupMember(value="u-1"), ScimGroupMember(value="u-2")],
|
||||
)
|
||||
)
|
||||
|
||||
assert created.id is not None
|
||||
assert created.display_name == "eng"
|
||||
assert [m.value for m in created.members] == ["u-1", "u-2"]
|
||||
|
||||
fetched = await store.get_group(created.id)
|
||||
assert fetched is not None
|
||||
assert [m.value for m in fetched.members] == ["u-1", "u-2"]
|
||||
|
||||
|
||||
async def test_group_list_and_delete():
|
||||
store = DbIdentityStore(_FakeTeamPrisma(), _FakeCache())
|
||||
a = await store.upsert_group(ScimGroup(display_name="a"))
|
||||
await store.upsert_group(ScimGroup(display_name="b"))
|
||||
|
||||
assert {g.display_name for g in await store.list_groups(None)} == {"a", "b"}
|
||||
|
||||
await store.delete_group(a.id)
|
||||
assert await store.get_group(a.id) is None
|
||||
assert {g.display_name for g in await store.list_groups(None)} == {"b"}
|
||||
|
|
|
|||
201
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
201
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -416,6 +416,43 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/admin/teams": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** List Teams */
|
||||
get: operations["list_teams_admin_teams_get"];
|
||||
put?: never;
|
||||
/** Create Team */
|
||||
post: operations["create_team_admin_teams_post"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/admin/teams/{team_id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Get Team */
|
||||
get: operations["get_team_admin_teams__team_id__get"];
|
||||
/** Upsert Team */
|
||||
put: operations["upsert_team_admin_teams__team_id__put"];
|
||||
post?: never;
|
||||
/** Delete Team */
|
||||
delete: operations["delete_team_admin_teams__team_id__delete"];
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/agent/daily/activity": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -30580,6 +30617,22 @@ export interface components {
|
|||
/** Team Id */
|
||||
team_id: string;
|
||||
};
|
||||
/** TeamUpsert */
|
||||
TeamUpsert: {
|
||||
/** Members */
|
||||
members?: string[];
|
||||
/** Name */
|
||||
name: string;
|
||||
};
|
||||
/** TeamView */
|
||||
TeamView: {
|
||||
/** Id */
|
||||
id: string;
|
||||
/** Members */
|
||||
members: string[];
|
||||
/** Name */
|
||||
name: string;
|
||||
};
|
||||
/**
|
||||
* TestCustomCodeGuardrailRequest
|
||||
* @description Request model for testing custom code guardrails.
|
||||
|
|
@ -33107,6 +33160,154 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
list_teams_admin_teams_get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["TeamView"][];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
create_team_admin_teams_post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["TeamUpsert"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["TeamView"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_team_admin_teams__team_id__get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
team_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["TeamView"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
upsert_team_admin_teams__team_id__put: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
team_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["TeamUpsert"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["TeamView"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
delete_team_admin_teams__team_id__delete: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
team_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
204: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_agent_daily_activity_agent_daily_activity_get: {
|
||||
parameters: {
|
||||
query?: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue