test(ui): pin the key edit payload paths fed by component state

Most of the key edit payload is bound form fields, and the existing
contract test pins those with a strict equality against a fixed object.
Two keys are not fields: budget_fallbacks and tag_rpm_limit are computed
in the submit handler from React state seeded off the loaded key, so a
rewrite that moves that state can drop them without failing anything.
budget_fallbacks had no coverage at all and tag_rpm_limit only appeared
in the untouched-save constant, where it is an empty map either way.

Pins that stored fallbacks and stored per-tag limits both survive an
untouched save, and that a key with no fallbacks omits the field rather
than sending an empty map, since the backend treats those differently.

Verified by mutation: dropping the budget_fallbacks assignment reddens
the resend case while correctly leaving the omit case green, and
dropping the tag_rpm_limit assignment reddens its own case.
This commit is contained in:
Yuneng Jiang 2026-08-18 17:31:32 -07:00
parent 4493c826e7
commit 5beb78fd1c
No known key found for this signature in database

View file

@ -1777,5 +1777,50 @@ describe("KeyEditView", () => {
});
expect(onSubmitMock.mock.calls[0][0]).toHaveProperty("duration", null);
});
it("resends stored budget fallbacks on an untouched save", async () => {
const onSubmitMock = vi.fn().mockResolvedValue(undefined);
renderForPayload(onSubmitMock, {
...MOCK_KEY_DATA,
budget_fallbacks: { "gpt-4": ["gpt-4o-mini"] },
} as KeyResponse);
await screen.findByRole("button", { name: /save changes/i });
await userEvent.click(screen.getByRole("button", { name: /save changes/i }));
await waitFor(() => {
expect(onSubmitMock).toHaveBeenCalled();
});
expect(onSubmitMock.mock.calls[0][0]).toHaveProperty("budget_fallbacks", { "gpt-4": ["gpt-4o-mini"] });
});
it("omits budget fallbacks entirely for a key that has none", async () => {
const onSubmitMock = vi.fn().mockResolvedValue(undefined);
renderForPayload(onSubmitMock);
await screen.findByRole("button", { name: /save changes/i });
await userEvent.click(screen.getByRole("button", { name: /save changes/i }));
await waitFor(() => {
expect(onSubmitMock).toHaveBeenCalled();
});
expect(onSubmitMock.mock.calls[0][0]).not.toHaveProperty("budget_fallbacks");
});
it("resends the stored per-tag rpm limits on an untouched save", async () => {
const onSubmitMock = vi.fn().mockResolvedValue(undefined);
renderForPayload(onSubmitMock, {
...MOCK_KEY_DATA,
metadata: { ...MOCK_KEY_DATA.metadata, tag_rpm_limit: { "test-tag": 7 } },
} as KeyResponse);
await screen.findByRole("button", { name: /save changes/i });
await userEvent.click(screen.getByRole("button", { name: /save changes/i }));
await waitFor(() => {
expect(onSubmitMock).toHaveBeenCalled();
});
expect(onSubmitMock.mock.calls[0][0]).toHaveProperty("tag_rpm_limit", { "test-tag": 7 });
});
});
});