/**
* mysql-mcp - OAuth Resource Server Unit Tests
*
* Tests for OAuthResourceServer RFC 9728 compliance including
* metadata generation, scope validation, and configuration.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { OAuthResourceServer, createOAuthResourceServer } from '../OAuthResourceServer.js';
import type { ResourceServerConfig } from '../types.js';
describe('OAuthResourceServer', () => {
let config: ResourceServerConfig;
let server: OAuthResourceServer;
beforeEach(() => {
config = {
resource: 'https://mysql-mcp.example.com',
authorizationServers: ['https://auth.example.com'],
scopesSupported: ['read', 'write', 'admin']
};
server = new OAuthResourceServer(config);
});
describe('Construction', () => {
it('should create server with config', () => {
expect(server).toBeInstanceOf(OAuthResourceServer);
});
it('should set default bearer methods when not provided', () => {
const metadata = server.getMetadata();
expect(metadata.bearer_methods_supported).toEqual(['header']);
});
it('should use provided bearer methods', () => {
const customServer = new OAuthResourceServer({
...config,
bearerMethodsSupported: ['header', 'body']
});
const metadata = customServer.getMetadata();
expect(metadata.bearer_methods_supported).toEqual(['header', 'body']);
});
});
describe('getMetadata()', () => {
it('should return RFC 9728 compliant structure', () => {
const metadata = server.getMetadata();
expect(metadata).toHaveProperty('resource');
expect(metadata).toHaveProperty('authorization_servers');
expect(metadata).toHaveProperty('scopes_supported');
expect(metadata).toHaveProperty('bearer_methods_supported');
expect(metadata).toHaveProperty('resource_documentation');
expect(metadata).toHaveProperty('resource_signing_alg_values_supported');
});
it('should include correct resource identifier', () => {
const metadata = server.getMetadata();
expect(metadata.resource).toBe('https://mysql-mcp.example.com');
});
it('should include authorization servers', () => {
const metadata = server.getMetadata();
expect(metadata.authorization_servers).toEqual(['https://auth.example.com']);
});
it('should include supported scopes', () => {
const metadata = server.getMetadata();
expect(metadata.scopes_supported).toEqual(['read', 'write', 'admin']);
});
it('should generate documentation URL', () => {
const metadata = server.getMetadata();
expect(metadata.resource_documentation).toBe('https://mysql-mcp.example.com/docs');
});
it('should include signing algorithms', () => {
const metadata = server.getMetadata();
expect(metadata.resource_signing_alg_values_supported).toContain('RS256');
expect(metadata.resource_signing_alg_values_supported).toContain('ES256');
});
});
describe('getWellKnownPath()', () => {
it('should return correct well-known path', () => {
expect(server.getWellKnownPath()).toBe('/.well-known/oauth-protected-resource');
});
});
describe('isScopeSupported()', () => {
it('should return true for explicitly supported scopes', () => {
expect(server.isScopeSupported('read')).toBe(true);
expect(server.isScopeSupported('write')).toBe(true);
expect(server.isScopeSupported('admin')).toBe(true);
});
it('should return false for unsupported scopes', () => {
expect(server.isScopeSupported('delete')).toBe(false);
expect(server.isScopeSupported('unknown')).toBe(false);
});
it('should accept db: prefixed scopes', () => {
expect(server.isScopeSupported('db:mydb')).toBe(true);
expect(server.isScopeSupported('db:testdb:read')).toBe(true);
});
it('should accept table: prefixed scopes', () => {
expect(server.isScopeSupported('table:users')).toBe(true);
expect(server.isScopeSupported('table:orders:write')).toBe(true);
});
it('should reject invalid scope patterns', () => {
expect(server.isScopeSupported('database:mydb')).toBe(false);
expect(server.isScopeSupported('TB:users')).toBe(false);
});
});
describe('getResourceId()', () => {
it('should return resource identifier', () => {
expect(server.getResourceId()).toBe('https://mysql-mcp.example.com');
});
});
describe('getSupportedScopes()', () => {
it('should return copy of scopes array', () => {
const scopes = server.getSupportedScopes();
expect(scopes).toEqual(['read', 'write', 'admin']);
// Verify it's a copy
scopes.push('modified');
expect(server.getSupportedScopes()).toEqual(['read', 'write', 'admin']);
});
});
describe('getAuthorizationServers()', () => {
it('should return copy of authorization servers', () => {
const servers = server.getAuthorizationServers();
expect(servers).toEqual(['https://auth.example.com']);
// Verify it's a copy
servers.push('https://auth2.example.com');
expect(server.getAuthorizationServers()).toEqual(['https://auth.example.com']);
});
it('should support multiple authorization servers', () => {
const multiServer = new OAuthResourceServer({
...config,
authorizationServers: [
'https://auth1.example.com',
'https://auth2.example.com'
]
});
expect(multiServer.getAuthorizationServers()).toEqual([
'https://auth1.example.com',
'https://auth2.example.com'
]);
});
});
});
describe('createOAuthResourceServer()', () => {
it('should create OAuthResourceServer instance', () => {
const server = createOAuthResourceServer({
resource: 'https://test.example.com',
authorizationServers: ['https://auth.example.com'],
scopesSupported: ['read']
});
expect(server).toBeInstanceOf(OAuthResourceServer);
});
it('should pass config to server', () => {
const server = createOAuthResourceServer({
resource: 'https://custom.example.com',
authorizationServers: ['https://auth.example.com'],
scopesSupported: ['custom-scope']
});
expect(server.getResourceId()).toBe('https://custom.example.com');
expect(server.getSupportedScopes()).toContain('custom-scope');
});
});