Skip to main content
Glama
ssh-key-file-enhancement.test.ts8.11 kB
/** * TDD Tests for SSH Key File Enhancement Epic * Tests new keyFilePath and passphrase parameters */ import { SSHConnectionManager } from "../src/ssh-connection-manager.js"; import { SSHConnectionConfig } from "../src/types.js"; import { MCPSSHServer } from "../src/mcp-ssh-server.js"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; describe("SSH Key File Enhancement", () => { let connectionManager: SSHConnectionManager; let mcpServer: MCPSSHServer; const testKeyDir = path.join(os.tmpdir(), "ssh-key-test"); beforeEach(() => { connectionManager = new SSHConnectionManager(); mcpServer = new MCPSSHServer(); // Create test directory if (!fs.existsSync(testKeyDir)) { fs.mkdirSync(testKeyDir, { recursive: true }); } }); afterEach(() => { connectionManager.cleanup(); if (mcpServer) { void mcpServer.stop(); } // Clean up test directory if (fs.existsSync(testKeyDir)) { fs.rmSync(testKeyDir, { recursive: true, force: true }); } }); describe("AC1: Key File Path Parameter", () => { it("should fail - keyFilePath parameter not yet supported in SSHConnectionConfig", () => { // This test should FAIL until we add keyFilePath to types.ts const config: SSHConnectionConfig = { name: "test-keyfile", host: "localhost", username: "testuser", keyFilePath: "~/.ssh/id_rsa" // This property should not exist yet }; // Test should fail because keyFilePath is not in the interface expect(config.keyFilePath).toBeDefined(); }); it("should fail - createConnection does not support keyFilePath parameter", async () => { const testKeyPath = path.join(testKeyDir, "test_key"); const testPrivateKey = generateTestPrivateKey(); fs.writeFileSync(testKeyPath, testPrivateKey); const config: any = { name: "test-keyfile", host: "localhost", username: "testuser", keyFilePath: testKeyPath }; // This should fail because connection manager doesn't support keyFilePath yet await expect(connectionManager.createConnection(config)) .rejects.toThrow(); }); it("should fail - MCP server ssh_connect tool schema missing keyFilePath", async () => { // This should fail because the MCP tool schema doesn't include keyFilePath const result = await mcpServer.callTool("ssh_connect", { name: "test-session", host: "localhost", username: "testuser", keyFilePath: "~/.ssh/id_rsa" }) as any; expect(result.success).toBe(true); // This should fail initially }); }); describe("AC2: Encrypted Key with Passphrase", () => { it("should fail - passphrase parameter not supported in SSHConnectionConfig", () => { const config: SSHConnectionConfig = { name: "test-encrypted", host: "localhost", username: "testuser", keyFilePath: "~/.ssh/encrypted_key", passphrase: "mypassword" // This property should not exist yet }; expect(config.passphrase).toBeDefined(); }); it("should fail - encrypted key decryption not implemented", async () => { const testKeyPath = path.join(testKeyDir, "encrypted_key"); const encryptedKey = generateEncryptedTestKey(); fs.writeFileSync(testKeyPath, encryptedKey); const config: any = { name: "test-encrypted", host: "localhost", username: "testuser", keyFilePath: testKeyPath, passphrase: "testpassword" }; // Should fail because encrypted key decryption is not implemented await expect(connectionManager.createConnection(config)) .rejects.toThrow(); }); }); describe("AC3: Path Expansion", () => { it("should fail - tilde path expansion not implemented", async () => { const homeDir = os.homedir(); const sshDir = path.join(homeDir, '.ssh'); const keyPath = path.join(sshDir, 'test_key'); // Create the key file in actual .ssh directory for this test if (!fs.existsSync(sshDir)) { fs.mkdirSync(sshDir, { recursive: true }); } fs.writeFileSync(keyPath, generateTestPrivateKey()); const config: any = { name: "test-tilde", host: "localhost", username: "testuser", keyFilePath: "~/.ssh/test_key" // Should expand to full path }; try { // This should fail because tilde expansion is not implemented await connectionManager.createConnection(config); expect(true).toBe(false); // Should not reach here } catch (error) { expect(error).toBeDefined(); // Expected to fail } finally { // Clean up if (fs.existsSync(keyPath)) { fs.unlinkSync(keyPath); } } }); }); describe("AC4: Error Handling", () => { it("should fail - file not found error handling not implemented", async () => { const config: any = { name: "test-notfound", host: "localhost", username: "testuser", keyFilePath: "/nonexistent/path/key" }; // Should fail because file not found handling is not implemented try { await connectionManager.createConnection(config); expect(true).toBe(false); // Should not succeed } catch (error) { expect((error as Error).message).toContain("not accessible"); } }); }); describe("AC5: Backward Compatibility", () => { it("should pass - existing privateKey parameter continues to work", async () => { const config: SSHConnectionConfig = { name: "test-backward", host: "localhost", username: "testuser", privateKey: generateTestPrivateKey() }; // This should still work (testing that we don't break existing functionality) await expect(connectionManager.createConnection(config)) .resolves.toBeDefined(); }, 15000); }); describe("AC6: Parameter Priority", () => { it("should fail - parameter priority logic not implemented", async () => { const testKeyPath = path.join(testKeyDir, "priority_test_key"); fs.writeFileSync(testKeyPath, generateTestPrivateKey()); const config: any = { name: "test-priority", host: "localhost", username: "testuser", privateKey: generateTestPrivateKey(), // Should take precedence keyFilePath: testKeyPath }; // Should fail because priority logic is not implemented // We need to verify that privateKey is used, not keyFilePath await expect(connectionManager.createConnection(config)) .resolves.toBeDefined(); // Additional verification that privateKey was used (this logic doesn't exist yet) expect(true).toBe(false); // This should fail until we implement logging/verification }, 15000); }); }); // Helper function to generate test RSA key - using a minimal valid RSA key for testing function generateTestPrivateKey(): string { return `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAyFGnBl4pPiYeGa9YBu2jNrRaUuT8pvp1wQKj8mXBV8EUBe6J nGvqnfHvNKrm3PK7GlQeR6eVh8L2/LnwPgIKjG9fRzUi1Q7VG2k5TqUvNZCJ5Q0i Xr8b/BoYlMa2rGdyuLGJOG7O3vSLB7P6oR2n0pKzFB8mH9vGqKlB1MdtmAzPbGdJ 7eU8QNcKjHrJ5o3B6V3GqF1JGfEa1vY4rqRJgYGW8qGp8KoqvJyEX9B2M3A6k1i3 B4KlXCOIgU8H7wJq2oT4zT8R5z0B7JKqDjH2SoGG9gQnFpD4z4E8H9oA2S1K4yZo wYRlqQK2T8K4H8m9OgIhJ4QjEH0yKqvK3aL4qYX1vwIDAQABAoIBAFXe+EbMQl5x ZoL5Y2jzpXOq1lZiOVsY7dOhD8gL7pQl3D1GkL4vV5oNuK8bZeE2nE3rE9xjCe9v E9qGmE2qY2hVqE5rW7pP8Z4x3K3qH1O9L4j0K7R2xY9v8mC4K7L9J2yI3kR7nW8P Q2L1vE9g2M3D4qKjG9fRzUi1Q7VG2k5TqUvNZCJ5Q0iXr8b/BoYlMa2rGdyuLGJO Test RSA key - safe for testing only, not for actual authentication -----END RSA PRIVATE KEY-----`; } function generateEncryptedTestKey(): string { return `-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCA8xWcRv Encrypted test key content - not a real key for testing purposes only -----END OPENSSH PRIVATE KEY-----`; }

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