Files
cloudflare__vinext/tests/operation-token.test.ts
T
Nathan Nguyen 5b3ea11153 fix(navigation): promote OperationToken to the eligibility authority (#2009)
The App Router client carried an OperationToken on every navigation
decision but only read its `lane` field. The authority the token was
meant to own lived inline elsewhere: the commit-staleness gate was a raw
boolean (startedNavigationId !== activeNavigationId or a visible-commit
version mismatch) evaluated before the token was even built, and cache
reuse was gated separately. The token also lacked the lifecycle
navigation id, so it could not answer "does this result belong to the
active navigation?" at all — its operationId is a per-render counter.

Promote OperationToken to the single proof-of-eligibility object. A new
operation-token.ts module owns the token type and verifyOperationToken,
a pure verifier over four dimensions (active-navigation, visible-commit,
graph-version, cache-variant) that returns a branded VerifiedOperationToken
on success. The token verifies; ApprovedVisibleCommit still mutates — the
two stay separate.

- Add navigationId to the token (sourced from startedNavigationId) so it
  can carry the active-navigation authority.
- Route the commit-staleness gate through verifyOperationTokenForCommit
  and delete the inline boolean. The staleOperation skip + trace are
  byte-identical; planPendingRootBoundaryFlightResponse now requires the
  VerifiedOperationToken brand, making "verify before plan" a compile-time
  guarantee.
- Share the authority with cache reuse: planFlightResponseArrived gates an
  accepted cache-entry reuse decision through verifyOperationTokenForCacheReuse,
  hard-navigating (cacheReuseTokenRejected) when the proof's graph version no
  longer matches the installed route graph. Behavior-preserving today (the
  token's graphVersion is minted from the same manifest); a real guard once
  cross-document and segment reuse can diverge (PR 6/7).

Absence is not permission: a checked dimension tolerates an absent
authority fact, but a *required* one fails closed, and require implies
evaluation — the contract segment-cache/BFCache writes need in PR 6/7.

Tests: operation-token.test.ts covers every verdict dimension, the brand,
the absent/mismatch split, and require⇒evaluate; navigation-planner and
app-browser-entry suites stay green, proving the gate refactor preserved
behavior; two new planner tests cover the cache-reuse graph-version seam.
2026-06-15 23:21:38 +01:00

196 lines
7.1 KiB
TypeScript

import { describe, expect, it } from "vite-plus/test";
import {
verifyOperationToken,
verifyOperationTokenForCacheReuse,
verifyOperationTokenForCommit,
type OperationToken,
type OperationTokenAuthority,
type VerifiedOperationToken,
} from "../packages/vinext/src/server/operation-token.js";
function createToken(overrides: Partial<OperationToken> = {}): OperationToken {
return {
operationId: 7,
lane: "navigation",
navigationId: 3,
baseVisibleCommitVersion: 2,
graphVersion: "graph:test",
deploymentVersion: null,
targetSnapshotFingerprint: "route:/dashboard|root:/",
...overrides,
};
}
function createAuthority(
overrides: Partial<OperationTokenAuthority> = {},
): OperationTokenAuthority {
return {
activeNavigationId: 3,
visibleCommitVersion: 2,
graphVersion: "graph:test",
installedCacheVariantFingerprint: null,
...overrides,
};
}
// Compile-time assertion that the verified token narrows to the branded type.
function consumeVerified(_token: VerifiedOperationToken): void {}
describe("verifyOperationTokenForCommit", () => {
it("authorizes when the navigation and visible-commit dimensions match", () => {
const token = createToken();
const verdict = verifyOperationTokenForCommit(token, createAuthority());
expect(verdict.authorized).toBe(true);
if (verdict.authorized) {
// Same evidence object, now carrying the verified brand.
expect(verdict.token).toBe(token);
consumeVerified(verdict.token);
}
});
it("rejects staleNavigation when the token started under a superseded navigation", () => {
const verdict = verifyOperationTokenForCommit(
createToken({ navigationId: 3 }),
createAuthority({ activeNavigationId: 4 }),
);
expect(verdict).toEqual({ authorized: false, reason: "staleNavigation" });
});
it("rejects staleVisibleCommit when visible state advanced after the operation started", () => {
const verdict = verifyOperationTokenForCommit(
createToken({ baseVisibleCommitVersion: 2 }),
createAuthority({ visibleCommitVersion: 3 }),
);
expect(verdict).toEqual({ authorized: false, reason: "staleVisibleCommit" });
});
it("reports staleNavigation first when both navigation and visible commit diverge", () => {
const verdict = verifyOperationTokenForCommit(
createToken({ navigationId: 3, baseVisibleCommitVersion: 2 }),
createAuthority({ activeNavigationId: 4, visibleCommitVersion: 3 }),
);
expect(verdict).toEqual({ authorized: false, reason: "staleNavigation" });
});
it("does not gate commits on the cache-variant or graph-version dimensions", () => {
// A commit eligibility check must not reject on cache/graph divergence; that
// authority belongs to the cache-reuse boundary and the RSC compatibility path.
const verdict = verifyOperationTokenForCommit(
createToken({ graphVersion: "graph:stale", cacheVariantFingerprint: "cv:stale" }),
createAuthority({
graphVersion: "graph:fresh",
installedCacheVariantFingerprint: "cv:fresh",
}),
);
expect(verdict.authorized).toBe(true);
});
});
describe("verifyOperationTokenForCacheReuse", () => {
it("authorizes reuse when the graph version matches the installed graph", () => {
const verdict = verifyOperationTokenForCacheReuse(createToken({ graphVersion: "graph:a" }), {
graphVersion: "graph:a",
installedCacheVariantFingerprint: null,
});
expect(verdict.authorized).toBe(true);
});
it("rejects graphVersionMismatch when the proof was produced under a different graph", () => {
const verdict = verifyOperationTokenForCacheReuse(createToken({ graphVersion: "graph:a" }), {
graphVersion: "graph:b",
installedCacheVariantFingerprint: null,
});
expect(verdict).toEqual({ authorized: false, reason: "graphVersionMismatch" });
});
it("tolerates an absent graph version rather than rejecting low-context reuse", () => {
const verdict = verifyOperationTokenForCacheReuse(createToken({ graphVersion: null }), {
graphVersion: null,
installedCacheVariantFingerprint: null,
});
expect(verdict.authorized).toBe(true);
});
it("rejects cacheVariantMismatch when the installed variant differs from the proof", () => {
const verdict = verifyOperationTokenForCacheReuse(
createToken({ graphVersion: "graph:a", cacheVariantFingerprint: "cv:a" }),
{ graphVersion: "graph:a", installedCacheVariantFingerprint: "cv:b" },
);
expect(verdict).toEqual({ authorized: false, reason: "cacheVariantMismatch" });
});
it("tolerates an absent cache-variant fingerprint until segment cache supplies it", () => {
const verdict = verifyOperationTokenForCacheReuse(
createToken({ graphVersion: "graph:a", cacheVariantFingerprint: undefined }),
{ graphVersion: "graph:a", installedCacheVariantFingerprint: "cv:b" },
);
expect(verdict.authorized).toBe(true);
});
it("reports graphVersionMismatch before cacheVariantMismatch when both diverge", () => {
const verdict = verifyOperationTokenForCacheReuse(
createToken({ graphVersion: "graph:a", cacheVariantFingerprint: "cv:a" }),
{ graphVersion: "graph:b", installedCacheVariantFingerprint: "cv:b" },
);
expect(verdict).toEqual({ authorized: false, reason: "graphVersionMismatch" });
});
});
describe("verifyOperationToken required dimensions", () => {
it("fails closed when a required dimension's authority fact is absent", () => {
// Future segment-cache writes will require the cache-variant dimension. A
// forgotten fingerprint must reject, not silently pass: absence is not
// permission.
const verdict = verifyOperationToken(
createToken({ cacheVariantFingerprint: undefined }),
createAuthority({ installedCacheVariantFingerprint: null }),
{ check: ["cacheVariant"], require: ["cacheVariant"] },
);
expect(verdict).toEqual({ authorized: false, reason: "cacheVariantMissing" });
});
it("rejects a required-but-absent graph version with graphVersionMissing", () => {
const verdict = verifyOperationToken(
createToken({ graphVersion: null }),
createAuthority({ graphVersion: "graph:test" }),
{ check: ["graphVersion"], require: ["graphVersion"] },
);
expect(verdict).toEqual({ authorized: false, reason: "graphVersionMissing" });
});
it("skips an unchecked dimension entirely even when it diverges", () => {
const verdict = verifyOperationToken(
createToken({ graphVersion: "graph:a" }),
createAuthority({ graphVersion: "graph:b" }),
{ check: ["navigation"], require: ["navigation"] },
);
expect(verdict.authorized).toBe(true);
});
it("evaluates a required dimension even when it is omitted from check", () => {
// require implies evaluation: a dimension you require but forget to list in
// check must still be verified, never silently waved through.
const verdict = verifyOperationToken(
createToken({ graphVersion: "graph:a" }),
createAuthority({ graphVersion: "graph:b" }),
{ check: ["navigation"], require: ["graphVersion"] },
);
expect(verdict).toEqual({ authorized: false, reason: "graphVersionMismatch" });
});
});