πŸ§ͺ QA Test Cases

Page 13 Β· v1.9 Β· 61 test cases across 14 systems Β· Maps to AC-01–AC-13
How to read these tests: Each case is GIVEN/WHEN/THEN format. GIVEN = state before you act. WHEN = the action. THEN = what must be true after. FAIL PATH = the unhappy path you must also verify β€” shown in orange. Test IDs (e.g. TC-AUTH-01) are for bug tracking.

Coverage Summary

SectionTestsWhat's Covered
TC-AUTH7Teacher signup, email verify, login/logout, child PIN login, lockout, device reuse, inactivity timeout
TC-ROSTER6Class creation, single/bulk student add, 33-cap enforcement, teacher invite, parent linking
TC-READ6Book open, vocab tap, miles/tokens, RSVP age gate, theme persistence, offline sync
TC-TEL4Event dedup, vocab-taps pipeline, slow page detection, event ordering
TC-BOT6Placement β†’ memory, nightly reports, inactive alert, quiz feedback, free-tier skip, adaptive leveling
TC-TEACH5Dashboard, learner profile, PIN reset, login cards, DPA wizard
TC-BILL7Trial access, Stripe failure, grace expiry, entitlement resolution order, cache TTL, enterprise grant, child→teacher chain
TC-ADMIN6Impersonation start/end, entitlement grant/revoke, GDPR deletion, audit log immutability, audio not stored
TC-PARENT1Read-only access + write block
TC-FLUENCY2Recording/scoring, multilingual thresholds
TC-CONTENT2Recommendations, pre-warming cache
TC-SEC5Tenant isolation, no secrets in responses, internal key auth, rate limiting, no cross-DB
TC-PWA2Service worker registration, IndexedDB sync
TC-NOTIF2Notification triggers, email log

1. Authentication & Identity

Reference: Page 03 β€” Identity & Auth

TC-AUTH-01 β€” Teacher registration: happy path

AC-01
Given

A fresh browser with no session.

When

Visit account.readingtester.com/register, fill name + valid email + password β‰₯8 chars, submit.

Then
  • HTTP 201 in browser Network tab
  • "Check your email" confirmation screen shown
  • users row: state=pending_verification, email_verified=false
  • subscriptions row: state=trialing, trial_ends_at = ~14 days from now
  • audit_log row: action=register
  • Email received in inbox (VERIFY_EMAIL template)
FAIL PATH: Duplicate email β†’ same form shows "Email already registered" (HTTP 409, no new DB row).

TC-AUTH-02 β€” Teacher email verification

AC-01
Given

TC-AUTH-01 completed, verification email received.

When

Click the verification link in the email.

Then
  • users.email_verified=true, state=active
  • Browser redirected to /onboarding
  • uc_session cookie set (httpOnly, Secure)
  • audit_log row: action=email_verified
  • WELCOME_TEACHER email sent
FAIL PATH: Click expired token (>24h) β†’ HTTP 410 + "Resend verification" button visible.

TC-AUTH-03 β€” Teacher login and logout

Given

Verified teacher account.

When (login)

Submit correct email + password at account.readingtester.com/login.

Then
  • Redirect to Teacher Portal (teacher.readingtester.com)
  • uc_session cookie present
  • audit_log row: action=login
When (logout)

Click logout.

Then
  • uc_session cookie cleared
  • Redirect to /login
  • Attempting /dashboard β†’ redirect to /login (no access without session)

TC-AUTH-04 β€” Child login: happy path

AC-03 AC-13
Given

Teacher created student account (username: emma001, PIN: 1234), placement_test_completed=false.

When

Visit app.readingtester.com, enter username + PIN.

Then
  • device_sessions row created (learner_id mapped, policy_applied=shared)
  • audit_log row: action=child_login
  • Redirected to /placement-test (not library)
When (repeat login after placement completed)

Login again after placement test done.

Then

Redirect directly to /library β€” placement test NOT shown again.

TC-AUTH-05 β€” Child login: PIN lockout

AC-03
Given

Valid child account with PIN 1234.

When

Enter wrong PIN 5 times in a row.

Then
  • After 5th failure: HTTP 423
  • students.locked=true
  • teacher_notifications row inserted (type=pin_lockout)
  • "Account locked β€” ask your teacher" shown to child
  • Teacher Portal shows notification badge
FAIL PATH: Correct PIN as attempt 6 β†’ still blocked. Locked flag checked before PIN.

TC-AUTH-06 β€” Device reuse / shared device session

Given

Child A (emma001) logged in on a tablet, device_session_id=AAA.

When

Child B (james002) logs in on the same tablet.

Then
  • Previous device_sessions row for AAA: expired_at=NOW(), end_reason=device_reuse
  • New device_sessions row created for james002
  • Child A's session is invalid β€” navigating redirects to login

TC-AUTH-07 β€” Session inactivity timeout

Given

Child logged in, shared_device_mode=true, child_inactivity_timeout_min=30.

When

No activity for 31 minutes.

Then
  • device_sessions.expired_at=NOW(), end_reason=timeout
  • Child sees "Session ended β€” log in again"
  • If β‰₯3 pages AND β‰₯50 words read: progress saved, miles credited
  • If below threshold: session data discarded β€” no partial miles credited

2. Roster & Class Management

Reference: Page 05 β€” Roster & Classroom

TC-ROSTER-01 β€” Create a class

AC-02
When

POST /api/v1/classes with {class_name: "Year3Blue", year_level: 3}

Then
  • HTTP 201
  • classes row: teacher_id=session.user_id, state=active
  • Class visible in Teacher Portal immediately
  • audit_log row: action=create_class
FAIL PATH: Missing class_name β†’ HTTP 422, no DB row, error shown in form.

TC-ROSTER-02 β€” Add a single student

When

POST /api/v1/classes/:class_id/students with {name: "Tommy Anderson", year_level: 3}

Then
  • HTTP 201
  • students row: auto-generated username + 4-digit PIN, state=created, placement_test_completed=false
  • class_memberships row inserted
  • Response body contains username and pin (shown once β€” teacher must record)
  • audit_log row: action=create_student

TC-ROSTER-03 β€” Bulk import students (CSV)

When

POST /api/students/import with CSV file (columns: name, year_level).

Then
  • HTTP 200 with array of {name, username, pin} for each row
  • All students rows created, all class_memberships inserted
  • Login cards renderable (name + username + PIN + QR code)
FAIL PATH: Import 34 students into a full class β†’ HTTP 422 CLASS_FULL for row 34; rows 1–33 succeed.

TC-ROSTER-04 β€” Class hard cap enforcement (33 students)

Given

Class already has 33 active students.

When

Teacher attempts to add 1 more student.

Then
  • HTTP 422 {error: "CLASS_FULL"}
  • No new class_memberships row
  • Teacher Portal shows "Class is full (33/33)"
When (unblock)

Teacher archives one student (students.archived_at=NOW()).

Then

Cap slot freed immediately β€” next add succeeds.

TC-ROSTER-05 β€” Teacher invites a colleague

When

school_admin POSTs /api/v1/schools/:school_id/invites with colleague email + role teacher.

Then
  • invites row: token_hash stored, expires_at=NOW()+7d
  • INVITE_EMAIL sent
When colleague accepts
  • users row: role=teacher, school_id set, state=active
  • subscriptions row: state=trialing
FAIL PATH: Expired invite (>7d) β†’ HTTP 410 "Invite expired β€” ask your admin".

TC-ROSTER-06 β€” Parent linking via link code

Given

Parent has a Pickatale account, teacher has generated a 6-char link code (e.g. PIP423) for the student from the Teacher Portal.

When

Parent logs in to parents.readingtester.com, clicks "Link Your Child's Account", enters child display name + link code.

Then
  • Client validates code against account.readingtester.com/api/public/link-code/PIP423
  • Returns {valid: true, uuid: "..."} β€” UUID resolved
  • parent_children row inserted: parentId, childName, learnerServiceId=uuid
  • Child's reading progress visible to parent (read-only)
FAIL PATH: Invalid code β†’ {valid: false} β†’ "Invalid code. Please check with your child's teacher." No DB row inserted.

3. Reader App & Reading Session

Reference: Page 06 β€” Reading & Telemetry

TC-READ-01 β€” Open a book and read (online)

AC-04
When

Authenticated child (placement complete) taps a book on app.readingtester.com/library.

Then
  • reading_sessions row created: state=open, book_id, learner_id, started_at
  • Telemetry event book_opened fired (POST /api/v1/events)
  • Book content displayed (FK-leveled if applicable)
When child turns each page
  • Telemetry event page_turned fired per page: {device_session_id, learner_id, page_number, time_on_page_ms}
When child finishes / closes
  • session_ended event fired
  • reading_sessions.state=completed, words_read, pages_read updated
  • Miles incremented: floor(words_read / 100)

TC-READ-02 β€” Vocabulary tap

AC-04
When

Child taps word "photosynthesis" in Finger-Follow mode.

Then
  • Telemetry event word_tapped fired: {word, page_number, device_session_id, learner_id}
  • TTS pronounces the word
  • Definition popup shown (if enabled)
  • Word stored in Learner Bot vocabularyGaps after session ends

TC-READ-03 β€” Miles and token accumulation

Given

Child has miles=9, reads a book with 150 words.

Then
  • floor(150/100)=1 new mile credited β†’ students.miles=10
  • 10 miles threshold crossed β†’ students.tokens incremented by 1
  • audit_log row: action=token_earned

TC-READ-04 β€” RSVP Speedread (age gate)

Given

Child account with age=7.

Then

Speedread button hidden or HTTP 403 β€” mode not accessible.

Given (passes gate)

Child with age=9, fk_level β‰₯ 3.0.

Then
  • RSVP interface shown, starts at 60 WPM
  • Max session: 10 minutes enforced
  • RSVP time does NOT count toward book completion or miles

TC-READ-05 β€” Theme persistence

When

Child opens Aa button β†’ selects "Sepia".

Then
  • Background changes to #F5E6C8
  • localStorage["reader_bg_theme"]="sepia" written
When browser reopened

Sepia theme restored from localStorage β€” no white flash on load.

TC-READ-06 β€” Offline reading and sync

AC-12
When

Child goes offline (DevTools β†’ Network β†’ Offline) mid-session, turns pages, taps words.

Then
  • All events queued in IndexedDB (telemetry-queue tag)
  • App continues β€” no error shown to child
  • Miles NOT yet persisted to server
When network restored
  • Service Worker replays all queued events
  • Server deduplicates by event_id β€” re-sending same events twice produces no duplicate rows
  • reading_sessions.state=completed updated
  • Miles credited to server DB
FAIL PATH: Events NOT replayed within 72h β†’ events lost. reading_sessions marked abandoned by 24h cron. No crash, no data corruption in other sessions.

4. Telemetry Pipeline

TC-TEL-01 β€” Event deduplication

When

Same event (identical event_id UUID) POSTed to /api/v1/events twice.

Then
  • First call: HTTP 200, row inserted
  • Second call: HTTP 200 (idempotent), no duplicate row
  • Only 1 row in events table for that event_id

TC-TEL-02 β€” Vocab-taps pipeline (Telemetry β†’ Bot)

When

session_ended fires β€” Telemetry calls POST /api/v1/telemetry/vocab-taps.

Then
  • Learner Bot vocabularyGaps: word=magnificent, tap_count=2 (for 2 taps)
  • Re-tap in next session: tap_count incremented via SQL (tap_count + N, not overwritten)
  • Response to Telemetry: HTTP 200 within 5 seconds
FAIL PATH: Learner Bot down β†’ Telemetry logs error, continues. No 500 returned to client.

TC-TEL-03 β€” Session summary (slow pages detection)

Given

Session: 5 pages, time_on_page_ms=[30000, 45000, 120000, 40000, 35000] (avg=54s, page 3 = 120s = 2.2Γ— avg).

Then
  • slow_pages=[3]
  • Bot stores reading_pattern memory (confidence=0.60 if pages read < 80% of total)
  • If slow_pages.length > 3: Bot stores engagement_signal={signal:"struggling"}

TC-TEL-04 β€” Event ordering guarantee

When

Three events from same device_session_id arrive out of server order (network jitter).

Then
  • Events ordered by client_ts, NOT created_at
  • session_ended processing waits until all prior events for same device_session_id are processed

5. Learner Bot & Adaptive Engine

TC-BOT-01 β€” Placement test β†’ bot memory

AC-13
When

Child completes placement test β†’ POST /api/placement-result {learner_id, fk_level: 3.5}

Then
  • placement_results row inserted
  • students.placement_test_completed=true
  • Bot stores reading_level memory with confidence=1.0
  • Child redirected to /library
FAIL PATH: API fails mid-test β†’ retry from question 1. No partial fk_level saved.

TC-BOT-02 β€” Nightly report generation

AC-06
Given

Learner has active subscription, read β‰₯1 book in past 7 days.

When

Nightly cron fires at 04:00 UTC.

Then
  • nightly_reports row: report_type=teacher_report, includes FK progression + reasoning
  • nightly_reports row: report_type=parent_digest, warm tone
  • Both portals display the new reports

TC-BOT-03 β€” Nightly bot: inactive learner alert

AC-06
Given

Learner has not read any book in 4+ days.

Then
  • teacher_notifications row: type=inactive
  • Teacher Portal shows alert badge

TC-BOT-04 β€” Quiz result β†’ bot feedback

Given

Child completes comprehension quiz, score 55%.

When

POST /api/v1/bot/:learner_id/quiz-result {scorePct: 55, levelRecommendation: "hold", ...}

Then
  • Bot stores assessment_result memory: confidence=0.55
  • Bot triggers re-run for this learner (setImmediate)
  • If this is 2nd quiz with avg < 65%: teacher_notifications type=low_score

TC-BOT-05 β€” Bot: free-tier learner skipped

AC-06
Given

Child's teacher subscription has expired (state=expired).

When

Nightly cron runs.

Then
  • checkEntitlement β†’ tier=free
  • Bot SKIPS this learner β€” no nightly_reports row generated
  • Teacher Portal shows "Upgrade to view reports"

TC-BOT-06 β€” Adaptive content leveling

When

POST /api/v1/level-page {bookId, pageNumber, text, targetFkLevel: 4.0}

Then
  • HTTP 200: {leveledText, actualFkScore, cached: false}
  • Cache key stored: book_id + page_number + target_level_rounded + source_hash
  • Identical second call: cached: true, no new GPT call
  • Short text (<30 words): returned unchanged, no leveling attempted

6. Teacher Portal

TC-TEACH-01 β€” Dashboard: class overview

When

Visit teacher.readingtester.com/class/:id (class has 5 students).

Then
  • All 5 students listed with: name, reading level, books read this week, miles, latest bot report
  • Attention list flags: placement_test_completed=false OR books_read=0 OR quiz avg < 65% (2+ quizzes)

TC-TEACH-02 β€” Learner profile view

When

Teacher clicks a student's name.

Then
  • GET /api/v1/bot/:learner_id/status called with X-Internal-Key
  • Displays: reading_level, books_read, total_miles, readingLevelHistory, vocabGaps, latestReport

TC-TEACH-03 β€” PIN reset (teacher flow)

Given

Student account locked after 5 failed PIN attempts.

When

Teacher clicks "Reset PIN".

Then
  • New 4-digit PIN generated, shown once to teacher
  • students.locked=false, pin_hash=bcrypt(new_pin)
  • audit_log row: action=pin_reset
FAIL PATH: Parent attempts PIN reset β†’ HTTP 403 β€” parents cannot reset PINs.

TC-TEACH-04 β€” Print login cards

When

Teacher clicks "Print Login Cards" after bulk import.

Then
  • Printable cards shown: student name + username + PIN + QR code (app.readingtester.com?user=username) + school name + Pip logo
FAIL PATH: Teacher navigates away before printing β†’ PIN permanently gone. On return: only username visible. Teacher must issue new PIN via Reset flow.

TC-TEACH-05 β€” DPA setup wizard

Given

New teacher account, first login.

When

School details form submitted (School Name, Country, DPA checkbox ticked).

Then
  • school_dpa_agreements row: school_id, user_id, accepted_at=NOW(), ip_address
  • Teacher redirected to dashboard
FAIL PATH: DPA checkbox not ticked β†’ "You must accept the DPA to continue" β€” submit blocked (frontend + backend both validate).

7. Billing & Entitlement

Reference: Page 04 β€” Billing & Entitlement

TC-BILL-01 β€” Trial period access

Given

subscriptions.state=trialing, trial_ends_at=NOW()+14d

Then

checkEntitlement β†’ tier=full. All features accessible, no paywall.

When trial expires

checkEntitlement β†’ tier=free. Full-tier features locked, upgrade prompt shown.

TC-BILL-02 β€” Stripe payment failure β†’ grace period

AC-08
When

Stripe webhook invoice.payment_failed received (valid Stripe-Signature).

Then
  • subscriptions.state=past_due, grace_ends_at=NOW()+7d
  • RENEWAL_FAILED email sent (email_log INSERT)
  • Teacher Portal: yellow banner "Payment failed. Update billing within 7 days."
  • All features remain accessible (grace active)
  • audit_log row: action=stripe_webhook
  • HTTP 200 returned to Stripe
FAIL PATH: Invalid Stripe-Signature β†’ HTTP 400, audit_log action=stripe_webhook_invalid, NO state change.

TC-BILL-03 β€” Grace period expiry

AC-08
Given

subscriptions.state=past_due, grace_ends_at=NOW()-1h.

When

Hourly expiry cron runs.

Then
  • subscriptions.state=expired
  • All learners' full-tier features locked
  • Learner Bot skips all learners on next nightly run
  • Teacher Portal: red banner "Subscription expired"
  • SCHOOL_SUBSCRIPTION_EXPIRED email sent
  • audit_log row: action=subscription_expired

TC-BILL-04 β€” Entitlement resolution order

Given

Teacher has expired subscriptions but platform_admin granted entitlement_grants row with expires_at=NULL.

Then

entitlement_grants checked first β†’ found β†’ returns tier=full. subscriptions table NOT consulted.

When grant revoked

Falls back to subscriptions β†’ state=expired β†’ tier=free. Features locked.

TC-BILL-05 β€” Entitlement cache (60s TTL)

When

Admin changes subscriptions.state=expired + broadcasts cache invalidation.

Then
  • Within 60 seconds: all service instances clear cache for this user_id
  • Next checkEntitlement call fetches fresh from Account Center β†’ tier=free
FAIL PATH: Account Center unavailable β†’ entitlement resolves tier=free (fail-safe). audit_log action=entitlement_check_failed.

TC-BILL-06 β€” Enterprise entitlement (external grant)

Given

School has no Stripe subscription; platform_admin inserted entitlement_grants row.

Then

checkEntitlement finds grant β†’ tier=full. No Stripe interaction. No upgrade prompt.

TC-BILL-07 β€” Child entitlement resolves from teacher

Given

Child is in a class owned by teacher with active subscription.

When

Child opens book beyond free tier limit.

Then

Entitlement chain: child β†’ class_memberships β†’ teacher β†’ subscriptions.state=active β†’ tier=full. Book accessible.

FAIL PATH: Teacher subscription expires β†’ HTTP 403 {error: "entitlement_required"}. Child sees "Your school's subscription has expired. Ask your teacher." (no billing details shown to child).

8. Admin & Compliance

Reference: Page 08 β€” Admin & Compliance

TC-ADMIN-01 β€” Impersonation: start

AC-09
When

platform_admin POSTs /api/admin/users/:teacher_id/impersonate with reason.

Then
  • Target must NOT be a platform_admin β€” if admin: HTTP 403
  • audit_log: action=impersonation_started, actor_id, target_id
  • New session: expires in 1h, impersonator_id stored
  • Orange "Impersonating [Name]" banner visible
  • All actions during session logged with impersonator_id

TC-ADMIN-02 β€” Impersonation: end

AC-09
When

Click "End Impersonation".

Then
  • Impersonated session invalidated
  • audit_log: action=impersonation_ended
  • Admin's own session restored, orange banner removed
FAIL PATH: Session hits 1h limit without manual end β†’ auto-invalidated by expiry cron. Admin must log in again.

TC-ADMIN-03 β€” Grant entitlement (admin)

AC-10
When

POST /api/admin/entitlement/grant {school_id: 5, tier: "full", expires_at: null}

Then
  • entitlement_grants row inserted
  • audit_log: action=entitlement_grant_created
  • Cache invalidation broadcast to all services
  • Within 60 seconds: school resolves tier=full

TC-ADMIN-04 β€” GDPR deletion

AC-11
When

POST /api/gdpr/delete {learner_id: "uuid"}

Then (immediate)
  • students.state=archived β€” child cannot log in
  • gdpr_requests row: type=deletion, state=pending
Then (async, within 30 days)
  • Telemetry events deleted
  • Bot memories/reports deleted
  • LRS statements deleted
  • users.name="Deleted User", email="{uuid}@deleted.invalid"
  • audit_log PII stripped (rows retained)
  • gdpr_requests.state=complete
FAIL PATH: One service deletion fails β†’ gdpr_requests.error_log populated. State NOT set to complete. Admin dashboard flags for manual resolution.

TC-ADMIN-05 β€” Audit log immutability

Test via direct DB connection

Attempt: DELETE FROM audit_log WHERE ...

Then

DB trigger blocks DELETE β€” error raised. Rows remain intact.

TC-ADMIN-06 β€” Voice audio not stored

Given

Fluency assessment in progress, child speaks into microphone.

Then
  • Audio processed in-memory only β€” no file written to disk
  • No audio data in DB rows, queues, logs, or retry buffers
  • audit_log row contains only text transcript + scores β€” never raw audio bytes

9. Parent Portal

TC-PARENT-01 β€” Parent read-only access

Given

Parent linked to child via 6-char link code.

Then
  • Child's progress visible: books read, miles, latest parent digest
  • FK level NOT shown (parents see engagement metrics only)
FAIL PATH: Parent attempts any write action (add student, change class, modify reading level) β†’ HTTP 403 on all write endpoints.

10. Fluency Assessment

TC-FLUENCY-01 β€” Recording and scoring

When

Child reads a passage aloud, recording submitted.

Then
  • WCPM calculated
  • FK grade score returned
  • CEFR level assigned (Pre-A1 β†’ B2+) and stored in assessment_scores
  • Multilingual learner (multilingual_flag=true): relaxed thresholds (40% accuracy vs 50%, WCPM β‰₯5 vs β‰₯10)
  • No audio stored after processing

TC-FLUENCY-02 β€” Score storage and bot update

Then
  • assessment_scores row: FK grade + CEFR level
  • POST to Learner Bot /api/v1/bot/:learner_id/quiz-result
  • Bot updates curriculum_state memory

11. Content & Recommendations

TC-CONTENT-01 β€” "Picked for You" recommendation

When

Child (fk_level=3.5) loads library page.

Then
  • Bot queried for recommendation record
  • If Bot has recommendation: returned with permanentId, title, author, coverUrl, reason
  • If no Bot recommendation: fallback to /api/catalog?fk_min=3.0&fk_max=4.0 β€” first result used
  • LibraryPage renders "Picked for You" hero card
FAIL PATH: Both Bot and catalog return empty β†’ no hero card shown (graceful empty state, no crash).

TC-CONTENT-02 β€” Book pre-warming cache

Given

Pre-warming pipeline has run for a book.

When

Child with fk_level=3.5 opens any pre-warmed page.

Then

cached: true returned from Adaptive Engine β€” no GPT call, sub-100ms response.

12. Security & Tenant Isolation

TC-SEC-01 β€” Tenant isolation

When

Teacher A calls GET /api/v1/students.

Then
  • Response contains ONLY Teacher A's students
  • Teacher B's students NOT returned (not even partially)
  • Teacher A requesting Teacher B's class_id directly β†’ HTTP 403

TC-SEC-02 β€” No secrets in API responses

When

Any authenticated API call inspected.

Then
  • No pin_hash field in any response
  • No password_hash field in any response
  • No JWT secret or internal key visible
  • No server file paths in error messages

TC-SEC-03 β€” X-Internal-Key required for internal routes

When (no key)

Call internal endpoint without X-Internal-Key header.

Then

HTTP 401 β€” rejected.

FAIL PATH (wrong key): HTTP 403 β€” rejected. Correct key β†’ HTTP 200.

TC-SEC-04 β€” Rate limiting

When

100 POST requests to /api/auth/login within 1 minute from same IP.

Then

After threshold: HTTP 429 {error: "Too Many Requests", retryAfter: 60}. Legitimate logins resume after retry window.

TC-SEC-05 β€” No cross-service DB access

Verify

Inspect each service's DB connection string β€” service connects ONLY to its own schema. All cross-service data fetched via HTTP API with X-Internal-Key. No service has credentials to another service's DB.

13. Offline & PWA Behaviour

TC-PWA-01 β€” Service Worker registered

When

Visit app.readingtester.com in a modern browser β†’ DevTools β†’ Application β†’ Service Workers.

Then
  • sw.js registered and active (status: "running", not "waiting" or "redundant")
  • GET /sw.js β†’ HTTP 200

TC-PWA-02 β€” Background sync queue visible

When

Child goes offline, reads pages β†’ DevTools β†’ Application β†’ IndexedDB β†’ telemetry-queue.

Then

Queued event rows visible with event_id, event_type, client_ts. Count matches events fired.

When network restored
  • IndexedDB telemetry-queue empty (all replayed)
  • Server events table has all rows (verify by device_session_id)

14. Notifications

TC-NOTIF-01 β€” Teacher notification triggers

When any of these occur
  • Student PIN locked (5 failed attempts)
  • Quiz average < 65% (2+ quizzes)
  • Student inactive 4+ days
Then
  • teacher_notifications row inserted with correct type
  • Notification badge visible in Teacher Portal header
  • Clears when teacher marks as read

TC-NOTIF-02 β€” Email log audit

When

Any platform email dispatched (welcome, invite, RENEWAL_FAILED, etc.).

Then

email_log row inserted: recipient, template, sent_at β€” row present even if delivery fails (log the attempt, not just success).

⚑ Post-Deploy Smoke Checklist

Run after every deploy β€” ~10 minutes, covers the most critical paths.

#TestExpected
1Teacher loginRedirect to portal, session cookie set
2Create classHTTP 201, appears in list
3Add studentUsername + PIN returned, student in class
4Child login (correct PIN)Redirect to placement test or library
5Child login (wrong PIN Γ—5)Account locked, teacher notified
6Open bookreading_sessions row created, book renders
7Turn pagepage_turned event in Telemetry DB
8checkEntitlement (active teacher)Returns tier=full, no 403
9checkEntitlement (expired)Returns tier=free, gated feature returns 403
10Stripe webhook (invalid sig)HTTP 400, no state change
11Admin impersonate β†’ orange bannerBanner visible, action logged
12sw.js served at /sw.jsHTTP 200

Test Environment

# Reader App β€” app.readingtester.com
# Students (PIN: 1234 for all)
emma001 / james002 / olivia003 / liam004 / sophia005 / sofia001

# Teacher Portal β€” teacher.readingtester.com
sig@pickatale.com   (setup wizard already complete)
demo@pickatale.com

# DB access for verification
mysql -h 172.17.0.1 -P 3316 -u reader_app -preader_app_pw reader_app
mysql -h 172.17.0.1 -P 3316 -u teacherportal -pteacher_pw_2026 teacher_portal

# Internal key (for testing X-Internal-Key endpoints)
# Check INTERNAL_KEY in each service's .env

QA Test Cases v1.0 β€” 2026-04-19 Β· 61 tests across 14 systems Β· Ref: Wiki v1.9, AC-01–AC-13

On this page

Coverage Summary 1. Authentication 2. Roster & Classes 3. Reader App 4. Telemetry 5. Learner Bot 6. Teacher Portal 7. Billing 8. Admin & Compliance 9. Parent Portal 10. Fluency 11. Content 12. Security 13. PWA / Offline 14. Notifications ⚑ Smoke Checklist Test Environment