Skip to content

Commit 632d227

Browse files
Abort timers when log wait strategy completes
1 parent 431b2a7 commit 632d227

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,23 @@ describe("LogWaitStrategy", { timeout: 180_000 }, () => {
5555
expect(await getRunningContainerNames()).not.toContain(containerName);
5656
});
5757

58-
it("should throw an error if the message is never received", async () => {
58+
it("should throw an error if the log stream ends and the message is not received", async () => {
5959
const containerName = `container-${new RandomUuid().nextUuid()}`;
6060

6161
await expect(
6262
new GenericContainer("cristianrgreco/testcontainer:1.1.14")
6363
.withName(containerName)
64-
.withCommand("/bin/sh", "-c", 'echo "Ready"')
64+
.withCommand(["/bin/sh", "-c", 'echo "Ready"'])
6565
.withWaitStrategy(Wait.forLogMessage("unexpected"))
6666
.start()
6767
).rejects.toThrowError(`Log stream ended and message "unexpected" was not received`);
6868

6969
expect(await getRunningContainerNames()).not.toContain(containerName);
7070
});
7171

72+
// TODO
73+
it.skip("should throw an error if the log stream is open and the strategy times out", async () => {});
74+
7275
it("does not matter if container does not send all content in a single line", async () => {
7376
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
7477
.withCommand([

packages/testcontainers/src/wait-strategies/log-wait-strategy.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { AbstractWaitStrategy } from "./wait-strategy";
99
export type Log = string;
1010

1111
export class LogWaitStrategy extends AbstractWaitStrategy {
12+
private abortController!: AbortController;
13+
1214
constructor(
1315
private readonly message: Log | RegExp,
1416
private readonly times: number
@@ -17,12 +19,20 @@ export class LogWaitStrategy extends AbstractWaitStrategy {
1719
}
1820

1921
public async waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void> {
22+
this.abortController = new AbortController();
2023
await Promise.race([this.handleTimeout(container.id), this.handleLogs(container, startTime)]);
2124
}
2225

2326
async handleTimeout(containerId: string): Promise<void> {
24-
await setTimeout(this.startupTimeout);
27+
try {
28+
await setTimeout(this.startupTimeout, undefined, { signal: this.abortController.signal });
29+
} catch (err) {
30+
if (!(err instanceof Error && err.name === "AbortError")) {
31+
throw err;
32+
}
33+
}
2534
this.throwError(containerId, `Log message "${this.message}" not received after ${this.startupTimeout}ms`);
35+
this.abortController.abort();
2636
}
2737

2838
async handleLogs(container: Dockerode.Container, startTime?: Date): Promise<void> {
@@ -32,21 +42,26 @@ export class LogWaitStrategy extends AbstractWaitStrategy {
3242

3343
let matches = 0;
3444
for await (const line of byline(stream)) {
45+
if (this.abortController.signal.aborted) {
46+
break;
47+
}
3548
if (this.matches(line)) {
3649
if (++matches === this.times) {
37-
return log.debug(`Log wait strategy complete`, { containerId: container.id });
50+
log.debug(`Log wait strategy complete`, { containerId: container.id });
51+
this.abortController.abort();
52+
return;
3853
}
3954
}
4055
}
4156

4257
this.throwError(container.id, `Log stream ended and message "${this.message}" was not received`);
4358
}
4459

45-
matches(line: string): boolean {
60+
private matches(line: string): boolean {
4661
return this.message instanceof RegExp ? this.message.test(line) : line.includes(this.message);
4762
}
4863

49-
throwError(containerId: string, message: string): void {
64+
private throwError(containerId: string, message: string): void {
5065
log.error(message, { containerId });
5166
throw new Error(message);
5267
}

0 commit comments

Comments
 (0)