""" Test script for Phase 5 features Tests social features (follows, channel messages) and event system """ import asyncio import httpx import json from datetime import datetime BASE_URL = "http://localhost:8000/api" # Test user credentials (assuming these exist from previous tests) TEST_USER = { "username": "testuser", "password": "testpass123" } async def get_auth_token(): """Login and get JWT token""" async with httpx.AsyncClient(timeout=30.0, proxies={}) as client: # Try to register first (in case user doesn't exist) register_response = await client.post( f"{BASE_URL}/auth/register", json={ "username": TEST_USER["username"], "password": TEST_USER["password"], "email": "test@example.com" } ) # If register fails (user exists), try to login if register_response.status_code != 200: response = await client.post( f"{BASE_URL}/auth/login", json={ "username": TEST_USER["username"], "password": TEST_USER["password"] } ) else: response = register_response if response.status_code == 200: data = response.json() return data.get("access_token") else: print(f"Login failed: {response.status_code} - {response.text}") return None async def test_follow_operations(token): """Test user follow operations""" print("\n=== Testing Follow Operations ===") headers = {"Authorization": f"Bearer {token}"} async with httpx.AsyncClient(timeout=30.0, proxies={}) as client: # Test: Follow a celestial body (Mars) print("\n1. Following Mars (499)...") response = await client.post( f"{BASE_URL}/social/follow/499", headers=headers ) print(f"Status: {response.status_code}") if response.status_code in [200, 400]: # 400 if already following print(f"Response: {response.json()}") # Test: Get user's follows print("\n2. Getting user follows...") response = await client.get( f"{BASE_URL}/social/follows", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: follows = response.json() print(f"Following {len(follows)} bodies:") for follow in follows[:5]: # Show first 5 print(f" - Body ID: {follow['body_id']}, Since: {follow['created_at']}") # Test: Check if following Mars print("\n3. Checking if following Mars...") response = await client.get( f"{BASE_URL}/social/follows/check/499", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: print(f"Response: {response.json()}") return response.status_code == 200 async def test_channel_messages(token): """Test channel message operations""" print("\n=== Testing Channel Messages ===") headers = {"Authorization": f"Bearer {token}"} async with httpx.AsyncClient(timeout=30.0, proxies={}) as client: # Test: Post a message to Mars channel print("\n1. Posting message to Mars channel...") message_data = { "content": f"Test message at {datetime.now().isoformat()}" } response = await client.post( f"{BASE_URL}/social/channel/499/message", headers=headers, json=message_data ) print(f"Status: {response.status_code}") if response.status_code == 200: print(f"Response: {response.json()}") elif response.status_code == 403: print("Error: User is not following this body (need to follow first)") # Test: Get channel messages print("\n2. Getting Mars channel messages...") response = await client.get( f"{BASE_URL}/social/channel/499/messages?limit=10", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: messages = response.json() print(f"Found {len(messages)} messages:") for msg in messages[-3:]: # Show last 3 print(f" - {msg['username']}: {msg['content'][:50]}...") return response.status_code == 200 async def test_celestial_events(token): """Test celestial event operations""" print("\n=== Testing Celestial Events ===") headers = {"Authorization": f"Bearer {token}"} async with httpx.AsyncClient(timeout=30.0, proxies={}) as client: # Test: Get upcoming events print("\n1. Getting upcoming celestial events...") response = await client.get( f"{BASE_URL}/events?limit=10", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: events = response.json() print(f"Found {len(events)} events:") for event in events[:5]: # Show first 5 print(f" - {event['title']} at {event['event_time']}") print(f" Type: {event['event_type']}, Source: {event['source']}") # Test: Get events for a specific body print("\n2. Getting events for Mars (499)...") response = await client.get( f"{BASE_URL}/events?body_id=499&limit=5", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: events = response.json() print(f"Found {len(events)} events for Mars") return response.status_code == 200 async def test_scheduled_tasks(token): """Test scheduled task functionality""" print("\n=== Testing Scheduled Tasks ===") headers = {"Authorization": f"Bearer {token}"} async with httpx.AsyncClient(timeout=120.0, proxies={}) as client: # Test: Get available tasks print("\n1. Getting available scheduled tasks...") response = await client.get( f"{BASE_URL}/scheduled-jobs/available-tasks", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: tasks = response.json() print(f"Found {len(tasks)} available tasks") # Find our Phase 5 task phase5_task = None for task in tasks: if task['name'] == 'fetch_close_approach_events': phase5_task = task print(f"\nFound Phase 5 task: {task['name']}") print(f" Description: {task['description']}") print(f" Category: {task['category']}") break if phase5_task: # Test: Create a scheduled job for this task print("\n2. Creating a scheduled job for fetch_close_approach_events...") job_data = { "name": "Test Phase 5 Close Approach Events", "job_type": "predefined", "predefined_function": "fetch_close_approach_events", "function_params": { "days_ahead": 30, "dist_max": "0.2", "approach_body": "Earth", "limit": 50, "clean_old_events": False }, "cron_expression": "0 0 * * *", # Daily at midnight "description": "Test job for Phase 5", "is_active": False # Don't activate for test } response = await client.post( f"{BASE_URL}/scheduled-jobs", headers=headers, json=job_data ) print(f"Status: {response.status_code}") if response.status_code == 201: job = response.json() job_id = job['id'] print(f"Created job with ID: {job_id}") # Test: Run the job immediately print(f"\n3. Triggering job {job_id} to run now...") print(" (This may take 30-60 seconds...)") response = await client.post( f"{BASE_URL}/scheduled-jobs/{job_id}/run", headers=headers ) print(f"Status: {response.status_code}") if response.status_code == 200: print(f"Response: {response.json()}") # Wait a bit and check job status print("\n4. Waiting 60 seconds for job to complete...") await asyncio.sleep(60) # Get job status response = await client.get( f"{BASE_URL}/scheduled-jobs/{job_id}", headers=headers ) if response.status_code == 200: job_status = response.json() print(f"Job status: {job_status.get('last_run_status')}") print(f"Last run at: {job_status.get('last_run_at')}") # Check if events were created response = await client.get( f"{BASE_URL}/events?limit=10", headers=headers ) if response.status_code == 200: events = response.json() print(f"\nEvents in database: {len(events)}") for event in events[:3]: print(f" - {event['title']}") # Clean up: delete the test job await client.delete( f"{BASE_URL}/scheduled-jobs/{job_id}", headers=headers ) print(f"\nCleaned up test job {job_id}") return True else: print(f"Error triggering job: {response.text}") else: print(f"Error creating job: {response.text}") return False async def main(): """Main test function""" print("=" * 60) print("Phase 5 Feature Testing") print("=" * 60) # Get authentication token print("\nAuthenticating...") token = await get_auth_token() if not token: print("ERROR: Failed to authenticate. Please ensure test user exists.") print("You may need to create a test user first.") return print(f"✓ Authentication successful") # Run tests results = { "follow_operations": await test_follow_operations(token), "channel_messages": await test_channel_messages(token), "celestial_events": await test_celestial_events(token), "scheduled_tasks": await test_scheduled_tasks(token) } # Summary print("\n" + "=" * 60) print("Test Summary") print("=" * 60) for test_name, passed in results.items(): status = "✓ PASS" if passed else "✗ FAIL" print(f"{status} - {test_name}") total_passed = sum(results.values()) total_tests = len(results) print(f"\nTotal: {total_passed}/{total_tests} tests passed") if __name__ == "__main__": asyncio.run(main())