/**
* @fileoverview Container-based integration tests for VyOS MCP server
*
* These tests run against real VyOS containers using testcontainers to ensure
* complete end-to-end functionality. Tests cover:
* - Real VyOS API interaction
* - Container lifecycle management
* - Network configuration scenarios
* - Error handling with real systems
* - Performance with actual VyOS instances
*
* @author VyOS MCP Server Tests
* @version 1.0.0
* @since 2025-07-12
*/
import { describe, expect, it, beforeAll, afterAll } from 'bun:test';
import { createVyOSTestEnvironment, type StartedContainers } from './utils/container-manager';
import { VyOSClient } from '../src/vyos-client';
describe('VyOS MCP Container Integration Tests', () => {
let testEnv: StartedContainers;
let vyosClient: VyOSClient;
// Extended timeout for container operations
const CONTAINER_TIMEOUT = 300000; // 5 minutes
beforeAll(async () => {
console.log('Setting up VyOS test environment...');
testEnv = await createVyOSTestEnvironment({
vyosApiKey: 'container-test-key-123',
timeout: CONTAINER_TIMEOUT,
});
vyosClient = testEnv.vyosClient;
console.log('VyOS test environment ready!');
}, CONTAINER_TIMEOUT);
afterAll(async () => {
if (testEnv) {
console.log('Cleaning up test environment...');
await testEnv.cleanup();
}
});
describe('Container Setup and Connectivity', () => {
it('should connect to VyOS container successfully', async () => {
expect(testEnv.vyosContainer).toBeDefined();
expect(vyosClient).toBeDefined();
});
it('should retrieve VyOS system information', async () => {
const systemInfo = await vyosClient.getSystemInfo();
expect(systemInfo).toBeDefined();
expect(typeof systemInfo).toBe('object');
});
it('should get VyOS version information', async () => {
const version = await vyosClient.getVersion();
expect(version).toBeDefined();
expect(typeof version).toBe('object');
});
});
describe('Configuration Management with Real VyOS', () => {
it('should show current configuration', async () => {
const config = await vyosClient.showConfig();
expect(config).toBeDefined();
expect(typeof config).toBe('object');
});
it('should show specific configuration path', async () => {
const systemConfig = await vyosClient.showConfig(['system']);
expect(systemConfig).toBeDefined();
expect(typeof systemConfig).toBe('object');
});
it('should set and commit configuration changes', async () => {
// Set a test configuration
await vyosClient.setConfig(
['system', 'domain-name'],
'container-test.local'
);
// Commit the change
await vyosClient.commit('Container test configuration');
// Verify the change
const domainConfig = await vyosClient.showConfig(['system', 'domain-name']);
expect(domainConfig).toBeDefined();
});
it('should delete configuration and commit', async () => {
// First set a test value
await vyosClient.setConfig(
['system', 'domain-search', 'domain', 'test.local'],
null
);
await vyosClient.commit('Add test domain search');
// Then delete it
await vyosClient.deleteConfig(['system', 'domain-search', 'domain', 'test.local']);
await vyosClient.commit('Remove test domain search');
// Verify it's gone
const exists = await vyosClient.configExists(['system', 'domain-search', 'domain', 'test.local']);
expect(exists).toBe(false);
});
it('should save configuration to startup config', async () => {
await vyosClient.save();
// If this doesn't throw, the save was successful
});
});
describe('Interface Configuration', () => {
it('should list available interfaces', async () => {
const interfaces = await vyosClient.getInterfaces();
expect(interfaces).toBeDefined();
expect(typeof interfaces).toBe('object');
});
it('should configure a loopback interface', async () => {
// Create loopback interface
await vyosClient.setConfig(
['interfaces', 'loopback', 'lo100', 'address'],
'10.100.100.1/32'
);
await vyosClient.setConfig(
['interfaces', 'loopback', 'lo100', 'description'],
'Container test loopback'
);
await vyosClient.commit('Add container test loopback interface');
// Verify the interface configuration
const loopbackConfig = await vyosClient.showConfig(['interfaces', 'loopback', 'lo100']);
expect(loopbackConfig).toBeDefined();
// Check if the interface exists in the interface list
const interfaces = await vyosClient.getInterfaces();
expect(interfaces).toBeDefined();
});
it('should get interface statistics', async () => {
const stats = await vyosClient.getInterfaceStatistics();
expect(stats).toBeDefined();
expect(typeof stats).toBe('object');
});
});
describe('Routing Configuration', () => {
it('should show current routing table', async () => {
const routes = await vyosClient.getRoutes();
expect(routes).toBeDefined();
});
it('should configure static routes', async () => {
// Add a static route
await vyosClient.setConfig(
['protocols', 'static', 'route', '192.168.200.0/24', 'next-hop'],
'10.100.100.1'
);
await vyosClient.setConfig(
['protocols', 'static', 'route', '192.168.200.0/24', 'description'],
'Container test route'
);
await vyosClient.commit('Add container test static route');
// Verify the route configuration
const routeConfig = await vyosClient.showConfig(['protocols', 'static']);
expect(routeConfig).toBeDefined();
});
});
describe('Operational Commands', () => {
it('should execute show commands', async () => {
const result = await vyosClient.showOperational(['version']);
expect(result).toBeDefined();
});
it('should execute show interfaces command', async () => {
const result = await vyosClient.showOperational(['interfaces']);
expect(result).toBeDefined();
});
it('should execute show ip route command', async () => {
const result = await vyosClient.showOperational(['ip', 'route']);
expect(result).toBeDefined();
});
});
describe('Network Diagnostics', () => {
it('should ping localhost successfully', async () => {
const pingResult = await vyosClient.ping('127.0.0.1', {
count: 3,
timeout: 5,
});
expect(pingResult).toBeDefined();
expect(typeof pingResult).toBe('object');
});
it('should ping container gateway', async () => {
// Try to ping the container gateway
const pingResult = await vyosClient.ping('172.17.0.1', {
count: 2,
timeout: 10,
});
expect(pingResult).toBeDefined();
});
it('should execute traceroute to localhost', async () => {
const traceResult = await vyosClient.traceroute('127.0.0.1', {
maxHops: 5,
});
expect(traceResult).toBeDefined();
expect(typeof traceResult).toBe('object');
});
});
describe('System Management', () => {
it('should get system uptime', async () => {
const uptime = await vyosClient.getUptime();
expect(uptime).toBeDefined();
});
it('should get system resources', async () => {
const resources = await vyosClient.getSystemResources();
expect(resources).toBeDefined();
expect(typeof resources).toBe('object');
});
it('should get system memory information', async () => {
const memory = await vyosClient.getSystemMemory();
expect(memory).toBeDefined();
expect(typeof memory).toBe('object');
});
it('should get system storage information', async () => {
const storage = await vyosClient.getSystemStorage();
expect(storage).toBeDefined();
expect(typeof storage).toBe('object');
});
});
describe('Error Handling with Real System', () => {
it('should handle invalid configuration paths gracefully', async () => {
await expect(
vyosClient.showConfig(['invalid', 'configuration', 'path'])
).rejects.toThrow();
});
it('should handle invalid set operations gracefully', async () => {
await expect(
vyosClient.setConfig(['invalid', 'config', 'path'], 'test-value')
).rejects.toThrow();
});
it('should handle invalid operational commands gracefully', async () => {
await expect(
vyosClient.showOperational(['invalid', 'command'])
).rejects.toThrow();
});
it('should handle unreachable ping targets gracefully', async () => {
// Try to ping an unreachable address
const pingResult = await vyosClient.ping('192.168.255.255', {
count: 1,
timeout: 2,
});
// Should not throw, but may return failure information
expect(pingResult).toBeDefined();
});
});
describe('Configuration Workflows', () => {
it('should complete a full interface configuration workflow', async () => {
const interfaceName = 'lo200';
const interfaceIP = '10.200.200.1/32';
const description = 'Full workflow test interface';
// Step 1: Configure interface
await vyosClient.setConfig(
['interfaces', 'loopback', interfaceName, 'address'],
interfaceIP
);
await vyosClient.setConfig(
['interfaces', 'loopback', interfaceName, 'description'],
description
);
// Step 2: Commit configuration
await vyosClient.commit('Full workflow interface configuration');
// Step 3: Verify configuration
const config = await vyosClient.showConfig(['interfaces', 'loopback', interfaceName]);
expect(config).toBeDefined();
// Step 4: Save configuration
await vyosClient.save();
// Step 5: Verify interface is active
const interfaces = await vyosClient.getInterfaces();
expect(interfaces).toBeDefined();
});
it('should handle configuration rollback scenario', async () => {
// Make a configuration change
await vyosClient.setConfig(
['system', 'time-zone'],
'US/Pacific'
);
// Commit with confirm (simulated by immediate rollback)
await vyosClient.commit('Test timezone change');
// Rollback by setting different value
await vyosClient.setConfig(
['system', 'time-zone'],
'UTC'
);
await vyosClient.commit('Rollback timezone to UTC');
// Verify rollback
const timezoneConfig = await vyosClient.showConfig(['system', 'time-zone']);
expect(timezoneConfig).toBeDefined();
});
});
describe('Performance and Reliability', () => {
it('should handle multiple concurrent operations', async () => {
const operations = [
vyosClient.getSystemInfo(),
vyosClient.getInterfaces(),
vyosClient.getRoutes(),
vyosClient.showConfig(['system']),
vyosClient.getSystemResources(),
];
const results = await Promise.all(operations);
// All operations should complete successfully
for (const result of results) {
expect(result).toBeDefined();
}
});
it('should maintain configuration consistency across operations', async () => {
const testValue = `container-test-${Date.now()}`;
// Set a unique value
await vyosClient.setConfig(
['system', 'host-name'],
testValue
);
await vyosClient.commit('Set test hostname');
// Read it back multiple times
for (let i = 0; i < 5; i++) {
const config = await vyosClient.showConfig(['system', 'host-name']);
expect(config).toBeDefined();
}
});
});
});
// Set timeout for the entire test suite
process.env.BUN_TEST_TIMEOUT = String(CONTAINER_TIMEOUT);