const jwt = require('jsonwebtoken'); const auth = require('../../middleware/auth'); describe('Auth Middleware', () => { let req, res, next; beforeEach(() => { req = { header: jest.fn(), }; res = { status: jest.fn().mockReturnThis(), json: jest.fn(), }; next = jest.fn(); }); describe('Valid Token', () => { it('should authenticate with valid token and call next()', () => { const userId = 'user123'; const token = jwt.sign( { user: { id: userId } }, process.env.JWT_SECRET, { expiresIn: 3600 } ); req.header.mockReturnValue(token); auth(req, res, next); expect(req.user).toBeDefined(); expect(req.user.id).toBe(userId); expect(next).toHaveBeenCalled(); expect(res.status).not.toHaveBeenCalled(); expect(res.json).not.toHaveBeenCalled(); }); it('should decode token with correct user data', () => { const userData = { id: 'user456' }; const token = jwt.sign( { user: userData }, process.env.JWT_SECRET, { expiresIn: 3600 } ); req.header.mockReturnValue(token); auth(req, res, next); expect(req.user).toEqual(userData); expect(next).toHaveBeenCalled(); }); }); describe('No Token', () => { it('should return 401 when no token is provided', () => { req.header.mockReturnValue(null); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'No token, authorization denied', }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 when token is undefined', () => { req.header.mockReturnValue(undefined); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'No token, authorization denied', }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 when token is empty string', () => { req.header.mockReturnValue(''); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'No token, authorization denied', }); expect(next).not.toHaveBeenCalled(); }); }); describe('Invalid Token', () => { it('should return 401 with malformed token', () => { req.header.mockReturnValue('invalid-token-format'); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'Token is not valid', }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 with expired token', () => { const expiredToken = jwt.sign( { user: { id: 'user123' } }, process.env.JWT_SECRET, { expiresIn: -1 } // Already expired ); req.header.mockReturnValue(expiredToken); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'Token is not valid', }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 with token signed with wrong secret', () => { const wrongToken = jwt.sign( { user: { id: 'user123' } }, 'wrong-secret', { expiresIn: 3600 } ); req.header.mockReturnValue(wrongToken); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'Token is not valid', }); expect(next).not.toHaveBeenCalled(); }); it('should return 401 with random string token', () => { req.header.mockReturnValue('randomstringnotajwt'); auth(req, res, next); expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'Token is not valid', }); expect(next).not.toHaveBeenCalled(); }); }); describe('Header Name', () => { it('should check for "x-auth-token" header', () => { req.header.mockReturnValue(null); auth(req, res, next); expect(req.header).toHaveBeenCalledWith('x-auth-token'); }); }); describe('Token Format', () => { it('should accept token with Bearer prefix if properly formatted', () => { // Note: The current middleware doesn't strip Bearer prefix // This test verifies current behavior const token = jwt.sign( { user: { id: 'user123' } }, process.env.JWT_SECRET, { expiresIn: 3600 } ); const bearerToken = `Bearer ${token}`; req.header.mockReturnValue(bearerToken); auth(req, res, next); // Will fail because middleware doesn't strip Bearer expect(res.status).toHaveBeenCalledWith(401); expect(res.json).toHaveBeenCalledWith({ success: false, msg: 'Token is not valid', }); }); it('should accept token without Bearer prefix', () => { const token = jwt.sign( { user: { id: 'user123' } }, process.env.JWT_SECRET, { expiresIn: 3600 } ); req.header.mockReturnValue(token); auth(req, res, next); expect(next).toHaveBeenCalled(); expect(res.status).not.toHaveBeenCalled(); }); }); describe('Request Mutation', () => { it('should add user object to request', () => { const userId = 'user789'; const token = jwt.sign( { user: { id: userId } }, process.env.JWT_SECRET, { expiresIn: 3600 } ); req.header.mockReturnValue(token); expect(req.user).toBeUndefined(); auth(req, res, next); expect(req.user).toBeDefined(); expect(req.user.id).toBe(userId); }); it('should not modify request when token is invalid', () => { req.header.mockReturnValue('invalid'); expect(req.user).toBeUndefined(); auth(req, res, next); expect(req.user).toBeUndefined(); }); }); describe('Multiple Token Formats', () => { it('should handle token with extra whitespace', () => { const token = jwt.sign( { user: { id: 'user123' } }, process.env.JWT_SECRET, { expiresIn: 3600 } ); // Token with leading/trailing spaces req.header.mockReturnValue(` ${token} `); auth(req, res, next); // Current middleware doesn't trim, so this will fail expect(res.status).toHaveBeenCalledWith(401); }); }); describe('Edge Cases', () => { it('should handle token with missing user data', () => { const token = jwt.sign( { someOtherData: 'value' }, process.env.JWT_SECRET, { expiresIn: 3600 } ); req.header.mockReturnValue(token); auth(req, res, next); // Middleware will accept token if valid, even without user field expect(next).toHaveBeenCalled(); expect(req.user).toBeUndefined(); }); it('should handle very long tokens', () => { const largePayload = { user: { id: 'user123', data: 'x'.repeat(10000), }, }; const token = jwt.sign(largePayload, process.env.JWT_SECRET, { expiresIn: 3600, }); req.header.mockReturnValue(token); auth(req, res, next); expect(next).toHaveBeenCalled(); expect(req.user.id).toBe('user123'); }); }); });