From e4a6e2e540c0fa3930683b7e9252b63a1575a79f Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Wed, 20 May 2026 16:31:43 +0000 Subject: [PATCH] fix(GRO-1365): address QA review findings on api/#21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Fix vi.mock factory: importOriginal -> db.and/eq/exists/or stubs (removes ReferenceError from undeclared imports in test) 2. Remove MedicalAlert.id — not in schema/migration/DB, only in types 3. Replace z.string().max(100) coatType with z.enum for CoatType union 4. Fix test expecting coatType "smooth" (invalid) -> "double" (valid) 5. Add TC-API-3.8 through TC-API-3.15 to UAT_PLAYBOOK.md §4.3 Co-Authored-By: Claude Opus 4.7 --- UAT_PLAYBOOK.md | 8 ++++++++ apps/api/src/__tests__/petsExtendedFields.test.ts | 15 ++++++++------- apps/api/src/routes/pets.ts | 2 +- apps/api/src/types/index.ts | 1 - 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/UAT_PLAYBOOK.md b/UAT_PLAYBOOK.md index 68d6d25..8c31eb6 100644 --- a/UAT_PLAYBOOK.md +++ b/UAT_PLAYBOOK.md @@ -51,6 +51,14 @@ GroomBook API is a Hono-based REST service (TypeScript/Node.js) powering the pet | TC-API-3.5 | Delete pet | DELETE /api/pets/{id} | 200 OK, pet deleted | | TC-API-3.6 | Upload pet photo | POST /api/pets/{id}/photo/upload-url, then confirm | 200 OK, photo uploaded and key stored | | TC-API-3.7 | View pet photo | GET /api/pets/{id}/photo | 200 OK, presigned URL returned | +| TC-API-3.8 | Create pet with extended fields | POST /api/pets with coatType, temperamentScore, temperamentFlags, medicalAlerts, preferredCuts | 201 Created, all extended fields stored and returned | +| TC-API-3.9 | Update pet extended fields | PATCH /api/pets/{id} with coatType, temperamentScore, medicalAlerts | 200 OK, extended fields updated | +| TC-API-3.10 | Reject invalid coatType | POST /api/pets with coatType: "smooth" | 400 Bad Request, invalid coatType rejected | +| TC-API-3.11 | Reject out-of-range temperamentScore | POST /api/pets with temperamentScore: 0 or 6 | 400 Bad Request, score out of range rejected | +| TC-API-3.12 | Reject invalid medicalAlert severity | POST /api/pets with medicalAlerts severity: "critical" | 400 Bad Request, invalid severity rejected | +| TC-API-3.13 | Reject too many temperamentFlags | POST /api/pets with 21 temperamentFlags | 400 Bad Request, max 20 flags enforced | +| TC-API-3.14 | Reject too many preferredCuts | POST /api/pets with 21 preferredCuts | 400 Bad Request, max 20 cuts enforced | +| TC-API-3.15 | Reject too many medicalAlerts | POST /api/pets with 51 medicalAlerts | 400 Bad Request, max 50 alerts enforced | ### 4.4 Appointment Scheduling diff --git a/apps/api/src/__tests__/petsExtendedFields.test.ts b/apps/api/src/__tests__/petsExtendedFields.test.ts index a013d88..78d94d4 100644 --- a/apps/api/src/__tests__/petsExtendedFields.test.ts +++ b/apps/api/src/__tests__/petsExtendedFields.test.ts @@ -145,7 +145,8 @@ function makeDeleteChainable(): unknown { return chain; } -vi.mock("../db", () => { +vi.mock("../db", async (importOriginal) => { + const db = await importOriginal(); const pets = new Proxy({ _name: "pets" }, { get: (t, p) => p === "_name" ? "pets" : {} }); const appointments = new Proxy({ _name: "appointments" }, { get: (t, p) => p === "_name" ? "appointments" : {} }); return { @@ -163,10 +164,10 @@ vi.mock("../db", () => { }), pets, appointments, - and, - eq, - exists, - or, + and: db.and, + eq: db.eq, + exists: db.exists, + or: db.or, }; }); @@ -322,11 +323,11 @@ describe("Extended pet profile fields — update", () => { const res = await app.request(`/pets/${PET_ID}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ coatType: "smooth" }), + body: JSON.stringify({ coatType: "double" }), }); expect(res.status).toBe(200); const body = await res.json(); - expect(body.coatType).toBe("smooth"); + expect(body.coatType).toBe("double"); }); it("updates temperamentScore", async () => { diff --git a/apps/api/src/routes/pets.ts b/apps/api/src/routes/pets.ts index 379d2be..2ac78fd 100644 --- a/apps/api/src/routes/pets.ts +++ b/apps/api/src/routes/pets.ts @@ -24,7 +24,7 @@ const createPetSchema = z.object({ shampooPreference: z.string().max(500).optional(), specialCareNotes: z.string().max(2000).optional(), customFields: z.record(z.string(), z.string()).optional(), - coatType: z.string().max(100).optional(), + coatType: z.enum(["short", "medium", "long", "double", "wire", "silky", "curly", "hairless"]).optional(), temperamentScore: z.number().int().min(1).max(5).optional(), temperamentFlags: z.array(z.string().max(100)).max(20).optional(), medicalAlerts: z.array(z.object({ diff --git a/apps/api/src/types/index.ts b/apps/api/src/types/index.ts index 664e5da..ab23286 100644 --- a/apps/api/src/types/index.ts +++ b/apps/api/src/types/index.ts @@ -31,7 +31,6 @@ export interface Client { export type AlertSeverity = "low" | "medium" | "high"; export interface MedicalAlert { - id: string; type: string; description: string; severity: AlertSeverity;