feat: execute route and release recovery contracts

This commit is contained in:
donghyeon-ka
2026-07-26 14:26:39 +09:00
parent a33e93d4d4
commit ce0040e407
49 changed files with 1761 additions and 373 deletions
@@ -25,6 +25,7 @@ const manifest = {
assetManifestHash: "hash-a",
releaseId: "release-a",
builtAt: "2026-07-25T00:00:00Z",
routeChunks: { "route-home": "assets/home.js" },
};
describe("release manifest boot boundary", () => {
@@ -46,6 +47,62 @@ describe("release manifest boot boundary", () => {
new Response(JSON.stringify({ ...manifest, buildId: "build-b" })),
},
),
).rejects.toMatchObject({
kind: "BUILD_MISMATCH",
code: "MANIFEST_BUILD_MISMATCH",
});
});
it.each([
[
{ configSchemaVersion: "2" },
{},
"CONFIG_MISMATCH",
"MANIFEST_CONFIG_SCHEMA_MISMATCH",
],
[
{ apiContractVersion: "2" },
{},
"API_CONTRACT_MISMATCH",
"MANIFEST_API_CONTRACT_MISMATCH",
],
[
{ releaseId: "release-b" },
{},
"RELEASE_MISMATCH",
"MANIFEST_RELEASE_MISMATCH",
],
[
{},
{ expectedAssetManifestHash: "different" },
"ASSET_MISMATCH",
"MANIFEST_ASSET_MISMATCH",
],
])(
"classifies tuple mismatch %# without a generic deploy error",
async (manifestOverride, options, kind, code) => {
await expect(
loadReleaseManifest(
/** @type {Parameters<typeof loadReleaseManifest>[0]} */ (runtime),
{
fetcher: async () =>
Response.json({ ...manifest, ...manifestOverride }),
...options,
},
),
).rejects.toMatchObject({ kind, code });
},
);
it("rejects a manifest without a complete route chunk map", async () => {
const malformed = Object.fromEntries(
Object.entries(manifest).filter(([key]) => key !== "routeChunks"),
);
await expect(
loadReleaseManifest(
/** @type {Parameters<typeof loadReleaseManifest>[0]} */ (runtime),
{ fetcher: async () => Response.json(malformed) },
),
).rejects.toBeInstanceOf(ReleaseManifestError);
});
});