385 lines
15 KiB
Python
385 lines
15 KiB
Python
|
|
# ================================================================
|
|||
|
|
# validate_local.py - Script completo con todos los imports
|
|||
|
|
# ================================================================
|
|||
|
|
|
|||
|
|
import asyncio
|
|||
|
|
import httpx
|
|||
|
|
import json
|
|||
|
|
import sys
|
|||
|
|
import traceback
|
|||
|
|
from datetime import datetime
|
|||
|
|
from typing import Optional, Dict, Any
|
|||
|
|
|
|||
|
|
# Configuración
|
|||
|
|
AUTH_URL = "http://localhost:8001"
|
|||
|
|
DATA_URL = "http://localhost:8004"
|
|||
|
|
GATEWAY_URL = "http://localhost:8000" # Si usas gateway
|
|||
|
|
|
|||
|
|
class DataServiceValidator:
|
|||
|
|
"""Validador completo para el Data Service"""
|
|||
|
|
|
|||
|
|
def __init__(self, use_gateway: bool = False):
|
|||
|
|
self.auth_token: Optional[str] = None
|
|||
|
|
self.use_gateway = use_gateway
|
|||
|
|
self.base_url = GATEWAY_URL if use_gateway else DATA_URL
|
|||
|
|
self.auth_base_url = GATEWAY_URL if use_gateway else AUTH_URL
|
|||
|
|
|
|||
|
|
async def test_service_health(self) -> bool:
|
|||
|
|
"""Verificar que los servicios estén funcionando"""
|
|||
|
|
print("🔍 Checking service health...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|||
|
|
# Test auth service
|
|||
|
|
auth_response = await client.get(f"{AUTH_URL}/health")
|
|||
|
|
if auth_response.status_code == 200:
|
|||
|
|
print("✅ Auth service is healthy")
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Auth service unhealthy: {auth_response.status_code}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# Test data service
|
|||
|
|
data_response = await client.get(f"{DATA_URL}/health")
|
|||
|
|
if data_response.status_code == 200:
|
|||
|
|
print("✅ Data service is healthy")
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Data service unhealthy: {data_response.status_code}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except httpx.ConnectError as e:
|
|||
|
|
print(f"❌ Connection error: {e}")
|
|||
|
|
print("💡 Make sure services are running with: docker-compose up -d")
|
|||
|
|
return False
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ Health check failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
async def authenticate(self) -> bool:
|
|||
|
|
"""Autenticar y obtener token"""
|
|||
|
|
print("🔐 Authenticating...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
async with httpx.AsyncClient(timeout=15.0) as client:
|
|||
|
|
# Datos de usuario de prueba
|
|||
|
|
user_data = {
|
|||
|
|
"email": "test@bakery.es",
|
|||
|
|
"password": "TestPass123",
|
|||
|
|
"full_name": "Test User",
|
|||
|
|
"language": "es"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Intentar registrar usuario (puede fallar si ya existe)
|
|||
|
|
register_endpoint = f"{self.auth_base_url}/api/v1/auth/register"
|
|||
|
|
register_response = await client.post(register_endpoint, json=user_data)
|
|||
|
|
|
|||
|
|
if register_response.status_code == 200:
|
|||
|
|
print("✅ User registered successfully")
|
|||
|
|
elif register_response.status_code == 409:
|
|||
|
|
print("ℹ️ User already exists, proceeding with login")
|
|||
|
|
else:
|
|||
|
|
print(f"⚠️ Registration response: {register_response.status_code}")
|
|||
|
|
|
|||
|
|
# Login
|
|||
|
|
login_data = {
|
|||
|
|
"email": user_data["email"],
|
|||
|
|
"password": user_data["password"]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
login_endpoint = f"{self.auth_base_url}/api/v1/auth/login"
|
|||
|
|
login_response = await client.post(login_endpoint, json=login_data)
|
|||
|
|
|
|||
|
|
if login_response.status_code == 200:
|
|||
|
|
response_data = login_response.json()
|
|||
|
|
self.auth_token = response_data["access_token"]
|
|||
|
|
print("✅ Authentication successful")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Login failed: {login_response.status_code}")
|
|||
|
|
print(f"Response: {login_response.text}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ Authentication failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def get_headers(self) -> Dict[str, str]:
|
|||
|
|
"""Obtener headers con token de autenticación"""
|
|||
|
|
if not self.auth_token:
|
|||
|
|
raise ValueError("No authentication token available")
|
|||
|
|
return {"Authorization": f"Bearer {self.auth_token}"}
|
|||
|
|
|
|||
|
|
async def test_weather_endpoints(self) -> bool:
|
|||
|
|
"""Probar endpoints de clima"""
|
|||
|
|
print("🌤️ Testing weather endpoints...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
headers = self.get_headers()
|
|||
|
|
madrid_coords = {"latitude": 40.4168, "longitude": -3.7038}
|
|||
|
|
|
|||
|
|
async with httpx.AsyncClient(timeout=20.0) as client:
|
|||
|
|
# Current weather
|
|||
|
|
current_endpoint = f"{self.base_url}/api/v1/weather/current" if not self.use_gateway else f"{self.base_url}/api/v1/data/weather/current"
|
|||
|
|
current_response = await client.get(
|
|||
|
|
current_endpoint,
|
|||
|
|
params=madrid_coords,
|
|||
|
|
headers=headers
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if current_response.status_code == 200:
|
|||
|
|
weather = current_response.json()
|
|||
|
|
print(f"✅ Current weather: {weather.get('temperature')}°C, {weather.get('description')}")
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Current weather failed: {current_response.status_code}")
|
|||
|
|
print(f"Response: {current_response.text}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# Weather forecast
|
|||
|
|
forecast_endpoint = f"{self.base_url}/api/v1/weather/forecast" if not self.use_gateway else f"{self.base_url}/api/v1/data/weather/forecast"
|
|||
|
|
forecast_response = await client.get(
|
|||
|
|
forecast_endpoint,
|
|||
|
|
params={**madrid_coords, "days": 3},
|
|||
|
|
headers=headers
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if forecast_response.status_code == 200:
|
|||
|
|
forecast = forecast_response.json()
|
|||
|
|
print(f"✅ Weather forecast: {len(forecast)} days")
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Weather forecast failed: {forecast_response.status_code}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ Weather tests failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
async def test_traffic_endpoints(self) -> bool:
|
|||
|
|
"""Probar endpoints de tráfico"""
|
|||
|
|
print("🚦 Testing traffic endpoints...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
headers = self.get_headers()
|
|||
|
|
madrid_coords = {"latitude": 40.4168, "longitude": -3.7038}
|
|||
|
|
|
|||
|
|
async with httpx.AsyncClient(timeout=20.0) as client:
|
|||
|
|
# Current traffic
|
|||
|
|
current_endpoint = f"{self.base_url}/api/v1/traffic/current" if not self.use_gateway else f"{self.base_url}/api/v1/data/traffic/current"
|
|||
|
|
current_response = await client.get(
|
|||
|
|
current_endpoint,
|
|||
|
|
params=madrid_coords,
|
|||
|
|
headers=headers
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if current_response.status_code == 200:
|
|||
|
|
traffic = current_response.json()
|
|||
|
|
print(f"✅ Current traffic: {traffic.get('traffic_volume')} vehicles, {traffic.get('congestion_level')} congestion")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Current traffic failed: {current_response.status_code}")
|
|||
|
|
print(f"Response: {current_response.text}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ Traffic tests failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
async def test_sales_endpoints(self) -> bool:
|
|||
|
|
"""Probar endpoints de ventas"""
|
|||
|
|
print("📊 Testing sales endpoints...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
headers = self.get_headers()
|
|||
|
|
|
|||
|
|
# Datos de prueba
|
|||
|
|
sales_data = {
|
|||
|
|
"tenant_id": "123e4567-e89b-12d3-a456-426614174000",
|
|||
|
|
"date": datetime.now().isoformat(),
|
|||
|
|
"product_name": "Pan Integral Test",
|
|||
|
|
"quantity_sold": 25,
|
|||
|
|
"revenue": 37.50,
|
|||
|
|
"location_id": "madrid_centro",
|
|||
|
|
"source": "test"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async with httpx.AsyncClient(timeout=20.0) as client:
|
|||
|
|
# Create sales record
|
|||
|
|
create_endpoint = f"{self.base_url}/api/v1/sales/" if not self.use_gateway else f"{self.base_url}/api/v1/data/sales/"
|
|||
|
|
create_response = await client.post(
|
|||
|
|
create_endpoint,
|
|||
|
|
json=sales_data,
|
|||
|
|
headers=headers
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if create_response.status_code == 200:
|
|||
|
|
record = create_response.json()
|
|||
|
|
print(f"✅ Sales record created: ID {record.get('id')}")
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Sales creation failed: {create_response.status_code}")
|
|||
|
|
print(f"Response: {create_response.text}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# Test import template
|
|||
|
|
template_endpoint = f"{self.base_url}/api/v1/sales/import/template/csv" if not self.use_gateway else f"{self.base_url}/api/v1/data/sales/import/template/csv"
|
|||
|
|
template_response = await client.get(
|
|||
|
|
template_endpoint,
|
|||
|
|
headers=headers
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if template_response.status_code == 200:
|
|||
|
|
print("✅ Import template generated successfully")
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Template generation failed: {template_response.status_code}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ Sales tests failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
async def test_import_functionality(self) -> bool:
|
|||
|
|
"""Probar funcionalidad de importación"""
|
|||
|
|
print("📄 Testing import functionality...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
headers = self.get_headers()
|
|||
|
|
|
|||
|
|
# Crear CSV de prueba
|
|||
|
|
csv_content = """fecha,producto,cantidad,ingresos
|
|||
|
|
15/01/2024,Pan Integral,25,37.50
|
|||
|
|
15/01/2024,Croissant,15,22.50
|
|||
|
|
15/01/2024,Café con Leche,10,20.00"""
|
|||
|
|
|
|||
|
|
# Test CSV import
|
|||
|
|
import_data = {
|
|||
|
|
"tenant_id": "123e4567-e89b-12d3-a456-426614174000",
|
|||
|
|
"data_format": "csv",
|
|||
|
|
"data": csv_content
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|||
|
|
import_endpoint = f"{self.base_url}/api/v1/sales/import/json" if not self.use_gateway else f"{self.base_url}/api/v1/data/sales/import/json"
|
|||
|
|
import_response = await client.post(
|
|||
|
|
import_endpoint,
|
|||
|
|
json=import_data,
|
|||
|
|
headers=headers
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if import_response.status_code == 200:
|
|||
|
|
result = import_response.json()
|
|||
|
|
if result.get("success"):
|
|||
|
|
print(f"✅ CSV import successful: {result.get('records_created')} records created")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
print(f"❌ CSV import failed: {result.get('error')}")
|
|||
|
|
return False
|
|||
|
|
else:
|
|||
|
|
print(f"❌ Import request failed: {import_response.status_code}")
|
|||
|
|
print(f"Response: {import_response.text}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ Import tests failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
async def main():
|
|||
|
|
"""Función principal de validación"""
|
|||
|
|
print("🚀 Starting Data Service Validation")
|
|||
|
|
print("=" * 50)
|
|||
|
|
|
|||
|
|
# Preguntar si usar gateway
|
|||
|
|
use_gateway_input = input("Use API Gateway? (y/N): ").lower()
|
|||
|
|
use_gateway = use_gateway_input == 'y'
|
|||
|
|
|
|||
|
|
if use_gateway:
|
|||
|
|
print("📡 Testing via API Gateway")
|
|||
|
|
else:
|
|||
|
|
print("🔗 Testing direct service connections")
|
|||
|
|
|
|||
|
|
validator = DataServiceValidator(use_gateway=use_gateway)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 1. Health checks
|
|||
|
|
if not await validator.test_service_health():
|
|||
|
|
print("\n❌ Health checks failed. Ensure services are running.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 2. Authentication
|
|||
|
|
if not await validator.authenticate():
|
|||
|
|
print("\n❌ Authentication failed.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 3. Weather tests
|
|||
|
|
if not await validator.test_weather_endpoints():
|
|||
|
|
print("\n❌ Weather endpoint tests failed.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 4. Traffic tests
|
|||
|
|
if not await validator.test_traffic_endpoints():
|
|||
|
|
print("\n❌ Traffic endpoint tests failed.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 5. Sales tests
|
|||
|
|
if not await validator.test_sales_endpoints():
|
|||
|
|
print("\n❌ Sales endpoint tests failed.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 6. Import tests
|
|||
|
|
if not await validator.test_import_functionality():
|
|||
|
|
print("\n❌ Import functionality tests failed.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
print("\n" + "=" * 50)
|
|||
|
|
print("🎉 ALL TESTS PASSED! Data Service is working correctly!")
|
|||
|
|
print("=" * 50)
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except KeyboardInterrupt:
|
|||
|
|
print("\n⚠️ Tests interrupted by user")
|
|||
|
|
return False
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"\n❌ Unexpected error: {e}")
|
|||
|
|
print("Traceback:")
|
|||
|
|
traceback.print_exc()
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def check_dependencies():
|
|||
|
|
"""Verificar que las dependencias estén instaladas"""
|
|||
|
|
try:
|
|||
|
|
import httpx
|
|||
|
|
print("✅ httpx is available")
|
|||
|
|
except ImportError:
|
|||
|
|
print("❌ httpx not found. Install with: pip install httpx")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
print("🔧 Checking dependencies...")
|
|||
|
|
|
|||
|
|
if not check_dependencies():
|
|||
|
|
print("\n💡 Install dependencies with:")
|
|||
|
|
print("pip install httpx")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
print("✅ All dependencies available")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# Ejecutar validación
|
|||
|
|
success = asyncio.run(main())
|
|||
|
|
|
|||
|
|
if success:
|
|||
|
|
print("\n🎯 Next steps:")
|
|||
|
|
print("1. Check logs: docker-compose logs -f data-service")
|
|||
|
|
print("2. Connect to DB: docker-compose exec data-db psql -U data_user -d data_db")
|
|||
|
|
print("3. Test with real data imports")
|
|||
|
|
sys.exit(0)
|
|||
|
|
else:
|
|||
|
|
print("\n🔧 Troubleshooting:")
|
|||
|
|
print("1. Check services: docker-compose ps")
|
|||
|
|
print("2. Restart services: docker-compose restart")
|
|||
|
|
print("3. Check logs: docker-compose logs data-service")
|
|||
|
|
sys.exit(1)
|