import { describe, expect, it } from "vitest"; import { queryClient } from "./queryClient"; /** * The MutationCache seeds `meta.updates` entries into the cache and then skips * exactly those keys when running `meta.invalidates`. Getting that skip wrong * silently reintroduces the refetch it exists to avoid, so it is worth pinning. */ const runOnSuccess = (meta: Record, data: unknown, variables: unknown) => { const handler = (queryClient.getMutationCache() as unknown as { config: { onSuccess?: ( data: unknown, variables: unknown, context: unknown, mutation: { meta?: Record }, ) => void; }; }).config.onSuccess; handler?.(data, variables, undefined, { meta }); }; describe("MutationCache updates/invalidates", () => { it("writes the mutation response into the seeded key and leaves it fresh", () => { const seededKey = ["train-builder", "composition", "t1"] as const; const siblingKey = ["train-builder", "list", {}] as const; queryClient.setQueryData(seededKey, { code: "STALE" }); queryClient.setQueryData(siblingKey, { items: [] }); const response = { code: "FRESH" }; runOnSuccess( { updates: (_v: unknown, d: unknown) => [[seededKey, d]], invalidates: () => [["train-builder"]], }, response, { id: "t1" }, ); // Seeded key holds the response, and was NOT invalidated back to stale. expect(queryClient.getQueryData(seededKey)).toStrictEqual(response); expect(queryClient.getQueryState(seededKey)?.isInvalidated).toBe(false); // Its siblings under the same root still get invalidated. expect(queryClient.getQueryState(siblingKey)?.isInvalidated).toBe(true); }); it("invalidates everything when a mutation declares no updates", () => { const key = ["train-builder", "composition", "t2"] as const; queryClient.setQueryData(key, { code: "X" }); runOnSuccess({ invalidates: () => [["train-builder"]] }, undefined, undefined); expect(queryClient.getQueryState(key)?.isInvalidated).toBe(true); }); });