- Add lib/db (Drizzle schema: 7 tables + 6 enums + relations, postgres.js client), drizzle.config.ts, lib/db/enums.ts (Prisma-compatible enum objects) - Rewrite all 14 app consumers + 4 admin components to Drizzle - Move the test DB layer to Drizzle + in-process PGlite; convert all 8 integration test files + factories (371 tests green) - Remove Prisma: deps, prisma/ schema+migrations, lib/prisma.ts, Dockerfile prisma generate; wire db:generate/migrate/push/studio to drizzle-kit; update Makefile - Preserve the old seed as scripts/legacy-prisma-seed.cjs (needs a Drizzle rewrite)
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { readFileSync, readdirSync } from "fs";
|
|
import path from "path";
|
|
|
|
import { sql } from "drizzle-orm";
|
|
import { afterAll, beforeEach, vi } from "vitest";
|
|
|
|
import * as schema from "@/lib/db/schema";
|
|
|
|
/**
|
|
* Integration database wiring (Drizzle).
|
|
*
|
|
* - If TEST_DATABASE_URL is set, the real `@/lib/db` singleton is used unchanged,
|
|
* pointed at that Postgres. Migrations are applied once by the global setup;
|
|
* files run serially and truncate between tests.
|
|
*
|
|
* - Otherwise, `@/lib/db` is mocked with a Drizzle client backed by an in-process
|
|
* PGlite database (Postgres compiled to WASM) — real Postgres semantics, fully
|
|
* isolated per worker, no external server. Production code is never modified.
|
|
*/
|
|
|
|
const realDbUrl = process.env.TEST_DATABASE_URL?.trim();
|
|
if (realDbUrl) {
|
|
process.env.DATABASE_URL = realDbUrl;
|
|
}
|
|
|
|
vi.mock("@/lib/db", async () => {
|
|
if (process.env.TEST_DATABASE_URL?.trim()) {
|
|
return await vi.importActual<typeof import("@/lib/db")>("@/lib/db");
|
|
}
|
|
|
|
const { PGlite } = await import("@electric-sql/pglite");
|
|
const { drizzle } = await import("drizzle-orm/pglite");
|
|
|
|
const client = new PGlite();
|
|
const migrationsDir = path.resolve(process.cwd(), "lib", "db", "migrations");
|
|
const files = readdirSync(migrationsDir)
|
|
.filter((entry) => entry.endsWith(".sql"))
|
|
.sort();
|
|
for (const file of files) {
|
|
await client.exec(readFileSync(path.join(migrationsDir, file), "utf8"));
|
|
}
|
|
|
|
const db = drizzle(client, { schema });
|
|
return { db, schema };
|
|
});
|
|
|
|
const { db } = await import("@/lib/db");
|
|
|
|
export { db };
|
|
|
|
// Truncated in dependency order (children first) between every test for isolation.
|
|
const TABLES = [
|
|
"media_usage",
|
|
"media_asset",
|
|
"portfolio_asset",
|
|
"portfolio_section",
|
|
"portfolio_project",
|
|
"category",
|
|
"app_config",
|
|
];
|
|
|
|
export async function resetDb() {
|
|
const list = TABLES.map((table) => `"${table}"`).join(", ");
|
|
await db.execute(sql.raw(`TRUNCATE TABLE ${list} RESTART IDENTITY CASCADE;`));
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await resetDb();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// PGlite is in-process and torn down with the worker; the real postgres.js
|
|
// client is a shared singleton and is left open on purpose.
|
|
});
|