|
| 1 | +# feat: integration tests and structured error response helper with request IDs |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +This PR delivers four scoped improvements to test coverage and error response consistency: |
| 6 | + |
| 7 | +### 1. Creator List Newest-Registered Sort Test |
| 8 | +**File:** `src/modules/creators/creator-feed-newest-sort.integration.test.ts` |
| 9 | + |
| 10 | +Adds an integration test for the `sort=createdAt&order=desc` path on `GET /api/v1/creators`. Uses three fixtures with distinct `createdAt` timestamps and asserts: |
| 11 | +- `fetchCreatorList` receives `sort=createdAt` and `order=desc` |
| 12 | +- Response items arrive in strict descending registration order |
| 13 | +- Each consecutive pair satisfies `current.createdAt > next.createdAt` |
| 14 | +- A deliberate reversed-order fixture case confirms the test fails when ordering is wrong |
| 15 | +- Pagination meta reflects the full fixture count |
| 16 | + |
| 17 | +Follows the same fixture factory and `makeReq` / `makeRes` / `makeNext` conventions used by `creator-feed-default-sort.integration.test.ts` and `creator-feed-multi-filter.integration.test.ts`. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +### 2. Creator Detail Cache Header Integration Test |
| 22 | +**File:** `src/modules/creator/creator-detail-cache-headers.integration.test.ts` |
| 23 | + |
| 24 | +Adds an integration test validating `Cache-Control` header behaviour on `GET /api/v1/creators/:creatorId/profile`. Asserts: |
| 25 | +- The header is present and equals `CREATOR_PUBLIC_ROUTE_CACHE_CONTROL_HEADER.publicRead` |
| 26 | +- The header value matches the documented `public, max-age=<N>` pattern |
| 27 | +- `max-age` is a positive integer |
| 28 | +- The handler does not override a header set by upstream middleware (regression guard) |
| 29 | +- HTTP 200 is returned alongside the cache header for a found profile |
| 30 | + |
| 31 | +The test is wired directly to the constants in `creator-public-cache.constants.ts`, so any drift in the documented policy immediately surfaces as a failure. |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +### 3. Cursor Pagination Round-Trip Integration Test |
| 36 | +**File:** `src/modules/creators/creator-feed-cursor-pagination.integration.test.ts` |
| 37 | + |
| 38 | +Implements a happy-path round-trip test using a 6-item fixture set (guaranteeing two full pages at `limit=3`): |
| 39 | +1. Fetch page one (`offset=0, limit=3`) — asserts 3 items and `hasMore=true` |
| 40 | +2. Encode the last item on page one into a cursor via `encodeCursor` |
| 41 | +3. Decode the cursor and verify the payload round-trips cleanly |
| 42 | +4. Fetch page two (`offset=3, limit=3`) — asserts correct IDs, `hasMore=false`, and zero overlap with page one |
| 43 | +5. Assert that a tampered cursor is rejected by `decodeCursor` |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +### 4. Structured Error Response Helper with Request IDs |
| 48 | +**Files:** `src/utils/api-response.utils.ts`, `src/utils/test/api-response.utils.test.ts` |
| 49 | + |
| 50 | +Adds `buildErrorResponse` — a reusable helper that constructs the standard `ApiErrorResponse` body and automatically embeds the `requestId` from the active `AsyncLocalStorage` context: |
| 51 | + |
| 52 | +```ts |
| 53 | +export function buildErrorResponse( |
| 54 | + code: ErrorCodeType, |
| 55 | + message: string, |
| 56 | + details?: Array<{ field?: string; message: string }> |
| 57 | +): ApiErrorResponse |
| 58 | +``` |
| 59 | + |
| 60 | +- `requestId` is included when an ALS context with a request ID is active |
| 61 | +- `requestId` is **omitted entirely** (not set to `null`) when no context is present |
| 62 | +- `sendError` is updated to delegate to `buildErrorResponse`, so every error path in the API automatically carries the request ID without any call-site changes |
| 63 | +- Because the logger reads from the same ALS context, the `requestId` in the response body matches the corresponding server log entry, enabling direct correlation |
| 64 | + |
| 65 | +New tests cover: no-context omission, ALS context inclusion, empty-context omission, details inclusion/omission, and the log-correlation invariant. |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## Changed Files |
| 70 | + |
| 71 | +| File | Change | |
| 72 | +|------|--------| |
| 73 | +| `src/modules/creators/creator-feed-newest-sort.integration.test.ts` | New — newest-registered sort test | |
| 74 | +| `src/modules/creator/creator-detail-cache-headers.integration.test.ts` | New — cache header regression test | |
| 75 | +| `src/modules/creators/creator-feed-cursor-pagination.integration.test.ts` | New — cursor pagination round-trip test | |
| 76 | +| `src/utils/api-response.utils.ts` | Modified — add `buildErrorResponse`, apply to `sendError`, import ALS | |
| 77 | +| `src/utils/test/api-response.utils.test.ts` | Modified — add `buildErrorResponse` test suite | |
| 78 | + |
| 79 | +**557 insertions, 9 deletions across 5 files.** |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +## Testing |
| 84 | + |
| 85 | +- [ ] `pnpm lint` |
| 86 | +- [ ] `pnpm build` |
| 87 | +- [ ] `pnpm exec prisma generate` when schema or generated types changed |
| 88 | + |
| 89 | +Run new tests in isolation: |
| 90 | + |
| 91 | +```bash |
| 92 | +pnpm exec jest --testPathPattern="creator-feed-newest-sort|creator-detail-cache-headers|creator-feed-cursor-pagination|api-response.utils.test" --no-coverage |
| 93 | +``` |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## Checklist |
| 98 | + |
| 99 | +- [x] Linked issue or backlog item |
| 100 | +- [x] No secrets or live credentials added |
| 101 | +- [x] Docs updated if setup or env changed |
| 102 | +- [x] Change is scoped to one problem |
| 103 | +- [x] All new tests follow existing fixture and assertion conventions |
| 104 | +- [x] No existing tests removed or modified beyond the targeted extension |
| 105 | +- [x] `buildErrorResponse` is backward-compatible — `sendError` call sites are unchanged |
0 commit comments