mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 20:20:09 +00:00
270 lines
9.2 KiB
TypeScript
270 lines
9.2 KiB
TypeScript
/**
|
|
* Unit tests for MCP Manager module
|
|
*
|
|
* Tests web search MCP configuration logic without filesystem dependencies.
|
|
* These tests focus on the pure logic functions that can be tested without
|
|
* modifying the actual config files.
|
|
*/
|
|
import { describe, it, expect } from 'bun:test';
|
|
|
|
/**
|
|
* Test helper: Simulate hasWebSearch detection logic
|
|
* This matches the logic in mcp-manager.ts hasMcpWebSearch()
|
|
*/
|
|
function detectWebSearchMcp(mcpServers: Record<string, unknown>): boolean {
|
|
return Object.keys(mcpServers).some((key) => {
|
|
const lowerKey = key.toLowerCase();
|
|
return (
|
|
lowerKey.includes('web-search') ||
|
|
lowerKey.includes('websearch') ||
|
|
lowerKey.includes('tavily') ||
|
|
lowerKey.includes('brave')
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test helper: Simulate provider configuration logic
|
|
* Returns which providers would be added given config and env
|
|
*/
|
|
function getProvidersToAdd(
|
|
wsConfig: { enabled: boolean; provider: string; fallback: boolean },
|
|
hasExistingWebSearch: boolean,
|
|
apiKeys: { brave?: string; tavily?: string }
|
|
): string[] {
|
|
if (!wsConfig.enabled) return [];
|
|
if (hasExistingWebSearch) return [];
|
|
|
|
const providers: string[] = [];
|
|
|
|
if (wsConfig.provider === 'auto') {
|
|
providers.push('web-search-prime');
|
|
if (wsConfig.fallback) {
|
|
if (apiKeys.brave) providers.push('brave-search');
|
|
if (apiKeys.tavily) providers.push('tavily');
|
|
}
|
|
} else if (wsConfig.provider === 'web-search-prime') {
|
|
providers.push('web-search-prime');
|
|
} else if (wsConfig.provider === 'brave') {
|
|
if (apiKeys.brave) {
|
|
providers.push('brave-search');
|
|
} else if (wsConfig.fallback) {
|
|
// Fallback chain
|
|
providers.push('web-search-prime');
|
|
if (apiKeys.tavily) providers.push('tavily');
|
|
}
|
|
} else if (wsConfig.provider === 'tavily') {
|
|
if (apiKeys.tavily) {
|
|
providers.push('tavily');
|
|
} else if (wsConfig.fallback) {
|
|
// Fallback chain
|
|
providers.push('web-search-prime');
|
|
if (apiKeys.brave) providers.push('brave-search');
|
|
}
|
|
}
|
|
|
|
return providers;
|
|
}
|
|
|
|
describe('mcp-manager logic', () => {
|
|
describe('web search detection', () => {
|
|
it('should detect web-search-prime', () => {
|
|
const servers = { 'web-search-prime': { type: 'http' } };
|
|
expect(detectWebSearchMcp(servers)).toBe(true);
|
|
});
|
|
|
|
it('should detect brave-search', () => {
|
|
const servers = { 'brave-search': { type: 'stdio' } };
|
|
expect(detectWebSearchMcp(servers)).toBe(true);
|
|
});
|
|
|
|
it('should detect tavily', () => {
|
|
const servers = { tavily: { type: 'stdio' } };
|
|
expect(detectWebSearchMcp(servers)).toBe(true);
|
|
});
|
|
|
|
it('should be case-insensitive', () => {
|
|
expect(detectWebSearchMcp({ 'WebSearch-Custom': {} })).toBe(true);
|
|
expect(detectWebSearchMcp({ 'WEB-SEARCH': {} })).toBe(true);
|
|
expect(detectWebSearchMcp({ TAVILY: {} })).toBe(true);
|
|
expect(detectWebSearchMcp({ 'Brave-Search': {} })).toBe(true);
|
|
});
|
|
|
|
it('should not detect unrelated MCPs', () => {
|
|
expect(detectWebSearchMcp({ 'my-custom-mcp': {} })).toBe(false);
|
|
expect(detectWebSearchMcp({ filesystem: {} })).toBe(false);
|
|
expect(detectWebSearchMcp({ 'github-copilot': {} })).toBe(false);
|
|
});
|
|
|
|
it('should return false for empty servers', () => {
|
|
expect(detectWebSearchMcp({})).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('provider selection logic', () => {
|
|
const defaultConfig = { enabled: true, provider: 'auto', fallback: true };
|
|
const noApiKeys = {};
|
|
const braveOnly = { brave: 'test-key' };
|
|
const tavilyOnly = { tavily: 'test-key' };
|
|
const bothKeys = { brave: 'brave-key', tavily: 'tavily-key' };
|
|
|
|
it('should add web-search-prime in auto mode', () => {
|
|
const providers = getProvidersToAdd(defaultConfig, false, noApiKeys);
|
|
expect(providers).toContain('web-search-prime');
|
|
});
|
|
|
|
it('should add brave-search when API key available in auto mode', () => {
|
|
const providers = getProvidersToAdd(defaultConfig, false, braveOnly);
|
|
expect(providers).toContain('web-search-prime');
|
|
expect(providers).toContain('brave-search');
|
|
});
|
|
|
|
it('should add tavily when API key available in auto mode', () => {
|
|
const providers = getProvidersToAdd(defaultConfig, false, tavilyOnly);
|
|
expect(providers).toContain('web-search-prime');
|
|
expect(providers).toContain('tavily');
|
|
});
|
|
|
|
it('should add all providers when both API keys available', () => {
|
|
const providers = getProvidersToAdd(defaultConfig, false, bothKeys);
|
|
expect(providers).toContain('web-search-prime');
|
|
expect(providers).toContain('brave-search');
|
|
expect(providers).toContain('tavily');
|
|
});
|
|
|
|
it('should not add fallbacks when fallback=false', () => {
|
|
const config = { enabled: true, provider: 'auto', fallback: false };
|
|
const providers = getProvidersToAdd(config, false, bothKeys);
|
|
expect(providers).toEqual(['web-search-prime']);
|
|
});
|
|
|
|
it('should skip when disabled', () => {
|
|
const config = { enabled: false, provider: 'auto', fallback: true };
|
|
const providers = getProvidersToAdd(config, false, bothKeys);
|
|
expect(providers).toEqual([]);
|
|
});
|
|
|
|
it('should skip when web search already exists', () => {
|
|
const providers = getProvidersToAdd(defaultConfig, true, bothKeys);
|
|
expect(providers).toEqual([]);
|
|
});
|
|
|
|
it('should use specific provider when configured', () => {
|
|
const config = { enabled: true, provider: 'brave', fallback: false };
|
|
const providers = getProvidersToAdd(config, false, braveOnly);
|
|
expect(providers).toEqual(['brave-search']);
|
|
});
|
|
|
|
it('should fallback when specific provider not available', () => {
|
|
const config = { enabled: true, provider: 'tavily', fallback: true };
|
|
// No tavily key, should fallback
|
|
const providers = getProvidersToAdd(config, false, braveOnly);
|
|
expect(providers).toContain('web-search-prime');
|
|
expect(providers).toContain('brave-search');
|
|
expect(providers).not.toContain('tavily');
|
|
});
|
|
|
|
it('should return empty when provider unavailable and fallback=false', () => {
|
|
const config = { enabled: true, provider: 'brave', fallback: false };
|
|
// No brave key and no fallback
|
|
const providers = getProvidersToAdd(config, false, noApiKeys);
|
|
expect(providers).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('MCP server config structures', () => {
|
|
it('should define correct web-search-prime structure', () => {
|
|
const webSearchPrimeConfig = {
|
|
type: 'http',
|
|
url: 'https://api.z.ai/api/mcp/web_search_prime/mcp',
|
|
headers: {},
|
|
};
|
|
|
|
expect(webSearchPrimeConfig.type).toBe('http');
|
|
expect(webSearchPrimeConfig.url).toContain('web_search_prime');
|
|
});
|
|
|
|
it('should define correct brave-search structure', () => {
|
|
const braveConfig = {
|
|
type: 'stdio',
|
|
command: 'npx',
|
|
args: ['-y', '@modelcontextprotocol/server-brave-search'],
|
|
env: { BRAVE_API_KEY: 'test-key' },
|
|
};
|
|
|
|
expect(braveConfig.type).toBe('stdio');
|
|
expect(braveConfig.command).toBe('npx');
|
|
expect(braveConfig.args).toContain('@modelcontextprotocol/server-brave-search');
|
|
expect(braveConfig.env.BRAVE_API_KEY).toBe('test-key');
|
|
});
|
|
|
|
it('should define correct tavily structure', () => {
|
|
const tavilyConfig = {
|
|
type: 'stdio',
|
|
command: 'npx',
|
|
args: ['-y', '@tavily/mcp-server'],
|
|
env: { TAVILY_API_KEY: 'test-key' },
|
|
};
|
|
|
|
expect(tavilyConfig.type).toBe('stdio');
|
|
expect(tavilyConfig.args).toContain('@tavily/mcp-server');
|
|
expect(tavilyConfig.env.TAVILY_API_KEY).toBe('test-key');
|
|
});
|
|
});
|
|
|
|
describe('hook configuration', () => {
|
|
it('should define correct PreToolUse hook structure', () => {
|
|
const hookConfig = {
|
|
PreToolUse: [
|
|
{
|
|
matcher: 'WebSearch',
|
|
hooks: [
|
|
{
|
|
type: 'command',
|
|
command: 'node "/path/to/hook.cjs"',
|
|
timeout: 5,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
expect(hookConfig.PreToolUse).toBeDefined();
|
|
expect(hookConfig.PreToolUse[0].matcher).toBe('WebSearch');
|
|
expect(hookConfig.PreToolUse[0].hooks[0].type).toBe('command');
|
|
expect(hookConfig.PreToolUse[0].hooks[0].timeout).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('MCP config file path', () => {
|
|
it('should be located in .claude directory', () => {
|
|
// The MCP config path should follow this pattern
|
|
const expectedPathPattern = '.claude/.mcp.json';
|
|
const testPath = '/home/user/.claude/.mcp.json';
|
|
expect(testPath).toContain(expectedPathPattern);
|
|
});
|
|
});
|
|
|
|
describe('WebSearch config defaults', () => {
|
|
it('should have correct default values', () => {
|
|
const defaults = {
|
|
enabled: true,
|
|
provider: 'auto',
|
|
fallback: true,
|
|
};
|
|
|
|
expect(defaults.enabled).toBe(true);
|
|
expect(defaults.provider).toBe('auto');
|
|
expect(defaults.fallback).toBe(true);
|
|
});
|
|
|
|
it('should validate provider options', () => {
|
|
const validProviders = ['auto', 'web-search-prime', 'brave', 'tavily'];
|
|
expect(validProviders).toContain('auto');
|
|
expect(validProviders).toContain('web-search-prime');
|
|
expect(validProviders).toContain('brave');
|
|
expect(validProviders).toContain('tavily');
|
|
});
|
|
});
|
|
});
|