9622b109d0
feat(GRO-1177): add pet profile summary endpoint (#30) Adds GET /api/pets/:id/profile-summary with aggregated pet profile, grooming history, visit count, and upcoming appointment. Co-Authored-By: Paperclip <noreply@paperclip.ing>
20 KiB
20 KiB
UAT Playbook — GroomBook API
Overview
GroomBook API is a Hono-based REST service (TypeScript/Node.js) powering the pet grooming management platform. Handles authentication, client/pet management, appointment scheduling, invoicing, payments, staff management, and the customer portal.
Environments
| Environment | URL |
|---|---|
| Dev | dev.groombook.dev |
| UAT | uat.groombook.dev |
| Prod | demo.groombook.app |
Pre-conditions
- UAT environment accessible and healthy
- Test accounts seeded (manager, staff, client personas)
- OIDC authentication provider configured
- Seed data present (clients, pets, services, staff)
Test Cases
4.0 Health Check
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-0.1 | Unauthenticated health check | GET /api/health | 200 OK, {"status":"ok"} |
Note (GRO-1544): Health endpoint registered on
apibasePath before auth middleware at/api/health. The old path/healthwas incorrect (routed to web pod via HTTPRoute/*rule).
4.1 Authentication
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-1.1 | Login via OIDC | POST to OIDC provider callback, verify JWT token issued | 200 OK, JWT returned with valid claims |
| TC-API-1.4 | Email+password login (UAT) | POST /api/auth/sign-in/email with uat-super@groombook.dev + SEED_UAT_SUPER_PASSWORD | 200 OK, session cookie returned |
| TC-API-1.5 | Email+password login — groomer | POST /api/auth/sign-in/email with uat-groomer@groombook.dev + SEED_UAT_GROOMER_PASSWORD | 200 OK, session cookie returned |
| TC-API-1.6 | Email+password login — customer | POST /api/auth/sign-in/email with uat-customer@groombook.dev + SEED_UAT_CUSTOMER_PASSWORD | 200 OK, session cookie returned |
| TC-API-1.7 | Email+password login — tester | POST /api/auth/sign-in/email with uat-tester@groombook.dev + SEED_UAT_TESTER_PASSWORD | 200 OK, session cookie returned |
| TC-API-1.8 | Email+password — invalid password | POST /api/auth/sign-in/email with wrong password | 400 Bad Request, error returned |
| TC-API-1.9 | Email+password — unknown user | POST /api/auth/sign-in/email with non-existent email | 400 Bad Request, error returned |
| TC-API-1.10 | Auto-provision on first OIDC login | First login as a Better-Auth user with no existing staff record | 200 OK, access granted; groomer staff record auto-created with name/email from user table |
| TC-API-1.11 | Existing staff unaffected by OIDC login | Login as uat-groomer@groombook.dev (email+password), then GET /api/staff to find that record | 200 OK, staff record unchanged — no duplicate created, original role and isSuperUser preserved |
| TC-API-1.12 | Auto-provisioned role and superUser flags | After TC-API-1.10, GET /api/staff and inspect the auto-created record | role = "groomer", isSuperUser = false, active = true |
| TC-API-1.13 | Name fallback — user.name present | Auto-provision where Better-Auth user has name set | Staff name = user.name value from user table |
| TC-API-1.14 | Name fallback — no name, email present | Auto-provision where Better-Auth user has name = null, email = "test@example.com" | Staff name = "test" (email prefix before @) |
| TC-API-1.15 | Name fallback — no name, no email | Auto-provision where Better-Auth user has name = null, email = null | Staff name = "Unknown" |
| TC-API-1.16 | OIDC login — Terraform-provisioned user | Initiate OIDC login as any UAT persona (uat-super, uat-groomer, uat-customer, uat-tester), complete authentik callback | 200 OK, session created — no account_not_linked error |
SSO Login Journey (Authentik OIDC end-to-end)
| # | Scenario | Steps | Pass Criteria | Fail Criteria |
|---|---|---|---|---|
| TC-API-1.17 | SSO redirect to Authentik | Navigate to app → sign-in page shown → click "Sign in with SSO" | Redirected to Authentik at auth.farh.net | 403 error, redirect loop, no SSO button |
| TC-API-1.18 | Authenticate with valid OIDC credentials | At Authentik login page, enter valid credentials and authenticate | Redirected back to app with valid session | Redirect loop, 403, missing session cookie |
| TC-API-1.19 | SSO user auto-provisioned as groomer | Complete SSO login as a user with no pre-existing staff record | 200 response; groomer staff record auto-created; session active | 403 Forbidden, staff record not created |
| TC-API-1.20 | Existing staff record resolves correctly | Complete SSO login as uat-groomer (pre-existing staff) | 200 OK, correct staff identity resolved, no duplicate record created | 403, duplicate record, wrong staff data |
| TC-API-1.21 | SSO session grants dashboard access | After TC-API-1.18 SSO login, GET /api/staff/me | 200 OK, valid staff record returned, correct role displayed | 401/403, missing session, wrong identity |
OOBE Flow Post-Login
| # | Scenario | Steps | Pass Criteria | Fail Criteria |
|---|---|---|---|---|
| TC-API-1.22 | Fresh DB reports needsSetup | On a fresh DB (no super user), GET /api/setup/status | needsSetup: true returned | needsSetup: false when it should be true |
| TC-API-1.23 | Configure OIDC via auth-provider endpoint | POST /api/setup/auth-provider with valid OIDC config | 200 OK, auth provider configured, no 403 | 403, setup blocked, invalid config rejected |
| TC-API-1.24 | Complete setup creates super user | POST /api/setup with business name (after TC-API-1.23) | First user becomes super user, setup completes | Setup errors, 403 on admin endpoints |
| TC-API-1.25 | Super user accesses admin features | After TC-API-1.24, GET /api/staff/me and verify isSuperUser: true | isSuperUser: true, admin endpoints accessible | 403 on admin, isSuperUser: false |
| TC-API-1.26 | Auto-provision skipped during OOBE | During fresh setup (needsSetup: true), complete OIDC login — verify no duplicate staff record created before setup completes | No duplicate staff, OOBE completes successfully | Duplicate staff record, 403 before setup, auto-provision interferes with OOBE |
4.2 Client Management
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-2.1 | List clients | GET /api/clients | 200 OK, list of active clients returned |
| TC-API-2.2 | Get client details | GET /api/clients/{id} | 200 OK, client details returned |
| TC-API-2.3 | Create client | POST /api/clients with valid data | 201 Created, client record created |
| TC-API-2.4 | Update client | PATCH /api/clients/{id} with updated fields | 200 OK, client updated |
| TC-API-2.5 | Disable client | PATCH /api/clients/{id} with status: "disabled" | 200 OK, client marked as disabled |
| TC-API-2.6 | Delete client | DELETE /api/clients/{id}?confirm=true | 200 OK, client deleted (if no appointments) |
4.3 Pet Management
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-3.1 | List pets | GET /api/pets | 200 OK, list of pets returned |
| TC-API-3.2 | Get pet details | GET /api/pets/{id} | 200 OK, pet details including history returned |
| TC-API-3.3 | Add pet | POST /api/pets with valid pet data | 201 Created, pet record created |
| TC-API-3.4 | Update pet | PATCH /api/pets/{id} with updated fields | 200 OK, pet updated |
| 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 |
| TC-API-3.16 | Get pet profile summary | GET /api/pets/{id}/profile-summary | 200 OK, aggregated profile with grooming history, visit count, upcoming appointment |
| TC-API-3.17 | Get pet profile summary — groomer restricted | GET /api/pets/{id}/profile-summary as groomer with no pet linkage | 403 Forbidden |
| TC-API-3.18 | Get pet profile summary — visitCount returns full count | GET /api/pets/{id}/profile-summary with 2+ completed appointments | visitCount >= 2 (not capped at 1) |
| TC-API-3.19 | Get pet profile summary — upcomingAppointment excludes past | GET /api/pets/{id}/profile-summary with a past confirmed/scheduled appointment | upcomingAppointment is null (past appointments filtered by startTime >= now) |
4.4 Appointment Scheduling
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-4.1 | List appointments | GET /api/appointments | 200 OK, list of appointments returned |
| TC-API-4.2 | Get appointment details | GET /api/appointments/{id} | 200 OK, appointment details returned |
| TC-API-4.3 | Create single appointment | POST /api/appointments with valid data | 201 Created, appointment created |
| TC-API-4.4 | Create recurring appointment | POST /api/appointments with recurrence object | 201 Created, series of appointments created |
| TC-API-4.5 | Update appointment | PATCH /api/appointments/{id} with updated fields | 200 OK, appointment updated |
| TC-API-4.6 | Reschedule with cascade | PATCH /api/appointments/{id} with cascadeMode: "this_and_future" | 200 OK, future appointments updated |
| TC-API-4.7 | Cancel appointment | DELETE /api/appointments/{id} | 200 OK, appointment marked as cancelled |
| TC-API-4.8 | Confirm appointment | POST /api/appointments/{id}/confirm | 200 OK, confirmation status set to confirmed |
| TC-API-4.9 | Cancel confirmation | POST /api/appointments/{id}/cancel | 200 OK, confirmation cancelled |
| TC-API-4.10 | Conflict detection | POST /api/appointments with conflicting time | 409 Conflict, error message returned |
4.5 Services
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-5.1 | List services | GET /api/services | 200 OK, list of active services returned |
| TC-API-5.2 | Get service details | GET /api/services/{id} | 200 OK, service details returned |
| TC-API-5.3 | Create service | POST /api/services with valid data | 201 Created, service created |
| TC-API-5.4 | Update service | PATCH /api/services/{id} with updated fields | 200 OK, service updated |
| TC-API-5.5 | Delete service | DELETE /api/services/{id} | 200 OK, service deleted |
4.6 Staff Management
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-6.1 | List staff | GET /api/staff | 200 OK, list of active staff returned |
| TC-API-6.2 | Get staff details | GET /api/staff/{id} | 200 OK, staff details returned |
| TC-API-6.3 | Create staff | POST /api/staff with valid data | 201 Created, staff created |
| TC-API-6.4 | Update staff | PATCH /api/staff/{id} with updated fields | 200 OK, staff updated |
| TC-API-6.5 | Delete staff | DELETE /api/staff/{id} | 200 OK, staff deleted (if no appointments) |
| TC-API-6.6 | RBAC check | Access manager-only endpoint as groomer | 403 Forbidden, error message returned |
4.7 Invoicing & Payments
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-7.1 | List invoices | GET /api/invoices | 200 OK, list of invoices returned |
| TC-API-7.2 | Get invoice details | GET /api/invoices/{id} | 200 OK, invoice with line items returned |
| TC-API-7.3 | Create invoice | POST /api/invoices with line items | 201 Created, invoice created |
| TC-API-7.4 | Create from appointment | POST /api/invoices/from-appointment/{appointmentId} | 201 Created, invoice created from appointment |
| TC-API-7.5 | Update invoice | PATCH /api/invoices/{id} with status and payment method | 200 OK, invoice updated |
| TC-API-7.6 | Process payment via Stripe | POST /api/invoices/{id}/pay with Stripe data | 200 OK, payment intent created |
| TC-API-7.7 | Save tip splits | POST /api/invoices/{id}/tip-splits with splits array | 201 Created, tip splits saved |
| TC-API-7.8 | Process refund | POST /api/invoices/{id}/refund with amount | 200 OK, refund processed |
4.8 Customer Portal
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-8.1 | Access portal | GET /api/portal/me with valid session token | 200 OK, client profile returned |
| TC-API-8.2 | View portal appointments | GET /api/portal/appointments | 200 OK, list of client's appointments returned |
| TC-API-8.3 | Confirm appointment via portal | POST /api/portal/appointments/{id}/confirm | 200 OK, appointment confirmed |
| TC-API-8.4 | Cancel appointment via portal | POST /api/portal/appointments/{id}/cancel | 200 OK, appointment cancelled |
| TC-API-8.5 | Add waitlist entry | POST /api/portal/waitlist with pet and service | 201 Created, waitlist entry created |
| TC-API-8.6 | View portal invoices | GET /api/portal/invoices | 200 OK, list of client's invoices returned |
| TC-API-8.7 | Pay multiple invoices | POST /api/portal/invoices/pay-multiple with invoice IDs | 200 OK, payment intent created |
4.9 Waitlist
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-9.1 | List waitlist | GET /api/waitlist | 200 OK, list of waitlist entries returned |
| TC-API-9.2 | Add to waitlist | POST /api/waitlist with client, pet, service | 201 Created, entry added |
| TC-API-9.3 | Promote from waitlist | Create appointment from waitlist entry | 201 Created, appointment created, waitlist updated |
4.10 Search
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-10.1 | Global search clients | GET /api/search?q={client_name} | 200 OK, matching clients returned |
| TC-API-10.2 | Global search pets | GET /api/search?q={pet_name} | 200 OK, matching pets with owners returned |
| TC-API-10.3 | Search by email | GET /api/search?q={email} | 200 OK, matching client returned |
| TC-API-10.4 | Search by phone | GET /api/search?q={phone} | 200 OK, matching client returned |
4.11 Reports
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-11.1 | Revenue summary | GET /api/reports/summary?from={date}&to={date} | 200 OK, revenue KPIs returned |
| TC-API-11.2 | Revenue by period | GET /api/reports/revenue?groupBy=day | 200 OK, daily revenue breakdown returned |
| TC-API-11.3 | Appointment analytics | GET /api/reports/appointments | 200 OK, appointment stats returned |
| TC-API-11.4 | Service popularity | GET /api/reports/services | 200 OK, service usage stats returned |
| TC-API-11.5 | Client retention | GET /api/reports/clients | 200 OK, new/returning/churn client data returned |
| TC-API-11.6 | Tip splits report | GET /api/reports/tip-splits | 200 OK, tip earnings per staff returned |
| TC-API-11.7 | Export revenue CSV | GET /api/reports/export.csv?type=revenue | 200 OK, CSV file downloaded |
4.12 Impersonation
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-12.1 | Start impersonation session | POST /api/impersonation/sessions with clientId | 201 Created, session token returned |
| TC-API-12.2 | Get session details | GET /api/impersonation/sessions/{id} | 200 OK, session details returned |
| TC-API-12.3 | Extend session | POST /api/impersonation/sessions/{id}/extend | 200 OK, session expiry extended |
| TC-API-12.4 | End session | POST /api/impersonation/sessions/{id}/end | 200 OK, session marked as ended |
| TC-API-12.5 | Log audit entry | POST /api/impersonation/sessions/{id}/log | 201 Created, audit log entry created |
| TC-API-12.6 | View audit log | GET /api/impersonation/sessions/{id}/audit-log | 200 OK, audit trail returned |
4.13 Settings & Setup
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-13.1 | Get business settings | GET /api/admin/settings | 200 OK, business settings returned |
| TC-API-13.2 | Update business settings | PATCH /api/admin/settings with updated values | 200 OK, settings updated |
| TC-API-13.3 | Upload logo | POST /api/admin/settings/logo/upload with file | 200 OK, logo uploaded and stored |
| TC-API-13.4 | View logo | GET /api/admin/settings/logo | 200 OK, logo image returned |
| TC-API-13.5 | Delete logo | DELETE /api/admin/settings/logo | 200 OK, logo removed |
| TC-API-13.6 | Check setup status | GET /api/setup/status | 200 OK, setup needs returned |
| TC-API-13.7 | Complete setup | POST /api/setup with business name | 201 Created, super user created |
| TC-API-13.8 | Configure auth provider | POST /api/setup/auth-provider with OIDC config | 201 Created, auth provider configured |
| TC-API-13.9 | Test auth provider | POST /api/setup/auth-provider/test with issuer URL | 200 OK, OIDC discovery successful |
4.14 Appointment Groups
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-14.1 | List appointment groups | GET /api/appointment-groups | 200 OK, list of groups returned |
| TC-API-14.2 | Get group details | GET /api/appointment-groups/{id} | 200 OK, group with appointments returned |
| TC-API-14.3 | Create group booking | POST /api/appointment-groups with client and pets | 201 Created, group and appointments created |
| 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 Buffer Rules
| # | Scenario | Steps | Expected |
|---|---|---|---|
| TC-API-15.1 | List buffer rules | GET /api/admin/buffer-rules | 200 OK, list of active buffer rules returned |
| TC-API-15.2 | Create buffer rule | POST /api/admin/buffer-rules with service, species, sizeCategory, bufferMinutes | 201 Created, buffer rule created |
| TC-API-15.3 | Update buffer rule | PATCH /api/admin/buffer-rules/{id} with updated bufferMinutes | 200 OK, buffer rule updated |
| TC-API-15.4 | Delete buffer rule | DELETE /api/admin/buffer-rules/{id} | 200 OK, buffer rule removed |
| TC-API-15.5 | Reject invalid bufferMinutes | POST /api/admin/buffer-rules with bufferMinutes: -5 | 400 Bad Request, invalid bufferMinutes rejected |
| TC-API-15.6 | Reject missing required fields | POST /api/admin/buffer-rules with service only | 400 Bad Request, species and sizeCategory required |
| TC-API-15.7 | Booking uses buffer | Book appointment for pet with sizeCategory; verify duration reflects buffer | 201 Created, appointment duration includes buffer time |
Pass/Fail Criteria
Pass:
- All test cases execute without errors
- Expected results match actual results
- No regressions in previously working features
- API responses have correct status codes and data structures
- Authentication and authorization enforced correctly
- Business rules (conflicts, validations) work as expected
Fail:
- Any unexpected result or error
- API returns incorrect status codes
- Data integrity issues
- Authentication/authorization bypass
- Business rules not enforced
- Severity documented with steps to reproduce and screenshot
Update Policy
Any PR that changes user-facing behaviour MUST update this file. Test cases must be added, modified, or removed to reflect the new behaviour. The PR description must reference which playbook section was updated (e.g., "Updated UAT_PLAYBOOK.md §4.4 — new appointment rescheduling flow").