fix: harden invalidation registry governance
This commit is contained in:
@@ -4,14 +4,6 @@ import {
|
||||
createRuntimeAdapters,
|
||||
createRuntimeHttpClient,
|
||||
} from "../../src/bootstrap/runtime-adapters.ts";
|
||||
import { INVALIDATION_REGISTRY } from "../../src/features/installed-feature-contracts.ts";
|
||||
import {
|
||||
REFERENCE_FEATURE_ID,
|
||||
REFERENCE_RESOURCE_INVALIDATION_TOPIC,
|
||||
} from "../../src/features/reference-feature/contracts/reference-feature-contract.ts";
|
||||
import { createRuntimeIdentityRegistry } from "../../src/contracts/query-keys.ts";
|
||||
import { bindQuery } from "../../src/contracts/server-state.ts";
|
||||
import type { CacheScopeSnapshot } from "../../src/contracts/server-state-scope.ts";
|
||||
import { TEST_HTTP_CONTRACT } from "../helpers/http-contract-fixture.ts";
|
||||
|
||||
type Runtime = Parameters<typeof createRuntimeAdapters>[0]["runtime"];
|
||||
@@ -60,33 +52,6 @@ const release: Release = {
|
||||
routeChunks: { "route-home": "assets/home.js" },
|
||||
};
|
||||
|
||||
function referenceBoundQueryKey() {
|
||||
const scope: CacheScopeSnapshot = {
|
||||
generation: 1,
|
||||
fingerprint: "runtime-scope-fingerprint-0001",
|
||||
identities: createRuntimeIdentityRegistry({
|
||||
tokenFactory: () => "runtime-identity-token-0001",
|
||||
}),
|
||||
signal: new AbortController().signal,
|
||||
isCurrent: () => true,
|
||||
};
|
||||
return bindQuery(
|
||||
{
|
||||
definitionId: "reference-resource-runtime-test-v1",
|
||||
definitionVersion: 1,
|
||||
owner: REFERENCE_FEATURE_ID,
|
||||
namespace: "reference-resource",
|
||||
namespaceVersion: 1,
|
||||
operationId: "GET_REFERENCE_RESOURCE",
|
||||
profileId: "DETAIL_STANDARD",
|
||||
measureResult: () => ({ itemCount: 1, estimatedBytes: 8 }),
|
||||
execute: async () => ({ ok: true as const, value: "reference-1" }),
|
||||
},
|
||||
"reference-1",
|
||||
scope,
|
||||
).queryKey;
|
||||
}
|
||||
|
||||
describe("runtime adapter composition", () => {
|
||||
it("constructs the local demo seam and infrastructure adapters", async () => {
|
||||
const adapters = await createRuntimeAdapters({
|
||||
@@ -112,66 +77,11 @@ describe("runtime adapter composition", () => {
|
||||
adapters.infrastructure.dispose();
|
||||
});
|
||||
|
||||
it("invalidates a real bound query through the installed production graph", async () => {
|
||||
const adapters = await createRuntimeAdapters({ runtime, release, host: {} });
|
||||
const queryKey = referenceBoundQueryKey();
|
||||
adapters.infrastructure.queryClient.setQueryData(queryKey, {
|
||||
resourceId: "reference-1",
|
||||
});
|
||||
|
||||
await adapters.infrastructure.queryInvalidation.invalidate([
|
||||
REFERENCE_RESOURCE_INVALIDATION_TOPIC,
|
||||
]);
|
||||
|
||||
expect(
|
||||
adapters.infrastructure.queryClient.getQueryState(queryKey)?.isInvalidated,
|
||||
).toBe(true);
|
||||
adapters.infrastructure.dispose();
|
||||
});
|
||||
|
||||
it("executes installed feature HTTP through the composed contract registry", async () => {
|
||||
const fetcher = vi.fn(async () =>
|
||||
Response.json([{ id: "reference-1", name: "Direct contract payload" }]),
|
||||
);
|
||||
const adapters = await createRuntimeAdapters({
|
||||
runtime,
|
||||
release,
|
||||
host: {},
|
||||
fetcher,
|
||||
});
|
||||
await adapters.outputPorts.session.beginSignIn();
|
||||
await vi.waitFor(() =>
|
||||
expect(adapters.infrastructure.serverStateScope.getPhase()).toBe("READY"),
|
||||
);
|
||||
|
||||
await expect(
|
||||
adapters.featureInputs[REFERENCE_FEATURE_ID].listResources({ limit: 20 }),
|
||||
).resolves.toEqual({
|
||||
ok: true,
|
||||
value: [
|
||||
{
|
||||
resourceId: "reference-1",
|
||||
title: "Direct contract payload",
|
||||
createdAt: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(fetcher).toHaveBeenCalledWith(
|
||||
"http://localhost:8080/api/reference-resources?limit=20",
|
||||
expect.objectContaining({
|
||||
method: "GET",
|
||||
redirect: "error",
|
||||
cache: "no-store",
|
||||
}),
|
||||
);
|
||||
adapters.infrastructure.dispose();
|
||||
});
|
||||
|
||||
it("replaces the QueryClient and coordinator for each session generation", async () => {
|
||||
const adapters = await createRuntimeAdapters({ runtime, release, host: {} });
|
||||
const previousClient = adapters.infrastructure.queryClient;
|
||||
const previousCoordinator = adapters.infrastructure.queryInvalidation;
|
||||
const invalidatePrevious = vi.spyOn(previousClient, "invalidateQueries");
|
||||
const clearPrevious = vi.spyOn(previousClient, "clear");
|
||||
|
||||
await adapters.outputPorts.session.beginSignIn();
|
||||
|
||||
@@ -182,10 +92,9 @@ describe("runtime adapter composition", () => {
|
||||
previousCoordinator,
|
||||
);
|
||||
|
||||
const topic = INVALIDATION_REGISTRY.topics[0];
|
||||
if (!topic) throw new Error("expected an installed invalidation topic");
|
||||
await previousCoordinator.invalidate([topic]);
|
||||
expect(invalidatePrevious).not.toHaveBeenCalled();
|
||||
const clearCallsAfterReplacement = clearPrevious.mock.calls.length;
|
||||
await previousCoordinator.resetLocal();
|
||||
expect(clearPrevious).toHaveBeenCalledTimes(clearCallsAfterReplacement);
|
||||
|
||||
adapters.infrastructure.dispose();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user