""" Script de verificación de correcciones de seguridad y límites de la API. Este script valida: 1. Permiso de CRUD en letras grupales: el administrador del grupo o miembros con perm_crud_letras pueden editar/borrar letras del grupo creadas por otros usuarios. 2. Restricción de montos negativos y cero en finanzas: la API rechaza montos <= 0 con error 422. """ import requests import uuid BASE_URL = "http://127.0.0.1:8000" def test_lyrics_and_finances_fixes(): print("=" * 60) print("INICIANDO PRUEBAS DE VERIFICACIÓN DE FIXES") print("=" * 60) # 1. Crear usuarios de prueba suffix = str(uuid.uuid4())[:8] user_a = f"admin_fix_{suffix}" user_b = f"member_fix_{suffix}" password = "password123" print("\n[1] Registrando y logueando usuarios...") for u in [user_a, user_b]: reg_res = requests.post(f"{BASE_URL}/auth/register", json={ "username": u, "email": f"{u}@example.com", "password": password }) assert reg_res.status_code in (200, 400), f"Error al registrar {u}: {reg_res.text}" # Login User A tok_a_res = requests.post(f"{BASE_URL}/auth/token", data={ "username": f"{user_a}@example.com", "password": password, "device_id": f"dev_a_{suffix}", "device_name": "Device Admin" }) assert tok_a_res.status_code == 200, f"Error login A: {tok_a_res.text}" token_a = tok_a_res.json()["access_token"] headers_a = {"Authorization": f"Bearer {token_a}"} # Login User B tok_b_res = requests.post(f"{BASE_URL}/auth/token", data={ "username": f"{user_b}@example.com", "password": password, "device_id": f"dev_b_{suffix}", "device_name": "Device Member" }) assert tok_b_res.status_code == 200, f"Error login B: {tok_b_res.text}" token_b = tok_b_res.json()["access_token"] headers_b = {"Authorization": f"Bearer {token_b}"} print(" [OK] Usuarios autenticados correctamente.") # 2. User A crea un grupo print("\n[2] User A (Admin) creando grupo...") group_res = requests.post(f"{BASE_URL}/lyrics/groups", json={"name": f"Grupo Fix {suffix}"}, headers=headers_a) assert group_res.status_code == 200, f"Error al crear grupo: {group_res.text}" group_id = group_res.json()["id"] print(f" [OK] Grupo creado con ID {group_id}") # 3. Invitar a User B al grupo print("\n[3] Generando invitación y User B uniéndose...") invite_res = requests.post(f"{BASE_URL}/lyrics/groups/{group_id}/invite", headers=headers_a) assert invite_res.status_code == 200, f"Error al crear invitación: {invite_res.text}" code = invite_res.json()["code"] join_res = requests.post(f"{BASE_URL}/lyrics/join/{code}", headers=headers_b) assert join_res.status_code == 200, f"Error al unirse: {join_res.text}" print(" [OK] User B se unió exitosamente al grupo.") # 4. User B crea una letra en el grupo print("\n[4] User B creando letra en el grupo...") lyric_res = requests.post(f"{BASE_URL}/lyrics", json={ "title": "Letra de Prueba Fix", "artist": "Artista Fix", "content": "Contenido de la letra", "group_id": group_id }, headers=headers_b) assert lyric_res.status_code == 200, f"Error al crear letra: {lyric_res.text}" lyric_id = lyric_res.json()["id"] print(f" [OK] Letra creada por User B con ID {lyric_id}") # 5. User A (Admin del grupo) intenta editar la letra creada por User B print("\n[5] User A (Admin) intenta editar la letra de User B (Prueba de Fix Bug 3)...") edit_res = requests.put(f"{BASE_URL}/lyrics/{lyric_id}", json={ "title": "Letra de Prueba Modificada por Admin", "artist": "Artista Fix Modificado", "content": "Nuevo contenido editado por el administrador", "group_id": group_id }, headers=headers_a) print(f" Código de estado de edición: {edit_res.status_code}") assert edit_res.status_code == 200, f"ERROR: El administrador del grupo no pudo editar la letra (Fix Bug 3 no funciona). Respuesta: {edit_res.text}" print(" [OK] ¡ÉXITO! El administrador del grupo pudo editar la letra sin ser el creador original.") # 6. User A (Admin) intenta eliminar la letra creada por User B print("\n[6] User A (Admin) intenta eliminar la letra de User B...") del_res = requests.delete(f"{BASE_URL}/lyrics/{lyric_id}", headers=headers_a) print(f" Código de estado de eliminación: {del_res.status_code}") assert del_res.status_code == 200, f"ERROR: El administrador no pudo borrar la letra. Respuesta: {del_res.text}" print(" [OK] ¡ÉXITO! El administrador del grupo pudo eliminar la letra sin ser el creador original.") # 7. Registrar Iglesia para probar Finanzas print("\n[7] Registrando iglesia para pruebas de finanzas...") church_res = requests.post(f"{BASE_URL}/iglesia/crear", json={ "nombre": f"Iglesia Fix {suffix}", "pastor": "Pastor Fix" }, headers=headers_a) assert church_res.status_code == 200, f"Error creando iglesia: {church_res.text}" print(" [OK] Iglesia creada correctamente.") # 8. Intentar crear finanza con monto negativo (esperado: 422 Unprocessable Entity) print("\n[8] Probando montos negativos en finanzas (Prueba de Fix Bug 4)...") fin_neg_res = requests.post(f"{BASE_URL}/iglesia/finanzas", json={ "tipo": "entrada", "monto": -150.0, "concepto": "Intento de monto negativo", "responsable": "Admin", "reference_uuid": f"ref_neg_{suffix}" }, headers=headers_a) print(f" Código de estado (negativo): {fin_neg_res.status_code}") assert fin_neg_res.status_code == 422, f"ERROR: La API aceptó un monto negativo (-150.0). Código: {fin_neg_res.status_code}" print(" [OK] ¡ÉXITO! La API rechazó correctamente un monto negativo con código 422.") # 9. Intentar crear finanza con monto cero (esperado: 422 Unprocessable Entity) print("\n[9] Probando monto cero en finanzas...") fin_zero_res = requests.post(f"{BASE_URL}/iglesia/finanzas", json={ "tipo": "entrada", "monto": 0.0, "concepto": "Intento de monto cero", "responsable": "Admin", "reference_uuid": f"ref_zero_{suffix}" }, headers=headers_a) print(f" Código de estado (cero): {fin_zero_res.status_code}") assert fin_zero_res.status_code == 422, f"ERROR: La API aceptó un monto de cero (0.0). Código: {fin_zero_res.status_code}" print(" [OK] ¡ÉXITO! La API rechazó correctamente un monto de cero con código 422.") # 10. Intentar crear finanza con monto positivo (esperado: 200 OK) print("\n[10] Probando monto positivo en finanzas...") fin_pos_res = requests.post(f"{BASE_URL}/iglesia/finanzas", json={ "tipo": "entrada", "monto": 75.50, "concepto": "Ofrenda válida", "responsable": "Admin", "reference_uuid": f"ref_pos_{suffix}" }, headers=headers_a) print(f" Código de estado (positivo): {fin_pos_res.status_code}") assert fin_pos_res.status_code == 200, f"ERROR: La API rechazó un monto positivo válido (75.50). Código: {fin_pos_res.status_code}. Respuesta: {fin_pos_res.text}" print(" [OK] ¡ÉXITO! La API registró correctamente el movimiento positivo.") print("\n" + "=" * 60) print(" ¡TODAS LAS PRUEBAS DE FIXES DE SEGURIDAD PASARON CON EXITO!") print("=" * 60 + "\n") if __name__ == "__main__": test_lyrics_and_finances_fixes()