test(housekeeping): prove 137 route parity
CI / check (pull_request) Successful in 1m9s
CI / release (pull_request) Skipped
CI / deploy (pull_request) Skipped

This commit is contained in:
Simo committed 2026-08-30 19:11:14 +02:00
1 parent b235ce08ff
commit 8a31556d51
36 files changed
+431 -77

No files matched your search

@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { createHousekeepingRegistry } from "../foundation/registry";
import { HOUSEKEEPING_MANIFESTS } from "../manifests";
import { discoverLegacyPages } from "../migration/discover-legacy-pages";
import { HOUSEKEEPING_MIGRATION_MATRIX } from "../migration/matrix";
import { HOUSEKEEPING_ROUTE_HANDLERS } from "../route-handlers";
import { verifyHousekeepingRuntimeParity } from "./parity";
describe("Housekeeping runtime parity", () => {
const report = verifyHousekeepingRuntimeParity(
HOUSEKEEPING_MIGRATION_MATRIX,
createHousekeepingRegistry(HOUSEKEEPING_MANIFESTS),
HOUSEKEEPING_ROUTE_HANDLERS,
);
it("closes all 137 discovered migration rows against the runtime", () => {
expect(discoverLegacyPages()).toHaveLength(137);
expect(report).toEqual({
discovered: 137,
mapped: 137,
verified: 137,
removed: 2,
unresolved: [],
capabilityGaps: [],
handlerGaps: [],
});
});
it("keeps every retained target under /ase with concrete parity evidence", () => {
for (const row of HOUSEKEEPING_MIGRATION_MATRIX) {
if (row.decision === "REMOVE") {
expect(row.targetPath, row.legacyPath).toBeNull();
expect(row.status, row.legacyPath).toBe("REMOVED");
expect(row.parityEvidence, row.legacyPath).toContain(
`runtime-parity:${row.legacyPath}`,
);
continue;
}
expect(row.targetPath, row.legacyPath).toMatch(/^\/ase(?:\/|$)/);
expect(row.status, row.legacyPath).toBe("VERIFIED");
expect(row.parityEvidence, row.legacyPath).toContain(
`runtime-parity:${row.legacyPath}`,
);
}
});
});
+133
View File
@@ -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(),
};
}
@@ -0,0 +1,93 @@
import { readdirSync, readFileSync } from "node:fs";
import { dirname, relative, resolve, sep } from "node:path";
import { describe, expect, it } from "vitest";
const housekeepingRoot = resolve(process.cwd(), "src/features/housekeeping");
function sourceFiles(directory: string): string[] {
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const path = resolve(directory, entry.name);
if (entry.isDirectory()) return sourceFiles(path);
if (!/\.(?:ts|tsx)$/u.test(entry.name) || /\.test\./u.test(entry.name)) {
return [];
}
return [path];
});
}
function moduleSpecifiers(source: string): string[] {
return [
...source.matchAll(
/(?:from\s+|import\s*\(|import\s+|require\s*\()\s*["']([^"']+)["']/gu,
),
].flatMap((match) => (match[1] ? [match[1]] : []));
}
function resolveHousekeepingImport(
file: string,
specifier: string,
): string | null {
if (specifier.startsWith(".")) {
return resolve(dirname(file), specifier).replaceAll("\\", "/");
}
const prefix = "@/features/housekeeping/";
if (specifier.startsWith(prefix)) {
return resolve(housekeepingRoot, specifier.slice(prefix.length)).replaceAll(
"\\",
"/",
);
}
return null;
}
function displayPath(path: string): string {
return relative(process.cwd(), path).split(sep).join("/");
}
describe("Housekeeping source boundaries", () => {
it("keeps foundation independent from domain internals, database, and actions", () => {
const violations = sourceFiles(resolve(housekeepingRoot, "foundation"))
.flatMap((file) =>
moduleSpecifiers(readFileSync(file, "utf8")).flatMap((specifier) => {
const resolved = resolveHousekeepingImport(file, specifier);
const importsDomain =
resolved?.includes("/features/housekeeping/domains/") === true;
const importsInfrastructure =
specifier === "@/db" ||
specifier.startsWith("@/db/") ||
specifier === "@/actions" ||
specifier.startsWith("@/actions/");
return importsDomain || importsInfrastructure
? [`${displayPath(file)} -> ${specifier}`]
: [];
}),
)
.sort();
expect(violations).toEqual([]);
});
it("prevents one domain from importing another domain's internals", () => {
const domainsRoot = resolve(housekeepingRoot, "domains");
const violations = sourceFiles(domainsRoot)
.flatMap((file) => {
const owner = relative(domainsRoot, file).split(sep)[0];
return moduleSpecifiers(readFileSync(file, "utf8")).flatMap(
(specifier) => {
const resolved = resolveHousekeepingImport(file, specifier);
if (!resolved) return [];
const marker = "/features/housekeeping/domains/";
const offset = resolved.indexOf(marker);
if (offset < 0) return [];
const target = resolved.slice(offset + marker.length).split("/")[0];
return target && target !== owner
? [`${displayPath(file)} -> ${specifier}`]
: [];
},
);
})
.sort();
expect(violations).toEqual([]);
});
});