🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
✨ Características: - 45 ejercicios universitarios (Basic → Advanced) - Renderizado LaTeX profesional - IA generativa (Z.ai/DashScope) - Docker 9 servicios - Tests 123/123 pasando - Seguridad enterprise (JWT, XSS, Rate limiting) 🐳 Infraestructura: - Next.js 14 + Node.js 20 - PostgreSQL 15 + Redis 7 - Docker Compose completo - Nginx + SSL ready 📚 Documentación: - 5 informes técnicos completos - README profesional - Scripts de deployment automatizados Estado: Producción lista ✅
This commit is contained in:
265
backend/tests/integration/auth.integration.test.ts
Normal file
265
backend/tests/integration/auth.integration.test.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* Integration Tests - Auth API
|
||||
*
|
||||
* Tests for:
|
||||
* - User registration
|
||||
* - User login
|
||||
* - JWT token validation
|
||||
* - Protected routes
|
||||
* - Password strength validation
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import express, { ErrorRequestHandler } from 'express';
|
||||
import { prisma } from '../../src/shared/database/prisma.client';
|
||||
import { authRoutes } from '../../src/modules/auth/auth.routes';
|
||||
|
||||
// Simple error handler for tests
|
||||
const testErrorHandler: ErrorRequestHandler = (err, req, res, _next) => {
|
||||
const statusCode = err.statusCode || err.status || 500;
|
||||
const code = err.code || 'INTERNAL_ERROR';
|
||||
const message = err.message || 'An unexpected error occurred';
|
||||
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
error: {
|
||||
code,
|
||||
message,
|
||||
},
|
||||
meta: {
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
describe('Auth API Integration', () => {
|
||||
let app: express.Application;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Ensure test database is clean
|
||||
await prisma.$connect();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
// Clean up test data before each test
|
||||
await prisma.refreshToken.deleteMany({});
|
||||
await prisma.passwordResetToken.deleteMany({});
|
||||
await prisma.exerciseAttempt.deleteMany({});
|
||||
await prisma.progress.deleteMany({});
|
||||
await prisma.user.deleteMany({
|
||||
where: {
|
||||
email: {
|
||||
contains: 'test@',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Create fresh app instance
|
||||
app = express();
|
||||
app.use(express.json());
|
||||
app.use('/api/auth', authRoutes);
|
||||
|
||||
// Add error handler last
|
||||
app.use(testErrorHandler);
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// REGISTRATION TESTS
|
||||
// ============================================
|
||||
|
||||
describe('POST /api/auth/register', () => {
|
||||
it('should register a new user successfully', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'test@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'testuser',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('token');
|
||||
expect(response.body.data.user).toHaveProperty('id');
|
||||
expect(response.body.data.user.email).toBe('test@example.com');
|
||||
expect(response.body.data.user).not.toHaveProperty('passwordHash');
|
||||
});
|
||||
|
||||
it('should reject weak passwords', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'test2@example.com',
|
||||
password: '123',
|
||||
username: 'testuser2',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.success).toBe(false);
|
||||
});
|
||||
|
||||
it('should reject duplicate emails', async () => {
|
||||
// Create first user
|
||||
await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'duplicate@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'user1',
|
||||
});
|
||||
|
||||
// Try to create second user with same email
|
||||
const response = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'duplicate@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'user2',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(409);
|
||||
expect(response.body.success).toBe(false);
|
||||
});
|
||||
|
||||
it('should reject duplicate usernames', async () => {
|
||||
// Create first user
|
||||
await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'user1@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'uniqueuser',
|
||||
});
|
||||
|
||||
// Try to create second user with same username
|
||||
const response = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'user2@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'uniqueuser',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(409);
|
||||
expect(response.body.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// LOGIN TESTS
|
||||
// ============================================
|
||||
|
||||
describe('POST /api/auth/login', () => {
|
||||
beforeEach(async () => {
|
||||
// Register a user for login tests
|
||||
await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'login@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'loginuser',
|
||||
});
|
||||
});
|
||||
|
||||
it('should login with correct credentials', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/auth/login')
|
||||
.send({
|
||||
email: 'login@example.com',
|
||||
password: 'SecurePass123!',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('token');
|
||||
expect(response.body.data.user.email).toBe('login@example.com');
|
||||
});
|
||||
|
||||
it('should reject incorrect password', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/auth/login')
|
||||
.send({
|
||||
email: 'login@example.com',
|
||||
password: 'WrongPass123!',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body.success).toBe(false);
|
||||
});
|
||||
|
||||
it('should reject non-existent user', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/auth/login')
|
||||
.send({
|
||||
email: 'nonexistent@example.com',
|
||||
password: 'SecurePass123!',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// PROFILE TESTS
|
||||
// ============================================
|
||||
|
||||
describe('GET /api/auth/profile', () => {
|
||||
let authToken: string;
|
||||
let userId: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Clean up any existing profile test user
|
||||
await prisma.refreshToken.deleteMany({});
|
||||
await prisma.exerciseAttempt.deleteMany({});
|
||||
await prisma.progress.deleteMany({});
|
||||
await prisma.user.deleteMany({
|
||||
where: {
|
||||
email: 'profile@example.com',
|
||||
},
|
||||
});
|
||||
|
||||
const registerResponse = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
email: 'profile@example.com',
|
||||
password: 'SecurePass123!',
|
||||
username: 'profileuser',
|
||||
});
|
||||
|
||||
authToken = registerResponse.body.data.token;
|
||||
userId = registerResponse.body.data.user.id;
|
||||
});
|
||||
|
||||
it('should get user profile with valid token', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/auth/me')
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.id).toBe(userId);
|
||||
expect(response.body.data.email).toBe('profile@example.com');
|
||||
});
|
||||
|
||||
it('should reject request without token', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/auth/me');
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should reject request with invalid token', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/auth/me')
|
||||
.set('Authorization', 'Bearer invalid-token');
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user