Skip to main content
Glama
danielbodnar

VyOS MCP Server

by danielbodnar
vyos-schemas.test.ts17.9 kB
import { describe, expect, it } from 'bun:test'; import { VyOSAuthSchema, ConfigPathSchema, ShowConfigRequestSchema, SetConfigRequestSchema, DeleteConfigRequestSchema, ConfigExistsRequestSchema, ReturnValuesRequestSchema, CommitRequestSchema, ShowOperationalRequestSchema, ResetRequestSchema, GenerateRequestSchema, InterfaceSchema, StaticRouteSchema, PingRequestSchema, TracerouteRequestSchema, HealthCheckRequestSchema, } from '../src/schemas/vyos-schemas'; /** * @fileoverview Comprehensive test suite for VyOS Zod schemas. * * Tests schema validation for all VyOS API operations including: * - Authentication schemas * - Configuration management schemas * - Operational command schemas * - Network interface schemas * - Diagnostic tool schemas * - Input validation and error handling * * @author VyOS MCP Server Tests * @version 1.0.0 * @since 2025-01-13 */ describe('VyOS Schemas', () => { describe('VyOSAuthSchema', () => { it('should validate correct authentication object', () => { const validAuth = { host: 'https://vyos.example.com', apiKey: 'valid-api-key-12345', timeout: 30000, verifySSL: false, }; const result = VyOSAuthSchema.parse(validAuth); expect(result).toEqual(validAuth); }); it('should validate with minimal required fields', () => { const minimalAuth = { host: 'https://192.168.1.1', apiKey: 'key', }; const result = VyOSAuthSchema.parse(minimalAuth); expect(result.host).toBe(minimalAuth.host); expect(result.apiKey).toBe(minimalAuth.apiKey); expect(result.timeout).toBe(30000); // default expect(result.verifySSL).toBe(false); // default }); it('should reject invalid URL format', () => { const invalidAuth = { host: 'not-a-valid-url', apiKey: 'valid-key', }; expect(() => VyOSAuthSchema.parse(invalidAuth)).toThrow(); }); it('should reject empty API key', () => { const invalidAuth = { host: 'https://vyos.test', apiKey: '', }; expect(() => VyOSAuthSchema.parse(invalidAuth)).toThrow(); }); it('should reject missing required fields', () => { const incomplete = { host: 'https://vyos.test', // missing apiKey }; expect(() => VyOSAuthSchema.parse(incomplete)).toThrow(); }); }); describe('ConfigPathSchema', () => { it('should validate valid configuration paths', () => { const validPaths = [ ['interfaces', 'ethernet', 'eth0'], ['protocols', 'bgp', '65001', 'neighbor', '192.168.1.1'], ['system', 'hostname'], [], ]; for (const path of validPaths) { const result = ConfigPathSchema.parse(path); expect(result).toEqual(path); } }); it('should reject non-array inputs', () => { const invalidPaths = [ 'not-an-array', 123, { path: 'object' }, null, ]; for (const path of invalidPaths) { expect(() => ConfigPathSchema.parse(path)).toThrow(); } }); it('should reject arrays with non-string elements', () => { const invalidPaths = [ ['interfaces', 123, 'eth0'], ['system', true], [null, 'hostname'], ]; for (const path of invalidPaths) { expect(() => ConfigPathSchema.parse(path)).toThrow(); } }); }); describe('ShowConfigRequestSchema', () => { it('should validate valid show config requests', () => { const validRequests = [ { path: ['interfaces'], format: 'json' }, { path: ['system'], format: 'commands' }, { format: 'json' }, // no path {}, // all defaults ]; for (const request of validRequests) { const result = ShowConfigRequestSchema.parse(request); expect(result.format).toBeDefined(); } }); it('should apply default format', () => { const request = { path: ['interfaces'] }; const result = ShowConfigRequestSchema.parse(request); expect(result.format).toBe('json'); }); it('should reject invalid format values', () => { const invalidRequest = { path: ['system'], format: 'xml', }; expect(() => ShowConfigRequestSchema.parse(invalidRequest)).toThrow(); }); }); describe('SetConfigRequestSchema', () => { it('should validate set config requests with value', () => { const validRequest = { path: ['interfaces', 'ethernet', 'eth0', 'address'], value: '192.168.1.1/24', comment: 'Set LAN interface IP', }; const result = SetConfigRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); it('should validate valueless set config requests', () => { const validRequest = { path: ['service', 'ssh'], comment: 'Enable SSH service', }; const result = SetConfigRequestSchema.parse(validRequest); expect(result.path).toEqual(['service', 'ssh']); expect(result.value).toBeUndefined(); }); it('should reject requests without path', () => { const invalidRequest = { value: '192.168.1.1/24', }; expect(() => SetConfigRequestSchema.parse(invalidRequest)).toThrow(); }); }); describe('DeleteConfigRequestSchema', () => { it('should validate delete config requests', () => { const validRequest = { path: ['interfaces', 'ethernet', 'eth1'], }; const result = DeleteConfigRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); it('should reject requests without path', () => { expect(() => DeleteConfigRequestSchema.parse({})).toThrow(); }); }); describe('ConfigExistsRequestSchema', () => { it('should validate config exists requests', () => { const validRequest = { path: ['protocols', 'bgp'], }; const result = ConfigExistsRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); }); describe('ReturnValuesRequestSchema', () => { it('should validate return values requests', () => { const validRequest = { path: ['interfaces', 'ethernet'], }; const result = ReturnValuesRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); }); describe('CommitRequestSchema', () => { it('should validate commit requests with all options', () => { const validRequest = { comment: 'Network configuration update', confirmTimeout: 10, }; const result = CommitRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); it('should validate minimal commit requests', () => { const result = CommitRequestSchema.parse({}); expect(result.comment).toBeUndefined(); expect(result.confirmTimeout).toBeUndefined(); }); it('should validate commit with only comment', () => { const validRequest = { comment: 'Test commit' }; const result = CommitRequestSchema.parse(validRequest); expect(result.comment).toBe('Test commit'); }); it('should validate commit with only timeout', () => { const validRequest = { confirmTimeout: 15 }; const result = CommitRequestSchema.parse(validRequest); expect(result.confirmTimeout).toBe(15); }); }); describe('ShowOperationalRequestSchema', () => { it('should validate operational show requests', () => { const validRequests = [ { path: ['interfaces', 'ethernet', 'statistics'], format: 'json' }, { path: ['protocols', 'bgp', 'summary'], format: 'raw' }, { path: ['system', 'uptime'] }, // default format ]; for (const request of validRequests) { const result = ShowOperationalRequestSchema.parse(request); expect(result.path).toBeDefined(); expect(result.format).toBeDefined(); } }); it('should apply default format', () => { const request = { path: ['system', 'uptime'] }; const result = ShowOperationalRequestSchema.parse(request); expect(result.format).toBe('json'); }); it('should reject invalid format', () => { const invalidRequest = { path: ['system'], format: 'xml', }; expect(() => ShowOperationalRequestSchema.parse(invalidRequest)).toThrow(); }); }); describe('ResetRequestSchema', () => { it('should validate reset requests', () => { const validRequest = { path: ['interfaces', 'ethernet', 'eth0', 'counters'], }; const result = ResetRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); }); describe('GenerateRequestSchema', () => { it('should validate generate requests', () => { const validRequest = { path: ['wireguard', 'keypair'], }; const result = GenerateRequestSchema.parse(validRequest); expect(result).toEqual(validRequest); }); }); describe('InterfaceSchema', () => { it('should validate complete interface configuration', () => { const validInterface = { name: 'eth0', address: '192.168.1.1/24', description: 'LAN Interface', enabled: true, mtu: 1500, vlan: 100, }; const result = InterfaceSchema.parse(validInterface); expect(result).toEqual(validInterface); }); it('should validate minimal interface configuration', () => { const minimalInterface = { name: 'eth1', }; const result = InterfaceSchema.parse(minimalInterface); expect(result.name).toBe('eth1'); expect(result.enabled).toBe(true); // default }); it('should validate VLAN interface', () => { const vlanInterface = { name: 'eth0.100', address: '10.100.1.1/24', vlan: 100, }; const result = InterfaceSchema.parse(vlanInterface); expect(result.vlan).toBe(100); }); it('should reject invalid interface names', () => { const invalidInterface = { name: '', address: '192.168.1.1/24', }; expect(() => InterfaceSchema.parse(invalidInterface)).toThrow(); }); it('should reject missing name', () => { const invalidInterface = { address: '192.168.1.1/24', }; expect(() => InterfaceSchema.parse(invalidInterface)).toThrow(); }); }); describe('StaticRouteSchema', () => { it('should validate complete static route', () => { const validRoute = { destination: '10.0.0.0/24', nextHop: '192.168.1.1', distance: 110, tag: 500, }; const result = StaticRouteSchema.parse(validRoute); expect(result).toEqual(validRoute); }); it('should validate minimal static route', () => { const minimalRoute = { destination: '0.0.0.0/0', nextHop: '192.168.1.1', }; const result = StaticRouteSchema.parse(minimalRoute); expect(result.destination).toBe('0.0.0.0/0'); expect(result.nextHop).toBe('192.168.1.1'); }); it('should reject routes without destination', () => { const invalidRoute = { nextHop: '192.168.1.1', }; expect(() => StaticRouteSchema.parse(invalidRoute)).toThrow(); }); it('should reject routes without next hop', () => { const invalidRoute = { destination: '10.0.0.0/24', }; expect(() => StaticRouteSchema.parse(invalidRoute)).toThrow(); }); }); describe('PingRequestSchema', () => { it('should validate complete ping request', () => { const validPing = { host: '8.8.8.8', count: 5, interval: 2, timeout: 10, size: 64, source: '192.168.1.100', }; const result = PingRequestSchema.parse(validPing); expect(result).toEqual(validPing); }); it('should validate minimal ping request with defaults', () => { const minimalPing = { host: '1.1.1.1', }; const result = PingRequestSchema.parse(minimalPing); expect(result.host).toBe('1.1.1.1'); expect(result.count).toBe(3); expect(result.interval).toBe(1); expect(result.timeout).toBe(5); expect(result.size).toBe(56); }); it('should reject ping without host', () => { const invalidPing = { count: 5, }; expect(() => PingRequestSchema.parse(invalidPing)).toThrow(); }); }); describe('TracerouteRequestSchema', () => { it('should validate complete traceroute request', () => { const validTraceroute = { host: '8.8.8.8', maxHops: 20, timeout: 3, source: '192.168.1.100', }; const result = TracerouteRequestSchema.parse(validTraceroute); expect(result).toEqual(validTraceroute); }); it('should validate minimal traceroute request with defaults', () => { const minimalTraceroute = { host: 'google.com', }; const result = TracerouteRequestSchema.parse(minimalTraceroute); expect(result.host).toBe('google.com'); expect(result.maxHops).toBe(30); expect(result.timeout).toBe(5); }); it('should reject traceroute without host', () => { const invalidTraceroute = { maxHops: 15, }; expect(() => TracerouteRequestSchema.parse(invalidTraceroute)).toThrow(); }); }); describe('HealthCheckRequestSchema', () => { it('should validate complete health check request', () => { const validHealthCheck = { includeInterfaces: true, includeRouting: true, includeServices: false, includeResources: true, }; const result = HealthCheckRequestSchema.parse(validHealthCheck); expect(result).toEqual(validHealthCheck); }); it('should validate minimal health check with defaults', () => { const minimalHealthCheck = {}; const result = HealthCheckRequestSchema.parse(minimalHealthCheck); expect(result.includeInterfaces).toBe(true); expect(result.includeRouting).toBe(true); expect(result.includeServices).toBe(true); expect(result.includeResources).toBe(true); }); it('should validate partial health check options', () => { const partialHealthCheck = { includeInterfaces: false, includeResources: false, }; const result = HealthCheckRequestSchema.parse(partialHealthCheck); expect(result.includeInterfaces).toBe(false); expect(result.includeRouting).toBe(true); // default expect(result.includeServices).toBe(true); // default expect(result.includeResources).toBe(false); }); }); describe('Schema edge cases and error handling', () => { it('should handle null and undefined values appropriately', () => { expect(() => VyOSAuthSchema.parse(null)).toThrow(); expect(() => VyOSAuthSchema.parse(undefined)).toThrow(); expect(() => ConfigPathSchema.parse(null)).toThrow(); expect(() => InterfaceSchema.parse(undefined)).toThrow(); }); it('should handle empty objects', () => { // These should work with defaults expect(() => ShowConfigRequestSchema.parse({})).not.toThrow(); expect(() => CommitRequestSchema.parse({})).not.toThrow(); expect(() => HealthCheckRequestSchema.parse({})).not.toThrow(); // These require specific fields expect(() => VyOSAuthSchema.parse({})).toThrow(); expect(() => SetConfigRequestSchema.parse({})).toThrow(); expect(() => InterfaceSchema.parse({})).toThrow(); }); it('should provide meaningful error messages for validation failures', () => { try { VyOSAuthSchema.parse({ host: 'invalid-url' }); } catch (error) { expect(error).toBeInstanceOf(Error); expect(error.message).toContain('Invalid url'); } try { PingRequestSchema.parse({ count: 'not-a-number' }); } catch (error) { expect(error).toBeInstanceOf(Error); expect(error.message).toContain('host'); } }); it('should handle extra properties gracefully', () => { const authWithExtra = { host: 'https://vyos.test', apiKey: 'test-key', extraProperty: 'should-be-ignored', }; const result = VyOSAuthSchema.parse(authWithExtra); expect(result.host).toBe(authWithExtra.host); expect(result.apiKey).toBe(authWithExtra.apiKey); expect(result).not.toHaveProperty('extraProperty'); }); }); describe('Schema type inference', () => { it('should infer correct types from schemas', () => { const auth = VyOSAuthSchema.parse({ host: 'https://test.vyos', apiKey: 'key', }); // TypeScript type checking - these should not cause compilation errors const host: string = auth.host; const apiKey: string = auth.apiKey; const timeout: number = auth.timeout; const verifySSL: boolean = auth.verifySSL; expect(host).toBe('https://test.vyos'); expect(apiKey).toBe('key'); expect(timeout).toBe(30000); expect(verifySSL).toBe(false); }); it('should handle optional fields correctly', () => { const iface = InterfaceSchema.parse({ name: 'eth0' }); // These should be optional const address: string | undefined = iface.address; const description: string | undefined = iface.description; const mtu: number | undefined = iface.mtu; const vlan: number | undefined = iface.vlan; // This should have a default const enabled: boolean = iface.enabled; expect(address).toBeUndefined(); expect(description).toBeUndefined(); expect(mtu).toBeUndefined(); expect(vlan).toBeUndefined(); expect(enabled).toBe(true); }); }); });

Latest Blog Posts

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/danielbodnar/vyos-mcp'

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