From e9f94a2bd77181ac4e3b321af36963dd30bc3e12 Mon Sep 17 00:00:00 2001 From: Flea Flicker Date: Tue, 2 Jun 2026 20:11:45 +0000 Subject: [PATCH] fix(seed): GRO-2100 run uat-groomer linkage AFTER services seed (regression in #151) (#153) fix(seed): GRO-2100 run uat-groomer linkage after services seed (#153) Co-authored-by: Flea Flicker Co-committed-by: Flea Flicker --- packages/db/src/seed.ts | 48 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/packages/db/src/seed.ts b/packages/db/src/seed.ts index 13cf103..8e9d376 100644 --- a/packages/db/src/seed.ts +++ b/packages/db/src/seed.ts @@ -401,7 +401,9 @@ const servicesDef = [ * * In seedKnownUsers() this replaces the inline UAT-staff block. */ -async function seedUatStaffAccounts(db: ReturnType) { +async function seedUatStaffAccounts( + db: ReturnType, +): Promise { // ── Staff: UAT Super User (oidcSub from SEED_UAT_SUPER_OIDC_SUB env var) ── const uatSuperOidcSub = process.env.SEED_UAT_SUPER_OIDC_SUB; if (uatSuperOidcSub) { @@ -677,7 +679,12 @@ async function seedUatStaffAccounts(db: ReturnType) { // We deterministically link the UAT groomer to the UAT customer's first pet // ("UAT Pup Alpha") and leave the second pet ("UAT Pup Beta") UNLINKED so // TC-UAT-2 (200) and TC-UAT-3 (403) can both hardcode the stable petIds. - await seedUatGroomerLinkage(db, uatCustomerClientId); + // + // The linkage call itself is performed by the caller AFTER the `services` + // catalogue has been seeded (this helper runs before services exist, + // which previously caused the linkage to be silently skipped on every + // reset). GRO-2100 follow-up. + return uatCustomerClientId; } /** @@ -692,12 +699,18 @@ async function seedUatStaffAccounts(db: ReturnType) { */ async function seedUatGroomerLinkage( db: ReturnType, - customerClientId: string, + customerClientId: string | null, ): Promise { const uatGroomerEmail = "uat-groomer@groombook.dev"; const LINKED_PET_ID = "c0000001-0000-0000-0000-000000000002"; // UAT Pup Alpha const APPT_ID = "a0000001-0000-0000-0000-000000000001"; + // Skip silently if the UAT Customer client wasn't created (non-UAT seed + // profile, e.g. seedKnownUsers() in an env without the UAT personas). + if (!customerClientId) { + return; + } + // Only run if the UAT groomer staff record actually exists — dev/test seeds // that don't set SEED_UAT_STAFF_OIDC_SUB should not crash. const [uatGroomerStaff] = await db @@ -720,6 +733,19 @@ async function seedUatGroomerLinkage( return; } + // Skip if the linked pet hasn't been seeded yet (defensive: caller should + // ensure pets exist; if the helper is re-ordered later we don't want to + // crash here). + const [linkedPet] = await db + .select({ id: schema.pets.id }) + .from(schema.pets) + .where(eq(schema.pets.id, LINKED_PET_ID)) + .limit(1); + if (!linkedPet) { + console.warn(`⚠ GRO-2100: UAT Pup Alpha (${LINKED_PET_ID}) not found — skipping uat-groomer linkage`); + return; + } + // The "Bath & Brush" service id is stable across the reset; falls back to // any active service if it has not been seeded yet (e.g. seedKnownUsers // runs in isolation). @@ -847,7 +873,7 @@ async function seedKnownUsers() { // ── UAT staff accounts + Better Auth credentials (shared impl) ────────────── // Extracted into seedUatStaffAccounts() so it runs in both seedKnownUsers() // and the full seed() UAT branch. - await seedUatStaffAccounts(db); + const uatCustomerClientId = await seedUatStaffAccounts(db); // ── Services: idempotent upsert keyed on `id` ───────────────────────────── // GRO-2064: previously keyed on `services.name` while writing a @@ -875,6 +901,12 @@ async function seedKnownUsers() { } console.log(`✓ Seeded ${demoSvcs.length} services`); + // GRO-2100: deterministic uat-groomer ↔ UAT Pup Alpha linkage. Must run + // AFTER services are seeded (this helper looks up an active service id + // to attach to the appointment; on a fresh reset there are none yet at + // the time seedUatStaffAccounts() returns). + await seedUatGroomerLinkage(db, uatCustomerClientId); + // ── Client: Demo Client ── const [existingClient] = await db .select() @@ -1031,7 +1063,7 @@ async function seed() { // ── UAT staff accounts + Better Auth credentials (shared impl) ────────────── // Seeds deterministic UAT staff with numeric OIDC subs and Better Auth credentials. // Must run AFTER random staff are created so upserts land correctly. - await seedUatStaffAccounts(db); + const uatCustomerClientId = await seedUatStaffAccounts(db); // ── Services ── // GRO-2064: key the upsert on `services.id` (not `name`) so deterministic @@ -1058,6 +1090,12 @@ async function seed() { } console.log(`✓ Created ${servicesDef.length} services`); + // GRO-2100: deterministic uat-groomer ↔ UAT Pup Alpha linkage. Must run + // AFTER services are seeded (this helper looks up an active service id + // to attach to the appointment; on a fresh reset there are none yet at + // the time seedUatStaffAccounts() returns). + await seedUatGroomerLinkage(db, uatCustomerClientId); + // ── Clients & Pets ── const now = new Date(); const appointmentsBackDate = new Date(now);