import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import assert from "node:assert/strict"; import { afterEach, describe, it } from "vitest"; import { checkPackageBoundaryImports, checkSchemaOutput, scanForbiddenPrismaOwnershipText, verifyPrismaOwnership, } from "./verify-prisma-ownership.mjs"; const fixtures = []; function writeFile(rootDir, relativePath, content) { const absolutePath = path.join(rootDir, relativePath); fs.mkdirSync(path.dirname(absolutePath), { recursive: true }); fs.writeFileSync(absolutePath, content); } function createFixture(overrides = {}) { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "prisma-ownership-")); fixtures.push(rootDir); writeFile( rootDir, "prisma/schema/schema.prisma", overrides.schema ?? `generator client { provider = "prisma-client" output = "../../packages/db/generated/prisma/client" } datasource db { provider = "postgresql" } `, ); writeFile( rootDir, "packages/db/src/client.server.ts", overrides.clientServer ?? `export * from "../generated/prisma/client/client.ts"; `, ); writeFile( rootDir, "packages/db/src/prisma-browser.ts", overrides.prismaBrowser ?? `export * from "../generated/prisma/client/browser.ts"; `, ); writeFile(rootDir, "apps/web/app/routes/example.tsx", overrides.appSource ?? "export const route = true;\n"); writeFile(rootDir, "packages/db/generated/prisma/client/client.ts", "app/generated/prisma fixture text ignored in generated output\n"); return rootDir; } afterEach(() => { while (fixtures.length > 0) { fs.rmSync(fixtures.pop(), { recursive: true, force: true }); } }); describe("verify-prisma-ownership", () => { it("passes for a repository using package-owned Prisma 7 TypeScript entrypoints", () => { const rootDir = createFixture({ appSource: `import { Prisma } from "@toolctrl/db/prisma-browser"; export const route = Prisma; `, }); const result = verifyPrismaOwnership(rootDir); assert.deepEqual(result.failures, []); assert.ok(result.passes.some((pass) => pass.includes("generator output is package-owned"))); assert.ok(result.passes.some((pass) => pass.includes("client.server.ts"))); assert.ok(result.passes.some((pass) => pass.includes("repository text scan"))); }); it("fails when app code imports an app-local generated Prisma browser client", () => { const rootDir = createFixture({ appSource: `import { Prisma } from "~/generated/prisma/client/browser"; export const route = Prisma; `, }); const result = scanForbiddenPrismaOwnershipText(rootDir); assert.match(result.failures.join("\n"), /app\/routes\/example\.tsx: forbidden app-local generated Prisma client import/); }); it("fails when app code imports the old root-local generated Prisma client", () => { const rootDir = createFixture({ appSource: `const relativeGeneratedClientPath = "../generated/prisma/client"; const appGeneratedClientPath = "app/generated/prisma/client"; `, }); const result = scanForbiddenPrismaOwnershipText(rootDir); assert.match(result.failures.join("\n"), /app\/routes\/example\.tsx: forbidden root\/app Prisma generated-client path/); }); it("fails when schema output points outside packages/db", () => { const rootDir = createFixture({ schema: `generator client { provider = "prisma-client" output = "../../app/generated/prisma/client" } `, }); const result = checkSchemaOutput(rootDir); assert.match(result.failures.join("\n"), /prisma\/schema\/schema\.prisma: expected generator output/); assert.match(result.failures.join("\n"), /old root\/app generated Prisma ownership/); }); it("fails when package boundary files do not use package-owned generated TypeScript entrypoints", () => { const rootDir = createFixture({ clientServer: `const crossedBoundaryPath = "../../../app/generated/prisma/client"; export const client = crossedBoundaryPath; `, prismaBrowser: `const wrongClientPath = "@prisma/client"; export { wrongClientPath }; `, }); const result = checkPackageBoundaryImports(rootDir); assert.match(result.failures.join("\n"), /packages\/db\/src\/client\.server\.ts: must import\/export/); assert.match(result.failures.join("\n"), /packages\/db\/src\/client\.server\.ts: must not cross out/); assert.match(result.failures.join("\n"), /packages\/db\/src\/prisma-browser\.ts: must import\/export/); }); });