Some checks failed
CI/CD Pipeline / 🧪 Tests (push) Has been cancelled
CI/CD Pipeline / 🏗️ Build (push) Has been cancelled
CI/CD Pipeline / 🚀 Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / 🚀 Deploy to Production (push) Has been cancelled
CI/CD Pipeline / 🏷️ Create Release (push) Has been cancelled
CI/CD Pipeline / 🧹 Cleanup (push) Has been cancelled
Implementados 4 módulos con agent swarm: 1. TESTING FUNCIONAL (Jest) - Configuración Jest + ts-jest - Tests unitarios: auth, booking, court (55 tests) - Tests integración: routes (56 tests) - Factories y utilidades de testing - Coverage configurado (70% servicios) - Scripts: test, test:watch, test:coverage 2. TESTING DE USUARIO (Beta) - Sistema de beta testers - Feedback con categorías y severidad - Beta issues tracking - 8 testers de prueba creados - API completa para gestión de feedback 3. DOCUMENTACIÓN COMPLETA - API.md - 150+ endpoints documentados - SETUP.md - Guía de instalación - DEPLOY.md - Deploy en VPS - ARCHITECTURE.md - Arquitectura del sistema - APP_STORE.md - Material para stores - Postman Collection completa - PM2 ecosystem config - Nginx config con SSL 4. GO LIVE Y PRODUCCIÓN - Sistema de monitoreo (logs, health checks) - Servicio de alertas multi-canal - Pre-deploy check script - Docker + docker-compose producción - Backup automatizado - CI/CD GitHub Actions - Launch checklist completo ESTADÍSTICAS FINALES: - Fases completadas: 7/7 - Archivos creados: 250+ - Líneas de código: 60,000+ - Endpoints API: 150+ - Tests: 110+ - Documentación: 5,000+ líneas PROYECTO COMPLETO Y LISTO PARA PRODUCCIÓN
305 lines
9.6 KiB
TypeScript
305 lines
9.6 KiB
TypeScript
import request from 'supertest';
|
|
import app from '../../../src/app';
|
|
import { setupTestDb, teardownTestDb, resetDatabase } from '../../utils/testDb';
|
|
import { createUser } from '../../utils/factories';
|
|
import { generateTokens } from '../../utils/auth';
|
|
|
|
describe('Auth Routes Integration Tests', () => {
|
|
beforeAll(async () => {
|
|
await setupTestDb();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await teardownTestDb();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await resetDatabase();
|
|
});
|
|
|
|
describe('POST /api/v1/auth/register', () => {
|
|
const validRegisterData = {
|
|
email: 'test@example.com',
|
|
password: 'Password123!',
|
|
firstName: 'Test',
|
|
lastName: 'User',
|
|
phone: '+1234567890',
|
|
playerLevel: 'BEGINNER',
|
|
handPreference: 'RIGHT',
|
|
positionPreference: 'BOTH',
|
|
};
|
|
|
|
it('should register a new user successfully', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/register')
|
|
.send(validRegisterData);
|
|
|
|
// Assert
|
|
expect(response.status).toBe(201);
|
|
expect(response.body).toHaveProperty('success', true);
|
|
expect(response.body.data).toHaveProperty('user');
|
|
expect(response.body.data).toHaveProperty('accessToken');
|
|
expect(response.body.data).toHaveProperty('refreshToken');
|
|
expect(response.body.data.user).toHaveProperty('email', validRegisterData.email);
|
|
expect(response.body.data.user).toHaveProperty('firstName', validRegisterData.firstName);
|
|
expect(response.body.data.user).not.toHaveProperty('password');
|
|
});
|
|
|
|
it('should return 409 when email already exists', async () => {
|
|
// Arrange
|
|
await createUser({ email: validRegisterData.email });
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/register')
|
|
.send(validRegisterData);
|
|
|
|
// Assert
|
|
expect(response.status).toBe(409);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
expect(response.body).toHaveProperty('message', 'El email ya está registrado');
|
|
});
|
|
|
|
it('should return 400 when email is invalid', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/register')
|
|
.send({ ...validRegisterData, email: 'invalid-email' });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(400);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
});
|
|
|
|
it('should return 400 when password is too short', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/register')
|
|
.send({ ...validRegisterData, password: '123' });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(400);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
});
|
|
|
|
it('should return 400 when required fields are missing', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/register')
|
|
.send({ email: 'test@example.com', password: 'Password123!' });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(400);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
});
|
|
});
|
|
|
|
describe('POST /api/v1/auth/login', () => {
|
|
const validLoginData = {
|
|
email: 'login@example.com',
|
|
password: 'Password123!',
|
|
};
|
|
|
|
it('should login with valid credentials', async () => {
|
|
// Arrange
|
|
await createUser({
|
|
email: validLoginData.email,
|
|
password: validLoginData.password,
|
|
firstName: 'Login',
|
|
lastName: 'Test',
|
|
});
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/login')
|
|
.send(validLoginData);
|
|
|
|
// Assert
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toHaveProperty('success', true);
|
|
expect(response.body.data).toHaveProperty('user');
|
|
expect(response.body.data).toHaveProperty('accessToken');
|
|
expect(response.body.data).toHaveProperty('refreshToken');
|
|
expect(response.body.data.user).toHaveProperty('email', validLoginData.email);
|
|
expect(response.body.data.user).not.toHaveProperty('password');
|
|
});
|
|
|
|
it('should return 401 with invalid password', async () => {
|
|
// Arrange
|
|
await createUser({
|
|
email: validLoginData.email,
|
|
password: validLoginData.password,
|
|
});
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/login')
|
|
.send({ ...validLoginData, password: 'WrongPassword123!' });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(401);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
expect(response.body).toHaveProperty('message', 'Email o contraseña incorrectos');
|
|
});
|
|
|
|
it('should return 401 when user not found', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/login')
|
|
.send({ email: 'nonexistent@example.com', password: 'Password123!' });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(401);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
expect(response.body).toHaveProperty('message', 'Email o contraseña incorrectos');
|
|
});
|
|
|
|
it('should return 401 when user is inactive', async () => {
|
|
// Arrange
|
|
await createUser({
|
|
email: validLoginData.email,
|
|
password: validLoginData.password,
|
|
isActive: false,
|
|
});
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/login')
|
|
.send(validLoginData);
|
|
|
|
// Assert
|
|
expect(response.status).toBe(401);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
expect(response.body).toHaveProperty('message', 'Usuario desactivado');
|
|
});
|
|
|
|
it('should return 400 with invalid email format', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/login')
|
|
.send({ email: 'invalid-email', password: 'Password123!' });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(400);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
});
|
|
});
|
|
|
|
describe('GET /api/v1/auth/me', () => {
|
|
it('should return user profile when authenticated', async () => {
|
|
// Arrange
|
|
const user = await createUser({
|
|
email: 'profile@example.com',
|
|
firstName: 'Profile',
|
|
lastName: 'User',
|
|
});
|
|
const { accessToken } = generateTokens({
|
|
userId: user.id,
|
|
email: user.email,
|
|
role: user.role,
|
|
});
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.get('/api/v1/auth/me')
|
|
.set('Authorization', `Bearer ${accessToken}`);
|
|
|
|
// Assert
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toHaveProperty('success', true);
|
|
expect(response.body.data).toHaveProperty('id', user.id);
|
|
expect(response.body.data).toHaveProperty('email', user.email);
|
|
expect(response.body.data).toHaveProperty('firstName', 'Profile');
|
|
expect(response.body.data).not.toHaveProperty('password');
|
|
});
|
|
|
|
it('should return 401 when no token provided', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.get('/api/v1/auth/me');
|
|
|
|
// Assert
|
|
expect(response.status).toBe(401);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
expect(response.body).toHaveProperty('message', 'Token de autenticación no proporcionado');
|
|
});
|
|
|
|
it('should return 401 with invalid token', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.get('/api/v1/auth/me')
|
|
.set('Authorization', 'Bearer invalid-token');
|
|
|
|
// Assert
|
|
expect(response.status).toBe(401);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
});
|
|
});
|
|
|
|
describe('POST /api/v1/auth/refresh', () => {
|
|
it('should refresh access token with valid refresh token', async () => {
|
|
// Arrange
|
|
const user = await createUser({ email: 'refresh@example.com' });
|
|
const { refreshToken } = generateTokens({
|
|
userId: user.id,
|
|
email: user.email,
|
|
role: user.role,
|
|
});
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/refresh')
|
|
.send({ refreshToken });
|
|
|
|
// Assert
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toHaveProperty('success', true);
|
|
expect(response.body.data).toHaveProperty('accessToken');
|
|
});
|
|
|
|
it('should return 400 when refresh token is missing', async () => {
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/refresh')
|
|
.send({});
|
|
|
|
// Assert
|
|
expect(response.status).toBe(400);
|
|
expect(response.body).toHaveProperty('success', false);
|
|
});
|
|
});
|
|
|
|
describe('POST /api/v1/auth/logout', () => {
|
|
it('should logout successfully', async () => {
|
|
// Arrange
|
|
const user = await createUser({ email: 'logout@example.com' });
|
|
const { accessToken } = generateTokens({
|
|
userId: user.id,
|
|
email: user.email,
|
|
role: user.role,
|
|
});
|
|
|
|
// Act
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/logout')
|
|
.set('Authorization', `Bearer ${accessToken}`);
|
|
|
|
// Assert
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toHaveProperty('success', true);
|
|
expect(response.body).toHaveProperty('message', 'Logout exitoso');
|
|
});
|
|
|
|
it('should allow logout without authentication', async () => {
|
|
// Act - logout endpoint doesn't require authentication
|
|
const response = await request(app)
|
|
.post('/api/v1/auth/logout');
|
|
|
|
// Assert - logout is allowed without auth (just returns success)
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toHaveProperty('success', true);
|
|
});
|
|
});
|
|
});
|