From fa0a7d1a4fe7ead84d02db08a651afed2a694eb4 Mon Sep 17 00:00:00 2001 From: quiloos39 Date: Mon, 16 Feb 2026 14:15:24 +0300 Subject: [PATCH] Add connection manager tests --- tests/connection-manager.test.ts | 177 +++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 tests/connection-manager.test.ts diff --git a/tests/connection-manager.test.ts b/tests/connection-manager.test.ts new file mode 100644 index 0000000..2842b82 --- /dev/null +++ b/tests/connection-manager.test.ts @@ -0,0 +1,177 @@ +import { describe, it, beforeEach, expect } from "vitest"; +import { Client, ConnectConfig } from "ssh2"; +import { + connections, + StoredConnection, + cleanupAllConnections, + MAX_OUTPUT_BYTES, +} from "../src/connections.js"; + +/** + * Helper to create a mock StoredConnection for testing purposes. + * Uses a real Client instance but never actually connects it. + */ +function makeMockConnection( + host: string, + ready: boolean = true +): StoredConnection { + const client = new Client(); + const connectConfig: ConnectConfig = { + host, + port: 22, + username: "testuser", + }; + return { client, connectConfig, ready, host }; +} + +describe("Connection Manager - Global connections map", () => { + beforeEach(() => { + // Ensure a clean state before each test by clearing any leftover entries + connections.clear(); + }); + + it("should start with an empty connections map", () => { + expect(connections.size).toBe(0); + }); + + it("should store a connection and retrieve it by ID", () => { + const conn = makeMockConnection("server1.example.com"); + connections.set("conn-1", conn); + + expect(connections.has("conn-1")).toBe(true); + const retrieved = connections.get("conn-1"); + expect(retrieved).toBeDefined(); + expect(retrieved!.host).toBe("server1.example.com"); + expect(retrieved!.connectConfig.username).toBe("testuser"); + expect(retrieved!.connectConfig.port).toBe(22); + }); + + it("should return undefined for a non-existent connection ID", () => { + const result = connections.get("does-not-exist"); + expect(result).toBeUndefined(); + expect(connections.has("does-not-exist")).toBe(false); + }); + + it("should remove a connection from the map using delete", () => { + const conn = makeMockConnection("server2.example.com"); + connections.set("conn-2", conn); + expect(connections.size).toBe(1); + + connections.delete("conn-2"); + expect(connections.size).toBe(0); + expect(connections.get("conn-2")).toBeUndefined(); + }); + + it("should track the ready state of a connection", () => { + const conn = makeMockConnection("server3.example.com", true); + connections.set("conn-3", conn); + + expect(connections.get("conn-3")!.ready).toBe(true); + + // Simulate the connection going dead (as the close/end handlers do) + conn.ready = false; + expect(connections.get("conn-3")!.ready).toBe(false); + + // Simulate reconnection becoming ready again + conn.ready = true; + expect(connections.get("conn-3")!.ready).toBe(true); + }); + + it("should allow multiple connections to coexist in the map", () => { + const conn1 = makeMockConnection("host-a.example.com"); + const conn2 = makeMockConnection("host-b.example.com"); + const conn3 = makeMockConnection("host-c.example.com"); + + connections.set("a", conn1); + connections.set("b", conn2); + connections.set("c", conn3); + + expect(connections.size).toBe(3); + expect(connections.get("a")!.host).toBe("host-a.example.com"); + expect(connections.get("b")!.host).toBe("host-b.example.com"); + expect(connections.get("c")!.host).toBe("host-c.example.com"); + }); + + it("should replace an existing connection when set with the same ID", () => { + const original = makeMockConnection("original-host.example.com"); + connections.set("shared-id", original); + expect(connections.get("shared-id")!.host).toBe( + "original-host.example.com" + ); + + const replacement = makeMockConnection("replacement-host.example.com"); + connections.set("shared-id", replacement); + + expect(connections.size).toBe(1); + expect(connections.get("shared-id")!.host).toBe( + "replacement-host.example.com" + ); + }); + + it("should iterate over all connections", () => { + connections.set("x", makeMockConnection("host-x.example.com")); + connections.set("y", makeMockConnection("host-y.example.com")); + + const ids: string[] = []; + const hosts: string[] = []; + for (const [id, stored] of connections) { + ids.push(id); + hosts.push(stored.host); + } + + expect(ids).toEqual(["x", "y"]); + expect(hosts).toEqual(["host-x.example.com", "host-y.example.com"]); + }); +}); + +describe("Connection Manager - cleanupAllConnections", () => { + beforeEach(() => { + connections.clear(); + }); + + it("should remove all connections from the map", () => { + connections.set("c1", makeMockConnection("host1.example.com")); + connections.set("c2", makeMockConnection("host2.example.com")); + connections.set("c3", makeMockConnection("host3.example.com")); + expect(connections.size).toBe(3); + + cleanupAllConnections(); + + expect(connections.size).toBe(0); + expect(connections.get("c1")).toBeUndefined(); + expect(connections.get("c2")).toBeUndefined(); + expect(connections.get("c3")).toBeUndefined(); + }); + + it("should handle cleanup when the map is already empty", () => { + expect(connections.size).toBe(0); + // Should not throw + cleanupAllConnections(); + expect(connections.size).toBe(0); + }); +}); + +describe("Connection Manager - MAX_OUTPUT_BYTES constant", () => { + it("should be set to 1MB (1024 * 1024 bytes)", () => { + expect(MAX_OUTPUT_BYTES).toBe(1024 * 1024); + expect(MAX_OUTPUT_BYTES).toBe(1_048_576); + }); +}); + +describe("Connection Manager - StoredConnection interface shape", () => { + it("should have all required fields with correct types", () => { + const conn = makeMockConnection("test-host.example.com", false); + + // Verify the shape of StoredConnection + expect(conn).toHaveProperty("client"); + expect(conn).toHaveProperty("connectConfig"); + expect(conn).toHaveProperty("ready"); + expect(conn).toHaveProperty("host"); + + // Verify types + expect(conn.client).toBeInstanceOf(Client); + expect(typeof conn.ready).toBe("boolean"); + expect(typeof conn.host).toBe("string"); + expect(typeof conn.connectConfig).toBe("object"); + }); +});