diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx index 2e4d0d97e4c..68f08a11e0e 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx @@ -84,6 +84,34 @@ vi.mock("../common_components/AccessGroupSelector", () => ({ ), })); +// Test stand-in for RouterSettingsAccordion: exposes the initial value and lets +// tests trigger an onChange without rendering its full tabbed UI / network deps. +vi.mock("../common_components/RouterSettingsAccordion", () => ({ + __esModule: true, + default: ({ + value, + onChange, + }: { + value?: { router_settings: Record }; + onChange?: (v: { router_settings: Record }) => void; + }) => ( +
+
{JSON.stringify(value ?? null)}
+ +
+ ), +})); + describe("KeyEditView", () => { const MOCK_KEY_DATA: KeyResponse = { token: "test-token-123", @@ -98,6 +126,8 @@ describe("KeyEditView", () => { config: {}, user_id: "default_user_id", team_id: null, + project_id: null, + last_active: null, max_parallel_requests: 10, metadata: { logging: [], @@ -587,6 +617,132 @@ describe("KeyEditView", () => { } }); + describe("router settings", () => { + it("should render the router settings section when accessToken is provided", async () => { + renderWithProviders( + {}} + onSubmit={async () => {}} + accessToken="test-token" + userID="test-user" + userRole="admin" + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(screen.getByText("Router Settings")).toBeInTheDocument(); + }); + expect(screen.getByTestId("router-settings-accordion")).toBeInTheDocument(); + }); + + it("should not render the router settings section when accessToken is missing", async () => { + renderWithProviders( + {}} + onSubmit={async () => {}} + accessToken="" + userID="" + userRole="" + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(screen.getByText("Save Changes")).toBeInTheDocument(); + }); + expect(screen.queryByTestId("router-settings-accordion")).not.toBeInTheDocument(); + }); + + it("should initialize the accordion from keyData.router_settings", async () => { + const keyDataWithRouterSettings = { + ...MOCK_KEY_DATA, + router_settings: { fallbacks: [{ "gpt-4": ["gpt-3.5-turbo"] }] }, + }; + + renderWithProviders( + {}} + onSubmit={async () => {}} + accessToken="test-token" + userID="test-user" + userRole="admin" + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(screen.getByTestId("router-settings-value")).toBeInTheDocument(); + }); + const value = JSON.parse(screen.getByTestId("router-settings-value").textContent || "null"); + expect(value).toEqual({ + router_settings: { fallbacks: [{ "gpt-4": ["gpt-3.5-turbo"] }] }, + }); + }); + + it("should pass router_settings through to onSubmit when the user sets them", async () => { + const onSubmitMock = vi.fn().mockResolvedValue(undefined); + + renderWithProviders( + {}} + onSubmit={onSubmitMock} + accessToken="test-token" + userID="test-user" + userRole="admin" + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(screen.getByTestId("router-settings-set-fallbacks")).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByTestId("router-settings-set-fallbacks")); + await userEvent.click(screen.getByRole("button", { name: /save changes/i })); + + await waitFor(() => { + expect(onSubmitMock).toHaveBeenCalled(); + }); + const callArgs = onSubmitMock.mock.calls[0][0]; + expect(callArgs.router_settings).toEqual({ + fallbacks: [{ "gpt-4": ["gpt-3.5-turbo"] }], + }); + }); + + it("should omit router_settings from onSubmit when the user does not touch them", async () => { + const onSubmitMock = vi.fn().mockResolvedValue(undefined); + + renderWithProviders( + {}} + onSubmit={onSubmitMock} + accessToken="test-token" + userID="test-user" + userRole="admin" + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save changes/i })).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole("button", { name: /save changes/i })); + + await waitFor(() => { + expect(onSubmitMock).toHaveBeenCalled(); + }); + const callArgs = onSubmitMock.mock.calls[0][0]; + expect("router_settings" in callArgs).toBe(false); + }); + }); + describe("organization dropdown", () => { it("should render the organization dropdown", async () => { renderWithProviders( diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx index a9a3e566c86..1a4164f50a9 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx @@ -750,6 +750,7 @@ export function KeyEditView({ { key: "router-settings", label: Router Settings, + forceRender: true, children: (