const { authenticateToken } = require('../../../middleware/auth'); const jwt = require('jsonwebtoken'); jest.mock('jsonwebtoken'); jest.mock('../../../models', () => ({ User: { findByPk: jest.fn() } })); const { User } = require('../../../models'); describe('Auth Middleware', () => { let req, res, next; beforeEach(() => { req = { cookies: {} }; res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; next = jest.fn(); jest.clearAllMocks(); process.env.JWT_SECRET = 'test-secret'; }); describe('Valid token', () => { it('should verify valid token from cookie and call next', async () => { const mockUser = { id: 1, email: 'test@test.com' }; req.cookies.accessToken = 'validtoken'; jwt.verify.mockReturnValue({ id: 1 }); User.findByPk.mockResolvedValue(mockUser); await authenticateToken(req, res, next); expect(jwt.verify).toHaveBeenCalledWith('validtoken', process.env.JWT_SECRET); expect(User.findByPk).toHaveBeenCalledWith(1); expect(req.user).toEqual(mockUser); expect(next).toHaveBeenCalled(); }); it('should handle token with valid user', async () => { const mockUser = { id: 2, email: 'user@test.com', firstName: 'Test' }; req.cookies.accessToken = 'validtoken2'; jwt.verify.mockReturnValue({ id: 2 }); User.findByPk.mockResolvedValue(mockUser); await authenticateToken(req, res, next); expect(jwt.verify).toHaveBeenCalledWith('validtoken2', process.env.JWT_SECRET); expect(User.findByPk).toHaveBeenCalledWith(2); expect(req.user).toEqual(mockUser); expect(next).toHaveBeenCalled(); }); }); describe('Invalid token', () => { it('should return 401 for missing token', async () => { req.cookies = {}; await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ error: 'Access token required', code: 'NO_TOKEN' }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 for invalid token', async () => { req.cookies.accessToken = 'invalidtoken'; jwt.verify.mockImplementation(() => { throw new Error('Invalid token'); }); await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(403); expect(res.json).toHaveBeenCalledWith({ error: 'Invalid token', code: 'INVALID_TOKEN' }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 for expired token', async () => { req.cookies.accessToken = 'expiredtoken'; const error = new Error('jwt expired'); error.name = 'TokenExpiredError'; jwt.verify.mockImplementation(() => { throw error; }); await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ error: 'Token expired', code: 'TOKEN_EXPIRED' }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 for invalid token format (missing user id)', async () => { req.cookies.accessToken = 'tokenwithnoid'; jwt.verify.mockReturnValue({ email: 'test@test.com' }); // Missing id await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ error: 'Invalid token format', code: 'INVALID_TOKEN_FORMAT' }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 when user not found', async () => { req.cookies.accessToken = 'validtoken'; jwt.verify.mockReturnValue({ id: 999 }); User.findByPk.mockResolvedValue(null); await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ error: 'User not found', code: 'USER_NOT_FOUND' }); expect(next).not.toHaveBeenCalled(); }); }); describe('Edge cases', () => { it('should handle empty string token', async () => { req.cookies.accessToken = ''; await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ error: 'Access token required', code: 'NO_TOKEN' }); }); it('should handle JWT malformed error', async () => { req.cookies.accessToken = 'malformed.token'; const error = new Error('jwt malformed'); error.name = 'JsonWebTokenError'; jwt.verify.mockImplementation(() => { throw error; }); await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(403); expect(res.json).toHaveBeenCalledWith({ error: 'Invalid token', code: 'INVALID_TOKEN' }); }); it('should handle database error when finding user', async () => { req.cookies.accessToken = 'validtoken'; jwt.verify.mockReturnValue({ id: 1 }); User.findByPk.mockRejectedValue(new Error('Database error')); await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(403); expect(res.json).toHaveBeenCalledWith({ error: 'Invalid token', code: 'INVALID_TOKEN' }); expect(next).not.toHaveBeenCalled(); }); it('should handle undefined cookies', async () => { req.cookies = undefined; await authenticateToken(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ error: 'Access token required', code: 'NO_TOKEN' }); }); }); });