#!/bin/bash # RFCP Frontend-Backend Integration Test # Tests API endpoints that frontend uses # Usage: ./rfcp-integration-test.sh [base_url] BASE_URL="${1:-https://api.rfcp.eliah.one}" PASSED=0 FAILED=0 # Colors GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " RFCP Integration Test (Frontend ↔ Backend)" echo " Target: $BASE_URL" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" test_endpoint() { local name="$1" local method="$2" local endpoint="$3" local expected="$4" local data="$5" if [ "$method" == "GET" ]; then response=$(curl -s -w "\n%{http_code}" "$BASE_URL$endpoint") else response=$(curl -s -w "\n%{http_code}" -X "$method" "$BASE_URL$endpoint" \ -H "Content-Type: application/json" \ -d "$data") fi http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') if [ "$http_code" == "200" ] && echo "$body" | grep -q "$expected"; then echo -e "${GREEN}✓${NC} $name" ((PASSED++)) return 0 else echo -e "${RED}✗${NC} $name (HTTP $http_code)" echo " Expected: $expected" echo " Got: ${body:0:100}..." ((FAILED++)) return 1 fi } # ═══════════════════════════════════════════════════ # Core API Tests # ═══════════════════════════════════════════════════ echo -e "${CYAN}Core API${NC}" echo "─────────────────────────────────────────────────" test_endpoint "Health check" "GET" "/api/health/" '"status":"ok"' test_endpoint "Database connection" "GET" "/api/health/db" '"database":"connected"' test_endpoint "API version" "GET" "/" '"version"' echo "" # ═══════════════════════════════════════════════════ # Project API (state persistence) # ═══════════════════════════════════════════════════ echo -e "${CYAN}Project API${NC}" echo "─────────────────────────────────────────────────" test_endpoint "Get project" "GET" "/api/projects/current" '"name":"global"' test_endpoint "Get settings" "GET" "/api/projects/current/settings" '"radius"' # Save test site test_endpoint "Save sites" "PUT" "/api/projects/current/sites" '"updated"' \ '[{"id":"integration-test","name":"Integration Test Site","lat":48.46,"lon":35.05,"height":30,"power":43}]' # Verify saved test_endpoint "Verify sites" "GET" "/api/projects/current/sites" "Integration Test Site" echo "" # ═══════════════════════════════════════════════════ # Coverage API (main functionality) # ═══════════════════════════════════════════════════ echo -e "${CYAN}Coverage API${NC}" echo "─────────────────────────────────────────────────" # Presets test_endpoint "Get presets" "GET" "/api/coverage/presets" '"fast"' # Coverage calculation echo -n "Coverage calculation (fast)... " start=$(date +%s) calc_response=$(curl -s -X POST "$BASE_URL/api/coverage/calculate" \ -H "Content-Type: application/json" \ -d '{ "sites": [{"lat": 48.46, "lon": 35.05, "height": 30, "power": 43, "gain": 15, "frequency": 1800}], "settings": {"radius": 1000, "resolution": 100, "preset": "fast"} }') end=$(date +%s) # Validate response structure has_points=$(echo "$calc_response" | jq 'has("points")') has_stats=$(echo "$calc_response" | jq 'has("stats")') has_time=$(echo "$calc_response" | jq 'has("computation_time")') has_models=$(echo "$calc_response" | jq 'has("models_used")') point_count=$(echo "$calc_response" | jq '.count') if [ "$has_points" == "true" ] && [ "$has_stats" == "true" ] && [ "$has_time" == "true" ] && [ "$has_models" == "true" ] && [ "$point_count" -gt 0 ]; then echo -e "${GREEN}✓${NC} Coverage calculation ($point_count points, $((end-start))s)" ((PASSED++)) else echo -e "${RED}✗${NC} Coverage calculation - missing fields or no points" ((FAILED++)) fi # Response structure check echo -n "Response structure... " min_rsrp=$(echo "$calc_response" | jq '.stats.min_rsrp') max_rsrp=$(echo "$calc_response" | jq '.stats.max_rsrp') models=$(echo "$calc_response" | jq -r '.models_used[]' | head -1) if [ "$min_rsrp" != "null" ] && [ "$max_rsrp" != "null" ] && [ -n "$models" ]; then echo -e "${GREEN}✓${NC} Response structure valid" echo " RSRP: $min_rsrp to $max_rsrp dBm" echo " Models: $(echo "$calc_response" | jq -r '.models_used | join(", ")')" ((PASSED++)) else echo -e "${RED}✗${NC} Invalid response structure" ((FAILED++)) fi # Buildings endpoint test_endpoint "Get buildings" "GET" "/api/coverage/buildings?min_lat=48.455&min_lon=35.045&max_lat=48.465&max_lon=35.055" '"count"' echo "" # ═══════════════════════════════════════════════════ # Terrain API # ═══════════════════════════════════════════════════ echo -e "${CYAN}Terrain API${NC}" echo "─────────────────────────────────────────────────" test_endpoint "Get elevation" "GET" "/api/terrain/elevation?lat=48.46&lon=35.05" '"elevation"' # Elevation profile echo -n "Elevation profile... " profile_response=$(curl -s "$BASE_URL/api/terrain/profile?lat1=48.46&lon1=35.05&lat2=48.47&lon2=35.06&points=10") profile_count=$(echo "$profile_response" | jq '.profile | length') if [ "$profile_count" -eq 10 ]; then echo -e "${GREEN}✓${NC} Elevation profile ($profile_count points)" ((PASSED++)) else echo -e "${RED}✗${NC} Elevation profile (expected 10, got $profile_count)" ((FAILED++)) fi # LoS check test_endpoint "Line of Sight" "GET" "/api/terrain/los?tx_lat=48.46&tx_lon=35.05&tx_height=30&rx_lat=48.47&rx_lon=35.06" '"has_los"' # Fresnel check test_endpoint "Fresnel clearance" "GET" "/api/terrain/fresnel?tx_lat=48.46&tx_lon=35.05&tx_height=30&rx_lat=48.47&rx_lon=35.06&rx_height=1.5&frequency=1800" '"clearance_percent"' echo "" # ═══════════════════════════════════════════════════ # Error Handling # ═══════════════════════════════════════════════════ echo -e "${CYAN}Error Handling${NC}" echo "─────────────────────────────────────────────────" # Invalid coordinates echo -n "Invalid coordinates... " error_response=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/terrain/elevation?lat=999&lon=999") if [ "$error_response" == "422" ] || [ "$error_response" == "400" ]; then echo -e "${GREEN}✓${NC} Returns error for invalid coords (HTTP $error_response)" ((PASSED++)) else echo -e "${YELLOW}⚠${NC} Unexpected response: HTTP $error_response" fi # Empty sites echo -n "Empty sites array... " empty_response=$(curl -s -X POST "$BASE_URL/api/coverage/calculate" \ -H "Content-Type: application/json" \ -d '{"sites": [], "settings": {"radius": 1000, "resolution": 100}}') empty_code=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE_URL/api/coverage/calculate" \ -H "Content-Type: application/json" \ -d '{"sites": [], "settings": {"radius": 1000, "resolution": 100}}') if [ "$empty_code" == "400" ]; then echo -e "${GREEN}✓${NC} Rejects empty sites (HTTP 400)" ((PASSED++)) else echo -e "${YELLOW}⚠${NC} Should reject empty sites (got HTTP $empty_code)" fi # Too large radius echo -n "Radius limit... " large_response=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE_URL/api/coverage/calculate" \ -H "Content-Type: application/json" \ -d '{"sites": [{"lat": 48.46, "lon": 35.05, "height": 30, "power": 43, "gain": 15, "frequency": 1800}], "settings": {"radius": 100000, "resolution": 100}}') if [ "$large_response" == "400" ]; then echo -e "${GREEN}✓${NC} Rejects >50km radius (HTTP 400)" ((PASSED++)) else echo -e "${YELLOW}⚠${NC} Should limit radius (got HTTP $large_response)" fi echo "" # ═══════════════════════════════════════════════════ # Performance Check # ═══════════════════════════════════════════════════ echo -e "${CYAN}Performance${NC}" echo "─────────────────────────────────────────────────" echo -n "Fast preset timing... " start=$(date +%s.%N) curl -s -X POST "$BASE_URL/api/coverage/calculate" \ -H "Content-Type: application/json" \ -d '{"sites": [{"lat": 48.46, "lon": 35.05, "height": 30, "power": 43, "gain": 15, "frequency": 1800}], "settings": {"radius": 2000, "resolution": 100, "preset": "fast"}}' > /dev/null end=$(date +%s.%N) duration=$(echo "$end - $start" | bc) if (( $(echo "$duration < 5" | bc -l) )); then echo -e "${GREEN}✓${NC} Fast preset: ${duration}s (< 5s)" ((PASSED++)) else echo -e "${YELLOW}⚠${NC} Fast preset slow: ${duration}s (expected < 5s)" fi echo "" # ═══════════════════════════════════════════════════ # Swagger/Docs # ═══════════════════════════════════════════════════ echo -e "${CYAN}Documentation${NC}" echo "─────────────────────────────────────────────────" swagger_code=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/docs") if [ "$swagger_code" == "200" ]; then echo -e "${GREEN}✓${NC} Swagger UI accessible" ((PASSED++)) else echo -e "${RED}✗${NC} Swagger UI (HTTP $swagger_code)" ((FAILED++)) fi test_endpoint "OpenAPI schema" "GET" "/openapi.json" '"openapi"' echo "" # ═══════════════════════════════════════════════════ # Summary # ═══════════════════════════════════════════════════ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" TOTAL=$((PASSED + FAILED)) if [ $FAILED -eq 0 ]; then echo -e " ${GREEN}All tests passed!${NC} ($PASSED/$TOTAL)" echo "" echo " Frontend can safely integrate with this backend." else echo -e " ${YELLOW}Results:${NC} ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}" echo "" echo " Fix failing tests before frontend integration." fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit $FAILED