""" Test script for the RoadTripService implementation. This script tests all functionality of the OSM Road Trip Service including: - Geocoding addresses - Route calculation - Park discovery along routes - Multi-park trip planning - Integration with existing Park models """ from django.core.cache import cache from parks.models import Park from parks.services.roadtrip import Coordinates from parks.services import RoadTripService import os import django # Setup Django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "thrillwiki.settings") django.setup() def test_geocoding(): """Test geocoding functionality.""" print("\n=== Testing Geocoding ===") service = RoadTripService() # Test various address formats test_addresses = [ "Cedar Point, Sandusky, Ohio", "Magic Kingdom, Orlando, Florida", "Disneyland, Anaheim, California", "Six Flags Great Adventure, Jackson, New Jersey", "Invalid Address That Should Not Work 123456789", ] for address in test_addresses: print(f"\nGeocoding: {address}") coords = service.geocode_address(address) if coords: print( f" ✅ Success: { coords.latitude:.6f}, { coords.longitude:.6f}" ) else: print(f" ❌ Failed") # Test cache functionality print(f"\nTesting cache...") coords1 = service.geocode_address("Cedar Point, Sandusky, Ohio") coords2 = service.geocode_address("Cedar Point, Sandusky, Ohio") if coords1 and coords2: print(f" ✅ Cache working: {coords1.latitude == coords2.latitude}") def test_route_calculation(): """Test route calculation between coordinates.""" print("\n=== Testing Route Calculation ===") service = RoadTripService() # Cedar Point to Magic Kingdom (long distance) cedar_point = Coordinates(41.4793, -82.6833) magic_kingdom = Coordinates(28.4177, -81.5812) print(f"Calculating route from Cedar Point to Magic Kingdom...") route = service.calculate_route(cedar_point, magic_kingdom) if route: print(f" ✅ Success:") print(f" Distance: {route.formatted_distance}") print(f" Duration: {route.formatted_duration}") print(f" Geometry: {'Yes' if route.geometry else 'No'}") else: print(f" ❌ Failed") # Test short distance (should use OSRM) disneyland = Coordinates(33.8121, -117.9190) knotts = Coordinates(33.8442, -118.0000) print(f"\nCalculating route from Disneyland to Knott's Berry Farm...") route = service.calculate_route(disneyland, knotts) if route: print(f" ✅ Success:") print(f" Distance: {route.formatted_distance}") print(f" Duration: {route.formatted_duration}") else: print(f" ❌ Failed") def test_park_integration(): """Test integration with Park models.""" print("\n=== Testing Park Integration ===") service = RoadTripService() # Get some parks from the database parks = Park.objects.select_related("location").all()[:5] if not parks: print(" ⚠️ No parks found in database") return print(f"Found {len(parks)} parks to test with:") for park in parks: print(f" - {park.name}") if hasattr(park, "location") and park.location: coords = park.coordinates if coords: print(f" 📍 {coords[0]:.4f}, {coords[1]:.4f}") else: print(f" 📍 No coordinates, will try to geocode...") success = service.geocode_park_if_needed(park) if success: coords = park.coordinates print( f" ✅ Geocoded to: { coords[0]:.4f}, { coords[1]:.4f}" ) else: print(f" ❌ Geocoding failed") else: print(f" ❌ No location data") def test_nearby_parks(): """Test finding nearby parks.""" print("\n=== Testing Nearby Park Discovery ===") service = RoadTripService() # Get a park with location data parks_with_location = Park.objects.filter( location__point__isnull=False ).select_related("location")[:1] if not parks_with_location: print(" ⚠️ No parks with location data found") return center_park = parks_with_location[0] print(f"Finding parks within 200km of {center_park.name}...") nearby_parks = service.get_park_distances(center_park, radius_km=200) if nearby_parks: print(f" ✅ Found {len(nearby_parks)} nearby parks:") for result in nearby_parks[:5]: # Show first 5 park = result["park"] print( f" { park.name}: { result['formatted_distance']}, { result['formatted_duration']}" ) else: print(f" ❌ No nearby parks found") def test_route_park_discovery(): """Test finding parks along a route.""" print("\n=== Testing Parks Along Route ===") service = RoadTripService() # Get two parks with location data parks_with_location = Park.objects.filter( location__point__isnull=False ).select_related("location")[:2] if len(parks_with_location) < 2: print(" ⚠️ Need at least 2 parks with location data") return start_park = parks_with_location[0] end_park = parks_with_location[1] print( f"Finding parks along route from { start_park.name} to { end_park.name}..." ) parks_along_route = service.find_parks_along_route( start_park, end_park, max_detour_km=100 ) if parks_along_route: print(f" ✅ Found {len(parks_along_route)} parks along route:") for park in parks_along_route[:3]: # Show first 3 print(f" - {park.name}") else: print(f" ❌ No parks found along route") def test_multi_park_trip(): """Test multi-park trip planning.""" print("\n=== Testing Multi-Park Trip Planning ===") service = RoadTripService() # Get parks with location data parks_with_location = Park.objects.filter( location__point__isnull=False ).select_related("location")[:4] if len(parks_with_location) < 3: print(" ⚠️ Need at least 3 parks with location data") return parks_list = list(parks_with_location) print(f"Planning trip for {len(parks_list)} parks:") for park in parks_list: print(f" - {park.name}") trip = service.create_multi_park_trip(parks_list) if trip: print(f" ✅ Trip planned successfully:") print(f" Total Distance: {trip.formatted_total_distance}") print(f" Total Duration: {trip.formatted_total_duration}") print(f" Route:") for i, leg in enumerate(trip.legs, 1): print(f" {i}. {leg.from_park.name} → {leg.to_park.name}") print( f" { leg.route.formatted_distance}, { leg.route.formatted_duration}" ) else: print(f" ❌ Trip planning failed") def test_error_handling(): """Test error handling and edge cases.""" print("\n=== Testing Error Handling ===") service = RoadTripService() # Test with invalid coordinates print("Testing invalid coordinates...") invalid_coords = Coordinates(999, 999) valid_coords = Coordinates(40.0, -80.0) route = service.calculate_route(invalid_coords, valid_coords) if route: print( f" ⚠️ Got route with invalid coords: { route.formatted_distance}" ) else: print(f" ✅ Correctly handled invalid coordinates") # Test with empty address print("Testing empty address geocoding...") coords = service.geocode_address("") if coords: print(f" ⚠️ Got coordinates for empty address") else: print(f" ✅ Correctly handled empty address") # Test with None values print("Testing None coordinates...") route = service.calculate_route(None, valid_coords) if route: print(f" ⚠️ Got route with None coordinates") else: print(f" ✅ Correctly handled None coordinates") def test_rate_limiting(): """Test rate limiting functionality.""" print("\n=== Testing Rate Limiting ===") service = RoadTripService() print("Making multiple rapid requests to test rate limiting...") import time start_time = time.time() # Make 3 rapid geocoding requests addresses = [ "Disney World, Orlando, FL", "Universal Studios, Orlando, FL", "SeaWorld, Orlando, FL", ] for address in addresses: coords = service.geocode_address(address) if coords: print( f" ✅ {address}: { coords.latitude:.4f}, { coords.longitude:.4f}" ) elapsed = time.time() - start_time print(f" Total time for 3 requests: {elapsed:.2f} seconds") if elapsed >= 2.0: # Should take at least 2 seconds with 1 req/sec limit print(f" ✅ Rate limiting appears to be working") else: print(f" ⚠️ Requests may have been cached or rate limiting not working") def main(): """Run all tests.""" print("🚗 ThrillWiki Road Trip Service Test Suite") print("=" * 50) # Clear cache to ensure fresh tests cache.clear() try: test_geocoding() test_route_calculation() test_park_integration() test_nearby_parks() test_route_park_discovery() test_multi_park_trip() test_error_handling() test_rate_limiting() print("\n" + "=" * 50) print("🎉 Test suite completed!") print("\nNote: Some tests may show failures if:") print("- No park data exists in the database") print("- Network connectivity issues") print("- OSM API rate limits exceeded") print("- Parks don't have location data") except Exception as e: print(f"\n❌ Test suite failed with error: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()