import { beforeEach, describe, expect, it, vi } from 'vitest'; import { decodeJwtPayload, isJwtExpired } from '../../src/auth/jwt'; function createJwt(payload: Record) { const header = { alg: 'HS256', typ: 'JWT' }; const encode = (value: object) => btoa(JSON.stringify(value)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); return `${encode(header)}.${encode(payload)}.signature`; } describe('decodeJwtPayload', () => { it('returns null for malformed tokens or invalid payloads', () => { expect(decodeJwtPayload('not-a-jwt')).toBeNull(); expect(decodeJwtPayload('a.b')).toBeNull(); expect(decodeJwtPayload('a.b.c.d')).toBeNull(); const nonJson = 'header.' + btoa('nope') + '.sig'; expect(decodeJwtPayload(nonJson)).toBeNull(); const nonObjectPayload = createJwt({ value: 1 }).replace( /\.[^.]+\./, `.${btoa('1').replace(/=+$/g, '')}.`, ); expect(decodeJwtPayload(nonObjectPayload)).toBeNull(); }); it('decodes valid base64url payloads', () => { const payload = { sub: 'user-1', role: 'ADMIN', exp: 1735689600 }; expect(decodeJwtPayload(createJwt(payload))).toEqual(payload); }); }); describe('isJwtExpired', () => { beforeEach(() => { vi.useFakeTimers(); }); it('returns false for malformed tokens and invalid exp claim', () => { expect(isJwtExpired('not-a-jwt')).toBe(false); expect(isJwtExpired(createJwt({}))).toBe(false); expect(isJwtExpired(createJwt({ exp: 'nope' }))).toBe(false); expect(isJwtExpired(createJwt({ exp: Number.POSITIVE_INFINITY }))).toBe(false); }); it('returns true when token is expired', () => { vi.setSystemTime(new Date('2026-01-01T00:00:00Z')); const exp = Math.floor(new Date('2025-12-31T23:59:00Z').getTime() / 1000); expect(isJwtExpired(createJwt({ exp }))).toBe(true); }); it('applies skew seconds when evaluating expiration', () => { vi.setSystemTime(new Date('2026-01-01T00:00:00Z')); const exp = Math.floor(new Date('2026-01-01T00:00:10Z').getTime() / 1000); expect(isJwtExpired(createJwt({ exp }), 15)).toBe(true); expect(isJwtExpired(createJwt({ exp }), 5)).toBe(false); }); });