Skip to main content
Glama
story2-websocket-response-format.test.ts10.7 kB
import { SSHConnectionManager } from "../src/ssh-connection-manager"; import { WebServerManager } from "../src/web-server-manager"; import { SSHConnectionConfig } from "../src/types"; import WebSocket from 'ws'; describe("Story 2: WebSocket Response Format - AC2.3", () => { let sshManager: SSHConnectionManager; let webServer: WebServerManager; let webPort: number; let testSessionName: string; beforeEach(async () => { sshManager = new SSHConnectionManager(); webServer = new WebServerManager(sshManager); testSessionName = `test-session-${Date.now()}`; await webServer.start(); webPort = await webServer.getPort(); }); afterEach(async () => { await webServer.stop(); sshManager.cleanup(); }); describe("AC2.3: WebSocket Response Format", () => { it("should include source identification in terminal_output responses", async () => { const sshConfig: SSHConnectionConfig = { name: testSessionName, host: "localhost", username: "test_user", password: "password123" }; await sshManager.createConnection(sshConfig); const ws = new WebSocket(`ws://localhost:${webPort}/ws/session/${testSessionName}`); return new Promise<void>((resolve, reject) => { ws.on('open', () => { const testMessage = { type: 'terminal_input', sessionName: testSessionName, command: 'echo "user command"', commandId: 'cmd_response_format_1' }; ws.send(JSON.stringify(testMessage)); }); ws.on('message', (data) => { const message = JSON.parse(data.toString()); if (message.type === 'terminal_output' && message.commandId === 'cmd_response_format_1') { // Verify response format includes source identification expect(message.source).toBeDefined(); expect(message.source).toBe('user'); expect(message.data).toBeDefined(); expect(message.commandId).toBe('cmd_response_format_1'); ws.close(); resolve(); } }); ws.on('error', (error) => { ws.close(); reject(error); }); setTimeout(() => { ws.close(); reject(new Error('Test timeout: response format not matching expected structure')); }, 8000); }); }, 15000); it("should include user-initiated flag when source is user", async () => { const sshConfig: SSHConnectionConfig = { name: testSessionName, host: "localhost", username: "test_user", password: "password123" }; await sshManager.createConnection(sshConfig); const ws = new WebSocket(`ws://localhost:${webPort}/ws/session/${testSessionName}`); return new Promise<void>((resolve, reject) => { ws.on('open', () => { const testMessage = { type: 'terminal_input', sessionName: testSessionName, command: 'pwd', commandId: 'cmd_user_flag_1' }; ws.send(JSON.stringify(testMessage)); }); ws.on('message', (data) => { const message = JSON.parse(data.toString()); if (message.type === 'terminal_output' && message.commandId === 'cmd_user_flag_1') { expect(message["user-initiated"]).toBe(true); expect(message.source).toBe('user'); ws.close(); resolve(); } }); ws.on('error', (error) => { ws.close(); reject(error); }); setTimeout(() => { ws.close(); reject(new Error('Test timeout: user-initiated flag not present')); }, 8000); }); }, 15000); it("should exclude command echo from terminal output response", async () => { const sshConfig: SSHConnectionConfig = { name: testSessionName, host: "localhost", username: "test_user", password: "password123" }; await sshManager.createConnection(sshConfig); const ws = new WebSocket(`ws://localhost:${webPort}/ws/session/${testSessionName}`); return new Promise<void>((resolve, reject) => { const testCommand = 'echo "unique_output_12345"'; ws.on('open', () => { const testMessage = { type: 'terminal_input', sessionName: testSessionName, command: testCommand, commandId: 'cmd_no_echo_1' }; ws.send(JSON.stringify(testMessage)); }); ws.on('message', (data) => { const message = JSON.parse(data.toString()); if (message.type === 'terminal_output' && message.commandId === 'cmd_no_echo_1') { // Response should contain command output but not command echo expect(message.data).toContain('unique_output_12345'); expect(message.data).not.toContain(testCommand); // Command should not be echoed ws.close(); resolve(); } }); ws.on('error', (error) => { ws.close(); reject(error); }); setTimeout(() => { ws.close(); reject(new Error('Test timeout: command echo filtering not working')); }, 8000); }); }, 15000); it("should include output content in data field", async () => { const sshConfig: SSHConnectionConfig = { name: testSessionName, host: "localhost", username: "test_user", password: "password123" }; await sshManager.createConnection(sshConfig); const ws = new WebSocket(`ws://localhost:${webPort}/ws/session/${testSessionName}`); return new Promise<void>((resolve, reject) => { ws.on('open', () => { const testMessage = { type: 'terminal_input', sessionName: testSessionName, command: 'echo "test output content"', commandId: 'cmd_output_content_1' }; ws.send(JSON.stringify(testMessage)); }); ws.on('message', (data) => { const message = JSON.parse(data.toString()); if (message.type === 'terminal_output' && message.commandId === 'cmd_output_content_1') { expect(message.data).toBeDefined(); expect(typeof message.data).toBe('string'); expect(message.data.length).toBeGreaterThan(0); expect(message.data).toContain('test output content'); ws.close(); resolve(); } }); ws.on('error', (error) => { ws.close(); reject(error); }); setTimeout(() => { ws.close(); reject(new Error('Test timeout: output content not present in data field')); }, 8000); }); }, 15000); it("should preserve commandId in response for request correlation", async () => { const sshConfig: SSHConnectionConfig = { name: testSessionName, host: "localhost", username: "test_user", password: "password123" }; await sshManager.createConnection(sshConfig); const ws = new WebSocket(`ws://localhost:${webPort}/ws/session/${testSessionName}`); return new Promise<void>((resolve, reject) => { const uniqueCommandId = `cmd_correlation_${Date.now()}`; ws.on('open', () => { const testMessage = { type: 'terminal_input', sessionName: testSessionName, command: 'echo "correlation test"', commandId: uniqueCommandId }; ws.send(JSON.stringify(testMessage)); }); ws.on('message', (data) => { const message = JSON.parse(data.toString()); if (message.type === 'terminal_output' && message.commandId === uniqueCommandId) { expect(message.commandId).toBe(uniqueCommandId); ws.close(); resolve(); } }); ws.on('error', (error) => { ws.close(); reject(error); }); setTimeout(() => { ws.close(); reject(new Error('Test timeout: commandId correlation not working')); }, 8000); }); }, 15000); it("should format response as valid JSON with all required fields", async () => { const sshConfig: SSHConnectionConfig = { name: testSessionName, host: "localhost", username: "test_user", password: "password123" }; await sshManager.createConnection(sshConfig); const ws = new WebSocket(`ws://localhost:${webPort}/ws/session/${testSessionName}`); return new Promise<void>((resolve, reject) => { ws.on('open', () => { const testMessage = { type: 'terminal_input', sessionName: testSessionName, command: 'echo "json validation test"', commandId: 'cmd_json_validation_1' }; ws.send(JSON.stringify(testMessage)); }); ws.on('message', (data) => { let message: any; try { message = JSON.parse(data.toString()); } catch (error) { ws.close(); reject(new Error('Response is not valid JSON')); return; } if (message.type === 'terminal_output' && message.commandId === 'cmd_json_validation_1') { // Validate all required fields are present expect(message.type).toBe('terminal_output'); expect(message.source).toBeDefined(); expect(message.data).toBeDefined(); expect(message.commandId).toBe('cmd_json_validation_1'); expect(message["user-initiated"]).toBeDefined(); // Validate field types expect(typeof message.type).toBe('string'); expect(typeof message.source).toBe('string'); expect(typeof message.data).toBe('string'); expect(typeof message.commandId).toBe('string'); expect(typeof message["user-initiated"]).toBe('boolean'); ws.close(); resolve(); } }); ws.on('error', (error) => { ws.close(); reject(error); }); setTimeout(() => { ws.close(); reject(new Error('Test timeout: JSON validation failed')); }, 8000); }); }, 15000); }); });

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