File size: 11,490 Bytes
440bac0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
"""
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())