Files
clean-architecture-frontend…/tests/unit/release-artifacts.test.ts
T

601 lines
19 KiB
TypeScript

import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
import { verifyBuildManifestOutputs } from "../../scripts/lib/build-manifest-outputs.ts";
import {
dependencyInventoryArtifactSchema,
fieldWebVitalsArtifactSchema,
jsonSchemaDocumentArtifactSchema,
labPerformanceArtifactSchema,
parseBuildManifestArtifact,
parseReleaseArtifact,
parseRuntimeConfigArtifact,
projectReleaseTokens,
registrySnapshotArtifactSchema,
supplyChainVerificationArtifactSchema,
} from "../../scripts/contracts/release-artifacts.ts";
const EMPTY_CONTRACT_SET_DIGEST =
"sha256:ad6aab71fea6a9ff87cbd170b984b339965afc90d85bb57f87801c9e0c020da2";
const releaseV2 = {
schemaVersion: 2,
appVersion: "0.1.0",
buildId: "build-a",
commitSha: "abc1234",
configSchemaVersion: "2.0",
assetManifestHash: "asset-hash",
releaseId: "release-a",
builtAt: "2026-08-01T00:00:00.000Z",
routeChunks: { "route-home": "assets/home.js" },
contractSet: {
setAlgorithm: "CA_CONTRACT_SET_V1",
setDigest: EMPTY_CONTRACT_SET_DIGEST,
packages: [],
},
} as const;
const buildManifest = {
schemaVersion: 1,
buildId: "build-a",
commitSha: "abc1234",
releaseId: "release-a",
moduleInventoryHash: "inventory-hash",
generatedAt: "2026-08-01T00:00:00.000Z",
buildContext: {
nodeVersion: "v24.14.0",
packageManagerVersion: "11.17.0",
runnerImage: "linux-x64",
sourceDateEpoch: null,
},
outputs: {
directory: "dist",
viteManifest: "dist/.vite/manifest.json",
moduleInventory: "artifacts/quality/vite-module-inventory.json",
routeChunks: { "route-home": "assets/home.js" },
runtimeConfigSchema: "dist/runtime-config.schema.json",
},
} as const;
describe("release artifact contracts", () => {
it("verifies confined build outputs and the raw module inventory bytes", async () => {
const moduleInventoryBytes = Buffer.from(
'{"schemaVersion":1,"chunks":[]}\n',
);
const files = new Map<string, Buffer>([
["/repo/dist/.vite/manifest.json", Buffer.from("{}\n")],
["/repo/artifacts/quality/vite-module-inventory.json", moduleInventoryBytes],
["/repo/dist/runtime-config.schema.json", Buffer.from("{}\n")],
["/repo/dist/assets/home.js", Buffer.from("chunk\n")],
]);
const manifest = {
...buildManifest,
moduleInventoryHash: createHash("sha256")
.update(moduleInventoryBytes)
.digest("hex"),
};
await expect(
verifyBuildManifestOutputs(manifest, {
repositoryRoot: "/repo",
readBytes: async (target) => files.get(target) ?? Promise.reject(Object.assign(new Error("missing"), { code: "ENOENT" })),
realpathPath: async (target) => target,
assertRegularFile: async () => undefined,
assertDirectory: async () => undefined,
}),
).resolves.toEqual([]);
});
it.each([
["missing", undefined, /moduleInventory:missing/u],
[
"tampered",
Buffer.from('{"schemaVersion":1,"chunks":[{"fileName":"other.js","modules":[]}]}\n'),
/moduleInventoryHash/u,
],
] as const)("rejects a %s module inventory", async (_name, bytes, expected) => {
const files = new Map<string, Buffer>([
["/repo/dist/.vite/manifest.json", Buffer.from("{}\n")],
["/repo/dist/runtime-config.schema.json", Buffer.from("{}\n")],
["/repo/dist/assets/home.js", Buffer.from("chunk\n")],
...(bytes ? [["/repo/artifacts/quality/vite-module-inventory.json", bytes] as const] : []),
]);
const mismatches = await verifyBuildManifestOutputs(buildManifest, {
repositoryRoot: "/repo",
readBytes: async (target) => files.get(target) ?? Promise.reject(Object.assign(new Error("missing"), { code: "ENOENT" })),
realpathPath: async (target) => target,
assertRegularFile: async () => undefined,
assertDirectory: async () => undefined,
});
expect(mismatches.join("\n")).toMatch(expected);
});
it.each(["../outside", "/absolute", "dist\\escape"])(
"rejects unsafe build-manifest output path %s",
async (unsafePath) => {
const mismatches = await verifyBuildManifestOutputs(
{
...buildManifest,
outputs: { ...buildManifest.outputs, moduleInventory: unsafePath },
},
{
repositoryRoot: "/repo",
realpathPath: async (target) => target,
},
);
expect(mismatches).toContain("buildManifest:moduleInventory:path");
},
);
it("requires the canonical Vite manifest declared by the build writer", async () => {
const mismatches = await verifyBuildManifestOutputs(
{
...buildManifest,
outputs: {
...buildManifest.outputs,
viteManifest: "dist/.vite/tampered-manifest.json",
},
},
{ repositoryRoot: process.cwd() },
);
expect(mismatches).toContain("buildManifest:viteManifest:path");
});
it("rejects a realpath escape from a declared build output", async () => {
const mismatches = await verifyBuildManifestOutputs(buildManifest, {
repositoryRoot: "/repo",
realpathPath: async (target) =>
target.endsWith("vite-module-inventory.json") ? "/outside/inventory.json" : target,
assertRegularFile: async () => undefined,
assertDirectory: async () => undefined,
readBytes: async () => Buffer.from("inventory\n"),
});
expect(mismatches).toContain("buildManifest:moduleInventory:path");
});
it("rejects a nested symlink that resolves inside the repository but outside dist", async () => {
const moduleInventoryBytes = Buffer.from(
'{"schemaVersion":1,"chunks":[]}\n',
);
const mismatches = await verifyBuildManifestOutputs(
{
...buildManifest,
moduleInventoryHash: createHash("sha256")
.update(moduleInventoryBytes)
.digest("hex"),
},
{
repositoryRoot: "/repo",
realpathPath: async (target) =>
target === "/repo/dist/assets/home.js"
? "/repo/src/home.js"
: target,
assertRegularFile: async () => undefined,
assertDirectory: async () => undefined,
readBytes: async () => moduleInventoryBytes,
},
);
expect(mismatches).toContain("buildManifest:routeChunk:route-home:path");
});
it("allows a legal dist child whose name begins with two dots", async () => {
const moduleInventoryBytes = Buffer.from(
'{"schemaVersion":1,"chunks":[]}\n',
);
const files = new Map<string, Buffer>([
["/repo/dist/.vite/manifest.json", Buffer.from("{}\n")],
["/repo/artifacts/quality/vite-module-inventory.json", moduleInventoryBytes],
["/repo/dist/runtime-config.schema.json", Buffer.from("{}\n")],
["/repo/dist/..assets/home.js", Buffer.from("chunk\n")],
]);
const mismatches = await verifyBuildManifestOutputs(
{
...buildManifest,
moduleInventoryHash: createHash("sha256")
.update(moduleInventoryBytes)
.digest("hex"),
outputs: {
...buildManifest.outputs,
routeChunks: { "route-home": "..assets/home.js" },
},
},
{
repositoryRoot: "/repo",
readBytes: async (target) =>
files.get(target) ?? Promise.reject(new Error("missing")),
realpathPath: async (target) => target,
assertRegularFile: async () => undefined,
assertDirectory: async () => undefined,
},
);
expect(mismatches).toEqual([]);
});
it.each([
["viteManifest", "package.json"],
["runtimeConfigSchema", "schemas/artifacts/build-manifest.schema.json"],
["moduleInventory", "package.json"],
] as const)("rejects %s outside its approved output root", async (field, value) => {
const mismatches = await verifyBuildManifestOutputs(
{
...buildManifest,
outputs: { ...buildManifest.outputs, [field]: value },
},
{ repositoryRoot: process.cwd() },
);
expect(mismatches).toContain(`buildManifest:${field}:path`);
});
it("rejects a parse-invalid module inventory even when its raw hash matches", async () => {
const bytes = Buffer.from("{}\n");
const mismatches = await verifyBuildManifestOutputs(
{
...buildManifest,
moduleInventoryHash: createHash("sha256").update(bytes).digest("hex"),
},
{
repositoryRoot: "/repo",
readBytes: async () => bytes,
realpathPath: async (target) => target,
assertRegularFile: async () => undefined,
assertDirectory: async () => undefined,
},
);
expect(mismatches).toContain("buildManifest:moduleInventory:invalid");
});
it("projects the nested V2 contract-set digest without a legacy scalar", () => {
const release = parseReleaseArtifact(releaseV2);
expect(projectReleaseTokens(release)).toMatchObject({
schemaVersion: 2,
buildId: "build-a",
contractSetDigest: EMPTY_CONTRACT_SET_DIGEST,
});
expect(projectReleaseTokens(release)).not.toHaveProperty(
"apiContractVersion",
);
});
it("projects the legacy scalar only for V1", () => {
const { contractSet: _contractSet, ...releaseWithoutContractSet } = releaseV2;
void _contractSet;
const release = parseReleaseArtifact({
...releaseWithoutContractSet,
schemaVersion: 1,
configSchemaVersion: "1",
apiContractVersion: "1.4.0",
});
expect(projectReleaseTokens(release)).toMatchObject({
schemaVersion: 1,
apiContractVersion: "1.4.0",
});
expect(projectReleaseTokens(release)).not.toHaveProperty(
"contractSetDigest",
);
});
it("rejects a V2 release carrying the removed scalar", () => {
expect(() =>
parseReleaseArtifact({
...releaseV2,
apiContractVersion: "1",
}),
).toThrow();
});
it("accepts the exact build manifest emitted by the generator", () => {
expect(parseBuildManifestArtifact(buildManifest)).toEqual(buildManifest);
});
it("rejects unknown build manifest output fields", () => {
expect(() =>
parseBuildManifestArtifact({
...buildManifest,
outputs: { ...buildManifest.outputs, unexpected: "value" },
}),
).toThrow();
});
it("accepts Runtime Config V2 without a legacy API scalar", () => {
expect(
parseRuntimeConfigArtifact({
APP_ENV: "local",
API_BASE_URL: "http://localhost:8080/",
REQUEST_TIMEOUT_MS: 10_000,
MAX_RETRY_ATTEMPTS: 2,
TELEMETRY_ENABLED: false,
AUTH_MODE: "demo",
CONFIG_SCHEMA_VERSION: "2.0",
RELEASE_MANIFEST_URL: "/release-manifest.json",
BUILD_ID: "build-a",
RELEASE_ID: "release-a",
CAPABILITY_OVERRIDES: {
REALTIME: "DEFAULT",
WEB_WORKER: "DEFAULT",
SERVICE_WORKER: "DEFAULT",
OFFLINE_COMMANDS: "DEFAULT",
},
}),
).not.toHaveProperty("API_CONTRACT_VERSION");
});
it("enforces dependency inventory counts at the executable writer boundary", () => {
const inventory = {
schemaVersion: 2,
packageManager: "pnpm@11.17.0",
lockfileSha256: "a".repeat(64),
dependencyCount: 1,
directDependencyCount: 1,
dependencies: [
{
name: "zod",
version: "4.4.3",
direct: true,
scope: "production",
optional: false,
license: "MIT",
integrity: `sha512-${"a".repeat(86)}`,
dependencies: [],
},
],
} as const;
expect(dependencyInventoryArtifactSchema.parse(inventory)).toEqual(
inventory,
);
expect(() =>
dependencyInventoryArtifactSchema.parse({
...inventory,
dependencyCount: 2,
}),
).toThrow();
expect(() =>
dependencyInventoryArtifactSchema.parse({
...inventory,
dependencyCount: 0,
directDependencyCount: 0,
dependencies: [],
}),
).toThrow();
});
it("rejects undeclared supply-chain verification evidence", () => {
const verification = {
schemaVersion: 1,
localStatus: "PASS",
promotionStatus: "FAIL_UNVERIFIED",
lockfileSha256: "a".repeat(64),
sourceSetSha256: "b".repeat(64),
distSha256: "c".repeat(64),
sbomSha256: "d".repeat(64),
dependencyDiff: {
added: [],
removed: [],
changed: [],
upgrades: [],
},
highRiskReview: [],
vulnerabilityStatus: "FAIL_UNVERIFIED",
provenanceAttestationStatus: "FAIL_UNVERIFIED",
failures: [],
} as const;
expect(supplyChainVerificationArtifactSchema.parse(verification)).toEqual(
verification,
);
expect(() =>
supplyChainVerificationArtifactSchema.parse({
...verification,
undocumented: true,
}),
).toThrow();
});
it("preserves no-baseline and failure registry evidence", () => {
const failureEvidence = {
schemaVersion: 2,
generatedAt: "2026-08-01T00:00:00.000Z",
baselineDigest: null,
currentDigest: "a".repeat(64),
compatibility: { impact: "not-evaluated", changes: [] },
failures: ["missing registry source"],
registries: [],
} as const;
expect(registrySnapshotArtifactSchema.parse(failureEvidence)).toEqual(
failureEvidence,
);
});
it("requires the complete repository registry set for success evidence", () => {
const successfulEvidence = {
schemaVersion: 2,
generatedAt: "2026-08-01T00:00:00.000Z",
baselineDigest: null,
currentDigest: "a".repeat(64),
compatibility: { impact: "not-evaluated", changes: [] },
failures: [],
registries: Array.from({ length: 11 }, (_, index) => ({
registryId: `registry-${index}`,
owner: "platform",
source: `src/registry-${index}.ts`,
rowCount: 0,
contract: {},
rows: {},
})),
} as const;
expect(registrySnapshotArtifactSchema.parse(successfulEvidence)).toEqual(
successfulEvidence,
);
expect(() =>
registrySnapshotArtifactSchema.parse({
...successfulEvidence,
registries: [],
}),
).toThrow();
});
it("preserves unverified field evidence when no samples are eligible", () => {
const report = {
schemaVersion: 1,
generatedAt: "2026-08-01T00:00:00.000Z",
window: {
days: 28,
start: "2026-07-04T00:00:00.000Z",
end: "2026-08-01T00:00:00.000Z",
},
context: {
source: "config/performance/field-input.example.json",
sourceSystem: null,
exportId: null,
network: "production-real-user",
routeAggregation: "route-id-only",
releaseId: null,
privacyApprovalRef: null,
thresholdDecisionRef: null,
validationFailures: ["input: invalid evidence"],
},
metrics: { p75LcpMs: null, p75Cls: null, p75InpMs: null },
thresholds: {
p75LcpMs: 2_500,
p75Cls: 0.1,
p75InpMs: 200,
minimumEligibleSamples: null,
},
eligibility: {
consentRequired: true,
totalSamples: 0,
eligibleSamples: 0,
minimumEligibleSamples: null,
routeSamples: {},
},
status: "FAIL_UNVERIFIED",
passed: false,
} as const;
expect(fieldWebVitalsArtifactSchema.parse(report)).toEqual(report);
});
it("preserves verified field evidence that fails an approved threshold", () => {
const report = {
schemaVersion: 1,
generatedAt: "2026-08-01T00:00:00.000Z",
window: {
days: 28,
start: "2026-07-04T00:00:00.000Z",
end: "2026-08-01T00:00:00.000Z",
},
context: {
source: "provider.json",
sourceSystem: "provider",
exportId: "export-1",
network: "production-real-user",
routeAggregation: "route-id-only",
releaseId: "release-1",
privacyApprovalRef: "privacy-1",
thresholdDecisionRef: "decision-1",
validationFailures: [],
},
metrics: { p75LcpMs: 2_501, p75Cls: 0.1, p75InpMs: 200 },
thresholds: {
p75LcpMs: 2_500,
p75Cls: 0.1,
p75InpMs: 200,
minimumEligibleSamples: 1,
},
eligibility: {
consentRequired: true,
totalSamples: 1,
eligibleSamples: 1,
minimumEligibleSamples: 1,
routeSamples: { APP_HOME: 1 },
},
status: "FAIL_THRESHOLD",
passed: false,
} as const;
expect(fieldWebVitalsArtifactSchema.parse(report)).toEqual(report);
});
it("accepts the lab writer's nested browser context as JSON evidence", () => {
const report = {
schemaVersion: 1,
generatedAt: "2026-08-01T00:00:00.000Z",
context: {
runner: { platform: "linux", architecture: "x64", nodeVersion: "v24" },
browser: { name: "chromium", version: "140" },
viewport: { width: 1280, height: 720 },
network: {
profile: "contract-fast-4g",
latencyMs: 40,
downloadBytesPerSecond: 200_000,
uploadBytesPerSecond: 93_750,
},
cpu: { throttlingRate: 4 },
cache: { state: "cold", isolation: "new-browser-context" },
build: { buildId: "build-1", releaseId: "release-1" },
},
metrics: { lcpMs: 1_000, cls: 0.01, namedInteractionMs: 100 },
thresholds: { lcpMs: 2_500, cls: 0.1, namedInteractionMs: 200 },
fixtures: [
{ name: "missing-context", passed: true },
{ name: "lcp-over-threshold", passed: true },
],
passed: true,
} as const;
expect(labPerformanceArtifactSchema.parse(report)).toEqual(report);
});
it("rejects non-JSON values in generated schema documents", () => {
expect(() =>
jsonSchemaDocumentArtifactSchema.parse({
$schema: "https://json-schema.org/draft/2020-12/schema",
invalid: () => undefined,
}),
).toThrow();
});
it("keeps machine-readable evidence publishers on the validated writer", async () => {
const writerFiles = [
"scripts/generate-build-manifest.ts",
"scripts/generate-supply-chain.ts",
"scripts/collect-web-vitals-evidence.ts",
"scripts/test-performance.ts",
"scripts/verify-release.ts",
"scripts/drill-runbook.ts",
"scripts/check-registries.ts",
] as const;
const sources = await Promise.all(
writerFiles.map(async (file) => ({
file,
source: await readFile(file, "utf8"),
})),
);
for (const { file, source } of sources) {
const directWrites = source.match(/\bwriteFile\s*\(/gu) ?? [];
if (file === "scripts/generate-supply-chain.ts") {
expect(directWrites, file).toHaveLength(1);
expect(source, file).toMatch(
/writeFile\(\s*["']artifacts\/release\/checksums\.txt["']/u,
);
} else {
expect(directWrites, file).toHaveLength(0);
}
}
expect(
sources.flatMap(({ source }) =>
source.match(/\bwriteValidatedJsonArtifact\s*\(/gu) ?? [],
),
).toHaveLength(19);
});
});