""" 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())