fix: unify release runtime coherence verification
This commit is contained in:
@@ -1,10 +1,103 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { verifyRollbackReleaseCoherence } from "../../scripts/drill-runbook.ts";
|
||||
import { verifyReleaseRuntimeCoherence } from "../../scripts/lib/release-runtime-coherence.ts";
|
||||
import type { InstalledContractPackageIdentity } from "../../src/contracts/external-contract-runtime.ts";
|
||||
import { computeContractSetDigest } from "../../src/contracts/contract-set-canonical.ts";
|
||||
import type {
|
||||
ReleaseArtifact,
|
||||
RuntimeConfigArtifact,
|
||||
} from "../../src/contracts/release-artifacts.ts";
|
||||
import {
|
||||
compareReleaseToRuntime,
|
||||
RELEASE_TOKEN_REGISTRY,
|
||||
} from "../../src/contracts/release-tokens.ts";
|
||||
|
||||
const contractPackages = [
|
||||
{
|
||||
packageId: "@example/accounts",
|
||||
version: "1.2.3",
|
||||
digest: `sha256:${"1".repeat(64)}`,
|
||||
runtimeProtocolVersion: 1,
|
||||
sourceRevision: "a".repeat(40),
|
||||
},
|
||||
{
|
||||
packageId: "@example/billing",
|
||||
version: "2.3.4",
|
||||
digest: `sha256:${"2".repeat(64)}`,
|
||||
runtimeProtocolVersion: 1,
|
||||
sourceRevision: "b".repeat(40),
|
||||
},
|
||||
] as const satisfies readonly InstalledContractPackageIdentity[];
|
||||
|
||||
const releaseV1 = {
|
||||
schemaVersion: 1,
|
||||
appVersion: "1.0.0",
|
||||
buildId: "build-a",
|
||||
commitSha: "abc1234",
|
||||
configSchemaVersion: "1",
|
||||
apiContractVersion: "1.4.0",
|
||||
assetManifestHash: "assets-a",
|
||||
releaseId: "release-a",
|
||||
builtAt: "2026-08-01T00:00:00.000Z",
|
||||
routeChunks: {},
|
||||
} as const satisfies ReleaseArtifact;
|
||||
|
||||
const runtimeV1 = {
|
||||
APP_ENV: "local",
|
||||
API_BASE_URL: "http://localhost:8080/",
|
||||
REQUEST_TIMEOUT_MS: 10_000,
|
||||
MAX_RETRY_ATTEMPTS: 2,
|
||||
TELEMETRY_ENABLED: false,
|
||||
AUTH_MODE: "external",
|
||||
CONFIG_SCHEMA_VERSION: "1",
|
||||
API_CONTRACT_VERSION: "1.6.0",
|
||||
RELEASE_MANIFEST_URL: "/release-manifest.json",
|
||||
BUILD_ID: "build-a",
|
||||
RELEASE_ID: "release-a",
|
||||
} as const satisfies RuntimeConfigArtifact;
|
||||
|
||||
const runtimeV2 = {
|
||||
APP_ENV: "local",
|
||||
API_BASE_URL: "http://localhost:8080/",
|
||||
REQUEST_TIMEOUT_MS: 10_000,
|
||||
MAX_RETRY_ATTEMPTS: 2,
|
||||
TELEMETRY_ENABLED: false,
|
||||
AUTH_MODE: "external",
|
||||
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",
|
||||
},
|
||||
} as const satisfies RuntimeConfigArtifact;
|
||||
|
||||
async function releaseV2With(
|
||||
packages: readonly InstalledContractPackageIdentity[],
|
||||
setDigest?: `sha256:${string}`,
|
||||
): Promise<ReleaseArtifact> {
|
||||
return {
|
||||
schemaVersion: 2,
|
||||
appVersion: "1.0.0",
|
||||
buildId: "build-a",
|
||||
commitSha: "abc1234",
|
||||
configSchemaVersion: "2.0",
|
||||
assetManifestHash: "assets-a",
|
||||
releaseId: "release-a",
|
||||
builtAt: "2026-08-01T00:00:00.000Z",
|
||||
routeChunks: {},
|
||||
contractSet: {
|
||||
setAlgorithm: "CA_CONTRACT_SET_V1",
|
||||
setDigest: setDigest ?? (await computeContractSetDigest(packages)),
|
||||
packages: [...packages],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("release coherence", () => {
|
||||
it("owns all nine release tokens and keeps builtAt diagnostic-only", () => {
|
||||
// §5.2 adds contractSetDigest beside the legacy apiContractVersion scalar.
|
||||
@@ -55,4 +148,112 @@ describe("release coherence", () => {
|
||||
mismatches: ["buildId", "configSchemaVersion", "apiContractVersion"],
|
||||
});
|
||||
});
|
||||
|
||||
it("gives release verification and rollback drills the same V1/V2 tamper verdicts", async () => {
|
||||
const packageAdded = [
|
||||
...contractPackages,
|
||||
{
|
||||
packageId: "@example/notifications",
|
||||
version: "3.0.0",
|
||||
digest: `sha256:${"3".repeat(64)}`,
|
||||
runtimeProtocolVersion: 1,
|
||||
sourceRevision: "c".repeat(40),
|
||||
},
|
||||
] as const satisfies readonly InstalledContractPackageIdentity[];
|
||||
const packageRemoved = contractPackages.slice(0, 1);
|
||||
const versionChanged = [
|
||||
{ ...contractPackages[0], version: "1.2.4" },
|
||||
contractPackages[1],
|
||||
] as const satisfies readonly InstalledContractPackageIdentity[];
|
||||
const packageDigestChanged = [
|
||||
{
|
||||
...contractPackages[0],
|
||||
digest: `sha256:${"f".repeat(64)}`,
|
||||
},
|
||||
contractPackages[1],
|
||||
] as const satisfies readonly InstalledContractPackageIdentity[];
|
||||
const exactV2 = await releaseV2With(contractPackages);
|
||||
const matrix = [
|
||||
{
|
||||
name: "V1 scalar success",
|
||||
release: releaseV1,
|
||||
runtime: runtimeV1,
|
||||
expectedCompatible: true,
|
||||
},
|
||||
{
|
||||
name: "V1 scalar mismatch",
|
||||
release: releaseV1,
|
||||
runtime: { ...runtimeV1, API_CONTRACT_VERSION: "2.0.0" },
|
||||
expectedCompatible: false,
|
||||
},
|
||||
{
|
||||
name: "V2 exact package set",
|
||||
release: exactV2,
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: true,
|
||||
},
|
||||
{
|
||||
name: "V2 exact package set in non-canonical manifest order",
|
||||
release: await releaseV2With([...contractPackages].reverse()),
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: true,
|
||||
},
|
||||
{
|
||||
name: "V2 package added",
|
||||
release: await releaseV2With(packageAdded),
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: false,
|
||||
},
|
||||
{
|
||||
name: "V2 package removed",
|
||||
release: await releaseV2With(packageRemoved),
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: false,
|
||||
},
|
||||
{
|
||||
name: "V2 package version tampered",
|
||||
release: await releaseV2With(versionChanged),
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: false,
|
||||
},
|
||||
{
|
||||
name: "V2 package digest tampered",
|
||||
release: await releaseV2With(packageDigestChanged),
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: false,
|
||||
},
|
||||
{
|
||||
name: "V2 set digest tampered",
|
||||
release: await releaseV2With(
|
||||
contractPackages,
|
||||
`sha256:${"0".repeat(64)}`,
|
||||
),
|
||||
runtime: runtimeV2,
|
||||
expectedCompatible: false,
|
||||
},
|
||||
];
|
||||
|
||||
for (const fixture of matrix) {
|
||||
const input = {
|
||||
release: fixture.release,
|
||||
runtime: fixture.runtime,
|
||||
contractPackages,
|
||||
};
|
||||
const verifierVerdict = (
|
||||
await verifyReleaseRuntimeCoherence(input)
|
||||
).compatible;
|
||||
const rollbackDrillVerdict = (
|
||||
await verifyRollbackReleaseCoherence(input)
|
||||
).compatible;
|
||||
expect(verifierVerdict, `${fixture.name}: verifier`).toBe(
|
||||
fixture.expectedCompatible,
|
||||
);
|
||||
expect(rollbackDrillVerdict, `${fixture.name}: rollback drill`).toBe(
|
||||
fixture.expectedCompatible,
|
||||
);
|
||||
expect(rollbackDrillVerdict, `${fixture.name}: identical verdict`).toBe(
|
||||
verifierVerdict,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user