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

Acceptance criteria against real server behaviour. Every item cites the code it guards. Reference 00-shared/10_QA_Baseline.md for general baseline + envelope conformance.


1. Envelope & contract conformance

  • Every result endpoint returns the v1 envelope (00-shared/07 §2-3): success {success,message:"OK",data,meta?,timestamp,requestId}, error with error.code from the allowed set.
  • GET /api/v1/results/student/:id and exam-subject/:id return arrays in data with no meta — pagination does not exist on result routes (examination-result.repository.ts:20-28); the client must not page.
  • 401 without a Bearer token on all result + exam routes (JwtAuthGuard, result.controller.ts:10).

2. Publish behaviour & immutability

  • POST /api/v1/examinations/:id/publish stamps publishedAt on every result row of every subject (examination.service.ts:203-207) and sets exam status: 'published' (:209-211).
  • Publish with zero subjects still succeeds (subjectIds.length guard, :206) — UI must handle "published with no subjects".
  • Repeat publish is idempotent — re-stamping publishedAt must not error or duplicate rows (examination-result.repository.ts:42-51).
  • No unpublish endpoint exists — QA confirms client never offers revert.
  • (planned) immutability: after publish, enterMarks still updates rows (examination.service.ts:139-195 has no publishedAt check). Client lock is UI-only; flag as known gap, revisit when server enforces.
  • ExamResultsPublished fires exactly once per publish (:212-219) → in-app/results-published (event-queue-map.ts:27); in-app job is idempotent (worker test).

3. Missing-marks handling

  • Report card with a student missing some subjects: marksObtained: 0 for missing rows (result.service.ts:70), totals still include that subject's maximum (:72), percentage and overallGrade computed on the 0-fill (:83-95).
  • Data limitation: a real "0" and "not entered" are indistinguishable (result.service.ts:70) — UI distinguishes via presence of a row in E1/E2; QA documents this in release notes.
  • Student with zero result rows: E1 returns [] (no error); E4 returns 404 only when the exam has no subjects (result.service.ts:51-52).

4. Grade bands (exact boundaries)

  • computeGrade boundaries verified against result.service.ts:130-138: 90→A+, 89.99→A, 80→A, 79.99→B+, 70→B+, 69.99→B, 60→B, 59.99→C, 50→C, 49.99→D, 40→D, 39.99→F, 0→F.
  • Percentage is rounded to 2 decimals (result.service.ts:83-86) before banding — test 89.995% (rounds to 90 → A+).
  • Per-subject grade is client-supplied and stored verbatim (examination-subject.dto.ts:57-60, examination.service.ts:157) — server does not compute per-subject grades; QA asserts UI never claims server-computed per-subject grades.

5. Marks entry validation (404-vs-422 quirk)

  • marksObtained > maximumMarks → server returns 404 RESOURCE_NOT_FOUND (examination.service.ts:145-146), not 422 BUSINESS_RULE_VIOLATION. Client normalises to "marks cannot exceed maximum" (08/09); QA asserts the user-facing message is correct despite the misleading code.
  • Negative marks → 400 VALIDATION_ERROR (Min(0), examination-subject.dto.ts:54).
  • Unknown examSubjectId → 404 (examination.service.ts:143-144).
  • Malformed Mongo id → 400.

6. Concurrency & idempotency

  • Two clients upsert the same (studentId, examinationSubjectId) — service-level check + unique index {tenantId, studentId, examinationSubjectId} (examination-result.schema.ts:31-33) → last-write-wins; no DUPLICATE_RESOURCE (409) is expected on sequential writes.
  • True race (both find nothing, both create): one may hit Mongo dup-key (11000) → surfaces as 5xx; QA verifies the client treats it as "retry — row now exists" and re-reads. Backend fix (planned): catch 11000 → read existing.
  • Re-submitting the same payload (offline replay) → identical end state, one row.
  • Cross-tenant id on any path → 404/[] (scoped repo), never another tenant's data.

7. Data quirks (client workarounds)

  • subjectName = raw subject ID (result.service.ts:75) — UI shows mapped names; QA checks no raw IDs leak to screens.
  • S3 orphan rows (exam soft-deleted → E5 404) are dropped without crash.
  • createdAt/updatedAt/version present on result rows (base.schema.ts); UI may use updatedAt for "last saved" display.

8. Accessibility & responsive

9. Performance

  • S1 grid stays interactive at 200+ students (virtualised — 15_Flutter §Table).
  • S5 aggregation of N subjects: ≤ 4 parallel fetches, skeleton progress (13 §6).
  • Offline draft replay: 100 rows → ordered, per-row success/failure, no deadlock on partial failure.

10. Regression baseline

  • All reads unchanged after publish (publishedAt stamp must not alter E1/E2/E4 shapes).
  • Soft-deleted exam subject → E2 [], marks POST 404, no crash.