Spaces:
Running
Running
| """ | |
| MELODIX - Test para Conexión Android | |
| Este script verifica que la API esté accesible desde dispositivos móviles | |
| en la misma red local. | |
| Uso: python tests/android_test.py | |
| """ | |
| import requests | |
| import os | |
| import sys | |
| import socket | |
| from dotenv import load_dotenv | |
| # ========================================== | |
| # CONFIGURACIÓN | |
| # ========================================== | |
| load_dotenv() | |
| # Obtener IP local automáticamente | |
| def get_local_ip(): | |
| """Obtiene la IP local del equipo""" | |
| try: | |
| # Crear socket temporal para obtener IP | |
| s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| s.connect(("8.8.8.8", 80)) | |
| ip = s.getsockname()[0] | |
| s.close() | |
| return ip | |
| except Exception: | |
| return "127.0.0.1" | |
| LOCAL_IP = get_local_ip() | |
| BASE_URL_LOCAL = f"http://localhost:8000" | |
| BASE_URL_NETWORK = f"http://{LOCAL_IP}:8000" | |
| TEST_USER = "android_test_user" | |
| TEST_PASS = "password123" | |
| TEST_EMAIL = "android@test.com" | |
| # ========================================== | |
| # COLORES PARA CONSOLA | |
| # ========================================== | |
| GREEN = "\033[92m" | |
| RED = "\033[91m" | |
| YELLOW = "\033[93m" | |
| BLUE = "\033[94m" | |
| END = "\033[0m" | |
| def print_header(text): | |
| print(f"\n{BLUE}{'=' * 60}{END}") | |
| print(f"{BLUE} {text}{END}") | |
| print(f"{BLUE}{'=' * 60}{END}\n") | |
| def print_success(text): | |
| print(f"{GREEN}[OK] {text}{END}") | |
| def print_error(text): | |
| print(f"{RED}[FAIL] {text}{END}") | |
| def print_warning(text): | |
| print(f"{YELLOW}[WARN] {text}{END}") | |
| # ========================================== | |
| # TESTS | |
| # ========================================== | |
| def test_local_connection(): | |
| """Test 1: Conexión local""" | |
| print_header("TEST 1: CONEXIÓN LOCAL (localhost)") | |
| try: | |
| res = requests.get(f"{BASE_URL_LOCAL}/health", timeout=5) | |
| if res.status_code == 200: | |
| print_success(f"API accesible en localhost:8000") | |
| print(f" Respuesta: {res.json()}") | |
| return True | |
| else: | |
| print_error(f"API respondió con error: {res.status_code}") | |
| return False | |
| except requests.exceptions.ConnectionError: | |
| print_error("No se pudo conectar a localhost:8000") | |
| print_warning("¿Está corriendo la API? Ejecuta: uvicorn main:app --host 0.0.0.0 --port 8000") | |
| return False | |
| except Exception as e: | |
| print_error(f"Error: {e}") | |
| return False | |
| def test_network_connection(): | |
| """Test 2: Conexión por red local""" | |
| print_header(f"TEST 2: CONEXIÓN POR RED LOCAL ({LOCAL_IP})") | |
| try: | |
| res = requests.get(f"{BASE_URL_NETWORK}/health", timeout=5) | |
| if res.status_code == 200: | |
| print_success(f"API accesible en {LOCAL_IP}:8000") | |
| print(f" Respuesta: {res.json()}") | |
| return True | |
| else: | |
| print_error(f"API respondió con error: {res.status_code}") | |
| return False | |
| except requests.exceptions.ConnectionError: | |
| print_error(f"No se pudo conectar a {LOCAL_IP}:8000") | |
| print_warning("\nPosibles causas:") | |
| print_warning(" 1. El firewall de Windows está bloqueando el puerto 8000") | |
| print_warning(" 2. La API no está escuchando en 0.0.0.0") | |
| print_warning("\nSolución:") | |
| print_warning(" - Ejecuta: uvicorn main:app --host 0.0.0.0 --port 8000") | |
| print_warning(" - Permite el puerto 8000 en el firewall de Windows") | |
| return False | |
| except Exception as e: | |
| print_error(f"Error: {e}") | |
| return False | |
| def test_cors_config(): | |
| """Test 3: Verificar configuración CORS""" | |
| print_header("TEST 3: CONFIGURACIÓN CORS") | |
| try: | |
| # Hacer petición OPTIONS para verificar CORS | |
| res = requests.options(f"{BASE_URL_LOCAL}/auth/token", timeout=5) | |
| # Verificar headers CORS | |
| cors_headers = res.headers | |
| if 'access-control-allow-origin' in cors_headers: | |
| print_success("CORS configurado correctamente") | |
| print(f" Access-Control-Allow-Origin: {cors_headers['access-control-allow-origin']}") | |
| if cors_headers['access-control-allow-origin'] == '*': | |
| print_warning("CORS permite todos los orígenes (*) - OK para desarrollo") | |
| else: | |
| print_success(f"Orígenes permitidos: {cors_headers['access-control-allow-origin']}") | |
| return True | |
| else: | |
| print_warning("No se encontraron headers CORS (puede ser normal)") | |
| return True | |
| except Exception as e: | |
| print_error(f"Error verificando CORS: {e}") | |
| return False | |
| def test_auth_flow(): | |
| """Test 4: Flujo completo de autenticación""" | |
| print_header("TEST 4: FLUJO DE AUTENTICACIÓN (simula app Android)") | |
| # 1. Registro | |
| print("1. Registrando usuario de prueba...") | |
| try: | |
| res = requests.post( | |
| f"{BASE_URL_LOCAL}/auth/register", | |
| json={ | |
| "username": TEST_USER, | |
| "email": TEST_EMAIL, | |
| "password": TEST_PASS | |
| }, | |
| timeout=10 | |
| ) | |
| if res.status_code == 200: | |
| print_success("Usuario registrado") | |
| elif res.status_code == 400: | |
| print_warning("Usuario ya existe (continuando con login)") | |
| else: | |
| print_error(f"Error en registro: {res.status_code}") | |
| return False | |
| except Exception as e: | |
| print_error(f"Error: {e}") | |
| return False | |
| # 2. Login | |
| print("\n2. Iniciando sesión (simula login desde Android)...") | |
| try: | |
| res = requests.post( | |
| f"{BASE_URL_LOCAL}/auth/token", | |
| data={ | |
| "username": TEST_USER, | |
| "password": TEST_PASS, | |
| "device_id": "android_device_001", | |
| "device_name": "Android Test Phone" | |
| }, | |
| timeout=10 | |
| ) | |
| if res.status_code != 200: | |
| print_error(f"Error en login: {res.status_code}") | |
| print(f" Respuesta: {res.text}") | |
| return False | |
| token_data = res.json() | |
| token = token_data["access_token"] | |
| print_success("Login exitoso") | |
| print(f" Token: {token[:50]}...") | |
| print(f" Expires in: {token_data.get('expires_in', 'N/A')}") | |
| except Exception as e: | |
| print_error(f"Error: {e}") | |
| return False | |
| # 3. Petición autenticada | |
| print("\n3. Consultando perfil (con token Bearer)...") | |
| try: | |
| headers = {"Authorization": f"Bearer {token}"} | |
| res = requests.get(f"{BASE_URL_LOCAL}/auth/users/me", headers=headers, timeout=10) | |
| if res.status_code == 200: | |
| user_data = res.json() | |
| print_success("Perfil obtenido correctamente") | |
| print(f" Username: {user_data.get('username')}") | |
| print(f" Créditos: {user_data.get('credits')}") | |
| return True | |
| else: | |
| print_error(f"Error obteniendo perfil: {res.status_code}") | |
| return False | |
| except Exception as e: | |
| print_error(f"Error: {e}") | |
| return False | |
| def test_api_docs(): | |
| """Test 5: Documentación Swagger""" | |
| print_header("TEST 5: DOCUMENTACIÓN SWAGGER") | |
| try: | |
| res = requests.get(f"{BASE_URL_LOCAL}/docs", timeout=5) | |
| if res.status_code == 200: | |
| print_success("Swagger UI accesible") | |
| print(f" URL: {BASE_URL_LOCAL}/docs") | |
| print(f" También disponible en: http://{LOCAL_IP}:8000/docs") | |
| return True | |
| else: | |
| print_error(f"Swagger no accesible: {res.status_code}") | |
| return False | |
| except Exception as e: | |
| print_error(f"Error: {e}") | |
| return False | |
| def test_b2_connection(): | |
| """Test 6: Verificar conexión a Backblaze B2""" | |
| print_header("TEST 6: CONEXIÓN A BACKBLAZE B2") | |
| try: | |
| # Añadir path de app temporalmente si es necesario | |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from app.utils.b2_storage import get_b2_client, B2_BUCKET_NAME | |
| client = get_b2_client() | |
| if client: | |
| res = client.list_objects_v2(Bucket=B2_BUCKET_NAME, MaxKeys=1) | |
| print_success(f"Conexión a Backblaze B2 EXITOSA") | |
| print(f" Bucket activo: {B2_BUCKET_NAME}") | |
| return True | |
| else: | |
| print_error("No se pudo instanciar el cliente de B2. Verifica tus credenciales.") | |
| return False | |
| except Exception as e: | |
| print_error(f"Error conectando a B2: {e}") | |
| return False | |
| def print_android_config(): | |
| """Imprime configuración para la App Móvil""" | |
| print_header("CONFIGURACIÓN PARA LA APP MÓVIL") | |
| print(f""" | |
| {BLUE}Opción A - Red Local (recomendado para testing local):{END} | |
| URL Base: {BASE_URL_NETWORK} | |
| En tu app React Native (config.js o similar): | |
| +-----------------------------------------------------+ | |
| export const API_URL = "{BASE_URL_NETWORK}"; | |
| +-----------------------------------------------------+ | |
| {YELLOW}Requisitos:{END} | |
| - Ambos dispositivos en la misma red WiFi | |
| - Firewall permite puerto 8000 | |
| - API ejecutada con --host 0.0.0.0 | |
| {BLUE}Opción B - API en Producción (Render):{END} | |
| URL Base: https://melodix-api.onrender.com | |
| Las descargas de stems se realizarán de manera directa y segura | |
| desde Backblaze B2 usando URLs firmadas de corta duración. | |
| {BLUE}Opción C - Emulador de Android (Local):{END} | |
| URL Base: http://10.0.2.2:8000 | |
| El emulador usa 10.0.2.2 para mapear al localhost del PC Host. | |
| {GREEN}¡Importante!{END} | |
| - Asegúrate de que ALLOWED_ORIGINS en tu .env local incluya la IP de tu PC. | |
| - Para dispositivo físico: {BASE_URL_NETWORK} | |
| """) | |
| # ========================================== | |
| # MAIN | |
| # ========================================== | |
| def main(): | |
| print(f"\n{BLUE}+--------------------------------------------------------+{END}") | |
| print(f"{BLUE}| MELODIX - TEST PARA CONEXIÓN MÓVIL / BACKEND |{END}") | |
| print(f"{BLUE}+--------------------------------------------------------+{END}") | |
| print(f"\n IP Local detectada: {GREEN}{LOCAL_IP}{END}") | |
| print(f" API Base URL: {GREEN}{BASE_URL_LOCAL}{END}") | |
| results = { | |
| "Conexión Local": test_local_connection(), | |
| "Conexión Red Local": test_network_connection(), | |
| "Configuración CORS": test_cors_config(), | |
| "Autenticación": test_auth_flow(), | |
| "Documentación": test_api_docs(), | |
| "Conexión Backblaze B2": test_b2_connection(), | |
| } | |
| # Resumen | |
| print_header("RESUMEN") | |
| passed = sum(1 for v in results.values() if v) | |
| total = len(results) | |
| for test, result in results.items(): | |
| status = f"{GREEN}[PASS]{END}" if result else f"{RED}[FAIL]{END}" | |
| print(f" {status} - {test}") | |
| print(f"\n Resultados: {GREEN}{passed}/{total}{END} tests aprobados") | |
| if passed == total: | |
| print(f"\n {GREEN}¡TODO LISTO PARA EL DESARROLLO DE LA APP MÓVIL!{END}") | |
| elif passed >= total - 1: | |
| print(f"\n {YELLOW}Casi listo. Revisa los tests fallidos.{END}") | |
| else: | |
| print(f"\n {RED}Hay problemas. Revisa los tests fallidos.{END}") | |
| # Configuración Móvil | |
| print_android_config() | |
| return 0 if passed == total else 1 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |