From 6033af3ecb58683fc1689242b6a39a0994b2a4ff Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Sat, 2 May 2026 20:26:34 -0700 Subject: [PATCH] test(ui): cover router_settings round-trip in key edit view - Mock RouterSettingsAccordion with a small test stand-in that exposes the value prop and lets tests trigger an onChange. - Verify the section renders only when an accessToken is present, initializes from keyData.router_settings, flows user edits through onSubmit, and stays absent from the payload when untouched. Also forceRender the antd Collapse so the underlying form state mounts even when the panel is collapsed (preserves user input across toggle and avoids hidden-DOM test gymnastics). --- .../templates/key_edit_view.test.tsx | 156 ++++++++++++++++++ .../components/templates/key_edit_view.tsx | 1 + 2 files changed, 157 insertions(+) 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: (