Skip to main content
Glama
e2e-session-fixes-validation.test.ts8.85 kB
import { SSHConnectionManager } from "../src/ssh-connection-manager"; import { SSHConnectionConfig } from "../src/types"; import * as os from 'os'; import * as path from 'path'; describe("E2E Session Fixes Validation", () => { let connectionManager: SSHConnectionManager; const testConfig: SSHConnectionConfig = { name: "e2e-session-fixes", host: "localhost", username: "jsbattig", keyFilePath: path.join(os.homedir(), '.ssh/id_ed25519'), }; beforeEach(() => { connectionManager = new SSHConnectionManager(); }); afterEach(() => { connectionManager.cleanup(); }); describe("Critical Fix: Shell-Terminating Command Prevention", () => { it("should prevent exit command from terminating shell session", async () => { await connectionManager.createConnection(testConfig); // Attempt to execute exit command should be rejected await expect( connectionManager.executeCommand(testConfig.name, "exit"), ).rejects.toThrow( "would terminate the shell session, breaking session state persistence", ); // Session should remain active and usable after prevention const result = await connectionManager.executeCommand( testConfig.name, 'echo "session still active"', ); expect(result.stdout.trim()).toBe("session still active"); expect(result.exitCode).toBe(0); }, 20000); it("should prevent exit with code from terminating shell session", async () => { await connectionManager.createConnection(testConfig); await expect( connectionManager.executeCommand(testConfig.name, "exit 42"), ).rejects.toThrow("would terminate the shell session"); // Verify session integrity maintained const verifyResult = await connectionManager.executeCommand( testConfig.name, "whoami", ); expect(verifyResult.stdout.trim()).toBe("test_user"); }, 20000); it("should prevent logout command from terminating shell session", async () => { await connectionManager.createConnection(testConfig); await expect( connectionManager.executeCommand(testConfig.name, "logout"), ).rejects.toThrow("would terminate the shell session"); }, 20000); }); describe("Critical Fix: STDERR Handling Documentation", () => { it("should document that stderr is combined with stdout in shell sessions", async () => { await connectionManager.createConnection(testConfig); // Error output should appear in stdout, not stderr const result = await connectionManager.executeCommand( testConfig.name, 'echo "This is an error" >&2', ); expect(result.stdout.trim()).toBe("This is an error"); expect(result.stderr).toBe(""); // Always empty for shell sessions expect(result.exitCode).toBe(0); }, 20000); it("should handle mixed stdout and stderr output correctly", async () => { await connectionManager.createConnection(testConfig); const result = await connectionManager.executeCommand( testConfig.name, 'echo "stdout message"; echo "stderr message" >&2; echo "more stdout"', ); // All output should be in stdout expect(result.stdout).toContain("stdout message"); expect(result.stdout).toContain("stderr message"); expect(result.stdout).toContain("more stdout"); expect(result.stderr).toBe(""); expect(result.exitCode).toBe(0); }, 20000); }); describe("Performance Fix: No Artificial Delays", () => { it("should execute commands without artificial delays", async () => { await connectionManager.createConnection(testConfig); const startTime = Date.now(); const result = await connectionManager.executeCommand( testConfig.name, 'echo "fast execution"', ); const endTime = Date.now(); expect(result.stdout.trim()).toBe("fast execution"); expect(result.exitCode).toBe(0); // Should complete quickly - well under 1 second for simple command // We allow some network latency but no artificial delays const executionTime = endTime - startTime; expect(executionTime).toBeLessThan(7000); // 7 second max including network and test environment overhead }, 20000); it("should execute multiple commands efficiently without accumulated delays", async () => { await connectionManager.createConnection(testConfig); const startTime = Date.now(); // Execute 5 simple commands in sequence for (let i = 1; i <= 5; i++) { const result = await connectionManager.executeCommand( testConfig.name, `echo "Command ${i}"`, ); expect(result.stdout.trim()).toBe(`Command ${i}`); } const totalTime = Date.now() - startTime; // Should not have accumulated artificial delays // Each command should be fast, total should be reasonable expect(totalTime).toBeLessThan(15000); // 15 seconds max for 5 commands }, 30000); }); describe("Session State Persistence Validation", () => { it("should maintain session state without unauthorized fallbacks", async () => { await connectionManager.createConnection(testConfig); // Set up persistent state await connectionManager.executeCommand( testConfig.name, 'export TEST_SESSION_VAR="persistent_state"', ); // Change directory await connectionManager.executeCommand( testConfig.name, "mkdir -p ~/test-session-validation && cd ~/test-session-validation", ); // Verify state persists across multiple commands const envResult = await connectionManager.executeCommand( testConfig.name, "echo $TEST_SESSION_VAR", ); expect(envResult.stdout.trim()).toBe("persistent_state"); const pwdResult = await connectionManager.executeCommand( testConfig.name, "pwd", ); expect(pwdResult.stdout.trim()).toContain("test-session-validation"); // Cleanup await connectionManager.executeCommand( testConfig.name, "cd ~ && rm -rf ~/test-session-validation", ); }, 30000); it("should handle shell unavailability with proper error messages", async () => { const manager = new SSHConnectionManager(); // Create connection but don't let shell initialize properly by cleanup immediately await manager.createConnection(testConfig); manager.cleanup(); // This should make shell unavailable // Attempting to use should give clear error await expect( manager.executeCommand(testConfig.name, 'echo "test"'), ).rejects.toThrow("Connection"); manager.cleanup(); }, 20000); }); describe("Exit Code Handling Robustness", () => { it("should correctly capture exit codes for successful commands", async () => { await connectionManager.createConnection(testConfig); const result = await connectionManager.executeCommand( testConfig.name, "true", ); expect(result.exitCode).toBe(0); }, 20000); it("should correctly capture exit codes for failed commands", async () => { await connectionManager.createConnection(testConfig); const result = await connectionManager.executeCommand( testConfig.name, "false", ); expect(result.exitCode).toBe(1); }, 20000); it("should correctly capture custom exit codes", async () => { await connectionManager.createConnection(testConfig); // Use a non-terminating way to return custom exit code const result = await connectionManager.executeCommand( testConfig.name, 'bash -c "exit 42"', ); expect(result.exitCode).toBe(42); }, 20000); }); describe("Edge Case Handling", () => { it("should handle commands with complex output parsing", async () => { await connectionManager.createConnection(testConfig); // Command that produces output similar to our parsing patterns const result = await connectionManager.executeCommand( testConfig.name, 'echo "This contains EXIT_CODE: pattern but is not real exit code"', ); expect(result.stdout.trim()).toBe( "This contains EXIT_CODE: pattern but is not real exit code", ); expect(result.exitCode).toBe(0); }, 20000); it("should handle commands with special characters and escaping", async () => { await connectionManager.createConnection(testConfig); const result = await connectionManager.executeCommand( testConfig.name, 'echo "Special chars: $PATH \\n \\t \'quotes\' \\"double\\""', ); expect(result.stdout).toContain("Special chars"); expect(result.exitCode).toBe(0); }, 20000); }); });

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