'use strict'; /** * Admin Auth Controller Unit Tests * Tests for admin authentication endpoints */ const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); process.env.ADMIN_JWT_SECRET = 'test-secret-for-unit-tests'; // Mock dependencies before requiring the controller const mockPool = { query: jest.fn(), connect: jest.fn(() => ({ query: jest.fn(), release: jest.fn() })) }; jest.mock('../db', () => ({ getPool: jest.fn(() => mockPool) })); const authController = require('../controllers/auth.controller'); const { getPool } = require('../db'); describe('Admin Auth Controller', () => { let mockReq; let mockRes; let mockPool; beforeEach(() => { mockReq = { body: {}, user: {} }; mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis() }; mockPool = getPool(); jest.clearAllMocks(); }); describe('register', () => { it('should register a new admin successfully', async () => { mockReq.body = { username: 'admin1', email: 'admin@example.com', password: 'SecurePass123!', name: 'Admin User' }; // No existing admin mockPool.query.mockResolvedValueOnce({ rows: [] }); // Insert returns new admin const newAdmin = { id: 'admin-id-1', username: 'admin1', email: 'admin@example.com', name: 'Admin User', permissions: ['all'] }; mockPool.query.mockResolvedValueOnce({ rows: [newAdmin] }); await authController.register(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(201); expect(mockRes.json).toHaveBeenCalledWith( expect.objectContaining({ admin: expect.objectContaining({ email: 'admin@example.com', username: 'admin1' }), tokens: expect.objectContaining({ accessToken: expect.any(String), refreshToken: expect.any(String) }) }) ); }); it('should return 400 if admin already exists by email', async () => { mockReq.body = { username: 'newadmin', email: 'existing@example.com', password: 'pass123', name: 'New Admin' }; mockPool.query.mockResolvedValueOnce({ rows: [{ id: 'existing-id' }] }); await authController.register(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(400); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Admin already exists' }); }); it('should return 400 if admin already exists by username', async () => { mockReq.body = { username: 'existingadmin', email: 'new@example.com', password: 'pass123', name: 'New Admin' }; mockPool.query.mockResolvedValueOnce({ rows: [{ id: 'existing-id' }] }); await authController.register(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(400); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Admin already exists' }); }); it('should return 500 if database is not connected', async () => { mockReq.body = { username: 'admin', email: 'a@b.com', password: 'pass', name: 'Admin' }; require('../db').getPool.mockReturnValueOnce(null); await authController.register(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(500); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Database not connected' }); }); it('should return 500 on database error', async () => { mockReq.body = { username: 'admin', email: 'a@b.com', password: 'pass', name: 'Admin' }; mockPool.query.mockRejectedValueOnce(new Error('DB error')); await authController.register(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(500); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Failed to register admin' }); }); }); describe('login', () => { it('should login admin with valid credentials', async () => { const password = 'SecurePass123!'; const hashedPassword = await bcrypt.hash(password, 12); mockReq.body = { email: 'admin@example.com', password: password }; const admin = { id: 'admin-id-1', username: 'admin1', email: 'admin@example.com', password_hash: hashedPassword, name: 'Admin User', permissions: ['all'] }; mockPool.query.mockResolvedValueOnce({ rows: [admin] }); await authController.login(mockReq, mockRes); expect(mockRes.json).toHaveBeenCalledWith( expect.objectContaining({ admin: expect.objectContaining({ email: 'admin@example.com' }), tokens: expect.objectContaining({ accessToken: expect.any(String), refreshToken: expect.any(String) }) }) ); // Password hash should be removed from response expect(mockRes.json.mock.calls[0][0].admin.password_hash).toBeUndefined(); }); it('should return 401 if admin not found', async () => { mockReq.body = { email: 'nonexistent@example.com', password: 'password123' }; mockPool.query.mockResolvedValueOnce({ rows: [] }); await authController.login(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(401); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Invalid credentials' }); }); it('should return 401 for invalid password', async () => { mockReq.body = { email: 'admin@example.com', password: 'wrongpassword' }; const admin = { id: 'admin-id-1', email: 'admin@example.com', password_hash: await bcrypt.hash('correctpassword', 12) }; mockPool.query.mockResolvedValueOnce({ rows: [admin] }); await authController.login(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(401); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Invalid credentials' }); }); it('should return 500 if database is not connected', async () => { mockReq.body = { email: 'admin@example.com', password: 'pass' }; require('../db').getPool.mockReturnValueOnce(null); await authController.login(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(500); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Database not connected' }); }); it('should return 500 on database error', async () => { mockReq.body = { email: 'admin@example.com', password: 'pass' }; mockPool.query.mockRejectedValueOnce(new Error('DB error')); await authController.login(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(500); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Login failed' }); }); }); describe('getMe', () => { it('should return current admin profile', async () => { mockReq.user = { id: 'admin-id-1' }; const admin = { id: 'admin-id-1', username: 'admin1', email: 'admin@example.com', name: 'Admin User', permissions: ['all'] }; mockPool.query.mockResolvedValueOnce({ rows: [admin] }); await authController.getMe(mockReq, mockRes); expect(mockRes.json).toHaveBeenCalledWith({ admin }); }); it('should return 404 if admin not found', async () => { mockReq.user = { id: 'nonexistent-id' }; mockPool.query.mockResolvedValueOnce({ rows: [] }); await authController.getMe(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(404); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Admin not found' }); }); it('should return 500 on database error', async () => { mockReq.user = { id: 'admin-id-1' }; mockPool.query.mockRejectedValueOnce(new Error('DB error')); await authController.getMe(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(500); expect(mockRes.json).toHaveBeenCalledWith({ error: 'Failed to fetch profile' }); }); }); });