74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
canonicalize,
|
|
createRuntimeIdentityRegistry,
|
|
runtimeIdentityToken,
|
|
} from "../../src/contracts/query-keys.ts";
|
|
|
|
const sparseValue = Array<string>(2);
|
|
sparseValue[1] = "sparse";
|
|
|
|
describe("strict server-state identity codec", () => {
|
|
it("is stable across plain-object key ordering without exposing input in the token", () => {
|
|
const left = { limit: 20, filters: { state: "open" } };
|
|
const right = { filters: { state: "open" }, limit: 20 };
|
|
expect(canonicalize(left)).toEqual(canonicalize(right));
|
|
const token = runtimeIdentityToken(left);
|
|
expect(token).toBe(runtimeIdentityToken(right));
|
|
expect(token).not.toContain("open");
|
|
});
|
|
|
|
it.each([
|
|
{ value: { missing: undefined }, label: "undefined" },
|
|
{ value: { number: Number.NaN }, label: "NaN" },
|
|
{ value: { date: new Date() }, label: "Date" },
|
|
{ value: sparseValue, label: "sparse array" },
|
|
])("rejects $label", ({ value }) => {
|
|
expect(() => canonicalize(value)).toThrow();
|
|
});
|
|
|
|
it("rejects cycles and shared-reference ambiguity", () => {
|
|
const shared = {};
|
|
expect(() => canonicalize({ left: shared, right: shared })).toThrow();
|
|
const cycle: Record<string, unknown> = {};
|
|
cycle.self = cycle;
|
|
expect(() => canonicalize(cycle)).toThrow();
|
|
});
|
|
|
|
it("keeps active identity leases non-evictable and evicts released LRU rows", () => {
|
|
let sequence = 0;
|
|
const registry = createRuntimeIdentityRegistry({
|
|
maxEntries: 1,
|
|
maxCanonicalBytes: 1_024,
|
|
tokenFactory: () => `identity-token-${sequence++}`,
|
|
});
|
|
const first = registry.intern({ resource: "first" });
|
|
first.acquire();
|
|
expect(() => registry.intern({ resource: "second" })).toThrow(
|
|
"capacity exceeded",
|
|
);
|
|
expect(registry.inspect().activeLeases).toBe(1);
|
|
|
|
first.release();
|
|
const second = registry.intern({ resource: "second" });
|
|
expect(second.token).not.toBe(first.token);
|
|
expect(registry.inspect()).toMatchObject({
|
|
entries: 1,
|
|
activeLeases: 0,
|
|
closed: false,
|
|
});
|
|
});
|
|
|
|
it("fails closed when token collisions cannot be resolved", () => {
|
|
const registry = createRuntimeIdentityRegistry({
|
|
maxEntries: 2,
|
|
tokenFactory: () => "identity-token-fixed",
|
|
});
|
|
registry.intern({ resource: "first" });
|
|
expect(() => registry.intern({ resource: "second" })).toThrow(
|
|
"token collision",
|
|
);
|
|
});
|
|
});
|