Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { GenericContainer } from "../generic-container/generic-container";
import { RandomUniquePortGenerator } from "../utils/port-generator";
import { createTestServer } from "../utils/test-helper";

describe("Port Forwarder reuse", { timeout: 180_000 }, () => {
it("should expose additional ports", async () => {
const portGen = new RandomUniquePortGenerator();

const { TestContainers: TC1 } = await import("../test-containers");
const { PortForwarderInstance: PFI1 } = await import("../port-forwarder/port-forwarder");
const port1 = await portGen.generatePort();
const server1 = await createTestServer(port1);
await TC1.exposeHostPorts(port1);
const portForwarder1ContainerId = (await PFI1.getInstance()).getContainerId();

vi.resetModules();
const { TestContainers: TC2 } = await import("../test-containers");
const { PortForwarderInstance: PFI2 } = await import("../port-forwarder/port-forwarder");
const port2 = await portGen.generatePort();
const server2 = await createTestServer(port2);
await TC2.exposeHostPorts(port2);
const portForwarder2ContainerId = (await PFI2.getInstance()).getContainerId();

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();

expect(portForwarder1ContainerId).toEqual(portForwarder2ContainerId);
const { output: output1 } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port1}`]);
expect(output1).toEqual(expect.stringContaining("hello world"));
const { output: output2 } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port2}`]);
expect(output2).toEqual(expect.stringContaining("hello world"));

await new Promise((resolve) => server1.close(resolve));
await new Promise((resolve) => server2.close(resolve));
await container.stop();
});

it("should reuse same ports", async () => {
const portGen = new RandomUniquePortGenerator();
const port = await portGen.generatePort();
const server = await createTestServer(port);

const { TestContainers: TC1 } = await import("../test-containers");
const { PortForwarderInstance: PFI1 } = await import("../port-forwarder/port-forwarder");
await TC1.exposeHostPorts(port);
const portForwarder1ContainerId = (await PFI1.getInstance()).getContainerId();

vi.resetModules();
const { TestContainers: TC2 } = await import("../test-containers");
const { PortForwarderInstance: PFI2 } = await import("../port-forwarder/port-forwarder");
await TC2.exposeHostPorts(port);
const portForwarder2ContainerId = (await PFI2.getInstance()).getContainerId();

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();

expect(portForwarder1ContainerId).toEqual(portForwarder2ContainerId);
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await new Promise((resolve) => server.close(resolve));
await container.stop();
});
});
149 changes: 34 additions & 115 deletions packages/testcontainers/src/port-forwarder/port-forwarder.test.ts
Original file line number Diff line number Diff line change
@@ -1,141 +1,60 @@
import { createServer, Server } from "http";
import { Server } from "http";
import { GenericContainer } from "../generic-container/generic-container";
import { Network } from "../network/network";
import { TestContainers } from "../test-containers";
import { RandomUniquePortGenerator } from "../utils/port-generator";
import { createTestServer } from "../utils/test-helper";

describe("PortForwarder", { timeout: 180_000 }, () => {
let randomPort: number;
let server: Server;

afterEach(() => {
server.close();
beforeEach(async () => {
randomPort = await new RandomUniquePortGenerator().generatePort();
server = await createTestServer(randomPort);
});

describe("Behaviour", () => {
beforeEach(async () => {
randomPort = await new RandomUniquePortGenerator().generatePort();
server = await createTestServer(randomPort);
});

it("should expose host ports to the container", async () => {
await TestContainers.exposeHostPorts(randomPort);

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
});

it("should expose host ports to the container with custom network", async () => {
await TestContainers.exposeHostPorts(randomPort);

const network = await new Network().start();
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
await network.stop();
});

it("should expose host ports to the container with custom network and network alias", async () => {
await TestContainers.exposeHostPorts(randomPort);

const network = await new Network().start();
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
.withNetwork(network)
.withNetworkAliases("foo")
.start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
await network.stop();
});
afterEach(async () => {
await new Promise((resolve) => server.close(resolve));
});

describe("Reuse", () => {
afterEach(() => {
vi.resetModules();
});
it("should expose host ports to the container", async () => {
await TestContainers.exposeHostPorts(randomPort);

describe("Different host ports", () => {
beforeEach(async () => {
randomPort = await new RandomUniquePortGenerator().generatePort();
server = await createTestServer(randomPort);
});
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();

it("1", async () => {
const { TestContainers } = await import("../test-containers");
await TestContainers.exposeHostPorts(randomPort);
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
});

it("2", async () => {
const { TestContainers } = await import("../test-containers");
await TestContainers.exposeHostPorts(randomPort);

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
});
});

describe("Same host ports", () => {
beforeAll(async () => {
randomPort = await new RandomUniquePortGenerator().generatePort();
});

beforeEach(async () => {
server = await createTestServer(randomPort);
});
await container.stop();
});

it("1", async () => {
const { TestContainers } = await import("../test-containers");
await TestContainers.exposeHostPorts(randomPort);
it("should expose host ports to the container with custom network", async () => {
await TestContainers.exposeHostPorts(randomPort);

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
const network = await new Network().start();
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
});
await container.stop();
await network.stop();
});

it("2", async () => {
const { TestContainers } = await import("../test-containers");
await TestContainers.exposeHostPorts(randomPort);
it("should expose host ports to the container with custom network and network alias", async () => {
await TestContainers.exposeHostPorts(randomPort);

const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
const network = await new Network().start();
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
.withNetwork(network)
.withNetworkAliases("foo")
.start();

const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
expect(output).toEqual(expect.stringContaining("hello world"));

await container.stop();
});
});
await container.stop();
await network.stop();
});
});

async function createTestServer(port: number): Promise<Server> {
const server = createServer((req, res) => {
res.writeHead(200);
res.end("hello world");
});
await new Promise<void>((resolve) => server.listen(port, resolve));
return server;
}
4 changes: 2 additions & 2 deletions packages/testcontainers/src/utils/port-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export interface PortGenerator {

class RandomPortGenerator {
public async generatePort(): Promise<number> {
const { default: getPort, portNumbers } = await import("get-port");
return getPort({ port: portNumbers(10000, 65535) });
const { default: getPort } = await import("get-port");
return getPort();
}
}

Expand Down
10 changes: 10 additions & 0 deletions packages/testcontainers/src/utils/test-helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GetEventsOptions, ImageInspectInfo } from "dockerode";
import { createServer, Server } from "http";
import { Readable } from "stream";
import { Agent } from "undici";
import { IntervalRetry } from "../common";
Expand Down Expand Up @@ -143,3 +144,12 @@ export async function stopStartingContainer(container: GenericContainer, name: s
await client.container.getById(name).stop();
await containerStartPromise;
}

export async function createTestServer(port: number): Promise<Server> {
const server = createServer((req, res) => {
res.writeHead(200);
res.end("hello world");
});
await new Promise<void>((resolve) => server.listen(port, resolve));
return server;
}