Files
tasks-backend/__tests__/auth.test.js
2025-12-26 23:56:32 +02:00

317 lines
9.4 KiB
JavaScript

const request = require('supertest');
const express = require('express');
const { v4: uuidv4 } = require('uuid');
const authRoutes = require('../routes/auth.routes');
const { User, GuestSession, QuizSession, sequelize } = require('../models');
// Create Express app for testing
const app = express();
app.use(express.json());
app.use('/api/auth', authRoutes);
describe('Authentication Endpoints', () => {
let testUser;
let authToken;
beforeAll(async () => {
// Sync database
await sequelize.sync({ force: true });
});
afterAll(async () => {
// Clean up
await User.destroy({ where: {}, force: true });
await sequelize.close();
});
describe('POST /api/auth/register', () => {
it('should register a new user successfully', async () => {
const userData = {
username: 'testuser',
email: 'test@example.com',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(201);
expect(response.body.success).toBe(true);
expect(response.body.message).toBe('User registered successfully');
expect(response.body.data).toHaveProperty('user');
expect(response.body.data).toHaveProperty('token');
expect(response.body.data.user.email).toBe(userData.email);
expect(response.body.data.user.username).toBe(userData.username);
expect(response.body.data.user).not.toHaveProperty('password');
testUser = response.body.data.user;
authToken = response.body.data.token;
});
it('should reject registration with duplicate email', async () => {
const userData = {
username: 'anotheruser',
email: 'test@example.com', // Same email
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Email already registered');
});
it('should reject registration with duplicate username', async () => {
const userData = {
username: 'testuser', // Same username
email: 'another@example.com',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Username already taken');
});
it('should reject registration with invalid email', async () => {
const userData = {
username: 'newuser',
email: 'invalid-email',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Validation failed');
});
it('should reject registration with weak password', async () => {
const userData = {
username: 'newuser',
email: 'new@example.com',
password: 'weak'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Validation failed');
});
it('should reject registration with username too short', async () => {
const userData = {
username: 'ab', // Only 2 characters
email: 'new@example.com',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Validation failed');
});
it('should reject registration with invalid username characters', async () => {
const userData = {
username: 'test-user!', // Contains invalid characters
email: 'new@example.com',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Validation failed');
});
});
describe('POST /api/auth/register with guest migration', () => {
let guestSession;
beforeAll(async () => {
// Create a guest session with quiz data
guestSession = await GuestSession.create({
id: uuidv4(),
guest_id: `guest_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
session_token: 'test-guest-token',
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000),
max_quizzes: 3,
quizzes_attempted: 2,
is_converted: false
});
// Create quiz sessions for the guest
await QuizSession.create({
id: uuidv4(),
guest_session_id: guestSession.id,
category_id: uuidv4(),
quiz_type: 'practice',
difficulty: 'easy',
status: 'completed',
questions_count: 5,
questions_answered: 5,
correct_answers: 4,
score: 40,
percentage: 80,
is_passed: true,
started_at: new Date(),
completed_at: new Date()
});
});
it('should register user and migrate guest data', async () => {
const userData = {
username: 'guestconvert',
email: 'guestconvert@example.com',
password: 'Test@123',
guestSessionId: guestSession.guest_id
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(201);
expect(response.body.success).toBe(true);
expect(response.body.data).toHaveProperty('migratedData');
expect(response.body.data.migratedData).toHaveProperty('quizzes');
expect(response.body.data.migratedData).toHaveProperty('stats');
// Verify guest session is marked as converted
await guestSession.reload();
expect(guestSession.is_converted).toBe(true);
expect(guestSession.converted_user_id).toBe(response.body.data.user.id);
});
});
describe('POST /api/auth/login', () => {
it('should login with valid credentials', async () => {
const credentials = {
email: 'test@example.com',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/login')
.send(credentials)
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.message).toBe('Login successful');
expect(response.body.data).toHaveProperty('user');
expect(response.body.data).toHaveProperty('token');
expect(response.body.data.user).not.toHaveProperty('password');
});
it('should reject login with invalid email', async () => {
const credentials = {
email: 'nonexistent@example.com',
password: 'Test@123'
};
const response = await request(app)
.post('/api/auth/login')
.send(credentials)
.expect(401);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Invalid email or password');
});
it('should reject login with invalid password', async () => {
const credentials = {
email: 'test@example.com',
password: 'WrongPassword123'
};
const response = await request(app)
.post('/api/auth/login')
.send(credentials)
.expect(401);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Invalid email or password');
});
it('should reject login with missing fields', async () => {
const credentials = {
email: 'test@example.com'
// Missing password
};
const response = await request(app)
.post('/api/auth/login')
.send(credentials)
.expect(400);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('Validation failed');
});
});
describe('GET /api/auth/verify', () => {
it('should verify valid token', async () => {
const response = await request(app)
.get('/api/auth/verify')
.set('Authorization', `Bearer ${authToken}`)
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.message).toBe('Token valid');
expect(response.body.data).toHaveProperty('user');
expect(response.body.data.user.email).toBe('test@example.com');
});
it('should reject request without token', async () => {
const response = await request(app)
.get('/api/auth/verify')
.expect(401);
expect(response.body.success).toBe(false);
expect(response.body.message).toContain('No token provided');
});
it('should reject request with invalid token', async () => {
const response = await request(app)
.get('/api/auth/verify')
.set('Authorization', 'Bearer invalid-token')
.expect(401);
expect(response.body.success).toBe(false);
expect(response.body.message).toContain('Invalid token');
});
});
describe('POST /api/auth/logout', () => {
it('should logout successfully', async () => {
const response = await request(app)
.post('/api/auth/logout')
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.message).toContain('Logout successful');
});
});
});