Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/scripts/lib/prisma-tenant-scope.test.mjs
T

77 lines
3.1 KiB
JavaScript

import path from "node:path";
import { describe, expect, it } from "vitest";
import { TENANT_SCOPE_MANIFEST } from "../../prisma/tenant-scope-manifest.mjs";
import { discoverIndirectTenantModels, parsePrismaSchemaDirectory, validateTenantScopeManifest } from "./prisma-tenant-scope.mjs";
function model(model, options = {}) {
return {
model,
table: model,
schema: options.schema ?? "fixture.prisma",
tenantField: options.tenantField ?? null,
owningRelationTargets: options.owningRelationTargets ?? [],
};
}
describe("tenant scope manifest", () => {
it("classifies every current Prisma model and every discovered indirect boundary", () => {
const models = parsePrismaSchemaDirectory(path.join(process.cwd(), "prisma", "schema"));
const result = validateTenantScopeManifest(models, TENANT_SCOPE_MANIFEST);
expect(result.errors).toEqual([]);
expect(result.modelCount).toBe(377);
expect(result.classifiedCount).toBe(377);
expect(result.scopeCounts).toEqual({
"tenant-required": 195,
"tenant-or-global": 29,
"derived-child": 36,
"tenant-root": 1,
"global-catalog": 11,
"global-shared": 16,
"platform-admin": 89,
});
expect(result.indirectModels.size).toBe(37);
expect(result.indirectModels).toContain("Registration");
expect(result.indirectModels).toContain("KnowledgeBaseArticleDownvotes");
});
it("reports an unclassified schema model", () => {
const models = [model("Known", { tenantField: { nullable: false } }), model("NewModel")];
const manifest = { Known: { scope: "tenant-required", enforcement: "tenant-policy" } };
const result = validateTenantScopeManifest(models, manifest);
expect(result.errors).toContain("fixture.prisma: NewModel is missing from the tenant scope manifest");
});
it("detects recursively derived FK children classified as global", () => {
const models = [
model("TenantRow", { tenantField: { nullable: false } }),
model("Child", { owningRelationTargets: ["TenantRow"] }),
model("GrandChild", { owningRelationTargets: ["Child"] }),
];
const manifest = {
TenantRow: { scope: "tenant-required", enforcement: "tenant-policy" },
Child: { scope: "derived-child", enforcement: "join-policy" },
GrandChild: { scope: "global-shared", enforcement: "none" },
};
const indirect = discoverIndirectTenantModels(models, manifest);
const result = validateTenantScopeManifest(models, manifest);
expect(indirect).toEqual(new Set(["Child", "GrandChild"]));
expect(result.errors).toContain("fixture.prisma: GrandChild has an owning FK path to tenant data but is classified global-shared");
});
it("requires a reason for enforcement overrides", () => {
const models = [model("Hybrid", { tenantField: { nullable: true } })];
const manifest = { Hybrid: { scope: "tenant-or-global", enforcement: "none" } };
const result = validateTenantScopeManifest(models, manifest);
expect(result.errors).toContain("Hybrid overrides tenant-or-global enforcement without an explicit reason");
});
});