melodix-api / tests /android_test.py
GitHub Action
deploy from github actions
440bac0
Raw
History Blame Contribute Delete
11.5 kB
"""
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())