# QA Test Report — Production
**Date:** 2026-06-24 06:33:17
**URL:** https://falahos.my/mobile

## Summary
| ✅ Pass | ❌ Fail | ⚠️ Warn | Total | Grade |
|:------:|:------:|:------:|:----:|:-----:|
| 98 | 0 | 5 | 103 | **A** |

## Details
| Result | Message |
|:------|:--------|
| ✅ | Landing page (/) → 200 in 111ms |
| ✅ | Learn catalog page (/learn) → 200 in 60ms |
| ✅ | Certificate verification (/verify/ABC123) → 200 in 90ms |
| ✅ | Health API (/api/health) → 200 in 55ms |
| ✅ | Learn courses API (/api/learn/courses) → 200 in 68ms |
| ✅ | Course detail (no auth) (/api/learn/courses/atomic-habits) → 401 in 64ms |
| ✅ | /this-does-not-exist → 404 |
| ✅ | /api/nonexistent → 404 |
| ✅ | Health API — status=ok, db=True, uptime=2084s |
| ✅ | Courses API — 1 course(s) returned |
| ✅ | Course #0: 'Atomic Habits' by James Clear — 5 modules, 25min, beginner |
| ✅ | Course detail without auth → 401 (correct) |
| ✅ | Course detail — 'Atomic Habits', 5 modules |
| ✅ | Course detail — 5 module(s) with content+quiz |
| ✅ | Module #1: 'The 1% Rule' — 5min, 3 quiz Qs |
| ✅ | Module #2: 'Identity-Based Habits' — 5min, 2 quiz Qs |
| ✅ | Module #3: 'The 4 Laws of Behavior Change' — 5min, 2 quiz Qs |
| ✅ | Module #4: 'Habit Stacking' — 5min, 1 quiz Qs |
| ✅ | Module #5: 'Design Your Environment' — 5min, 2 quiz Qs |
| ✅ | Verify page — 200 OK |
| ✅ | Verify — JSON response: None |
| ✅ | Landing (/) — clean (13749B) |
| ✅ | Landing (/) — contains "Falah" |
| ✅ | Learn Catalog (/learn) — clean (14195B) |
| ✅ | Learn Catalog (/learn) — contains "Falah" |
| ✅ | Verify Certificate (/verify/ABC123) — clean (16735B) |
| ✅ | Verify Certificate (/verify/ABC123) — contains "Verify" |
| ✅ | Verify Certificate (/verify/ABC123) — contains "Certificate" |
| ✅ | Pattern "Invalid response" — absent |
| ✅ | Pattern "Internal Server Error" — absent |
| ✅ | Pattern "Application error" — absent |
| ✅ | Pattern "Cannot read properties of" — absent |
| ✅ | Pattern "undefined is not" — absent |
| ✅ | Pattern "TypeError" — absent |
| ✅ | Pattern "Failed to fetch" — absent |
| ✅ | Pattern "500 Internal" — absent |
| ✅ | Pattern "JSON Parse error" — absent |
| ✅ | Pattern "Unexpected token" — absent |
| ✅ | Pattern "Cannot find module" — absent |
| ✅ | Pattern "Minified React error" — absent |
| ✅ | Unauthenticated / — accessible |
| ✅ | Unauthenticated /learn — accessible |
| ✅ | Login — obtained token (eyJhbGciOiJIUzI1...UDk8) |
| ✅ | Courses listed — Atomic Habits (slug: atomic-habits) |
| ✅ | Course detail loaded — 5 module(s) |
| ✅ |   Module: 'The 1% Rule' — 5min, quiz: 3 Qs |
| ✅ |   Module: 'Identity-Based Habits' — 5min, quiz: 2 Qs |
| ✅ |   Module: 'The 4 Laws of Behavior Change' — 5min, quiz: 2 Qs |
| ✅ |   Module: 'Habit Stacking' — 5min, quiz: 1 Qs |
| ✅ |   Module: 'Design Your Environment' — 5min, quiz: 2 Qs |
| ✅ | Certificate verification page loads |
| ✅ | api/learn/courses/atomic-habits with bad token → 401 (graceful) |
| ✅ | SQLi '' OR 1=1--...' on api/learn/courses/ → 401 |
| ⚠️ | SQLi on learn/ → 200 |
| ✅ | SQLi ''; DROP TABL...' on api/learn/courses/ → 401 |
| ⚠️ | SQLi on learn/ → 200 |
| ✅ | SQLi '" OR "1"%3...' on api/learn/courses/ → 401 |
| ⚠️ | SQLi on learn/ → 200 |
| ✅ | Path traversal '../../../etc/passwd' → 404 |
| ✅ | Path traversal '..%2F..%2F..%2Fetc%2Fpasswd' → 401 |
| ✅ | Verify code '' → 404 |
| ✅ | Verify code 'INVALID!@#$' → 200 |
| ⚠️ | Verify code 'aaaaaaaaaaaaaaaaaaaa' → 503 |
| ✅ | Burst — 10/10 OK (avg 123ms, total 0.2s) |
| ✅ | / — viewport meta present |
| ✅ | /learn — viewport meta present |
| ✅ | /verify/ABC123 — viewport meta present |
| ✅ | / — safe-area inset detected |
| ✅ | /learn — safe-area inset detected |
| ✅ | /verify/ABC123 — safe-area inset detected |
| ✅ | / — dark theme detected |
| ✅ | /learn — dark theme detected |
| ✅ | /verify/ABC123 — dark theme detected |
| ✅ | / — responsive container detected |
| ✅ | /learn — responsive container detected |
| ✅ | /verify/ABC123 — responsive container detected |
| ✅ | Landing page (/) — cold=102ms warm=123ms, 13KB |
| ✅ | Learn catalog (/learn) — cold=84ms warm=62ms, 14KB |
| ✅ | Health API (/api/health) — cold=45ms warm=76ms, 0KB |
| ✅ | Courses API (/api/learn/courses) — cold=86ms warm=69ms, 0KB |
| ✅ | / — no JWT token leak |
| ✅ | / — no API key leak |
| ✅ | / — no GitHub PAT leak |
| ✅ | / — no Private key leak |
| ✅ | /learn — no JWT token leak |
| ✅ | /learn — no API key leak |
| ✅ | /learn — no GitHub PAT leak |
| ✅ | /learn — no Private key leak |
| ✅ | /verify/ABC123 — no JWT token leak |
| ✅ | /verify/ABC123 — no API key leak |
| ✅ | /verify/ABC123 — no GitHub PAT leak |
| ✅ | /verify/ABC123 — no Private key leak |
| ✅ | Content-Type: text/html; charset=utf-8 |
| ✅ | X-Content-Type-Options: nosniff |
| ✅ | Learn page — HTTP 200 (Playwright) |
| ✅ | Learn page — no JS console errors |
| ⚠️ | Learn page — may be empty/loading (text not found) |
| ✅ | Learn page — title: Falah — Islamic Lifestyle |
| ✅ | Learn page — loaded with auth (Playwright) |
| ✅ | Learn page — course content visible after auth |
| ✅ | Auth token persists in localStorage |
| ✅ | Verify page — HTTP 200 (Playwright) |
| ✅ | Verify page — 159 chars rendered |

## Re-run
```bash
/usr/bin/python3.12 /root/falah-mobile/tests/qa-learn-prod.py
```