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 (CRM Module)

Module-specific checklist layered on 00-shared/10_QA_Baseline.md + 00-shared/09 accessibility baseline. Every item testable; backend rules cite source lines.


1. Duplicate lead detection

  • Same email twice → second POST returns 409 Lead with email "x" already exists. (crm.service.ts:42-47); no second lead doc.
  • Email comparison is case-insensitive: A@X.com vs a@x.com collide (lead.schema.ts:52-53 lowercase; lead.repository.ts:19 lowercases query).
  • Duplicate check is tenant-scoped (scopedFilter, base.repository.ts:20-30): same email in two tenants → both allowed.
  • Create UI: 409 shows inline banner, form keeps values, no data loss, no duplicate double-submit (00-shared/08 §6).
  • PATCH changing email to an existing one → 409? — known gap: update path does not check duplicates (crm.service.ts:88-100 only merges fields); flag + track.

2. Status validation & transitions

  • Status accepts only new|contacted|qualified|converted|closed (lead.schema.ts:7-13); invalid value → 400 VALIDATION_ERROR (@IsEnum, update-lead.dto.ts:42-45).
  • Setting status: closed without closedReasonclosedAt auto-stamped (crm.service.ts:94-96); with reason → closedAt still stamped, reason stored.
  • Convert on converted lead → 400 Lead is already converted. (crm.service.ts:120-122); on closed → 400 Cannot convert a closed lead. (:123-125).
  • Convert without gradeId+academicYearId+classId → 400 with exact placement message (crm.service.ts:145-149).
  • Successful convert: status converted, convertedAt, convertedToStudentId set (crm.service.ts:161-164); one User + one Student doc created; no duplicate when user email already exists (reuse path :132-143).
  • Admission decision only from decidable statuses (ADMISSION_DECIDABLE_STATUSES, admission.schema.ts:20-25); others → 400 (admission.service.ts:141-145).
  • Decision maps: approve→approved, reject→rejected, waitlist→waitlisted; decidedAt set; workflow stage appended with approver + comment (admission.service.ts:147-155,239-255).
  • Convert admission only from approved → else 400 (admission.service.ts:174-178).

3. Assignment & ownership

  • PATCH assignedTo accepts MongoId (update-lead.dto.ts:72-75); invalid → 400.
  • List filter by assignedTo does not exist server-side (only statuscrm.controller.ts:37-45): "my leads" is client-side; verify no misleading server claims (flag as gap).
  • assignedTo is Staff ref (lead.schema.ts:79-80); display names resolve from staff module (cross-module read (planned)).
  • Follow-up createdBy is stamped from JWT user, never from body (crm.service.ts:111) — client cannot spoof.

4. Soft-delete semantics

  • No delete endpoints exist in CRM — UI must not offer delete; verify 405 on any client-invented DELETE.
  • BaseRepository.softDelete (base.repository.ts:68-74) exists but is unreachable from CRM controllers — when (planned) delete lands: deleted leads excluded from lists (isDeleted: false in scoped filter, base.repository.ts:20-30), version incremented.
  • Cross-tenant read of another tenant's lead/admission id → 404, not 403 or 500 (no existence leak).
  • Closed leads remain visible under status filter (never auto-purged); metadata survives all PATCHes.

5. Follow-up integrity

  • POST follow-up with invalid scheduledAt → 400 (@IsDateString, create-follow-up.dto.ts:9-11).
  • Follow-ups append (never replace): two POSTs → array length 2 (crm.service.ts:114).
  • completedAt optional; uncompleted follow-ups drive the derived "next follow-up due" (client logic — unit test followUpSummary).
  • Timestamps round-trip ISO-8601 in tenant timezone (00-shared/10 §3 pattern); overdue detection uses tenant-local now.

6. Admissions documents / interview

  • Document type enum: tc|marksheet|certificate|photo|other (admission.schema.ts:27-33); invalid → 400.
  • filename defaults to fileId server-side when absent (admission.service.ts:107).
  • First document on submitted admission → status auto-advances to documents_pending (admission.service.ts:112-114); on other open statuses → status unchanged.
  • Scheduling interview sets interview{...} + status interview_scheduled when different (admission.service.ts:124-132); re-schedule overwrites cleanly.
  • Interview mode restricted online|offline (schedule-interview.dto.ts:22-25; default offline).
  • Document/interview/edit on closed admission → 400 not editable (admission.service.ts:257-269); UI locks simultaneously.

7. List/pagination/sort contracts

  • Leads default sort createdAt: -1 (lead.repository.ts:31); admissions submittedAt: -1 (admission.repository.ts:29); campaigns createdAt: -1 (campaign.repository.ts:25) — verify row order client-side.
  • limit bounds (default 20; 1–100 per 00-shared/07 §5): limit=200 behavior verified (server default cap).
  • meta = {page, limit, totalItems, totalPages, hasNext, hasPrevious} (buildPaginationMeta); infinite scroll stops at hasNext=false.
  • Invalid status query value → 400 (typed LeadStatus/AdmissionStatus params, crm.controller.ts:42,108).
  • Stats endpoint sums match list counts per status (admission.service.ts:227-237); conversionRate = converted/total × 100, 1 decimal, 0 when empty.

8. Events & workers

  • Create lead emits LeadCreated with leadId/name/email/source (crm.service.ts:50-63); convert emits LeadConverted with leadId/studentId/userId (:166-173).
  • Admission emits AdmissionSubmitted|Approved|Rejected|Converted at the right transitions (admission.service.ts:54-57,158-168,219-223); waitlist emits none (documented behavior).
  • Reminder/expiry workers (planned) (IMPLEMENTATION_PLAN.md §1.3): when landed, verify findStale global scan (admission.repository.ts:53-62) excludes soft-deleted + respects cutoff; no cross-tenant action without tenantId in event.

9. Permissions & RBAC

  • Permission strings crm.read, crm.lead.manage, crm.campaign.manage (permissions.constants.ts:34-36) referenced by UI gates.
  • Known gap: endpoints are JwtAuthGuard-only (crm.controller.ts:29) — any authenticated user can write today; assert current behavior and track RBAC enforcement (planned) (IMPLEMENTATION_PLAN.md §5.1).
  • Client hides write actions without crm.lead.manage / crm.campaign.manage (defense-in-depth until server enforces).
  • admissions/stats correctness: no per-status leak beyond tenant scope.

10. Performance & offline

  • Leads list (20 rows) first frame from cache < 300 ms; ListView.builder mandatory (00-shared/11 §13).
  • S5 stats + list parallel fetch: stats failure never blocks list.
  • Offline: cached list + AppOfflineBanner; all CRM writes blocked offline with clear message (no offline queue by design, 13 §12).
  • Large follow-up arrays: timeline uses builder, no layout jank.
  • Detail fetch per navigation (no stale cache) — verify no blank flash (skeleton shown).

11. Accessibility (module additions to 00-shared/09 §12)

  • Status badges announce icon + label (no color-only: converted/closed, approved/rejected) (00-shared/09:76).
  • Timeline (follow-ups, workflow) fully readable via TalkBack/VoiceOver, per-stage semantics + dates.
  • Convert/decision dialogs: warnings read before focus lands on confirm.
  • Text scale 2×: filter chip rows scroll, no clipped status text.
  • Keyboard-only: chip arrows + Enter; Esc closes sheets/dialogs; focus returns to invoker.

12. Localization & formatting

  • All crm.* i18n keys present in en + org locales; server business-4xx text mapped via error-code→key fallback (00-shared/11 §9).
  • Dates via Intl tenant locale; wire format YYYY-MM-DD/ISO-8601 independent of display; RTL-safe rows.

13. Performance budgets (module)

CheckBudget
List first frame (cached)< 300 ms
Status filter round-trip< 500 ms p95 with skeleton kept
Detail open< 500 ms to content (network)
Convert flow (server chain)< 3 s with in-dialog progress
Admissions stats tile< 1 s; failure → "—"

14. Release gates (module additions)

  • e2e parity: leads CRUD + 409 duplicate + convert 400s + admission decision 400s pass against live backend (pattern: p1-operations.e2e-spec.ts).
  • Goldens: LeadStatusChip ×6, StatusBadge ×9, WorkflowTimeline, LeadListTile, FunnelStatCard — light/dark × 3 sizes.
  • No pending P0: duplicate-email UX, terminal-state locks, cross-tenant 404.