/**
* Unit Tests for Authentication
*
* Tests for token validation and permission checking.
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { tokenHandler } from '../../src/mcp/auth/token-handler.js';
import { permissionChecker } from '../../src/mcp/auth/permission-checker.js';
describe('Token Handler', () => {
let mockEnv;
beforeEach(() => {
mockEnv = {
JWT_SECRET: 'test-secret-key-32-characters-long',
GITHUB_TOKEN: 'ghp_test_token_123456789'
};
tokenHandler.clearCache();
});
describe('extractToken', () => {
it('should extract token from Bearer scheme', () => {
const authHeader = 'Bearer ghp_test_token_123456789';
const token = tokenHandler.extractToken(authHeader);
expect(token).toBe('ghp_test_token_123456789');
});
it('should extract token from token scheme', () => {
const authHeader = 'token ghp_test_token_123456789';
const token = tokenHandler.extractToken(authHeader);
expect(token).toBe('ghp_test_token_123456789');
});
it('should extract token without scheme', () => {
const authHeader = 'ghp_test_token_123456789';
const token = tokenHandler.extractToken(authHeader);
expect(token).toBe('ghp_test_token_123456789');
});
it('should return null for empty header', () => {
const token = tokenHandler.extractToken(null);
expect(token).toBeNull();
});
it('should return null for malformed header', () => {
const token = tokenHandler.extractToken('Invalid Scheme Token');
expect(token).toBeNull();
});
});
describe('isJWT', () => {
it('should identify valid JWT', () => {
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
expect(tokenHandler.isJWT(jwt)).toBe(true);
});
it('should reject non-JWT', () => {
expect(tokenHandler.isJWT('ghp_test_token')).toBe(false);
expect(tokenHandler.isJWT('not-a-jwt')).toBe(false);
expect(tokenHandler.isJWT('invalid.jwt.token')).toBe(false);
});
});
describe('validateToken', () => {
it('should return error for missing header', async () => {
const result = await tokenHandler.validateToken(null, mockEnv);
expect(result.valid).toBe(false);
expect(result.reason).toBe('Missing authorization header');
});
it('should return error for invalid header format', async () => {
const result = await tokenHandler.validateToken('Invalid', mockEnv);
expect(result.valid).toBe(false);
});
});
describe('cache', () => {
it('should clear cache', () => {
tokenHandler.clearCache();
const stats = tokenHandler.getCacheStats();
expect(stats.size).toBe(0);
});
it('should get cache stats', () => {
const stats = tokenHandler.getCacheStats();
expect(stats).toHaveProperty('size');
expect(stats).toHaveProperty('ttl');
});
});
});
describe('Permission Checker', () => {
let mockEnv;
beforeEach(() => {
mockEnv = {
GITHUB_TOKEN: 'ghp_test_token_123456789'
};
permissionChecker.clearCache();
});
describe('checkPermissions', () => {
it('should return allowed for list_repositories', async () => {
global.fetch = vi.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve([])
})
);
const result = await permissionChecker.checkPermissions(
'list_repositories',
{},
mockEnv.GITHUB_TOKEN
);
expect(result.allowed).toBe(true);
});
it('should return denied for invalid token', async () => {
global.fetch = vi.fn(() =>
Promise.resolve({
ok: false,
status: 401
})
);
const result = await permissionChecker.checkPermissions(
'list_repositories',
{},
'invalid_token'
);
expect(result.allowed).toBe(false);
expect(result.reason).toBeDefined();
});
});
describe('cache', () => {
it('should clear cache', () => {
permissionChecker.clearCache();
const stats = permissionChecker.getCacheStats();
expect(stats.size).toBe(0);
});
it('should get cache stats', () => {
const stats = permissionChecker.getCacheStats();
expect(stats).toHaveProperty('size');
expect(stats).toHaveProperty('ttl');
});
});
});