Skip to main content
Glama
story2-acceptance-criteria.test.ts8.48 kB
/** * Story 2: Pre-WebSocket Command Execution - Acceptance Criteria Validation * * This test validates the complete Story 2 acceptance criteria: * * Given I have specified pre-WebSocket commands ["ssh_connect", "ssh_exec ls"] * When the testing framework executes the pre-phase * Then it should send each command via MCP stdin/stdout * And wait for MCP responses before proceeding to next command * And not capture any WebSocket data during this phase * And build up terminal history in the MCP server * And complete all pre-commands before WebSocket connection */ import { MCPServerManager } from './mcp-server-manager'; import { MCPClient } from './mcp-client'; import { PreWebSocketCommandExecutor } from './pre-websocket-command-executor'; describe('Story 2: Pre-WebSocket Command Execution - Acceptance Criteria', () => { let serverManager: MCPServerManager; let mcpClient: MCPClient; let executor: PreWebSocketCommandExecutor; beforeEach(async () => { // Start fresh MCP server for each test serverManager = new MCPServerManager(); await serverManager.start(); const processInfo = serverManager.getProcess(); if (!processInfo || !processInfo.stdin || !processInfo.stdout) { throw new Error('Failed to start MCP server for testing'); } mcpClient = new MCPClient({ stdin: processInfo.stdin, stdout: processInfo.stdout } as any); executor = new PreWebSocketCommandExecutor(mcpClient); }); afterEach(async () => { if (executor) { await executor.cleanup(); } if (mcpClient) { await mcpClient.disconnect(); } if (serverManager) { await serverManager.stop(); } }); test('Story 2 Full Acceptance Criteria: Pre-WebSocket Commands with Terminal History Building', async () => { // Given I have specified pre-WebSocket commands ["ssh_connect", "ssh_exec ls"] const preWebSocketCommands = [ { tool: 'ssh_connect', args: { name: 'story2-session', host: 'localhost', username: 'test_user', password: 'password123' } }, { tool: 'ssh_exec', args: { sessionName: 'story2-session', command: 'ls' } } ]; // When the testing framework executes the pre-phase const startTime = Date.now(); const results = await executor.executeCommands(preWebSocketCommands); const totalExecutionTime = Date.now() - startTime; // Then it should send each command via MCP stdin/stdout expect(results).toHaveLength(2); // Verify first command (ssh_connect) succeeded expect(results[0].tool).toBe('ssh_connect'); expect(results[0].success).toBe(true); expect(results[0].executionTime).toBeGreaterThan(0); // Verify second command (ssh_exec) succeeded expect(results[1].tool).toBe('ssh_exec'); expect(results[1].success).toBe(true); expect(results[1].executionTime).toBeGreaterThan(0); // And wait for MCP responses before proceeding to next command // This is verified by sequential execution time being greater than sum of individual times const expectedMinimumTime = results[0].executionTime + results[1].executionTime; expect(totalExecutionTime).toBeGreaterThanOrEqual(expectedMinimumTime * 0.8); // Allow for some timing variance // And not capture any WebSocket data during this phase // This is implicitly verified since we're only using MCP stdin/stdout communication // No WebSocket connections were established during this test expect(results.every(r => r.tool !== 'websocket_connect')).toBe(true); // And build up terminal history in the MCP server // Verify by checking that we can get monitoring URL for the session const urlResponse = await mcpClient.callTool('ssh_get_monitoring_url', { sessionName: 'story2-session' }); expect(urlResponse.success).toBe(true); // And complete all pre-commands before WebSocket connection // All commands completed successfully, session is ready for WebSocket phase expect(results.every(r => r.success)).toBe(true); // Verify session still exists and is ready for WebSocket connection const sessionStillActive = await mcpClient.callTool('ssh_exec', { sessionName: 'story2-session', command: 'echo "Session ready for WebSocket"' }); expect(sessionStillActive.success).toBe(true); }); test('Story 2 Sequential Processing Verification', async () => { // Test that commands are processed sequentially, not in parallel const commands = [ { tool: 'ssh_connect', args: { name: 'sequential-test', host: 'localhost', username: 'test_user', password: 'password123' } }, { tool: 'ssh_exec', args: { sessionName: 'sequential-test', command: 'echo "First command"' } }, { tool: 'ssh_exec', args: { sessionName: 'sequential-test', command: 'echo "Second command"' } }, { tool: 'ssh_exec', args: { sessionName: 'sequential-test', command: 'echo "Third command"' } } ]; const startTime = Date.now(); const results = await executor.executeCommands(commands); const totalTime = Date.now() - startTime; // All commands should succeed expect(results).toHaveLength(4); expect(results.every(r => r.success)).toBe(true); // Sequential execution should take longer than any individual command const longestIndividualCommand = Math.max(...results.map(r => r.executionTime)); expect(totalTime).toBeGreaterThan(longestIndividualCommand); // Each command should complete before the next starts // This is verified by the fact that the ssh_exec commands succeeded, // which means the ssh_connect completed first expect(results[0].tool).toBe('ssh_connect'); expect(results[1].tool).toBe('ssh_exec'); expect(results[2].tool).toBe('ssh_exec'); expect(results[3].tool).toBe('ssh_exec'); }); test('Story 2 Error Handling During Pre-Phase', async () => { // Test that pre-phase handles errors gracefully and continues const commandsWithError = [ { tool: 'ssh_connect', args: { name: 'error-handling-test', host: 'localhost', username: 'test_user', password: 'password123' } }, { tool: 'invalid_command_that_will_fail', args: {} }, { tool: 'ssh_exec', args: { sessionName: 'error-handling-test', command: 'echo "After error"' } } ]; const results = await executor.executeCommands(commandsWithError); expect(results).toHaveLength(3); expect(results[0].success).toBe(true); // ssh_connect should succeed expect(results[1].success).toBe(false); // invalid command should fail expect(results[2].success).toBe(true); // ssh_exec should succeed despite previous error // Terminal history should still be built despite the error const urlResponse = await mcpClient.callTool('ssh_get_monitoring_url', { sessionName: 'error-handling-test' }); expect(urlResponse.success).toBe(true); }); test('Story 2 MCP Communication Protocol Compliance', async () => { // Verify that the MCP communication follows proper protocol const testCommand = { tool: 'ssh_connect', args: { name: 'protocol-test', host: 'localhost', username: 'test_user', password: 'password123' } }; // Execute single command and verify MCP protocol compliance const results = await executor.executeCommands([testCommand]); expect(results).toHaveLength(1); expect(results[0]).toHaveProperty('tool', 'ssh_connect'); expect(results[0]).toHaveProperty('success'); expect(results[0]).toHaveProperty('executionTime'); expect(typeof results[0].success).toBe('boolean'); expect(typeof results[0].executionTime).toBe('number'); // If successful, should have result if (results[0].success) { expect(results[0]).toHaveProperty('result'); } else { // If failed, should have error message expect(results[0]).toHaveProperty('error'); expect(typeof results[0].error).toBe('string'); } }); });

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/LightspeedDMS/ssh-mcp'

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