/**
* @fileoverview Container-based MCP server tests
*
* Tests the MCP server functionality against a real VyOS container to ensure
* proper MCP tool operation with actual VyOS systems.
*
* @author VyOS MCP Server Tests
* @version 1.0.0
* @since 2025-07-12
*/
import { describe, expect, it, beforeAll, afterAll, spyOn } from 'bun:test';
import { createVyOSTestEnvironment, type StartedContainers } from './utils/container-manager';
import server, { __testUtils__ } from '../src/index';
describe('VyOS MCP Server with Container', () => {
let testEnv: StartedContainers;
let vyosHost: string;
let vyosApiKey: string;
const CONTAINER_TIMEOUT = 300000; // 5 minutes
beforeAll(async () => {
console.log('Setting up container environment for MCP server tests...');
testEnv = await createVyOSTestEnvironment({
vyosApiKey: 'mcp-server-test-key',
timeout: CONTAINER_TIMEOUT,
});
// Extract connection details
const port = testEnv.vyosContainer.getMappedPort(443);
const host = testEnv.vyosContainer.getHost();
vyosHost = `https://${host}:${port}`;
vyosApiKey = 'mcp-server-test-key';
console.log(`VyOS container ready at: ${vyosHost}`);
}, CONTAINER_TIMEOUT);
afterAll(async () => {
if (testEnv) {
await testEnv.cleanup();
}
});
beforeEach(() => {
// Reset MCP server transport state
__testUtils__.resetTransport();
});
describe('MCP Server Health and Basic Functionality', () => {
it('should provide health endpoint', async () => {
const response = await server.request('/health', {
method: 'GET',
});
expect(response.status).toBe(200);
const health = await response.json();
expect(health.status).toBe('healthy');
expect(health.version).toBeDefined();
expect(health.transport).toBe('disconnected');
});
it('should handle SSE endpoint for MCP transport', async () => {
const response = await server.request('/sse', {
method: 'GET',
headers: { 'Accept': 'text/event-stream' },
});
expect(response.status).toBe(200);
expect(response.headers.get('Content-Type')).toContain('text/event-stream');
});
});
describe('VyOS Connection Tool with Real Container', () => {
it('should connect to VyOS container via MCP tool', async () => {
const connectResponse = await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
expect(connectResponse.status).toBe(200);
const result = await connectResponse.json();
expect(result).toEqual([
{
type: 'text',
text: expect.stringContaining('Successfully connected to VyOS system'),
},
]);
});
it('should handle invalid connection parameters', async () => {
const connectResponse = await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: 'https://invalid-host:443',
apiKey: 'invalid-key',
timeout: 5000,
verifySSL: false,
}),
});
expect(connectResponse.status).toBe(200);
const result = await connectResponse.json();
expect(result[0].text).toContain('Failed to connect to VyOS system');
});
});
describe('Configuration Management Tools with Container', () => {
beforeEach(async () => {
// Establish connection for each test
await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
});
it('should retrieve VyOS system information', async () => {
const response = await server.request('/system-info', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('System Information');
});
it('should show VyOS configuration', async () => {
const response = await server.request('/show-config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: ['system'],
format: 'json',
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('Configuration');
});
it('should set and commit configuration changes', async () => {
// Set configuration
const setResponse = await server.request('/set-config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: ['system', 'domain-name'],
value: 'mcp-test.local',
comment: 'MCP container test',
}),
});
expect(setResponse.status).toBe(200);
// Commit configuration
const commitResponse = await server.request('/commit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
comment: 'MCP container test commit',
}),
});
expect(commitResponse.status).toBe(200);
const commitResult = await commitResponse.json();
expect(commitResult[0].text).toContain('successfully committed');
});
it('should save configuration', async () => {
const response = await server.request('/save-config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].text).toContain('saved successfully');
});
});
describe('Network Diagnostic Tools with Container', () => {
beforeEach(async () => {
// Establish connection for each test
await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
});
it('should ping localhost from VyOS container', async () => {
const response = await server.request('/ping', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: '127.0.0.1',
count: 3,
timeout: 5,
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('ping');
});
it('should execute traceroute from VyOS container', async () => {
const response = await server.request('/traceroute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: '127.0.0.1',
maxHops: 5,
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('traceroute');
});
});
describe('Interface Management with Container', () => {
beforeEach(async () => {
// Establish connection for each test
await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
});
it('should configure interface through MCP tool', async () => {
const response = await server.request('/configure-interface', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'lo300',
type: 'loopback',
address: '10.300.300.1/32',
description: 'MCP test loopback interface',
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].text).toContain('successfully configured');
});
it('should get interface statistics', async () => {
const response = await server.request('/interface-stats', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('Interface Statistics');
});
});
describe('Operational Commands with Container', () => {
beforeEach(async () => {
// Establish connection for each test
await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
});
it('should execute show operational commands', async () => {
const response = await server.request('/show-operational', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
command: ['version'],
format: 'text',
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('version');
});
it('should get routing table', async () => {
const response = await server.request('/routing-table', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
family: 'ipv4',
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('Routing Table');
});
});
describe('Health Check with Container', () => {
beforeEach(async () => {
// Establish connection for each test
await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
});
it('should perform comprehensive health check', async () => {
const response = await server.request('/health-check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
includeInterfaces: true,
includeRouting: true,
includeServices: true,
includeResources: true,
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].type).toBe('text');
expect(result[0].text).toContain('Health Check Report');
});
});
describe('Error Handling with Real Container', () => {
it('should handle connection failures gracefully', async () => {
// Try to connect to non-existent VyOS instance
const response = await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: 'https://127.0.0.1:9999',
apiKey: 'invalid-key',
timeout: 1000,
verifySSL: false,
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].text).toContain('Failed to connect');
});
it('should handle API errors from real VyOS', async () => {
// Connect with valid parameters first
await server.request('/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
host: vyosHost,
apiKey: vyosApiKey,
timeout: 30000,
verifySSL: false,
}),
});
// Try invalid configuration
const response = await server.request('/set-config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: ['invalid', 'config', 'path'],
value: 'test-value',
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result[0].text).toContain('Failed to set configuration');
});
});
});
// Set timeout for the entire test suite
process.env.BUN_TEST_TIMEOUT = String(CONTAINER_TIMEOUT);