Skip to main content
Glama
chatDeployment.test.ts19 kB
/** * @file Deployment Integration Tests * @description Tests for chat-based infrastructure deployment workflow */ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import ChatDeploymentHandler, { buildDeploymentChatResponse, } from '../chatDeploymentHandler.js'; import { validateCommands, formatCommandsForDisplay, CommandGenerationResponse, } from '../commandGeneration.js'; import { CommandExecutionManager, formatExecutionResults, } from '../commandExecution.js'; describe('Chat Deployment Integration', () => { let handler: ChatDeploymentHandler; let executionManager: CommandExecutionManager; beforeEach(() => { handler = new ChatDeploymentHandler(); executionManager = new CommandExecutionManager(); }); describe('Deployment Detection', () => { it('should detect DHCP deployment requests', async () => { const message = 'Deploy DHCP on 172.251.96.200 with pool 100-200'; const detection = await handler.detectDeploymentRequest(message); expect(detection.isDeploymentRequest).toBe(true); expect(detection.taskType).toBe('dhcp'); expect(detection.confidence).toBeGreaterThan(0.7); }); it('should detect DNS configuration requests', async () => { const message = 'Setup DNS records for example.com pointing to 10.0.0.1'; const detection = await handler.detectDeploymentRequest(message); expect(detection.isDeploymentRequest).toBe(true); expect(detection.taskType).toBe('dns'); expect(detection.confidence).toBeGreaterThan(0.7); }); it('should detect firewall rule requests', async () => { const message = 'Configure firewall rules to block port 22 from external'; const detection = await handler.detectDeploymentRequest(message); expect(detection.isDeploymentRequest).toBe(true); expect(detection.taskType).toBe('firewall'); expect(detection.confidence).toBeGreaterThan(0.6); }); it('should detect VLAN configuration requests', async () => { const message = 'Create VLAN 100 on switch for management network'; const detection = await handler.detectDeploymentRequest(message); expect(detection.isDeploymentRequest).toBe(true); expect(detection.taskType).toBe('vlan'); expect(detection.confidence).toBeGreaterThan(0.6); }); it('should detect routing configuration requests', async () => { const message = 'Setup BGP routing with AS 65000'; const detection = await handler.detectDeploymentRequest(message); expect(detection.isDeploymentRequest).toBe(true); expect(detection.taskType).toBe('routing'); expect(detection.confidence).toBeGreaterThan(0.6); }); it('should reject non-deployment requests', async () => { const message = 'Tell me about the weather today'; const detection = await handler.detectDeploymentRequest(message); expect(detection.isDeploymentRequest).toBe(false); expect(detection.confidence).toBeLessThan(0.5); }); it('should extract device IP from message', async () => { const message = 'Configure 172.251.96.200 for DHCP'; const context = handler.extractDeviceContext(message); expect(context.targetDevice).toBe('172.251.96.200'); }); it('should detect MikroTik device type', () => { const message = 'Deploy on MikroTik RouterOS'; const context = handler.extractDeviceContext(message); expect(context.deviceType).toBe('mikrotik'); }); it('should detect Linux device type', () => { const message = 'Configure Ubuntu 20.04 DHCP server'; const context = handler.extractDeviceContext(message); expect(context.deviceType).toBe('linux'); }); }); describe('Command Generation', () => { it('should generate DHCP deployment commands', async () => { const message = 'Deploy DHCP with pool 172.251.96.100-200'; const deployment = { isDeploymentRequest: true, taskType: 'dhcp' as const, targetDevice: '172.251.96.200', confidence: 0.95, }; const { generationId, response } = await handler.generateDeploymentCommands( message, deployment ); expect(generationId).toMatch(/^gen_/); expect(response.taskDescription).toContain('DHCP'); expect(response.commands.length).toBeGreaterThan(0); expect(response.commands[0].command).toBeTruthy(); expect(response.commands[0].riskLevel).toMatch(/^(low|medium|high|critical)$/); }); it('should include risk assessment in commands', async () => { const message = 'Deploy DHCP'; const deployment = { isDeploymentRequest: true, taskType: 'dhcp' as const, confidence: 0.9, }; const { response } = await handler.generateDeploymentCommands(message, deployment); response.commands.forEach(cmd => { expect(['low', 'medium', 'high', 'critical']).toContain(cmd.riskLevel); }); }); it('should include warnings for high-risk operations', async () => { const message = 'Deploy DHCP'; const deployment = { isDeploymentRequest: true, taskType: 'dhcp' as const, confidence: 0.9, }; const { response } = await handler.generateDeploymentCommands(message, deployment); expect(response.warnings).toBeDefined(); expect(response.warnings?.length).toBeGreaterThan(0); }); it('should include affected services in response', async () => { const message = 'Deploy DHCP'; const deployment = { isDeploymentRequest: true, taskType: 'dhcp' as const, confidence: 0.9, }; const { response } = await handler.generateDeploymentCommands(message, deployment); expect(response.affectedServices).toBeDefined(); expect(response.affectedServices?.length).toBeGreaterThan(0); }); it('should provide step-by-step explanation', async () => { const message = 'Deploy DHCP'; const deployment = { isDeploymentRequest: true, taskType: 'dhcp' as const, confidence: 0.9, }; const { response } = await handler.generateDeploymentCommands(message, deployment); expect(response.explanation).toBeTruthy(); expect(response.explanation.length).toBeGreaterThan(50); }); it('should estimate deployment duration', async () => { const message = 'Deploy DHCP'; const deployment = { isDeploymentRequest: true, taskType: 'dhcp' as const, confidence: 0.9, }; const { response } = await handler.generateDeploymentCommands(message, deployment); expect(response.estimatedDuration).toBeDefined(); expect(response.estimatedDuration).toBeGreaterThan(0); }); }); describe('Command Validation', () => { it('should validate command structure', () => { const commands = [ { id: 'cmd_001', command: '/ip/pool/add name=default', description: 'Create pool', category: 'config' as const, riskLevel: 'medium' as const, }, ]; const validation = validateCommands(commands); expect(validation.valid).toBe(true); expect(validation.errors.length).toBe(0); }); it('should reject empty command list', () => { const validation = validateCommands([]); expect(validation.valid).toBe(false); expect(validation.errors.length).toBeGreaterThan(0); }); it('should flag missing rollback for critical operations', () => { const commands = [ { id: 'cmd_001', command: 'rm -rf /etc', description: 'Critical operation', category: 'system' as const, riskLevel: 'critical' as const, }, ]; const validation = validateCommands(commands); expect(validation.valid).toBe(false); expect(validation.errors.some(e => e.includes('rollback'))).toBe(true); }); it('should detect suspicious command patterns', () => { const commands = [ { id: 'cmd_001', command: 'rm -rf /', description: 'Dangerous', category: 'system' as const, riskLevel: 'critical' as const, rollbackCommand: 'echo error', }, ]; const validation = validateCommands(commands); expect(validation.valid).toBe(false); expect(validation.errors.some(e => e.includes('Suspicious'))).toBe(true); }); }); describe('Execution Workflow', () => { it('should create execution session', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', 'cmd_002', ]); expect(session.sessionId).toMatch(/^exec_/); expect(session.status).toBe('pending'); expect(session.commandIds.length).toBe(2); }); it('should retrieve session', () => { const created = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', ]); const retrieved = executionManager.getSession(created.sessionId); expect(retrieved).toBeDefined(); expect(retrieved?.sessionId).toBe(created.sessionId); }); it('should approve execution', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', 'cmd_002', ]); const approved = executionManager.approveExecution(session.sessionId); expect(approved).toBe(true); const updated = executionManager.getSession(session.sessionId); expect(updated?.status).toBe('confirmed'); }); it('should filter commands on approval', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', 'cmd_002', 'cmd_003', ]); executionManager.approveExecution(session.sessionId, ['cmd_001', 'cmd_003']); const updated = executionManager.getSession(session.sessionId); expect(updated?.commandIds).toEqual(['cmd_001', 'cmd_003']); }); it('should cancel execution', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', ]); const cancelled = executionManager.cancelExecution(session.sessionId, 'User rejected'); expect(cancelled).toBe(true); const updated = executionManager.getSession(session.sessionId); expect(updated?.status).toBe('cancelled'); }); it('should record command results', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', ]); const recorded = executionManager.recordResult(session.sessionId, 'cmd_001', { success: true, stdout: 'Pool created', exitCode: 0, duration: 125, }); expect(recorded).toBe(true); const updated = executionManager.getSession(session.sessionId); expect(updated?.results?.length).toBe(1); expect(updated?.results?.[0]).toMatchObject({ commandId: 'cmd_001', success: true, }); }); it('should complete session with success', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', ]); executionManager.completeSession(session.sessionId, true); const updated = executionManager.getSession(session.sessionId); expect(updated?.status).toBe('completed'); expect(updated?.endTime).toBeDefined(); }); it('should complete session with error', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', ]); executionManager.completeSession(session.sessionId, false, 'Connection timeout'); const updated = executionManager.getSession(session.sessionId); expect(updated?.status).toBe('failed'); expect(updated?.errorMessage).toBe('Connection timeout'); }); }); describe('User Confirmation Handling', () => { it('should handle approval', async () => { const message = 'Deploy DHCP'; await handler.detectDeploymentRequest(message); await handler.generateDeploymentCommands(message, { isDeploymentRequest: true, taskType: 'dhcp', confidence: 0.9, }); const displayInfo = handler.getGenerationDisplay('test_gen_id'); if (!displayInfo?.sessionId) { throw new Error('No session ID'); } const result = await handler.handleUserConfirmation(displayInfo.sessionId, true); expect(result.approved).toBe(true); expect(result.session.status).toBe('confirmed'); }); it('should handle rejection', async () => { const message = 'Deploy DHCP'; await handler.generateDeploymentCommands(message, { isDeploymentRequest: true, taskType: 'dhcp', confidence: 0.9, }); const displayInfo = handler.getGenerationDisplay('test_gen_id'); if (!displayInfo?.sessionId) { throw new Error('No session ID'); } const result = await handler.handleUserConfirmation(displayInfo.sessionId, false); expect(result.approved).toBe(false); expect(result.session.status).toBe('cancelled'); }); }); describe('Display Formatting', () => { it('should format commands for display', () => { const response: CommandGenerationResponse = { taskDescription: 'Deploy DHCP', commands: [ { id: 'cmd_001', command: '/ip/pool/add name=default', description: 'Create pool', category: 'config', riskLevel: 'medium', }, ], explanation: 'This will create a DHCP pool', estimatedDuration: 5, warnings: ['Existing config will be replaced'], }; const display = formatCommandsForDisplay(response); expect(display).toContain('Deploy DHCP'); expect(display).toContain('cmd_001'); expect(display).toContain('Create pool'); expect(display).toContain('Estimated Duration'); }); it('should include risk levels in display', () => { const response: CommandGenerationResponse = { taskDescription: 'Deploy', commands: [ { id: 'cmd_001', command: 'test', description: 'Test', category: 'config', riskLevel: 'high', }, ], explanation: 'Test', }; const display = formatCommandsForDisplay(response); expect(display).toContain('HIGH'); }); it('should format execution results', () => { const session = executionManager.createSession('user123', '172.251.96.200', [ 'cmd_001', ]); executionManager.recordResult(session.sessionId, 'cmd_001', { success: true, stdout: 'Success', exitCode: 0, duration: 100, }); executionManager.completeSession(session.sessionId, true); const updated = executionManager.getSession(session.sessionId); if (!updated) throw new Error('Session not found'); const display = formatExecutionResults(updated); expect(display).toContain('EXECUTION RESULTS'); expect(display).toContain('COMPLETED'); expect(display).toContain('✅'); }); }); describe('Chat Response Building', () => { it('should build detection response', () => { const response = buildDeploymentChatResponse('detection', {}); expect(response).toContain('Deployment Request Detected'); }); it('should build generation response', () => { const response = buildDeploymentChatResponse('generation', { display: 'Test' }); expect(response).toContain('Commands Generated'); }); it('should build confirmation response', () => { const response = buildDeploymentChatResponse('confirmation', { sessionId: 'test' }); expect(response).toContain('Awaiting Your Confirmation'); expect(response).toContain('test'); }); it('should build completion response', () => { const response = buildDeploymentChatResponse('completed', { results: 'All ok' }); expect(response).toContain('Deployment Completed'); }); it('should build error response', () => { const response = buildDeploymentChatResponse('error', { error: 'Test error' }); expect(response).toContain('Deployment Failed'); expect(response).toContain('Test error'); }); }); afterEach(() => { vi.clearAllMocks(); }); });

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/babasida246/ai-mcp-gateway'

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