feat(01-01): Prisma schema with Tenant/User models and Vitest test setup

- prisma/schema.prisma with Tenant, User, Role, TenantStatus models
  - tenantId on all tenant-scoped models (RLS-ready convention)
  - @@unique([email, tenantId]) and @@index([tenantId]) on User
  - Grace period fields on Tenant (suspendedAt, gracePeriodEndsAt)
  - RLS comment block documenting tenantId convention for future models
- src/lib/prisma.ts singleton PrismaClient pattern (hot-reload safe)
- vitest.config.ts with node environment and @/* path alias
- src/lib/__tests__/setup.test.ts smoke test (2 tests passing)
- package.json scripts: test, test:watch, db:push, db:generate, db:studio
- Schema synced to PostgreSQL 16 via prisma db push
This commit is contained in:
kevin-asprec
2026-03-04 18:31:04 +08:00
parent 90bc5836fd
commit 1adeab2fbc
6 changed files with 2012 additions and 7 deletions

View File

@@ -0,0 +1,20 @@
/**
* Smoke test: verifies that the Prisma client singleton is importable
* and defined without needing an active database connection.
*
* This test validates the import chain:
* src/lib/prisma.ts -> @prisma/client -> generated Prisma client
*/
import { prisma } from "@/lib/prisma";
describe("Prisma client singleton", () => {
it("should be defined and importable", () => {
expect(prisma).toBeDefined();
});
it("should be a PrismaClient instance", () => {
// PrismaClient instances have $connect and $disconnect methods
expect(typeof prisma.$connect).toBe("function");
expect(typeof prisma.$disconnect).toBe("function");
});
});

25
src/lib/prisma.ts Normal file
View File

@@ -0,0 +1,25 @@
import { PrismaClient } from "@prisma/client";
// Singleton pattern to prevent multiple PrismaClient instances during Next.js
// hot-module replacement in development. Without this, each hot reload creates
// a new PrismaClient and exhausts the PostgreSQL connection pool.
//
// In production, module caching ensures a single instance per process.
declare global {
// eslint-disable-next-line no-var
var __prisma: PrismaClient | undefined;
}
export const prisma =
globalThis.__prisma ??
new PrismaClient({
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"]
: ["error"],
});
if (process.env.NODE_ENV !== "production") {
globalThis.__prisma = prisma;
}