# Backend API report — 2026-11-10

Environment: local isolated instance of branch `bugfix/qa-report` (HEAD 822ad43) on database `joshen_qa`. Hosted API: read-only checks only.

## Results (baseline, before the fixes)

| Measure | Value |
|---|---|
| PHPUnit | **1,742 passed, 0 failed**, 16,178 assertions, 1,106 s (previous QA: 1,728 passed, 1 failed; the fragile `TransportTrackingTest` now passes in the full run) |
| Postman gate (`postman:generate --check`) | OK. 424 routes, 99 baseline gaps remain (allowed; the list only shrinks) |
| Routes discovered | 413 under admin / client / provider / delivery (208 / 71 / 89 / 45), 424 in the Postman gate |
| Routes exercised by my sweep | 387 (26 auth-flow routes skipped) |
| 5xx / connection errors | **0 of 387** |
| Missing `{status,message,data}` envelope | 0 |
| Slow (> 3 s) locally | 0 |
| No token -> 401 | all guarded routes; exceptions are documented public guest routes and placeholder-404 routes |
| Wrong-app token refused | all guarded routes |
| Supervisor restriction | 403 on 180 of 203 admin routes |
| Codex-delegated tests | 421 routes **blocked** (sandbox could not reach the server); nothing claimed |

What the sweep proves: authentication, app isolation, RBAC denial, 404 on unknown ids, 422 on empty bodies, error envelope, no 5xx. It does **not** prove success paths, filters, sorting, pagination or persistence for each route; those were exercised only for the flows in `E2E_BUSINESS_CYCLES.md` and by the PHPUnit suite.

## Defects found (details in BUG_REGISTER.md)

- QA-03 (P2) customer id in admin payloads does not resolve at `admin/users/{id}`. Verified.
- QA-07 (P3) failed payment: 200 with generic message. Verified.
- QA-08 (P3) phone code format differs between endpoints. Verified.
- QA-09 (P3) package purchase stored as scheduled with NULL time. Verified.
- QA-NEW-01 (P3) Arabic validation messages contain English field labels and a hard-coded English sentence (coupon requests; others not audited).

## Documentation versus implementation

Not audited in depth. Verified only: the coupon request's field names are `discount_type` (`percent|fixed`), `discount_value`, `per_customer_limit`, `ends_at`, and admin login takes `login`, not `email`; I discovered these from code, so a client built from the docs should be checked against them. The admin list payload wraps rows under a resource key (`data.orders`) while other lists return an array; whether `docs/API_CONVENTIONS.md` allows both was not checked.

## Hosted environment (read-only)

QA-01 Telescope still 200; QA-02 no HTTPS redirect; CORS `*`; `/api/v1/health` 0.5 s. The Telescope and HTTPS fixes (bd2727f) were reverted in code by 822ad43, which is why the hosted server shows neither (or do not work there).

## Update after remediation (same day)

QA-03, QA-07, QA-NEW-01 (backend) are fixed in the working tree; QA-08 is documented; QA-09 awaits the owner. Full suite: 1,745 passed, 0 failed. Details, evidence and the remaining open items: `FULL_QA_REPORT.md` section 7 and `BUG_REGISTER.md`.

## Exhaustive request round (2026-11-11)

Local isolated instance (`joshen_qa`, fresh seed), scripts only; every number is from the run.

| Check | Result |
|---|---|
| Routes called with **real record ids** (resolved from the database, up to 6 candidates per route), empty body | 392 routes: 211 x 200, 93 x 422, 24 x 409, 32 x 403, 29 x 404, 3 x 429. The 32 x 403 came largely from my own sweep deactivating the seeded customer through `toggle-active`, so they are not counted as findings. Auth routes (login, register, verify, resend, logout, refresh) were tested separately |
| **Hostile payloads** on every route that returned a validation error | 87 routes x 8 payload kinds (5,000-character string, array, object, negative number, XSS/SQL-injection string, null, 20-digit number, emoji + bidi + NUL) = 696 requests: 628 x 422, 21 x 409, 17 x 429, 10 x 201, 7 x 200, 9 x 404 and **4 x 500 before the fix, 0 after** |
| Non-JSON or envelope-less responses | 0 |
| English text inside Arabic validation messages | 97 messages (18 distinct) before the fixes, 6 after (an enum value and echoed input keys) |
| Integer ids leaked instead of uuids | 0 found |
| Money fields returned as numbers | 0 real cases (77 hits were pagination `meta.total` counts) |
| Auth flows, 3 apps (client, provider, delivery) | PASS: register empty / BR-01 (each declaration missing) / bad phone, `966` normalised to `+966`, OTP verify wrong and right, resend, token reuse after refresh and logout refused, token used on another surface refused (403), logout without token 401, replay of a used verification token refused. OTP burst: 1 x 200 then 7 x 429 |
| Static translation audit (Codex, verified) | see QA-NEW-08; the key-parity count in the Codex report was wrong and is corrected in `BUG_REGISTER.md` |

Not exercised by the sweep (29 routes needing state it could not build, mostly detail, cancel and approve routes): admin cancellation detail, provider featured invoice detail, supervisor create/update/delete (needs `role_id`), category-request approve/reject, registration-request approve (needs uploaded documents), notification retry, template update, time-off and portfolio delete, change-request cancel/approve. Several were covered by hand in `E2E_BUSINESS_CYCLES.md` (change-request approve, support tickets, pages).
