155 lines
3.9 KiB
JavaScript
155 lines
3.9 KiB
JavaScript
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);
|
|
});
|
|
});
|
|
});
|