test(housekeeping): prove 137 route parity
This commit is contained in:
1 parent
b235ce08ff
commit
8a31556d51
36 files changed
+431
-77
No files matched your search
@@ -0,0 +1,133 @@
|
||||
import type { HousekeepingRegistry } from "../foundation/registry";
|
||||
import type { MigrationEntry } from "../migration/types";
|
||||
import type { HousekeepingRouteHandler } from "../route-handlers";
|
||||
|
||||
export interface HousekeepingParityReport {
|
||||
readonly discovered: 137;
|
||||
readonly mapped: number;
|
||||
readonly verified: number;
|
||||
readonly removed: number;
|
||||
readonly unresolved: readonly string[];
|
||||
readonly capabilityGaps: readonly string[];
|
||||
readonly handlerGaps: readonly string[];
|
||||
}
|
||||
|
||||
function expectedEvidence(row: MigrationEntry): string {
|
||||
return `runtime-parity:${row.legacyPath}`;
|
||||
}
|
||||
|
||||
function capabilityMatches(
|
||||
row: MigrationEntry,
|
||||
route: HousekeepingRegistry["domains"][number]["routes"][number],
|
||||
): boolean {
|
||||
const declared = new Set(row.capabilities.read);
|
||||
if (route.capability.mode === "all") {
|
||||
return route.capability.slugs.every((slug) => declared.has(slug));
|
||||
}
|
||||
return route.capability.slugs.some((slug) => declared.has(slug));
|
||||
}
|
||||
|
||||
export function verifyHousekeepingRuntimeParity(
|
||||
matrix: readonly MigrationEntry[],
|
||||
registry: HousekeepingRegistry,
|
||||
handlers: readonly HousekeepingRouteHandler[],
|
||||
): HousekeepingParityReport {
|
||||
if (matrix.length !== 137) {
|
||||
throw new Error(
|
||||
`Housekeeping parity requires exactly 137 discovered rows; received ${matrix.length}`,
|
||||
);
|
||||
}
|
||||
|
||||
const routes = registry.domains.flatMap((domain) => domain.routes);
|
||||
const routesByHref = new Map<string, (typeof routes)[number]>(
|
||||
routes.map((route) => [route.href, route]),
|
||||
);
|
||||
const routesById = new Map(routes.map((route) => [route.id, route]));
|
||||
const handlersByRouteId = new Map<string, HousekeepingRouteHandler[]>();
|
||||
for (const handler of handlers) {
|
||||
const matching = handlersByRouteId.get(handler.routeId) ?? [];
|
||||
matching.push(handler);
|
||||
handlersByRouteId.set(handler.routeId, matching);
|
||||
}
|
||||
|
||||
const unresolved = new Set<string>();
|
||||
const capabilityGaps = new Set<string>();
|
||||
const handlerGaps = new Set<string>();
|
||||
let mapped = 0;
|
||||
let verified = 0;
|
||||
let removed = 0;
|
||||
|
||||
for (const row of matrix) {
|
||||
let rowMapped = false;
|
||||
let rowCapabilityVerified = true;
|
||||
let rowHandlerVerified = true;
|
||||
|
||||
if (row.decision === "REMOVE") {
|
||||
removed += 1;
|
||||
rowMapped = row.targetPath === null;
|
||||
if (!rowMapped)
|
||||
unresolved.add(`${row.legacyPath}: removed target exists`);
|
||||
} else if (row.targetPath === null) {
|
||||
unresolved.add(`${row.legacyPath}: retained target is missing`);
|
||||
} else {
|
||||
const route = routesByHref.get(row.targetPath);
|
||||
if (!route) {
|
||||
unresolved.add(
|
||||
`${row.legacyPath}: no registered route for ${row.targetPath}`,
|
||||
);
|
||||
} else {
|
||||
rowMapped = true;
|
||||
if (!capabilityMatches(row, route)) {
|
||||
rowCapabilityVerified = false;
|
||||
capabilityGaps.add(
|
||||
`${row.legacyPath}: ${row.targetPath} capability mismatch`,
|
||||
);
|
||||
}
|
||||
|
||||
const matchingHandlers = handlersByRouteId.get(route.id) ?? [];
|
||||
if (matchingHandlers.length !== 1) {
|
||||
rowHandlerVerified = false;
|
||||
handlerGaps.add(
|
||||
`${row.legacyPath}: ${route.id} has ${matchingHandlers.length} handlers`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rowMapped) mapped += 1;
|
||||
const expectedStatus = row.decision === "REMOVE" ? "REMOVED" : "VERIFIED";
|
||||
if (
|
||||
rowMapped &&
|
||||
rowCapabilityVerified &&
|
||||
rowHandlerVerified &&
|
||||
row.status === expectedStatus &&
|
||||
row.parityEvidence.includes(expectedEvidence(row))
|
||||
) {
|
||||
verified += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (const route of routes) {
|
||||
const matchingHandlers = handlersByRouteId.get(route.id) ?? [];
|
||||
if (matchingHandlers.length !== 1) {
|
||||
handlerGaps.add(
|
||||
`registry route ${route.id} has ${matchingHandlers.length} handlers`,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const handler of handlers) {
|
||||
if (!routesById.has(handler.routeId)) {
|
||||
handlerGaps.add(`orphan handler ${handler.routeId}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
discovered: 137,
|
||||
mapped,
|
||||
verified,
|
||||
removed,
|
||||
unresolved: [...unresolved].sort(),
|
||||
capabilityGaps: [...capabilityGaps].sort(),
|
||||
handlerGaps: [...handlerGaps].sort(),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user