Files
tech-log-frontend/tests/unit/optimistic-layer-runtime.test.ts
T

252 lines
7.4 KiB
TypeScript

import { QueryClient } from "@tanstack/react-query";
import { describe, expect, it } from "vitest";
import { createOptimisticLayerRuntime } from "../../src/presentation/adapters/query/optimistic-layer-runtime.ts";
import { createRuntimeIdentityRegistry } from "../../src/contracts/query-keys.ts";
function scope() {
let current = true;
const lifetime = new AbortController();
return {
snapshot: {
generation: 1,
fingerprint: "scope-token-00000001",
identities: createRuntimeIdentityRegistry({
tokenFactory: () => crypto.randomUUID(),
}),
signal: lifetime.signal,
isCurrent: () => current,
},
expire: () => {
current = false;
lifetime.abort();
},
};
}
describe("revision-safe optimistic layer runtime", () => {
it.each(["ROLLBACK", "RECONCILE"] as const)(
"rejects a composite-invalid candidate without losing prior %s authority",
(authority) => {
const client = new QueryClient();
const key = ["query", "composite-candidate", authority];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const first = runtime.begin(
key,
"first",
(previous, input) => [...(previous as string[]), input],
selectedScope.snapshot,
);
if (authority === "RECONCILE") first?.markUncertain();
const second = runtime.begin(
key,
"second",
(previous, input) => {
const values = previous as string[];
if (values.includes("first")) {
throw new Error("candidate composes only over the base");
}
return [...values, input];
},
selectedScope.snapshot,
);
expect(second).toBeNull();
expect(client.getQueryData(key)).toEqual(["base", "first"]);
if (authority === "ROLLBACK") {
first?.rollback();
} else {
first?.reconcile("NOT_APPLIED");
}
expect(client.getQueryData(key)).toEqual(["base"]);
},
);
it("writes a prevalidated candidate without invoking its updater twice", () => {
const client = new QueryClient();
const key = ["query", "single-candidate-invocation"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const first = runtime.begin(
key,
"first",
(previous, input) => [...(previous as string[]), input],
selectedScope.snapshot,
);
let candidateCalls = 0;
const second = runtime.begin(
key,
"second",
(previous, input) => {
candidateCalls += 1;
if (candidateCalls > 1) {
throw new Error("candidate updater must not be replayed on admission");
}
return [...(previous as string[]), input];
},
selectedScope.snapshot,
);
expect(second).not.toBeNull();
expect(candidateCalls).toBe(1);
expect(client.getQueryData(key)).toEqual(["base", "first", "second"]);
second?.rollback();
expect(client.getQueryData(key)).toEqual(["base", "first"]);
first?.rollback();
expect(client.getQueryData(key)).toEqual(["base"]);
});
it("removes only the failed layer when commands settle out of order", () => {
const client = new QueryClient();
const key = ["query", "resources"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const append = (previous: unknown, input: string) => [
...(previous as string[]),
input,
];
const first = runtime.begin(
key,
"first",
append,
selectedScope.snapshot,
);
const second = runtime.begin(
key,
"second",
append,
selectedScope.snapshot,
);
expect(client.getQueryData(key)).toEqual(["base", "first", "second"]);
second?.commit();
first?.rollback();
expect(client.getQueryData(key)).toEqual(["base", "second"]);
});
it("reapplies pending layers over an authoritative external cache update", () => {
const client = new QueryClient();
const key = ["query", "resources"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
runtime.begin(
key,
"pending",
(previous, input) => [...(previous as string[]), input],
selectedScope.snapshot,
);
client.setQueryData(key, ["server"]);
expect(client.getQueryData(key)).toEqual(["server", "pending"]);
});
it("keeps later committed layers projected while an earlier effect is uncertain", () => {
const client = new QueryClient();
const key = ["query", "resources"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const append = (previous: unknown, input: string) => [
...(previous as string[]),
input,
];
const first = runtime.begin(
key,
"first",
append,
selectedScope.snapshot,
);
const second = runtime.begin(
key,
"second",
append,
selectedScope.snapshot,
);
first?.markUncertain();
first?.commit();
first?.rollback();
second?.commit();
expect(client.getQueryData(key)).toEqual(["base", "first", "second"]);
first?.reconcile("NOT_APPLIED");
expect(client.getQueryData(key)).toEqual(["base", "second"]);
});
it("collapses uncertain and later committed layers in order when reconciled as applied", () => {
const client = new QueryClient();
const key = ["query", "resources"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const append = (previous: unknown, input: string) => [
...(previous as string[]),
input,
];
const first = runtime.begin(
key,
"first",
append,
selectedScope.snapshot,
);
const second = runtime.begin(
key,
"second",
append,
selectedScope.snapshot,
);
first?.markUncertain();
second?.commit();
first?.reconcile("APPLIED");
expect(client.getQueryData(key)).toEqual(["base", "first", "second"]);
first?.reconcile("NOT_APPLIED");
expect(client.getQueryData(key)).toEqual(["base", "first", "second"]);
});
it("removes scoped data instead of restoring it after scope expiry", () => {
const client = new QueryClient();
const key = ["query", "resources"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const layer = runtime.begin(
key,
"pending",
(previous, input) => [...(previous as string[]), input],
selectedScope.snapshot,
);
selectedScope.expire();
layer?.rollback();
expect(client.getQueryData(key)).toBeUndefined();
});
it("does not restore an uncertain layer after its scope expires", () => {
const client = new QueryClient();
const key = ["query", "resources"];
client.setQueryData(key, ["base"]);
const selectedScope = scope();
const runtime = createOptimisticLayerRuntime(client);
const layer = runtime.begin(
key,
"uncertain",
(previous, input) => [...(previous as string[]), input],
selectedScope.snapshot,
);
layer?.markUncertain();
selectedScope.expire();
layer?.reconcile("APPLIED");
expect(client.getQueryData(key)).toBeUndefined();
});
});