diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx
index fe69257e21c..dca6c04ec48 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx
@@ -464,7 +464,7 @@ describe("UserEditView", () => {
user_info: {
...MOCK_USER_DATA.user_info,
tpm_limit: 100000,
- rpm_limit: null,
+ rpm_limit: 50,
},
});
@@ -472,7 +472,30 @@ describe("UserEditView", () => {
renderWithProviders();
expect(await screen.findByRole("spinbutton", { name: /tpm limit/i })).toHaveValue(100000);
+ expect(await screen.findByRole("spinbutton", { name: /rpm limit/i })).toHaveValue(50);
+ });
+
+ it("keeps unset rate limits empty and omits them from an untouched save", async () => {
+ const onSubmit = vi.fn();
+ const userDataWithNullRateLimits = {
+ ...MOCK_USER_DATA,
+ user_info: {
+ ...MOCK_USER_DATA.user_info,
+ tpm_limit: null,
+ rpm_limit: null,
+ },
+ };
+ renderWithProviders();
+
+ expect(await screen.findByRole("spinbutton", { name: /tpm limit/i })).toHaveValue(null);
expect(await screen.findByRole("spinbutton", { name: /rpm limit/i })).toHaveValue(null);
+ await userEvent.click(screen.getByRole("button", { name: /save changes/i }));
+
+ await waitFor(() => {
+ expect(onSubmit).toHaveBeenCalled();
+ });
+ expect(onSubmit.mock.calls[0][0]).not.toHaveProperty("tpm_limit");
+ expect(onSubmit.mock.calls[0][0]).not.toHaveProperty("rpm_limit");
});
it("omits unchanged rate limits from the submit payload", async () => {
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx
index 1b062c3d05f..3eae1dbdfc5 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen, waitFor, within } from "@testing-library/react";
+import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi, beforeEach } from "vitest";
import UserInfoView from "./user_info_view";
@@ -134,13 +134,36 @@ describe("UserInfoView", () => {
mockUserGetInfoV2.mockResolvedValue({
...MOCK_USER_DATA,
tpm_limit: 100000,
- rpm_limit: null,
+ rpm_limit: 50,
});
render();
expect(await screen.findByRole("spinbutton", { name: /tpm limit/i })).toHaveValue(100000);
- expect(await screen.findByRole("spinbutton", { name: /rpm limit/i })).toHaveValue(null);
+ expect(await screen.findByRole("spinbutton", { name: /rpm limit/i })).toHaveValue(50);
+ });
+
+ it("keeps the updated TPM and stored RPM when reopening the edit form", async () => {
+ mockUserGetInfoV2.mockResolvedValue({
+ ...MOCK_USER_DATA,
+ tpm_limit: 100000,
+ rpm_limit: 50,
+ });
+
+ render();
+
+ fireEvent.change(await screen.findByRole("spinbutton", { name: /tpm limit/i }), {
+ target: { value: "" },
+ });
+ await userEvent.click(screen.getByRole("button", { name: /save changes/i }));
+ await waitFor(() => {
+ expect(mockUserUpdateUserCall).toHaveBeenCalledTimes(1);
+ });
+
+ await userEvent.click(await screen.findByRole("button", { name: /edit settings/i }));
+
+ expect(await screen.findByRole("spinbutton", { name: /tpm limit/i })).toHaveValue(null);
+ expect(await screen.findByRole("spinbutton", { name: /rpm limit/i })).toHaveValue(50);
});
it("should render overview spend and budget with two decimal places", async () => {