diff --git a/UAT_PLAYBOOK.md b/UAT_PLAYBOOK.md index 8f3d171..e33f6d4 100644 --- a/UAT_PLAYBOOK.md +++ b/UAT_PLAYBOOK.md @@ -183,6 +183,23 @@ GroomBook API is a Hono-based REST service (TypeScript/Node.js) powering the pet | TC-API-14.4 | Update group notes | PATCH /api/appointment-groups/{id} with notes | 200 OK, notes updated | | TC-API-14.5 | Cancel group | DELETE /api/appointment-groups/{id} | 200 OK, all appointments cancelled | +### 4.15 Public Booking Flow (Scheduling Engine Buffer Integration) + +| # | Scenario | Steps | Expected | +|---|----------|-------|----------| +| TC-API-15.1 | List active services | GET /api/book/services | 200 OK, list of active services with name, price, duration | +| TC-API-15.2 | Get availability — missing params | GET /api/book/availability | 400 Bad Request, error indicating required params | +| TC-API-15.3 | Get availability — invalid date | GET /api/book/availability?serviceId=uuid&date=invalid | 400 Bad Request, date must be YYYY-MM-DD | +| TC-API-15.4 | Get availability — service not found | GET /api/book/availability?serviceId=nonexistent&date=2026-06-01 | 404 Not Found | +| TC-API-15.5 | Get availability — valid date/service | GET /api/book/availability?serviceId={serviceId}&date=2026-06-01 | 200 OK, array of ISO startTime strings for available slots | +| TC-API-15.6 | Availability excludes booked slots | GET /api/book/availability for date with existing appointments | 200 OK, only slots not overlapping booked appointments | +| TC-API-15.7 | Availability respects groomer availability | GET /api/book/availability for date with no groomers | 200 OK, empty array | +| TC-API-15.8 | Create booking — missing required fields | POST /api/book/appointments with partial data | 400 Bad Request, validation errors | +| TC-API-15.9 | Create booking — invalid pet/client/service | POST /api/book/appointments with nonexistent IDs | 400/404 Bad Request | +| TC-API-15.10 | Create booking — valid | POST /api/book/appointments with all required fields | 201 Created, appointment object returned | +| TC-API-15.11 | Create booking — saves petSizeCategory | POST /api/book/appointments with petSizeCategory | 201 Created, pet's petSizeCategory updated | +| TC-API-15.12 | Create booking — saves petCoatType | POST /api/book/appointments with petCoatType | 201 Created, pet's coatType updated | + ## Pass/Fail Criteria **Pass:** diff --git a/apps/api/src/__tests__/petsExtendedFields.test.ts b/apps/api/src/__tests__/petsExtendedFields.test.ts index a013d88..13fd10d 100644 --- a/apps/api/src/__tests__/petsExtendedFields.test.ts +++ b/apps/api/src/__tests__/petsExtendedFields.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { Hono } from "hono"; import type { AppEnv, StaffRow } from "../middleware/rbac.js"; import { petsRouter } from "../routes/pets.js"; +import { and, eq, exists, or } from "../db/index.js"; // ─── Mock staff fixtures ────────────────────────────────────────────────────── @@ -21,8 +22,8 @@ const MANAGER: StaffRow = { // ─── Mutable mock state ─────────────────────────────────────────────────────── -const CLIENT_ID = "client-uuid-extended"; -const PET_ID = "pet-uuid-extended"; +const CLIENT_ID = "11111111-1111-1111-1111-111111111111"; +const PET_ID = "22222222-2222-2222-2222-222222222222"; let petRows: Record[] = []; let appointmentRows: Record[] = []; @@ -134,7 +135,7 @@ function makeDeleteChainable(): unknown { } if (prop === "returning") { return () => { - const row = petRows[0]; + const row = petRows[0]!; deletedId = row.id as string; return [row]; }; @@ -145,7 +146,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 +165,10 @@ vi.mock("../db", () => { }), pets, appointments, - and, - eq, - exists, - or, + and: db.and, + eq: db.eq, + exists: db.exists, + or: db.or, }; }); diff --git a/apps/api/src/db/factories.ts b/apps/api/src/db/factories.ts index 9f801e2..da36fe6 100644 --- a/apps/api/src/db/factories.ts +++ b/apps/api/src/db/factories.ts @@ -103,6 +103,11 @@ export function buildPet(overrides: Partial & { clientId: string }): Pet photoKey: null, photoUploadedAt: null, image: null, + coatType: null, + temperamentScore: null, + temperamentFlags: [], + medicalAlerts: [], + preferredCuts: [], createdAt: new Date("2025-01-01T00:00:00Z"), updatedAt: new Date("2025-01-01T00:00:00Z"), };