UNPKG

mcp-cisco-support

Version:

MCP server for Cisco Support APIs including Bug Search and future tools

261 lines 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const mcp_server_1 = require("../src/mcp-server"); const mockData_1 = require("./mockData"); const setup_1 = require("./setup"); // Skip this entire test suite if mockFetch is unavailable (integration test mode) const isIntegrationMode = !setup_1.mockFetch; (isIntegrationMode ? describe.skip : describe)('Error Handling and Validation', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Parameter Validation', () => { test('should reject unknown tools', async () => { await expect((0, mcp_server_1.executeTool)('unknown_tool', {})).rejects.toThrow('Unknown tool: unknown_tool'); }); test('should validate severity parameter enum values', async () => { // Mock successful OAuth setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } // Should not reach this point due to validation return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) }); }); // This should pass with valid severity const result = await (0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test', severity: '3' }); expect(result).toBeDefined(); }); test('should validate status parameter enum values', async () => { // Mock successful OAuth setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) }); }); // This should pass with valid status const result = await (0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test', status: 'O' }); expect(result).toBeDefined(); }); test('should validate modified_date parameter enum values', async () => { // Mock successful OAuth setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) }); }); // This should pass with valid modified_date const result = await (0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test', modified_date: '3' }); expect(result).toBeDefined(); }); }); describe('OAuth2 Error Handling', () => { test('should handle OAuth2 authentication failures', async () => { setup_1.mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: false, status: 401, statusText: 'Unauthorized', text: () => Promise.resolve('Invalid credentials') })); await expect((0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('OAuth2 authentication failed: 401 Unauthorized - Invalid credentials'); }); test('should handle OAuth2 timeout errors', async () => { setup_1.mockFetch.mockImplementationOnce(() => Promise.reject(mockData_1.mockTimeoutError)); await expect((0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('OAuth2 authentication connection timed out'); }); test('should handle OAuth2 abort errors', async () => { setup_1.mockFetch.mockImplementationOnce(() => Promise.reject(mockData_1.mockAbortError)); await expect((0, mcp_server_1.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 setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } // API error return Promise.resolve({ ok: false, status: 500, statusText: 'Internal Server Error', text: () => Promise.resolve(JSON.stringify(mockData_1.mockErrorResponse)) }); }); await expect((0, mcp_server_1.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 setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } // Timeout error return Promise.reject(mockData_1.mockTimeoutError); }); await expect((0, mcp_server_1.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 setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } // Abort error return Promise.reject(mockData_1.mockAbortError); }); await expect((0, mcp_server_1.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; setup_1.mockFetch.mockImplementation((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } callCount++; if (callCount === 1) { // First call returns 401 return Promise.resolve({ ok: false, status: 401, statusText: 'Unauthorized', text: () => Promise.resolve('Token expired') }); } else { // Second call (after token refresh) succeeds return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) }); } }); const result = await (0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test' }); expect(result.bugs).toEqual([]); expect(setup_1.mockFetch).toHaveBeenCalledTimes(4); // 2 OAuth + 1 failed API + 1 successful API }); }); describe('Edge Cases', () => { test('should handle empty API responses', async () => { // Successful OAuth setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ bugs: [], total_results: 0 }) }); }); const result = await (0, mcp_server_1.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 setup_1.mockFetch.mockImplementationOnce((url, init) => { if (typeof url === 'string' && url.includes('oauth2')) { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData_1.mockOAuthResponse) }); } return Promise.resolve({ ok: true, status: 200, json: () => Promise.reject(new Error('Unexpected end of JSON input')) }); }); await expect((0, mcp_server_1.executeTool)('search_bugs_by_keyword', { keyword: 'test' })) .rejects.toThrow('Unexpected end of JSON input'); }); test('should handle network connectivity issues', async () => { setup_1.mockFetch.mockImplementationOnce(() => Promise.reject(new Error('fetch failed'))); await expect((0, mcp_server_1.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 (0, mcp_server_1.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'] }); }); }); }); //# sourceMappingURL=errorHandling.test.js.map