Skip to main content
Glama
connection-manager.test.ts7.17 kB
/** * DatabaseConnectionManager Unit Tests * * Tests for PostgreSQL connection management with pooling, transactions, and error handling. * All external dependencies (pg module) are mocked for isolation. */ import { Pool } from "pg"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DatabaseConnectionManager, type DatabaseConfig, } from "../../../database/connection-manager"; vi.mock("pg", () => { return { Pool: vi.fn(), }; }); type MockClient = { query: ReturnType<typeof vi.fn>; release: ReturnType<typeof vi.fn>; }; type MockPool = { connect: ReturnType<typeof vi.fn>; end: ReturnType<typeof vi.fn>; query: ReturnType<typeof vi.fn>; totalCount: number; idleCount: number; waitingCount: number; }; function createMockPoolAndClient(): { mockPool: MockPool; mockClient: MockClient } { const mockClient: MockClient = { query: vi.fn().mockResolvedValue({ rows: [{ result: 1 }] }), release: vi.fn(), }; const mockPool: MockPool = { connect: vi.fn().mockResolvedValue(mockClient), end: vi.fn().mockResolvedValue(undefined), query: vi.fn().mockResolvedValue({ rows: [{ result: 1 }] }), totalCount: 20, idleCount: 15, waitingCount: 0, }; return { mockPool, mockClient }; } describe("DatabaseConnectionManager", () => { let manager: DatabaseConnectionManager; let config: DatabaseConfig; let mockPool: MockPool; let mockClient: MockClient; beforeEach(() => { const mocks = createMockPoolAndClient(); mockPool = mocks.mockPool; mockClient = mocks.mockClient; vi.mocked(Pool).mockImplementation(() => mockPool as unknown as Pool); config = { host: "localhost", port: 5432, database: "test_db", user: "test_user", password: "test_pass", poolSize: 20, connectionTimeout: 5000, idleTimeout: 30000, }; manager = new DatabaseConnectionManager(config); }); afterEach(async () => { if (manager.pool) { try { await manager.disconnect(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); describe("Connection Pool Creation", () => { it("should create connection pool with valid configuration", async () => { await manager.connect(); expect(manager.pool).toBeDefined(); expect(manager.config.poolSize).toBe(20); expect(manager.config.connectionTimeout).toBe(5000); }); it("should use default values for optional configuration", async () => { const minimalConfig = { host: "localhost", port: 5432, database: "test_db", user: "test_user", password: "test_pass", }; const managerWithDefaults = new DatabaseConnectionManager(minimalConfig as DatabaseConfig); await managerWithDefaults.connect(); expect(managerWithDefaults.config.poolSize).toBe(20); expect(managerWithDefaults.config.connectionTimeout).toBe(5000); await managerWithDefaults.disconnect(); }); it("should not create duplicate pools on multiple connect calls", async () => { await manager.connect(); const firstPool = manager.pool; await manager.connect(); const secondPool = manager.pool; expect(firstPool).toBe(secondPool); }); }); describe("Connection Acquisition and Release", () => { it("should acquire connection from pool", async () => { await manager.connect(); const client = await manager.getConnection(); expect(client).toBeDefined(); expect(mockPool.connect).toHaveBeenCalled(); }); it("should release connection back to pool", async () => { await manager.connect(); const client = await manager.getConnection(); manager.releaseConnection(client); expect(mockClient.release).toHaveBeenCalled(); }); it("should throw error when getting connection before connect", async () => { await expect(manager.getConnection()).rejects.toThrow(); }); }); describe("Transaction Management", () => { it("should begin transaction", async () => { await manager.connect(); const client = await manager.beginTransaction(); expect(client).toBeDefined(); expect(mockClient.query).toHaveBeenCalledWith("BEGIN"); }); it("should commit transaction", async () => { await manager.connect(); const client = await manager.beginTransaction(); await manager.commitTransaction(client); expect(mockClient.query).toHaveBeenCalledWith("COMMIT"); expect(mockClient.release).toHaveBeenCalled(); }); it("should rollback transaction", async () => { await manager.connect(); const client = await manager.beginTransaction(); await manager.rollbackTransaction(client); expect(mockClient.query).toHaveBeenCalledWith("ROLLBACK"); expect(mockClient.release).toHaveBeenCalled(); }); }); describe("Health Check", () => { it("should return true when database is healthy", async () => { await manager.connect(); const isHealthy = await manager.healthCheck(); expect(isHealthy).toBe(true); }); it("should return false when database is unhealthy", async () => { await manager.connect(); mockPool.query.mockRejectedValueOnce(new Error("Connection failed")); const isHealthy = await manager.healthCheck(); expect(isHealthy).toBe(false); }); it("should return false when not connected", async () => { const isHealthy = await manager.healthCheck(); expect(isHealthy).toBe(false); }); }); describe("Pool Statistics", () => { it("should return pool statistics", async () => { await manager.connect(); const stats = manager.getPoolStats(); expect(stats.totalConnections).toBe(20); expect(stats.idleConnections).toBe(15); expect(stats.waitingClients).toBe(0); }); it("should throw error when getting stats before connect", () => { expect(() => manager.getPoolStats()).toThrow(); }); }); describe("Error Handling", () => { it("should handle connection failure gracefully", async () => { mockPool.connect.mockRejectedValueOnce(new Error("Connection failed")); await manager.connect(); await expect(manager.getConnection()).rejects.toThrow("Connection failed"); }); it("should handle disconnect when not connected", async () => { await expect(manager.disconnect()).resolves.not.toThrow(); }); it("should handle commit transaction error", async () => { await manager.connect(); const client = await manager.beginTransaction(); mockClient.query.mockRejectedValueOnce(new Error("Commit failed")); await expect(manager.commitTransaction(client)).rejects.toThrow(); }); it("should handle rollback transaction error", async () => { await manager.connect(); const client = await manager.beginTransaction(); mockClient.query.mockRejectedValueOnce(new Error("Rollback failed")); await expect(manager.rollbackTransaction(client)).rejects.toThrow(); }); }); });

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/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server