mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(ui): keep team usage available to internal users
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
d178e58c99
commit
7dde432e7b
3 changed files with 18 additions and 17 deletions
|
|
@ -14,6 +14,8 @@ import type { Organization } from "@/components/networking";
|
|||
import * as networking from "@/components/networking";
|
||||
import UsagePage from "./UsagePageView";
|
||||
|
||||
const mockEntityUsageMounted = vi.hoisted(() => vi.fn());
|
||||
|
||||
// Polyfill ResizeObserver for test environment
|
||||
beforeAll(() => {
|
||||
if (typeof window !== "undefined" && !window.ResizeObserver) {
|
||||
|
|
@ -29,7 +31,6 @@ beforeAll(() => {
|
|||
vi.mock("@/components/networking", () => ({
|
||||
userDailyActivityCall: vi.fn(),
|
||||
userDailyActivityAggregatedCall: vi.fn(),
|
||||
teamDailyActivityAggregatedCall: vi.fn(),
|
||||
gatewayDailyActivityCall: vi.fn(),
|
||||
tagListCall: vi.fn(),
|
||||
}));
|
||||
|
|
@ -52,7 +53,6 @@ vi.mock("@/components/UsagePage/components/EntityUsage/TopKeyView", () => ({
|
|||
|
||||
vi.mock("./EntityUsage/EntityUsage", async () => {
|
||||
const React = await import("react");
|
||||
const { teamDailyActivityAggregatedCall } = await import("@/components/networking");
|
||||
const EntityUsage = ({
|
||||
entityType,
|
||||
entityList,
|
||||
|
|
@ -65,9 +65,7 @@ vi.mock("./EntityUsage/EntityUsage", async () => {
|
|||
onSelectedEntitiesChange: (ids: readonly string[]) => void;
|
||||
}) => {
|
||||
React.useEffect(() => {
|
||||
if (entityType === "team") {
|
||||
void teamDailyActivityAggregatedCall("test-token", new Date(), new Date(), ["team-1"]);
|
||||
}
|
||||
mockEntityUsageMounted(entityType);
|
||||
}, [entityType]);
|
||||
return (
|
||||
<div
|
||||
|
|
@ -199,7 +197,6 @@ vi.mock("@/app/(dashboard)/hooks/users/useUsers", () => ({
|
|||
describe("UsagePage", () => {
|
||||
const mockUserDailyActivityAggregatedCall = vi.mocked(networking.userDailyActivityAggregatedCall);
|
||||
const mockUserDailyActivityCall = vi.mocked(networking.userDailyActivityCall);
|
||||
const mockTeamDailyActivityAggregatedCall = vi.mocked(networking.teamDailyActivityAggregatedCall);
|
||||
const mockTagListCall = vi.mocked(networking.tagListCall);
|
||||
const mockGatewayDailyActivityCall = vi.mocked(networking.gatewayDailyActivityCall);
|
||||
const mockUseCustomers = vi.mocked(useCustomers);
|
||||
|
|
@ -357,8 +354,10 @@ describe("UsagePage", () => {
|
|||
userRole: "Internal User",
|
||||
userRoleLabel: "Internal User",
|
||||
isViewOnly: false,
|
||||
loginMethod: null,
|
||||
premiumUser: true,
|
||||
disabledPersonalKeyCreation: false,
|
||||
passwordResetRequired: false,
|
||||
showSSOBanner: false,
|
||||
};
|
||||
|
||||
|
|
@ -416,7 +415,7 @@ describe("UsagePage", () => {
|
|||
} as any);
|
||||
mockUserDailyActivityAggregatedCall.mockClear();
|
||||
mockUserDailyActivityCall.mockClear();
|
||||
mockTeamDailyActivityAggregatedCall.mockClear();
|
||||
mockEntityUsageMounted.mockClear();
|
||||
mockTagListCall.mockClear();
|
||||
mockGatewayDailyActivityCall.mockClear();
|
||||
mockUserDailyActivityAggregatedCall.mockResolvedValue(mockSpendData);
|
||||
|
|
@ -842,8 +841,14 @@ describe("UsagePage", () => {
|
|||
expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const usageSelect = screen.getByTestId("usage-view-select");
|
||||
act(() => {
|
||||
fireEvent.change(screen.getByTestId("usage-view-select"), { target: { value: usageView } });
|
||||
fireEvent.change(usageSelect, { target: { value: "team" } });
|
||||
});
|
||||
expect(screen.getAllByText("Entity Usage").length).toBeGreaterThan(0);
|
||||
|
||||
act(() => {
|
||||
fireEvent.change(usageSelect, { target: { value: usageView } });
|
||||
});
|
||||
|
||||
expect(screen.queryByText("Entity Usage")).not.toBeInTheDocument();
|
||||
|
|
@ -1512,16 +1517,16 @@ describe("UsagePage", () => {
|
|||
expect(screen.queryByTestId("entity-usage")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render or fetch a disallowed team view for an internal user", async () => {
|
||||
it("does not render a disallowed customer view for an internal user", async () => {
|
||||
mockUseAuthorized.mockReturnValue(nonAdminSession);
|
||||
|
||||
renderAt("?view=team&team=team-1");
|
||||
renderAt("?view=customer&customer=c-1");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled();
|
||||
});
|
||||
expect(mockEntityUsageMounted).not.toHaveBeenCalledWith("customer");
|
||||
expect(screen.queryByTestId("entity-usage")).not.toBeInTheDocument();
|
||||
expect(mockTeamDailyActivityAggregatedCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("never fetches another user's usage for a non-admin who opens their link", async () => {
|
||||
|
|
|
|||
|
|
@ -96,16 +96,13 @@ describe("UsageViewSelect", () => {
|
|||
expect(offers(container, optionName)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["Team Usage", false],
|
||||
["Tag Usage", true],
|
||||
] as const)("should offer %s to an internal user: %s", async (optionName, expected) => {
|
||||
it.each(["Team Usage", "Tag Usage"])("should keep %s available to an internal user", async (optionName) => {
|
||||
const user = userEvent.setup();
|
||||
const { container } = render(
|
||||
<UsageViewSelect value="global" onChange={mockOnChange} userRole="Internal User" canViewTagUsage={true} />,
|
||||
);
|
||||
|
||||
await openMenu(user);
|
||||
expect(offers(container, optionName)).toBe(expected);
|
||||
expect(offers(container, optionName)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ const OPTIONS: OptionConfig[] = [
|
|||
label: "Team Usage",
|
||||
description: "View usage by team",
|
||||
icon: <Users className="size-4" />,
|
||||
adminOnly: true,
|
||||
},
|
||||
{
|
||||
value: "customer",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue