Skip to main content
Glama
live-websocket-test.test.ts5.9 kB
/** * Live WebSocket test - captures what the browser actually receives * This tests the real WebSocket flow that browsers use */ import { SSHConnectionManager } from '../src/ssh-connection-manager.js'; import { WebServerManager } from '../src/web-server-manager.js'; import WebSocket from 'ws'; describe('Live WebSocket Integration Test', () => { let sshManager: SSHConnectionManager; let webManager: WebServerManager; let port: number; beforeEach(async () => { sshManager = new SSHConnectionManager(); webManager = new WebServerManager(sshManager); await webManager.start(); port = await webManager.getPort(); }); afterEach(async () => { if (webManager) { await webManager.stop(); } if (sshManager) { sshManager.cleanup(); } }); test('Real WebSocket flow with actual terminal display data', async () => { // Create SSH connection const config = { name: 'live-test-session', host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }; await sshManager.createConnection(config); // Connect WebSocket like a real browser const wsUrl = `ws://localhost:${port}/ws/session/live-test-session`; const ws = new WebSocket(wsUrl); const messages: Array<{ timestamp: number; data: any; raw: string; }> = []; // Capture all WebSocket messages ws.on('message', (data: Buffer) => { const raw = data.toString(); try { const parsed = JSON.parse(raw); messages.push({ timestamp: Date.now(), data: parsed, raw }); } catch (error) { console.error('Failed to parse WebSocket message:', raw); } }); // Wait for WebSocket connection await new Promise<void>((resolve) => { ws.on('open', resolve); }); // Let initial history load await new Promise(resolve => setTimeout(resolve, 500)); console.log('\n=== INITIAL HISTORY MESSAGES ==='); messages.forEach((msg, index) => { if (msg.data.type === 'terminal_output') { console.log(`Message ${index + 1}:`); console.log(` Type: ${msg.data.type}`); console.log(` Source: ${msg.data.source}`); console.log(` Data: ${JSON.stringify(msg.data.data)}`); console.log(` Raw display: ${msg.data.data.replace(/\r/g, '\\r').replace(/\n/g, '\\n')}`); } }); // Execute a command via MCP (not WebSocket) console.log('\n=== EXECUTING MCP COMMAND: whoami ==='); const mcpResult = await sshManager.executeCommand('live-test-session', 'whoami', { source: 'claude' }); console.log('MCP Result:', mcpResult); // Wait for WebSocket messages await new Promise(resolve => setTimeout(resolve, 1000)); // Execute a command via WebSocket (simulating browser) console.log('\n=== EXECUTING WEBSOCKET COMMAND: pwd ==='); const commandMessage = { type: 'terminal_input', sessionName: 'live-test-session', command: 'pwd', commandId: 'test-cmd-' + Date.now() }; ws.send(JSON.stringify(commandMessage)); // Wait for command completion await new Promise(resolve => setTimeout(resolve, 2000)); ws.close(); // Analyze terminal output messages const terminalOutputs = messages.filter(msg => msg.data.type === 'terminal_output'); console.log('\n=== ALL TERMINAL OUTPUT ANALYSIS ==='); terminalOutputs.forEach((msg, index) => { console.log(`\nTerminal Output ${index + 1}:`); console.log(` Source: ${msg.data.source}`); console.log(` Content: ${JSON.stringify(msg.data.data)}`); console.log(` Raw view: ${msg.data.data.replace(/\r/g, '\\r').replace(/\n/g, '\\n')}`); // Check for line ending issues const hasInconsistentLineEndings = msg.data.data.includes('\r\n') && msg.data.data.includes('\n') && msg.data.data.replace(/\r\n/g, '').includes('\n'); if (hasInconsistentLineEndings) { console.log(` ⚠️ INCONSISTENT LINE ENDINGS DETECTED!`); } // Check for double prompts const promptMatches = (msg.data.data.match(/\[jsbattig@localhost[^\]]*\]\$/g) || []); if (promptMatches.length > 1) { console.log(` ⚠️ DOUBLE PROMPT DETECTED: ${promptMatches.length} prompts`); } // Check for missing commands if (msg.data.data.includes('[jsbattig@localhost') && !msg.data.data.match(/\]\$\s*[a-zA-Z]/)) { console.log(` ⚠️ MISSING COMMAND AFTER PROMPT`); } }); // Concatenate all terminal data as browser would see it const browserTerminalView = terminalOutputs .map(msg => msg.data.data) .join(''); console.log('\n=== BROWSER TERMINAL VIEW (concatenated) ==='); console.log('Raw concatenated:', browserTerminalView.replace(/\r/g, '\\r').replace(/\n/g, '\\n')); console.log('Display view:'); console.log(browserTerminalView); // Check for specific problems const issues = []; if (browserTerminalView.includes('whoami') && !browserTerminalView.includes('jsbattig')) { issues.push('Command executed but result missing'); } if ((browserTerminalView.match(/\[jsbattig@localhost[^\]]*\]\$/g) || []).length > 3) { issues.push('Too many prompts (concatenation issue)'); } if (browserTerminalView.includes('\r\n') && browserTerminalView.includes('\n') && browserTerminalView.replace(/\r\n/g, '').includes('\n')) { issues.push('Inconsistent line endings (CRLF/LF mixed)'); } console.log('\n=== IDENTIFIED ISSUES ==='); if (issues.length === 0) { console.log('✅ No major issues detected'); } else { issues.forEach((issue, index) => { console.log(`❌ Issue ${index + 1}: ${issue}`); }); } expect(terminalOutputs.length).toBeGreaterThan(0); }, 30000); });

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