litellm/ui/litellm-dashboard/tests/top_key_view.test.tsx
yuneng-jiang 0635b1cbf0 rename
2025-12-11 16:33:23 -08:00

310 lines
11 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderWithProviders, screen, fireEvent } from "./test-utils";
import TopKeyView from "../src/components/UsagePage/components/EntityUsage/TopKeyView";
import { TagUsage } from "../src/components/UsagePage/types";
import useAuthorized from "../src/app/(dashboard)/hooks/useAuthorized";
// Mock the networking module
vi.mock("../src/components/networking", () => ({
keyInfoV1Call: vi.fn(),
}));
// Mock the transform function
vi.mock("../src/components/key_team_helpers/transform_key_info", () => ({
transformKeyInfo: vi.fn((data) => data),
}));
vi.mock("../src/app/(dashboard)/hooks/useAuthorized", () => ({
default: vi.fn(),
}));
describe("TopKeyView", () => {
const mockUseAuthorized = vi.mocked(useAuthorized);
const mockProps = {
topKeys: [],
accessToken: "test-token",
userID: "test-user",
userRole: "admin",
teams: null,
premiumUser: true,
showTags: false,
};
const mockKeysWithTags = [
{
api_key: "key-1",
key_alias: "Production Key",
tags: [
{ tag: "production", usage: 0.005 } as TagUsage, // <$0.01
{ tag: "high-volume", usage: 125.5 } as TagUsage, // High spend
{ tag: "api-calls", usage: 0.003 } as TagUsage, // <$0.01
],
spend: 125.5,
},
{
api_key: "key-2",
key_alias: "Staging Key",
tags: [
{ tag: "staging", usage: 45.75 } as TagUsage, // Medium spend
{ tag: "testing", usage: 0.008 } as TagUsage, // <$0.01
{ tag: "development", usage: 12.25 } as TagUsage, // Low spend
],
spend: 58.0,
},
{
api_key: "key-3",
key_alias: "Development Key",
tags: [
{ tag: "dev", usage: 0.002 } as TagUsage, // <$0.01
{ tag: "experimental", usage: 0.001 } as TagUsage, // <$0.01
],
spend: 0.003,
},
];
beforeEach(() => {
vi.clearAllMocks();
mockUseAuthorized.mockReturnValue({
token: "mock-token",
accessToken: mockProps.accessToken,
userId: mockProps.userID,
userEmail: "test@example.com",
userRole: mockProps.userRole,
premiumUser: mockProps.premiumUser,
disabledPersonalKeyCreation: false,
showSSOBanner: false,
});
});
describe("Tags Column Visibility", () => {
it("should not show tags column when showTags is false", () => {
renderWithProviders(<TopKeyView {...mockProps} showTags={false} />);
expect(screen.queryByText("Tags")).not.toBeInTheDocument();
});
it("should show tags column when showTags is true", () => {
renderWithProviders(<TopKeyView {...mockProps} showTags={true} />);
expect(screen.getByText("Tags")).toBeInTheDocument();
});
});
describe("Tags Display and Sorting", () => {
beforeEach(() => {
renderWithProviders(<TopKeyView {...mockProps} topKeys={mockKeysWithTags} showTags={true} />);
});
it("should display tags for each key", () => {
// Check that tags are displayed (truncated to 7 chars + ...)
expect(screen.getByText("product...")).toBeInTheDocument();
expect(screen.getByText("high-vo...")).toBeInTheDocument();
expect(screen.getByText("staging...")).toBeInTheDocument();
});
it("should display top 2 tags by default (sorted by spend)", () => {
// Only the top 2 tags by spend should be visible initially
// Production Key: high-volume (125.50), production (0.005) - sorted by spend
expect(screen.getByText("high-vo...")).toBeInTheDocument();
expect(screen.getByText("product...")).toBeInTheDocument();
// Staging Key: staging (45.75), development (12.25) - sorted by spend
expect(screen.getByText("staging...")).toBeInTheDocument();
expect(screen.getByText("develop...")).toBeInTheDocument();
// Development Key: dev (0.002), experimental (0.001) - sorted by spend
expect(screen.getByText("dev...")).toBeInTheDocument();
expect(screen.getByText("experim...")).toBeInTheDocument();
// These should NOT be visible initially (3rd+ tags)
expect(screen.queryByText("api-cal...")).not.toBeInTheDocument();
expect(screen.queryByText("testing...")).not.toBeInTheDocument();
});
it("should show expand/collapse arrows for keys with more than 2 tags", () => {
// Production Key has 3 tags, so it should have an expand arrow
// Look for the chevron down icon (expand button)
const expandButtons = screen.getAllByRole("button");
const expandButton = expandButtons.find((button) => button.getAttribute("title") === "Show all tags");
expect(expandButton).toBeInTheDocument();
});
it("should show all tags when expanded", async () => {
// Find and click the expand button for Production Key (has 3 tags)
const expandButtons = screen.getAllByRole("button");
const expandButton = expandButtons.find((button) => button.getAttribute("title") === "Show all tags");
if (expandButton) {
fireEvent.click(expandButton);
// Now all tags should be visible
expect(screen.getByText("api-cal...")).toBeInTheDocument();
}
});
it("should show tooltip on hover with tag information", async () => {
// Hover over a tag to trigger tooltip
const tagElement = screen.getByText("high-vo...");
fireEvent.mouseOver(tagElement);
// Check that tooltip content appears
// Note: The exact tooltip content depends on your tooltip implementation
// You might need to adjust this based on how Ant Design Tooltip renders
});
});
describe("Tag Spend Formatting", () => {
it("should handle high spend amounts", () => {
renderWithProviders(<TopKeyView {...mockProps} topKeys={mockKeysWithTags} showTags={true} />);
// Test that high spend amounts are displayed
// This would require checking tooltip content or finding a way to access the formatted values
expect(screen.getByText("high-vo...")).toBeInTheDocument();
});
it("should handle micro spend amounts", () => {
renderWithProviders(<TopKeyView {...mockProps} topKeys={mockKeysWithTags} showTags={true} />);
// Test that very small amounts are displayed (only the top 2 tags are visible by default)
// product... has usage: 0.005 (<$0.01)
expect(screen.getByText("product...")).toBeInTheDocument();
// dev... has usage: 0.002 (<$0.01)
expect(screen.getByText("dev...")).toBeInTheDocument();
// experimental... has usage: 0.001 (<$0.01)
expect(screen.getByText("experim...")).toBeInTheDocument();
});
});
describe("Edge Cases", () => {
it("should handle keys with no tags", () => {
const keysWithoutTags = [
{
api_key: "key-no-tags",
key_alias: "No Tags Key",
tags: [],
spend: 10.0,
},
];
renderWithProviders(<TopKeyView {...mockProps} topKeys={keysWithoutTags} showTags={true} />);
expect(screen.getByText("-")).toBeInTheDocument();
});
it("should handle keys with undefined tags", () => {
const keysWithUndefinedTags = [
{
api_key: "key-undefined-tags",
key_alias: "Undefined Tags Key",
tags: undefined,
spend: 5.0,
},
];
renderWithProviders(<TopKeyView {...mockProps} topKeys={keysWithUndefinedTags} showTags={true} />);
expect(screen.getByText("-")).toBeInTheDocument();
});
it("should handle keys with null tags", () => {
const keysWithNullTags = [
{
api_key: "key-null-tags",
key_alias: "Null Tags Key",
tags: null,
spend: 3.0,
},
];
renderWithProviders(<TopKeyView {...mockProps} topKeys={keysWithNullTags} showTags={true} />);
expect(screen.getByText("-")).toBeInTheDocument();
});
});
describe("Tag Truncation", () => {
it("should truncate long tag names to 7 characters", () => {
const keysWithLongTags = [
{
api_key: "key-long-tags",
key_alias: "Long Tags Key",
tags: [{ tag: "very-long-tag-name", usage: 10.0 } as TagUsage, { tag: "short", usage: 5.0 } as TagUsage],
spend: 15.0,
},
];
renderWithProviders(<TopKeyView {...mockProps} topKeys={keysWithLongTags} showTags={true} />);
// Should show truncated version
expect(screen.getByText("very-lo...")).toBeInTheDocument();
// Short tags should still be truncated (all tags get ...)
expect(screen.getByText("short...")).toBeInTheDocument();
});
});
describe("Multiple Keys with Different Tag Spend Patterns", () => {
it("should handle mixed spend patterns across multiple keys", () => {
const mixedSpendKeys = [
{
api_key: "key-mixed-1",
key_alias: "Mixed Key 1",
tags: [{ tag: "expensive", usage: 999.99 } as TagUsage, { tag: "cheap", usage: 0.001 } as TagUsage],
spend: 1000.0,
},
{
api_key: "key-mixed-2",
key_alias: "Mixed Key 2",
tags: [{ tag: "moderate", usage: 50.0 } as TagUsage, { tag: "tiny", usage: 0.005 } as TagUsage],
spend: 50.01,
},
];
renderWithProviders(<TopKeyView {...mockProps} topKeys={mixedSpendKeys} showTags={true} />);
// Verify that all tag types are displayed
expect(screen.getByText("expensi...")).toBeInTheDocument();
expect(screen.getByText("cheap...")).toBeInTheDocument();
expect(screen.getByText("moderat...")).toBeInTheDocument();
expect(screen.getByText("tiny...")).toBeInTheDocument();
});
});
describe("Table Structure", () => {
it("should render table with correct headers when showTags is true", () => {
renderWithProviders(<TopKeyView {...mockProps} showTags={true} />);
expect(screen.getByText("Key ID")).toBeInTheDocument();
expect(screen.getByText("Key Alias")).toBeInTheDocument();
expect(screen.getByText("Tags")).toBeInTheDocument();
expect(screen.getByText("Spend (USD)")).toBeInTheDocument();
});
it("should render table with correct headers when showTags is false", () => {
renderWithProviders(<TopKeyView {...mockProps} showTags={false} />);
expect(screen.getByText("Key ID")).toBeInTheDocument();
expect(screen.getByText("Key Alias")).toBeInTheDocument();
expect(screen.queryByText("Tags")).not.toBeInTheDocument();
expect(screen.getByText("Spend (USD)")).toBeInTheDocument();
});
});
describe("Key Data Display", () => {
it("should display key information correctly", () => {
const simpleKeys = [
{
api_key: "test-key-123",
key_alias: "Test Key",
tags: [],
spend: 25.5,
},
];
renderWithProviders(<TopKeyView {...mockProps} topKeys={simpleKeys} showTags={true} />);
// Check that key alias is displayed
expect(screen.getByText("Test Key")).toBeInTheDocument();
// Check that spend is formatted correctly
expect(screen.getByText("$25.50")).toBeInTheDocument();
});
});
});