109 lines
2.9 KiB
JavaScript
109 lines
2.9 KiB
JavaScript
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<typeof loadReleaseManifest>[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<typeof loadReleaseManifest>[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<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);
|
|
});
|
|
});
|