14 — QA Checklist (Attendance Module)
- 1. Data correctness — uniqueness & upsert
- 2. Edit-after-notification / correction flows
- 3. Timezone & date handling
- 4. Large-class & performance
- 5. Offline marking
- 6. Idempotent retries & partial failures
- 7. Permissions & roles
- 8. Biometric integration
- 9. Accessibility (module additions to 00-shared/09 §12)
- 10. Localization & formatting
- 11. Performance budgets (module)
- 12. Release gates (module additions)
Module-specific checklist layered on 00-shared/10_QA_Baseline.md + 00-shared/09 accessibility baseline. Every item is testable; backend-rule items cite the source line.
1. Data correctness — uniqueness & upsert
-
Marking the same student+date twice → one doc, second write overwrites
(
attendance.repository.ts:23-29); grid shows single chip state, no duplicates. -
Unique index
{tenantId, studentId, date}(attendance.schema.ts:59-62) enforced: two concurrent POSTs for same student+date → one doc, other gets 409 or converges. -
Same student marked from a second class on the same date → original
classIdis retained (attendance.repository.ts:29-33); grid shows "already marked in Class A" tooltip. -
Batch: N records → N docs, order preserved; one
AttendanceMarkedevent per record (attendance.service.ts:48-54). -
PATCH sends only changed fields: unchanged fields untouched (
$set,base.repository.ts:57-66);versionincrements on every update. -
PATCH
statuswith valid enum values only (OQ-6: server does not validate — client enforces; test that UI cannot submit invalid). -
Cross-tenant: reading another tenant's record id → 404
RESOURCE_NOT_FOUND(scoped filter,base.repository.ts:20-30), no existence leak.
2. Edit-after-notification / correction flows
-
Correct a record after a notification was sent (when dispatch
(planned)lands): PATCH works on the same doc id;AttendanceUpdated {changes:[...]}emitted (attendance.service.ts:88-99). - Wrong-date fix: mark correct day; wrong-day record found in S4 day view; PATCH to correct status; no delete endpoint exists (OQ-11) — verify the "holiday/remark" guidance is reachable.
- Undo: UndoBar re-sends previous status; after 4 s it's gone; grid reflects server state after refresh.
3. Timezone & date handling
-
datesent asYYYY-MM-DDin tenant-local calendar day — client never sends UTC-shifted dates (mark-attendance.dto.ts:21; stored as Dateattendance.schema.ts:31-32). - Marking at 23:50 local ≠ next-day doc; at 00:10 local ≠ previous-day doc.
-
checkIn/checkOut ISO-8601 with correct timezone offset survive round-trip
(
attendance.service.ts:32-33). - DST boundary day: month heatmap has 28/29/30/31 days correctly (client month-math); server stores UTC, client displays local.
-
Scheduler context:
attendance-report-daily07:00 UTC (scheduler.service.ts:99-104) — report periods use UTC boundaries; UI must not imply tenant-local "7 AM" semantics.
4. Large-class & performance
-
60+ student class: grid scrolls at 60 fps (0 dropped frames profiled —
00-shared/10 §1);
ListView.builder+itemExtentverified. - Month heatmap (S4, 30 fan-out day fetches) shows skeleton first, fills as days arrive; cache prevents refetch on month switch.
-
Bulk of 60 records: request completes < 5 s (sequential server loop,
attendance.service.ts:48-54); UI shows progress, not freeze. -
GET /attendance/summaryon a full term returns within budget or shows progressive loading (in-memory aggregation,attendance.service.ts:103-113— OQ-8). -
Report job: no request timeout during
queued/processing; polling capped; UI terminal oncompleted|failed(report-job.schema.ts:13-18).
5. Offline marking
-
Airplane mode: cached roster renders,
AppOfflineBannershown, marks queue locally, rows flagged dashed+cloud (00-shared/10 §2; 13_State_Management.md §4). - Kill app offline → relaunch → queue intact (persisted Hive box).
-
Reconnect: auto-flush,
Idempotency-Keyper op, snackbar "Synced N marks". - Retry: idempotent — a duplicate flush does not create duplicates (upsert).
- Offline collision (server has newer data): 409/conflict preview sheet; user chooses overwrite or discard; no silent data loss.
- Offline PATCH correction: blocked (pessimistic policy, 13 §3) with guidance.
6. Idempotent retries & partial failures
- Double-tap submit on chip → one request (anti-double-submit, 00-shared/08 §6).
- Bulk partial failure simulation (server down mid-loop): failed subset retried alone; no duplicates for the succeeded subset.
-
Worker
bulk-importbehavior (OQ-2): per-record failures swallowed → if surfaced in UI later, failure report matches actual saved count (attendance.worker.ts:91-101). - 409 on single mark → grid refresh, no ghost chip.
7. Permissions & roles
-
Teacher (role default
['student.read','attendance.mark','attendance.edit'],role.schema.ts:31): can mark + correct; cannot see S5/S6 reports (noreport.*,role.schema.ts:31) — verify server-side response & hidden UI. -
Parent/student (no attendance perms,
role.schema.ts:55,63): S7 read-only per OQ-7 decision; test 403 handling once server enforces. -
All write endpoints reject requests from tokens lacking
attendance.mark/edit— currently not decorator-enforced (OQ-5): assert current behavior and track. - 403 vs 404: cross-tenant read = 404; same-tenant wrong role = 403 (if enforced) — UI matches (00-shared/06 §5).
8. Biometric integration
-
POST /biometric/ingestwith valid{studentId, deviceId, timestamp}→ 201 log doc; no attendance doc created (current behavior — OQ-3); S8 shows "captured, not applied" honestly. -
Invalid studentId → 400
VALIDATION_ERROR(@IsMongoId,create-biometric-log.dto.ts:7). -
Device status mapping:
active/inactive/offline(biometric-device.schema.ts:7-11); "active but no punches 24 h" warning. -
Biometric-sourced docs (when pipeline
(planned)lands):source:'biometric'(attendance.schema.ts:16-21) displayed in S7 day detail.
9. Accessibility (module additions to 00-shared/09 §12)
- Grid: TalkBack/VoiceOver full marking flow without sight; chips announce status + "double-tap to change"; count bar live region.
- No color-only status anywhere (icons + labels; 00-shared/09:76).
-
Keyboard-only: P/A/L/H/V/D shortcuts,
Escselection exit, focus ring visible. - Text scale 2×: chips collapse gracefully; heatmap readable; no clipped rows.
- Conflict banner announced; undo announced; haptics off in reduced-motion.
10. Localization & formatting
-
Status labels/i18n keys (
att.status.*); date formats viaIntltenant locale;YYYY-MM-DDwire format independent of display format. - Long student names + long remarks do not break chip/row layout (RTL ready).
11. Performance budgets (module)
| Check | Budget |
|---|---|
| Grid first frame (cached) | < 300 ms (00-shared/10 §1) |
| Grid scroll 60 rows | 0 dropped frames |
| Chip mark round-trip feel | pending indicator ≤ 100 ms after tap |
| Month heatmap full render | < 2 s network, skeleton first |
| Report poll cycle | ≤ 5 min cap, no leaks |
12. Release gates (module additions)
-
e2e parity:
p1-operations.e2e-spec.ts:200-228(mark 201, class read 200, invalid status 400) pass against live backend. - Golden tests: grid, chips, heatmap, batch sheet — light/dark × 3 sizes.
- Offline matrix (§5) fully green on mid-range device.