Skip to main content
Glama
errorHandling.test.tsโ€ข10.4 kB
import { executeTool } from '../src/mcp-server'; import { mockOAuthResponse, mockErrorResponse, mockTimeoutError, mockAbortError } from './mockData'; import { mockFetch } from './setup'; // Skip this entire test suite if mockFetch is unavailable (integration test mode) const isIntegrationMode = !mockFetch; (isIntegrationMode ? describe.skip : describe)('Error Handling and Validation', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Parameter Validation', () => { test('should reject unknown tools', async () => { await expect(executeTool('unknown_tool', {})).rejects.toThrow('Unknown tool: unknown_tool'); }); test('should validate severity parameter enum values', async () => { // Mock successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } // Should not reach this point due to validation return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) } as Response); }); // This should pass with valid severity const result = await executeTool('search_bugs_by_keyword', { keyword: 'test', severity: '3' }); expect(result).toBeDefined(); }); test('should validate status parameter enum values', async () => { // Mock successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) } as Response); }); // This should pass with valid status const result = await executeTool('search_bugs_by_keyword', { keyword: 'test', status: 'O' }); expect(result).toBeDefined(); }); test('should validate modified_date parameter enum values', async () => { // Mock successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) } as Response); }); // This should pass with valid modified_date const result = await executeTool('search_bugs_by_keyword', { keyword: 'test', modified_date: '3' }); expect(result).toBeDefined(); }); }); describe('OAuth2 Error Handling', () => { test('should handle OAuth2 authentication failures', async () => { mockFetch!.mockImplementationOnce(() => Promise.resolve({ ok: false, status: 401, statusText: 'Unauthorized', text: () => Promise.resolve('Invalid credentials') } as Response) ); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('OAuth2 authentication failed: 401 Unauthorized - Invalid credentials'); }); test('should handle OAuth2 timeout errors', async () => { mockFetch!.mockImplementationOnce(() => Promise.reject(mockTimeoutError)); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('OAuth2 authentication connection timed out'); }); test('should handle OAuth2 abort errors', async () => { mockFetch!.mockImplementationOnce(() => Promise.reject(mockAbortError)); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('OAuth2 authentication timed out after 30 seconds'); }); }); describe('API Error Handling', () => { test('should handle 500 Internal Server Error from Cisco API', async () => { // Successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } // API error return Promise.resolve({ ok: false, status: 500, statusText: 'Internal Server Error', text: () => Promise.resolve(JSON.stringify(mockErrorResponse)) } as Response); }); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('Cisco API call failed: 500 Internal Server Error'); }); test('should handle API timeout errors', async () => { // Successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } // Timeout error return Promise.reject(mockTimeoutError); }); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('Cisco API connection timed out while waiting for response headers'); }); test('should handle API abort errors', async () => { // Successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } // Abort error return Promise.reject(mockAbortError); }); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('Cisco API call timed out after 60 seconds'); }); test('should handle 401 errors and retry with new token', async () => { let callCount = 0; mockFetch!.mockImplementation((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } callCount++; if (callCount === 1) { // First call returns 401 return Promise.resolve({ ok: false, status: 401, statusText: 'Unauthorized', text: () => Promise.resolve('Token expired') } as Response); } else { // Second call (after token refresh) succeeds return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) } as Response); } }); const result = await executeTool('search_bugs_by_keyword', { keyword: 'test' }); expect(result.bugs).toEqual([]); expect(mockFetch).toHaveBeenCalledTimes(4); // 2 OAuth + 1 failed API + 1 successful API }); }); describe('Edge Cases', () => { test('should handle empty API responses', async () => { // Successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) } as Response); }); const result = await executeTool('search_bugs_by_keyword', { keyword: 'nonexistent' }); expect(result.bugs).toEqual([]); expect(result.total_results).toBe(0); }); test('should handle malformed JSON responses', async () => { // Successful OAuth mockFetch!.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockOAuthResponse) } as Response); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.reject(new Error('Unexpected end of JSON input')) } as Response); }); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('Unexpected end of JSON input'); }); test('should handle network connectivity issues', async () => { mockFetch!.mockImplementationOnce(() => Promise.reject(new Error('fetch failed')) ); await expect(executeTool('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('fetch failed'); }); }); describe('EoX API Placeholder', () => { test('should return helpful error for EoX API placeholder', async () => { const result = await executeTool('eox_placeholder', {}); expect(result).toEqual({ error: 'EoX API Not Implemented', message: 'The Cisco EoX API is not yet implemented in this MCP server. Currently, only the Bug and Case APIs are available.', alternatives: [ 'Use search_bugs_by_keyword to find bugs related to your topic', 'Use search_bugs_by_product_id if you have a specific product ID', 'Use get_case_details if you have a case ID to investigate' ], example: 'Try: "Search for bugs related to your eox topic with keyword search"', available_apis: ['bug', 'case'], planned_apis: ['eox', 'product', 'serial', 'rma', 'software', 'psirt'] }); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sieteunoseis/mcp-cisco-support'

If you have feedback or need assistance with the MCP directory API, please join our Discord server