# 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 ```