Skip to main content
Glama
story3-command-queue-management.test.ts12 kB
import { SSHConnectionManager } from "../src/ssh-connection-manager"; import { SSHConnectionConfig, CommandSource, } from "../src/types"; describe("Story 3: MCP Server Command Queue Management", () => { let connectionManager: SSHConnectionManager; // Mock SSH connection config for testing const mockConfig: SSHConnectionConfig = { name: "queue-test-session", host: "localhost", username: "testuser", password: "testpass" }; beforeEach(() => { connectionManager = new SSHConnectionManager(); }); afterEach(() => { connectionManager.cleanup(); }); describe("AC3.1: Command Queuing System - FIFO queue for commands when session busy", () => { it("should queue second command when first command is executing", async () => { // This test will fail initially - we need to implement queuing // Create connection and setup concurrent commands await connectionManager.createConnection(mockConfig); const firstCommandPromise = connectionManager.executeCommand( mockConfig.name, "sleep 2 && echo 'first command'", { source: "user" as CommandSource } ); // Start second command immediately after first - should be queued const secondCommandPromise = connectionManager.executeCommand( mockConfig.name, "echo 'second command'", { source: "claude" as CommandSource } ); // Both commands should resolve, second should wait for first const [firstResult, secondResult] = await Promise.all([ firstCommandPromise, secondCommandPromise ]); expect(firstResult.stdout).toContain("first command"); expect(secondResult.stdout).toContain("second command"); // Second command should have executed after first completed // (we'll verify timing in integration tests) }, 10000); it("should maintain FIFO order for multiple queued commands", async () => { await connectionManager.createConnection(mockConfig); // Start multiple commands that should queue in FIFO order const command1 = connectionManager.executeCommand(mockConfig.name, "echo 'command1'", { source: "user" }); const command2 = connectionManager.executeCommand(mockConfig.name, "echo 'command2'", { source: "claude" }); const command3 = connectionManager.executeCommand(mockConfig.name, "echo 'command3'", { source: "user" }); const results = await Promise.all([command1, command2, command3]); // All commands should complete successfully in FIFO order expect(results[0].stdout).toContain("command1"); expect(results[1].stdout).toContain("command2"); expect(results[2].stdout).toContain("command3"); }, 15000); it("should preserve source information in queue", async () => { await connectionManager.createConnection(mockConfig); const userCommand = connectionManager.executeCommand( mockConfig.name, "echo 'user command'", { source: "user" as CommandSource } ); const claudeCommand = connectionManager.executeCommand( mockConfig.name, "echo 'claude command'", { source: "claude" as CommandSource } ); const [userResult, claudeResult] = await Promise.all([userCommand, claudeCommand]); // Results should maintain source context even when queued expect(userResult.stdout).toContain("user command"); expect(claudeResult.stdout).toContain("claude command"); }, 10000); }); describe("AC3.2: Concurrent Execution Prevention - second command waits if one executing", () => { it("should not execute commands simultaneously on same session", async () => { await connectionManager.createConnection(mockConfig); let firstCommandStarted = false; let secondCommandStarted = false; let firstCommandFinished = false; // Track command execution timing const firstCommand = connectionManager.executeCommand( mockConfig.name, "sleep 1 && echo 'first done'", { source: "user" } ).then(result => { firstCommandStarted = true; firstCommandFinished = true; return result; }); // Small delay to ensure first command starts await new Promise(resolve => setTimeout(resolve, 100)); const secondCommand = connectionManager.executeCommand( mockConfig.name, "echo 'second done'", { source: "claude" } ).then(result => { // Second command should only start after first finishes secondCommandStarted = true; expect(firstCommandFinished).toBe(true); return result; }); await Promise.all([firstCommand, secondCommand]); expect(firstCommandStarted).toBe(true); expect(secondCommandStarted).toBe(true); expect(firstCommandFinished).toBe(true); }, 10000); }); describe("AC3.3: Queue State Management - next command starts immediately when current completes", () => { it("should start next queued command immediately when current completes", async () => { await connectionManager.createConnection(mockConfig); const startTime = Date.now(); // First command with known duration const firstCommand = connectionManager.executeCommand( mockConfig.name, "sleep 1 && echo 'first'", { source: "user" } ); // Second command should start immediately after first const secondCommand = connectionManager.executeCommand( mockConfig.name, "echo 'second'", { source: "claude" } ); const [firstResult, secondResult] = await Promise.all([firstCommand, secondCommand]); const totalTime = Date.now() - startTime; expect(firstResult.stdout).toContain("first"); expect(secondResult.stdout).toContain("second"); // Total time should be close to sleep duration (allowing for overhead) // This tests that second command starts immediately after first expect(totalTime).toBeLessThan(2000); // Should be ~1000ms + overhead }, 10000); it("should properly clear queue when all commands complete", async () => { await connectionManager.createConnection(mockConfig); // Execute several commands const commands = await Promise.all([ connectionManager.executeCommand(mockConfig.name, "echo 'cmd1'", { source: "user" }), connectionManager.executeCommand(mockConfig.name, "echo 'cmd2'", { source: "claude" }), connectionManager.executeCommand(mockConfig.name, "echo 'cmd3'", { source: "user" }) ]); // All commands should complete successfully commands.forEach((result, index) => { expect(result.stdout).toContain(`cmd${index + 1}`); }); // New command should execute immediately (no queue delay) const startTime = Date.now(); const immediateResult = await connectionManager.executeCommand( mockConfig.name, "echo 'immediate'", { source: "claude" } ); const executionTime = Date.now() - startTime; expect(immediateResult.stdout).toContain("immediate"); expect(executionTime).toBeLessThan(500); // Should be very fast }, 15000); }); describe("AC3.4: Command Source Preservation - responses maintain original command source", () => { it("should preserve source information through queue processing", async () => { await connectionManager.createConnection(mockConfig); // Track command sources through execution const userCmd = connectionManager.executeCommand( mockConfig.name, "echo 'user-originated'", { source: "user" as CommandSource } ); const claudeCmd = connectionManager.executeCommand( mockConfig.name, "echo 'claude-originated'", { source: "claude" as CommandSource } ); const [userResult, claudeResult] = await Promise.all([userCmd, claudeCmd]); // Source information should be preserved in command execution expect(userResult.stdout).toContain("user-originated"); expect(claudeResult.stdout).toContain("claude-originated"); // TODO: Once we implement queue tracking, verify source is preserved in queue }, 10000); }); describe("AC3.5: Error Handling in Queue - failed commands don't block queue", () => { it("should continue processing queue when a command fails", async () => { await connectionManager.createConnection(mockConfig); // First command that will fail const failingCommand = connectionManager.executeCommand( mockConfig.name, "invalidcommandthatdoesnotexist", { source: "user" } ); // Second command should still execute despite first failing const successCommand = connectionManager.executeCommand( mockConfig.name, "echo 'success after failure'", { source: "claude" } ); // First should fail, second should succeed try { await failingCommand; } catch (error) { // Expected to fail expect(error).toBeDefined(); } const successResult = await successCommand; expect(successResult.stdout).toContain("success after failure"); }, 10000); it("should provide proper error responses with source ID for failed queued commands", async () => { await connectionManager.createConnection(mockConfig); try { await connectionManager.executeCommand( mockConfig.name, "command_that_will_fail_with_bad_syntax_!!!", { source: "user" } ); fail("Expected command to throw error"); } catch (error) { expect(error).toBeDefined(); // Error should contain relevant information about the failed command expect((error as Error).message).toBeDefined(); } // Subsequent command should still work const result = await connectionManager.executeCommand( mockConfig.name, "echo 'recovered'", { source: "claude" } ); expect(result.stdout).toContain("recovered"); }, 10000); }); describe("Backward Compatibility", () => { it("should execute single commands immediately when no queue exists", async () => { await connectionManager.createConnection(mockConfig); const startTime = Date.now(); const result = await connectionManager.executeCommand( mockConfig.name, "echo 'single command'", { source: "claude" } ); const executionTime = Date.now() - startTime; expect(result.stdout).toContain("single command"); // Single command should execute quickly without queue delay expect(executionTime).toBeLessThan(1000); }, 10000); }); // Integration test combining multiple queue scenarios describe("Queue Integration", () => { it("should handle mixed user and Claude commands in queue correctly", async () => { await connectionManager.createConnection(mockConfig); // Mix of user and Claude commands const commands = [ { cmd: "echo 'user1'", source: "user" as CommandSource }, { cmd: "echo 'claude1'", source: "claude" as CommandSource }, { cmd: "echo 'user2'", source: "user" as CommandSource }, { cmd: "echo 'claude2'", source: "claude" as CommandSource } ]; const promises = commands.map(({ cmd, source }) => connectionManager.executeCommand(mockConfig.name, cmd, { source }) ); const results = await Promise.all(promises); // All commands should complete in order expect(results[0].stdout).toContain("user1"); expect(results[1].stdout).toContain("claude1"); expect(results[2].stdout).toContain("user2"); expect(results[3].stdout).toContain("claude2"); }, 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