mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-13 03:05:31 +00:00
Adding unit tests for coverage
This commit is contained in:
+185
@@ -0,0 +1,185 @@
|
||||
import { Form } from "antd";
|
||||
import { act, fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../../../../../tests/test-utils";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import BaseSSOSettingsForm, { renderProviderFields } from "./BaseSSOSettingsForm";
|
||||
|
||||
describe("BaseSSOSettingsForm", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should render", () => {
|
||||
const TestWrapper = () => {
|
||||
const [form] = Form.useForm();
|
||||
const handleSubmit = vi.fn();
|
||||
|
||||
return <BaseSSOSettingsForm form={form} onFormSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
expect(screen.getByText("SSO Provider")).toBeInTheDocument();
|
||||
expect(screen.getByText("Proxy Admin Email")).toBeInTheDocument();
|
||||
expect(screen.getByText("Proxy Base URL")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render provider fields when provider is selected", async () => {
|
||||
const TestWrapper = () => {
|
||||
const [form] = Form.useForm();
|
||||
const handleSubmit = vi.fn();
|
||||
|
||||
return <BaseSSOSettingsForm form={form} onFormSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
const providerSelect = screen.getByLabelText("SSO Provider");
|
||||
await act(async () => {
|
||||
fireEvent.mouseDown(providerSelect);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const googleOption = screen.getByText(/google sso/i);
|
||||
fireEvent.click(googleOption);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Google Client ID")).toBeInTheDocument();
|
||||
expect(screen.getByText("Google Client Secret")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should show role mappings fields for okta provider", async () => {
|
||||
const TestWrapper = () => {
|
||||
const [form] = Form.useForm();
|
||||
const handleSubmit = vi.fn();
|
||||
|
||||
return <BaseSSOSettingsForm form={form} onFormSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
const providerSelect = screen.getByLabelText("SSO Provider");
|
||||
await act(async () => {
|
||||
fireEvent.mouseDown(providerSelect);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const oktaOption = screen.getByText(/okta/i);
|
||||
fireEvent.click(oktaOption);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Use Role Mappings")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should validate proxy base url format", async () => {
|
||||
const TestWrapper = () => {
|
||||
const [form] = Form.useForm();
|
||||
const handleSubmit = vi.fn();
|
||||
|
||||
return <BaseSSOSettingsForm form={form} onFormSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
const urlInput = screen.getByPlaceholderText("https://example.com");
|
||||
await act(async () => {
|
||||
fireEvent.change(urlInput, { target: { value: "invalid-url" } });
|
||||
fireEvent.blur(urlInput);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/URL must start with http:\/\/ or https:\/\//i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should validate proxy base url trailing slash", async () => {
|
||||
const TestWrapper = () => {
|
||||
const [form] = Form.useForm();
|
||||
const handleSubmit = vi.fn();
|
||||
|
||||
return <BaseSSOSettingsForm form={form} onFormSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
const urlInput = screen.getByPlaceholderText("https://example.com");
|
||||
await act(async () => {
|
||||
fireEvent.change(urlInput, { target: { value: "https://example.com/" } });
|
||||
fireEvent.blur(urlInput);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/URL must not end with a trailing slash/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should show role mappings fields when use_role_mappings is checked for generic provider", async () => {
|
||||
const TestWrapper = () => {
|
||||
const [form] = Form.useForm();
|
||||
const handleSubmit = vi.fn();
|
||||
|
||||
return <BaseSSOSettingsForm form={form} onFormSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
const providerSelect = screen.getByLabelText("SSO Provider");
|
||||
await act(async () => {
|
||||
fireEvent.mouseDown(providerSelect);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const genericOption = screen.getByText(/generic sso/i);
|
||||
fireEvent.click(genericOption);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Use Role Mappings")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const checkbox = screen.getByLabelText("Use Role Mappings");
|
||||
await act(async () => {
|
||||
fireEvent.click(checkbox);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Group Claim")).toBeInTheDocument();
|
||||
expect(screen.getByText("Default Role")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderProviderFields", () => {
|
||||
it("should return null for unknown provider", () => {
|
||||
const result = renderProviderFields("unknown");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should return fields for google provider", () => {
|
||||
const result = renderProviderFields("google");
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should return fields for microsoft provider", () => {
|
||||
const result = renderProviderFields("microsoft");
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should return fields for okta provider", () => {
|
||||
const result = renderProviderFields("okta");
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.length).toBe(5);
|
||||
});
|
||||
|
||||
it("should return fields for generic provider", () => {
|
||||
const result = renderProviderFields("generic");
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.length).toBe(5);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
import * as networking from "@/components/networking";
|
||||
import { act, fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import AvailableTeamsPanel from "./available_teams";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
availableTeamListCall: vi.fn(),
|
||||
teamMemberAddCall: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("AvailableTeamsPanel", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
vi.mocked(networking.availableTeamListCall).mockResolvedValue([]);
|
||||
|
||||
renderWithProviders(<AvailableTeamsPanel accessToken="token-123" userID="user-123" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Team Name")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display teams when available", async () => {
|
||||
const mockTeams = [
|
||||
{
|
||||
team_id: "team-1",
|
||||
team_alias: "Test Team 1",
|
||||
description: "Test Description 1",
|
||||
models: ["gpt-4"],
|
||||
members_with_roles: [{ user_id: "user-1", user_email: "user1@test.com", role: "admin" }],
|
||||
},
|
||||
{
|
||||
team_id: "team-2",
|
||||
team_alias: "Test Team 2",
|
||||
description: "Test Description 2",
|
||||
models: [],
|
||||
members_with_roles: [{ user_id: "user-2", user_email: "user2@test.com", role: "user" }],
|
||||
},
|
||||
];
|
||||
|
||||
vi.mocked(networking.availableTeamListCall).mockResolvedValue(mockTeams);
|
||||
|
||||
renderWithProviders(<AvailableTeamsPanel accessToken="token-123" userID="user-123" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Team 1")).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Team 2")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display empty state when no teams are available", async () => {
|
||||
vi.mocked(networking.availableTeamListCall).mockResolvedValue([]);
|
||||
|
||||
renderWithProviders(<AvailableTeamsPanel accessToken="token-123" userID="user-123" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("No available teams to join")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should call teamMemberAddCall when join team button is clicked", async () => {
|
||||
const mockTeams = [
|
||||
{
|
||||
team_id: "team-1",
|
||||
team_alias: "Test Team 1",
|
||||
description: "Test Description 1",
|
||||
models: ["gpt-4"],
|
||||
members_with_roles: [{ user_id: "user-1", user_email: "user1@test.com", role: "admin" }],
|
||||
},
|
||||
];
|
||||
|
||||
vi.mocked(networking.availableTeamListCall).mockResolvedValue(mockTeams);
|
||||
vi.mocked(networking.teamMemberAddCall).mockResolvedValue({});
|
||||
|
||||
renderWithProviders(<AvailableTeamsPanel accessToken="token-123" userID="user-123" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Team 1")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const joinButtons = screen.getAllByRole("button", { name: /join team/i });
|
||||
await act(async () => {
|
||||
fireEvent.click(joinButtons[0]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.teamMemberAddCall).toHaveBeenCalledWith("token-123", "team-1", {
|
||||
user_id: "user-123",
|
||||
role: "user",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should display All Proxy Models badge when team has no models", async () => {
|
||||
const mockTeams = [
|
||||
{
|
||||
team_id: "team-1",
|
||||
team_alias: "Test Team 1",
|
||||
description: "Test Description 1",
|
||||
models: [],
|
||||
members_with_roles: [{ user_id: "user-1", user_email: "user1@test.com", role: "admin" }],
|
||||
},
|
||||
];
|
||||
|
||||
vi.mocked(networking.availableTeamListCall).mockResolvedValue(mockTeams);
|
||||
|
||||
renderWithProviders(<AvailableTeamsPanel accessToken="token-123" userID="user-123" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display model badges when team has models", async () => {
|
||||
const mockTeams = [
|
||||
{
|
||||
team_id: "team-1",
|
||||
team_alias: "Test Team 1",
|
||||
description: "Test Description 1",
|
||||
models: ["gpt-4", "gpt-3.5-turbo"],
|
||||
members_with_roles: [{ user_id: "user-1", user_email: "user1@test.com", role: "admin" }],
|
||||
},
|
||||
];
|
||||
|
||||
vi.mocked(networking.availableTeamListCall).mockResolvedValue(mockTeams);
|
||||
|
||||
renderWithProviders(<AvailableTeamsPanel accessToken="token-123" userID="user-123" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("gpt-4")).toBeInTheDocument();
|
||||
expect(screen.getByText("gpt-3.5-turbo")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,160 @@
|
||||
import * as networking from "@/components/networking";
|
||||
import { act, fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import MemberPermissions from "./member_permissions";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getTeamPermissionsCall: vi.fn(),
|
||||
teamPermissionsUpdateCall: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("MemberPermissions", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValue({
|
||||
all_available_permissions: ["/key/generate", "/key/list"],
|
||||
team_member_permissions: ["/key/generate"],
|
||||
});
|
||||
|
||||
renderWithProviders(<MemberPermissions teamId="team-123" accessToken="token-123" canEditTeam={true} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Member Permissions")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display permissions table when permissions are available", async () => {
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValue({
|
||||
all_available_permissions: ["/key/generate", "/key/list"],
|
||||
team_member_permissions: ["/key/generate"],
|
||||
});
|
||||
|
||||
renderWithProviders(<MemberPermissions teamId="team-123" accessToken="token-123" canEditTeam={true} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Method")).toBeInTheDocument();
|
||||
expect(screen.getByText("Endpoint")).toBeInTheDocument();
|
||||
expect(screen.getByText("Description")).toBeInTheDocument();
|
||||
expect(screen.getByText("Allow Access")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display empty state when no permissions are available", async () => {
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValue({
|
||||
all_available_permissions: [],
|
||||
team_member_permissions: [],
|
||||
});
|
||||
|
||||
renderWithProviders(<MemberPermissions teamId="team-123" accessToken="token-123" canEditTeam={true} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("No permissions available")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should save permissions when save button is clicked", async () => {
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValue({
|
||||
all_available_permissions: ["/key/generate", "/key/list"],
|
||||
team_member_permissions: ["/key/generate"],
|
||||
});
|
||||
vi.mocked(networking.teamPermissionsUpdateCall).mockResolvedValue({});
|
||||
|
||||
renderWithProviders(<MemberPermissions teamId="team-123" accessToken="token-123" canEditTeam={true} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Member Permissions")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
const unselectedCheckbox = checkboxes.find((cb) => !(cb as HTMLInputElement).checked);
|
||||
|
||||
if (unselectedCheckbox) {
|
||||
await act(async () => {
|
||||
fireEvent.click(unselectedCheckbox);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const saveButton = screen.getByRole("button", { name: /save changes/i });
|
||||
expect(saveButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const saveButton = screen.getByRole("button", { name: /save changes/i });
|
||||
await act(async () => {
|
||||
fireEvent.click(saveButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.teamPermissionsUpdateCall).toHaveBeenCalledWith(
|
||||
"token-123",
|
||||
"team-123",
|
||||
expect.arrayContaining(["/key/generate", "/key/list"]),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("should not show save button when canEditTeam is false", async () => {
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValue({
|
||||
all_available_permissions: ["/key/generate", "/key/list"],
|
||||
team_member_permissions: ["/key/generate"],
|
||||
});
|
||||
|
||||
renderWithProviders(<MemberPermissions teamId="team-123" accessToken="token-123" canEditTeam={false} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Member Permissions")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
checkboxes.forEach((checkbox) => {
|
||||
expect(checkbox).toBeDisabled();
|
||||
});
|
||||
|
||||
expect(screen.queryByRole("button", { name: /save changes/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle reset button click", async () => {
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValue({
|
||||
all_available_permissions: ["/key/generate", "/key/list"],
|
||||
team_member_permissions: ["/key/generate"],
|
||||
});
|
||||
|
||||
renderWithProviders(<MemberPermissions teamId="team-123" accessToken="token-123" canEditTeam={true} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Member Permissions")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
const unselectedCheckbox = checkboxes.find((cb) => !(cb as HTMLInputElement).checked);
|
||||
|
||||
if (unselectedCheckbox) {
|
||||
await act(async () => {
|
||||
fireEvent.click(unselectedCheckbox);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const resetButton = screen.getByRole("button", { name: /reset/i });
|
||||
expect(resetButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
vi.mocked(networking.getTeamPermissionsCall).mockResolvedValueOnce({
|
||||
all_available_permissions: ["/key/generate", "/key/list"],
|
||||
team_member_permissions: ["/key/generate"],
|
||||
});
|
||||
|
||||
const resetButton = screen.getByRole("button", { name: /reset/i });
|
||||
await act(async () => {
|
||||
fireEvent.click(resetButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.getTeamPermissionsCall).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getMethodForEndpoint, getPermissionInfo, PERMISSION_DESCRIPTIONS } from "./permission_definitions";
|
||||
|
||||
describe("permission_definitions", () => {
|
||||
describe("getMethodForEndpoint", () => {
|
||||
it("should return GET for info endpoints", () => {
|
||||
expect(getMethodForEndpoint("/key/info")).toBe("GET");
|
||||
});
|
||||
|
||||
it("should return GET for list endpoints", () => {
|
||||
expect(getMethodForEndpoint("/key/list")).toBe("GET");
|
||||
});
|
||||
|
||||
it("should return POST for other endpoints", () => {
|
||||
expect(getMethodForEndpoint("/key/generate")).toBe("POST");
|
||||
expect(getMethodForEndpoint("/key/update")).toBe("POST");
|
||||
expect(getMethodForEndpoint("/key/delete")).toBe("POST");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPermissionInfo", () => {
|
||||
it("should return correct info for exact match permission", () => {
|
||||
const result = getPermissionInfo("/key/generate");
|
||||
expect(result.method).toBe("POST");
|
||||
expect(result.endpoint).toBe("/key/generate");
|
||||
expect(result.description).toBe(PERMISSION_DESCRIPTIONS["/key/generate"]);
|
||||
expect(result.route).toBe("/key/generate");
|
||||
});
|
||||
|
||||
it("should return GET method for info endpoint", () => {
|
||||
const result = getPermissionInfo("/key/info");
|
||||
expect(result.method).toBe("GET");
|
||||
expect(result.endpoint).toBe("/key/info");
|
||||
expect(result.description).toBe(PERMISSION_DESCRIPTIONS["/key/info"]);
|
||||
});
|
||||
|
||||
it("should return GET method for list endpoint", () => {
|
||||
const result = getPermissionInfo("/key/list");
|
||||
expect(result.method).toBe("GET");
|
||||
expect(result.endpoint).toBe("/key/list");
|
||||
expect(result.description).toBe(PERMISSION_DESCRIPTIONS["/key/list"]);
|
||||
});
|
||||
|
||||
it("should find partial match for permission with pattern", () => {
|
||||
const result = getPermissionInfo("/key/service-account/generate");
|
||||
expect(result.method).toBe("POST");
|
||||
expect(result.endpoint).toBe("/key/service-account/generate");
|
||||
expect(result.description).toBe(PERMISSION_DESCRIPTIONS["/key/service-account/generate"]);
|
||||
});
|
||||
|
||||
it("should return fallback description for unknown permission", () => {
|
||||
const result = getPermissionInfo("/unknown/endpoint");
|
||||
expect(result.method).toBe("POST");
|
||||
expect(result.endpoint).toBe("/unknown/endpoint");
|
||||
expect(result.description).toBe("Access /unknown/endpoint");
|
||||
expect(result.route).toBe("/unknown/endpoint");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user