317 lines
9.4 KiB
JavaScript
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');
|
|
});
|
|
});
|
|
});
|