import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { createToken, verifyToken, decodeToken, isTokenExpired, getTokenExpiresIn } from '../../server/utils/jwt-utils.js'; describe('JWT Utilities', () => { const testSecret = 'test-secret-key'; let token; beforeEach(() => { // Create a test token token = createToken( { userId: '123', isAdmin: true, permissions: ['admin'] }, testSecret, 3600 // 1 hour ); }); describe('createToken', () => { it('creates a valid token', () => { expect(token).toBeDefined(); expect(typeof token).toBe('string'); expect(token.split('.')).toHaveLength(3); }); it('includes payload data', () => { const decoded = decodeToken(token); expect(decoded.userId).toBe('123'); expect(decoded.isAdmin).toBe(true); expect(decoded.permissions).toContain('admin'); }); it('includes timestamps', () => { const decoded = decodeToken(token); expect(decoded.iat).toBeDefined(); expect(decoded.exp).toBeDefined(); expect(decoded.exp).toBeGreaterThan(decoded.iat); }); it('respects custom expiration time', () => { const shortToken = createToken( { userId: '123' }, testSecret, 60 // 1 minute ); const longToken = createToken( { userId: '123' }, testSecret, 7200 // 2 hours ); const shortDecoded = decodeToken(shortToken); const longDecoded = decodeToken(longToken); expect(longDecoded.exp - longDecoded.iat).toBeGreaterThan( shortDecoded.exp - shortDecoded.iat ); }); }); describe('verifyToken', () => { it('verifies a valid token', () => { const decoded = verifyToken(token, testSecret); expect(decoded.userId).toBe('123'); expect(decoded.isAdmin).toBe(true); }); it('throws on invalid secret', () => { expect(() => { verifyToken(token, 'wrong-secret'); }).toThrow(); }); it('throws on malformed token', () => { expect(() => { verifyToken('not.a.token', testSecret); }).toThrow(); }); it('throws on expired token', () => { // Create an already expired token const expiredToken = createToken( { userId: '123' }, testSecret, -1 // Already expired ); expect(() => { verifyToken(expiredToken, testSecret); }).toThrow(); }); }); describe('decodeToken', () => { it('decodes token without verification', () => { const decoded = decodeToken(token); expect(decoded.userId).toBe('123'); expect(decoded.isAdmin).toBe(true); }); it('returns null for invalid token', () => { const result = decodeToken('invalid'); expect(result).toBeNull(); }); }); describe('isTokenExpired', () => { it('returns false for valid token', () => { expect(isTokenExpired(token)).toBe(false); }); it('returns true for expired token', () => { const expiredToken = createToken({ userId: '123' }, testSecret, -1); expect(isTokenExpired(expiredToken)).toBe(true); }); it('returns true for invalid token', () => { expect(isTokenExpired('invalid')).toBe(true); }); }); describe('getTokenExpiresIn', () => { it('returns remaining time in milliseconds', () => { const remaining = getTokenExpiresIn(token); expect(remaining).toBeGreaterThan(0); expect(remaining).toBeLessThanOrEqual(3600000); // 1 hour in ms }); it('returns 0 for expired token', () => { const expiredToken = createToken({ userId: '123' }, testSecret, -1); expect(getTokenExpiresIn(expiredToken)).toBe(0); }); it('returns 0 for invalid token', () => { expect(getTokenExpiresIn('invalid')).toBe(0); }); }); });