import { describe, expect, it } from "vitest"; import { loadReleaseManifest, ReleaseManifestError, } from "../../src/bootstrap/load-release-manifest.js"; const runtime = { build: { buildId: "build-a" }, config: { RELEASE_MANIFEST_URL: "/release-manifest.json", BUILD_ID: "build-a", RELEASE_ID: "release-a", CONFIG_SCHEMA_VERSION: "1", API_CONTRACT_VERSION: "1", }, }; const manifest = { schemaVersion: 1, appVersion: "0.1.0", buildId: "build-a", commitSha: "abc123", configSchemaVersion: "1", apiContractVersion: "1", assetManifestHash: "hash-a", releaseId: "release-a", builtAt: "2026-07-25T00:00:00Z", routeChunks: { "route-home": "assets/home.js" }, }; describe("release manifest boot boundary", () => { it("loads a coherent release tuple", async () => { await expect( loadReleaseManifest( /** @type {Parameters[0]} */ (runtime), { fetcher: async () => new Response(JSON.stringify(manifest)) }, ), ).resolves.toMatchObject({ releaseId: "release-a" }); }); it("fails before mount when release and runtime differ", async () => { await expect( loadReleaseManifest( /** @type {Parameters[0]} */ (runtime), { fetcher: async () => 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[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[0]} */ (runtime), { fetcher: async () => Response.json(malformed) }, ), ).rejects.toBeInstanceOf(ReleaseManifestError); }); });