From 65c62ffb1b0669bb1f78903c303ede7c49aeaa2c Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 2 Feb 2026 14:16:50 -0800 Subject: [PATCH] Adding tests --- .../Modals/BaseSSOSettingsForm.test.tsx | 111 ++++++++++++++ .../Modals/EditSSOSettingsModal.test.tsx | 106 +++++++++++++ .../AdminSettings/SSOSettings/utils.test.ts | 144 ++++++++++++++++++ 3 files changed, 361 insertions(+) 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 index a885bffa710..c68e2716f5b 100644 --- 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 @@ -151,6 +151,117 @@ describe("BaseSSOSettingsForm", () => { expect(screen.getByText("Default Role")).toBeInTheDocument(); }); }); + + it("should show team mappings checkbox 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 Team Mappings")).toBeInTheDocument(); + }); + }); + + it("should show team mappings checkbox 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 Team Mappings")).toBeInTheDocument(); + }); + }); + + it("should show team IDs JWT field when use_team_mappings is checked 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 Team Mappings")).toBeInTheDocument(); + }); + + const checkbox = screen.getByLabelText("Use Team Mappings"); + await act(async () => { + fireEvent.click(checkbox); + }); + + await waitFor(() => { + expect(screen.getByText("Team IDs JWT Field")).toBeInTheDocument(); + }); + }); + + it("should not show team mappings checkbox for google 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 googleOption = screen.getByText(/google sso/i); + fireEvent.click(googleOption); + }); + + await waitFor(() => { + expect(screen.getByText("Google Client ID")).toBeInTheDocument(); + }); + + expect(screen.queryByText("Use Team Mappings")).not.toBeInTheDocument(); + }); }); describe("renderProviderFields", () => { diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/EditSSOSettingsModal.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/EditSSOSettingsModal.test.tsx index 559d837b409..d2d54033395 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/EditSSOSettingsModal.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/EditSSOSettingsModal.test.tsx @@ -105,6 +105,14 @@ const createRoleMappingsSSOData = (overrides: Record = {}) => ...overrides, }); +const createTeamMappingsSSOData = (overrides: Record = {}) => + createGenericSSOData({ + team_mappings: { + team_ids_jwt_field: overrides.team_ids_jwt_field || "teams", + }, + ...overrides, + }); + // Mock utilities const createMockHooks = (): { useSSOSettings: SSOSettingsHookReturn; @@ -577,6 +585,104 @@ describe("EditSSOSettingsModal", () => { }); }); }); + }); + + describe("Team Mappings", () => { + it("processes team mappings when team_mappings exists", async () => { + const ssoData = createTeamMappingsSSOData(); + + setupMocks({ + useSSOSettings: { data: ssoData, isLoading: false, error: null }, + }); + + renderComponent(); + + await waitFor(() => { + expect(mockForm.setFieldsValue).toHaveBeenCalledWith({ + sso_provider: SSO_PROVIDERS.GENERIC, + ...ssoData.values, + use_team_mappings: true, + team_ids_jwt_field: "teams", + }); + }); + }); + + it("handles team mappings with custom JWT field name", async () => { + const ssoData = createTeamMappingsSSOData({ + team_ids_jwt_field: "custom_teams_field", + }); + + setupMocks({ + useSSOSettings: { data: ssoData, isLoading: false, error: null }, + }); + + renderComponent(); + + await waitFor(() => { + expect(mockForm.setFieldsValue).toHaveBeenCalledWith({ + sso_provider: SSO_PROVIDERS.GENERIC, + ...ssoData.values, + use_team_mappings: true, + team_ids_jwt_field: "custom_teams_field", + }); + }); + }); + + it("handles team mappings and role mappings together", async () => { + const ssoData = createGenericSSOData({ + role_mappings: { + group_claim: "groups", + default_role: "internal_user", + roles: { + proxy_admin: ["admin-group"], + proxy_admin_viewer: [], + internal_user: [], + internal_user_viewer: [], + }, + }, + team_mappings: { + team_ids_jwt_field: "teams", + }, + }); + + setupMocks({ + useSSOSettings: { data: ssoData, isLoading: false, error: null }, + }); + + renderComponent(); + + await waitFor(() => { + expect(mockForm.setFieldsValue).toHaveBeenCalledWith({ + sso_provider: SSO_PROVIDERS.GENERIC, + ...ssoData.values, + use_role_mappings: true, + group_claim: "groups", + default_role: "internal_user", + proxy_admin_teams: "admin-group", + admin_viewer_teams: "", + internal_user_teams: "", + internal_viewer_teams: "", + use_team_mappings: true, + team_ids_jwt_field: "teams", + }); + }); + }); + + it("does not set team mapping fields when team_mappings is not present", async () => { + const ssoData = createGenericSSOData(); + + setupMocks({ + useSSOSettings: { data: ssoData, isLoading: false, error: null }, + }); + + renderComponent(); + + await waitFor(() => { + const callArgs = mockForm.setFieldsValue.mock.calls[0][0]; + expect(callArgs.use_team_mappings).toBeUndefined(); + expect(callArgs.team_ids_jwt_field).toBeUndefined(); + }); + }); it("handles provider detection with partial SSO data", async () => { const ssoData = createSSOData({ diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/utils.test.ts b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/utils.test.ts index 718302d35fe..722d52d64f9 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/utils.test.ts +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/utils.test.ts @@ -12,6 +12,8 @@ describe("processSSOSettingsPayload", () => { default_role: "proxy_admin", group_claim: "groups", use_role_mappings: false, + use_team_mappings: false, + team_ids_jwt_field: "teams", other_field: "value", another_field: 123, }; @@ -23,6 +25,7 @@ describe("processSSOSettingsPayload", () => { another_field: 123, }); expect(result.role_mappings).toBeUndefined(); + expect(result.team_mappings).toBeUndefined(); }); it("should return all fields except role mapping fields when use_role_mappings is not present", () => { @@ -33,6 +36,8 @@ describe("processSSOSettingsPayload", () => { internal_viewer_teams: "viewer1", default_role: "proxy_admin", group_claim: "groups", + use_team_mappings: false, + team_ids_jwt_field: "teams", other_field: "value", }; @@ -42,6 +47,7 @@ describe("processSSOSettingsPayload", () => { other_field: "value", }); expect(result.role_mappings).toBeUndefined(); + expect(result.team_mappings).toBeUndefined(); }); }); @@ -253,6 +259,143 @@ describe("processSSOSettingsPayload", () => { }); }); + describe("without team mappings", () => { + it("should return all fields except team mapping fields when use_team_mappings is false", () => { + const formValues = { + use_team_mappings: false, + team_ids_jwt_field: "teams", + sso_provider: "okta", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result).toEqual({ + sso_provider: "okta", + other_field: "value", + }); + expect(result.team_mappings).toBeUndefined(); + }); + + it("should return all fields except team mapping fields when use_team_mappings is not present", () => { + const formValues = { + team_ids_jwt_field: "teams", + sso_provider: "generic", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result).toEqual({ + sso_provider: "generic", + other_field: "value", + }); + expect(result.team_mappings).toBeUndefined(); + }); + + it("should not include team mappings for unsupported providers even when use_team_mappings is true", () => { + const formValues = { + use_team_mappings: true, + team_ids_jwt_field: "teams", + sso_provider: "google", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result).toEqual({ + sso_provider: "google", + other_field: "value", + }); + expect(result.team_mappings).toBeUndefined(); + }); + + it("should not include team mappings for microsoft provider even when use_team_mappings is true", () => { + const formValues = { + use_team_mappings: true, + team_ids_jwt_field: "teams", + sso_provider: "microsoft", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result).toEqual({ + sso_provider: "microsoft", + other_field: "value", + }); + expect(result.team_mappings).toBeUndefined(); + }); + }); + + describe("with team mappings enabled", () => { + it("should create team mappings for okta provider when use_team_mappings is true", () => { + const formValues = { + use_team_mappings: true, + team_ids_jwt_field: "teams", + sso_provider: "okta", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result.other_field).toBe("value"); + expect(result.team_mappings).toEqual({ + team_ids_jwt_field: "teams", + }); + }); + + it("should create team mappings for generic provider when use_team_mappings is true", () => { + const formValues = { + use_team_mappings: true, + team_ids_jwt_field: "custom_teams", + sso_provider: "generic", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result.other_field).toBe("value"); + expect(result.team_mappings).toEqual({ + team_ids_jwt_field: "custom_teams", + }); + }); + + it("should exclude team mapping fields from payload when team mappings are included", () => { + const formValues = { + use_team_mappings: true, + team_ids_jwt_field: "teams", + sso_provider: "okta", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result.use_team_mappings).toBeUndefined(); + expect(result.team_ids_jwt_field).toBeUndefined(); + }); + + it("should handle team mappings and role mappings together", () => { + const formValues = { + use_team_mappings: true, + team_ids_jwt_field: "teams", + use_role_mappings: true, + group_claim: "groups", + default_role: "internal_user", + sso_provider: "okta", + other_field: "value", + }; + + const result = processSSOSettingsPayload(formValues); + + expect(result.team_mappings).toEqual({ + team_ids_jwt_field: "teams", + }); + expect(result.role_mappings).toBeDefined(); + expect(result.role_mappings.group_claim).toBe("groups"); + }); + }); + describe("edge cases", () => { it("should handle empty form values", () => { const result = processSSOSettingsPayload({}); @@ -263,6 +406,7 @@ describe("processSSOSettingsPayload", () => { it("should preserve other fields in the payload", () => { const formValues = { use_role_mappings: false, + use_team_mappings: false, sso_provider: "google", client_id: "123", client_secret: "secret",