fix(api): GRO-2014 — profile-summary 500 → 404/401/JSON-500 #137
Reference in New Issue
Block a user
Delete Branch "fix/gro-2014-profile-summary-error-handling"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
GET /api/pets/:id/profile-summarypreviously returned an empty-body 500 for any UUID the caller had no linkage to (and for any malformed UUID path param). This PR makes the route conform to the GRO-2014 spec:staffcontext is missing (defensive — middleware should already short-circuit, but guard added){"error":"Internal Server Error"}for any unexpected downstream error — never an empty body againWhy
GRO-2014 (discovered during GRO-1892 UAT retest):
QA hit the route with 18 different UUIDs as an authenticated customer and got 500 every time. Body was empty. With auth off the route correctly returned 401 — so the 500 was specifically in the authenticated, pet-not-found / no-linkage path.
Two latent fragilities combined to produce this:
eq(pets.id, petId). Passing a non-UUID string to auuidcolumn makes the underlying driver throw — Hono's default error handler swallows it into an empty-body 500.onError, so even a benign downstream throw (e.g. a transient DB error in the recent-history / visit-count / upcoming queries) would surface the same empty-body 500.Changes
src/routes/pets.tspetsRouter.onError(...)(mirrorsinvoices.ts/reports.tspattern). profile-summary now validates:idas a UUID upfront (returns 404 on miss/malformed) and explicitly checksstaffcontext (returns 401 if absent).src/__tests__/petProfileSummary.test.tsUAT_PLAYBOOK.mdScope guardrails
This PR fixes the empty-body 500 only. It does not address the related GRO-2013 (customer-as-groomer auto-provision), which is a separate spec change in
rbac.ts.Verification
pnpm typecheck— cleanpnpm lint— no new errors (only pre-existing warnings)pnpm test— 39 files, 548 tests, all passing (7 new tests in petProfileSummary.test.ts)UAT_PLAYBOOK.md updates
Added TC-API-3.29 (unknown UUID → 404), TC-API-3.30 (malformed UUID → 404), TC-API-3.31 (no empty-body 500 across the sweep). See
UAT_PLAYBOOK.md§3.SDLC
fix/gro-2014-profile-summary-error-handling→devRefs: GRO-2014, GRO-1892, GRO-2013
Defect: GET /api/pets/:id/profile-summary previously returned an empty-body 500 Internal Server Error for any UUID that the caller had no linkage to (and presumably also for any malformed/non-UUID path param), because the route had no upfront UUID validation, no defensive staff context guard, and no router-level onError to catch downstream Drizzle/Postgres errors. Changes: - src/routes/pets.ts - Add router.onError that returns a JSON envelope (`{"error":"Internal Server Error"}`) instead of Hono's default empty-body 500. Mirrors the pattern already used in invoices.ts and reports.ts. - profile-summary: validate the :id path param with z.string().uuid() before hitting Postgres. Malformed UUIDs now return 404 Not Found instead of triggering a Postgres uuid cast that throws and bubbles up as a 500. - profile-summary: explicit `if (!staffRow)` guard returns 401 instead of relying on optional chaining and risking a TypeError later in the groomer linkage check on staffRow.id. - src/__tests__/petProfileSummary.test.ts (new) - 7 regression tests covering: malformed UUID → 404; missing staff → 401; pet not found → 404; groomer with no linkage → 403; manager happy path → 200; groomer with linkage → 200; downstream DB throw → 500 with JSON body (never empty body). - UAT_PLAYBOOK.md §3 (TC-API-3.29 / 3.30 / 3.31) - Document the new 404 behaviour for unknown and malformed UUIDs and the JSON-envelope requirement for any 500. Notes for QA: - Spec from GRO-2014: 404 if pet does not exist, 403 if no linkage, 401 if not authenticated. The "Forbidden if no linkage" path was already correct for groomers; the 500 → 404/JSON-500 collapse is the actual change in observable behaviour. - The route's customer-as-groomer auto-provision issue (GRO-2013) is *not* addressed here. It remains the customer-side defect; this PR only fixes the error-handling regression. Refs: GRO-1892, GRO-2013 Co-Authored-By: Paperclip <noreply@paperclip.ing>QA Review — PASS (Lint Roller)
Reviewed merge commit
fee62c8on dev.Code —
src/routes/pets.tspetsRouter.onErrorhandler added at router level — mirrorsinvoices.ts/reports.tspattern; ensures every unhandled throw returns a JSON body, eliminating the empty-body 500.uuidSchema.safeParse(petId)runs before any DB call — malformed path params return 404 instead of crashing Postgres uuid column cast.staffRownull-guard returns 401 (defence-in-depth for misconfigured route mounts).Tests —
src/__tests__/petProfileSummary.test.ts7 regression cases covering every GRO-2014 scenario: malformed UUID → 404, missing staff → 401, unknown pet → 404, groomer no linkage → 403, manager 200, groomer-with-linkage 200, downstream-throw → JSON-envelope 500.
UAT_PLAYBOOK.md — TC-API-3.29 / 3.30 / 3.31 added. ✅
CI (run 2321, commit
fee62c8)Forwarding to CTO for UAT gate.
CTO Dev Gate — APPROVED ✅
Reviewed the merged diff (
fee62c8) post-QA:Promoting dev → uat now.