feat: deterministic seed, impersonation migration, test factories (GRO-110)
Phase 1 — Seed Hardening: - Replace all Math.random() calls in seed.ts with a Mulberry32 seeded PRNG (seed 42) so the same data set is reproduced on every run - Replace crypto.randomUUID() with a PRNG-based UUID v4 generator - Add manager (Jordan Lee) and receptionist (Sam Rivera) staff members to seed — previously all staff were groomers - New packages/db/src/reset.ts drops all tables/enums and re-runs migrate + seed; exposed as `pnpm db:reset` at root - Generate migration 0010_impersonation_sessions.sql for the impersonation_sessions and impersonation_audit_logs tables that were already in schema.ts but had no corresponding migration Phase 2 — Test Factories: - New packages/db/src/factories.ts with buildStaff, buildClient, buildPet, buildService, buildAppointment and resetFactoryCounters helpers - Exported via @groombook/db/factories subpath (package.json + vitest alias) - impersonation.test.ts updated to use buildStaff instead of hand-rolled fixture objects Closes #90 (Phases 1 + 2) Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* reset.ts — Drop all application tables and re-run migrations + seed.
|
||||
*
|
||||
* Intended for local development only. Never run against production.
|
||||
*
|
||||
* Usage:
|
||||
* DATABASE_URL=postgres://... npx tsx packages/db/src/reset.ts
|
||||
*/
|
||||
|
||||
import postgres from "postgres";
|
||||
|
||||
async function reset() {
|
||||
const url = process.env.DATABASE_URL;
|
||||
if (!url) {
|
||||
console.error("DATABASE_URL is not set");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
console.error("[FATAL] db:reset must not be run in production.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = postgres(url, { max: 1 });
|
||||
|
||||
console.log("Dropping all application tables...\n");
|
||||
|
||||
// Drop in dependency order (children before parents)
|
||||
await client`
|
||||
DO $$ DECLARE
|
||||
r RECORD;
|
||||
BEGIN
|
||||
FOR r IN (
|
||||
SELECT tablename FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
) LOOP
|
||||
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE';
|
||||
END LOOP;
|
||||
END $$;
|
||||
`;
|
||||
|
||||
// Drop custom enums
|
||||
await client`
|
||||
DO $$ DECLARE
|
||||
r RECORD;
|
||||
BEGIN
|
||||
FOR r IN (
|
||||
SELECT typname FROM pg_type
|
||||
WHERE typtype = 'e' AND typnamespace = (
|
||||
SELECT oid FROM pg_namespace WHERE nspname = 'public'
|
||||
)
|
||||
) LOOP
|
||||
EXECUTE 'DROP TYPE IF EXISTS ' || quote_ident(r.typname) || ' CASCADE';
|
||||
END LOOP;
|
||||
END $$;
|
||||
`;
|
||||
|
||||
// Drop the drizzle migrations tracking table
|
||||
await client`DROP TABLE IF EXISTS drizzle.__drizzle_migrations CASCADE`;
|
||||
await client`DROP SCHEMA IF EXISTS drizzle CASCADE`;
|
||||
|
||||
console.log("✓ All tables and enums dropped\n");
|
||||
|
||||
await client.end();
|
||||
}
|
||||
|
||||
reset().catch((err) => {
|
||||
console.error("Reset failed:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user