From c00b1625fae850845c8fc291831881ee6bf61593 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Mon, 22 Jun 2026 18:45:45 +0700 Subject: [PATCH] chore: add E2E test script for production stack - Tests: health, dashboard/stats, recordings, messages, config, auth, voice guilds, frontend, 404 - Validates JSON fields + HTTP status codes - Runs against real production (imphnen.asepharyana.my.id) - Usage: bash scripts/e2e-test.sh [url] --- scripts/e2e-test.sh | 150 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100755 scripts/e2e-test.sh diff --git a/scripts/e2e-test.sh b/scripts/e2e-test.sh new file mode 100755 index 0000000..92a20f4 --- /dev/null +++ b/scripts/e2e-test.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ═══════════════════════════════════════════════════════════════ +# E2E Test — IMPHNEN Moderation Stack +# Tests frontend, backend API, DB connectivity, and moderation +# pipeline against the real production environment. +# ═══════════════════════════════════════════════════════════════ + +BASE_URL="${1:-https://imphnen.asepharyana.my.id}" +API="${BASE_URL}/api" + +PASS=0 +FAIL=0 +TIMEOUT=10 + +red() { printf "\033[31m%s\033[0m\n" "$*"; } +green() { printf "\033[32m%s\033[0m\n" "$*"; } +blue() { printf "\033[36m%s\033[0m\n" "$*"; } + +assert() { + local desc="$1" method="$2" url="$3" expect="$4" extra="$5" + local code body + if [ "$method" = "GET" ]; then + body=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT" "$url" 2>&1) + code="$body" + elif [ "$method" = "POST" ]; then + body=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT" -X POST -H "Content-Type: application/json" -d "$extra" "$url" 2>&1) + code="$body" + fi + + if [ "$code" = "$expect" ]; then + green " ✓ $desc" + PASS=$((PASS + 1)) + else + red " ✗ $desc — expected $expect, got $code" + FAIL=$((FAIL + 1)) + fi +} + +assert_json_field() { + local desc="$1" url="$2" field="$3" extra="${4:-}" + local val + if [ -n "$extra" ]; then + val=$(curl -s --max-time "$TIMEOUT" -X POST -H "Content-Type: application/json" -d "$extra" "$url" 2>&1 | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + keys = '${field}'.split('.') + for k in keys: + d = d[k] + print(d) +except: print('__MISSING__') +") + else + val=$(curl -s --max-time "$TIMEOUT" "$url" 2>&1 | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + keys = '${field}'.split('.') + for k in keys: + d = d[k] + print(d) +except: print('__MISSING__') +") + fi + + if [ "$val" != "__MISSING__" ] && [ -n "$val" ]; then + green " ✓ $desc (${field}=${val:0:80})" + PASS=$((PASS + 1)) + else + red " ✗ $desc — field '${field}' not found" + FAIL=$((FAIL + 1)) + fi +} + +assert_contains() { + local desc="$1" url="$2" needle="$3" + local body + body=$(curl -s --max-time "$TIMEOUT" "$url" 2>&1) + if echo "$body" | grep -q "$needle"; then + green " ✓ $desc" + PASS=$((PASS + 1)) + else + red " ✗ $desc — expected response to contain '${needle}'" + FAIL=$((FAIL + 1)) + fi +} + +# ─────────────────────────────────────────────────────────────── +blue "" +blue "════════════════════════════════════════════════════════" +blue " E2E Test: IMPHNEN Moderation Stack" +blue " Target: ${BASE_URL}" +blue "════════════════════════════════════════════════════════" +blue "" + +# ── 1. Health Check ──────────────────────────────────────────── +blue "── API: Health Check ──" +assert "GET /api/health → 200" GET "${API}/health" 200 "" +assert_json_field "health.status == healthy" "${API}/health" "status" + +# ── 2. Dashboard ─────────────────────────────────────────────── +blue "── API: Dashboard ──" +assert "GET /api/dashboard/stats → 200" GET "${API}/dashboard/stats" 200 "" +assert_json_field "dashboard.total_messages" "${API}/dashboard/stats" "total_messages" +assert_json_field "dashboard.total_flagged" "${API}/dashboard/stats" "total_flagged" +assert_json_field "dashboard.active_users_24h" "${API}/dashboard/stats" "active_users_24h" + +# ── 3. Recordings ────────────────────────────────────────────── +blue "── API: Recordings ──" +assert "GET /api/recordings → 200" GET "${API}/recordings?limit=5" 200 "" +assert_json_field "recordings.items array" "${API}/recordings?limit=5" "items" + +# ── 4. Messages ──────────────────────────────────────────────── +blue "── API: Messages ──" +assert "GET /api/messages (no channelId) → 400" GET "${API}/messages?limit=3" 400 "" + +# ── 5. Config ────────────────────────────────────────────────── +blue "── API: Config ──" +assert "GET /api/config → 200" GET "${API}/config" 200 "" + +# ── 6. Auth ──────────────────────────────────────────────────── +blue "── API: Auth (POST) → 401" POST "${API}/auth" 401 '{"password": "wrong"}' + +# ── 7. Voice Guilds ──────────────────────────────────────────── +blue "── API: Voice ──" +assert "GET /api/guilds → 200" GET "${API}/guilds" 200 "" + +# ── 8. Frontend ──────────────────────────────────────────────── +blue "── Frontend ──" +assert "GET / → 200" GET "${BASE_URL}/" 200 "" +assert_contains "Frontend renders title" "${BASE_URL}/" "IMPHNEN" + +# ── 9. Endpoints that should 404 ────────────────────────────── +blue "── Negative Tests ──" +assert "GET /api/nonexistent → 404" GET "${API}/nonexistent" 404 "" + +# ── Summary ──────────────────────────────────────────────────── +blue "" +blue "════════════════════════════════════════════════════════" +if [ "$FAIL" -eq 0 ]; then + green " ALL ${PASS} TESTS PASSED" +else + red " ${PASS} passed, ${FAIL} failed" +fi +blue "════════════════════════════════════════════════════════" +blue "" + +exit "$FAIL"