Files
Portfolio/scripts/verify-e2e-rate-limiting.sh
T
damjan_savicandClaude Sonnet 4.5 1f1582f627 auto-claude: subtask-5-1 - E2E verification documentation and middleware fix
Created comprehensive E2E verification framework:
- E2E_VERIFICATION.md: Complete manual testing guide with 9 scenarios
- scripts/verify-e2e-rate-limiting.sh: Automated API testing script
- scripts/test-concurrent-rate-limit.sh: Concurrent request testing
- scripts/reset-rate-limit.sh: Database reset utility for testing
- SUBTASK_5-1_VERIFICATION_REPORT.md: Status and blocker documentation

Fixed middleware configuration:
- Updated src/middleware.ts matcher to exclude /api/ routes
- Changed from complex negative lookahead to explicit locale matching
- Pattern now: ['/', '/(de|en|sr)/:path*']

Known issue:
- Middleware fix requires dev server restart to take effect
- API routes currently return 404 until server is restarted
- All implementation code is complete and ready for testing

Test coverage:
- Basic rate limiting flow (5 requests succeed, 6th fails)
- Response header verification (X-RateLimit-Remaining, Retry-After)
- Persistence testing (across page refreshes, browser sessions)
- Multi-locale support (en, de, sr)
- Error handling and validation
- Concurrent request handling
- Database record verification

Next steps:
1. Restart dev server: npm run dev
2. Run automated tests: bash ./scripts/verify-e2e-rate-limiting.sh
3. Perform manual browser testing per E2E_VERIFICATION.md
4. Verify database records in Supabase
5. Mark subtask as completed

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 12:20:50 +01:00

220 lines
6.5 KiB
Bash

#!/bin/bash
# End-to-End Rate Limiting Verification Script
# This script tests the Supabase-based rate limiting implementation
set -e
echo "================================================"
echo "E2E Rate Limiting Verification"
echo "================================================"
echo ""
# Configuration
API_URL="http://localhost:3000/api/contact"
MAX_REQUESTS=5
EXPECTED_WINDOW_MS=3600000
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Test counters
TESTS_PASSED=0
TESTS_FAILED=0
# Helper function to print test result
print_result() {
local test_name=$1
local result=$2
if [ "$result" = "PASS" ]; then
echo -e "${GREEN}✅ PASS${NC}: $test_name"
((TESTS_PASSED++))
else
echo -e "${RED}❌ FAIL${NC}: $test_name"
((TESTS_FAILED++))
fi
}
# Helper function to make API request
make_request() {
local name=$1
local email=$2
local message=$3
curl -s -w "\n%{http_code}\n%{header_json}" -X POST "$API_URL" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$name\",\"email\":\"$email\",\"message\":\"$message\"}"
}
echo "Step 1: Check if dev server is running..."
if ! curl -s -f -o /dev/null "$API_URL" -X POST -H "Content-Type: application/json" -d '{}'; then
if [ $? -eq 7 ]; then
echo -e "${RED}❌ Dev server is not running!${NC}"
echo "Please start it with: npm run dev"
exit 1
fi
fi
echo -e "${GREEN}✅ Dev server is running${NC}"
echo ""
echo "Step 2: Testing basic rate limiting flow..."
echo "Sending $MAX_REQUESTS requests (should all succeed)..."
# Track response headers
declare -a status_codes
declare -a remaining_counts
for i in $(seq 1 $MAX_REQUESTS); do
echo -n "Request #$i... "
response=$(make_request "Test User $i" "test$i@example.com" "Test message $i")
# Extract HTTP status code
status_code=$(echo "$response" | tail -n 2 | head -n 1)
status_codes[$i]=$status_code
# Extract X-RateLimit-Remaining header
remaining=$(echo "$response" | grep -i "x-ratelimit-remaining" | grep -oP '\d+' | head -n 1 || echo "N/A")
remaining_counts[$i]=$remaining
if [ "$status_code" = "200" ]; then
echo -e "${GREEN}200 OK${NC} (Remaining: $remaining)"
else
echo -e "${RED}$status_code${NC} (Unexpected!)"
fi
sleep 0.5
done
echo ""
# Verify all requests succeeded
echo "Verifying first $MAX_REQUESTS requests..."
all_success=true
for i in $(seq 1 $MAX_REQUESTS); do
if [ "${status_codes[$i]}" != "200" ]; then
all_success=false
print_result "Request #$i should return 200" "FAIL"
fi
done
if [ "$all_success" = true ]; then
print_result "First $MAX_REQUESTS requests succeeded" "PASS"
fi
echo ""
# Verify remaining counts decrement
echo "Verifying rate limit counters..."
for i in $(seq 1 $((MAX_REQUESTS - 1))); do
current=${remaining_counts[$i]}
next=${remaining_counts[$((i + 1))]}
if [ "$current" != "N/A" ] && [ "$next" != "N/A" ]; then
expected=$((current - 1))
if [ "$next" -eq "$expected" ]; then
print_result "Counter decremented from $current to $next" "PASS"
else
print_result "Counter should decrement (expected $expected, got $next)" "FAIL"
fi
fi
done
echo ""
echo "Step 3: Testing rate limit enforcement..."
echo "Sending 6th request (should be rate limited)..."
response=$(make_request "Test User 6" "test6@example.com" "Test message 6")
status_code=$(echo "$response" | tail -n 2 | head -n 1)
body=$(echo "$response" | head -n -2)
echo "Status Code: $status_code"
echo "Response: $body"
echo ""
if [ "$status_code" = "429" ]; then
print_result "6th request returns 429 Too Many Requests" "PASS"
# Check for Retry-After header
retry_after=$(echo "$response" | grep -i "retry-after" | grep -oP '\d+' || echo "")
if [ -n "$retry_after" ]; then
print_result "Retry-After header present ($retry_after seconds)" "PASS"
else
print_result "Retry-After header should be present" "FAIL"
fi
# Check for X-RateLimit-Remaining: 0
remaining=$(echo "$response" | grep -i "x-ratelimit-remaining" | grep -oP '\d+' || echo "")
if [ "$remaining" = "0" ]; then
print_result "X-RateLimit-Remaining is 0" "PASS"
else
print_result "X-RateLimit-Remaining should be 0" "FAIL"
fi
# Check for error message in body
if echo "$body" | grep -q "too many\|rate limit"; then
print_result "Response body contains rate limit error message" "PASS"
else
print_result "Response body should contain rate limit error" "FAIL"
fi
else
print_result "6th request should return 429" "FAIL"
fi
echo ""
echo "Step 4: Testing rate limit persistence..."
echo "Sending another request (should still be rate limited)..."
response=$(make_request "Test User 7" "test7@example.com" "Test message 7")
status_code=$(echo "$response" | tail -n 2 | head -n 1)
if [ "$status_code" = "429" ]; then
print_result "Subsequent request still rate limited" "PASS"
else
print_result "Rate limiting should persist" "FAIL"
fi
echo ""
echo "Step 5: Testing validation (should not count against rate limit)..."
echo "Sending request with invalid data..."
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/json" \
-d '{"name":"","email":"invalid","message":""}')
status_code=$(echo "$response" | tail -n 1)
if [ "$status_code" = "400" ] || [ "$status_code" = "429" ]; then
print_result "Invalid data returns 400 or still rate limited (429)" "PASS"
else
print_result "Invalid data handling (got $status_code)" "FAIL"
fi
echo ""
echo "================================================"
echo "Test Summary"
echo "================================================"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
echo -e "${RED}Failed: $TESTS_FAILED${NC}"
echo ""
if [ $TESTS_FAILED -eq 0 ]; then
echo -e "${GREEN}✅ All tests passed!${NC}"
echo ""
echo "Next steps:"
echo "1. Verify database records in Supabase dashboard"
echo "2. Test browser UI at http://localhost:3000/en/contact"
echo "3. Test persistence across page refreshes"
echo "4. Complete manual verification scenarios in E2E_VERIFICATION.md"
exit 0
else
echo -e "${RED}❌ Some tests failed${NC}"
echo "Please review the failures above and check:"
echo "- Is the migration applied in Supabase?"
echo "- Are environment variables set correctly?"
echo "- Check server logs for errors"
exit 1
fi