Skip to content

Commit 42a522b

Browse files
Fix flaky port forwarder tests (#951)
1 parent 15e3227 commit 42a522b

File tree

4 files changed

+108
-117
lines changed

4 files changed

+108
-117
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { GenericContainer } from "../generic-container/generic-container";
2+
import { RandomUniquePortGenerator } from "../utils/port-generator";
3+
import { createTestServer } from "../utils/test-helper";
4+
5+
describe("Port Forwarder reuse", { timeout: 180_000 }, () => {
6+
it("should expose additional ports", async () => {
7+
const portGen = new RandomUniquePortGenerator();
8+
9+
const { TestContainers: TC1 } = await import("../test-containers");
10+
const { PortForwarderInstance: PFI1 } = await import("../port-forwarder/port-forwarder");
11+
const port1 = await portGen.generatePort();
12+
const server1 = await createTestServer(port1);
13+
await TC1.exposeHostPorts(port1);
14+
const portForwarder1ContainerId = (await PFI1.getInstance()).getContainerId();
15+
16+
vi.resetModules();
17+
const { TestContainers: TC2 } = await import("../test-containers");
18+
const { PortForwarderInstance: PFI2 } = await import("../port-forwarder/port-forwarder");
19+
const port2 = await portGen.generatePort();
20+
const server2 = await createTestServer(port2);
21+
await TC2.exposeHostPorts(port2);
22+
const portForwarder2ContainerId = (await PFI2.getInstance()).getContainerId();
23+
24+
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
25+
26+
expect(portForwarder1ContainerId).toEqual(portForwarder2ContainerId);
27+
const { output: output1 } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port1}`]);
28+
expect(output1).toEqual(expect.stringContaining("hello world"));
29+
const { output: output2 } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port2}`]);
30+
expect(output2).toEqual(expect.stringContaining("hello world"));
31+
32+
await new Promise((resolve) => server1.close(resolve));
33+
await new Promise((resolve) => server2.close(resolve));
34+
await container.stop();
35+
});
36+
37+
it("should reuse same ports", async () => {
38+
const portGen = new RandomUniquePortGenerator();
39+
const port = await portGen.generatePort();
40+
const server = await createTestServer(port);
41+
42+
const { TestContainers: TC1 } = await import("../test-containers");
43+
const { PortForwarderInstance: PFI1 } = await import("../port-forwarder/port-forwarder");
44+
await TC1.exposeHostPorts(port);
45+
const portForwarder1ContainerId = (await PFI1.getInstance()).getContainerId();
46+
47+
vi.resetModules();
48+
const { TestContainers: TC2 } = await import("../test-containers");
49+
const { PortForwarderInstance: PFI2 } = await import("../port-forwarder/port-forwarder");
50+
await TC2.exposeHostPorts(port);
51+
const portForwarder2ContainerId = (await PFI2.getInstance()).getContainerId();
52+
53+
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
54+
55+
expect(portForwarder1ContainerId).toEqual(portForwarder2ContainerId);
56+
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port}`]);
57+
expect(output).toEqual(expect.stringContaining("hello world"));
58+
59+
await new Promise((resolve) => server.close(resolve));
60+
await container.stop();
61+
});
62+
});
Lines changed: 34 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,60 @@
1-
import { createServer, Server } from "http";
1+
import { Server } from "http";
22
import { GenericContainer } from "../generic-container/generic-container";
33
import { Network } from "../network/network";
44
import { TestContainers } from "../test-containers";
55
import { RandomUniquePortGenerator } from "../utils/port-generator";
6+
import { createTestServer } from "../utils/test-helper";
67

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

11-
afterEach(() => {
12-
server.close();
12+
beforeEach(async () => {
13+
randomPort = await new RandomUniquePortGenerator().generatePort();
14+
server = await createTestServer(randomPort);
1315
});
1416

15-
describe("Behaviour", () => {
16-
beforeEach(async () => {
17-
randomPort = await new RandomUniquePortGenerator().generatePort();
18-
server = await createTestServer(randomPort);
19-
});
20-
21-
it("should expose host ports to the container", async () => {
22-
await TestContainers.exposeHostPorts(randomPort);
23-
24-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
25-
26-
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
27-
expect(output).toEqual(expect.stringContaining("hello world"));
28-
29-
await container.stop();
30-
});
31-
32-
it("should expose host ports to the container with custom network", async () => {
33-
await TestContainers.exposeHostPorts(randomPort);
34-
35-
const network = await new Network().start();
36-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start();
37-
38-
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
39-
expect(output).toEqual(expect.stringContaining("hello world"));
40-
41-
await container.stop();
42-
await network.stop();
43-
});
44-
45-
it("should expose host ports to the container with custom network and network alias", async () => {
46-
await TestContainers.exposeHostPorts(randomPort);
47-
48-
const network = await new Network().start();
49-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
50-
.withNetwork(network)
51-
.withNetworkAliases("foo")
52-
.start();
53-
54-
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
55-
expect(output).toEqual(expect.stringContaining("hello world"));
56-
57-
await container.stop();
58-
await network.stop();
59-
});
17+
afterEach(async () => {
18+
await new Promise((resolve) => server.close(resolve));
6019
});
6120

62-
describe("Reuse", () => {
63-
afterEach(() => {
64-
vi.resetModules();
65-
});
21+
it("should expose host ports to the container", async () => {
22+
await TestContainers.exposeHostPorts(randomPort);
6623

67-
describe("Different host ports", () => {
68-
beforeEach(async () => {
69-
randomPort = await new RandomUniquePortGenerator().generatePort();
70-
server = await createTestServer(randomPort);
71-
});
24+
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
7225

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

77-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
78-
79-
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
80-
expect(output).toEqual(expect.stringContaining("hello world"));
81-
82-
await container.stop();
83-
});
84-
85-
it("2", async () => {
86-
const { TestContainers } = await import("../test-containers");
87-
await TestContainers.exposeHostPorts(randomPort);
88-
89-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
90-
91-
const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]);
92-
expect(output).toEqual(expect.stringContaining("hello world"));
93-
94-
await container.stop();
95-
});
96-
});
97-
98-
describe("Same host ports", () => {
99-
beforeAll(async () => {
100-
randomPort = await new RandomUniquePortGenerator().generatePort();
101-
});
102-
103-
beforeEach(async () => {
104-
server = await createTestServer(randomPort);
105-
});
29+
await container.stop();
30+
});
10631

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

111-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
35+
const network = await new Network().start();
36+
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start();
11237

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

116-
await container.stop();
117-
});
41+
await container.stop();
42+
await network.stop();
43+
});
11844

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

123-
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start();
48+
const network = await new Network().start();
49+
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
50+
.withNetwork(network)
51+
.withNetworkAliases("foo")
52+
.start();
12453

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

128-
await container.stop();
129-
});
130-
});
57+
await container.stop();
58+
await network.stop();
13159
});
13260
});
133-
134-
async function createTestServer(port: number): Promise<Server> {
135-
const server = createServer((req, res) => {
136-
res.writeHead(200);
137-
res.end("hello world");
138-
});
139-
await new Promise<void>((resolve) => server.listen(port, resolve));
140-
return server;
141-
}

packages/testcontainers/src/utils/port-generator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ export interface PortGenerator {
44

55
class RandomPortGenerator {
66
public async generatePort(): Promise<number> {
7-
const { default: getPort, portNumbers } = await import("get-port");
8-
return getPort({ port: portNumbers(10000, 65535) });
7+
const { default: getPort } = await import("get-port");
8+
return getPort();
99
}
1010
}
1111

packages/testcontainers/src/utils/test-helper.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { GetEventsOptions, ImageInspectInfo } from "dockerode";
2+
import { createServer, Server } from "http";
23
import { Readable } from "stream";
34
import { Agent } from "undici";
45
import { IntervalRetry } from "../common";
@@ -143,3 +144,12 @@ export async function stopStartingContainer(container: GenericContainer, name: s
143144
await client.container.getById(name).stop();
144145
await containerStartPromise;
145146
}
147+
148+
export async function createTestServer(port: number): Promise<Server> {
149+
const server = createServer((req, res) => {
150+
res.writeHead(200);
151+
res.end("hello world");
152+
});
153+
await new Promise<void>((resolve) => server.listen(port, resolve));
154+
return server;
155+
}

0 commit comments

Comments
 (0)