101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { indexInvalidationRegistry } from "../../src/contracts/query-invalidation.ts";
|
|
import {
|
|
createBoundQueryKey,
|
|
createQueryInvalidationPrefix,
|
|
defineQueryNamespaceIdentity,
|
|
queryNamespaceIdentityKey,
|
|
} from "../../src/contracts/query-keys.ts";
|
|
|
|
describe("query namespace identity", () => {
|
|
it("uses a canonical JSON tuple as the namespace identity key", () => {
|
|
const namespace = defineQueryNamespaceIdentity("reference-resource", 1);
|
|
|
|
expect(queryNamespaceIdentityKey(namespace)).toBe(
|
|
'["reference-resource",1]',
|
|
);
|
|
});
|
|
|
|
it("creates one namespace-first prefix for invalidation and bound keys", () => {
|
|
const namespace = defineQueryNamespaceIdentity("reference-resource", 1);
|
|
const prefix = createQueryInvalidationPrefix(namespace);
|
|
const key = createBoundQueryKey(
|
|
namespace,
|
|
"scope-fingerprint-0001",
|
|
3,
|
|
"identity-token-0001",
|
|
);
|
|
|
|
expect(prefix).toEqual(["query", 2, "reference-resource", 1]);
|
|
expect(key).toEqual([
|
|
"query",
|
|
2,
|
|
"reference-resource",
|
|
1,
|
|
"scope-fingerprint-0001",
|
|
3,
|
|
"identity-token-0001",
|
|
]);
|
|
expect(key.slice(0, 4)).toEqual(prefix);
|
|
expect(Object.isFrozen(prefix)).toBe(true);
|
|
expect(Object.isFrozen(key)).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
["empty", ""],
|
|
["C0 control", "orders\u0000private"],
|
|
["DEL control", "orders\u007fprivate"],
|
|
["81 UTF-8 bytes", "가".repeat(27)],
|
|
])("rejects a $0 namespace ID", (_label, namespaceId) => {
|
|
expect(() => defineQueryNamespaceIdentity(namespaceId, 1)).toThrow(
|
|
/namespace identity is invalid/u,
|
|
);
|
|
});
|
|
|
|
it.each([0, -1, 1.5, Number.MAX_SAFE_INTEGER + 1])(
|
|
"rejects the non-positive-safe namespace version %s",
|
|
(namespaceVersion) => {
|
|
expect(() =>
|
|
defineQueryNamespaceIdentity("reference-resource", namespaceVersion),
|
|
).toThrow(/namespace identity is invalid/u);
|
|
},
|
|
);
|
|
|
|
it("rejects a non-positive definition version before creating a bound key", () => {
|
|
const namespace = defineQueryNamespaceIdentity("reference-resource", 1);
|
|
|
|
expect(() =>
|
|
createBoundQueryKey(
|
|
namespace,
|
|
"scope-fingerprint-0001",
|
|
0,
|
|
"identity-token-0001",
|
|
),
|
|
).toThrow(/definition version is invalid/u);
|
|
});
|
|
});
|
|
|
|
describe("query invalidation registry", () => {
|
|
it.each([
|
|
{
|
|
label: "topic",
|
|
registry: {
|
|
topics: ["orders\u0000private"],
|
|
namespaces: ["orders"],
|
|
edges: [{ topicId: "orders\u0000private", namespace: "orders" }],
|
|
},
|
|
},
|
|
{
|
|
label: "namespace",
|
|
registry: {
|
|
topics: ["orders"],
|
|
namespaces: ["orders\u001fprivate"],
|
|
edges: [{ topicId: "orders", namespace: "orders\u001fprivate" }],
|
|
},
|
|
},
|
|
])("rejects control characters in a registry $label", ({ registry }) => {
|
|
expect(() => indexInvalidationRegistry(registry)).toThrow(/is invalid/u);
|
|
});
|
|
});
|