137 lines
3.5 KiB
TypeScript
137 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { LegacyPage, MigrationEntry } from "./types";
|
|
import { validateMigrationEntries } from "./validate-matrix";
|
|
|
|
const page = (legacyPath: string): LegacyPage => ({
|
|
surface: legacyPath.startsWith("/mod") ? "mod" : "admin",
|
|
legacyPath,
|
|
sourceFile: `src/app${legacyPath}/page.tsx`,
|
|
});
|
|
|
|
const entry = (legacyPath: string): MigrationEntry => ({
|
|
...page(legacyPath),
|
|
targetDomain: "system",
|
|
targetPath: "/admin/system/example",
|
|
decision: "REHOST",
|
|
capabilities: { read: ["admin.dashboard"], mutate: [] },
|
|
dependencies: { queries: [], mutations: [] },
|
|
auditRequirement: "NONE",
|
|
localization: "COMPLETE",
|
|
accessibility: "COMPLETE",
|
|
requiredTests: ["unit"],
|
|
parityEvidence: [],
|
|
status: "PLANNED",
|
|
notes: [],
|
|
});
|
|
|
|
describe("validateMigrationEntries", () => {
|
|
it("reports missing, duplicate, and unknown legacy rows", () => {
|
|
const discovered = [page("/admin"), page("/admin/users")];
|
|
|
|
expect(
|
|
validateMigrationEntries(discovered, [
|
|
entry("/admin"),
|
|
entry("/admin"),
|
|
entry("/admin/ghost"),
|
|
]),
|
|
).toEqual([
|
|
"duplicate legacyPath: /admin",
|
|
"missing legacyPath: /admin/users",
|
|
"unknown legacyPath: /admin/ghost",
|
|
]);
|
|
});
|
|
|
|
it("rejects incomplete decisions", () => {
|
|
const issues = validateMigrationEntries(
|
|
[page("/admin")],
|
|
[{ ...entry("/admin"), targetPath: null, decision: "REBUILD" }],
|
|
);
|
|
|
|
expect(issues).toContain("REBUILD requires targetPath: /admin");
|
|
});
|
|
|
|
it("rejects a discovery surface mismatch for the same legacy path", () => {
|
|
expect(
|
|
validateMigrationEntries(
|
|
[page("/admin")],
|
|
[{ ...entry("/admin"), surface: "mod" }],
|
|
),
|
|
).toEqual([
|
|
"surface mismatch for legacyPath /admin: expected admin, received mod",
|
|
]);
|
|
});
|
|
|
|
it("rejects a discovery source file mismatch for the same legacy path", () => {
|
|
expect(
|
|
validateMigrationEntries(
|
|
[page("/admin")],
|
|
[
|
|
{
|
|
...entry("/admin"),
|
|
sourceFile: "src/app/admin/renamed/page.tsx",
|
|
},
|
|
],
|
|
),
|
|
).toEqual([
|
|
"sourceFile mismatch for legacyPath /admin: expected src/app/admin/page.tsx, received src/app/admin/renamed/page.tsx",
|
|
]);
|
|
});
|
|
|
|
it("rejects removal with a non-null target", () => {
|
|
expect(
|
|
validateMigrationEntries(
|
|
[page("/admin")],
|
|
[
|
|
{
|
|
...entry("/admin"),
|
|
decision: "REMOVE",
|
|
targetPath: "/admin/system/legacy",
|
|
},
|
|
],
|
|
),
|
|
).toEqual(["REMOVE requires null targetPath: /admin"]);
|
|
});
|
|
|
|
it("enforces migration safety evidence", () => {
|
|
const issues = validateMigrationEntries(
|
|
[page("/admin")],
|
|
[
|
|
{
|
|
...entry("/admin"),
|
|
targetPath: "/mod/unsupported",
|
|
decision: "MERGE",
|
|
requiredTests: [],
|
|
dependencies: { queries: [], mutations: ["users.disable"] },
|
|
status: "VERIFIED",
|
|
notes: ["TODO confirm ownership"],
|
|
},
|
|
],
|
|
);
|
|
|
|
expect(issues).toEqual([
|
|
"MERGE requires requiredTests: /admin",
|
|
"mutation dependencies require auditRequirement: /admin",
|
|
"mutation dependencies require mutate capabilities: /admin",
|
|
"non-REMOVE targetPath must start with /admin/: /admin",
|
|
"prohibited placeholder: /admin",
|
|
"VERIFIED requires parityEvidence: /admin",
|
|
]);
|
|
});
|
|
|
|
it("allows removal without a target and rejects non-removal null targets", () => {
|
|
expect(
|
|
validateMigrationEntries(
|
|
[page("/admin")],
|
|
[{ ...entry("/admin"), decision: "REMOVE", targetPath: null }],
|
|
),
|
|
).toEqual([]);
|
|
|
|
expect(
|
|
validateMigrationEntries(
|
|
[page("/admin")],
|
|
[{ ...entry("/admin"), targetPath: null }],
|
|
),
|
|
).toEqual(["REHOST requires targetPath: /admin"]);
|
|
});
|
|
});
|