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;