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 (Timetable 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. Conflict & overlap matrix (the core rule)

#ScenarioExpectedServer reality
QA-1Same teacher, same day, overlapping time409 "Schedule conflict detected"$or clause {teacherId, dayOfWeek} + timeOverlaps (timetable.service.ts:17-22,62-69)
QA-2Same room, same day, overlapping time (both entries have roomId)409{roomId, dayOfWeek} clause (timetable.service.ts:20)
QA-3Same room, same day, overlapping — one entry has no roomIdNo conflict (different rooms)roomId: undefined is stripped from the query → clause no-ops (timetable.service.ts:20) — verify with E2E
QA-4Same class, same day, overlapping time (two subjects)Client highlights only; server acceptsNo classId check (timetable.service.ts:17-22) — [backend] confirm intent (OQ-1); UI must not block
QA-5Back-to-back: entry A ends 09:00, entry B starts 09:00Allowedstart1 < end2 && start2 < end1 → false (timetable.service.ts:68)
QA-6Same teacher, different day, same timeAllowed✅ day filter (timetable.service.ts:19-20)
QA-7Boundary: start1 == start2Conflict✅ overlap true
QA-8Contained: A 08:00–10:00 vs B 08:30–09:00Conflict✅ overlap true
QA-9Two admins race the same slotSecond gets 409 (no corruption)✅ create-time check; client pre-flight is advisory
QA-10Non-zero-padded time ("8:00") vs "09:00"Broken math (lexical: "8:00" > "09:00")@IsString only (create-timetable-entry.dto.ts:27-33) — [backend] add HH:MM pattern; client picker-only input (proposed)

2. Time / day validation

#ScenarioExpected
QA-11dayOfWeek: "sunday"400 — @IsEnum(DayOfWeek) (create-timetable-entry.dto.ts:23-25); saturday legal, sunday not (timetable.schema.ts:7-14)
QA-12endTime <= startTimeServer accepts (no order check) — client blocks pre-submit (proposed); [backend] consider validation
QA-13Malformed time "9:00 am"Server accepts (@IsString) — client picker prevents; grid math would be wrong if sent
QA-14Unknown body key (e.g. tenantId)400 — forbidNonWhitelisted (main.ts:50-57) — client never sends tenantId

3. Read paths & sorting

#ScenarioExpected
QA-15GET /timetable?classId=[] or entries sorted dayOfWeek, startTime (timetable.service.ts:48-53) — client renders as-is, no re-sort
QA-16GET /timetable?teacherId=Same sort contract (timetable.service.ts:55-60)
QA-17Both classId + teacherIdclassId wins (timetable.controller.ts:26-27) — client never sends both
QA-18Bare GET /timetable[] (timetable.controller.ts:28) — never present as "all entries"
QA-19?roomId= filter404-ish/ignored — param doesn't exist (timetable.controller.ts:22-25) — room view is client composition; native filter (planned); [backend]
QA-20Sort with equal dayOfWeek + startTime (duplicate slots)Insertion order — verify grid handles ties (no flicker)

4. Mutation lifecycle (or lack of it)

#ScenarioExpected
QA-21PATCH /timetable/:idNo endpoint (timetable.controller.ts:10) — editor offers Duplicate+create only; edit UI (planned)[backend] (OQ-2)
QA-22DELETE /timetable/:idNo endpoint — soft-delete columns exist on BaseSchema but are unused by the module; delete (planned)
QA-23Teacher soft-deleted with live timetable entriesEntries remain (no cascade — no delete path; teacher ref dangles) — grid shows "—" name; [backend] consider cascade/substitution (IMPLEMENTATION_PLAN.md:226)
QA-24Room deleted with live entriesEntries remain; room chip drops (roomId ref unresolved → "—")
QA-25Entry created for soft-deleted teacher/class/roomServer performs no existence checks on create — only the conflict query runs (timetable.service.ts:16-30) — pickers must only offer live records; [backend]
QA-26Duplicate identical entry (same everything, same class)Accepted (no unique index, no dup check) — by design for classes (OQ-1); verify UX intent

5. Tenant isolation & security

#ScenarioExpected
QA-27Cross-tenant entry id in create/read404/scope-excluded (base.repository.ts:20-30) — never leak existence (00-shared/07 §3)
QA-28No tenantId in bodiesClient never sends it (07 §6)
QA-29Unauthenticated call401 → refresh flow
QA-30RBAC: endpoints carry no timetable.read/create metadata despite perms existingpermissions.constants.ts:44-45 vs timetable.controller.ts:9 — client-side gate only; [backend] add metadata

6. Event/side-effect correctness [backend]

#ItemEvidence
QA-31TimetableEntryCreated emitted but no queue mapping found (event-queue-map.ts)timetable.service.ts:33-44 — confirm intended consumers (notification/audit/search) or drop emission

7. Client QA (per 00-shared/10)

#Item
QA-32Grid perf: 6×~10 grid with ≥ 40 slot cards ≤ 60 fps on low-end; lazy row build + RepaintBoundary (15 §9)
QA-33A11y: slot labels complete (subject/teacher/room/day/time), conflict announced live, Sunday column absent
QA-34Text scale 2×: slot cards don't clip (subject shortName truncation)
QA-35Dark mode parity: conflict tokens verified both modes
QA-36Offline: grid from cache + banner; editor blocked
QA-37Week nav: offset only re-filters (no server date) — Today resets without refetch
QA-38Desktop drag: valid/invalid drop targets, no optimistic persistence (10 §2)
QA-39Analytics (proposed): timetable.entry.{submit,conflict,success}, timetable.class.view (05)
QA-40Golden tests: TimetableSlotCard ×2×2, ConflictBanner, WeekNavigator

8. Release gates (module)

  • Backend gaps QA-4/10/12/19/21/22/23/25/30/31 triaged (none block v1 read UI; editor ships with client-side guards + (planned) markers)
  • All envelope error codes map to i18n keys (no raw 5xx text)
  • E2E: create entry → conflict 409 → fix → grid shows slot; cross-tenant 404
  • E2E: bare GET /timetable[] path never triggered by UI