refactor: 리펙토링
This commit is contained in:
@@ -4,6 +4,9 @@ import {
|
||||
loadReleaseManifest,
|
||||
ReleaseManifestError,
|
||||
} from "../../src/bootstrap/load-release-manifest.ts";
|
||||
import { computeContractSetDigest } from "../../src/contracts/contract-set-canonical.ts";
|
||||
|
||||
const EMPTY_SET_DIGEST = await computeContractSetDigest([]);
|
||||
|
||||
const runtime: Parameters<typeof loadReleaseManifest>[0] = {
|
||||
build: {
|
||||
@@ -22,12 +25,45 @@ const runtime: Parameters<typeof loadReleaseManifest>[0] = {
|
||||
RELEASE_MANIFEST_URL: "/release-manifest.json",
|
||||
BUILD_ID: "build-a",
|
||||
RELEASE_ID: "release-a",
|
||||
CONFIG_SCHEMA_VERSION: "1",
|
||||
API_CONTRACT_VERSION: "1",
|
||||
CONFIG_SCHEMA_VERSION: "2.0",
|
||||
CAPABILITY_OVERRIDES: {
|
||||
REALTIME: "DEFAULT",
|
||||
WEB_WORKER: "DEFAULT",
|
||||
SERVICE_WORKER: "DEFAULT",
|
||||
OFFLINE_COMMANDS: "DEFAULT",
|
||||
},
|
||||
},
|
||||
configSchema: "V2",
|
||||
validationDurationMs: 0,
|
||||
};
|
||||
const manifest = {
|
||||
schemaVersion: 2,
|
||||
appVersion: "0.1.0",
|
||||
buildId: "build-a",
|
||||
commitSha: "abc123",
|
||||
configSchemaVersion: "2.0",
|
||||
assetManifestHash: "hash-a",
|
||||
releaseId: "release-a",
|
||||
builtAt: "2026-07-25T00:00:00Z",
|
||||
routeChunks: { "route-home": "assets/home.js" },
|
||||
contractSet: {
|
||||
setAlgorithm: "CA_CONTRACT_SET_V1",
|
||||
setDigest: EMPTY_SET_DIGEST,
|
||||
packages: [],
|
||||
},
|
||||
};
|
||||
|
||||
const runtimeV1: Parameters<typeof loadReleaseManifest>[0] = {
|
||||
...runtime,
|
||||
config: {
|
||||
...runtime.config,
|
||||
CONFIG_SCHEMA_VERSION: "1",
|
||||
LEGACY_API_CONTRACT_VERSION: "1",
|
||||
},
|
||||
configSchema: "V1",
|
||||
};
|
||||
|
||||
const manifestV1 = {
|
||||
schemaVersion: 1,
|
||||
appVersion: "0.1.0",
|
||||
buildId: "build-a",
|
||||
@@ -40,12 +76,18 @@ const manifest = {
|
||||
routeChunks: { "route-home": "assets/home.js" },
|
||||
};
|
||||
|
||||
function jsonResponse(body: unknown): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("release manifest boot boundary", () => {
|
||||
it("loads a coherent release tuple", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtime,
|
||||
{ fetcher: async () => new Response(JSON.stringify(manifest)) },
|
||||
{ fetcher: async () => jsonResponse(manifest) },
|
||||
),
|
||||
).resolves.toMatchObject({ releaseId: "release-a" });
|
||||
});
|
||||
@@ -56,7 +98,7 @@ describe("release manifest boot boundary", () => {
|
||||
runtime,
|
||||
{
|
||||
fetcher: async () =>
|
||||
new Response(JSON.stringify({ ...manifest, buildId: "build-b" })),
|
||||
jsonResponse({ ...manifest, buildId: "build-b" }),
|
||||
},
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
@@ -66,18 +108,6 @@ describe("release manifest boot boundary", () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
[
|
||||
{ configSchemaVersion: "2" },
|
||||
{},
|
||||
"CONFIG_MISMATCH",
|
||||
"MANIFEST_CONFIG_SCHEMA_MISMATCH",
|
||||
],
|
||||
[
|
||||
{ apiContractVersion: "2" },
|
||||
{},
|
||||
"API_CONTRACT_MISMATCH",
|
||||
"MANIFEST_API_CONTRACT_MISMATCH",
|
||||
],
|
||||
[
|
||||
{ releaseId: "release-b" },
|
||||
{},
|
||||
@@ -98,7 +128,7 @@ describe("release manifest boot boundary", () => {
|
||||
runtime,
|
||||
{
|
||||
fetcher: async () =>
|
||||
Response.json({ ...manifest, ...manifestOverride }),
|
||||
jsonResponse({ ...manifest, ...manifestOverride }),
|
||||
...options,
|
||||
},
|
||||
),
|
||||
@@ -106,6 +136,98 @@ describe("release manifest boot boundary", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["3.0", "9.0"])(
|
||||
"rejects unsupported V2 manifest config version %s at the schema boundary",
|
||||
async (configSchemaVersion) => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtime, {
|
||||
fetcher: async () =>
|
||||
jsonResponse({ ...manifest, configSchemaVersion }),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "RELEASE_MANIFEST_FAILURE",
|
||||
code: "MANIFEST_SCHEMA_INVALID",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects a contract set the build did not compile", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtime, {
|
||||
fetcher: async () =>
|
||||
jsonResponse({
|
||||
...manifest,
|
||||
contractSet: {
|
||||
setAlgorithm: "CA_CONTRACT_SET_V1",
|
||||
setDigest: EMPTY_SET_DIGEST,
|
||||
packages: [
|
||||
{
|
||||
packageId: "@org-contracts/worklog",
|
||||
version: "1.2.3",
|
||||
digest: `sha256:${"a".repeat(64)}`,
|
||||
runtimeProtocolVersion: 1,
|
||||
sourceRevision: "abc1234",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "CONTRACT_SET_MISMATCH",
|
||||
code: "CONTRACT_SET_PACKAGE_UNEXPECTED",
|
||||
});
|
||||
});
|
||||
|
||||
it("still reads a V1 manifest during the compatibility window", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtimeV1,
|
||||
{
|
||||
fetcher: async () => jsonResponse(manifestV1),
|
||||
},
|
||||
),
|
||||
).resolves.toMatchObject({ schemaVersion: 1, contractSet: null });
|
||||
});
|
||||
|
||||
it("rejects a V2 runtime paired with a V1 manifest", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtime, {
|
||||
fetcher: async () => jsonResponse({
|
||||
...manifestV1,
|
||||
configSchemaVersion: "2.0",
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "PROTOCOL_PAIR_MISMATCH",
|
||||
code: "MANIFEST_PROTOCOL_PAIR_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a V1 runtime paired with a V2 manifest", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtimeV1, {
|
||||
fetcher: async () => jsonResponse(manifest),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "PROTOCOL_PAIR_MISMATCH",
|
||||
code: "MANIFEST_PROTOCOL_PAIR_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it("requires matching legacy scalar versions for a V1 pair", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtimeV1, {
|
||||
fetcher: async () => jsonResponse({
|
||||
...manifestV1,
|
||||
apiContractVersion: "2",
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "API_CONTRACT_MISMATCH",
|
||||
code: "MANIFEST_API_CONTRACT_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a manifest without a complete route chunk map", async () => {
|
||||
const malformed = Object.fromEntries(
|
||||
Object.entries(manifest).filter(([key]) => key !== "routeChunks"),
|
||||
@@ -113,7 +235,7 @@ describe("release manifest boot boundary", () => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtime,
|
||||
{ fetcher: async () => Response.json(malformed) },
|
||||
{ fetcher: async () => jsonResponse(malformed) },
|
||||
),
|
||||
).rejects.toBeInstanceOf(ReleaseManifestError);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user