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:
Scrubs McBarkley
2026-03-21 18:59:23 +00:00
parent 49c6550bf9
commit ad6024f3d9
9 changed files with 327 additions and 42 deletions
+4 -14
View File
@@ -2,22 +2,12 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
import { Hono } from "hono";
import type { JwtPayload } from "../middleware/auth.js";
import type { AppEnv, StaffRow } from "../middleware/rbac.js";
import { buildStaff } from "@groombook/db/factories";
// ─── Mock data ───────────────────────────────────────────────────────────────
// ─── Mock data (built with factories for schema-safe defaults) ────────────────
const MANAGER_STAFF = {
id: "staff-manager-id",
oidcSub: "oidc-manager-sub",
role: "manager",
name: "Manager",
};
const GROOMER_STAFF = {
id: "staff-groomer-id",
oidcSub: "oidc-groomer-sub",
role: "groomer",
name: "Groomer",
};
const MANAGER_STAFF = buildStaff({ id: "staff-manager-id", oidcSub: "oidc-manager-sub", role: "manager", name: "Manager" });
const GROOMER_STAFF = buildStaff({ id: "staff-groomer-id", oidcSub: "oidc-groomer-sub", role: "groomer", name: "Groomer" });
const CLIENT = { id: "aabbccdd-1111-2222-3333-444444444444", name: "Fido Owner" };