Files
coder/site/src/api/queries/users.test.ts
T
Jaayden Halko 8650a2ee22 fix: rollback appearance mutation cache updates (#25182)
## Summary

- Add appearance mutation context so optimistic cache updates can be
rolled back on failure.
- Restore previous appearance settings, or remove the optimistic cache
entry when no previous data exists.
- Merge successful server responses back into the appearance cache while
preserving request fields when responses are partial.

## Dependencies

- Stacked on #25076 because the tests and generated types use the new
appearance theme mode fields.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/api/queries/users.test.ts`
- `pnpm -C site lint:types`
- Pre-commit hook passed on the branch commit.
2026-05-15 10:50:29 +01:00

124 lines
3.7 KiB
TypeScript

import { QueryClient } from "react-query";
import { describe, expect, it } from "vitest";
import type {
UpdateUserAppearanceSettingsRequest,
UserAppearanceSettings,
} from "#/api/typesGenerated";
import { myAppearanceKey, updateAppearanceSettings } from "./users";
const appearanceSettings = (
overrides: Partial<UserAppearanceSettings> = {},
): UserAppearanceSettings => ({
theme_preference: "dark-tritan",
theme_mode: "sync",
theme_light: "light-tritan",
theme_dark: "dark-tritan",
terminal_font: "geist-mono",
...overrides,
});
const updateRequest = (
overrides: Partial<UpdateUserAppearanceSettingsRequest> = {},
): UpdateUserAppearanceSettingsRequest => ({
theme_preference: "dark",
theme_mode: "single",
theme_light: "light-tritan",
theme_dark: "dark-tritan",
terminal_font: "fira-code",
...overrides,
});
describe("updateAppearanceSettings", () => {
it("rolls back optimistic appearance updates when the mutation fails", async () => {
const queryClient = new QueryClient();
const previousSettings = appearanceSettings({
theme_light: "light-protan-deuter",
theme_dark: "dark-protan-deuter",
});
const optimisticSettings = updateRequest();
queryClient.setQueryData<UserAppearanceSettings>(
myAppearanceKey,
previousSettings,
);
const mutation = updateAppearanceSettings(queryClient);
const context = await mutation.onMutate?.(optimisticSettings);
expect(queryClient.getQueryData(myAppearanceKey)).toEqual(
optimisticSettings,
);
mutation.onError?.(new Error("failed"), optimisticSettings, context);
expect(queryClient.getQueryData(myAppearanceKey)).toEqual(previousSettings);
});
it("removes optimistic appearance data when rollback has no prior cache", async () => {
const queryClient = new QueryClient();
const optimisticSettings = updateRequest();
const mutation = updateAppearanceSettings(queryClient);
const context = await mutation.onMutate?.(optimisticSettings);
expect(queryClient.getQueryData(myAppearanceKey)).toEqual(
optimisticSettings,
);
mutation.onError?.(new Error("failed"), optimisticSettings, context);
expect(queryClient.getQueryData(myAppearanceKey)).toBeUndefined();
});
it("stores the server response after a successful appearance update", async () => {
const queryClient = new QueryClient();
const optimisticSettings = updateRequest();
const serverSettings = appearanceSettings({
theme_preference: "dark-protan-deuter",
theme_light: "light-protan-deuter",
theme_dark: "dark-protan-deuter",
});
const mutation = updateAppearanceSettings(queryClient);
const context = await mutation.onMutate?.(optimisticSettings);
if (!context) {
throw new Error("expected mutation context");
}
expect(queryClient.getQueryData(myAppearanceKey)).toEqual(
optimisticSettings,
);
mutation.onSuccess?.(serverSettings, optimisticSettings, context);
expect(queryClient.getQueryData(myAppearanceKey)).toEqual(serverSettings);
});
it("keeps patch values when a successful appearance update response is partial", async () => {
const queryClient = new QueryClient();
const optimisticSettings = updateRequest({
theme_mode: "sync",
theme_light: "light-protan-deuter",
theme_dark: "dark-protan-deuter",
});
const serverSettings = {
theme_preference: "dark-tritan",
terminal_font: "jetbrains-mono",
} satisfies Partial<UserAppearanceSettings>;
const mutation = updateAppearanceSettings(queryClient);
const context = await mutation.onMutate?.(optimisticSettings);
if (!context) {
throw new Error("expected mutation context");
}
mutation.onSuccess?.(
serverSettings as UserAppearanceSettings,
optimisticSettings,
context,
);
expect(queryClient.getQueryData(myAppearanceKey)).toEqual({
...optimisticSettings,
...serverSettings,
});
});
});