Skip to main content
Glama
aciApi.test.ts9.29 kB
import { describe, test, expect, beforeEach, jest } from '@jest/globals'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { ACIApiService, ACIConfig } from '../src/services/aciApi.js'; describe('ACIApiService', () => { let aciApi: ACIApiService; let mockAxios: MockAdapter; let config: ACIConfig; beforeEach(() => { config = { apicUrl: 'https://test-apic.example.com', username: 'testuser', password: 'testpass', validateCerts: false, timeout: 10000 }; aciApi = new ACIApiService(config); mockAxios = new MockAdapter(axios); }); afterEach(() => { mockAxios.restore(); }); describe('Authentication', () => { test('should authenticate with password successfully', async () => { const loginResponse = { imdata: [{ aaaLogin: { attributes: { token: 'test-token-123', sessionTimeoutSeconds: '600' } } }] }; mockAxios.onPost('/api/aaaLogin.json').reply(200, loginResponse); await aciApi.authenticate(); expect(mockAxios.history.post).toHaveLength(1); expect(mockAxios.history.post[0].url).toBe('/api/aaaLogin.json'); const requestData = JSON.parse(mockAxios.history.post[0].data); expect(requestData.aaaUser.attributes.name).toBe('testuser'); expect(requestData.aaaUser.attributes.pwd).toBe('testpass'); }); test('should handle authentication failure', async () => { mockAxios.onPost('/api/aaaLogin.json').reply(401, { imdata: [{ error: { attributes: { code: '401', text: 'Authentication failed' } } }] }); await expect(aciApi.authenticate()).rejects.toThrow(); }); test('should handle certificate authentication', async () => { const certConfig: ACIConfig = { apicUrl: 'https://test-apic.example.com', username: 'testuser', certificateName: 'test-cert', privateKey: 'test-private-key', validateCerts: false, timeout: 10000 }; const certAciApi = new ACIApiService(certConfig); const refreshResponse = { imdata: [{ aaaLogin: { attributes: { token: 'cert-token-123', sessionTimeoutSeconds: '600' } } }] }; mockAxios.onGet('/api/aaaRefresh.json').reply(200, refreshResponse); await certAciApi.authenticate(); expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get[0].url).toBe('/api/aaaRefresh.json'); expect(mockAxios.history.get[0].headers?.['X-ACI-Certificate']).toBe('test-cert'); }); }); describe('Tenant Operations', () => { beforeEach(async () => { // Mock successful authentication const loginResponse = { imdata: [{ aaaLogin: { attributes: { token: 'test-token-123', sessionTimeoutSeconds: '600' } } }] }; mockAxios.onPost('/api/aaaLogin.json').reply(200, loginResponse); await aciApi.authenticate(); }); test('should list tenants successfully', async () => { const tenantsResponse = { imdata: [ { fvTenant: { attributes: { name: 'tenant1', dn: 'uni/tn-tenant1' } } }, { fvTenant: { attributes: { name: 'tenant2', dn: 'uni/tn-tenant2' } } } ] }; mockAxios.onGet('/api/node/class/fvTenant.json').reply(200, tenantsResponse); const tenants = await aciApi.listTenants(); expect(tenants).toEqual(tenantsResponse); expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get[0].url).toBe('/api/node/class/fvTenant.json'); }); test('should get specific tenant', async () => { const tenantResponse = { imdata: [{ fvTenant: { attributes: { name: 'test-tenant', dn: 'uni/tn-test-tenant' } } }] }; mockAxios.onGet('/api/node/class/fvTenant.json').reply(200, tenantResponse); const tenant = await aciApi.getTenant('test-tenant'); expect(tenant).toEqual(tenantResponse.imdata[0]); }); test('should create tenant successfully', async () => { const createResponse = { imdata: [] }; mockAxios.onPost('/api/node/mo/uni.json').reply(200, createResponse); await aciApi.createTenant('new-tenant', 'Test tenant description'); expect(mockAxios.history.post).toHaveLength(2); // 1 for auth, 1 for create const createRequest = JSON.parse(mockAxios.history.post[1].data); expect(createRequest.fvTenant.attributes.name).toBe('new-tenant'); expect(createRequest.fvTenant.attributes.descr).toBe('Test tenant description'); }); test('should delete tenant successfully', async () => { mockAxios.onDelete('/api/node/mo/uni/tn-test-tenant.json').reply(200, { imdata: [] }); await aciApi.deleteTenant('test-tenant'); expect(mockAxios.history.delete).toHaveLength(1); expect(mockAxios.history.delete[0].url).toBe('/api/node/mo/uni/tn-test-tenant.json'); }); }); describe('Health Monitoring', () => { beforeEach(async () => { // Mock successful authentication const loginResponse = { imdata: [{ aaaLogin: { attributes: { token: 'test-token-123', sessionTimeoutSeconds: '600' } } }] }; mockAxios.onPost('/api/aaaLogin.json').reply(200, loginResponse); await aciApi.authenticate(); }); test('should get fabric health', async () => { const healthResponse = { imdata: [{ fabricHealthTotal: { attributes: { cur: '95', dn: 'topology/health' } } }] }; mockAxios.onGet('/api/node/class/fabricHealthTotal.json').reply(200, healthResponse); const health = await aciApi.getFabricHealth(); expect(health).toEqual(healthResponse); expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get[0].url).toBe('/api/node/class/fabricHealthTotal.json'); }); test('should list faults with severity filter', async () => { const faultsResponse = { imdata: [{ faultInst: { attributes: { severity: 'critical', descr: 'Test critical fault', dn: 'topology/pod-1/node-101/fault-123' } } }] }; mockAxios.onGet('/api/node/class/faultInst.json?query-target-filter=eq(faultInst.severity,"critical")') .reply(200, faultsResponse); const faults = await aciApi.listFaults('critical'); expect(faults).toEqual(faultsResponse); expect(mockAxios.history.get).toHaveLength(1); }); test('should list all faults when no severity specified', async () => { const faultsResponse = { imdata: [ { faultInst: { attributes: { severity: 'critical', descr: 'Critical fault' } } }, { faultInst: { attributes: { severity: 'warning', descr: 'Warning fault' } } } ] }; mockAxios.onGet('/api/node/class/faultInst.json').reply(200, faultsResponse); const faults = await aciApi.listFaults(); expect(faults).toEqual(faultsResponse); expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get[0].url).toBe('/api/node/class/faultInst.json'); }); }); describe('Configuration Management', () => { test('should handle missing configuration gracefully', () => { expect(() => { new ACIApiService({} as ACIConfig); }).not.toThrow(); }); test('should apply default configuration values', () => { const minimalConfig = { apicUrl: 'https://test.com', username: 'user' }; const api = new ACIApiService(minimalConfig); // Check that defaults are applied via the constructor expect(api).toBeDefined(); }); }); describe('Error Handling', () => { test('should handle network errors gracefully', async () => { mockAxios.onPost('/api/aaaLogin.json').networkError(); await expect(aciApi.authenticate()).rejects.toThrow(); }); test('should handle malformed responses', async () => { mockAxios.onPost('/api/aaaLogin.json').reply(200, 'invalid json'); await expect(aciApi.authenticate()).rejects.toThrow(); }); test('should handle APIC errors', async () => { mockAxios.onPost('/api/aaaLogin.json').reply(500, { error: 'Internal server error' }); await expect(aciApi.authenticate()).rejects.toThrow(); }); }); });

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