#!/bin/bash # Test endpoint response formats to identify inconsistencies # Usage: ./scripts/test-endpoint-formats.sh [base_url] # Example: ./scripts/test-endpoint-formats.sh http://localhost:8080/api/v1 set -e # Colors for output GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration BASE_URL="${1:-http://localhost:8080/api/v1}" OUTPUT_FILE="${2:-endpoint-formats-report.json}" SWAGGER_SPEC="${3:-veza-backend-api/docs/swagger.json}" echo -e "${BLUE}🔍 Testing endpoint response formats...${NC}" echo -e "${BLUE}Base URL: ${BASE_URL}${NC}" echo -e "${BLUE}Output: ${OUTPUT_FILE}${NC}" echo "" # Check if jq is available if ! command -v jq &> /dev/null; then echo -e "${RED}❌ Error: jq is required but not installed${NC}" exit 1 fi # Check if swagger spec exists if [ ! -f "$SWAGGER_SPEC" ]; then echo -e "${YELLOW}⚠️ Warning: Swagger spec not found at $SWAGGER_SPEC${NC}" echo -e "${YELLOW} Will test endpoints manually${NC}" SWAGGER_SPEC="" fi # Initialize results array RESULTS="[]" # Function to test an endpoint test_endpoint() { local method=$1 local path=$2 local full_url="${BASE_URL}${path}" local description=$3 echo -e "${BLUE}Testing: ${method} ${path}${NC}" # Build curl command based on method local curl_cmd="curl -s -w '\n%{http_code}' -X ${method}" # Add headers curl_cmd="${curl_cmd} -H 'Content-Type: application/json'" curl_cmd="${curl_cmd} -H 'X-API-Version: v1'" # Add body for POST/PUT/PATCH if [[ "$method" == "POST" || "$method" == "PUT" || "$method" == "PATCH" ]]; then # Try to determine if auth is needed (skip auth endpoints) if [[ "$path" == *"/auth/login"* ]] || [[ "$path" == *"/auth/register"* ]]; then curl_cmd="${curl_cmd} -d '{}'" else # For other endpoints, skip if they require auth (will get 401) curl_cmd="${curl_cmd} -d '{}'" fi fi # Execute request local response=$(eval "$curl_cmd '${full_url}'" 2>/dev/null || echo "") if [ -z "$response" ]; then echo -e "${YELLOW} ⚠️ No response (endpoint may be down or require auth)${NC}" RESULTS=$(echo "$RESULTS" | jq --arg method "$method" \ --arg path "$path" \ --arg desc "$description" \ --arg status "no_response" \ --arg format "unknown" \ '. += [{ method: $method, path: $path, description: $desc, status: $status, format: $format, error: "No response received" }]') return fi # Extract HTTP status code (last line) local http_code=$(echo "$response" | tail -n1) local body=$(echo "$response" | sed '$d') # Determine response format local format="unknown" local has_success=$(echo "$body" | jq -r '.success // empty' 2>/dev/null || echo "") local has_data=$(echo "$body" | jq -r '.data // empty' 2>/dev/null || echo "") local has_error=$(echo "$body" | jq -r '.error // empty' 2>/dev/null || echo "") if [ "$http_code" == "200" ] || [ "$http_code" == "201" ]; then if [ -n "$has_success" ] && [ -n "$has_data" ]; then format="wrapped" echo -e "${GREEN} ✅ Wrapped format: { success, data }${NC}" elif [ -n "$has_data" ] && [ -z "$has_success" ]; then format="direct_data" echo -e "${YELLOW} ⚠️ Direct format: { data } (no success field)${NC}" else format="direct" echo -e "${YELLOW} ⚠️ Direct format: no wrapper${NC}" fi elif [ "$http_code" == "401" ] || [ "$http_code" == "403" ]; then format="auth_required" echo -e "${YELLOW} 🔒 Auth required (${http_code})${NC}" elif [ "$http_code" == "404" ]; then format="not_found" echo -e "${YELLOW} ❌ Not found (${http_code})${NC}" else format="error" echo -e "${YELLOW} ⚠️ Error (${http_code})${NC}" fi # Add to results (handle non-JSON responses) local body_json="null" if [ -n "$body" ]; then # Try to parse as JSON first if echo "$body" | jq . >/dev/null 2>&1; then body_json=$(echo "$body" | jq -c .) else # Not valid JSON, store as escaped string (truncate to 200 chars) body_json=$(echo "$body" | head -c 200 | jq -Rs .) fi fi # Use conditional jq based on whether body_json is null if [ "$body_json" = "null" ]; then RESULTS=$(echo "$RESULTS" | jq --arg method "$method" \ --arg path "$path" \ --arg desc "$description" \ --arg status "$http_code" \ --arg format "$format" \ '. += [{ method: $method, path: $path, description: $desc, status: $status, format: $format, response_sample: null }]') else RESULTS=$(echo "$RESULTS" | jq --arg method "$method" \ --arg path "$path" \ --arg desc "$description" \ --arg status "$http_code" \ --arg format "$format" \ --argjson body "$body_json" \ '. += [{ method: $method, path: $path, description: $desc, status: $status, format: $format, response_sample: $body }]') fi } # Extract endpoints from Swagger spec if available if [ -n "$SWAGGER_SPEC" ] && [ -f "$SWAGGER_SPEC" ]; then echo -e "${GREEN}📋 Reading endpoints from Swagger spec...${NC}" # Get all paths and store in temp file to avoid subshell issues TMP_ENDPOINTS=$(mktemp) jq -r '.paths | to_entries[] | .key as $path | .value | to_entries[] | "\(.key | ascii_upcase) \($path)"' "$SWAGGER_SPEC" > "$TMP_ENDPOINTS" # Process endpoints while IFS= read -r line; do method=$(echo "$line" | cut -d' ' -f1) path=$(echo "$line" | cut -d' ' -f2-) # Get description if available desc=$(jq -r ".paths[\"$path\"][\"${method,,}\"].summary // .paths[\"$path\"][\"${method,,}\"].description // \"\"" "$SWAGGER_SPEC" 2>/dev/null || echo "") # Skip if path requires path parameters (we can't test those easily) if [[ "$path" == *"{"* ]]; then echo -e "${YELLOW}Skipping ${method} ${path} (has path parameters)${NC}" continue fi test_endpoint "$method" "$path" "$desc" sleep 0.1 # Small delay to avoid overwhelming the server done < "$TMP_ENDPOINTS" rm -f "$TMP_ENDPOINTS" else echo -e "${YELLOW}⚠️ No Swagger spec, testing common endpoints manually...${NC}" # Test common endpoints manually test_endpoint "GET" "/health" "Health check" test_endpoint "GET" "/auth/me" "Get current user" test_endpoint "GET" "/tracks" "List tracks" test_endpoint "GET" "/playlists" "List playlists" test_endpoint "GET" "/users" "List users" fi # Generate summary echo "" echo -e "${BLUE}📊 Generating summary...${NC}" # Count formats wrapped_count=$(echo "$RESULTS" | jq '[.[] | select(.format == "wrapped")] | length') direct_count=$(echo "$RESULTS" | jq '[.[] | select(.format == "direct" or .format == "direct_data")] | length') auth_required=$(echo "$RESULTS" | jq '[.[] | select(.format == "auth_required")] | length') error_count=$(echo "$RESULTS" | jq '[.[] | select(.format == "error" or .format == "not_found")] | length') # Create summary SUMMARY=$(jq -n \ --argjson results "$RESULTS" \ --arg wrapped "$wrapped_count" \ --arg direct "$direct_count" \ --arg auth "$auth_required" \ --arg errors "$error_count" \ '{ summary: { total_tested: ($results | length), wrapped_format: ($wrapped | tonumber), direct_format: ($direct | tonumber), auth_required: ($auth | tonumber), errors: ($errors | tonumber) }, endpoints: $results }') # Save to file echo "$SUMMARY" | jq '.' > "$OUTPUT_FILE" echo "" echo -e "${GREEN}✅ Testing complete!${NC}" echo -e "${GREEN}Results saved to: ${OUTPUT_FILE}${NC}" echo "" echo -e "${BLUE}Summary:${NC}" echo -e " Wrapped format (✅): ${wrapped_count}" echo -e " Direct format (⚠️): ${direct_count}" echo -e " Auth required (🔒): ${auth_required}" echo -e " Errors (❌): ${error_count}" # List inconsistent endpoints if [ "$direct_count" -gt 0 ]; then echo "" echo -e "${YELLOW}⚠️ Endpoints with direct format (inconsistent):${NC}" echo "$RESULTS" | jq -r '.[] | select(.format == "direct" or .format == "direct_data") | " \(.method) \(.path)"' fi