From 1cc9bfd3a188da145af386f600b71fb5765233d6 Mon Sep 17 00:00:00 2001 From: mytec Date: Sat, 31 Jan 2026 01:43:29 +0200 Subject: [PATCH] @mytec: added scrr --- scripts/rfcp-integration-test.sh | 274 +++++++++++++++++++++++++++++++ scripts/rfcp-propagation-test.sh | 214 ++++++++++++++++++++++++ 2 files changed, 488 insertions(+) create mode 100644 scripts/rfcp-integration-test.sh create mode 100644 scripts/rfcp-propagation-test.sh diff --git a/scripts/rfcp-integration-test.sh b/scripts/rfcp-integration-test.sh new file mode 100644 index 0000000..64c9e6e --- /dev/null +++ b/scripts/rfcp-integration-test.sh @@ -0,0 +1,274 @@ +#!/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 diff --git a/scripts/rfcp-propagation-test.sh b/scripts/rfcp-propagation-test.sh new file mode 100644 index 0000000..ad4591f --- /dev/null +++ b/scripts/rfcp-propagation-test.sh @@ -0,0 +1,214 @@ +#!/bin/bash +# RFCP Propagation Models Test +# Tests all propagation presets and validates results +# Usage: ./rfcp-propagation-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' + +# Test site (центр Дніпра) +TEST_LAT=48.46 +TEST_LON=35.05 +TEST_HEIGHT=30 +TEST_POWER=43 +TEST_GAIN=15 +TEST_FREQ=1800 + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " RFCP Propagation Models Test" +echo " Target: $BASE_URL" +echo " Site: $TEST_LAT, $TEST_LON (Dnipro)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Helper function +test_preset() { + local preset="$1" + local radius="$2" + local resolution="$3" + local expected_models="$4" + local max_time="$5" + + echo -e "${CYAN}Testing preset: $preset${NC}" + echo " Radius: ${radius}m, Resolution: ${resolution}m" + + start_time=$(date +%s.%N) + + response=$(curl -s -X POST "$BASE_URL/api/coverage/calculate" \ + -H "Content-Type: application/json" \ + -d "{ + \"sites\": [{ + \"lat\": $TEST_LAT, + \"lon\": $TEST_LON, + \"height\": $TEST_HEIGHT, + \"power\": $TEST_POWER, + \"gain\": $TEST_GAIN, + \"frequency\": $TEST_FREQ + }], + \"settings\": { + \"radius\": $radius, + \"resolution\": $resolution, + \"preset\": \"$preset\" + } + }") + + end_time=$(date +%s.%N) + curl_time=$(echo "$end_time - $start_time" | bc) + + # Parse response + count=$(echo "$response" | jq -r '.count // 0') + comp_time=$(echo "$response" | jq -r '.computation_time // 0') + models=$(echo "$response" | jq -r '.models_used | join(", ") // "none"') + min_rsrp=$(echo "$response" | jq -r '.stats.min_rsrp // 0' | xargs printf "%.1f") + max_rsrp=$(echo "$response" | jq -r '.stats.max_rsrp // 0' | xargs printf "%.1f") + avg_rsrp=$(echo "$response" | jq -r '.stats.avg_rsrp // 0' | xargs printf "%.1f") + los_pct=$(echo "$response" | jq -r '.stats.los_percentage // 0' | xargs printf "%.1f") + terrain_pts=$(echo "$response" | jq -r '.stats.points_with_terrain_loss // 0') + building_pts=$(echo "$response" | jq -r '.stats.points_with_buildings // 0') + reflection_pts=$(echo "$response" | jq -r '.stats.points_with_reflection_gain // 0') + + # Validate + local errors=0 + + # Check count > 0 + if [ "$count" -eq 0 ]; then + echo -e " ${RED}✗ No points returned${NC}" + ((errors++)) + else + echo -e " ${GREEN}✓${NC} Points: $count" + fi + + # Check models + if [[ "$models" == *"$expected_models"* ]] || [ -z "$expected_models" ]; then + echo -e " ${GREEN}✓${NC} Models: $models" + else + echo -e " ${YELLOW}⚠${NC} Models: $models (expected: $expected_models)" + fi + + # Check computation time + if (( $(echo "$comp_time > $max_time" | bc -l) )); then + echo -e " ${YELLOW}⚠${NC} Time: ${comp_time}s (expected < ${max_time}s)" + else + echo -e " ${GREEN}✓${NC} Time: ${comp_time}s" + fi + + # Check RSRP range (sanity check) + if (( $(echo "$max_rsrp > 0" | bc -l) )); then + echo -e " ${RED}✗ Max RSRP > 0 dBm (impossible): $max_rsrp${NC}" + ((errors++)) + elif (( $(echo "$min_rsrp < -150" | bc -l) )); then + echo -e " ${RED}✗ Min RSRP < -150 dBm (too low): $min_rsrp${NC}" + ((errors++)) + else + echo -e " ${GREEN}✓${NC} RSRP range: $min_rsrp to $max_rsrp dBm (avg: $avg_rsrp)" + fi + + # Stats + echo " LoS: ${los_pct}% | Terrain: $terrain_pts | Buildings: $building_pts | Reflections: $reflection_pts" + + if [ $errors -eq 0 ]; then + echo -e " ${GREEN}PASSED${NC}" + ((PASSED++)) + else + echo -e " ${RED}FAILED${NC}" + ((FAILED++)) + fi + echo "" +} + +# Test presets endpoint +echo -e "${CYAN}Testing presets endpoint...${NC}" +presets_response=$(curl -s "$BASE_URL/api/coverage/presets") +presets_count=$(echo "$presets_response" | jq '.presets | keys | length') + +if [ "$presets_count" -eq 4 ]; then + echo -e "${GREEN}✓${NC} Presets endpoint: $presets_count presets found" + ((PASSED++)) +else + echo -e "${RED}✗${NC} Presets endpoint: expected 4, got $presets_count" + ((FAILED++)) +fi +echo "" + +# Test terrain endpoint +echo -e "${CYAN}Testing terrain elevation...${NC}" +elevation=$(curl -s "$BASE_URL/api/terrain/elevation?lat=$TEST_LAT&lon=$TEST_LON" | jq '.elevation') +if (( $(echo "$elevation > 0 && $elevation < 500" | bc -l) )); then + echo -e "${GREEN}✓${NC} Elevation at test site: ${elevation}m" + ((PASSED++)) +else + echo -e "${RED}✗${NC} Invalid elevation: $elevation" + ((FAILED++)) +fi +echo "" + +# Test each preset +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Preset Tests" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Fast - 2km radius, should be quick +test_preset "fast" 2000 100 "terrain_los" 10 + +# Standard - 1km radius with buildings +test_preset "standard" 1000 100 "materials" 60 + +# Detailed - smaller radius, dominant path +test_preset "detailed" 500 100 "dominant_path" 120 + +# Full - smallest radius, all models +test_preset "full" 300 50 "reflections" 300 + +# Test buildings endpoint +echo -e "${CYAN}Testing buildings endpoint...${NC}" +buildings_count=$(curl -s "$BASE_URL/api/coverage/buildings?min_lat=48.455&min_lon=35.045&max_lat=48.465&max_lon=35.055" | jq '.count') +if [ "$buildings_count" -gt 0 ]; then + echo -e "${GREEN}✓${NC} Buildings in test area: $buildings_count" + ((PASSED++)) +else + echo -e "${YELLOW}⚠${NC} No buildings found (may be cached or OSM issue)" +fi +echo "" + +# Multi-site test +echo -e "${CYAN}Testing multi-site coverage...${NC}" +multi_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}, + {"lat": 48.47, "lon": 35.06, "height": 25, "power": 40, "gain": 12, "frequency": 1800} + ], + "settings": {"radius": 1000, "resolution": 100, "preset": "fast"} + }') + +multi_count=$(echo "$multi_response" | jq '.count') +if [ "$multi_count" -gt 0 ]; then + echo -e "${GREEN}✓${NC} Multi-site coverage: $multi_count points" + ((PASSED++)) +else + echo -e "${RED}✗${NC} Multi-site coverage failed" + ((FAILED++)) +fi +echo "" + +# Summary +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +TOTAL=$((PASSED + FAILED)) +if [ $FAILED -eq 0 ]; then + echo -e " ${GREEN}All tests passed!${NC} ($PASSED/$TOTAL)" +else + echo -e " ${YELLOW}Results:${NC} ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}" +fi +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +exit $FAILED