feat: harden test and registry evidence
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
canonicalRegistryJson,
|
||||
diffRegistrySnapshots,
|
||||
registrySnapshotDigest,
|
||||
validateBreakingEvidence,
|
||||
verifyRegistryBaselineApproval,
|
||||
} from "../../scripts/lib/registry-compatibility.mjs";
|
||||
|
||||
function snapshot(
|
||||
rows: Readonly<Record<string, Readonly<Record<string, unknown>>>>,
|
||||
) {
|
||||
return {
|
||||
schemaVersion: 2,
|
||||
registries: [
|
||||
{
|
||||
registryId: "FE-REG-TEST",
|
||||
contract: { breakingFields: ["path"] },
|
||||
rows,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
describe("registry compatibility evidence", () => {
|
||||
it("canonicalizes object and primitive-array ordering", () => {
|
||||
expect(
|
||||
canonicalRegistryJson({
|
||||
second: ["b", "a"],
|
||||
first: { z: 1, a: 2 },
|
||||
}),
|
||||
).toBe('{"first":{"a":2,"z":1},"second":["a","b"]}');
|
||||
});
|
||||
|
||||
it("classifies additive, behavioral and breaking actual diffs", () => {
|
||||
expect(
|
||||
diffRegistrySnapshots(
|
||||
snapshot({ A: { path: "/a" } }),
|
||||
snapshot({ A: { path: "/a" }, B: { path: "/b" } }),
|
||||
).impact,
|
||||
).toBe("additive");
|
||||
expect(
|
||||
diffRegistrySnapshots(
|
||||
snapshot({ A: { path: "/a", owner: "one" } }),
|
||||
snapshot({ A: { path: "/a", owner: "two" } }),
|
||||
).impact,
|
||||
).toBe("behavior-change");
|
||||
expect(
|
||||
diffRegistrySnapshots(
|
||||
snapshot({ A: { path: "/a" } }),
|
||||
snapshot({ A: { path: "/moved" } }),
|
||||
).impact,
|
||||
).toBe("breaking");
|
||||
expect(
|
||||
diffRegistrySnapshots(
|
||||
snapshot({ A: { path: "/a" } }),
|
||||
snapshot({}),
|
||||
).impact,
|
||||
).toBe("breaking");
|
||||
});
|
||||
|
||||
it("verifies the approved digest and complete breaking evidence", () => {
|
||||
const before = snapshot({ A: { path: "/before" } });
|
||||
const after = snapshot({ A: { path: "/after" } });
|
||||
const digest = registrySnapshotDigest(before);
|
||||
expect(
|
||||
verifyRegistryBaselineApproval(before, {
|
||||
schemaVersion: 1,
|
||||
snapshotDigest: digest,
|
||||
owner: "platform",
|
||||
approvedAt: "2026-07-26T00:00:00.000Z",
|
||||
}).passed,
|
||||
).toBe(true);
|
||||
expect(
|
||||
verifyRegistryBaselineApproval(before, {
|
||||
schemaVersion: 1,
|
||||
snapshotDigest: "tampered",
|
||||
owner: "platform",
|
||||
approvedAt: "2026-07-26T00:00:00.000Z",
|
||||
}).passed,
|
||||
).toBe(false);
|
||||
|
||||
const diff = diffRegistrySnapshots(before, after);
|
||||
expect(validateBreakingEvidence(diff, { changes: [] }).passed).toBe(false);
|
||||
const changeId = diff.changes[0]?.changeId;
|
||||
expect(
|
||||
validateBreakingEvidence(diff, {
|
||||
changes: [
|
||||
{
|
||||
changeId,
|
||||
versionBump: "2",
|
||||
migration: "dual-read",
|
||||
compatibilityWindow: "one release",
|
||||
rollback: "restore previous registry snapshot",
|
||||
owner: "platform",
|
||||
},
|
||||
],
|
||||
}).passed,
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -2,24 +2,32 @@ import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("registry governance manifest", () => {
|
||||
it("declares exactly nine single-owner registries and impact labels", async () => {
|
||||
it("declares ten typed, single-owner executable registries", async () => {
|
||||
const governance = JSON.parse(
|
||||
await readFile("config/contracts/registry-governance.json", "utf8"),
|
||||
);
|
||||
const registries =
|
||||
/** @type {Array<{registryId: string, owner: string}>} */ (
|
||||
/** @type {Array<{
|
||||
* registryId: string,
|
||||
* owner: string,
|
||||
* requiredFields: string[],
|
||||
* fieldTypes: Record<string, string>
|
||||
* }>} */ (
|
||||
governance.registries
|
||||
);
|
||||
expect(governance.registries).toHaveLength(9);
|
||||
expect(governance.registries).toHaveLength(10);
|
||||
expect(new Set(registries.map((entry) => entry.registryId)).size).toBe(
|
||||
9,
|
||||
10,
|
||||
);
|
||||
expect(registries.every((entry) => entry.owner)).toBe(true);
|
||||
expect(governance.compatibilityImpact.allowed).toEqual([
|
||||
"none",
|
||||
"additive",
|
||||
"behavior-change",
|
||||
"breaking",
|
||||
]);
|
||||
expect(
|
||||
registries.every(
|
||||
(entry) =>
|
||||
Array.isArray(entry.requiredFields) &&
|
||||
entry.requiredFields.length > 0 &&
|
||||
entry.fieldTypes &&
|
||||
Object.keys(entry.fieldTypes).length > 0,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user