const request = require('supertest'); const app = require('../server'); const { sequelize } = require('../models'); describe('Integration Tests - Complete User Flow', () => { let server; const testUser = { username: 'integrationtest', email: 'integration@test.com', password: 'Test123!@#' }; let userToken = null; let adminToken = null; beforeAll(async () => { // Start server server = app.listen(0); // Login as admin for setup const adminRes = await request(app) .post('/api/auth/login') .send({ email: 'admin@example.com', password: 'Admin123!@#' }); if (adminRes.status === 200) { adminToken = adminRes.body.data.token; } }); afterAll(async () => { // Close server and database connections if (server) { await new Promise(resolve => server.close(resolve)); } await sequelize.close(); }); describe('1. User Registration Flow', () => { it('should register a new user successfully', async () => { const res = await request(app) .post('/api/auth/register') .send(testUser) .expect(201); expect(res.body.success).toBe(true); expect(res.body.data.user.username).toBe(testUser.username); expect(res.body.data.user.email).toBe(testUser.email); expect(res.body.data.token).toBeDefined(); userToken = res.body.data.token; }); it('should not register user with duplicate email', async () => { const res = await request(app) .post('/api/auth/register') .send(testUser) .expect(409); expect(res.body.success).toBe(false); expect(res.body.message).toContain('already'); }); it('should not register user with invalid email', async () => { const res = await request(app) .post('/api/auth/register') .send({ username: 'testuser2', email: 'invalid-email', password: 'Test123!@#' }) .expect(400); expect(res.body.success).toBe(false); }); it('should not register user with weak password', async () => { const res = await request(app) .post('/api/auth/register') .send({ username: 'testuser3', email: 'test3@example.com', password: '123' }) .expect(400); expect(res.body.success).toBe(false); }); }); describe('2. User Login Flow', () => { it('should login with correct credentials', async () => { const res = await request(app) .post('/api/auth/login') .send({ email: testUser.email, password: testUser.password }) .expect(200); expect(res.body.success).toBe(true); expect(res.body.data.token).toBeDefined(); expect(res.body.data.user.email).toBe(testUser.email); }); it('should not login with incorrect password', async () => { const res = await request(app) .post('/api/auth/login') .send({ email: testUser.email, password: 'WrongPassword123' }) .expect(401); expect(res.body.success).toBe(false); }); it('should not login with non-existent email', async () => { const res = await request(app) .post('/api/auth/login') .send({ email: 'nonexistent@example.com', password: 'Test123!@#' }) .expect(401); expect(res.body.success).toBe(false); }); }); describe('3. Token Verification Flow', () => { it('should verify valid token', async () => { const res = await request(app) .get('/api/auth/verify') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(res.body.data.user).toBeDefined(); }); it('should reject invalid token', async () => { const res = await request(app) .get('/api/auth/verify') .set('Authorization', 'Bearer invalid-token') .expect(401); expect(res.body.success).toBe(false); }); it('should reject request without token', async () => { const res = await request(app) .get('/api/auth/verify') .expect(401); expect(res.body.success).toBe(false); }); }); describe('4. Complete Quiz Flow', () => { let quizSessionId = null; let categoryId = null; it('should get available categories', async () => { const res = await request(app) .get('/api/categories') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(Array.isArray(res.body.data)).toBe(true); if (res.body.data.length > 0) { categoryId = res.body.data[0].id; } }); it('should start a quiz session', async () => { if (!categoryId) { console.log('Skipping quiz tests - no categories available'); return; } const res = await request(app) .post('/api/quiz/start') .set('Authorization', `Bearer ${userToken}`) .send({ categoryId: categoryId, quizType: 'practice', difficulty: 'easy', questionCount: 5 }) .expect(201); expect(res.body.success).toBe(true); expect(res.body.data.sessionId).toBeDefined(); quizSessionId = res.body.data.sessionId; }); it('should get current quiz session', async () => { if (!quizSessionId) { return; } const res = await request(app) .get(`/api/quiz/session/${quizSessionId}`) .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(res.body.data.id).toBe(quizSessionId); }); it('should submit an answer', async () => { if (!quizSessionId) { return; } // Get the first question const sessionRes = await request(app) .get(`/api/quiz/session/${quizSessionId}`) .set('Authorization', `Bearer ${userToken}`); const questions = sessionRes.body.data.questions; if (questions && questions.length > 0) { const questionId = questions[0].id; const correctOption = questions[0].correctOption; const res = await request(app) .post(`/api/quiz/session/${quizSessionId}/answer`) .set('Authorization', `Bearer ${userToken}`) .send({ questionId: questionId, selectedOption: correctOption }) .expect(200); expect(res.body.success).toBe(true); } }); it('should complete quiz session', async () => { if (!quizSessionId) { return; } const res = await request(app) .post(`/api/quiz/session/${quizSessionId}/complete`) .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(res.body.data.status).toBe('completed'); }); }); describe('5. Authorization Scenarios', () => { it('should allow authenticated user to access protected route', async () => { const res = await request(app) .get('/api/user/profile') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); }); it('should deny unauthenticated access to protected route', async () => { const res = await request(app) .get('/api/user/profile') .expect(401); expect(res.body.success).toBe(false); }); it('should deny non-admin access to admin route', async () => { const res = await request(app) .get('/api/admin/statistics') .set('Authorization', `Bearer ${userToken}`) .expect(403); expect(res.body.success).toBe(false); }); it('should allow admin access to admin route', async () => { if (!adminToken) { console.log('Skipping admin test - admin not logged in'); return; } const res = await request(app) .get('/api/admin/statistics') .set('Authorization', `Bearer ${adminToken}`) .expect(200); expect(res.body.success).toBe(true); }); }); describe('6. Guest User Flow', () => { let guestToken = null; let guestId = null; it('should create guest session', async () => { const res = await request(app) .post('/api/guest/session') .expect(201); expect(res.body.success).toBe(true); expect(res.body.data.token).toBeDefined(); expect(res.body.data.guestId).toBeDefined(); guestToken = res.body.data.token; guestId = res.body.data.guestId; }); it('should allow guest to access public categories', async () => { const res = await request(app) .get('/api/guest/categories') .set('Authorization', `Bearer ${guestToken}`) .expect(200); expect(res.body.success).toBe(true); }); it('should convert guest to registered user', async () => { if (!guestToken) { return; } const res = await request(app) .post('/api/guest/convert') .set('Authorization', `Bearer ${guestToken}`) .send({ username: 'convertedguest', email: 'converted@guest.com', password: 'Test123!@#' }) .expect(201); expect(res.body.success).toBe(true); expect(res.body.data.user).toBeDefined(); expect(res.body.data.token).toBeDefined(); }); }); describe('7. Error Handling Scenarios', () => { it('should return 404 for non-existent route', async () => { const res = await request(app) .get('/api/nonexistent') .expect(404); expect(res.body.success).toBe(false); }); it('should handle malformed JSON', async () => { const res = await request(app) .post('/api/auth/login') .set('Content-Type', 'application/json') .send('invalid json{') .expect(400); }); it('should validate required fields', async () => { const res = await request(app) .post('/api/auth/register') .send({ username: 'test' // missing email and password }) .expect(400); expect(res.body.success).toBe(false); }); it('should handle database errors gracefully', async () => { // Try to access non-existent resource const res = await request(app) .get('/api/quiz/session/00000000-0000-0000-0000-000000000000') .set('Authorization', `Bearer ${userToken}`) .expect(404); expect(res.body.success).toBe(false); }); }); describe('8. User Profile Flow', () => { it('should get user profile', async () => { const res = await request(app) .get('/api/user/profile') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(res.body.data.username).toBe(testUser.username); }); it('should get user statistics', async () => { const res = await request(app) .get('/api/user/statistics') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(res.body.data.totalQuizzesTaken).toBeDefined(); }); it('should get quiz history', async () => { const res = await request(app) .get('/api/user/quiz-history') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); expect(Array.isArray(res.body.data.sessions)).toBe(true); }); }); describe('9. Logout Flow', () => { it('should logout successfully', async () => { const res = await request(app) .post('/api/auth/logout') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); }); it('should not access protected route after logout', async () => { // Note: JWT tokens are stateless, so this test depends on token expiration // In a real scenario with token blacklisting, this would fail // For now, we just verify the logout endpoint works const res = await request(app) .post('/api/auth/logout') .set('Authorization', `Bearer ${userToken}`) .expect(200); expect(res.body.success).toBe(true); }); }); });