- Add tests/ directory with 12 bash/curl/jq test stages covering all API endpoints - Add tests/lib/common.sh shared library with assertions and helpers - Add tests/run-all.sh orchestrator script - Update CLAUDE.md with test data reference and DevTools MCP docs - Increase dev rate limit to 5000 for test suite runs - Configure Chrome DevTools MCP in project settings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
134 lines
4.0 KiB
Bash
134 lines
4.0 KiB
Bash
#!/usr/bin/env bash
|
|
# tests/lib/common.sh — Shared testing utilities
|
|
|
|
BASE_URL="${BASE_URL:-http://localhost:3000}"
|
|
CLIENT_URL="${CLIENT_URL:-http://localhost:5173}"
|
|
PASS_COUNT=0
|
|
FAIL_COUNT=0
|
|
SKIP_COUNT=0
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
# --- HTTP helpers ---
|
|
api_call() {
|
|
local method="$1" path="$2" token="$3" data="$4"
|
|
local args=(-s -w '\n%{http_code}' -X "$method")
|
|
[[ -n "$token" ]] && args+=(-H "Authorization: Bearer $token")
|
|
[[ -n "$data" ]] && args+=(-H "Content-Type: application/json" -d "$data")
|
|
curl "${args[@]}" "${BASE_URL}${path}" 2>/dev/null
|
|
}
|
|
|
|
get_body() { echo "$1" | sed '$ d'; }
|
|
get_status() { echo "$1" | tail -n1; }
|
|
|
|
# --- Login helper (returns token, retries on rate limit) ---
|
|
login_as() {
|
|
local email="$1" password="${2:-password123}"
|
|
local result body status attempt
|
|
for attempt in 1 2 3 4 5; do
|
|
result=$(api_call POST "/api/auth/login" "" "{\"email\":\"$email\",\"password\":\"$password\"}")
|
|
status=$(get_status "$result")
|
|
if [[ "$status" == "429" || "$status" == "500" || "$status" == "503" ]]; then
|
|
sleep "$attempt"
|
|
continue
|
|
fi
|
|
body=$(get_body "$result")
|
|
echo "$body" | jq -r '.accessToken // empty' 2>/dev/null
|
|
return
|
|
done
|
|
echo ""
|
|
}
|
|
|
|
# --- Assertions ---
|
|
assert_status() {
|
|
local name="$1" expected="$2" actual="$3"
|
|
if [[ "$expected" == "$actual" ]]; then
|
|
printf " ${GREEN}PASS${NC} %s (HTTP %s)\n" "$name" "$actual"
|
|
((PASS_COUNT++))
|
|
else
|
|
printf " ${RED}FAIL${NC} %s (expected %s, got %s)\n" "$name" "$expected" "$actual"
|
|
((FAIL_COUNT++))
|
|
fi
|
|
}
|
|
|
|
assert_json() {
|
|
local name="$1" body="$2" field="$3" expected="$4"
|
|
local actual
|
|
actual=$(echo "$body" | jq -r "$field" 2>/dev/null)
|
|
if [[ "$actual" == "$expected" ]]; then
|
|
printf " ${GREEN}PASS${NC} %s (%s = %s)\n" "$name" "$field" "$actual"
|
|
((PASS_COUNT++))
|
|
else
|
|
printf " ${RED}FAIL${NC} %s (%s: expected '%s', got '%s')\n" "$name" "$field" "$expected" "$actual"
|
|
((FAIL_COUNT++))
|
|
fi
|
|
}
|
|
|
|
assert_json_exists() {
|
|
local name="$1" body="$2" field="$3"
|
|
local actual
|
|
actual=$(echo "$body" | jq -r "$field" 2>/dev/null)
|
|
if [[ -n "$actual" && "$actual" != "null" ]]; then
|
|
printf " ${GREEN}PASS${NC} %s (%s exists)\n" "$name" "$field"
|
|
((PASS_COUNT++))
|
|
else
|
|
printf " ${RED}FAIL${NC} %s (%s is null/missing)\n" "$name" "$field"
|
|
((FAIL_COUNT++))
|
|
fi
|
|
}
|
|
|
|
assert_json_array_min() {
|
|
local name="$1" body="$2" field="$3" min="${4:-1}"
|
|
local length
|
|
length=$(echo "$body" | jq "$field | length" 2>/dev/null)
|
|
if [[ "$length" -ge "$min" ]] 2>/dev/null; then
|
|
printf " ${GREEN}PASS${NC} %s (%s items)\n" "$name" "$length"
|
|
((PASS_COUNT++))
|
|
else
|
|
printf " ${RED}FAIL${NC} %s (expected >= %s items, got %s)\n" "$name" "$min" "$length"
|
|
((FAIL_COUNT++))
|
|
fi
|
|
}
|
|
|
|
assert_contains() {
|
|
local name="$1" body="$2" substring="$3"
|
|
if echo "$body" | grep -q "$substring"; then
|
|
printf " ${GREEN}PASS${NC} %s (contains '%s')\n" "$name" "$substring"
|
|
((PASS_COUNT++))
|
|
else
|
|
printf " ${RED}FAIL${NC} %s (missing '%s')\n" "$name" "$substring"
|
|
((FAIL_COUNT++))
|
|
fi
|
|
}
|
|
|
|
skip_test() {
|
|
local name="$1" reason="$2"
|
|
printf " ${YELLOW}SKIP${NC} %s (%s)\n" "$name" "$reason"
|
|
((SKIP_COUNT++))
|
|
}
|
|
|
|
# --- Stage header/summary ---
|
|
stage_header() {
|
|
echo ""
|
|
printf "${CYAN}${BOLD}════════════════════════════════════════${NC}\n"
|
|
printf "${CYAN}${BOLD} %s${NC}\n" "$1"
|
|
printf "${CYAN}${BOLD}════════════════════════════════════════${NC}\n"
|
|
}
|
|
|
|
print_summary() {
|
|
local name="$1"
|
|
echo ""
|
|
printf "${BOLD}--- %s Results ---${NC}\n" "$name"
|
|
printf " ${GREEN}Passed: %d${NC}\n" "$PASS_COUNT"
|
|
[[ $FAIL_COUNT -gt 0 ]] && printf " ${RED}Failed: %d${NC}\n" "$FAIL_COUNT"
|
|
[[ $SKIP_COUNT -gt 0 ]] && printf " ${YELLOW}Skipped: %d${NC}\n" "$SKIP_COUNT"
|
|
printf " Total: %d\n" "$((PASS_COUNT + FAIL_COUNT + SKIP_COUNT))"
|
|
[[ $FAIL_COUNT -gt 0 ]] && return 1
|
|
return 0
|
|
}
|