mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
test(e2e): cover tag creation persisting for spend categorization (#34018)
This commit is contained in:
parent
51df801159
commit
72be5a9bc0
3 changed files with 80 additions and 1 deletions
|
|
@ -26,6 +26,10 @@ from models import (
|
|||
OrgInfoResponse,
|
||||
OrgNewBody,
|
||||
OrgNewResponse,
|
||||
TagDeleteBody,
|
||||
TagListEntry,
|
||||
TagListResponse,
|
||||
TagNewBody,
|
||||
TeamData,
|
||||
TeamDeleteBody,
|
||||
TeamInfoParams,
|
||||
|
|
@ -259,6 +263,36 @@ class ManagementClient:
|
|||
)
|
||||
)
|
||||
|
||||
def create_tag(self, body: TagNewBody) -> None:
|
||||
_ = unwrap(
|
||||
self.proxy.transport.post(
|
||||
"/tag/new",
|
||||
headers=self.proxy.transport.master,
|
||||
json=body,
|
||||
response_type=NoBody,
|
||||
)
|
||||
)
|
||||
|
||||
def delete_tag(self, name: str) -> None:
|
||||
_ = self.proxy.transport.post(
|
||||
"/tag/delete",
|
||||
headers=self.proxy.transport.master,
|
||||
json=TagDeleteBody(name=name),
|
||||
response_type=NoBody,
|
||||
)
|
||||
|
||||
def tag_list(self) -> tuple[TagListEntry, ...]:
|
||||
return tuple(
|
||||
unwrap(
|
||||
self.proxy.transport.get(
|
||||
"/tag/list",
|
||||
headers=self.proxy.transport.master,
|
||||
params=NoBody(),
|
||||
response_type=TagListResponse,
|
||||
)
|
||||
).root
|
||||
)
|
||||
|
||||
def chat_status(self, key: str, model: str, content: str) -> StreamingResponse:
|
||||
return self.proxy.transport.send(
|
||||
"/chat/completions",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from management_client import (
|
|||
ROUTE_NOT_ALLOWED_MARKER,
|
||||
ManagementClient,
|
||||
)
|
||||
from models import KeyGenerateBody, OrgNewBody, TeamNewBody, UserNewBody
|
||||
from models import KeyGenerateBody, OrgNewBody, TagListEntry, TagNewBody, TeamNewBody, UserNewBody
|
||||
|
||||
pytestmark = pytest.mark.e2e
|
||||
|
||||
|
|
@ -271,6 +271,28 @@ class TestOrganizationRoutes:
|
|||
)
|
||||
|
||||
|
||||
class TestTagRoutes:
|
||||
@pytest.mark.covers("mgmt.tag.new.happy_path")
|
||||
def test_new_persists_to_tag_list(self, client: ManagementClient, resources: ResourceManager) -> None:
|
||||
name = f"e2e-mgmt-tag-{unique_marker()}"
|
||||
description = "Tag for spend categorization"
|
||||
|
||||
assert all(entry.name != name for entry in client.tag_list()), (
|
||||
f"tag {name!r} was already listed by /tag/list before /tag/new created it"
|
||||
)
|
||||
|
||||
client.create_tag(TagNewBody(name=name, description=description))
|
||||
resources.defer(lambda: client.delete_tag(name))
|
||||
|
||||
def listed() -> TagListEntry | None:
|
||||
return next((entry for entry in client.tag_list() if entry.name == name), None)
|
||||
|
||||
entry = _poll(client, listed, f"/tag/list never listed {name!r} after /tag/new")
|
||||
assert entry.description == description, (
|
||||
f"/tag/list reports description {entry.description!r} for {name!r}, configured {description!r}"
|
||||
)
|
||||
|
||||
|
||||
def _assert_route_forbidden(route: str, outcome: StreamingResponse) -> None:
|
||||
assert outcome.status_code == 403, (
|
||||
f"llm-only key POSTing {route} must be denied exactly 403, got {outcome.status_code}: {outcome.body[:300]}"
|
||||
|
|
|
|||
|
|
@ -699,3 +699,26 @@ class OrgInfoResponse(BaseModel):
|
|||
|
||||
class OrgDeleteBody(BaseModel):
|
||||
organization_ids: list[str]
|
||||
|
||||
|
||||
# ---------- tags (management) ----------
|
||||
|
||||
|
||||
class TagNewBody(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class TagDeleteBody(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
class TagListEntry(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class TagListResponse(RootModel[list[TagListEntry]]):
|
||||
"""GET /tag/list answers with a bare array of tag configs (the stored tags plus
|
||||
any dynamically-seen spend tags), not an object wrapping them. Read the rows off
|
||||
.root."""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue