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

Test scenarios across both backend (module spec) and client (this design). Every check maps to a source line. Severity: 🔴 blocker, 🟡 major, 🟢 minor.


1. Audience targeting

#SeverityCheckExpected / source
1.1🔴ROLE teacher resolves to active members onlyorg members roles: 'teacher', status: 'active' → userIds (announcement.service.ts:114-119); inactive excluded
1.2🔴GRADE by name resolves students of that gradegradeRepo.findOne({name})studentRepo.find({gradeId}) (:120-125); unknown grade ⇒ []
1.3🔴SECTION by name resolves students of that sectionsectionRepo.findOne({name}) → students (:126-135)
1.4🟡CUSTOM with single string vs arrayboth handled (:136-143); non-array string wrapped to [value]
1.5🟡CUSTOM with invalid ObjectIdnew Types.ObjectId(v) throws — unhandled ⇒ 500; gap (:142) — verify error UX
1.6🔴ALL type ⇒ targetUserIds = []:144-146 — UI must not render "0 recipients"; shows "Everyone"
1.7🔴Tenant isolation of audience resolutiongrade/section lookups scoped by tenantId (repos) — no cross-tenant leakage
1.8🟢Typo'd grade/section name silently broadcasts to nobody:122,129 — composer preview (planned); backend returns no warning
1.9🟢"For me" client filter matches server truthall ⇒ always; else meId ∈ targetUserIds (announcement.schema.ts:42-43)

2. Draft / publish lifecycle

#SeverityCheckExpected / source
2.1🔴Create ⇒ published: false, createdBy = token userannouncement.service.ts:31-37
2.2🔴Draft never appears in recipient "For me"client filter published (schema :45-46)
2.3🔴Publish idempotent — double publish no-opearly return when published (:68-70)
2.4🔴Publish sets publishedAt + resolved targetUserIds:73-78
2.5🟡Publish 404 for unknown idNotFoundException (:62-66)
2.6🟡Draft cannot be edited (no update route)recreate only — client dirty-guard, no false "edit" affordance
2.7🟢AnnouncementCreated emitted on draft save (not only publish):39-51 — downstream must tolerate draft events

3. Read receipts

#SeverityCheckExpected / source
3.1🔴Read adds exactly one receipt per user$addToSet (announcement.repository.ts:27-31) — idempotent
3.2🔴Receipt has userId + readAt{userId, readAt: now} (:28; announcement.schema.ts:23-29)
3.3🔴Mark-read 404announcement.service.ts:100
3.4🟡Duplicate read tap (double POST) no-ops$addToSet + UI disabled while in flight
3.5🔴Reads endpoint returns receipts without targets:104-107 — client must join targetUserIds from list payload
3.6🟡Unread count = targetUserIds − readBy.userIdderived client-side; test with partial reads
3.7🟢ALL-audience counts hiddentargetUserIds.length === 0 (:144-146)

4. Delivery via events / queues

#SeverityCheckExpected / source
4.1🔴AnnouncementPublished emitted with {announcementId, title, audienceType}announcement.service.ts:80-91; communication-events.ts:9-13
4.2🔴Gap: event-queue map has no Announcement routesevent-queue-map.ts:6-43 — verify a consumer exists before claiming delivery; otherwise delivery is (planned)
4.3🟡correlationId, tenantId, actorId on events:82-85 — audit continuity
4.4🟢Draft event (AnnouncementCreated) does not trigger recipient deliverypayload audienceType only; consumers must key on AnnouncementPublished

5. Expiry & priority (planned)

#SeverityCheckExpected / source
5.1🟡Expiry field absent today — UI must not render itschema has none (announcement.schema.ts:31-53)
5.2🟢When shipped: expired items hidden from feed, flagged on detaildesign intent only
5.3🟢Priority/pinned ordering affects feed sorttoday sort is createdAt: -1 only (announcement.service.ts:59)

6. List / query behavior

#SeverityCheckExpected / source
6.1🟡?audience= filter equals audience.typefilter['audience.type'] = audience (:58)
6.2🟡Sorted createdAt desc, tenant-scoped:59 + BaseRepository scope (announcement.repository.ts:12-18)
6.3🟢Unbounded list (no pagination) — perf test with 1k+ docsgap, (planned)
6.4🟢Index supports feed query{tenantId, published, createdAt: -1} (announcement.schema.ts:57)

7. Client states (per 06)

#SeverityCheck
7.1🔴Feed: loading / empty / error-offline / loaded each render correctly
7.2🔴Offline: banner + last-fetch cache; mark-read queued and flushed (forward-looking)
7.3🟡Composer dirty-guard + double-tap publish protection
7.4🟡Publish failure keeps draft id; retry doesn't duplicate
7.5🟢Receipts sheet auto-refresh (30 s, proposed) doesn't reset scroll

8. API contract conformance

#SeverityCheck
8.1🔴Requests match CreateAnnouncementDto exactly (create-announcement.dto.ts:29-49) — no extra fields
8.2🟡Validation errors from class-validator rendered inline per field (00-shared/07)
8.3🟡401 (JWT guard, announcements.controller.ts:11) → global login flow
8.4🟢Envelope (success/error) parsed per 00-shared/07

9. Visual & a11y

#SeverityCheck
9.1🟡Unread dot + badge contrast in light and dark theme (11 §6)
9.2🟡No color-only semantics: draft/published/unread all have text labels
9.3🟢Composer errors announced via live region; focus moves to first invalid
9.4🟢Tablet two-pane feed/detail syncs selection correctly

10. Data integrity

#SeverityCheck
10.1🔴No cross-tenant reads (receipts of tenant A invisible to tenant B)
10.2🟡Soft-delete filter (BaseRepository) hides deleted announcements from all lists
10.3🟢title/body trimmed on save (announcement.schema.ts:33-37)