mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(ui): invalidate keys list after key update so table reflects changes
handleKeyUpdate in key_info_view.tsx updated local component state and called
onKeyDataUpdate after a successful /key/update, but never invalidated the
React Query keys list -- unlike handleDelete in the same file, which already
calls queryClient.invalidateQueries({ queryKey: keyKeys.lists() }).
As a result, editing a key (e.g. daily budget / budget reset) and returning
to the keys table showed stale cached values: the change looked like it "didn't
save" even though /key/update had persisted it and refreshed the runtime key
cache on the backend.
Fix: invalidate keyKeys.lists() after a successful update, mirroring the
delete path. Adds a regression test that fails before the fix (spy never
called with { queryKey: ['keys','list'] }) and passes after.
This commit is contained in:
parent
b79fc9f1b0
commit
d3ea03699f
2 changed files with 27 additions and 4 deletions
|
|
@ -7,11 +7,12 @@ vi.mock("@/lib/toast", () => ({
|
|||
}));
|
||||
|
||||
// ---- Hoisted shared mocks (safe to use inside vi.mock factories) ----
|
||||
const { keyUpdateCallMock, keyDeleteCallMock, mockUseAuthorized } = vi.hoisted(() => {
|
||||
const { keyUpdateCallMock, keyDeleteCallMock, mockUseAuthorized, invalidateQueriesMock } = vi.hoisted(() => {
|
||||
return {
|
||||
keyUpdateCallMock: vi.fn().mockResolvedValue({}),
|
||||
keyDeleteCallMock: vi.fn().mockResolvedValue({}),
|
||||
mockUseAuthorized: vi.fn(),
|
||||
invalidateQueriesMock: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -165,12 +166,11 @@ vi.mock("@/app/(dashboard)/hooks/keys/useSetKeyBlockedState", () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
// useQueryClient also needs a provider; the delete-path invalidation is covered in key_info_view.test.tsx
|
||||
vi.mock("@tanstack/react-query", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tanstack/react-query")>();
|
||||
return {
|
||||
...actual,
|
||||
useQueryClient: () => ({ invalidateQueries: vi.fn() }),
|
||||
useQueryClient: () => ({ invalidateQueries: invalidateQueriesMock }),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -196,6 +196,7 @@ vi.mock("./key_edit_view", async () => {
|
|||
|
||||
// ---- SUT import AFTER mocks ----
|
||||
import KeyInfoView from "./key_info_view";
|
||||
import { keyKeys } from "@/app/(dashboard)/hooks/keys/useKeys";
|
||||
|
||||
// ---- Test data helpers ----
|
||||
const baseKeyData = {
|
||||
|
|
@ -528,6 +529,28 @@ describe("KeyInfoView handleKeyUpdate empty strings", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("KeyInfoView handleKeyUpdate keys-list invalidation", () => {
|
||||
it("invalidates the keys list query after a successful update so the table reflects the change", async () => {
|
||||
renderView(true);
|
||||
|
||||
fireEvent.click(screen.getByText("Settings"));
|
||||
fireEvent.click(screen.getByText("Edit Settings"));
|
||||
(globalThis as any).__TEST_FORM_VALUES = {
|
||||
token: "tok_123",
|
||||
max_budget: 60,
|
||||
budget_duration: "24h",
|
||||
};
|
||||
|
||||
fireEvent.click(screen.getByText("Mock Submit"));
|
||||
|
||||
await waitFor(() => expect(keyUpdateCallMock).toHaveBeenCalled());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(invalidateQueriesMock).toHaveBeenCalledWith({ queryKey: keyKeys.lists() });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("KeyInfoView handleKeyUpdate soft_budget", () => {
|
||||
const premiumAdminAuth = {
|
||||
accessToken: "access_abc",
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ export default function KeyInfoView({
|
|||
}
|
||||
toast.success("Key updated successfully");
|
||||
setIsEditing(false);
|
||||
// Refresh key data here if needed
|
||||
await queryClient.invalidateQueries({ queryKey: keyKeys.lists() });
|
||||
} catch (error) {
|
||||
toast.fromError(parseErrorMessage(error));
|
||||
console.error("Error updating key:", error);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue