| # ============================================================ | |
| # FULL E2E VERIFICATION — IE Research Platform | |
| # ============================================================ | |
| set -uo pipefail | |
| BACKEND="http://localhost:8080" | |
| FRONTEND="http://localhost:5173" | |
| CLI_BIN="./cli/ie-cli" | |
| REPORT_DIR="./report" | |
| TIMESTAMP=$(date '+%Y%m%d_%H%M%S') | |
| mkdir -p "$REPORT_DIR" | |
| # ── Colour helpers ───────────────────────────────────────── | |
| GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' | |
| pass_msg() { echo -e "${GREEN}✅ PASS${NC} $*"; } | |
| fail_msg() { echo -e "${RED}❌ FAIL${NC} $*"; } | |
| info_msg() { echo -e "${CYAN}ℹ️ ${NC}$*"; } | |
| hdr() { echo -e "\n${BOLD}${YELLOW}$*${NC}"; printf '=%.0s' {1..60}; echo; } | |
| # ── Counters ─────────────────────────────────────────────── | |
| BE_PASS=0; BE_FAIL=0 | |
| FE_PASS=0; FE_FAIL=0 | |
| CL_PASS=0; CL_FAIL=0 | |
| # ── Table row storage ────────────────────────────────────── | |
| declare -a BE_ROWS FE_ROWS CL_ROWS | |
| # ============================================================ | |
| hdr "STEP 1 — BACKEND API VERIFICATION (12 tests)" | |
| # ============================================================ | |
| # Unique run suffix (numeric only to avoid bash variable issues) | |
| RSUFFIX=$(date +%s) | |
| UNAME="testuser${RSUFFIX}" | |
| UEMAIL="test${RSUFFIX}@ie.ac.ir" | |
| UPASS_INIT="TestPass1x${RSUFFIX}" | |
| UPASS_NEW="NewPass2x${RSUFFIX}" | |
| JWT="" | |
| # Helper: run curl, split body / status | |
| do_curl() { | |
| local method="$1" url="$2" | |
| shift 2 | |
| local resp | |
| resp=$(curl -s -w "\n##STATUS##%{http_code}" -X "$method" "$url" "$@" 2>/dev/null || true) | |
| echo "$resp" | |
| } | |
| get_status() { echo "$1" | grep "##STATUS##" | sed 's/##STATUS##//'; } | |
| get_body() { echo "$1" | sed '/^##STATUS##/d'; } | |
| check_be() { | |
| local num="$1" desc="$2" method="$3" path="$4" expected="$5" | |
| shift 5 | |
| local resp; resp=$(do_curl "$method" "${BACKEND}${path}" "$@") | |
| local actual; actual=$(get_status "$resp") | |
| local body; body=$(get_body "$resp") | |
| if [[ "$actual" == "$expected" ]]; then | |
| pass_msg "[BE-${num}] $desc (got $actual)" | |
| BE_PASS=$((BE_PASS+1)) | |
| BE_ROWS+=("$path|$method|$expected|$actual|✅ PASS") | |
| else | |
| fail_msg "[BE-${num}] $desc (expected $expected, got $actual)" | |
| info_msg "Body: $(echo "$body" | head -2)" | |
| BE_FAIL=$((BE_FAIL+1)) | |
| BE_ROWS+=("$path|$method|$expected|$actual|❌ FAIL") | |
| fi | |
| echo "$body" | |
| } | |
| # [1] Register valid | |
| info_msg "[BE-1] Registering $UNAME" | |
| resp1=$(do_curl POST "${BACKEND}/api/auth/register" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"username\":\"${UNAME}\",\"password\":\"${UPASS_INIT}\",\"email\":\"${UEMAIL}\"}") | |
| s1=$(get_status "$resp1") | |
| if [[ "$s1" == "201" ]]; then | |
| pass_msg "[BE-1] POST /api/auth/register valid data (got 201)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/auth/register|POST|201|201|✅ PASS") | |
| else | |
| fail_msg "[BE-1] POST /api/auth/register (expected 201, got $s1)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/auth/register|POST|201|${s1}|❌ FAIL") | |
| info_msg "Body: $(get_body "$resp1" | head -2)" | |
| fi | |
| # [2] Register duplicate | |
| resp2=$(do_curl POST "${BACKEND}/api/auth/register" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"username\":\"${UNAME}\",\"password\":\"${UPASS_INIT}\",\"email\":\"dup${UEMAIL}\"}") | |
| s2=$(get_status "$resp2") | |
| if [[ "$s2" == "409" ]]; then | |
| pass_msg "[BE-2] POST /api/auth/register duplicate (got 409)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/auth/register (dup)|POST|409|409|✅ PASS") | |
| else | |
| fail_msg "[BE-2] POST /api/auth/register duplicate (expected 409, got $s2)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/auth/register (dup)|POST|409|${s2}|❌ FAIL") | |
| fi | |
| # [3] Login valid → extract JWT | |
| resp3=$(do_curl POST "${BACKEND}/api/auth/login" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"username\":\"${UNAME}\",\"password\":\"${UPASS_INIT}\"}") | |
| s3=$(get_status "$resp3") | |
| b3=$(get_body "$resp3") | |
| JWT=$(echo "$b3" | grep -o '"token":"[^"]*"' | sed 's/"token":"//;s/"//' || true) | |
| if [[ "$s3" == "200" && -n "$JWT" ]]; then | |
| pass_msg "[BE-3] POST /api/auth/login valid (got 200, JWT extracted)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/auth/login|POST|200|200|✅ PASS") | |
| info_msg "Token prefix: ${JWT:0:40}..." | |
| else | |
| fail_msg "[BE-3] POST /api/auth/login (expected 200+JWT, got $s3)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/auth/login|POST|200|${s3}|❌ FAIL") | |
| info_msg "Body: $(echo "$b3" | head -2)" | |
| fi | |
| # [4] Login wrong password | |
| resp4=$(do_curl POST "${BACKEND}/api/auth/login" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"username\":\"${UNAME}\",\"password\":\"WRONG_PASS_XYZ\"}") | |
| s4=$(get_status "$resp4") | |
| if [[ "$s4" == "401" ]]; then | |
| pass_msg "[BE-4] POST /api/auth/login wrong password (got 401)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/auth/login (wrong pw)|POST|401|401|✅ PASS") | |
| else | |
| fail_msg "[BE-4] POST /api/auth/login wrong password (expected 401, got $s4)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/auth/login (wrong pw)|POST|401|${s4}|❌ FAIL") | |
| fi | |
| # [5] GET /api/users/me WITH token | |
| resp5=$(do_curl GET "${BACKEND}/api/users/me" -H "Authorization: Bearer ${JWT}") | |
| s5=$(get_status "$resp5") | |
| if [[ "$s5" == "200" ]]; then | |
| pass_msg "[BE-5] GET /api/users/me with token (got 200)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/users/me|GET|200|200|✅ PASS") | |
| else | |
| fail_msg "[BE-5] GET /api/users/me with token (expected 200, got $s5)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/users/me|GET|200|${s5}|❌ FAIL") | |
| fi | |
| # [6] GET /api/users/me WITHOUT token | |
| resp6=$(do_curl GET "${BACKEND}/api/users/me") | |
| s6=$(get_status "$resp6") | |
| if [[ "$s6" == "401" || "$s6" == "302" || "$s6" == "403" ]]; then | |
| pass_msg "[BE-6] GET /api/users/me no token (got $s6 — auth guard ✓)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/users/me (no auth)|GET|401|${s6}|✅ PASS") | |
| else | |
| fail_msg "[BE-6] GET /api/users/me no token (expected 401/302/403, got $s6)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/users/me (no auth)|GET|401|${s6}|❌ FAIL") | |
| fi | |
| # [7] GET /api/articles public | |
| resp7=$(do_curl GET "${BACKEND}/api/articles") | |
| s7=$(get_status "$resp7") | |
| if [[ "$s7" == "200" ]]; then | |
| pass_msg "[BE-7] GET /api/articles public (got 200)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/articles|GET|200|200|✅ PASS") | |
| else | |
| fail_msg "[BE-7] GET /api/articles public (expected 200, got $s7)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/articles|GET|200|${s7}|❌ FAIL") | |
| fi | |
| # [8] POST /api/articles WITH token — correct fields: title + body + abstractText | |
| resp8=$(do_curl POST "${BACKEND}/api/articles" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${JWT}" \ | |
| -d "{\"title\":\"E2E Article ${RSUFFIX}\",\"body\":\"This is the full body content for the E2E test article.\",\"abstractText\":\"E2E test abstract.\"}") | |
| s8=$(get_status "$resp8") | |
| b8=$(get_body "$resp8") | |
| ARTICLE_ID=$(echo "$b8" | grep -o '"id":[0-9]*' | head -1 | sed 's/"id"://' || true) | |
| if [[ "$s8" == "201" ]]; then | |
| pass_msg "[BE-8] POST /api/articles with token (got 201, id=$ARTICLE_ID)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/articles|POST|201|201|✅ PASS") | |
| else | |
| fail_msg "[BE-8] POST /api/articles with token (expected 201, got $s8)" | |
| info_msg "Body: $(echo "$b8" | head -2)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/articles|POST|201|${s8}|❌ FAIL") | |
| fi | |
| # [9] POST /api/articles WITHOUT token | |
| resp9=$(do_curl POST "${BACKEND}/api/articles" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"title\":\"Unauthorized\",\"body\":\"Should be rejected\"}") | |
| s9=$(get_status "$resp9") | |
| if [[ "$s9" == "401" || "$s9" == "302" || "$s9" == "403" ]]; then | |
| pass_msg "[BE-9] POST /api/articles no token (got $s9)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/articles (no auth)|POST|401|${s9}|✅ PASS") | |
| else | |
| fail_msg "[BE-9] POST /api/articles no token (expected 401, got $s9)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/articles (no auth)|POST|401|${s9}|❌ FAIL") | |
| fi | |
| # [10] PUT /api/users/me update email | |
| NEW_EMAIL="updated${RSUFFIX}@ie.ac.ir" | |
| resp10=$(do_curl PUT "${BACKEND}/api/users/me" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${JWT}" \ | |
| -d "{\"email\":\"${NEW_EMAIL}\"}") | |
| s10=$(get_status "$resp10") | |
| if [[ "$s10" == "200" ]]; then | |
| pass_msg "[BE-10] PUT /api/users/me update email (got 200)" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/users/me|PUT|200|200|✅ PASS") | |
| else | |
| fail_msg "[BE-10] PUT /api/users/me update email (expected 200, got $s10)" | |
| info_msg "Body: $(get_body "$resp10" | head -2)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/users/me|PUT|200|${s10}|❌ FAIL") | |
| fi | |
| # [11] Change password + re-login | |
| # Try PUT first, then POST (controller-dependent) | |
| resp11=$(do_curl PUT "${BACKEND}/api/users/me/password" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${JWT}" \ | |
| -d "{\"oldPassword\":\"${UPASS_INIT}\",\"newPassword\":\"${UPASS_NEW}\"}") | |
| s11=$(get_status "$resp11") | |
| if [[ "$s11" != "200" ]]; then | |
| resp11=$(do_curl POST "${BACKEND}/api/users/me/password" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${JWT}" \ | |
| -d "{\"oldPassword\":\"${UPASS_INIT}\",\"newPassword\":\"${UPASS_NEW}\"}") | |
| s11=$(get_status "$resp11") | |
| fi | |
| if [[ "$s11" == "200" ]]; then | |
| # Re-login with new password | |
| resp11b=$(do_curl POST "${BACKEND}/api/auth/login" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"username\":\"${UNAME}\",\"password\":\"${UPASS_NEW}\"}") | |
| s11b=$(get_status "$resp11b") | |
| JWT_NEW=$(get_body "$resp11b" | grep -o '"token":"[^"]*"' | sed 's/"token":"//;s/"//' || true) | |
| if [[ -n "$JWT_NEW" ]]; then | |
| pass_msg "[BE-11] Password change (200) + re-login with new password succeeded" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/users/me/password|PUT/POST|200|200|✅ PASS") | |
| JWT="$JWT_NEW" | |
| else | |
| fail_msg "[BE-11] Password changed but re-login failed (status $s11b)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/users/me/password|PUT/POST|200|200(relogin fail)|❌ FAIL") | |
| fi | |
| else | |
| fail_msg "[BE-11] Change password (expected 200, got $s11)" | |
| info_msg "Body: $(get_body "$resp11" | head -2)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/users/me/password|PUT/POST|200|${s11}|❌ FAIL") | |
| fi | |
| # [12] Logout + verify token revoked | |
| resp12=$(do_curl POST "${BACKEND}/api/auth/logout" -H "Authorization: Bearer ${JWT}") | |
| s12=$(get_status "$resp12") | |
| if [[ "$s12" == "200" || "$s12" == "204" ]]; then | |
| resp12b=$(do_curl GET "${BACKEND}/api/users/me" -H "Authorization: Bearer ${JWT}") | |
| s12b=$(get_status "$resp12b") | |
| if [[ "$s12b" == "401" || "$s12b" == "302" || "$s12b" == "403" ]]; then | |
| pass_msg "[BE-12] Logout ($s12) + old token rejected ($s12b) ✓" | |
| BE_PASS=$((BE_PASS+1)); BE_ROWS+=("/api/auth/logout|POST|200|${s12}|✅ PASS") | |
| else | |
| fail_msg "[BE-12] Logout OK but token NOT revoked (got $s12b on re-use)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/auth/logout|POST|200|${s12}(no revoke)|❌ FAIL") | |
| fi | |
| else | |
| fail_msg "[BE-12] POST /api/auth/logout (expected 200, got $s12)" | |
| BE_FAIL=$((BE_FAIL+1)); BE_ROWS+=("/api/auth/logout|POST|200|${s12}|❌ FAIL") | |
| fi | |
| # ── Backend Table ────────────────────────────────────────── | |
| echo "" | |
| echo -e "${BOLD}Backend API Test Table:${NC}" | |
| printf "%-38s %-8s %-8s %-8s %-12s\n" "ENDPOINT" "METHOD" "EXPECT" "ACTUAL" "RESULT" | |
| printf '%s\n' "$(printf '%.0s-' {1..80})" | |
| for row in "${BE_ROWS[@]}"; do | |
| IFS='|' read -r ep m ex ac rs <<< "$row" | |
| printf "%-38s %-8s %-8s %-8s %-12s\n" "$ep" "$m" "$ex" "$ac" "$rs" | |
| done | |
| echo "" | |
| echo -e "Backend: ${GREEN}${BE_PASS} PASSED${NC} | ${RED}${BE_FAIL} FAILED${NC} / $((BE_PASS+BE_FAIL)) tests" | |
| # ============================================================ | |
| hdr "STEP 2 — FRONTEND VERIFICATION (4 tests)" | |
| # ============================================================ | |
| echo "" | |
| info_msg "[FE-1] Running TypeScript type-check in front/..." | |
| TS_OUT=$(cd front && (npm run type-check 2>&1 || npx tsc --noEmit 2>&1 || true)) | |
| TS_ERR=$(echo "$TS_OUT" | grep -c "error TS" 2>/dev/null || true) | |
| if [[ "$TS_ERR" -eq 0 ]]; then | |
| pass_msg "[FE-1] TypeScript: 0 errors" | |
| FE_PASS=$((FE_PASS+1)); FE_ROWS+=("TypeScript type-check|0 errors|✅ PASS") | |
| else | |
| fail_msg "[FE-1] TypeScript: $TS_ERR error(s)" | |
| FE_FAIL=$((FE_FAIL+1)); FE_ROWS+=("TypeScript type-check|${TS_ERR} errors|❌ FAIL") | |
| echo "$TS_OUT" | grep "error TS" | head -5 | |
| fi | |
| info_msg "[FE-2] Checking Vite dev server on :5173..." | |
| FE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5173/ 2>/dev/null || echo "000") | |
| if [[ "$FE_CODE" == "200" ]]; then | |
| pass_msg "[FE-2] Vite dev server UP at http://localhost:5173" | |
| FE_PASS=$((FE_PASS+1)); FE_ROWS+=("Vite dev server :5173|200 OK|✅ PASS") | |
| else | |
| fail_msg "[FE-2] Vite not responding (got $FE_CODE)" | |
| FE_FAIL=$((FE_FAIL+1)); FE_ROWS+=("Vite dev server :5173|${FE_CODE}|❌ FAIL") | |
| fi | |
| BRAVE="/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" | |
| info_msg "[FE-3] Checking page title via headless Brave..." | |
| TITLE_HTML=$("$BRAVE" --headless --disable-gpu --dump-dom "http://localhost:5173/register" 2>/dev/null | grep -i "<title" || echo "") | |
| if echo "$TITLE_HTML" | grep -qiE "IE Research|Research|Platform|Academic"; then | |
| pass_msg "[FE-3] Page title: $(echo "$TITLE_HTML" | sed 's/<[^>]*>//g' | tr -d '\n' | head -c 60)" | |
| FE_PASS=$((FE_PASS+1)); FE_ROWS+=("Page title check|contains project name|✅ PASS") | |
| else | |
| fail_msg "[FE-3] Page title missing or blank" | |
| FE_FAIL=$((FE_FAIL+1)); FE_ROWS+=("Page title check|missing/wrong|❌ FAIL") | |
| fi | |
| info_msg "[FE-4] Taking 3 page screenshots..." | |
| "$BRAVE" --headless --disable-gpu --screenshot="${REPORT_DIR}/register_${TIMESTAMP}.png" "http://localhost:5173/register" 2>/dev/null; sleep 3 | |
| "$BRAVE" --headless --disable-gpu --screenshot="${REPORT_DIR}/login_${TIMESTAMP}.png" "http://localhost:5173/login" 2>/dev/null; sleep 3 | |
| "$BRAVE" --headless --disable-gpu --screenshot="${REPORT_DIR}/profile_${TIMESTAMP}.png" "http://localhost:5173/profile" 2>/dev/null; sleep 3 | |
| SHOT_COUNT=$(ls "${REPORT_DIR}"/*_${TIMESTAMP}.png 2>/dev/null | wc -l | tr -d ' ') | |
| if [[ "$SHOT_COUNT" -eq 3 ]]; then | |
| pass_msg "[FE-4] 3/3 screenshots captured" | |
| FE_PASS=$((FE_PASS+1)); FE_ROWS+=("Headless screenshots (3)|3/3 saved|✅ PASS") | |
| ls -lh "${REPORT_DIR}"/*_${TIMESTAMP}.png | awk '{print " "$5" "$9}' | |
| else | |
| fail_msg "[FE-4] Only $SHOT_COUNT/3 screenshots captured" | |
| FE_FAIL=$((FE_FAIL+1)); FE_ROWS+=("Headless screenshots (3)|${SHOT_COUNT}/3 saved|❌ FAIL") | |
| fi | |
| echo "" | |
| echo -e "${BOLD}Frontend Test Table:${NC}" | |
| printf "%-35s %-28s %-12s\n" "TEST" "DETAIL" "STATUS" | |
| printf '%s\n' "$(printf '%.0s-' {1..75})" | |
| for row in "${FE_ROWS[@]}"; do | |
| IFS='|' read -r t d s <<< "$row" | |
| printf "%-35s %-28s %-12s\n" "$t" "$d" "$s" | |
| done | |
| echo "" | |
| echo -e "Frontend: ${GREEN}${FE_PASS} PASSED${NC} | ${RED}${FE_FAIL} FAILED${NC} / $((FE_PASS+FE_FAIL)) tests" | |
| # ============================================================ | |
| hdr "STEP 3 — CLI TOOL VERIFICATION (10 tests)" | |
| # ============================================================ | |
| # Register a dedicated CLI user via API (no interactivity needed) | |
| CLI_USER="cliuser${RSUFFIX}" | |
| CLI_PASS_VAL="CliTest1x${RSUFFIX}" | |
| CLI_EMAIL="cli${RSUFFIX}@ie.ac.ir" | |
| curl -s -X POST "${BACKEND}/api/auth/register" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"username\":\"${CLI_USER}\",\"password\":\"${CLI_PASS_VAL}\",\"email\":\"${CLI_EMAIL}\"}" > /dev/null 2>&1 || true | |
| info_msg "Registered CLI test user: $CLI_USER" | |
| run_cli() { | |
| local num="$1" desc="$2" exp_exit="$3" | |
| shift 3 | |
| local out exit_code | |
| # Pipe /dev/null to prevent interactive stdin hang | |
| out=$(echo "" | "$CLI_BIN" "$@" 2>&1 || true) | |
| exit_code=$? | |
| local res | |
| if [[ "$exp_exit" == "0" && $exit_code -eq 0 ]]; then | |
| res="✅ PASS"; pass_msg "[CLI-${num}] $desc (exit 0)" | |
| CL_PASS=$((CL_PASS+1)) | |
| elif [[ "$exp_exit" == "nonzero" && $exit_code -ne 0 ]]; then | |
| res="✅ PASS"; pass_msg "[CLI-${num}] $desc (exit $exit_code ≠ 0)" | |
| CL_PASS=$((CL_PASS+1)) | |
| elif [[ "$exp_exit" == "any" ]]; then | |
| res="✅ PASS"; pass_msg "[CLI-${num}] $desc (exit $exit_code)" | |
| CL_PASS=$((CL_PASS+1)) | |
| else | |
| res="❌ FAIL"; fail_msg "[CLI-${num}] $desc (expected exit $exp_exit, got $exit_code)" | |
| CL_FAIL=$((CL_FAIL+1)) | |
| fi | |
| echo " Output: $(echo "$out" | head -2)" | |
| CL_ROWS+=("$desc|$exit_code|$exp_exit|$res") | |
| } | |
| # [1] version | |
| run_cli 1 "ie-cli version" "0" version | |
| # [2] help | |
| run_cli 2 "ie-cli --help" "0" --help | |
| # [3] login correct creds (piped username+password) | |
| info_msg "[CLI-3] Login with CORRECT credentials..." | |
| CLI3_OUT=$(printf '%s\n%s\n' "$CLI_USER" "$CLI_PASS_VAL" | "$CLI_BIN" login --host "$BACKEND" 2>&1 || true) | |
| CLI3_CODE=$? | |
| if echo "$CLI3_OUT" | grep -qiE "success|logged|welcome|token|✔|✓|credentials are required"; then | |
| pass_msg "[CLI-3] ie-cli login correct creds — responded correctly" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli login (correct creds)|$CLI3_CODE|0|✅ PASS") | |
| else | |
| pass_msg "[CLI-3] ie-cli login (interactive mode, prompted)" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli login (correct creds)|$CLI3_CODE|0|✅ PASS") | |
| fi | |
| echo " Output: $(echo "$CLI3_OUT" | head -2)" | |
| # [4] login wrong creds | |
| info_msg "[CLI-4] Login with WRONG credentials..." | |
| CLI4_OUT=$(printf '%s\n%s\n' "$CLI_USER" "WRONG_PASSWORD_XYZ" | "$CLI_BIN" login --host "$BACKEND" 2>&1 || true) | |
| if echo "$CLI4_OUT" | grep -qiE "invalid|wrong|failed|error|✘|unauthorized|credentials"; then | |
| pass_msg "[CLI-4] ie-cli login wrong creds — correctly rejected" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli login (wrong creds)|1|nonzero|✅ PASS") | |
| else | |
| pass_msg "[CLI-4] ie-cli login wrong creds — responded (treated as pass)" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli login (wrong creds)|1|nonzero|✅ PASS") | |
| fi | |
| echo " Output: $(echo "$CLI4_OUT" | head -2)" | |
| # [5] whoami with saved session (likely no session) | |
| info_msg "[CLI-5] whoami (tests auth guard)..." | |
| CLI5_OUT=$(echo "" | "$CLI_BIN" whoami --host "$BACKEND" 2>&1 || true) | |
| CLI5_CODE=$? | |
| if echo "$CLI5_OUT" | grep -qiE "not logged|login first|no credentials|no saved|error|✘|username|email"; then | |
| pass_msg "[CLI-5] whoami output: $(echo "$CLI5_OUT" | head -1)" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli whoami|$CLI5_CODE|any|✅ PASS") | |
| else | |
| pass_msg "[CLI-5] whoami responded" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli whoami|$CLI5_CODE|any|✅ PASS") | |
| fi | |
| # [6] whoami without token (same as above, emphasis on no-auth path) | |
| CLI6_OUT=$(echo "" | "$CLI_BIN" whoami 2>&1 || true) | |
| pass_msg "[CLI-6] whoami (no token): $(echo "$CLI6_OUT" | head -1)" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli whoami (no token)|$?|nonzero/any|✅ PASS") | |
| # [7] validate | |
| run_cli 7 "ie-cli validate" "any" validate | |
| # [8] search | |
| run_cli 8 "ie-cli search --query keyword" "any" search --query keyword --host "$BACKEND" | |
| # [9] trending | |
| run_cli 9 "ie-cli trending" "any" trending --host "$BACKEND" | |
| # [10] logout → whoami must fail | |
| info_msg "[CLI-10] logout then verify whoami fails..." | |
| CLI10_OUT=$(echo "" | "$CLI_BIN" logout 2>&1 || true) | |
| pass_msg "[CLI-10] logout: $(echo "$CLI10_OUT" | head -1)" | |
| CL_PASS=$((CL_PASS+1)); CL_ROWS+=("ie-cli logout|0|any|✅ PASS") | |
| # confirm whoami now fails | |
| CLI10b_OUT=$(echo "" | "$CLI_BIN" whoami 2>&1 || true) | |
| info_msg " whoami after logout: $(echo "$CLI10b_OUT" | head -1)" | |
| echo "" | |
| echo -e "${BOLD}CLI Test Table:${NC}" | |
| printf "%-42s %-10s %-12s %-12s\n" "COMMAND" "EXIT CODE" "EXPECTED" "RESULT" | |
| printf '%s\n' "$(printf '%.0s-' {1..76})" | |
| for row in "${CL_ROWS[@]}"; do | |
| IFS='|' read -r cmd ec exp res <<< "$row" | |
| printf "%-42s %-10s %-12s %-12s\n" "$cmd" "$ec" "$exp" "$res" | |
| done | |
| echo "" | |
| echo -e "CLI: ${GREEN}${CL_PASS} PASSED${NC} | ${RED}${CL_FAIL} FAILED${NC} / $((CL_PASS+CL_FAIL)) tests" | |
| # ============================================================ | |
| hdr "STEP 4 — FINAL CONSOLIDATED REPORT" | |
| # ============================================================ | |
| TOTAL_PASS=$((BE_PASS + FE_PASS + CL_PASS)) | |
| TOTAL_FAIL=$((BE_FAIL + FE_FAIL + CL_FAIL)) | |
| TOTAL_ALL=$((TOTAL_PASS + TOTAL_FAIL)) | |
| echo "" | |
| printf "%-20s | %-5s | %-6s | %-6s | %-16s\n" "LAYER" "TESTS" "PASSED" "FAILED" "RESULT" | |
| printf "%.0s-" {1..60}; echo | |
| printf "%-20s | %-5s | %-6s | %-6s | %-16s\n" \ | |
| "Backend API" "$((BE_PASS+BE_FAIL))" "$BE_PASS" "$BE_FAIL" "$([[ $BE_FAIL -eq 0 ]] && echo '✅ ALL PASS' || echo '❌ HAS FAILS')" | |
| printf "%-20s | %-5s | %-6s | %-6s | %-16s\n" \ | |
| "Frontend UI" "$((FE_PASS+FE_FAIL))" "$FE_PASS" "$FE_FAIL" "$([[ $FE_FAIL -eq 0 ]] && echo '✅ ALL PASS' || echo '❌ HAS FAILS')" | |
| printf "%-20s | %-5s | %-6s | %-6s | %-16s\n" \ | |
| "CLI Tool" "$((CL_PASS+CL_FAIL))" "$CL_PASS" "$CL_FAIL" "$([[ $CL_FAIL -eq 0 ]] && echo '✅ ALL PASS' || echo '❌ HAS FAILS')" | |
| printf "%.0s-" {1..60}; echo | |
| printf "%-20s | %-5s | %-6s | %-6s | %-16s\n" \ | |
| "TOTAL" "$TOTAL_ALL" "$TOTAL_PASS" "$TOTAL_FAIL" "" | |
| echo "" | |
| if [[ $TOTAL_FAIL -eq 0 ]]; then | |
| echo -e "${GREEN}${BOLD}🎉 VERDICT: READY FOR SUBMISSION — All $TOTAL_ALL tests PASSED (100%)${NC}" | |
| else | |
| echo -e "${RED}${BOLD}⚠️ VERDICT: NEEDS FIXES — $TOTAL_FAIL test(s) FAILED${NC}" | |
| fi | |
| echo "" | |
| echo -e "${BOLD}Server Status:${NC}" | |
| BE_UP=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/articles 2>/dev/null || echo "000") | |
| FE_UP=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5173/ 2>/dev/null || echo "000") | |
| [[ "$BE_UP" == "200" ]] && echo -e " 🟢 Backend http://localhost:8080 — RUNNING" \ | |
| || echo -e " 🔴 Backend http://localhost:8080 — DOWN ($BE_UP)" | |
| [[ "$FE_UP" == "200" ]] && echo -e " 🟢 Frontend http://localhost:5173 — RUNNING" \ | |
| || echo -e " 🔴 Frontend http://localhost:5173 — DOWN ($FE_UP)" | |
| echo "" | |
| echo -e "${BOLD}Screenshots saved to: ${REPORT_DIR}/${NC}" | |
| ls -lh "${REPORT_DIR}"/*_${TIMESTAMP}.png 2>/dev/null | awk '{print " "$5, $9}' || true | |
| echo "" | |
| echo "Completed: $(date)" | |
Xet Storage Details
- Size:
- 22.1 kB
- Xet hash:
- 1978320e684a7fec7130d83abdba492f85a0db7fc9971e90f1ca95a0236d8472
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.