import type Docker from "dockerode";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { HostConfig } from "../../../types.js";
import type { IDockerClientFactory } from "./client-factory.js";
import { ClientManager } from "./client-manager.js";
describe("ClientManager", () => {
let manager: ClientManager;
let mockFactory: IDockerClientFactory;
let mockClient: Docker;
beforeEach(() => {
mockClient = {
listContainers: vi.fn(),
ping: vi.fn(),
} as unknown as Docker;
mockFactory = {
createClient: vi.fn(() => Promise.resolve(mockClient)),
};
manager = new ClientManager(mockFactory);
});
it("should create a new client on first access", async (): Promise<void> => {
const config: HostConfig = {
name: "test-host",
host: "localhost",
};
const client = await manager.getClient(config);
expect(mockFactory.createClient).toHaveBeenCalledWith(config);
expect(mockFactory.createClient).toHaveBeenCalledTimes(1);
expect(client).toBe(mockClient);
});
it("should return cached client on subsequent access", async (): Promise<void> => {
const config: HostConfig = {
name: "test-host",
host: "localhost",
};
const client1 = await manager.getClient(config);
const client2 = await manager.getClient(config);
expect(mockFactory.createClient).toHaveBeenCalledTimes(1);
expect(client1).toBe(client2);
expect(client1).toBe(mockClient);
});
it("should use name and host for cache key", async (): Promise<void> => {
const config1: HostConfig = {
name: "host1",
host: "server1.example.com",
};
const config2: HostConfig = {
name: "host2",
host: "server2.example.com",
};
const client1 = await manager.getClient(config1);
const client2 = await manager.getClient(config2);
expect(mockFactory.createClient).toHaveBeenCalledTimes(2);
expect(client1).toBe(mockClient);
expect(client2).toBe(mockClient);
});
it("should maintain separate cache entries per host", async (): Promise<void> => {
const config1: HostConfig = {
name: "host1",
host: "server1.example.com",
};
const config2: HostConfig = {
name: "host1", // same name
host: "server2.example.com", // different host
};
await manager.getClient(config1);
await manager.getClient(config2);
expect(mockFactory.createClient).toHaveBeenCalledTimes(2);
});
it("should clear all cached clients", async (): Promise<void> => {
const config: HostConfig = {
name: "test-host",
host: "localhost",
};
// Create and cache a client
const client1 = await manager.getClient(config);
expect(mockFactory.createClient).toHaveBeenCalledTimes(1);
// Clear cache
manager.clearClients();
// Next access should create new client
const client2 = await manager.getClient(config);
expect(mockFactory.createClient).toHaveBeenCalledTimes(2);
expect(client1).toBe(mockClient);
expect(client2).toBe(mockClient);
});
it("should return same client after clearing if re-fetched", async (): Promise<void> => {
const config: HostConfig = {
name: "test-host",
host: "localhost",
};
await manager.getClient(config);
manager.clearClients();
const client = await manager.getClient(config);
expect(client).toBe(mockClient);
expect(mockFactory.createClient).toHaveBeenCalledTimes(2);
});
it("should clear specific client by host name", async (): Promise<void> => {
const config1: HostConfig = {
name: "host1",
host: "server1.example.com",
};
const config2: HostConfig = {
name: "host2",
host: "server2.example.com",
};
// Create two clients
await manager.getClient(config1);
await manager.getClient(config2);
expect(mockFactory.createClient).toHaveBeenCalledTimes(2);
// Clear only host1
manager.clearClient("host1");
// Access host1 again - should create new client
manager.getClient(config1);
expect(mockFactory.createClient).toHaveBeenCalledTimes(3);
// Access host2 again - should use cached client
manager.getClient(config2);
expect(mockFactory.createClient).toHaveBeenCalledTimes(3);
});
it("should not throw when clearing non-existent client", (): void => {
expect(() => manager.clearClient("non-existent")).not.toThrow();
});
it("should return stats with total clients and hosts list", async (): Promise<void> => {
const config1: HostConfig = {
name: "host1",
host: "server1.example.com",
};
const config2: HostConfig = {
name: "host2",
host: "server2.example.com",
};
const config3: HostConfig = {
name: "host3",
host: "server3.example.com",
};
// Create three clients
await manager.getClient(config1);
await manager.getClient(config2);
await manager.getClient(config3);
const stats = manager.getStats();
expect(stats.totalClients).toBe(3);
expect(stats.hosts).toHaveLength(3);
expect(stats.hosts).toContain("host1-server1.example.com");
expect(stats.hosts).toContain("host2-server2.example.com");
expect(stats.hosts).toContain("host3-server3.example.com");
});
it("should return empty stats when no clients cached", (): void => {
const stats = manager.getStats();
expect(stats.totalClients).toBe(0);
expect(stats.hosts).toHaveLength(0);
});
});