Testing coverage with v8

This commit is contained in:
yuneng-jiang 2025-12-31 12:24:01 -08:00
parent a17980c744
commit 81c78931d8
8 changed files with 1259 additions and 3 deletions

3
.gitignore vendored
View File

@ -103,4 +103,5 @@ scripts/test_vertex_ai_search.py
LAZY_LOADING_IMPROVEMENTS.md
**/test-results
**/playwright-report
**/*.storageState.json
**/*.storageState.json
**/coverage

View File

@ -9,6 +9,7 @@
"lint": "next lint",
"test": "vitest",
"test:watch": "vitest -w",
"test:coverage": "vitest run --coverage",
"format": "prettier --write .",
"format:check": "prettier --check .",
"e2e": "playwright test --config e2e_tests/playwright.config.ts",

View File

@ -0,0 +1,284 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React, { ReactNode } from "react";
import { useGuardrails } from "./useGuardrails";
import { getGuardrailsList } from "@/components/networking";
// Mock the networking function
vi.mock("@/components/networking", () => ({
getGuardrailsList: vi.fn(),
}));
// Mock the queryKeysFactory - we'll mock the specific return value
vi.mock("../common/queryKeysFactory", () => ({
createQueryKeys: vi.fn((resource: string) => ({
all: [resource],
lists: () => [resource, "list"],
list: (params?: any) => [resource, "list", { params }],
details: () => [resource, "detail"],
detail: (uid: string) => [resource, "detail", uid],
})),
}));
// Mock useAuthorized hook - we can override this in individual tests
const mockUseAuthorized = vi.fn();
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
default: () => mockUseAuthorized(),
}));
// Mock data
const mockGuardrailsResponse = {
guardrails: [
{ guardrail_name: "content-safety" },
{ guardrail_name: "toxicity-filter" },
{ guardrail_name: "pii-detection" },
],
};
const expectedGuardrailNames = ["content-safety", "toxicity-filter", "pii-detection"];
describe("useGuardrails", () => {
let queryClient: QueryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
// Reset all mocks
vi.clearAllMocks();
// Set default mock for useAuthorized (enabled state)
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "test-user-id",
userRole: "Admin",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
});
const wrapper = ({ children }: { children: ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
it("should return guardrail names when query is successful", async () => {
// Mock successful API call
(getGuardrailsList as any).mockResolvedValue(mockGuardrailsResponse);
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
expect(result.current.data).toBeUndefined();
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(expectedGuardrailNames);
expect(result.current.error).toBeNull();
expect(getGuardrailsList).toHaveBeenCalledWith("test-access-token");
expect(getGuardrailsList).toHaveBeenCalledTimes(1);
});
it("should handle error when getGuardrailsList fails", async () => {
const errorMessage = "Failed to fetch guardrails";
const testError = new Error(errorMessage);
// Mock failed API call
(getGuardrailsList as any).mockRejectedValue(testError);
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
// Wait for error
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(testError);
expect(result.current.data).toBeUndefined();
expect(getGuardrailsList).toHaveBeenCalledWith("test-access-token");
expect(getGuardrailsList).toHaveBeenCalledTimes(1);
});
it("should not execute query when accessToken is missing", async () => {
// Mock missing accessToken
mockUseAuthorized.mockReturnValue({
accessToken: null,
userId: "test-user-id",
userRole: "Admin",
token: null,
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(getGuardrailsList).not.toHaveBeenCalled();
});
it("should not execute query when userId is missing", async () => {
// Mock missing userId
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: null,
userRole: "Admin",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(getGuardrailsList).not.toHaveBeenCalled();
});
it("should not execute query when userRole is missing", async () => {
// Mock missing userRole
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "test-user-id",
userRole: null,
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(getGuardrailsList).not.toHaveBeenCalled();
});
it("should not execute query when all auth values are missing", async () => {
// Mock all auth values missing
mockUseAuthorized.mockReturnValue({
accessToken: null,
userId: null,
userRole: null,
token: null,
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(getGuardrailsList).not.toHaveBeenCalled();
});
it("should execute query when all auth values are present", async () => {
// Mock successful API call
(getGuardrailsList as any).mockResolvedValue(mockGuardrailsResponse);
// Ensure all auth values are present (already set in beforeEach)
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Wait for query to execute
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(getGuardrailsList).toHaveBeenCalledWith("test-access-token");
expect(getGuardrailsList).toHaveBeenCalledTimes(1);
});
it("should return empty array when API returns empty guardrails", async () => {
// Mock API returning empty guardrails array
(getGuardrailsList as any).mockResolvedValue({ guardrails: [] });
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual([]);
expect(getGuardrailsList).toHaveBeenCalledWith("test-access-token");
});
it("should handle network timeout error", async () => {
const timeoutError = new Error("Network timeout");
// Mock network timeout
(getGuardrailsList as any).mockRejectedValue(timeoutError);
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Wait for error
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(timeoutError);
expect(result.current.data).toBeUndefined();
});
it("should correctly transform guardrail objects to names array", async () => {
const customGuardrailsResponse = {
guardrails: [{ guardrail_name: "custom-guardrail-1" }, { guardrail_name: "custom-guardrail-2" }],
};
const expectedNames = ["custom-guardrail-1", "custom-guardrail-2"];
// Mock API call with custom data
(getGuardrailsList as any).mockResolvedValue(customGuardrailsResponse);
const { result } = renderHook(() => useGuardrails(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(expectedNames);
expect(result.current.data).toHaveLength(2);
expect(result.current.data).toContain("custom-guardrail-1");
expect(result.current.data).toContain("custom-guardrail-2");
});
});

View File

@ -0,0 +1,293 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React, { ReactNode } from "react";
import { useOrganizations } from "./useOrganizations";
import { organizationListCall } from "@/components/networking";
import type { Organization } from "@/components/networking";
// Mock the networking function
vi.mock("@/components/networking", () => ({
organizationListCall: vi.fn(),
}));
// Mock the queryKeysFactory - we'll mock the specific return value
vi.mock("../common/queryKeysFactory", () => ({
createQueryKeys: vi.fn((resource: string) => ({
all: [resource],
lists: () => [resource, "list"],
list: (params?: any) => [resource, "list", { params }],
details: () => [resource, "detail"],
detail: (uid: string) => [resource, "detail", uid],
})),
}));
// Mock useAuthorized hook - we can override this in individual tests
const mockUseAuthorized = vi.fn();
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
default: () => mockUseAuthorized(),
}));
// Mock data
const mockOrganizations: Organization[] = [
{
organization_id: "org-1",
organization_alias: "Test Organization 1",
budget_id: "budget-1",
metadata: {},
models: ["gpt-3.5-turbo", "gpt-4"],
spend: 100.5,
model_spend: { "gpt-3.5-turbo": 50.25, "gpt-4": 50.25 },
created_at: "2024-01-01T00:00:00Z",
created_by: "user-1",
updated_at: "2024-01-01T00:00:00Z",
updated_by: "user-1",
litellm_budget_table: null,
teams: null,
users: null,
members: [
{ user_id: "user-1", user_role: "admin" },
{ user_id: "user-2", user_role: "member" },
],
},
{
organization_id: "org-2",
organization_alias: "Test Organization 2",
budget_id: "budget-2",
metadata: {},
models: ["claude-3"],
spend: 250.75,
model_spend: { "claude-3": 250.75 },
created_at: "2024-01-01T00:00:00Z",
created_by: "user-3",
updated_at: "2024-01-01T00:00:00Z",
updated_by: "user-3",
litellm_budget_table: null,
teams: null,
users: null,
members: [{ user_id: "user-3", user_role: "admin" }],
},
];
describe("useOrganizations", () => {
let queryClient: QueryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
// Reset all mocks
vi.clearAllMocks();
// Set default mock for useAuthorized (enabled state)
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "test-user-id",
userRole: "Admin",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
});
const wrapper = ({ children }: { children: ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
it("should return organizations data when query is successful", async () => {
// Mock successful API call
(organizationListCall as any).mockResolvedValue(mockOrganizations);
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
expect(result.current.data).toBeUndefined();
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(mockOrganizations);
expect(result.current.error).toBeNull();
expect(organizationListCall).toHaveBeenCalledWith("test-access-token");
expect(organizationListCall).toHaveBeenCalledTimes(1);
});
it("should handle error when organizationListCall fails", async () => {
const errorMessage = "Failed to fetch organizations";
const testError = new Error(errorMessage);
// Mock failed API call
(organizationListCall as any).mockRejectedValue(testError);
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
// Wait for error
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(testError);
expect(result.current.data).toBeUndefined();
expect(organizationListCall).toHaveBeenCalledWith("test-access-token");
expect(organizationListCall).toHaveBeenCalledTimes(1);
});
it("should not execute query when accessToken is missing", async () => {
// Mock missing accessToken
mockUseAuthorized.mockReturnValue({
accessToken: null,
userId: "test-user-id",
userRole: "Admin",
token: null,
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(organizationListCall).not.toHaveBeenCalled();
});
it("should not execute query when userId is missing", async () => {
// Mock missing userId
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: null,
userRole: "Admin",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(organizationListCall).not.toHaveBeenCalled();
});
it("should not execute query when userRole is missing", async () => {
// Mock missing userRole
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "test-user-id",
userRole: null,
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(organizationListCall).not.toHaveBeenCalled();
});
it("should not execute query when all auth values are missing", async () => {
// Mock all auth values missing
mockUseAuthorized.mockReturnValue({
accessToken: null,
userId: null,
userRole: null,
token: null,
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(organizationListCall).not.toHaveBeenCalled();
});
it("should execute query when all auth values are present", async () => {
// Mock successful API call
(organizationListCall as any).mockResolvedValue(mockOrganizations);
// Ensure all auth values are present (already set in beforeEach)
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Wait for query to execute
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(organizationListCall).toHaveBeenCalledWith("test-access-token");
expect(organizationListCall).toHaveBeenCalledTimes(1);
});
it("should return empty array when API returns empty data", async () => {
// Mock API returning empty array
(organizationListCall as any).mockResolvedValue([]);
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual([]);
expect(organizationListCall).toHaveBeenCalledWith("test-access-token");
});
it("should handle network timeout error", async () => {
const timeoutError = new Error("Network timeout");
// Mock network timeout
(organizationListCall as any).mockRejectedValue(timeoutError);
const { result } = renderHook(() => useOrganizations(), { wrapper });
// Wait for error
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(timeoutError);
expect(result.current.data).toBeUndefined();
});
});

View File

@ -0,0 +1,193 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React, { ReactNode } from "react";
import { useProviderFields } from "./useProviderFields";
import { getProviderCreateMetadata } from "@/components/networking";
import type { ProviderCreateInfo } from "@/components/networking";
// Mock the networking function
vi.mock("@/components/networking", () => ({
getProviderCreateMetadata: vi.fn(),
}));
// Mock the queryKeysFactory - we'll mock the specific return value
vi.mock("../common/queryKeysFactory", () => ({
createQueryKeys: vi.fn((resource: string) => ({
all: [resource],
lists: () => [resource, "list"],
list: (params?: any) => [resource, "list", { params }],
details: () => [resource, "detail"],
detail: (uid: string) => [resource, "detail", uid],
})),
}));
// Mock data
const mockProviderFields: ProviderCreateInfo[] = [
{
provider: "OpenAI",
provider_display_name: "OpenAI",
litellm_provider: "openai",
default_model_placeholder: "gpt-3.5-turbo",
credential_fields: [],
},
{
provider: "Anthropic",
provider_display_name: "Anthropic",
litellm_provider: "anthropic",
default_model_placeholder: "claude-3-sonnet-20240229",
credential_fields: [],
},
{
provider: "Azure",
provider_display_name: "Azure OpenAI",
litellm_provider: "azure",
default_model_placeholder: "gpt-35-turbo",
credential_fields: [],
},
];
describe("useProviderFields", () => {
let queryClient: QueryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
// Reset all mocks
vi.clearAllMocks();
});
const wrapper = ({ children }: { children: ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
it("should return provider fields data when query is successful", async () => {
// Mock successful API call
(getProviderCreateMetadata as any).mockResolvedValue(mockProviderFields);
const { result } = renderHook(() => useProviderFields(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
expect(result.current.data).toBeUndefined();
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(mockProviderFields);
expect(result.current.error).toBeNull();
expect(getProviderCreateMetadata).toHaveBeenCalledTimes(1);
});
it("should handle error when getProviderCreateMetadata fails", async () => {
const errorMessage = "Failed to fetch provider fields";
const testError = new Error(errorMessage);
// Mock failed API call
(getProviderCreateMetadata as any).mockRejectedValue(testError);
const { result } = renderHook(() => useProviderFields(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
// Wait for error
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(testError);
expect(result.current.data).toBeUndefined();
expect(getProviderCreateMetadata).toHaveBeenCalledTimes(1);
});
it("should return empty array when API returns empty data", async () => {
// Mock API returning empty array
(getProviderCreateMetadata as any).mockResolvedValue([]);
const { result } = renderHook(() => useProviderFields(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual([]);
expect(getProviderCreateMetadata).toHaveBeenCalledTimes(1);
});
it("should handle network timeout error", async () => {
const timeoutError = new Error("Network timeout");
// Mock network timeout
(getProviderCreateMetadata as any).mockRejectedValue(timeoutError);
const { result } = renderHook(() => useProviderFields(), { wrapper });
// Wait for error
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(timeoutError);
expect(result.current.data).toBeUndefined();
});
it("should have correct query configuration", async () => {
// Mock successful API call
(getProviderCreateMetadata as any).mockResolvedValue(mockProviderFields);
const { result } = renderHook(() => useProviderFields(), { wrapper });
// Wait for query to complete
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});
// Verify the query was called
expect(getProviderCreateMetadata).toHaveBeenCalledTimes(1);
// The hook should have the expected properties from useQuery
expect(result.current).toHaveProperty("data");
expect(result.current).toHaveProperty("isLoading");
expect(result.current).toHaveProperty("isError");
expect(result.current).toHaveProperty("isSuccess");
expect(result.current).toHaveProperty("error");
});
it("should return provider fields with populated credential fields", async () => {
const mockFieldsWithCredentials: ProviderCreateInfo[] = [
{
provider: "TestProvider",
provider_display_name: "Test Provider",
litellm_provider: "test",
default_model_placeholder: "test-model",
credential_fields: [], // Keeping empty as per existing test patterns
},
];
// Mock successful API call with provider that has credential fields
(getProviderCreateMetadata as any).mockResolvedValue(mockFieldsWithCredentials);
const { result } = renderHook(() => useProviderFields(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(mockFieldsWithCredentials);
expect(result.current.data?.[0].provider).toBe("TestProvider");
expect(result.current.data?.[0].litellm_provider).toBe("test");
});
});

View File

@ -0,0 +1,294 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React, { ReactNode } from "react";
import { useTags } from "./useTags";
import { tagListCall } from "@/components/networking";
import type { TagListResponse } from "@/components/tag_management/types";
// Mock the networking function
vi.mock("@/components/networking", () => ({
tagListCall: vi.fn(),
}));
// Mock the queryKeysFactory - we'll mock the specific return value
vi.mock("../common/queryKeysFactory", () => ({
createQueryKeys: vi.fn((resource: string) => ({
all: [resource],
lists: () => [resource, "list"],
list: (params?: any) => [resource, "list", { params }],
details: () => [resource, "detail"],
detail: (uid: string) => [resource, "detail", uid],
})),
}));
// Mock useAuthorized hook - we can override this in individual tests
const mockUseAuthorized = vi.fn();
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
default: () => mockUseAuthorized(),
}));
// Mock data
const mockTags: TagListResponse = {
"tag-1": {
name: "tag-1",
description: "Test tag 1 description",
models: ["gpt-3.5-turbo", "gpt-4"],
model_info: { "gpt-3.5-turbo": "GPT-3.5 Turbo", "gpt-4": "GPT-4" },
created_at: "2024-01-01T00:00:00Z",
updated_at: "2024-01-01T00:00:00Z",
created_by: "user-1",
updated_by: "user-1",
litellm_budget_table: {
max_budget: 1000,
soft_budget: 800,
tpm_limit: 100000,
rpm_limit: 1000,
max_parallel_requests: 10,
budget_duration: "monthly",
model_max_budget: { "gpt-3.5-turbo": 500, "gpt-4": 500 },
},
},
"tag-2": {
name: "tag-2",
description: "Test tag 2 description",
models: ["claude-3"],
model_info: { "claude-3": "Claude 3" },
created_at: "2024-01-02T00:00:00Z",
updated_at: "2024-01-02T00:00:00Z",
created_by: "user-2",
updated_by: "user-2",
litellm_budget_table: {
max_budget: 2000,
soft_budget: 1500,
tpm_limit: 200000,
rpm_limit: 2000,
max_parallel_requests: 20,
budget_duration: "monthly",
model_max_budget: { "claude-3": 2000 },
},
},
};
describe("useTags", () => {
let queryClient: QueryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
// Reset all mocks
vi.clearAllMocks();
// Set default mock for useAuthorized (enabled state)
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "test-user-id",
userRole: "Admin",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
});
const wrapper = ({ children }: { children: ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
it("should return tags data when query is successful", async () => {
// Mock successful API call
(tagListCall as any).mockResolvedValue(mockTags);
const { result } = renderHook(() => useTags(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
expect(result.current.data).toBeUndefined();
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(mockTags);
expect(result.current.error).toBeNull();
expect(tagListCall).toHaveBeenCalledWith("test-access-token");
expect(tagListCall).toHaveBeenCalledTimes(1);
});
it("should handle error when tagListCall fails", async () => {
const errorMessage = "Failed to fetch tags";
const testError = new Error(errorMessage);
// Mock failed API call
(tagListCall as any).mockRejectedValue(testError);
const { result } = renderHook(() => useTags(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
// Wait for error
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(testError);
expect(result.current.data).toBeUndefined();
expect(tagListCall).toHaveBeenCalledWith("test-access-token");
expect(tagListCall).toHaveBeenCalledTimes(1);
});
it("should not execute query when accessToken is missing", async () => {
// Mock missing accessToken
mockUseAuthorized.mockReturnValue({
accessToken: null,
userId: "test-user-id",
userRole: "Admin",
token: null,
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useTags(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(tagListCall).not.toHaveBeenCalled();
});
it("should not execute query when userId is missing", async () => {
// Mock missing userId
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: null,
userRole: "Admin",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useTags(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(tagListCall).not.toHaveBeenCalled();
});
it("should not execute query when userRole is missing", async () => {
// Mock missing userRole
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "test-user-id",
userRole: null,
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useTags(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(tagListCall).not.toHaveBeenCalled();
});
it("should not execute query when all auth values are missing", async () => {
// Mock all auth values missing
mockUseAuthorized.mockReturnValue({
accessToken: null,
userId: null,
userRole: null,
token: null,
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
const { result } = renderHook(() => useTags(), { wrapper });
// Query should not execute
expect(result.current.isLoading).toBe(false);
expect(result.current.data).toBeUndefined();
expect(result.current.isFetched).toBe(false);
// API should not be called
expect(tagListCall).not.toHaveBeenCalled();
});
it("should execute query when all auth values are present", async () => {
// Mock successful API call
(tagListCall as any).mockResolvedValue(mockTags);
// Ensure all auth values are present (already set in beforeEach)
const { result } = renderHook(() => useTags(), { wrapper });
// Wait for query to execute
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(tagListCall).toHaveBeenCalledWith("test-access-token");
expect(tagListCall).toHaveBeenCalledTimes(1);
});
it("should return empty object when API returns empty data", async () => {
// Mock API returning empty object
(tagListCall as any).mockResolvedValue({});
const { result } = renderHook(() => useTags(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual({});
expect(tagListCall).toHaveBeenCalledWith("test-access-token");
});
it("should handle network timeout error", async () => {
const timeoutError = new Error("Network timeout");
// Mock network timeout
(tagListCall as any).mockRejectedValue(timeoutError);
const { result } = renderHook(() => useTags(), { wrapper });
// Wait for error
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(timeoutError);
expect(result.current.data).toBeUndefined();
});
});

View File

@ -0,0 +1,169 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React, { ReactNode } from "react";
import { useUIConfig } from "./useUIConfig";
import { getUiConfig, LiteLLMWellKnownUiConfig } from "@/components/networking";
// Mock the networking function
vi.mock("@/components/networking", () => ({
getUiConfig: vi.fn(),
}));
// Mock the queryKeysFactory - we'll mock the specific return value
vi.mock("../common/queryKeysFactory", () => ({
createQueryKeys: vi.fn((resource: string) => ({
all: [resource],
lists: () => [resource, "list"],
list: (params?: any) => [resource, "list", { params }],
details: () => [resource, "detail"],
detail: (uid: string) => [resource, "detail", uid],
})),
}));
// Mock data
const mockUIConfig: LiteLLMWellKnownUiConfig = {
server_root_path: "/api",
proxy_base_url: "https://proxy.example.com",
auto_redirect_to_sso: true,
admin_ui_disabled: false,
};
describe("useUIConfig", () => {
let queryClient: QueryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
// Reset all mocks
vi.clearAllMocks();
});
const wrapper = ({ children }: { children: ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
it("should return UI config data when query is successful", async () => {
// Mock successful API call
(getUiConfig as any).mockResolvedValue(mockUIConfig);
const { result } = renderHook(() => useUIConfig(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
expect(result.current.data).toBeUndefined();
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(mockUIConfig);
expect(result.current.error).toBeNull();
expect(getUiConfig).toHaveBeenCalledWith();
expect(getUiConfig).toHaveBeenCalledTimes(1);
});
it("should handle error when getUiConfig fails", async () => {
const errorMessage = "Failed to fetch UI config";
const testError = new Error(errorMessage);
// Mock failed API call
(getUiConfig as any).mockRejectedValue(testError);
const { result } = renderHook(() => useUIConfig(), { wrapper });
// Initially loading
expect(result.current.isLoading).toBe(true);
// Wait for error
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(testError);
expect(result.current.data).toBeUndefined();
expect(getUiConfig).toHaveBeenCalledWith();
expect(getUiConfig).toHaveBeenCalledTimes(1);
});
it("should return different UI config data correctly", async () => {
const alternativeUIConfig: LiteLLMWellKnownUiConfig = {
server_root_path: "/v1",
proxy_base_url: null,
auto_redirect_to_sso: false,
admin_ui_disabled: true,
};
// Mock successful API call with different data
(getUiConfig as any).mockResolvedValue(alternativeUIConfig);
const { result } = renderHook(() => useUIConfig(), { wrapper });
// Wait for success
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(alternativeUIConfig);
expect(result.current.error).toBeNull();
});
it("should handle network timeout error", async () => {
const timeoutError = new Error("Network timeout");
// Mock network timeout
(getUiConfig as any).mockRejectedValue(timeoutError);
const { result } = renderHook(() => useUIConfig(), { wrapper });
// Wait for error
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(timeoutError);
expect(result.current.data).toBeUndefined();
});
it("should handle malformed response error", async () => {
const malformedError = new Error("Invalid JSON response");
// Mock malformed response
(getUiConfig as any).mockRejectedValue(malformedError);
const { result } = renderHook(() => useUIConfig(), { wrapper });
// Wait for error
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(malformedError);
expect(result.current.data).toBeUndefined();
});
it("should use correct query key structure", async () => {
// Mock successful API call
(getUiConfig as any).mockResolvedValue(mockUIConfig);
const { result } = renderHook(() => useUIConfig(), { wrapper });
// Wait for query to execute
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
// The query key should be generated by createQueryKeys("uiConfig").list({})
// Based on our mock, this should be ["uiConfig", "list", {}]
expect(getUiConfig).toHaveBeenCalledTimes(1);
});
});

View File

@ -7,8 +7,29 @@ export default defineConfig({
setupFiles: ["tests/setupTests.ts"],
globals: true,
css: true, // lets you import CSS/modules without extra mocks
coverage: { reporter: ["text", "lcov"] },
exclude: ["e2e_tests/**,", "node_modules/**"],
coverage: {
provider: "v8",
reporter: ["text", "lcov"],
include: ["src/**/*.{ts,tsx}"],
exclude: [
"**/*.d.ts",
"**/*.test.*",
"**/*.spec.*",
"tests/**",
"e2e_tests/**",
"node_modules/**",
".next/**",
"out/**",
"**/*.config.*",
"postcss.config.*",
"tailwind.config.*",
"next.config.*",
],
},
exclude: ["e2e_tests/**", "node_modules/**"],
include: ["src/**/*.test.ts", "src/**/*.test.tsx", "tests/**/*.test.ts", "tests/**/*.test.tsx"],
},
resolve: {