testcontainers 12.0.3 → 12.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DockerContainerClient = void 0;
4
4
  const stream_1 = require("stream");
5
+ const promises_1 = require("stream/promises");
5
6
  const common_1 = require("../../../common");
6
7
  class DockerContainerClient {
7
8
  dockerode;
@@ -216,11 +217,18 @@ class DockerContainerClient {
216
217
  };
217
218
  processStream(stdoutStream, stdoutChunks);
218
219
  processStream(stderrStream, stderrChunks);
219
- await new Promise((res, rej) => {
220
- stream.on("end", res);
221
- stream.on("error", rej);
222
- });
223
- stream.destroy();
220
+ try {
221
+ await new Promise((res, rej) => {
222
+ stream.on("end", res);
223
+ stream.on("error", rej);
224
+ });
225
+ stdoutStream.end();
226
+ stderrStream.end();
227
+ await Promise.all([(0, promises_1.finished)(stdoutStream), (0, promises_1.finished)(stderrStream)]);
228
+ }
229
+ finally {
230
+ stream.destroy();
231
+ }
224
232
  const inspectResult = await exec.inspect();
225
233
  const exitCode = inspectResult.ExitCode ?? -1;
226
234
  const output = outputChunks.join("");
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "12.0.3";
1
+ export declare const LIB_VERSION = "12.0.4";
package/build/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
- exports.LIB_VERSION = "12.0.3";
4
+ exports.LIB_VERSION = "12.0.4";
@@ -23,10 +23,10 @@ export declare class HttpWaitStrategy extends AbstractWaitStrategy {
23
23
  [key: string]: string;
24
24
  }): this;
25
25
  withBasicCredentials(username: string, password: string): this;
26
- withReadTimeout(startupTimeoutMs: number): this;
26
+ withReadTimeout(readTimeoutMs: number): this;
27
27
  usingTls(): this;
28
28
  allowInsecure(): this;
29
29
  waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts): Promise<void>;
30
30
  private handleContainerExit;
31
- private getAgent;
31
+ private createInsecureAgent;
32
32
  }
@@ -47,8 +47,8 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
47
47
  this.headers = { ...this.headers, Authorization: `Basic ${base64Encoded}` };
48
48
  return this;
49
49
  }
50
- withReadTimeout(startupTimeoutMs) {
51
- this.readTimeoutMs = startupTimeoutMs;
50
+ withReadTimeout(readTimeoutMs) {
51
+ this.readTimeoutMs = readTimeoutMs;
52
52
  return this;
53
53
  }
54
54
  usingTls() {
@@ -65,50 +65,60 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
65
65
  let containerExited = false;
66
66
  const client = await (0, container_runtime_1.getContainerRuntimeClient)();
67
67
  const { abortOnContainerExit } = this.options;
68
- await new common_1.IntervalRetry(this.readTimeoutMs).retryUntil(async () => {
69
- try {
70
- const url = `${this.protocol}://${client.info.containerRuntime.host}:${boundPorts.getBinding(this.port)}${this.path}`;
71
- if (abortOnContainerExit) {
72
- const containerStatus = (await client.container.inspect(container)).State.Status;
73
- if (containerStatus === exitStatus) {
74
- containerExited = true;
75
- return;
68
+ // Scoped per invocation: one strategy instance can drive concurrent waits, so a shared
69
+ // agent would let one finished wait destroy a dispatcher another is still using.
70
+ const agent = this.createInsecureAgent();
71
+ try {
72
+ await new common_1.IntervalRetry(this.readTimeoutMs).retryUntil(async () => {
73
+ try {
74
+ const url = `${this.protocol}://${client.info.containerRuntime.host}:${boundPorts.getBinding(this.port)}${this.path}`;
75
+ if (abortOnContainerExit) {
76
+ const containerStatus = (await client.container.inspect(container)).State.Status;
77
+ if (containerStatus === exitStatus) {
78
+ containerExited = true;
79
+ return;
80
+ }
76
81
  }
82
+ return (0, undici_response_parser_1.undiciResponseToFetchResponse)(await (0, undici_1.request)(url, {
83
+ method: this.method,
84
+ signal: AbortSignal.timeout(this.readTimeoutMs),
85
+ headers: this.headers,
86
+ dispatcher: agent,
87
+ }));
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ }, async (response) => {
93
+ if (abortOnContainerExit && containerExited) {
94
+ return true;
95
+ }
96
+ if (response === undefined) {
97
+ return false;
77
98
  }
78
- return (0, undici_response_parser_1.undiciResponseToFetchResponse)(await (0, undici_1.request)(url, {
79
- method: this.method,
80
- signal: AbortSignal.timeout(this.readTimeoutMs),
81
- headers: this.headers,
82
- dispatcher: this.getAgent(),
83
- }));
84
- }
85
- catch {
86
- return undefined;
87
- }
88
- }, async (response) => {
89
- if (abortOnContainerExit && containerExited) {
90
- return true;
91
- }
92
- if (response === undefined) {
93
- return false;
94
- }
95
- else if (!this.predicates.length) {
96
- return response.ok;
97
- }
98
- else {
99
- for (const predicate of this.predicates) {
100
- const result = await predicate(response);
101
- if (!result) {
102
- return false;
99
+ else if (!this.predicates.length) {
100
+ return response.ok;
101
+ }
102
+ else {
103
+ for (const predicate of this.predicates) {
104
+ const result = await predicate(response);
105
+ if (!result) {
106
+ return false;
107
+ }
103
108
  }
109
+ return true;
104
110
  }
105
- return true;
106
- }
107
- }, () => {
108
- const message = `URL ${this.path} not accessible after ${this.startupTimeoutMs}ms`;
109
- common_1.log.error(message, { containerId: container.id });
110
- throw new Error(message);
111
- }, this.startupTimeoutMs);
111
+ }, () => {
112
+ const message = `URL ${this.path} not accessible after ${this.startupTimeoutMs}ms`;
113
+ common_1.log.error(message, { containerId: container.id });
114
+ throw new Error(message);
115
+ }, this.startupTimeoutMs);
116
+ }
117
+ finally {
118
+ // Force-close: status-only predicates never read the body, so a graceful close() could
119
+ // hang waiting on unreleased connections. Nothing left to drain once the wait is done.
120
+ await agent?.destroy();
121
+ }
112
122
  if (abortOnContainerExit && containerExited) {
113
123
  return this.handleContainerExit(container);
114
124
  }
@@ -132,14 +142,15 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
132
142
  common_1.log.error(message, { containerId: container.id });
133
143
  throw new Error(message);
134
144
  }
135
- getAgent() {
136
- if (this._allowInsecure) {
137
- return new undici_1.Agent({
138
- connect: {
139
- rejectUnauthorized: false,
140
- },
141
- });
145
+ createInsecureAgent() {
146
+ if (!this._allowInsecure) {
147
+ return undefined;
142
148
  }
149
+ return new undici_1.Agent({
150
+ connect: {
151
+ rejectUnauthorized: false,
152
+ },
153
+ });
143
154
  }
144
155
  }
145
156
  exports.HttpWaitStrategy = HttpWaitStrategy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testcontainers",
3
- "version": "12.0.3",
3
+ "version": "12.0.4",
4
4
  "author": "Cristian Greco",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -38,13 +38,13 @@
38
38
  "debug": "^4.4.3",
39
39
  "docker-compose": "^1.4.2",
40
40
  "dockerode": "^5.0.0",
41
- "get-port": "^7.2.0",
41
+ "get-port": "^5.1.1",
42
42
  "proper-lockfile": "^4.1.2",
43
43
  "properties-reader": "^3.0.1",
44
44
  "ssh-remote-port-forward": "^1.0.4",
45
45
  "tar-fs": "^3.1.2",
46
46
  "tmp": "^0.2.7",
47
- "undici": "^8.3.0"
47
+ "undici": "^8.5.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/archiver": "^7.0.0",