14 — QA Checklist (Teachers Module)
- 1. Assignment conflict & duplicate matrix
- 2. Duplicate teacher rules
- 3. Deactivate rules
- 4. Pagination & list integrity
- 5. Tenant isolation & security
- 6. Event/side-effect correctness [backend]
- 7. Client QA (per 00-shared/10)
- 8. Release gates (module)
Module-specific additions to 00-shared/10. Items marked [backend] are bugs or gaps derivable from source and should be raised with the API team, not papered over in the UI.
1. Assignment conflict & duplicate matrix
| # | Scenario | Expected | Server reality |
|---|---|---|---|
| QA-1 | Same (teacher, subject, class, year) assigned twice | Client blocks + inline error | No server guard (subject-assignment.service.ts:13-17; non-unique indexes subject-assignment.schema.ts:24-25) — [backend] add unique index or 409 |
| QA-2 | Same teacher, same subject, two classes (same year) | Allowed (two rows) | Allowed — expected |
| QA-3 | Two admins assign same triple concurrently | Client can't prevent (server race) | Both succeed — [backend]; client shows both rows, dedup display |
| QA-4 | Assign teacher to subject not in class curriculum | Client warns (no server model of curriculum) | No check — accept (no curriculum entity) |
| QA-5 | Timetable conflict for teacher (two periods same time) | Handled by Timetable module, not this one | timetable.service.ts:16-30 409 — cross-module QA |
| QA-6 | by-teacher / by-class without academicYearId | Client never sends; if API called bare → expect [] | Returns [] (subject-assignment.service.ts:26-31) — verify client picker always set |
2. Duplicate teacher rules
| # | Scenario | Expected |
|---|---|---|
| QA-7 | POST /teachers with userId already profiled | 409 DUPLICATE_RESOURCE (teacher.service.ts:27-31) — UI banner |
| QA-8 | POST /teachers with existing employeeNumber | 409 (teacher.service.ts:32-38) — banner |
| QA-9 | PATCH to existing employeeNumber | No 409 (teacher.service.ts:80-93 only checks record exists) — [backend]; client pre-check + accept race |
| QA-10 | userId change via PATCH | Blocked by DTO absence (update-teacher.dto.ts) — verify immutability in UI |
3. Deactivate rules
| # | Scenario | Expected |
|---|---|---|
| QA-11 | DELETE /teachers/:id | Soft-delete; all subsequent list/detail queries exclude (base.repository.ts:20-30,68-74) |
| QA-12 | Deactivate teacher with active assignments/timetable | Allowed server-side (no guard) — dialog warns; [backend] consider BUSINESS_RULE_VIOLATION guard (OQ-5) |
| QA-13 | Re-activation | No endpoint — not offered in UI |
| QA-14 | Deactivate already-deleted id | 404 → snackbar "already removed" → back |
| QA-15 | Audit trail | TeacherDeleted → audit-write job (event-queue-map.ts:33) — verify audit UI shows it |
4. Pagination & list integrity
| # | Scenario | Expected |
|---|---|---|
| QA-16 | page=1,limit=20 → meta math | totalPages = max(1, ceil(total/limit)), hasNext/hasPrevious (pagination-query.dto.ts:41-55) |
| QA-17 | limit > 100 | 400 VALIDATION_ERROR (pagination-query.dto.ts:13-19) — client clamps |
| QA-18 | sort/q params | Ignored by service (teacher.service.ts:66-78) — UI must not claim server search; local filter only; [backend] (OQ-3) |
| QA-19 | Soft-deleted rows in middle page | Never returned (scope) — no ghost rows |
| QA-20 | Page race on pull-to-refresh + load-more | Request generation token; no duplicate items |
| QA-21 | Subject-assignments/by-* non-paginated | Full array; verify memory on large schools; server pagination (planned) |
5. Tenant isolation & security
| # | Scenario | Expected |
|---|---|---|
| QA-22 | Cross-tenant GET /teachers/:id | 404 (scope tenantId injected, base.repository.ts:20-30) — never leak existence (00-shared/07 §3) |
| QA-23 | No tenantId in bodies | Client never sends it (07 §6) |
| QA-24 | Unauthenticated call | 401 → refresh flow |
6. Event/side-effect correctness [backend]
| # | Item | Evidence |
|---|---|---|
| QA-25 | TeacherCreated routed to in-app, not emails — PLAN.md:36 promises welcome email | event-queue-map.ts:31 vs PLAN.md:36 — confirm email intent |
| QA-26 | In-app notification type='TeacherCreated' vs NotificationType enum (email_verified|password_reset|verification_resent|welcome) | inapp.worker.ts:46-53 + notification.schema.ts:7-12 — likely enum validation failure → job retries → DLQ |
| QA-27 | Search index title for TeacherCreated payload {teacherId, employeeNumber} — no name → not indexed | search-indexer.service.ts:62-88 + teacher.service.ts:47-49 |
7. Client QA (per 00-shared/10)
| # | Item |
|---|---|
| QA-28 | A11y: status chips label+icon (color-blind), focus order S1→S2, dialog focus trap |
| QA-29 | Text scale 2×: long designation names + status chips don't clip |
| QA-30 | Dark mode parity: all status token pairs verified |
| QA-31 | Offline: list from cache + banner; forms blocked |
| QA-32 | Perf: S1 skeleton ≤ 200 ms; no duplicate parallel requests (10 §1) |
| QA-33 | Analytics (proposed): teachers.list.search, teachers.create.submit, teachers.assignments.add, teachers.deactivate.confirm (10 §8 naming) |
| QA-34 | Golden tests: StatusChip ×4 ×2 modes, TeacherListTile, AssignmentCard, TeacherCard ×3 sizes |
8. Release gates (module)
- Server gaps QA-1/9/12/18/25/26/27 triaged with API team (none block v1 UI if client compensates: dedup, pre-check, warning copy)
- All envelope error codes map to i18n keys (no raw 5xx text)
- E2E: create → assign → edit → deactivate journey on device cloud
- Cross-tenant 404 verified in E2E