diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx
new file mode 100644
index 0000000000..a885bffa71
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx
@@ -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 ;
+ };
+
+ renderWithProviders();
+
+ 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 ;
+ };
+
+ renderWithProviders();
+
+ 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 ;
+ };
+
+ renderWithProviders();
+
+ 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 ;
+ };
+
+ renderWithProviders();
+
+ 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 ;
+ };
+
+ renderWithProviders();
+
+ 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 ;
+ };
+
+ renderWithProviders();
+
+ 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);
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/team/available_teams.test.tsx b/ui/litellm-dashboard/src/components/team/available_teams.test.tsx
new file mode 100644
index 0000000000..af2a0247b4
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/team/available_teams.test.tsx
@@ -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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ await waitFor(() => {
+ expect(screen.getByText("gpt-4")).toBeInTheDocument();
+ expect(screen.getByText("gpt-3.5-turbo")).toBeInTheDocument();
+ });
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx b/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx
new file mode 100644
index 0000000000..cee2b8d587
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx
@@ -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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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);
+ });
+ }
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/team/permission_definitions.test.tsx b/ui/litellm-dashboard/src/components/team/permission_definitions.test.tsx
new file mode 100644
index 0000000000..2d8aeabf38
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/team/permission_definitions.test.tsx
@@ -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");
+ });
+ });
+});