From a13a8cb0e8f504237e1bdbf5bf2af88142a48161 Mon Sep 17 00:00:00 2001 From: icamarillo Date: Mon, 9 Feb 2026 13:33:49 -0700 Subject: [PATCH] feat: Add maintenance scripts and testing utilities - Added password management utilities for user administration - Added PowerShell scripts for testing attachment endpoints - Enhanced development and testing workflow capabilities - Completed v1.4.0 with all maintenance tools included" --- backend/check_test_user_password.py | 77 +++++++++++++++++++++++++++++ backend/set_test_password.py | 59 ++++++++++++++++++++++ test_attachments.ps1 | 34 +++++++++++++ test_attachments_endpoint.ps1 | 45 +++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 backend/check_test_user_password.py create mode 100644 backend/set_test_password.py create mode 100644 test_attachments.ps1 create mode 100644 test_attachments_endpoint.ps1 diff --git a/backend/check_test_user_password.py b/backend/check_test_user_password.py new file mode 100644 index 0000000..8b7eb81 --- /dev/null +++ b/backend/check_test_user_password.py @@ -0,0 +1,77 @@ +""" +Script para verificar y actualizar password del test_user +""" +import asyncio +import sys +import os +from sqlalchemy import select +from passlib.context import CryptContext + +# Configurar el path +backend_path = os.path.join(os.path.dirname(__file__), 'backend') +sys.path.insert(0, backend_path) + +from app.core.database import AsyncSessionLocal +from app.models.user import User + +# Configurar passlib igual que en security.py +pwd_context = CryptContext( + schemes=["argon2", "bcrypt"], + deprecated="auto", + argon2__memory_cost=65536, + argon2__time_cost=3, + argon2__parallelism=4, +) + +async def check_and_fix_test_user(): + """Verificar y actualizar password del test_user""" + + # Contraseñas posibles + possible_passwords = [ + "admin123", + "TestPassword123!", + "password123", + "test123", + "hashed_password" + ] + + async with AsyncSessionLocal() as session: + try: + # Buscar test_user + result = await session.execute( + select(User).where(User.email == "test_user@example.com") + ) + user = result.scalar_one_or_none() + + if not user: + print("❌ Usuario test_user@example.com no encontrado") + return + + print(f"✅ Usuario encontrado: {user.email}") + print(f"Hash actual: {user.password_hash}") + + # Probar contraseñas posibles + found_password = None + for password in possible_passwords: + if pwd_context.verify(password, user.password_hash): + found_password = password + break + + if found_password: + print(f"🎉 Contraseña encontrada: {found_password}") + else: + print("❌ Ninguna contraseña coincide") + print("Estableciendo nueva contraseña: admin123") + + # Establecer nueva contraseña + new_password = "admin123" + user.password_hash = pwd_context.hash(new_password) + await session.commit() + print(f"✅ Contraseña actualizada a: {new_password}") + + except Exception as e: + print(f"❌ Error: {e}") + await session.rollback() + +if __name__ == "__main__": + asyncio.run(check_and_fix_test_user()) \ No newline at end of file diff --git a/backend/set_test_password.py b/backend/set_test_password.py new file mode 100644 index 0000000..e27071d --- /dev/null +++ b/backend/set_test_password.py @@ -0,0 +1,59 @@ +""" +Script para establecer contraseña real al test_user +""" +import asyncio +import sys +import os +from sqlalchemy import select +from passlib.context import CryptContext + +# Configurar el path +backend_path = os.path.join(os.path.dirname(__file__), 'backend') +sys.path.insert(0, backend_path) + +from app.core.database import AsyncSessionLocal +from app.models.user import User + +# Configurar passlib +pwd_context = CryptContext( + schemes=["argon2", "bcrypt"], + deprecated="auto", + argon2__memory_cost=65536, + argon2__time_cost=3, + argon2__parallelism=4, +) + +async def set_test_user_password(): + """Establecer contraseña admin123 para test_user""" + + new_password = "admin123" + password_hash = pwd_context.hash(new_password) + + async with AsyncSessionLocal() as session: + try: + # Buscar test_user + result = await session.execute( + select(User).where(User.email == "test_user@example.com") + ) + user = result.scalar_one_or_none() + + if not user: + print("❌ Usuario test_user@example.com no encontrado") + return + + print(f"✅ Usuario encontrado: {user.email}") + print(f"Hash anterior: {user.password_hash}") + + # Establecer nueva contraseña hash + user.password_hash = password_hash + await session.commit() + + print(f"🎉 Contraseña establecida: {new_password}") + print(f"✅ Nuevo hash: {password_hash[:50]}...") + + except Exception as e: + print(f"❌ Error: {e}") + await session.rollback() + +if __name__ == "__main__": + asyncio.run(set_test_user_password()) \ No newline at end of file diff --git a/test_attachments.ps1 b/test_attachments.ps1 new file mode 100644 index 0000000..1d4f2cf --- /dev/null +++ b/test_attachments.ps1 @@ -0,0 +1,34 @@ +# Test de Attachments API +# Ejecutar desde PowerShell + +# 1. Login +$body = '{"email":"admin@aduanasoft.com","password":"admin123","tenant_slug":"aduanasoft"}' +$login = Invoke-RestMethod -Uri "http://localhost:8000/v1/auth/login" -Method POST -Body $body -ContentType "application/json" + +$token = $login.data.access_token +$tenantId = $login.data.user.tenant_id + +$headers = @{ + "Authorization" = "Bearer $token" + "X-Tenant-ID" = $tenantId +} + +Write-Host "Autenticacion exitosa" -ForegroundColor Green + +# 2. Listar tickets +$tickets = Invoke-RestMethod -Uri "http://localhost:8000/v1/tickets" -Headers $headers +$ticketId = $tickets.data[0].id + +Write-Host "Ticket ID obtenido: $ticketId" -ForegroundColor Green + +# 3. Listar attachments del ticket +$attachments = Invoke-RestMethod -Uri "http://localhost:8000/v1/tickets/$ticketId/attachments" -Headers $headers + +Write-Host "Attachments listados: $($attachments.Count) encontrados" -ForegroundColor Green + +if ($attachments.Count -gt 0) { + $attachments | Format-Table id, original_filename, file_size, created_at +} + +Write-Host "" +Write-Host "TEST COMPLETADO" -ForegroundColor Cyan diff --git a/test_attachments_endpoint.ps1 b/test_attachments_endpoint.ps1 new file mode 100644 index 0000000..4cc5416 --- /dev/null +++ b/test_attachments_endpoint.ps1 @@ -0,0 +1,45 @@ +# Test script para attachments endpoint +Write-Host "=== Testing Attachments Endpoint ===" -ForegroundColor Cyan + +# 1. Login +$body = '{"email":"admin@aduanasoft.com","password":"admin123","tenant_slug":"aduanasoft"}' +try { + $login = Invoke-RestMethod -Uri "http://localhost:8000/v1/auth/login" -Method POST -Body $body -ContentType "application/json" + Write-Host "[OK] Login exitoso" -ForegroundColor Green + + $token = $login.access_token + $tenantId = $login.user.tenant_id + + Write-Host "Token: $($token.Substring(0,20))..." -ForegroundColor Gray + Write-Host "Tenant ID: $tenantId" -ForegroundColor Gray + + # 2. Test attachments endpoint + $headers = @{ + "Authorization" = "Bearer $token" + "X-Tenant-ID" = $tenantId + } + + $url = "http://localhost:8000/v1/tickets/68eaf0fe-b5c3-4d42-9b36-895b439be583/attachments" + Write-Host "`nProbando GET $url" -ForegroundColor Yellow + + try { + $response = Invoke-WebRequest -Uri $url -Headers $headers -Method GET + Write-Host "[OK] Status Code: $($response.StatusCode)" -ForegroundColor Green + + $data = $response.Content | ConvertFrom-Json + Write-Host "[OK] Attachments encontrados: $($data.Count)" -ForegroundColor Green + + if ($data.Count -gt 0) { + $data | Format-Table id, original_filename, file_size + } else { + Write-Host " (No hay attachments para este ticket)" -ForegroundColor Gray + } + + } catch { + Write-Host "[ERROR] En request: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "Status Code: $($_.Exception.Response.StatusCode.value__)" -ForegroundColor Red + } + +} catch { + Write-Host "[ERROR] En login: $($_.Exception.Message)" -ForegroundColor Red +}