const request = require('supertest'); const express = require('express'); // Mock CouchDB service before importing routes jest.mock('../../services/couchdbService', () => ({ initialize: jest.fn().mockResolvedValue(true), create: jest.fn(), getById: jest.fn(), find: jest.fn(), createDocument: jest.fn(), updateDocument: jest.fn(), deleteDocument: jest.fn(), findByType: jest.fn(), findUserById: jest.fn(), update: jest.fn(), })); const authRoutes = require('../../routes/auth'); const User = require('../../models/User'); const couchdbService = require('../../services/couchdbService'); const { createTestUser } = require('../utils/testHelpers'); // Mock User.findOne method for login tests jest.spyOn(User, 'findOne'); // Create Express app for testing const app = express(); app.use(express.json()); app.use('/api/auth', authRoutes); describe('Auth Routes', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('POST /api/auth/register', () => { it('should register a new user and return a token', async () => { const userData = { name: 'John Doe', email: 'john@example.com', password: 'Password123', }; // Mock CouchDB responses couchdbService.findUserByEmail.mockResolvedValue(null); const mockCreatedUser = { _id: 'user_123', _rev: '1-abc', type: 'user', ...userData, password: '$2a$10$hashedpassword', isPremium: false, points: 0, adoptedStreets: [], completedTasks: [], posts: [], events: [], earnedBadges: [], stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 }, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; couchdbService.createDocument.mockResolvedValue(mockCreatedUser); const response = await request(app) .post('/api/auth/register') .send(userData) .expect(200); expect(response.body).toHaveProperty('token'); expect(typeof response.body.token).toBe('string'); expect(response.body.success).toBe(true); // Verify CouchDB service was called correctly expect(couchdbService.findUserByEmail).toHaveBeenCalledWith(userData.email); expect(couchdbService.createDocument).toHaveBeenCalled(); }); it('should not register a user with an existing email', async () => { const existingUserData = { _id: 'user_123', _rev: '1-abc', type: 'user', email: 'existing@example.com', name: 'Existing User', password: '$2a$10$hashedpassword', isPremium: false, points: 0, adoptedStreets: [], completedTasks: [], posts: [], events: [], earnedBadges: [], stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 }, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; const existingUser = new User(existingUserData); couchdbService.findUserByEmail.mockResolvedValue(existingUser.toJSON()); const userData = { name: 'Jane Doe', email: 'existing@example.com', password: 'Password123', }; const response = await request(app) .post('/api/auth/register') .send(userData) .expect(400); expect(response.body).toHaveProperty('msg', 'User already exists'); expect(response.body.success).toBe(false); }); it('should handle missing required fields', async () => { const response = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com' }) .expect(400); expect(response.body).toBeDefined(); expect(response.body.success).toBe(false); expect(response.body.errors).toBeDefined(); }); }); describe('POST /api/auth/login', () => { beforeEach(() => { jest.clearAllMocks(); }); it('should login with valid credentials and return a token', async () => { const mockUserData = { _id: 'user_123', _rev: '1-abc', type: 'user', name: 'Test User', email: 'login@example.com', password: '$2a$10$hashedpassword', isPremium: false, points: 0, adoptedStreets: [], completedTasks: [], posts: [], events: [], earnedBadges: [], stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 }, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; const mockUser = new User(mockUserData); jest.spyOn(mockUser, 'comparePassword').mockResolvedValue(true); User.findOne.mockResolvedValue(mockUser); const loginData = { email: 'login@example.com', password: 'Password123', }; const response = await request(app) .post('/api/auth/login') .send(loginData) .expect(200); expect(response.body).toHaveProperty('token'); expect(typeof response.body.token).toBe('string'); expect(response.body.success).toBe(true); }); it('should not login with invalid email', async () => { User.findOne.mockResolvedValue(null); const loginData = { email: 'nonexistent@example.com', password: 'password123', }; const response = await request(app) .post('/api/auth/login') .send(loginData) .expect(400); expect(response.body).toHaveProperty('msg', 'Invalid credentials'); expect(response.body.success).toBe(false); }); it('should not login with invalid password', async () => { const mockUserData = { _id: 'user_123', _rev: '1-abc', type: 'user', name: 'Test User', email: 'login@example.com', password: '$2a$10$hashedpassword', isPremium: false, points: 0, adoptedStreets: [], completedTasks: [], posts: [], events: [], earnedBadges: [], stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 }, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; const mockUser = new User(mockUserData); jest.spyOn(mockUser, 'comparePassword').mockResolvedValue(false); User.findOne.mockResolvedValue(mockUser); const loginData = { email: 'login@example.com', password: 'WrongPassword123', }; const response = await request(app) .post('/api/auth/login') .send(loginData) .expect(400); expect(response.body).toHaveProperty('msg', 'Invalid credentials'); expect(response.body.success).toBe(false); }); it('should handle missing email or password', async () => { const response = await request(app) .post('/api/auth/login') .send({ email: 'test@example.com' }) .expect(400); expect(response.body).toBeDefined(); expect(response.body.success).toBe(false); expect(response.body.errors).toBeDefined(); }); }); describe('GET /api/auth', () => { it('should get authenticated user with valid token', async () => { const mockUserData = { _id: 'user_123', _rev: '1-abc', type: 'user', name: 'Test User', email: 'test@example.com', password: '$2a$10$hashedpassword', isPremium: false, points: 0, adoptedStreets: [], completedTasks: [], posts: [], events: [], earnedBadges: [], stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 }, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; const mockUser = new User(mockUserData); couchdbService.findUserById.mockResolvedValue(mockUser.toJSON()); // Create a valid token const jwt = require('jsonwebtoken'); const token = jwt.sign( { user: { id: 'user_123' } }, process.env.JWT_SECRET, { expiresIn: 3600 } ); const response = await request(app) .get('/api/auth') .set('x-auth-token', token) .expect(200); expect(response.body).toHaveProperty('_id', 'user_123'); expect(response.body).toHaveProperty('name', 'Test User'); expect(response.body).toHaveProperty('email', 'test@example.com'); expect(response.body).not.toHaveProperty('password'); }); it('should reject request without token', async () => { const response = await request(app) .get('/api/auth') .expect(401); expect(response.body).toHaveProperty('msg', 'No token, authorization denied'); }); it('should reject request with invalid token', async () => { const response = await request(app) .get('/api/auth') .set('x-auth-token', 'invalid-token') .expect(401); expect(response.body).toHaveProperty('msg', 'Token is not valid'); }); }); });