mcp-cisco-support
Version:
MCP server for Cisco Support APIs including Bug Search and future tools
261 lines • 12.6 kB
JavaScript
"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