14 - QA Checklist (Health Module)
- 1. Contract tests (endpoint)
- 2. Failure modes
- 3. Client behaviours (proposed screen)
- 4. Security / leakage
- 5. Load-shedding / performance
- 6. Planned / forward-looking (flag in tests)
Verification checklist for the Health module. Baseline in 00-shared/10. Items marked with the failure class they catch; every item maps to a source line so tests stay honest to the contract.
1. Contract tests (endpoint)
| # | Check | Expectation | Source |
|---|---|---|---|
| C1 | All-up request | 200; data.status: 'ok'; 4 keys in info; error: {} | health.controller.spec.ts:21-36, health.controller.ts:31-36 |
| C2 | Indicator count | exactly mongodb, redis, storage, bullmq - no more, no less | health.controller.ts:31-36 |
| C3 | Redis detail | info.redis.ping === 'PONG' on success | redis-health.indicator.ts:12-23 |
| C4 | BullMQ detail | info.bullmq.pendingJobs is a 4-element int array in injection order [emails, in-app, webhook-deliver, dlq] | bullmq-health.indicator.ts:21-30, queue.constants.ts:2,5,14 |
| C5 | Version both | /api/v1/health and /api/v2/health both 200 | @Version(['1','2']), health.controller.ts:28 |
| C6 | Auth bypass | request without any Authorization header → 200 (not 401/403) | @Public(), health.controller.ts:15; guards app.module.ts:129-131 |
| C7 | Rate cap | > 30 req/min → 429 | rate-limit.constants.ts:4 |
| C8 | Envelope | success body has success/message/data/timestamp/requestId | response-envelope.interceptor.ts:47-60 |
| C9 | No DTO | no validation 400 on any query/body (none accepted) | health.controller.ts:27-37 |
2. Failure modes
| # | Scenario | Expected | Gaps to flag |
|---|---|---|---|
| F1 | Redis down | 503; data.error.redis.status === 'down' in the result Terminus built; error envelope code: 'INTERNAL_SERVER_ERROR' (503 unmapped in filter) | error body does not carry the key - http-exception.filter.ts:56-58 |
| F2 | Partial - Redis + BullMQ down, Mongo + storage up | 503; info keeps healthy keys up | verify client never blanks healthy cards (13 §4) |
| F3 | All four down | 503; all keys in error | - |
| F4 | Storage provider throws | indicator catches → HealthCheckError → down | storage-health.indicator.ts:16-26 |
| F5 | Redis PING returns non-PONG | down (status computed, :15) | - |
| F6 | Queue unreachable (Redis-backed queues) | getJobCounts rejects → HealthCheckError → down | bullmq-health.indicator.ts:31-36 |
| F7 | BullMQ partial queue failure | Promise.all fails whole indicator → all-or-nothing; no per-queue down granularity | acceptable; document |
3. Client behaviours (proposed screen)
| # | Check | Expectation |
|---|---|---|
| G1 | 503 mapped to down-state, not error state | HealthRepository maps error envelope → HealthSnapshot(down); never a red error page (13 §4) |
| G2 | Partial failure rendering | red only on culprit cards; healthy cards stay green (06 S1 row 3) |
| G3 | Stale guard | snapshot > 60 s old renders stale + amber, never fresh-green (07 §4) |
| G4 | Poll pause/resume | backgrounded → timer canceled; resume → immediate refetch (13 §2) |
| G5 | 429 handling | poll pauses 60 s; retry disabled with countdown (10 I5) |
| G6 | In-flight guard | double-tap Retry issues exactly one request |
| G7 | PendingJobs drift | dlq > 0 shows amber chip (07 §3) |
| G8 | Detail sheet | renders from cache; zero network calls on open (06 S2) |
4. Security / leakage
| # | Check | Expectation |
|---|---|---|
| S1 | No secret leakage | payload carries only statuses + counts; no Redis URLs/credentials, queue connection strings, storage keys |
| S2 | Public endpoint abuse | 30 req/min cap enforced; no auth bypass of other endpoints via @Public() scope creep (health.controller.ts:15 is controller-scoped only) |
| S3 | No tenant data | response must never contain tenant names/ids; health is global (01 §5) |
| S4 | DoS via hanging probe | check has no internal timeout - a hung dependency stalls the request until client/middleware timeout; monitor p99 of /health |
5. Load-shedding / performance
| # | Check | Expectation |
|---|---|---|
| L1 | Probe storm | 30 concurrent probes → all served within rate window; no queue buildup (each probe runs 4 checks in parallel, health.controller.ts:31-36) |
| L2 | Indicator parallelism | 4 checks run concurrently (Promise batch via HealthCheckService) - total latency ≈ slowest check, not the sum |
| L3 | Monitor drift | pendingJobs in info is a sum, not per-state; alert thresholds must not be built on it (bullmq-health.indicator.ts:27-29) |
6. Planned / forward-looking (flag in tests)
| # | Item | Status |
|---|---|---|
| P1 | /health/live + /health/ready route tests | (planned) - END_TO_END_USER_FLOWS.md:738-740 |
| P2 | Disk/memory indicator tests | (planned) - not wired |
| P3 | Uptime/version fields | (planned) - absent from payload |
| P4 | Failing-key in 503 body | (proposed) - filter change needed (http-exception.filter.ts:56-58) |
| P5 | Authenticated admin variant | (proposed) - needs health.* permission (permissions.constants.ts:1-97) |