#!/usr/bin/env bash # ============================================================ # test_helpers.sh — Shared test utilities # Source this in any test script: source shared/lib/test_helpers.sh # ============================================================ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' PASS_COUNT=0 FAIL_COUNT=0 SKIP_COUNT=0 TEST_RESULTS=() JSON_MODE=false # Parse --json flag for arg in "$@"; do [ "$arg" = "--json" ] && JSON_MODE=true done pass() { local msg="$1" ((PASS_COUNT++)) TEST_RESULTS+=("{\"status\":\"pass\",\"test\":\"$msg\"}") $JSON_MODE || echo -e " ${GREEN}✅${NC} $msg" } fail() { local msg="$1" local detail="${2:-}" ((FAIL_COUNT++)) TEST_RESULTS+=("{\"status\":\"fail\",\"test\":\"$msg\",\"detail\":\"$detail\"}") $JSON_MODE || echo -e " ${RED}❌${NC} $msg${detail:+ — $detail}" } skip() { local msg="$1" local reason="${2:-}" ((SKIP_COUNT++)) TEST_RESULTS+=("{\"status\":\"skip\",\"test\":\"$msg\",\"reason\":\"$reason\"}") $JSON_MODE || echo -e " ${YELLOW}⏭️${NC} $msg${reason:+ — $reason}" } section() { $JSON_MODE || echo -e "\n${CYAN}━━━ $1 ━━━${NC}" } # HTTP helper: returns "status_code|body|time_ms" http_get() { local url="$1" local tmpfile=$(mktemp) local http_code time_total body http_code=$(curl -sk -o "$tmpfile" -w "%{http_code}" "$url" 2>/dev/null) time_total=$(curl -sk -o /dev/null -w "%{time_total}" "$url" 2>/dev/null) body=$(cat "$tmpfile") rm -f "$tmpfile" local ms=$(echo "$time_total * 1000" | bc 2>/dev/null | cut -d'.' -f1) echo "${http_code}|${body}|${ms:-0}" } http_post() { local url="$1" local data="${2:-{}}" local tmpfile=$(mktemp) local http_code http_code=$(curl -sk -X POST -o "$tmpfile" -w "%{http_code}" \ -H "Content-Type: application/json" -d "$data" "$url" 2>/dev/null) local body=$(cat "$tmpfile") rm -f "$tmpfile" echo "${http_code}|${body}" } # JSON field extractor (requires python3) json_field() { local json="$1" local field="$2" echo "$json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$field',''))" 2>/dev/null } is_valid_json() { echo "$1" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null } # Print summary and exit with appropriate code test_summary() { local total=$((PASS_COUNT + FAIL_COUNT + SKIP_COUNT)) if $JSON_MODE; then echo "{\"total\":$total,\"pass\":$PASS_COUNT,\"fail\":$FAIL_COUNT,\"skip\":$SKIP_COUNT,\"results\":[$(IFS=,; echo "${TEST_RESULTS[*]}")]}" else echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo -e " TOTAL: $total | ${GREEN}PASS: $PASS_COUNT${NC} | ${RED}FAIL: $FAIL_COUNT${NC} | ${YELLOW}SKIP: $SKIP_COUNT${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" fi [ "$FAIL_COUNT" -eq 0 ] && return 0 || return 1 }