mcp-cisco-support
Version:
MCP server for Cisco Support APIs including Bug Search and future tools
420 lines • 18.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
// Set environment before imports to ensure proper API initialization
process.env.SUPPORT_API = 'case';
const mcp_server_1 = require("../src/mcp-server");
const mockData_1 = require("./mockData");
const setup_1 = require("./setup");
// Mock Case API responses
const mockCaseSummaryResponse = {
cases: [
{
case_id: '12345678',
title: 'Network connectivity issue',
status: 'O',
severity: '2',
created_date: '2024-01-15T10:30:00Z',
last_modified_date: '2024-01-16T14:20:00Z',
contract_id: 'CON123456',
owner: 'john.doe@company.com'
},
{
case_id: '87654321',
title: 'Router configuration problem',
status: 'C',
severity: '3',
created_date: '2024-01-10T09:15:00Z',
last_modified_date: '2024-01-12T16:45:00Z',
contract_id: 'CON789012',
owner: 'jane.smith@company.com'
}
],
total_results: 2
};
const mockCaseDetailsResponse = {
case_id: '12345678',
title: 'Network connectivity issue with Cisco ASR router',
description: 'Customer experiencing intermittent connectivity issues with ASR 9000 series router',
status: 'O',
severity: '2',
priority: 'High',
created_date: '2024-01-15T10:30:00Z',
last_modified_date: '2024-01-16T14:20:00Z',
contract_id: 'CON123456',
owner: 'john.doe@company.com',
product: 'Cisco ASR 9000 Series',
software_version: '7.5.2',
case_notes: [
{
date: '2024-01-15T10:30:00Z',
author: 'TAC Engineer',
note: 'Initial case opened. Investigating connectivity issues.'
}
]
};
const mockCasesByContractResponse = {
cases: [
{
case_id: '11111111',
title: 'Contract-related case 1',
status: 'O',
severity: '3',
contract_id: 'CON123456'
},
{
case_id: '22222222',
title: 'Contract-related case 2',
status: 'W',
severity: '2',
contract_id: 'CON123456'
}
],
total_results: 2
};
const mockCasesByUserResponse = {
cases: [
{
case_id: '33333333',
title: 'User case 1',
status: 'O',
severity: '1',
owner: 'user123@company.com'
},
{
case_id: '44444444',
title: 'User case 2',
status: 'C',
severity: '4',
owner: 'user123@company.com'
}
],
total_results: 2
};
// Skip this entire test suite if mockFetch is unavailable (integration test mode)
const isIntegrationMode = !setup_1.mockFetch;
(isIntegrationMode ? describe.skip : describe)('Cisco Case API Tools', () => {
beforeEach(() => {
jest.clearAllMocks();
// Set environment to enable Case API
process.env.SUPPORT_API = 'case';
// Default successful OAuth response
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),
text: () => Promise.resolve(JSON.stringify(mockData_1.mockOAuthResponse))
});
}
// Default Case API response
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockCaseSummaryResponse),
text: () => Promise.resolve(JSON.stringify(mockCaseSummaryResponse))
});
});
});
describe('Case API Tool Discovery', () => {
test('should discover case API tools when case API is enabled', async () => {
const tools = await (0, mcp_server_1.getAvailableTools)();
const caseTools = tools.filter(tool => tool.name.includes('case'));
expect(caseTools.length).toBeGreaterThan(0);
expect(caseTools.map(t => t.name)).toEqual(expect.arrayContaining([
'get_case_summary',
'get_case_details',
'search_cases_by_contract',
'search_cases_by_user'
]));
});
test('should not discover case tools when only bug API is enabled', async () => {
process.env.SUPPORT_API = 'bug';
const tools = await (0, mcp_server_1.getAvailableTools)();
const caseTools = tools.filter(tool => tool.name.includes('case'));
expect(caseTools.length).toBe(0);
});
});
describe('get_case_summary', () => {
test('should get case summary for multiple case IDs', async () => {
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)
});
}
if (typeof url === 'string' && url.includes('case_ids')) {
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockCaseSummaryResponse)
});
}
return Promise.reject(new Error('Unexpected URL'));
});
const result = await (0, mcp_server_1.executeTool)('get_case_summary', {
case_ids: '12345678,87654321'
});
expect(result).toHaveProperty('cases');
expect(result.cases).toHaveLength(2);
expect(result.cases[0]).toHaveProperty('case_id', '12345678');
expect(result.cases[1]).toHaveProperty('case_id', '87654321');
});
test('should validate case_ids parameter format', async () => {
// This should pass validation at the schema level, but let's test with clearly invalid format
await expect((0, mcp_server_1.executeTool)('get_case_summary', {
case_ids: 'abc-def-ghi'
})).rejects.toThrow();
});
test('should handle API errors gracefully', async () => {
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)
});
}
return Promise.resolve({
ok: false,
status: 404,
statusText: 'Not Found',
text: () => Promise.resolve('Case not found')
});
});
await expect((0, mcp_server_1.executeTool)('get_case_summary', {
case_ids: '99999999'
})).rejects.toThrow('Case API call failed: 404 Not Found');
});
});
describe('get_case_details', () => {
test('should get detailed information for a single case', async () => {
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)
});
}
if (typeof url === 'string' && url.includes('details/case_id')) {
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockCaseDetailsResponse)
});
}
return Promise.reject(new Error('Unexpected URL'));
});
const result = await (0, mcp_server_1.executeTool)('get_case_details', {
case_id: '12345678'
});
expect(result).toHaveProperty('case_id', '12345678');
expect(result).toHaveProperty('title');
expect(result).toHaveProperty('description');
expect(result).toHaveProperty('case_notes');
});
test('should validate single case_id format', async () => {
await expect((0, mcp_server_1.executeTool)('get_case_details', {
case_id: '12345,67890'
})).rejects.toThrow();
});
});
describe('search_cases_by_contract', () => {
test('should search cases by contract ID', async () => {
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)
});
}
if (typeof url === 'string' && url.includes('contracts/contract_ids')) {
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockCasesByContractResponse)
});
}
return Promise.reject(new Error('Unexpected URL'));
});
const result = await (0, mcp_server_1.executeTool)('search_cases_by_contract', {
contract_ids: 'CON123456'
});
expect(result).toHaveProperty('cases');
expect(result.cases).toHaveLength(2);
expect(result.cases[0]).toHaveProperty('contract_id', 'CON123456');
});
test('should support status filtering with single values only', async () => {
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)
});
}
if (typeof url === 'string' && url.includes('contracts/contract_ids') && url.includes('status=O')) {
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({
cases: [mockCasesByContractResponse.cases[0]],
total_results: 1
})
});
}
return Promise.reject(new Error('Unexpected URL'));
});
const result = await (0, mcp_server_1.executeTool)('search_cases_by_contract', {
contract_ids: 'CON123456',
status: 'O'
});
expect(result).toHaveProperty('cases');
expect(result.cases).toHaveLength(1);
expect(result.cases[0]).toHaveProperty('status', 'O');
});
test('should support status_flag filtering', async () => {
const result = await (0, mcp_server_1.executeTool)('search_cases_by_contract', {
contract_ids: 'CON123456',
status_flag: 'Active'
});
expect(result).toHaveProperty('cases');
});
});
describe('search_cases_by_user', () => {
test('should search cases by user ID', async () => {
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)
});
}
if (typeof url === 'string' && url.includes('users/user_ids')) {
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockCasesByUserResponse)
});
}
return Promise.reject(new Error('Unexpected URL'));
});
const result = await (0, mcp_server_1.executeTool)('search_cases_by_user', {
user_ids: 'user123@company.com'
});
expect(result).toHaveProperty('cases');
expect(result.cases).toHaveLength(2);
expect(result.cases[0]).toHaveProperty('owner', 'user123@company.com');
});
test('should handle multiple user IDs', async () => {
const result = await (0, mcp_server_1.executeTool)('search_cases_by_user', {
user_ids: 'user1@company.com,user2@company.com'
});
expect(result).toHaveProperty('cases');
});
test('should support status filtering', async () => {
const result = await (0, mcp_server_1.executeTool)('search_cases_by_user', {
user_ids: 'user123@company.com',
status: 'O',
status_flag: 'Active'
});
expect(result).toHaveProperty('cases');
});
});
describe('Case API Error Handling', () => {
test('should handle authentication errors', async () => {
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)
});
}
return Promise.resolve({
ok: false,
status: 401,
statusText: 'Unauthorized',
text: () => Promise.resolve('Unauthorized')
});
});
await expect((0, mcp_server_1.executeTool)('get_case_details', {
case_id: '12345678'
})).rejects.toThrow('Case API call failed after token refresh: 401 Unauthorized');
});
test('should handle access denied errors', async () => {
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)
});
}
return Promise.resolve({
ok: false,
status: 403,
statusText: 'Forbidden',
text: () => Promise.resolve('Forbidden')
});
});
await expect((0, mcp_server_1.executeTool)('get_case_details', {
case_id: '12345678'
})).rejects.toThrow('Case API call failed: 403 Forbidden');
});
test('should handle timeout errors', async () => {
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)
});
}
return Promise.reject(mockData_1.mockAbortError);
});
await expect((0, mcp_server_1.executeTool)('get_case_details', {
case_id: '12345678'
})).rejects.toThrow('Case API call timed out after 60 seconds');
});
});
describe('Case API Parameter Validation', () => {
test('should validate case_ids pattern (numbers and commas only)', async () => {
await expect((0, mcp_server_1.executeTool)('get_case_summary', {
case_ids: 'CSC12345,67890'
})).rejects.toThrow();
});
test('should validate contract_ids pattern', async () => {
// Valid patterns should work
await expect((0, mcp_server_1.executeTool)('search_cases_by_contract', {
contract_ids: 'CON123-456,CON789-012'
})).resolves.toBeDefined();
});
test('should validate user_ids pattern (allows email format)', async () => {
// Valid email patterns should work
await expect((0, mcp_server_1.executeTool)('search_cases_by_user', {
user_ids: 'user@company.com,user.name@company.co.uk'
})).resolves.toBeDefined();
});
test('should validate single status values only', async () => {
// Single status should work
await expect((0, mcp_server_1.executeTool)('search_cases_by_contract', {
contract_ids: 'CON123456',
status: 'O'
})).resolves.toBeDefined();
// Multiple statuses should not be allowed by schema
const tool = (await (0, mcp_server_1.getAvailableTools)()).find(t => t.name === 'search_cases_by_contract');
expect(tool?.inputSchema?.properties).toBeDefined();
if (tool?.inputSchema?.properties) {
const statusProperty = tool.inputSchema.properties.status;
expect(statusProperty?.enum).not.toContain('O,C');
}
});
});
});
//# sourceMappingURL=caseApi.test.js.map