feat: QA test suite (12 stages, 205 tests) + Chrome DevTools MCP config
- 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>
This commit is contained in:
133
tests/lib/common.sh
Normal file
133
tests/lib/common.sh
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/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
|
||||
}
|
||||
Reference in New Issue
Block a user