Skip to content

Commit 6ec9474

Browse files
Remove redundant wait strategy integration tests
1 parent e00c98e commit 6ec9474

5 files changed

Lines changed: 111 additions & 168 deletions

File tree

packages/testcontainers/fixtures/docker-compose/docker-compose-with-disabled-healthcheck.yml

Lines changed: 0 additions & 9 deletions
This file was deleted.

packages/testcontainers/fixtures/docker/docker-with-delayed-health-check/Dockerfile

Lines changed: 0 additions & 8 deletions
This file was deleted.

packages/testcontainers/fixtures/docker/docker-with-disabled-health-check/Dockerfile

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/testcontainers/src/docker-compose-environment/docker-compose-environment.test.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,6 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => {
108108
await checkEnvironmentContainerIsHealthy(startedEnvironment, "container-1");
109109
});
110110

111-
it("should use listening ports if a service disables healthcheck", async () => {
112-
await using startedEnvironment = await new DockerComposeEnvironment(
113-
fixtures,
114-
"docker-compose-with-disabled-healthcheck.yml"
115-
)
116-
.withStartupTimeout(1_000)
117-
.up();
118-
const container = startedEnvironment.getContainer("container-1");
119-
120-
await checkEnvironmentContainerIsHealthy(startedEnvironment, "container-1");
121-
expect(await getHealthCheckStatus(container)).toBeUndefined();
122-
});
123-
124111
it("should support log message wait strategy", async () => {
125112
await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml")
126113
.withWaitStrategy("container-1", Wait.forLogMessage("Listening on port 8080"))
Lines changed: 111 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
import { ContainerInspectInfo, ImageInspectInfo } from "dockerode";
2-
import path from "path";
3-
import { randomUuid } from "../common/uuid";
42
import { ContainerRuntimeClient } from "../container-runtime";
5-
import { checkContainerIsHealthy, getHealthCheckStatus } from "../utils/test-helper";
63
import { HealthCheckWaitStrategy } from "../wait-strategies/health-check-wait-strategy";
74
import { HostPortWaitStrategy } from "../wait-strategies/host-port-wait-strategy";
85
import { Wait } from "../wait-strategies/wait";
6+
import { WaitStrategy } from "../wait-strategies/wait-strategy";
97
import { GenericContainer } from "./generic-container";
108

11-
const fixtures = path.resolve(__dirname, "..", "..", "fixtures", "docker");
12-
139
class TestGenericContainer extends GenericContainer {
14-
public selectWaitStrategyForTest(client: ContainerRuntimeClient, inspectResult: ContainerInspectInfo) {
15-
return this.selectWaitStrategy(client, inspectResult);
10+
public selectWaitStrategyForTest(
11+
client: ContainerRuntimeClient,
12+
inspectResult: ContainerInspectInfo,
13+
waitStrategy?: WaitStrategy
14+
) {
15+
return this.selectWaitStrategy(client, inspectResult, waitStrategy);
1616
}
1717
}
1818

19-
const containerInspectResult = (healthcheck?: { Test: string[] }): ContainerInspectInfo =>
19+
type ContainerInspectResultOptions = {
20+
healthcheck?: { Test: string[] };
21+
healthcheckStatus?: string;
22+
};
23+
24+
const containerInspectResult = ({
25+
healthcheck,
26+
healthcheckStatus,
27+
}: ContainerInspectResultOptions = {}): ContainerInspectInfo =>
2028
({
2129
Config: {
2230
Hostname: "hostname",
31+
Image: "image:latest",
2332
Labels: {},
2433
Healthcheck: healthcheck,
2534
},
@@ -28,168 +37,137 @@ const containerInspectResult = (healthcheck?: { Test: string[] }): ContainerInsp
2837
Running: true,
2938
StartedAt: "2026-05-14T10:00:00.000Z",
3039
FinishedAt: "0001-01-01T00:00:00.000Z",
40+
...(healthcheckStatus === undefined ? {} : { Healthcheck: { Status: healthcheckStatus } }),
3141
},
3242
NetworkSettings: {
3343
Ports: {},
3444
Networks: {},
3545
},
3646
}) as unknown as ContainerInspectInfo;
3747

48+
const imageInspectResultWithHealthCheck = (): ImageInspectInfo =>
49+
({
50+
Config: {
51+
Healthcheck: {
52+
Test: ["CMD-SHELL", "test -f /tmp/ready"],
53+
},
54+
},
55+
}) as unknown as ImageInspectInfo;
56+
3857
const client = (imageInspectResult: ImageInspectInfo): ContainerRuntimeClient =>
3958
({
4059
image: {
4160
inspect: vi.fn().mockResolvedValue(imageInspectResult),
4261
},
4362
}) as unknown as ContainerRuntimeClient;
4463

45-
describe("GenericContainer default wait strategy", { timeout: 180_000 }, () => {
46-
it("should select listening ports when no healthcheck is configured", async () => {
64+
const clientWithImageInspectFailure = (): ContainerRuntimeClient =>
65+
({
66+
image: {
67+
inspect: vi.fn().mockRejectedValue(new Error("inspect failed")),
68+
},
69+
}) as unknown as ContainerRuntimeClient;
70+
71+
describe("GenericContainer default wait strategy", () => {
72+
it("should use an explicitly defined wait strategy", async () => {
73+
const runtimeClient = client(imageInspectResultWithHealthCheck());
74+
const waitStrategy = Wait.forLogMessage("ready");
75+
4776
await expect(
48-
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
49-
client({} as ImageInspectInfo),
50-
containerInspectResult()
51-
)
52-
).resolves.toBeInstanceOf(HostPortWaitStrategy);
77+
new TestGenericContainer("image:latest")
78+
.withHealthCheck({
79+
test: ["CMD-SHELL", "test -f /tmp/ready"],
80+
})
81+
.selectWaitStrategyForTest(
82+
runtimeClient,
83+
containerInspectResult({
84+
healthcheck: {
85+
Test: ["CMD-SHELL", "test -f /tmp/ready"],
86+
},
87+
}),
88+
waitStrategy
89+
)
90+
).resolves.toBe(waitStrategy);
91+
expect(runtimeClient.image.inspect).not.toHaveBeenCalled();
5392
});
5493

55-
it("should select image healthcheck when container inspect omits healthcheck config", async () => {
56-
const imageInspectResult = {
57-
Config: {
58-
Healthcheck: {
59-
Test: ["CMD-SHELL", "test -f /tmp/ready"],
60-
},
61-
},
62-
} as unknown as ImageInspectInfo;
94+
it("should select a healthcheck configured with withHealthCheck", async () => {
95+
const runtimeClient = client({} as ImageInspectInfo);
6396

6497
await expect(
65-
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
66-
client(imageInspectResult),
67-
containerInspectResult()
68-
)
98+
new TestGenericContainer("image:latest")
99+
.withHealthCheck({
100+
test: ["CMD-SHELL", "test -f /tmp/ready"],
101+
})
102+
.selectWaitStrategyForTest(runtimeClient, containerInspectResult())
69103
).resolves.toBeInstanceOf(HealthCheckWaitStrategy);
104+
expect(runtimeClient.image.inspect).not.toHaveBeenCalled();
70105
});
71106

72-
it("should select listening ports when the container disables image healthchecks", async () => {
73-
const imageInspectResult = {
74-
Config: {
75-
Healthcheck: {
76-
Test: ["CMD-SHELL", "test -f /tmp/ready"],
77-
},
78-
},
79-
} as unknown as ImageInspectInfo;
107+
it("should select a healthcheck configured on the container", async () => {
108+
const runtimeClient = client({} as ImageInspectInfo);
80109

81110
await expect(
82111
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
83-
client(imageInspectResult),
84-
containerInspectResult({ Test: ["NONE"] })
112+
runtimeClient,
113+
containerInspectResult({
114+
healthcheck: {
115+
Test: ["CMD-SHELL", "test -f /tmp/ready"],
116+
},
117+
})
85118
)
86-
).resolves.toBeInstanceOf(HostPortWaitStrategy);
119+
).resolves.toBeInstanceOf(HealthCheckWaitStrategy);
120+
expect(runtimeClient.image.inspect).not.toHaveBeenCalled();
87121
});
88122

89-
it("should wait for a healthcheck configured with withHealthCheck", async () => {
90-
await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
91-
.withExposedPorts(8080)
92-
.withCommand(["sh", "-c", "rm -f /tmp/ready; (sleep 4; touch /tmp/ready) & node index.js"])
93-
.withHealthCheck({
94-
test: ["CMD-SHELL", "test -f /tmp/ready"],
95-
interval: 1_000,
96-
timeout: 1_000,
97-
retries: 10,
98-
})
99-
.start();
100-
101-
expect(await getHealthCheckStatus(container)).toBe("healthy");
102-
await checkContainerIsHealthy(container);
103-
});
123+
it("should select a healthcheck when container inspect includes healthcheck status", async () => {
124+
const runtimeClient = client({} as ImageInspectInfo);
104125

105-
it("should prefer a healthcheck configured with withHealthCheck over an image healthcheck", async () => {
106-
const context = path.resolve(fixtures, "docker-with-delayed-health-check");
107-
const genericContainer = await GenericContainer.fromDockerfile(context).build();
108-
await using container = await genericContainer
109-
.withExposedPorts(8080)
110-
.withCommand(["sh", "-c", "rm -f /tmp/ready /tmp/custom-ready; touch /tmp/custom-ready; node index.js"])
111-
.withHealthCheck({
112-
test: ["CMD-SHELL", "test -f /tmp/custom-ready"],
113-
interval: 1_000,
114-
timeout: 1_000,
115-
retries: 10,
116-
})
117-
.start();
118-
119-
expect(await getHealthCheckStatus(container)).toBe("healthy");
120-
await checkContainerIsHealthy(container);
126+
await expect(
127+
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
128+
runtimeClient,
129+
containerInspectResult({ healthcheckStatus: "starting" })
130+
)
131+
).resolves.toBeInstanceOf(HealthCheckWaitStrategy);
132+
expect(runtimeClient.image.inspect).not.toHaveBeenCalled();
121133
});
122134

123-
it("should wait for a healthcheck defined in the image", async () => {
124-
const context = path.resolve(fixtures, "docker-with-delayed-health-check");
125-
const genericContainer = await GenericContainer.fromDockerfile(context).build();
126-
await using container = await genericContainer.withExposedPorts(8080).start();
127-
128-
expect(await getHealthCheckStatus(container)).toBe("healthy");
129-
await checkContainerIsHealthy(container);
135+
it("should select listening ports when no healthcheck is configured", async () => {
136+
await expect(
137+
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
138+
client({} as ImageInspectInfo),
139+
containerInspectResult()
140+
)
141+
).resolves.toBeInstanceOf(HostPortWaitStrategy);
130142
});
131143

132-
it("should use listening ports if the image disables healthcheck", async () => {
133-
const context = path.resolve(fixtures, "docker-with-disabled-health-check");
134-
const genericContainer = await GenericContainer.fromDockerfile(context).build();
135-
await using container = await genericContainer.withExposedPorts(8080).withStartupTimeout(1_000).start();
136-
137-
await checkContainerIsHealthy(container);
138-
expect(await getHealthCheckStatus(container)).toBeUndefined();
144+
it("should select image healthcheck when container inspect omits healthcheck config", async () => {
145+
await expect(
146+
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
147+
client(imageInspectResultWithHealthCheck()),
148+
containerInspectResult()
149+
)
150+
).resolves.toBeInstanceOf(HealthCheckWaitStrategy);
139151
});
140152

141-
it.sequential("should wait for an image healthcheck when reusing a stopped container", async () => {
142-
vi.stubEnv("TESTCONTAINERS_REUSE_ENABLE", "true");
143-
144-
const imageName = `localhost/${randomUuid()}:${randomUuid()}`;
145-
const containerName = `reusable-healthcheck-${randomUuid()}`;
146-
const context = path.resolve(fixtures, "docker-with-delayed-health-check");
147-
await GenericContainer.fromDockerfile(context).build(imageName);
148-
149-
const container1 = await new GenericContainer(imageName)
150-
.withName(containerName)
151-
.withExposedPorts(8080)
152-
.withReuse()
153-
.start();
154-
await container1.stop({ remove: false, timeout: 10_000 });
155-
156-
await using container2 = await new GenericContainer(imageName)
157-
.withName(containerName)
158-
.withExposedPorts(8080)
159-
.withReuse()
160-
.start();
161-
162-
expect(container2.getId()).toBe(container1.getId());
163-
expect(await getHealthCheckStatus(container2)).toBe("healthy");
164-
await container2.stop({ remove: true });
153+
it("should select listening ports when image inspect fails", async () => {
154+
await expect(
155+
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
156+
clientWithImageInspectFailure(),
157+
containerInspectResult()
158+
)
159+
).resolves.toBeInstanceOf(HostPortWaitStrategy);
165160
});
166161

167-
it("should use an explicitly defined wait strategy even if image defines healthcheck", async () => {
168-
const context = path.resolve(fixtures, "docker-with-delayed-health-check");
169-
const genericContainer = await GenericContainer.fromDockerfile(context).build();
170-
await using container = await genericContainer
171-
.withExposedPorts(8080)
172-
.withWaitStrategy(Wait.forListeningPorts())
173-
.withStartupTimeout(1_000)
174-
.start();
175-
176-
await checkContainerIsHealthy(container);
177-
});
162+
it("should select listening ports when the container disables image healthchecks", async () => {
163+
const runtimeClient = client(imageInspectResultWithHealthCheck());
178164

179-
it("should use an explicitly defined wait strategy even if withHealthCheck is called", async () => {
180-
await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
181-
.withExposedPorts(8080)
182-
.withCommand(["sh", "-c", "rm -f /tmp/ready; (sleep 4; touch /tmp/ready) & node index.js"])
183-
.withHealthCheck({
184-
test: ["CMD-SHELL", "test -f /tmp/ready"],
185-
interval: 1_000,
186-
timeout: 1_000,
187-
retries: 10,
188-
})
189-
.withWaitStrategy(Wait.forListeningPorts())
190-
.withStartupTimeout(1_000)
191-
.start();
192-
193-
await checkContainerIsHealthy(container);
165+
await expect(
166+
new TestGenericContainer("image:latest").selectWaitStrategyForTest(
167+
runtimeClient,
168+
containerInspectResult({ healthcheck: { Test: ["NONE"] } })
169+
)
170+
).resolves.toBeInstanceOf(HostPortWaitStrategy);
171+
expect(runtimeClient.image.inspect).not.toHaveBeenCalled();
194172
});
195173
});

0 commit comments

Comments
 (0)