|
|
|
@@ -4,6 +4,7 @@ import { join, posix } from "node:path";
|
|
|
|
|
import { createElement, type ReactElement } from "react";
|
|
|
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
|
import { SYSTEM_ROUTE_IDS } from "../domains/system/routes";
|
|
|
|
|
import { HOUSEKEEPING_MANIFESTS } from "../manifests";
|
|
|
|
|
import { discoverLegacyPages } from "../migration/discover-legacy-pages";
|
|
|
|
|
import { HOUSEKEEPING_MIGRATION_MATRIX } from "../migration/matrix";
|
|
|
|
@@ -17,6 +18,81 @@ const SERVER_CAPABILITY_CONTEXT =
|
|
|
|
|
"src/features/housekeeping/foundation/server-capability-context.ts";
|
|
|
|
|
const PERMISSIONS_ADAPTER = "src/lib/permissions";
|
|
|
|
|
const DOMAIN_MODULE_ROOT = "src/features/housekeeping/domains";
|
|
|
|
|
const approvedSystemRuntimeImports = new Map<string, ReadonlySet<string>>([
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/commands/system-commands.ts",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/services/mutations"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/access.tsx",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/queries/access"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/configuration.tsx",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/queries/configuration"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/observability.tsx",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/queries/observability"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/operations.tsx",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/queries/operations"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/queries/access.ts",
|
|
|
|
|
new Set(["src/lib/db", "drizzle-orm"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/queries/configuration.ts",
|
|
|
|
|
new Set(["src/lib/db", "drizzle-orm"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/queries/observability.ts",
|
|
|
|
|
new Set(["src/lib/db", "drizzle-orm"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/queries/operations.ts",
|
|
|
|
|
new Set([
|
|
|
|
|
"src/features/housekeeping/domains/system/queries/observability",
|
|
|
|
|
"src/lib/db",
|
|
|
|
|
"drizzle-orm",
|
|
|
|
|
]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/services/mutations.ts",
|
|
|
|
|
new Set([
|
|
|
|
|
"src/app/admin/settings/cms-settings-config",
|
|
|
|
|
"src/lib/db",
|
|
|
|
|
"drizzle-orm",
|
|
|
|
|
"mysql2",
|
|
|
|
|
]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/manifest.ts",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/routes"]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/domains/system/route-handlers.ts",
|
|
|
|
|
new Set([
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/access",
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/configuration",
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/observability",
|
|
|
|
|
"src/features/housekeeping/domains/system/pages/operations",
|
|
|
|
|
"src/features/housekeeping/domains/system/routes",
|
|
|
|
|
]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/foundation/commands/bootstrap.ts",
|
|
|
|
|
new Set([
|
|
|
|
|
"src/features/housekeeping/domains/system/commands/system-commands",
|
|
|
|
|
]),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"src/features/housekeeping/route-handlers.ts",
|
|
|
|
|
new Set(["src/features/housekeeping/domains/system/route-handlers"]),
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
const forbiddenModuleRoots = [
|
|
|
|
|
"src/lib/db",
|
|
|
|
|
"src/lib/db-pool",
|
|
|
|
@@ -358,6 +434,17 @@ function isAllowedPermissionSetTypeImport(
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isApprovedSystemRuntimeImport(
|
|
|
|
|
canonical: CanonicalModuleSpecifier,
|
|
|
|
|
sourceFile: string,
|
|
|
|
|
): boolean {
|
|
|
|
|
const allowed = approvedSystemRuntimeImports.get(sourceFile);
|
|
|
|
|
return (
|
|
|
|
|
allowed !== undefined &&
|
|
|
|
|
canonical.candidates.some((candidate) => allowed.has(candidate))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function findHousekeepingImportBoundaryViolations(
|
|
|
|
|
source: string,
|
|
|
|
|
sourceFile: string,
|
|
|
|
@@ -370,6 +457,7 @@ function findHousekeepingImportBoundaryViolations(
|
|
|
|
|
if (canonical.violation) violations.push(canonical.violation);
|
|
|
|
|
if (isAllowedPermissionSetTypeImport(access, canonical, sourceFile))
|
|
|
|
|
continue;
|
|
|
|
|
if (isApprovedSystemRuntimeImport(canonical, sourceFile)) continue;
|
|
|
|
|
const forbiddenPath = canonical.candidates.find((candidate) =>
|
|
|
|
|
isForbiddenModulePath(candidate, sourceFile),
|
|
|
|
|
);
|
|
|
|
@@ -398,6 +486,33 @@ describe("housekeeping runtime import boundary", () => {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows only the approved System vertical runtime edges", () => {
|
|
|
|
|
expect(
|
|
|
|
|
findHousekeepingImportBoundaryViolations(
|
|
|
|
|
'import { systemMutationService } from "../services/mutations";',
|
|
|
|
|
"src/features/housekeeping/domains/system/commands/system-commands.ts",
|
|
|
|
|
),
|
|
|
|
|
).toEqual([]);
|
|
|
|
|
expect(
|
|
|
|
|
findHousekeepingImportBoundaryViolations(
|
|
|
|
|
'import { db } from "@/lib/db";',
|
|
|
|
|
"src/features/housekeeping/domains/system/queries/access.ts",
|
|
|
|
|
),
|
|
|
|
|
).toEqual([]);
|
|
|
|
|
expect(
|
|
|
|
|
findHousekeepingImportBoundaryViolations(
|
|
|
|
|
'import { db } from "@/lib/db";',
|
|
|
|
|
"src/features/housekeeping/domains/system/commands/system-commands.ts",
|
|
|
|
|
),
|
|
|
|
|
).toEqual(["src/lib/db"]);
|
|
|
|
|
expect(
|
|
|
|
|
findHousekeepingImportBoundaryViolations(
|
|
|
|
|
'import service from "../system/services/mutations";',
|
|
|
|
|
"src/features/housekeeping/domains/people/manifest.ts",
|
|
|
|
|
),
|
|
|
|
|
).toEqual(["src/features/housekeeping/domains/system/services/mutations"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it.each([
|
|
|
|
|
[
|
|
|
|
|
"aliased database import",
|
|
|
|
@@ -561,7 +676,7 @@ describe("housekeeping foundation completion contracts", () => {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("creates the real six-domain registry in locked order without workflows", () => {
|
|
|
|
|
it("creates the real six-domain registry with only the System routes enabled", () => {
|
|
|
|
|
const registry = createHousekeepingRegistry(HOUSEKEEPING_MANIFESTS);
|
|
|
|
|
|
|
|
|
|
expect(registry.domains.map((domain) => domain.id)).toEqual([
|
|
|
|
@@ -572,9 +687,16 @@ describe("housekeeping foundation completion contracts", () => {
|
|
|
|
|
"hotel",
|
|
|
|
|
"system",
|
|
|
|
|
]);
|
|
|
|
|
expect(registry.domains.every((domain) => domain.routes.length === 0)).toBe(
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
registry.domains
|
|
|
|
|
.filter((domain) => domain.id !== "system")
|
|
|
|
|
.every((domain) => domain.routes.length === 0),
|
|
|
|
|
).toBe(true);
|
|
|
|
|
expect(
|
|
|
|
|
registry.domains
|
|
|
|
|
.find((domain) => domain.id === "system")
|
|
|
|
|
?.routes.map((route) => route.id),
|
|
|
|
|
).toEqual(SYSTEM_ROUTE_IDS);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("keeps production preview disabled even when the flag is true", () => {
|
|
|
|
|