Skip to main content
Glama
integration.test.ts•13 kB
#!/usr/bin/env node /** * Integration Test Script * * This script tests the complete ACI MCP server against either a real APIC * or the mock server. It exercises all major tools and validates responses. */ import { ACIApiService } from '../src/services/aciApi.js'; import { loadConfig } from '../src/utils/config.js'; import { MockAPICServer } from './mock-server.js'; interface TestResult { name: string; passed: boolean; error?: string; duration: number; } class IntegrationTester { private aciApi: ACIApiService; private mockServer?: MockAPICServer; private results: TestResult[] = []; constructor(useMockServer: boolean = true) { if (useMockServer) { this.mockServer = new MockAPICServer(8443); // Configure for mock server const config = { apicUrl: 'http://localhost:8443', username: 'admin', password: 'admin', validateCerts: false, timeout: 5000 }; this.aciApi = new ACIApiService(config); } else { // Use real APIC configuration const config = loadConfig(); this.aciApi = new ACIApiService(config); } } async runTests(): Promise<TestResult[]> { console.log('šŸš€ Starting ACI MCP Server Integration Tests\n'); if (this.mockServer) { console.log('šŸ“” Starting mock APIC server...'); this.mockServer.start(); // Wait a moment for server to start await this.sleep(1000); } try { // Authentication tests await this.runTest('Authentication', () => this.testAuthentication()); // Tenant management tests await this.runTest('List Tenants', () => this.testListTenants()); await this.runTest('Get Tenant', () => this.testGetTenant()); await this.runTest('Create Tenant', () => this.testCreateTenant()); // Health monitoring tests await this.runTest('Fabric Health', () => this.testFabricHealth()); await this.runTest('List Faults', () => this.testListFaults()); await this.runTest('List Faults by Severity', () => this.testListFaultsBySeverity()); // Infrastructure tests await this.runTest('List Nodes', () => this.testListNodes()); await this.runTest('System Info', () => this.testSystemInfo()); // Configuration object tests await this.runTest('List Application Profiles', () => this.testListApplicationProfiles()); await this.runTest('List Endpoint Groups', () => this.testListEndpointGroups()); await this.runTest('List Bridge Domains', () => this.testListBridgeDomains()); await this.runTest('List VRFs', () => this.testListVRFs()); await this.runTest('List Contracts', () => this.testListContracts()); } catch (error) { console.error('āŒ Test suite failed:', error); } finally { if (this.mockServer) { console.log('\nšŸ›‘ Stopping mock APIC server...'); this.mockServer.stop(); } } this.printResults(); return this.results; } private async runTest(name: string, testFunction: () => Promise<void>): Promise<void> { const startTime = Date.now(); try { await testFunction(); const duration = Date.now() - startTime; this.results.push({ name, passed: true, duration }); console.log(`āœ… ${name} (${duration}ms)`); } catch (error) { const duration = Date.now() - startTime; const errorMsg = error instanceof Error ? error.message : String(error); this.results.push({ name, passed: false, error: errorMsg, duration }); console.log(`āŒ ${name} (${duration}ms): ${errorMsg}`); } } private async testAuthentication(): Promise<void> { await this.aciApi.authenticate(); // If we get here without throwing, authentication succeeded } private async testListTenants(): Promise<void> { const tenants = await this.aciApi.listTenants(); if (!tenants || !tenants.imdata || !Array.isArray(tenants.imdata)) { throw new Error('Invalid tenants response format'); } if (tenants.imdata.length === 0) { console.log(' āš ļø No tenants found (this might be expected)'); } else { console.log(` šŸ“Š Found ${tenants.imdata.length} tenants`); } } private async testGetTenant(): Promise<void> { const tenants = await this.aciApi.listTenants(); if (tenants.imdata && tenants.imdata.length > 0) { const firstTenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (firstTenantName) { const tenant = await this.aciApi.getTenant(firstTenantName); if (!tenant) { throw new Error(`Failed to get tenant ${firstTenantName}`); } console.log(` šŸ¢ Retrieved tenant: ${firstTenantName}`); } else { throw new Error('No valid tenant found to test with'); } } else { console.log(' āš ļø No tenants to test with'); } } private async testCreateTenant(): Promise<void> { const testTenantName = `test-tenant-${Date.now()}`; try { await this.aciApi.createTenant(testTenantName, 'Integration test tenant'); console.log(` šŸ†• Created tenant: ${testTenantName}`); // Clean up - try to delete the test tenant try { await this.aciApi.deleteTenant(testTenantName); console.log(` šŸ—‘ļø Cleaned up tenant: ${testTenantName}`); } catch (cleanupError) { console.log(` āš ļø Could not clean up tenant: ${testTenantName}`); } } catch (error) { throw new Error(`Failed to create tenant: ${error}`); } } private async testFabricHealth(): Promise<void> { const health = await this.aciApi.getFabricHealth(); if (!health || !health.imdata || !Array.isArray(health.imdata)) { throw new Error('Invalid fabric health response format'); } if (health.imdata.length > 0) { const healthScore = health.imdata[0].fabricHealthTotal?.attributes?.cur; console.log(` šŸ’š Fabric health score: ${healthScore}`); } else { console.log(' āš ļø No fabric health data available'); } } private async testListFaults(): Promise<void> { const faults = await this.aciApi.listFaults(); if (!faults || !faults.imdata || !Array.isArray(faults.imdata)) { throw new Error('Invalid faults response format'); } console.log(` 🚨 Found ${faults.imdata.length} total faults`); } private async testListFaultsBySeverity(): Promise<void> { const criticalFaults = await this.aciApi.listFaults('critical'); if (!criticalFaults || !criticalFaults.imdata || !Array.isArray(criticalFaults.imdata)) { throw new Error('Invalid critical faults response format'); } console.log(` šŸ”“ Found ${criticalFaults.imdata.length} critical faults`); const warningFaults = await this.aciApi.listFaults('warning'); if (!warningFaults || !warningFaults.imdata || !Array.isArray(warningFaults.imdata)) { throw new Error('Invalid warning faults response format'); } console.log(` 🟔 Found ${warningFaults.imdata.length} warning faults`); } private async testListNodes(): Promise<void> { const nodes = await this.aciApi.listNodes(); if (!nodes || !nodes.imdata || !Array.isArray(nodes.imdata)) { throw new Error('Invalid nodes response format'); } console.log(` šŸ–„ļø Found ${nodes.imdata.length} fabric nodes`); } private async testSystemInfo(): Promise<void> { const sysInfo = await this.aciApi.getSystemInfo(); if (!sysInfo) { throw new Error('No system information returned'); } const name = sysInfo.topSystem?.attributes?.name; const version = sysInfo.topSystem?.attributes?.version; console.log(` ā„¹ļø System: ${name}, Version: ${version}`); } private async testListApplicationProfiles(): Promise<void> { // Get first tenant to test with const tenants = await this.aciApi.listTenants(); if (tenants.imdata && tenants.imdata.length > 0) { const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (tenantName) { const aps = await this.aciApi.listApplicationProfiles(tenantName); if (!aps || !aps.imdata || !Array.isArray(aps.imdata)) { throw new Error('Invalid application profiles response format'); } console.log(` šŸ“± Found ${aps.imdata.length} application profiles in ${tenantName}`); } } else { console.log(' āš ļø No tenants available to test application profiles'); } } private async testListEndpointGroups(): Promise<void> { const tenants = await this.aciApi.listTenants(); if (tenants.imdata && tenants.imdata.length > 0) { const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (tenantName) { const aps = await this.aciApi.listApplicationProfiles(tenantName); if (aps.imdata && aps.imdata.length > 0) { const apName = aps.imdata[0].fvAp?.attributes?.name; if (apName) { const epgs = await this.aciApi.listEndpointGroups(tenantName, apName); if (!epgs || !epgs.imdata || !Array.isArray(epgs.imdata)) { throw new Error('Invalid endpoint groups response format'); } console.log(` šŸŽÆ Found ${epgs.imdata.length} EPGs in ${tenantName}/${apName}`); return; } } } } console.log(' āš ļø No application profiles available to test EPGs'); } private async testListBridgeDomains(): Promise<void> { const tenants = await this.aciApi.listTenants(); if (tenants.imdata && tenants.imdata.length > 0) { const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (tenantName) { const bds = await this.aciApi.listBridgeDomains(tenantName); if (!bds || !bds.imdata || !Array.isArray(bds.imdata)) { throw new Error('Invalid bridge domains response format'); } console.log(` šŸŒ‰ Found ${bds.imdata.length} bridge domains in ${tenantName}`); } } else { console.log(' āš ļø No tenants available to test bridge domains'); } } private async testListVRFs(): Promise<void> { const tenants = await this.aciApi.listTenants(); if (tenants.imdata && tenants.imdata.length > 0) { const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (tenantName) { const vrfs = await this.aciApi.listVRFs(tenantName); if (!vrfs || !vrfs.imdata || !Array.isArray(vrfs.imdata)) { throw new Error('Invalid VRFs response format'); } console.log(` šŸ›£ļø Found ${vrfs.imdata.length} VRFs in ${tenantName}`); } } else { console.log(' āš ļø No tenants available to test VRFs'); } } private async testListContracts(): Promise<void> { const tenants = await this.aciApi.listTenants(); if (tenants.imdata && tenants.imdata.length > 0) { const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (tenantName) { const contracts = await this.aciApi.listContracts(tenantName); if (!contracts || !contracts.imdata || !Array.isArray(contracts.imdata)) { throw new Error('Invalid contracts response format'); } console.log(` šŸ“‹ Found ${contracts.imdata.length} contracts in ${tenantName}`); } } else { console.log(' āš ļø No tenants available to test contracts'); } } private sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } private printResults(): void { console.log('\nšŸ“Š Test Results Summary:'); console.log('=' .repeat(50)); const passed = this.results.filter(r => r.passed).length; const failed = this.results.filter(r => !r.passed).length; const totalDuration = this.results.reduce((sum, r) => sum + r.duration, 0); console.log(`āœ… Passed: ${passed}`); console.log(`āŒ Failed: ${failed}`); console.log(`ā±ļø Total time: ${totalDuration}ms`); console.log(`šŸ“ˆ Success rate: ${((passed / this.results.length) * 100).toFixed(1)}%`); if (failed > 0) { console.log('\nāŒ Failed Tests:'); this.results .filter(r => !r.passed) .forEach(r => console.log(` - ${r.name}: ${r.error}`)); } console.log('\n' + '=' .repeat(50)); } } // Run tests if this script is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const useMock = process.argv.includes('--mock') || !process.env.ACI_APIC_URL; console.log(`šŸ”§ Running tests with ${useMock ? 'Mock APIC Server' : 'Real APIC'}`); const tester = new IntegrationTester(useMock); tester.runTests() .then((results) => { const failed = results.filter(r => !r.passed).length; process.exit(failed > 0 ? 1 : 0); }) .catch((error) => { console.error('šŸ’„ Test suite crashed:', error); process.exit(1); }); } export { IntegrationTester };

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/jim-coyne/ACI_MCP'

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