Files
EpicNext-Cms/src/features/housekeeping/migration/validate-matrix.ts
T

94 lines
2.6 KiB
TypeScript

import type { LegacyPage, MigrationEntry } from "./types";
const prohibitedPlaceholder = /\b(?:TBD|TODO|FIXME|UNCLASSIFIED)\b/i;
function hasProhibitedPlaceholder(value: unknown): boolean {
if (typeof value === "string") return prohibitedPlaceholder.test(value);
if (Array.isArray(value)) return value.some(hasProhibitedPlaceholder);
if (value && typeof value === "object")
return Object.values(value).some(hasProhibitedPlaceholder);
return false;
}
export function validateMigrationEntries(
discovered: readonly LegacyPage[],
entries: readonly MigrationEntry[],
): string[] {
const issues = new Set<string>();
const discoveredPaths = new Set(discovered.map((page) => page.legacyPath));
const entriesByPath = new Map<string, MigrationEntry[]>();
for (const entry of entries) {
const matchingEntries = entriesByPath.get(entry.legacyPath) ?? [];
matchingEntries.push(entry);
entriesByPath.set(entry.legacyPath, matchingEntries);
if (!discoveredPaths.has(entry.legacyPath)) {
issues.add(`unknown legacyPath: ${entry.legacyPath}`);
}
}
for (const page of discovered) {
const matchingEntries = entriesByPath.get(page.legacyPath) ?? [];
if (matchingEntries.length === 0) {
issues.add(`missing legacyPath: ${page.legacyPath}`);
continue;
}
if (matchingEntries.length > 1) {
issues.add(`duplicate legacyPath: ${page.legacyPath}`);
}
}
for (const entry of entries) {
const { legacyPath } = entry;
if (entry.targetPath === null && entry.decision !== "REMOVE") {
issues.add(`${entry.decision} requires targetPath: ${legacyPath}`);
}
if (
entry.targetPath !== null &&
entry.decision !== "REMOVE" &&
!entry.targetPath.startsWith("/admin/")
) {
issues.add(
`non-REMOVE targetPath must start with /admin/: ${legacyPath}`,
);
}
if (
(entry.decision === "MERGE" || entry.decision === "REBUILD") &&
entry.requiredTests.length === 0
) {
issues.add(`${entry.decision} requires requiredTests: ${legacyPath}`);
}
if (entry.dependencies.mutations.length > 0) {
if (entry.capabilities.mutate.length === 0) {
issues.add(
`mutation dependencies require mutate capabilities: ${legacyPath}`,
);
}
if (entry.auditRequirement === "NONE") {
issues.add(
`mutation dependencies require auditRequirement: ${legacyPath}`,
);
}
}
if (entry.status === "VERIFIED" && entry.parityEvidence.length === 0) {
issues.add(`VERIFIED requires parityEvidence: ${legacyPath}`);
}
if (hasProhibitedPlaceholder(entry)) {
issues.add(`prohibited placeholder: ${legacyPath}`);
}
}
return [...issues].sort((a, b) => a.localeCompare(b));
}