Skip to main content
Glama
localstack
by localstack
docker.client.test.ts4.69 kB
import { DockerApiClient } from "./docker.client"; import { PassThrough } from "stream"; jest.mock("dockerode", () => { const listContainers = jest.fn(); const execInspect = jest.fn(); const start = jest.fn((opts: any, cb: any) => { const stream = new PassThrough(); setImmediate(() => cb(null, stream)); return undefined as unknown as NodeJS.ReadableStream; }); const exec = jest.fn(async () => ({ start, inspect: execInspect })); const getContainer = jest.fn(() => ({ exec })); const __state = { demuxTarget: "stdout" as "stdout" | "stderr" }; class DockerMock { static __mocks = { listContainers, getContainer, exec, start, execInspect, __state }; modem: any; constructor() { this.modem = { demuxStream: ( combined: NodeJS.ReadableStream, stdout: PassThrough, stderr: PassThrough ) => { combined.on("data", (d) => { if (__state.demuxTarget === "stdout") stdout.write(d); else stderr.write(d); }); combined.on("end", () => { stdout.end(); stderr.end(); }); }, }; } listContainers = listContainers; getContainer = getContainer; } return DockerMock as any; }); const getDockerMocks = () => (require("dockerode") as any).__mocks; describe("DockerApiClient", () => { beforeEach(() => { const mocks = getDockerMocks(); mocks.listContainers.mockReset(); mocks.getContainer.mockReset(); mocks.exec.mockReset(); mocks.start.mockReset(); mocks.execInspect.mockReset(); // Restore default implementations after reset mocks.getContainer.mockImplementation(() => ({ exec: mocks.exec })); mocks.exec.mockImplementation(async () => ({ start: mocks.start, inspect: mocks.execInspect })); mocks.__state.demuxTarget = "stdout"; }); test("findLocalStackContainer throws when none found", async () => { const mocks = getDockerMocks(); mocks.listContainers.mockResolvedValueOnce([]); const client = new DockerApiClient(); await expect(client.findLocalStackContainer()).rejects.toThrow( /Could not find a running LocalStack container/i ); }); test("findLocalStackContainer returns id when found", async () => { const mocks = getDockerMocks(); mocks.listContainers.mockResolvedValueOnce([{ Id: "abc123", Names: ["/localstack-main"] }]); const client = new DockerApiClient(); await expect(client.findLocalStackContainer()).resolves.toBe("abc123"); }); test("executeInContainer returns stdout on success", async () => { const mocks = getDockerMocks(); mocks.listContainers.mockResolvedValueOnce([{ Id: "abc123", Names: ["/localstack-main"] }]); // prepare exec.inspect to return 0 mocks.execInspect.mockResolvedValueOnce({ ExitCode: 0 }); const client = new DockerApiClient(); const containerId = await client.findLocalStackContainer(); // Start call: we must simulate demux writing to stdout, then end the stream const stream = new PassThrough(); mocks.start.mockImplementationOnce((opts: any, cb: any) => { setImmediate(() => { cb(null, stream); setImmediate(() => { }); }); }); const execPromise = client.executeInContainer(containerId, ["echo", "hello"]); // After a tick, feed data to combined stream and end it setImmediate(() => { stream.write("hello-world\n"); stream.end(); }); const res = await execPromise; expect(res.exitCode).toBe(0); expect(res.stdout).toContain("hello-world"); }); test("executeInContainer returns stderr on failure", async () => { const mocks = getDockerMocks(); mocks.listContainers.mockResolvedValueOnce([{ Id: "abc123", Names: ["/localstack-main"] }]); mocks.execInspect.mockResolvedValueOnce({ ExitCode: 2 }); const client = new DockerApiClient(); const containerId = await client.findLocalStackContainer(); const stream = new PassThrough(); mocks.start.mockImplementationOnce((opts: any, cb: any) => { setImmediate(() => cb(null, stream)); }); // route combined stream to stderr for this test getDockerMocks().__state.demuxTarget = "stderr"; const execPromise = client.executeInContainer(containerId, ["sh", "-c", "exit 2"]); setImmediate(() => { // our default demux pipes to stdout; simulate stderr by writing a marker and expect stderr to capture it stream.write("something went wrong\n"); stream.end(); }); const res = await execPromise; expect(res.exitCode).toBe(2); expect(res.stderr).toContain("something went wrong"); }); });

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/localstack/localstack-mcp-server'

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