Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

14 — QA Checklist (Scheduler Module)

Test matrix for the scheduler backend + the (proposed) console. Sources: scheduler.service.ts, jobs/*.job.ts, bullmq.module.ts, dlq.setup.ts, idempotency.service.ts, tenant-context.service.ts. Backend items are testable today; console items are (proposed).


1. Registration & idempotency of defaults

  • Fresh boot registers exactly 10 repeatables, one per row of the table (01 §2; scheduler.service.ts:48-119).
  • Reboot with existing repeatables → no duplicates (:128-133 skip).
  • GET /scheduler returns 10 rows with correct {queue,name,pattern,tz}, tz defaulting UTC (:152-186).
  • Redis flush mid-run → next boot re-registers all 10 (journey 3, 03).
  • Custom job with identical name+pattern is silently skipped (:128-133) — UI mirrors the warn (08 §F1).

2. Missed runs & recovery

  • Simulate outage: kill worker > 1 h, restart → audit-flush resumes; backlog bounded by repeatable semantics (missed fire = skipped, not burst).
  • Manual re-trigger path (S5 (proposed)) enqueues exactly one child job.
  • Re-trigger after partial success does not double-deliver (IdempotencyService, idempotency.service.ts:11-20).
  • Operator can confirm "no run in expected window" from S1 badges (missed, C1) — depends on E4 (proposed).

3. Timezone / DST

  • All 10 defaults registered with tz: 'UTC' (scheduler.service.ts:54,62,70,76,82,89,96,103,110,117).
  • Custom schedules accept explicit tz (dto:38-41); cron fires at the wall-clock instant in that tz across a DST boundary.
  • Console renders UTC patterns without pretending to local run times (09 B8).
  • No 6-field cron accepted (5-field regex dto:20-24).

4. Overlap protection

  • A long-running child job that outlives its next trigger does not produce concurrent duplicate work (BullMQ repeatable + attempts config bullmq.module.ts:60-65; verify overlap behaviour is intended fire-and-forget per queue).
  • fee-reminder enqueues exactly one send-payment-reminder per due invoice (ISSUED/PARTIAL, due ≤ 3 days, fee-reminder.job.ts:16-23,25-41).
  • attendance-report-daily payload carries reportType correctly (attendance-report.job.ts:13-28).

5. Failure path & DLQ

  • Job fails 1×, 2× → retried with exponential 5 s backoff (bullmq.module.ts:60-65).
  • Fails 3rd time → DLQ record with originalQueue, originalJobId, originalJobName, data, failedReason, attemptsMade, failedAt (dlq.setup.ts:8-21).
  • DLQ replay preserves correlationId/tenantId (idempotency keys stay stable, idempotency.service.ts:12).
  • Failed-trigger retention: 7 d (scheduler.service.ts:146); global failed retention 14 d (bullmq.module.ts:63-64) — DLQ viewer shows what actually exists (S6 empty states honest, 09 B2).

6. Idempotency

  • sl:idempotency:{jobId} key set via SET NX PX 300 s (idempotency.service.ts:11-20); second delivery within TTL is dropped.
  • Expired key (job > 5 min) re-processes — accepted trade-off, documented.
  • QueueBridge event path dedupes on {correlationId}:{eventType} (queue-bridge.service.ts:44-46).

7. Tenant isolation

  • Workers restore context via tenantContext.run before touching repositories (report.worker.ts:18-30; also finance/attendance/admission/ inapp workers, 01 §7).
  • Gap: FeeReminderJob queries InvoiceRepository directly without wrapping in tenantContext.run (fee-reminder.job.ts:20-23) while the trigger payload carries tenantId: 'system' (scheduler.service.ts:139) — verify query scoping and either wrap the job or confirm intended cross-tenant scan. Open QA risk.
  • Trigger payloads never read tenantId from operator input; custom create DTO has no tenant field (create-schedule.dto.ts:18-41).

8. Security & RBAC

  • scheduler.read/create/delete enforced on E1/E2/E3 (scheduler.controller.ts:24,31,38).
  • Custom-job payload cannot inject arbitrary queue names (whitelist dto:4-16).
  • Console hides actions without permission (09 B4).

9. Console QA (proposed)

  • S1 poll loop: 60 s cadence, single-flight, background pause (10 §2, 13 §3).
  • S2 run history matches retention reality (1 h/100, 7 d, 14 d — S2 note).
  • S3 duplicate warn; invalid cron inline error matches server message "Invalid cron pattern (5 fields required)" (dto:22).
  • S6 replay button disabled until E8 exists.
  • a11y: badges icon+text; live regions; focus traps (11 §6).

10. Perf & load

  • listJobs across 10 queues stays < 1 s at 100+ repeatables each (scheduler.service.ts:152-186).
  • Poll of E1 + enrich does not exceed api tier 100 req/min (00-shared/07 §4).
  • DLQ listener attaches once per worker — no duplicate records on restart (dlq-listener.service.ts:31-44).