Initial commit: Complete DevServer monitoring framework
- FastAPI application with OpenTelemetry instrumentation - Redis caching and Celery task processing - Docker Compose setup with monitoring stack - Grafana dashboards and Prometheus metrics - Jaeger distributed tracing - Comprehensive documentation in docs/ folder - Monitoring configuration and troubleshooting guides
This commit is contained in:
275
test_all_apis.py
Normal file
275
test_all_apis.py
Normal file
@@ -0,0 +1,275 @@
|
||||
"""
|
||||
Complete API Test Suite
|
||||
Tests all endpoints including Redis operations
|
||||
"""
|
||||
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
BASE_URL = "http://localhost:8000"
|
||||
|
||||
print("=" * 70)
|
||||
print("DevServer API - Complete Test Suite")
|
||||
print("=" * 70)
|
||||
print(f"Base URL: {BASE_URL}")
|
||||
print(f"Started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("=" * 70)
|
||||
|
||||
def test_api_info():
|
||||
"""Test API information endpoint"""
|
||||
print("\n1. API Information")
|
||||
print("-" * 70)
|
||||
response = requests.get(f"{BASE_URL}/")
|
||||
data = response.json()
|
||||
print(f" Service: {data['service']}")
|
||||
print(f" Version: {data['version']}")
|
||||
print(f" Endpoints: {len(data['endpoints'])} categories")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_health():
|
||||
"""Test health check"""
|
||||
print("\n2. Health Check")
|
||||
print("-" * 70)
|
||||
response = requests.get(f"{BASE_URL}/health")
|
||||
data = response.json()
|
||||
print(f" Status: {data['status']}")
|
||||
print(f" Redis: {data['redis']}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_dice_roll():
|
||||
"""Test dice rolling"""
|
||||
print("\n3. Dice Roll (FastAPI + Tracing)")
|
||||
print("-" * 70)
|
||||
start = time.time()
|
||||
response = requests.get(f"{BASE_URL}/api/dice/roll")
|
||||
elapsed = time.time() - start
|
||||
data = response.json()
|
||||
print(f" Total rolls: {data['total']}")
|
||||
print(f" Service: {data['service']}")
|
||||
print(f" Duration: {elapsed:.3f}s")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_redis_single():
|
||||
"""Test single Redis operations"""
|
||||
print("\n4. Redis Single Operations")
|
||||
print("-" * 70)
|
||||
|
||||
# Set
|
||||
key = f"test_{int(time.time())}"
|
||||
value = f"hello_world_{int(time.time())}"
|
||||
response = requests.post(f"{BASE_URL}/api/cache/set", params={
|
||||
"key": key,
|
||||
"value": value,
|
||||
"ttl": 300
|
||||
})
|
||||
print(f" SET {key} = {value}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
# Get
|
||||
response = requests.get(f"{BASE_URL}/api/cache/{key}")
|
||||
data = response.json()
|
||||
print(f" GET {key}")
|
||||
print(f" Value: {data['value']}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
# Delete
|
||||
response = requests.delete(f"{BASE_URL}/api/cache/{key}")
|
||||
data = response.json()
|
||||
print(f" DELETE {key}")
|
||||
print(f" Deleted: {data['deleted']}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_redis_batch():
|
||||
"""Test batch Redis operations"""
|
||||
print("\n5. Redis Batch Operations")
|
||||
print("-" * 70)
|
||||
|
||||
# Batch Set
|
||||
items = {
|
||||
f"batch_key_1_{int(time.time())}": "value1",
|
||||
f"batch_key_2_{int(time.time())}": "value2",
|
||||
f"batch_key_3_{int(time.time())}": "value3"
|
||||
}
|
||||
response = requests.post(f"{BASE_URL}/api/cache/batch-set", json=items)
|
||||
data = response.json()
|
||||
print(f" BATCH SET: {len(items)} items")
|
||||
print(f" Successful: {data['successful']}/{data['total']}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
# Batch Get
|
||||
keys = ",".join(items.keys())
|
||||
response = requests.get(f"{BASE_URL}/api/cache/batch-get", params={"keys": keys})
|
||||
data = response.json()
|
||||
print(f" BATCH GET: {len(items)} keys")
|
||||
print(f" Found: {data['found']}/{data['requested']}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_redis_stats():
|
||||
"""Test Redis statistics"""
|
||||
print("\n6. Redis Statistics")
|
||||
print("-" * 70)
|
||||
response = requests.get(f"{BASE_URL}/api/cache/stats")
|
||||
data = response.json()
|
||||
stats = data['stats']
|
||||
print(f" Memory Used: {stats['used_memory']}")
|
||||
print(f" Total Keys: {stats['total_keys']}")
|
||||
print(f" Connected Clients: {stats['connected_clients']}")
|
||||
print(f" Cache Hits: {stats['hits']}")
|
||||
print(f" Cache Misses: {stats['misses']}")
|
||||
print(f" Hit Ratio: {stats['hit_ratio_percent']}%")
|
||||
print(f" Uptime: {stats['uptime_seconds']}s")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_redis_keys():
|
||||
"""Test listing cache keys"""
|
||||
print("\n7. List Cache Keys")
|
||||
print("-" * 70)
|
||||
response = requests.get(f"{BASE_URL}/api/cache/keys", params={"pattern": "*"})
|
||||
data = response.json()
|
||||
print(f" Pattern: {data['pattern']}")
|
||||
print(f" Keys Found: {data['count']}")
|
||||
if data['keys'] and len(data['keys']) <= 5:
|
||||
print(f" Keys: {data['keys']}")
|
||||
elif data['keys']:
|
||||
print(f" Sample Keys: {data['keys'][:5]}...")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
def test_celery_fibonacci():
|
||||
"""Test Celery Fibonacci task"""
|
||||
print("\n8. Celery Task (Fibonacci Calculation)")
|
||||
print("-" * 70)
|
||||
|
||||
# Create task
|
||||
n = 15
|
||||
start = time.time()
|
||||
response = requests.post(f"{BASE_URL}/api/tasks/fibonacci", params={"n": n})
|
||||
data = response.json()
|
||||
task_id = data['task_id']
|
||||
print(f" Creating Fibonacci({n}) task...")
|
||||
print(f" Task ID: {task_id}")
|
||||
print(f" Status: {response.status_code}")
|
||||
|
||||
# Wait for completion
|
||||
print(f" Waiting for task completion...")
|
||||
max_attempts = 30
|
||||
for attempt in range(max_attempts):
|
||||
response = requests.get(f"{BASE_URL}/api/tasks/{task_id}")
|
||||
data = response.json()
|
||||
|
||||
if data['status'] == 'completed':
|
||||
elapsed = time.time() - start
|
||||
result = data['result']
|
||||
print(f" Task completed in {elapsed:.2f}s")
|
||||
print(f" Fibonacci({n}) = {result['fibonacci']}")
|
||||
print(f" Calculation time: {result['calculation_time']}")
|
||||
print(f" Service: {result['service']}")
|
||||
break
|
||||
|
||||
time.sleep(1)
|
||||
else:
|
||||
print(f" Task did not complete within {max_attempts}s")
|
||||
|
||||
def test_process_workflow():
|
||||
"""Test combined cache + Celery workflow"""
|
||||
print("\n9. Data Processing Workflow (Cache + Celery)")
|
||||
print("-" * 70)
|
||||
|
||||
data_payload = {
|
||||
"user_id": 12345,
|
||||
"action": "test_workflow",
|
||||
"timestamp": time.time(),
|
||||
"data": "sample processing data"
|
||||
}
|
||||
|
||||
# First request - should process with Celery
|
||||
print(f" First request (cache miss - will use Celery)...")
|
||||
start1 = time.time()
|
||||
elapsed1 = 0.0
|
||||
response = requests.post(f"{BASE_URL}/api/process", json=data_payload)
|
||||
data1 = response.json()
|
||||
print(f" Source: {data1['source']}")
|
||||
|
||||
if data1['source'] == 'celery_task':
|
||||
task_id = data1['task_id']
|
||||
print(f" Task ID: {task_id}")
|
||||
|
||||
# Wait for task
|
||||
for _ in range(30):
|
||||
response = requests.get(f"{BASE_URL}/api/tasks/{task_id}")
|
||||
result = response.json()
|
||||
if result['status'] == 'completed':
|
||||
elapsed1 = time.time() - start1
|
||||
print(f" Task completed in {elapsed1:.2f}s")
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# Second request - should return from cache
|
||||
print(f" Second request (cache hit - should be instant)...")
|
||||
start2 = time.time()
|
||||
response = requests.post(f"{BASE_URL}/api/process", json=data_payload)
|
||||
data2 = response.json()
|
||||
elapsed2 = time.time() - start2
|
||||
print(f" Source: {data2['source']}")
|
||||
print(f" Duration: {elapsed2:.3f}s")
|
||||
|
||||
if data1['source'] == 'celery_task' and data2['source'] == 'cache' and elapsed1 > 0:
|
||||
speedup = elapsed1 / elapsed2 if elapsed2 > 0 else 0
|
||||
print(f" Cache speedup: {speedup:.1f}x faster!")
|
||||
|
||||
def run_all_tests():
|
||||
"""Run complete test suite"""
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
test_api_info()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_health()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_dice_roll()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_redis_single()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_redis_batch()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_redis_stats()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_redis_keys()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_celery_fibonacci()
|
||||
time.sleep(0.5)
|
||||
|
||||
test_process_workflow()
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(f"All tests completed successfully in {elapsed:.2f}s!")
|
||||
print("=" * 70)
|
||||
print("\nNext Steps:")
|
||||
print(" 1. Open Jaeger UI: http://localhost:16686")
|
||||
print(" 2. Select service: 'devserver.api.fastapi' or 'devserver.celery.worker'")
|
||||
print(" 3. Click 'Find Traces' to see distributed tracing")
|
||||
print(" 4. Explore traces to see request flow across services")
|
||||
print(" 5. Check logs in ./logs/ directory")
|
||||
print("=" * 70)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("\nERROR: Cannot connect to DevServer")
|
||||
print(" Make sure the server is running:")
|
||||
print(" docker-compose up -d")
|
||||
except Exception as e:
|
||||
print(f"\nERROR: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_all_tests()
|
||||
Reference in New Issue
Block a user