import { QueryClient } from "@tanstack/react-query"; import { describe, expect, it, vi } from "vitest"; import { createQueryCacheAdapter, createQueryClient, } from "../../src/adapters/query-cache/tanstack-query-cache.js"; import { canonicalize } from "../../src/contracts/query-keys.js"; const queryKeys = Object.freeze({ all: () => Object.freeze(["entity", 1]), list: (filters = {}) => Object.freeze(["entity", 1, "list", canonicalize(filters)]), /** @param {string} entityId */ detail: (entityId) => Object.freeze(["entity", 1, "detail", String(entityId)]), }); describe("query key registry", () => { it("canonicalizes filter order into the same stable key", () => { expect(queryKeys.list({ page: 1, status: "open" })).toEqual( queryKeys.list({ status: "open", page: 1 }), ); }); it("contains no raw URL or token material", () => { expect(JSON.stringify(queryKeys.detail("entity-1"))).toBe( '["entity",1,"detail","entity-1"]', ); }); }); describe("TanStack QueryCachePort adapter", () => { it("reads, writes, and invalidates only the declared namespace", async () => { const client = createQueryClient(); const adapter = createQueryCacheAdapter(client); const listKey = queryKeys.list({ page: 1 }); const otherKey = ["other", 1]; expect(adapter.write(listKey, [{ id: "resource-1" }])).toEqual({ ok: true }); expect(adapter.write(otherKey, "preserved")).toEqual({ ok: true }); expect(adapter.read(listKey)).toMatchObject({ ok: true, value: [{ id: "resource-1" }], }); await adapter.invalidate(queryKeys.all()); expect(client.getQueryState(listKey)?.isInvalidated).toBe(true); expect(client.getQueryState(otherKey)?.isInvalidated).toBe(false); }); it("normalizes adapter exceptions without raw key data", async () => { const client = new QueryClient(); vi.spyOn(client, "invalidateQueries").mockRejectedValue(new Error("secret-key")); const record = vi.fn(); const adapter = createQueryCacheAdapter(client, { diagnostics: { record }, }); const result = await adapter.invalidate(["resource", "sensitive-filter"]); expect(result).toMatchObject({ ok: false, error: { kind: "QUERY_CACHE_FAILURE" }, }); expect(JSON.stringify(result)).not.toContain("sensitive-filter"); expect(record).toHaveBeenCalledWith({ level: "warn", eventId: "cache.operation.failed", context: { operation: "invalidate", error_kind: "QUERY_CACHE_FAILURE", }, }); expect(JSON.stringify(record.mock.calls)).not.toMatch( /sensitive-filter|secret-key/, ); }); });