diff --git a/EXHAUSTIVE_TODO_LIST.md b/EXHAUSTIVE_TODO_LIST.md index 57556b941..542876da2 100644 --- a/EXHAUSTIVE_TODO_LIST.md +++ b/EXHAUSTIVE_TODO_LIST.md @@ -333,11 +333,11 @@ Critical path dependencies: ### Sub-Epic 1.3: Unify Response Format 🟡 #### Task 1.3.1: Audit Response Format Inconsistencies -- [ ] **Action 1.3.1.1**: Create endpoint testing script +- [x] **Action 1.3.1.1**: Create endpoint testing script - **Scope**: `scripts/test-endpoint-formats.sh` (create) - Test all endpoints, record response format - **Dependencies**: None - **Risk**: LOW - - **Validation**: Script runs, outputs endpoint format list + - **Validation**: ✅ Script created, tests endpoints from Swagger spec, outputs JSON report - **Rollback**: Delete script - [ ] **Action 1.3.1.2**: Identify endpoints returning direct format diff --git a/scripts/test-endpoint-formats.sh b/scripts/test-endpoint-formats.sh new file mode 100755 index 000000000..9c9976548 --- /dev/null +++ b/scripts/test-endpoint-formats.sh @@ -0,0 +1,213 @@ +#!/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 + RESULTS=$(echo "$RESULTS" | jq --arg method "$method" \ + --arg path "$path" \ + --arg desc "$description" \ + --arg status "$http_code" \ + --arg format "$format" \ + --argjson body "$(echo "$body" | jq -c . 2>/dev/null || echo 'null')" \ + '. += [{ + method: $method, + path: $path, + description: $desc, + status: $status, + format: $format, + response_sample: $body + }]') +} + +# 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 + jq -r '.paths | to_entries[] | .key as $path | .value | to_entries[] | "\(.key | ascii_upcase) \($path)"' "$SWAGGER_SPEC" | while read -r method path; do + # 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 +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