diff --git a/biome.json b/biome.json index 44bb21126..0dff2a5b9 100644 --- a/biome.json +++ b/biome.json @@ -24,6 +24,7 @@ "rules": { "preset": "recommended", "complexity": { + "noExcessiveCognitiveComplexity": "error", "noExcessiveLinesPerFunction": { "level": "error", "options": { @@ -38,8 +39,14 @@ "noFloatingPromises": "error", "useNullishCoalescing": "error" }, + "style": { + "noUselessElse": "error", + "useBlockStatements": "error", + "useReadonlyClassProperties": "error" + }, "suspicious": { - "noImportCycles": "error" + "noImportCycles": "error", + "noShadow": "error" } } }, diff --git a/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.ts b/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.ts index df405de7d..db706a50c 100644 --- a/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.ts +++ b/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.ts @@ -17,11 +17,11 @@ const DEFAULT_EXPLORER_ENABLED = false; const COSMOS_READY_LOG_MESSAGE = "Now listening on: "; export class AzureCosmosDbEmulatorContainer extends GenericContainer { - private key = DEFAULT_KEY; + private readonly key = DEFAULT_KEY; private protocol: Protocol = DEFAULT_PROTOCOL; private telemetryEnabled = DEFAULT_TELEMETRY_ENABLED; - private explorerEnabled = DEFAULT_EXPLORER_ENABLED; - private portGenerator: PortGenerator; + private readonly explorerEnabled = DEFAULT_EXPLORER_ENABLED; + private readonly portGenerator: PortGenerator; constructor(image: string) { super(image); diff --git a/packages/modules/azurite/src/azurite-container.ts b/packages/modules/azurite/src/azurite-container.ts index d9cc78c83..66d015d81 100755 --- a/packages/modules/azurite/src/azurite-container.ts +++ b/packages/modules/azurite/src/azurite-container.ts @@ -278,28 +278,25 @@ export class StartedAzuriteContainer extends AbstractStartedContainer { public getBlobPort(): number { if (hasHostBinding(this.blobPort)) { return this.blobPort.host; - } else { - const containerPort = getContainerPort(this.blobPort); - return this.getMappedPort(containerPort); } + const containerPort = getContainerPort(this.blobPort); + return this.getMappedPort(containerPort); } public getQueuePort(): number { if (hasHostBinding(this.queuePort)) { return this.queuePort.host; - } else { - const containerPort = getContainerPort(this.queuePort); - return this.getMappedPort(containerPort); } + const containerPort = getContainerPort(this.queuePort); + return this.getMappedPort(containerPort); } public getTablePort(): number { if (hasHostBinding(this.tablePort)) { return this.tablePort.host; - } else { - const containerPort = getContainerPort(this.tablePort); - return this.getMappedPort(containerPort); } + const containerPort = getContainerPort(this.tablePort); + return this.getMappedPort(containerPort); } public getBlobEndpoint(): string { diff --git a/packages/modules/couchbase/src/couchbase-container.ts b/packages/modules/couchbase/src/couchbase-container.ts index fff37c978..c7db831c7 100644 --- a/packages/modules/couchbase/src/couchbase-container.ts +++ b/packages/modules/couchbase/src/couchbase-container.ts @@ -311,6 +311,20 @@ export class CouchbaseContainer extends GenericContainer { body.set("mgmtSSL", container.getMappedPort(PORTS.MGMT_SSL_PORT).toString()); } + this.setServicePorts(body, container); + + const response = await this.doHttpRequest( + container.getHost(), + container.getMappedPort(PORTS.MGMT_PORT), + "/node/controller/setupAlternateAddresses/external", + "PUT", + body, + true + ); + await this.checkResponse(response, "Could not configure external ports"); + } + + private setServicePorts(body: URLSearchParams, container: StartedTestContainer) { if (this.enabledServices.has(CouchbaseService.KV)) { body.set("kv", container.getMappedPort(PORTS.KV_PORT).toString()); body.set("capi", container.getMappedPort(PORTS.VIEW_PORT).toString()); @@ -348,16 +362,6 @@ export class CouchbaseContainer extends GenericContainer { body.set("eventingSSL", container.getMappedPort(PORTS.EVENTING_SSL_PORT).toString()); } } - - const response = await this.doHttpRequest( - container.getHost(), - container.getMappedPort(PORTS.MGMT_PORT), - "/node/controller/setupAlternateAddresses/external", - "PUT", - body, - true - ); - await this.checkResponse(response, "Could not configure external ports"); } private async configureIndexer(container: StartedTestContainer) { diff --git a/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts b/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts index e9c5934b5..07a14e3f0 100644 --- a/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts +++ b/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts @@ -15,7 +15,9 @@ async function getRequestBodyFromReadableStream(stream: ReadableStream { - if (request.url.includes("/_internal/config")) configUpdated = true; + if (request.url.includes("/_internal/config")) { + configUpdated = true; + } }); await using container = await new CloudStorageEmulatorContainer(IMAGE).start(); @@ -126,7 +130,9 @@ describe("CloudStorageEmulatorContainer", { concurrent: false, timeout: 240_000 let configUpdated = false; server.events.on("request:start", ({ request }) => { - if (request.url.includes("/_internal/config")) configUpdated = true; + if (request.url.includes("/_internal/config")) { + configUpdated = true; + } }); await using container = await new CloudStorageEmulatorContainer(IMAGE).withAutoUpdateExternalUrl(false).start(); diff --git a/packages/modules/gcloud/src/emulator-flags-manager.ts b/packages/modules/gcloud/src/emulator-flags-manager.ts index cc7a43d2b..e0cbe6f47 100644 --- a/packages/modules/gcloud/src/emulator-flags-manager.ts +++ b/packages/modules/gcloud/src/emulator-flags-manager.ts @@ -9,9 +9,14 @@ export class EmulatorFlagsManager { * @returns this instance for chaining. */ public withFlag(name: string, value: string): this { - if (!name) throw new Error("Flag name must be set."); - if (name.startsWith("--")) this.flags[name] = value; - else this.flags[`--${name}`] = value; + if (!name) { + throw new Error("Flag name must be set."); + } + if (name.startsWith("--")) { + this.flags[name] = value; + } else { + this.flags[`--${name}`] = value; + } return this; } diff --git a/packages/modules/gcloud/src/spanner-emulator-helper.ts b/packages/modules/gcloud/src/spanner-emulator-helper.ts index 7d9161df9..d4de789a7 100644 --- a/packages/modules/gcloud/src/spanner-emulator-helper.ts +++ b/packages/modules/gcloud/src/spanner-emulator-helper.ts @@ -18,8 +18,8 @@ export class SpannerEmulatorHelper { * Lazily get or create the Spanner client. */ public async client(): Promise { - const { Spanner } = await import("@google-cloud/spanner"); - this.clientInstance ??= new Spanner({ + const { Spanner: SpannerClient } = await import("@google-cloud/spanner"); + this.clientInstance ??= new SpannerClient({ projectId: this.emulator.getProjectId(), apiEndpoint: this.emulator.getHost(), port: this.emulator.getGrpcPort(), diff --git a/packages/modules/hivemq/src/hivemq-container.test.ts b/packages/modules/hivemq/src/hivemq-container.test.ts index 3d6530f71..132527f3c 100644 --- a/packages/modules/hivemq/src/hivemq-container.test.ts +++ b/packages/modules/hivemq/src/hivemq-container.test.ts @@ -13,7 +13,7 @@ describe("HiveMQContainer", { timeout: 240_000 }, () => { const mqttClient = await mqtt.connectAsync(container.getConnectionString()); const firstMessagePromise = new Promise<{ topic: string; message: Buffer }>((resolve, reject) => { - mqttClient.once("message", (topic, message) => resolve({ topic, message })); + mqttClient.once("message", (topic, payload) => resolve({ topic, message: payload })); mqttClient.once("error", (err) => reject(err)); }); diff --git a/packages/modules/kurrentdb/src/kurrentdb-container.test.ts b/packages/modules/kurrentdb/src/kurrentdb-container.test.ts index 695d12b7f..97b9a5af3 100644 --- a/packages/modules/kurrentdb/src/kurrentdb-container.test.ts +++ b/packages/modules/kurrentdb/src/kurrentdb-container.test.ts @@ -89,8 +89,8 @@ describe("KurrentDbContainer", { concurrent: false, timeout: 240_000 }, () => { await stream.unsubscribe(); await client.dispose(); - async function getStreamFirstEvent(stream: StreamSubscription): Promise { - for await (const event of stream) { + async function getStreamFirstEvent(subscription: StreamSubscription): Promise { + for await (const event of subscription) { return event; } } diff --git a/packages/modules/mongodb/src/mongodb-atlas-local-container.ts b/packages/modules/mongodb/src/mongodb-atlas-local-container.ts index 36031ce85..981c5159f 100644 --- a/packages/modules/mongodb/src/mongodb-atlas-local-container.ts +++ b/packages/modules/mongodb/src/mongodb-atlas-local-container.ts @@ -15,13 +15,17 @@ export class MongoDBAtlasLocalContainer extends GenericContainer { } public withUsername(username: string): this { - if (!username) throw new Error("Username should not be empty."); + if (!username) { + throw new Error("Username should not be empty."); + } this.username = username; return this; } public withPassword(password: string): this { - if (!password) throw new Error("Password should not be empty."); + if (!password) { + throw new Error("Password should not be empty."); + } this.password = password; return this; } diff --git a/packages/modules/mongodb/src/mongodb-container.ts b/packages/modules/mongodb/src/mongodb-container.ts index 7b66ff344..5b4f3e60a 100644 --- a/packages/modules/mongodb/src/mongodb-container.ts +++ b/packages/modules/mongodb/src/mongodb-container.ts @@ -13,20 +13,26 @@ export class MongoDBContainer extends GenericContainer { } public withUsername(username: string): this { - if (!username) throw new Error("Username should not be empty."); + if (!username) { + throw new Error("Username should not be empty."); + } this.username = username; return this; } public withPassword(password: string): this { - if (!password) throw new Error("Password should not be empty."); + if (!password) { + throw new Error("Password should not be empty."); + } this.password = password; return this; } public override async start(): Promise { const cmdArgs = ["--replSet", "rs0"]; - if (!this.healthCheck) this.withWaitForRsHealthCheck(); + if (!this.healthCheck) { + this.withWaitForRsHealthCheck(); + } if (this.username && this.password) { cmdArgs.push("--keyFile", "/data/db/key.txt"); this.withEnvironment({ @@ -58,18 +64,23 @@ export class MongoDBContainer extends GenericContainer { private buildMongoEvalCommand(command: string) { const args = []; - if (this.isV5OrLater()) args.push("mongosh"); - else args.push("mongo", "admin"); - if (this.username && this.password) args.push("-u", this.username, "-p", this.password); + if (this.isV5OrLater()) { + args.push("mongosh"); + } else { + args.push("mongo", "admin"); + } + if (this.username && this.password) { + args.push("-u", this.username, "-p", this.password); + } args.push("--quiet", "--eval", command); return args.join(" "); } private getRsInitCmd() { - if (this.isV5OrLater()) + if (this.isV5OrLater()) { return `'try { rs.status(); } catch (e) { rs.initiate(); } while (db.runCommand({isMaster: 1}).ismaster==false) { sleep(100); }'`; - else - return `'try { rs.initiate(); } catch (e) {} while (db.runCommand({isMaster: 1}).ismaster==false) { sleep(100); }'`; + } + return `'try { rs.initiate(); } catch (e) {} while (db.runCommand({isMaster: 1}).ismaster==false) { sleep(100); }'`; } private isV5OrLater() { @@ -92,8 +103,9 @@ export class StartedMongoDBContainer extends AbstractStartedContainer { } public getConnectionString(): string { - if (this.username && this.password) + if (this.username && this.password) { return `mongodb://${this.username}:${this.password}@${this.getHost()}:${this.getMappedPort(MONGODB_PORT)}?authSource=admin`; + } return `mongodb://${this.getHost()}:${this.getMappedPort(MONGODB_PORT)}`; } } diff --git a/packages/modules/mosquitto/src/mosquitto-container.test.ts b/packages/modules/mosquitto/src/mosquitto-container.test.ts index defd8bb58..b241fd402 100644 --- a/packages/modules/mosquitto/src/mosquitto-container.test.ts +++ b/packages/modules/mosquitto/src/mosquitto-container.test.ts @@ -15,7 +15,7 @@ describe("MosquittoContainer", { timeout: 240_000 }, () => { const mqttClient = await mqtt.connectAsync(container.getConnectionString()); const firstMessagePromise = new Promise<{ topic: string; message: Buffer }>((resolve, reject) => { - mqttClient.once("message", (topic, message) => resolve({ topic, message })); + mqttClient.once("message", (topic, payload) => resolve({ topic, message: payload })); mqttClient.once("error", (err) => reject(err)); }); @@ -43,7 +43,7 @@ describe("MosquittoContainer", { timeout: 240_000 }, () => { const mqttClient = await mqtt.connectAsync(container.getConnectionString()); const firstMessagePromise = new Promise<{ topic: string; message: Buffer }>((resolve, reject) => { - mqttClient.once("message", (topic, message) => resolve({ topic, message })); + mqttClient.once("message", (topic, payload) => resolve({ topic, message: payload })); mqttClient.once("error", (err) => reject(err)); }); diff --git a/packages/modules/mosquitto/src/mosquitto-container.ts b/packages/modules/mosquitto/src/mosquitto-container.ts index 371f5ec6c..09f5944e3 100644 --- a/packages/modules/mosquitto/src/mosquitto-container.ts +++ b/packages/modules/mosquitto/src/mosquitto-container.ts @@ -14,13 +14,17 @@ export class MosquittoContainer extends GenericContainer { } public withUsername(username: string): this { - if (!username) throw new Error("Username should not be empty."); + if (!username) { + throw new Error("Username should not be empty."); + } this.username = username; return this; } public withPassword(password: string): this { - if (!password) throw new Error("Password should not be empty."); + if (!password) { + throw new Error("Password should not be empty."); + } this.password = password; return this; } diff --git a/packages/modules/mssqlserver/src/mssqlserver-container.ts b/packages/modules/mssqlserver/src/mssqlserver-container.ts index f5b68f356..1742eb78f 100644 --- a/packages/modules/mssqlserver/src/mssqlserver-container.ts +++ b/packages/modules/mssqlserver/src/mssqlserver-container.ts @@ -4,7 +4,7 @@ const MSSQL_PORT = 1433; export class MSSQLServerContainer extends GenericContainer { private database = "master"; - private username = "sa"; + private readonly username = "sa"; private password = "Passw0rd"; private acceptEula = "N"; private message: string | RegExp = /.*Recovery is complete.*/; diff --git a/packages/modules/nats/src/nats-container.ts b/packages/modules/nats/src/nats-container.ts index e86c8fcfe..6c141775e 100755 --- a/packages/modules/nats/src/nats-container.ts +++ b/packages/modules/nats/src/nats-container.ts @@ -8,8 +8,8 @@ const USER_ARGUMENT_KEY = "--user"; const PASS_ARGUMENT_KEY = "--pass"; export class NatsContainer extends GenericContainer { - private args = new Set(); - private values = new Map(); + private readonly args = new Set(); + private readonly values = new Map(); constructor(image: string) { super(image); @@ -62,9 +62,8 @@ export class NatsContainer extends GenericContainer { if (name.length === 1) { return `-${name}`; - } else { - return `--${name}`; } + return `--${name}`; } public override async start(): Promise { diff --git a/packages/modules/postgresql/src/postgresql-container.ts b/packages/modules/postgresql/src/postgresql-container.ts index 4e3f7f622..469476df5 100755 --- a/packages/modules/postgresql/src/postgresql-container.ts +++ b/packages/modules/postgresql/src/postgresql-container.ts @@ -60,9 +60,15 @@ export class PostgreSqlContainer extends GenericContainer { } public withSSL(certFile: string, keyFile: string, caCertFile?: string): this { - if (!certFile) throw new Error("SSL certificate file should not be empty."); - if (!keyFile) throw new Error("SSL key file should not be empty."); - if (caCertFile !== undefined && !caCertFile) throw new Error("SSL CA certificate file should not be empty."); + if (!certFile) { + throw new Error("SSL certificate file should not be empty."); + } + if (!keyFile) { + throw new Error("SSL key file should not be empty."); + } + if (caCertFile !== undefined && !caCertFile) { + throw new Error("SSL CA certificate file should not be empty."); + } const filesToCopy = [ { source: certFile, target: POSTGRES_SSL_CERT_PATH, mode: 0o600 }, @@ -91,7 +97,9 @@ export class PostgreSqlContainer extends GenericContainer { } public withSSLCert(caCertFile: string, certFile: string, keyFile: string): this { - if (!caCertFile) throw new Error("SSL CA certificate file should not be empty."); + if (!caCertFile) { + throw new Error("SSL CA certificate file should not be empty."); + } return this.withSSL(certFile, keyFile, caCertFile); } diff --git a/packages/modules/redis/src/redis-container.ts b/packages/modules/redis/src/redis-container.ts index 874cb7ce5..2f1a3fe78 100644 --- a/packages/modules/redis/src/redis-container.ts +++ b/packages/modules/redis/src/redis-container.ts @@ -83,8 +83,9 @@ export class RedisContainer extends GenericContainer { private async importInitialData(container: StartedTestContainer) { const re = await container.exec(`/tmp/import.sh ${this.password}`); - if (re.exitCode !== 0 || re.output.includes("ERR")) + if (re.exitCode !== 0 || re.output.includes("ERR")) { throw Error(`Could not import initial data from ${this.initialImportScriptFile}: ${re.output}`); + } } } diff --git a/packages/modules/toxiproxy/src/toxiproxy-container.ts b/packages/modules/toxiproxy/src/toxiproxy-container.ts index 78564cf3d..c7e094354 100644 --- a/packages/modules/toxiproxy/src/toxiproxy-container.ts +++ b/packages/modules/toxiproxy/src/toxiproxy-container.ts @@ -51,8 +51,8 @@ export class StartedToxiProxyContainer extends AbstractStartedContainer { const proxies = await this.client.getAll(); const usedPorts = PORT_ARRAY.reduce( - (acc, port) => { - acc[port] = false; + (acc, candidatePort) => { + acc[candidatePort] = false; return acc; }, {} as Record diff --git a/packages/testcontainers/src/common/retry.test.ts b/packages/testcontainers/src/common/retry.test.ts index 6674e7228..3755aa974 100644 --- a/packages/testcontainers/src/common/retry.test.ts +++ b/packages/testcontainers/src/common/retry.test.ts @@ -7,7 +7,7 @@ describe("Retry", () => { const result = await new IntervalRetry(1).retryUntil( () => fnMock(), - (result) => result, + (value) => value, () => new Error(), 10 ); @@ -21,7 +21,7 @@ describe("Retry", () => { const result = await new IntervalRetry(1).retryUntil( () => fnMock(), - (result) => result, + (value) => value, () => new Error(), 10 ); @@ -36,7 +36,7 @@ describe("Retry", () => { const result = await new IntervalRetry(1).retryUntil( () => fnMock(), - (result) => result, + (value) => value, () => timeoutMock(), 10 ); diff --git a/packages/testcontainers/src/container-runtime/auth/credential-provider.test.ts b/packages/testcontainers/src/container-runtime/auth/credential-provider.test.ts index 41939126d..a7d74c841 100644 --- a/packages/testcontainers/src/container-runtime/auth/credential-provider.test.ts +++ b/packages/testcontainers/src/container-runtime/auth/credential-provider.test.ts @@ -74,9 +74,9 @@ describe("CredentialProvider", { concurrent: false }, () => { }); it("should return undefined when provider name not provided", async () => { - const credentialProvider = new TestCredentialProvider("name", undefined); + const unnamedCredentialProvider = new TestCredentialProvider("name", undefined); - expect(await credentialProvider.getAuthConfig("registry", containerRuntimeConfig)).toBeUndefined(); + expect(await unnamedCredentialProvider.getAuthConfig("registry", containerRuntimeConfig)).toBeUndefined(); }); it("should return undefined when get credentials fails because we lookup optimistically", async () => { diff --git a/packages/testcontainers/src/container-runtime/auth/get-auth-config.ts b/packages/testcontainers/src/container-runtime/auth/get-auth-config.ts index 57ea3bd0a..cdf56c976 100644 --- a/packages/testcontainers/src/container-runtime/auth/get-auth-config.ts +++ b/packages/testcontainers/src/container-runtime/auth/get-auth-config.ts @@ -16,15 +16,15 @@ const dockerConfigFile = path.resolve(dockerConfigLocation, "config.json"); const readDockerConfig = async (): Promise => { if (process.env.DOCKER_AUTH_CONFIG) { return parseDockerConfig(process.env.DOCKER_AUTH_CONFIG); - } else if (existsSync(dockerConfigFile)) { + } + if (existsSync(dockerConfigFile)) { return parseDockerConfig((await readFile(dockerConfigFile)).toString()); - } else { - return Promise.resolve({}); } + return Promise.resolve({}); }; -function parseDockerConfig(dockerConfig: string) { - const object = JSON.parse(dockerConfig); +function parseDockerConfig(json: string) { + const object = JSON.parse(json); return { credsStore: object.credsStore, diff --git a/packages/testcontainers/src/container-runtime/clients/compose/parse-compose-container-name.ts b/packages/testcontainers/src/container-runtime/clients/compose/parse-compose-container-name.ts index fc195acc0..e0cd487f0 100644 --- a/packages/testcontainers/src/container-runtime/clients/compose/parse-compose-container-name.ts +++ b/packages/testcontainers/src/container-runtime/clients/compose/parse-compose-container-name.ts @@ -1,11 +1,11 @@ export function parseComposeContainerName(projectName: string, containerName: string): string { if (containerName.startsWith(`/${projectName}-`)) { return containerName.substring(`/${projectName}-`.length); - } else if (containerName.startsWith("/")) { + } + if (containerName.startsWith("/")) { return containerName.substring(1); - } else { - throw new Error( - `Unable to resolve container name for container name: "${containerName}", project name: "${projectName}"` - ); } + throw new Error( + `Unable to resolve container name for container name: "${containerName}", project name: "${projectName}"` + ); } diff --git a/packages/testcontainers/src/container-runtime/clients/container/docker-container-client.ts b/packages/testcontainers/src/container-runtime/clients/container/docker-container-client.ts index 8ef6c7b7d..bcf21d381 100644 --- a/packages/testcontainers/src/container-runtime/clients/container/docker-container-client.ts +++ b/packages/testcontainers/src/container-runtime/clients/container/docker-container-client.ts @@ -53,10 +53,9 @@ export class DockerContainerClient implements ContainerClient { if (containers.length === 0) { log.debug(`No container found with label "${labelName}=${labelValue}"`); return undefined; - } else { - log.debug(`Fetched container by label "${labelName}=${labelValue}"`); - return this.getById(containers[0].Id); } + log.debug(`Fetched container by label "${labelName}=${labelValue}"`); + return this.getById(containers[0].Id); } catch (err) { log.error(`Failed to fetch container by label "${labelName}=${labelValue}": ${err}`); throw err; @@ -227,8 +226,8 @@ export class DockerContainerClient implements ContainerClient { this.dockerode.modem.demuxStream(stream, stdoutStream, stderrStream); - const processStream = (stream: Readable, chunks: string[]) => { - stream.on("data", (chunk) => { + const processStream = (source: Readable, chunks: string[]) => { + source.on("data", (chunk) => { chunks.push(chunk.toString()); outputChunks.push(chunk.toString()); diff --git a/packages/testcontainers/src/container-runtime/image-name.ts b/packages/testcontainers/src/container-runtime/image-name.ts index a49c7b1a2..53dc8bffc 100644 --- a/packages/testcontainers/src/container-runtime/image-name.ts +++ b/packages/testcontainers/src/container-runtime/image-name.ts @@ -36,26 +36,30 @@ export class ImageName { } log.info(message); } + this.string = this.buildString(); + } + + private buildString(): string { const tagAndDigest = this.digest ? `${this.tag}@${this.digest}` : this.tag; if (this.registry) { if (this.tag.startsWith("sha256:")) { - this.string = `${this.registry}/${this.image}@${this.tag}`; - } else { - this.string = `${this.registry}/${this.image}:${tagAndDigest}`; + return `${this.registry}/${this.image}@${this.tag}`; } - } else if (this.tag === "latest" && ImageName.hexRE.test(this.image)) { + return `${this.registry}/${this.image}:${tagAndDigest}`; + } + if (this.tag === "latest" && ImageName.hexRE.test(this.image)) { // 64 byte hex string. This refers to an image sha256 directly. // Do not put the tag as the docker does not accept it. // It will fail with: // // invalid repository name (), cannot specify 64-byte hexadecimal strings. // - this.string = this.image; - } else if (this.tag.startsWith("sha256:")) { - this.string = `${this.image}@${this.tag}`; - } else { - this.string = `${this.image}:${tagAndDigest}`; + return this.image; } + if (this.tag.startsWith("sha256:")) { + return `${this.image}@${this.tag}`; + } + return `${this.image}:${tagAndDigest}`; } public equals(other: ImageName): boolean { @@ -76,11 +80,11 @@ export class ImageName { if (imageAndTag.includes(":")) { const [image, tag] = imageAndTag.split(":"); return new ImageName(registry, image, tag, digest); - } else if (digest) { + } + if (digest) { return new ImageName(registry, imageAndTag, digest); - } else { - return new ImageName(registry, imageAndTag, "latest"); } + return new ImageName(registry, imageAndTag, "latest"); } private static getRegistry(string: string): string | undefined { diff --git a/packages/testcontainers/src/container-runtime/strategies/configuration-strategy.test.ts b/packages/testcontainers/src/container-runtime/strategies/configuration-strategy.test.ts index a844eb209..e044d8a9e 100644 --- a/packages/testcontainers/src/container-runtime/strategies/configuration-strategy.test.ts +++ b/packages/testcontainers/src/container-runtime/strategies/configuration-strategy.test.ts @@ -69,9 +69,15 @@ describe("ConfigurationStrategy", { concurrent: false }, () => { dockerCertPath: "/certs", }); mockReadFile.mockImplementation(async (path) => { - if (path === "/certs/ca.pem") return "ca"; - if (path === "/certs/cert.pem") return "cert"; - if (path === "/certs/key.pem") return "key"; + if (path === "/certs/ca.pem") { + return "ca"; + } + if (path === "/certs/cert.pem") { + return "cert"; + } + if (path === "/certs/key.pem") { + return "key"; + } throw new Error(`Unexpected path: ${path}`); }); diff --git a/packages/testcontainers/src/container-runtime/strategies/rootless-unix-socket-strategy.ts b/packages/testcontainers/src/container-runtime/strategies/rootless-unix-socket-strategy.ts index 677e61ef1..143c751d1 100644 --- a/packages/testcontainers/src/container-runtime/strategies/rootless-unix-socket-strategy.ts +++ b/packages/testcontainers/src/container-runtime/strategies/rootless-unix-socket-strategy.ts @@ -46,9 +46,8 @@ export class RootlessUnixSocketStrategy implements ContainerRuntimeClientStrateg if (xdgRuntimeDir !== undefined) { return path.join(xdgRuntimeDir, "docker.sock"); - } else { - return undefined; } + return undefined; } private getSocketPathFromHomeRunDir(): string { diff --git a/packages/testcontainers/src/container-runtime/utils/lookup-host-ips.ts b/packages/testcontainers/src/container-runtime/utils/lookup-host-ips.ts index 85283636d..89be6e868 100644 --- a/packages/testcontainers/src/container-runtime/utils/lookup-host-ips.ts +++ b/packages/testcontainers/src/container-runtime/utils/lookup-host-ips.ts @@ -5,7 +5,6 @@ import type { HostIp } from "../clients/types"; export const lookupHostIps = async (host: string): Promise => { if (net.isIP(host) === 0) { return await dnsLookup(host, { all: true }); - } else { - return [{ address: host, family: net.isIP(host) }]; } + return [{ address: host, family: net.isIP(host) }]; }; diff --git a/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.test.ts b/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.test.ts index c7980286f..79c117ecd 100644 --- a/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.test.ts +++ b/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.test.ts @@ -56,7 +56,7 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { { await using dockerEventStream = await getDockerEventStream(); const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); - await using _ = await env.withPullPolicy(PullPolicy.alwaysPull()).up(); + await using _pulled = await env.withPullPolicy(PullPolicy.alwaysPull()).up(); await dockerPullEventPromise; } }); @@ -69,7 +69,7 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { { await using dockerEventStream = await getDockerEventStream(); const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); - await using _ = await env.withPullPolicy(PullPolicy.alwaysPull()).up(["service-b"]); + await using _pulled = await env.withPullPolicy(PullPolicy.alwaysPull()).up(["service-b"]); await dockerPullEventPromise; } }); diff --git a/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts b/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts index c1356bda4..8a6c29b74 100644 --- a/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts +++ b/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts @@ -30,7 +30,7 @@ export class DockerComposeEnvironment { private environment: Environment = {}; private pullPolicy: ImagePullPolicy = PullPolicy.defaultPolicy(); private defaultWaitStrategy: WaitStrategy | undefined; - private waitStrategy: { [containerName: string]: WaitStrategy } = {}; + private readonly waitStrategy: { [containerName: string]: WaitStrategy } = {}; private startupTimeoutMs?: number; private clientOptions: Partial = {}; diff --git a/packages/testcontainers/src/generic-container/generic-container-logs.test.ts b/packages/testcontainers/src/generic-container/generic-container-logs.test.ts index 65b7027d4..05159631a 100644 --- a/packages/testcontainers/src/generic-container/generic-container-logs.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-logs.test.ts @@ -7,7 +7,7 @@ describe("GenericContainer logs", { timeout: 180_000 }, () => { const line = await new Promise((resolve) => { return new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) - .withLogConsumer((stream) => stream.on("data", (line) => resolve(line))) + .withLogConsumer((stream) => stream.on("data", (chunk) => resolve(chunk))) .start() .then((startedContainer) => startedContainer.stop()); }); diff --git a/packages/testcontainers/src/generic-container/generic-container.test.ts b/packages/testcontainers/src/generic-container/generic-container.test.ts index eccf94c0b..805de6c97 100644 --- a/packages/testcontainers/src/generic-container/generic-container.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container.test.ts @@ -339,7 +339,7 @@ describe("GenericContainer", { timeout: 180_000 }, () => { { await using dockerEventStream = await getDockerEventStream(); const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); - await using _ = await container.withPullPolicy(PullPolicy.alwaysPull()).start(); + await using _pulled = await container.withPullPolicy(PullPolicy.alwaysPull()).start(); await dockerPullEventPromise; } }); diff --git a/packages/testcontainers/src/generic-container/inspect-container-util-ports-exposed.ts b/packages/testcontainers/src/generic-container/inspect-container-util-ports-exposed.ts index 964db47ec..c7d1ba1db 100644 --- a/packages/testcontainers/src/generic-container/inspect-container-util-ports-exposed.ts +++ b/packages/testcontainers/src/generic-container/inspect-container-util-ports-exposed.ts @@ -10,7 +10,9 @@ export async function inspectContainerUntilPortsExposed( () => inspectFn(), (inspectResult) => { const portBindings = inspectResult?.HostConfig?.PortBindings; - if (!portBindings) return true; + if (!portBindings) { + return true; + } const expectedlyBoundPorts = Object.keys(portBindings); return expectedlyBoundPorts.every((exposedPort) => inspectResult.NetworkSettings.Ports[exposedPort]?.length > 0); }, diff --git a/packages/testcontainers/src/port-forwarder/port-forwarder.ts b/packages/testcontainers/src/port-forwarder/port-forwarder.ts index ba64dc8a8..1a2a070aa 100644 --- a/packages/testcontainers/src/port-forwarder/port-forwarder.ts +++ b/packages/testcontainers/src/port-forwarder/port-forwarder.ts @@ -97,7 +97,7 @@ export class PortForwarderInstance { log.debug(`Reusing existing PortForwarder for session "${sessionId}"...`); const host = client.info.containerRuntime.host; - const port = container.Ports.find((port) => port.PrivatePort === 22)?.PublicPort; + const port = container.Ports.find((mapping) => mapping.PrivatePort === 22)?.PublicPort; if (!port) { throw new Error("Expected PortForwarder to map exposed port 22"); } diff --git a/packages/testcontainers/src/reaper/reaper.ts b/packages/testcontainers/src/reaper/reaper.ts index 99f3ad395..364981bea 100644 --- a/packages/testcontainers/src/reaper/reaper.ts +++ b/packages/testcontainers/src/reaper/reaper.ts @@ -32,7 +32,7 @@ export interface Reaper { } let reaper: Reaper; -let sessionId: string; +let reaperSessionId: string; export async function getReaper(client: ContainerRuntimeClient): Promise { if (reaper) { @@ -44,14 +44,14 @@ export async function getReaper(client: ContainerRuntimeClient): Promise const reaperContainers = await findReaperContainers(client); if (process.env.TESTCONTAINERS_RYUK_DISABLED === "true") { - sessionId = new RandomUuid().nextUuid(); - return new DisabledReaper(sessionId, ""); + reaperSessionId = new RandomUuid().nextUuid(); + return new DisabledReaper(reaperSessionId, ""); } for (const reaperContainer of reaperContainers) { try { - sessionId = reaperContainer.Labels[LABEL_TESTCONTAINERS_SESSION_ID]; - return await useExistingReaper(reaperContainer, sessionId, client.info.containerRuntime.host); + reaperSessionId = reaperContainer.Labels[LABEL_TESTCONTAINERS_SESSION_ID]; + return await useExistingReaper(reaperContainer, reaperSessionId, client.info.containerRuntime.host); } catch (error) { const message = error instanceof Error ? error.message : String(error); log.warn(`Failed to reuse existing Reaper: ${message}. Trying another Reaper...`, { @@ -60,11 +60,11 @@ export async function getReaper(client: ContainerRuntimeClient): Promise } } - sessionId = new RandomUuid().nextUuid(); - return await createNewReaper(sessionId, client.info.containerRuntime.remoteSocketPath); + reaperSessionId = new RandomUuid().nextUuid(); + return await createNewReaper(reaperSessionId, client.info.containerRuntime.remoteSocketPath); }); - reaper.addSession(sessionId); + reaper.addSession(reaperSessionId); return reaper; } @@ -167,9 +167,8 @@ async function connectToReaperSocket(host: string, port: number, containerId: st if (retryResult instanceof Socket) { return retryResult; - } else { - throw retryResult; } + throw retryResult; } class RyukReaper implements Reaper { diff --git a/packages/testcontainers/src/socat/socat-container.ts b/packages/testcontainers/src/socat/socat-container.ts index fa9f7844b..ba76e7559 100644 --- a/packages/testcontainers/src/socat/socat-container.ts +++ b/packages/testcontainers/src/socat/socat-container.ts @@ -3,7 +3,7 @@ import { AbstractStartedContainer } from "../generic-container/abstract-started- import { GenericContainer } from "../generic-container/generic-container"; export class SocatContainer extends GenericContainer { - private targets: { [exposePort: number]: string } = {}; + private readonly targets: { [exposePort: number]: string } = {}; constructor(image = "alpine/socat:1.7.4.3-r0") { super(image); diff --git a/packages/testcontainers/src/utils/bound-ports.ts b/packages/testcontainers/src/utils/bound-ports.ts index 14cba5ac2..0127d2a13 100644 --- a/packages/testcontainers/src/utils/bound-ports.ts +++ b/packages/testcontainers/src/utils/bound-ports.ts @@ -30,9 +30,8 @@ export class BoundPorts { if (!firstBinding) { throw new Error("No port bindings found"); - } else { - return firstBinding; } + return firstBinding; } public setBinding(key: string | number, value: number, protocol: string = "tcp"): void { diff --git a/packages/testcontainers/src/utils/map-inspect-result.ts b/packages/testcontainers/src/utils/map-inspect-result.ts index 378217266..e9bb1d2d6 100644 --- a/packages/testcontainers/src/utils/map-inspect-result.ts +++ b/packages/testcontainers/src/utils/map-inspect-result.ts @@ -44,9 +44,8 @@ function mapHealthCheckStatus(inspectResult: ContainerInspectInfo): HealthCheckS if (status === undefined) { return "none"; - } else { - return status; } + return status; } function mapNetworkSettings(inspectResult: ContainerInspectInfo): { [networkName: string]: NetworkSettings } { diff --git a/packages/testcontainers/src/utils/port.ts b/packages/testcontainers/src/utils/port.ts index dfa50e344..159502367 100644 --- a/packages/testcontainers/src/utils/port.ts +++ b/packages/testcontainers/src/utils/port.ts @@ -11,15 +11,15 @@ const portWithProtocolRegex = RegExp(/^(\d+)(?:\/(udp|tcp))?$/i); export const getContainerPort = (port: PortWithOptionalBinding): number => { if (typeof port === "number") { return port; - } else if (typeof port === "string") { + } + if (typeof port === "string") { const match = portWithProtocolRegex.exec(port); if (match) { return parseInt(match[1], 10); } throw new Error(`Invalid port format: ${port}`); - } else { - return port.container; } + return port.container; }; export const hasHostBinding = (port: PortWithOptionalBinding): port is PortWithBinding => { @@ -29,13 +29,13 @@ export const hasHostBinding = (port: PortWithOptionalBinding): port is PortWithB export const getProtocol = (port: PortWithOptionalBinding): string => { if (typeof port === "number") { return "tcp"; - } else if (typeof port === "string") { + } + if (typeof port === "string") { const match = portWithProtocolRegex.exec(port); if (match?.[2]) { return match[2].toLowerCase(); } return "tcp"; - } else { - return port.protocol ? port.protocol.toLowerCase() : "tcp"; } + return port.protocol ? port.protocol.toLowerCase() : "tcp"; }; diff --git a/packages/testcontainers/src/utils/test-helper.ts b/packages/testcontainers/src/utils/test-helper.ts index 5f6b1baf6..57911b019 100644 --- a/packages/testcontainers/src/utils/test-helper.ts +++ b/packages/testcontainers/src/utils/test-helper.ts @@ -153,12 +153,14 @@ export async function stopStartingContainer(container: GenericContainer, name: s .inspect() .then((i) => i.State.Running) .catch(() => false), - (status) => status, + (running) => running, () => false, 10000 ); - if (!status) throw Error("failed start container"); + if (!status) { + throw Error("failed start container"); + } await client.container.getById(name).stop(); await containerStartPromise; diff --git a/packages/testcontainers/src/wait-strategies/http-wait-strategy.ts b/packages/testcontainers/src/wait-strategies/http-wait-strategy.ts index 1f851024b..d0a8e5332 100644 --- a/packages/testcontainers/src/wait-strategies/http-wait-strategy.ts +++ b/packages/testcontainers/src/wait-strategies/http-wait-strategy.ts @@ -147,17 +147,17 @@ export class HttpWaitStrategy extends AbstractWaitStrategy { private async matchesPredicates(response: Response | undefined): Promise { if (response === undefined) { return false; - } else if (!this.predicates.length) { + } + if (!this.predicates.length) { return response.ok; - } else { - for (const predicate of this.predicates) { - const result = await predicate(response); - if (!result) { - return false; - } + } + for (const predicate of this.predicates) { + const result = await predicate(response); + if (!result) { + return false; } - return true; } + return true; } private async handleContainerExit(container: Dockerode.Container) { diff --git a/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts b/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts index 51ca97bed..51af54586 100644 --- a/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts +++ b/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts @@ -36,9 +36,8 @@ export class LogWaitStrategy extends AbstractWaitStrategy { this.message.lastIndex = 0; } return this.message.test(line); - } else { - return line.includes(this.message); } + return line.includes(this.message); }; let count = 0; diff --git a/packages/testcontainers/src/wait-strategies/startup-check-strategy.test.ts b/packages/testcontainers/src/wait-strategies/startup-check-strategy.test.ts index bd8f3f438..0def1a895 100644 --- a/packages/testcontainers/src/wait-strategies/startup-check-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/startup-check-strategy.test.ts @@ -10,9 +10,8 @@ describe("StartupCheckStrategy", { timeout: 180_000 }, () => { if (this.count < 1) { this.count++; return "PENDING"; - } else { - return "SUCCESS"; } + return "SUCCESS"; } })(); diff --git a/packages/testcontainers/src/wait-strategies/startup-check-strategy.ts b/packages/testcontainers/src/wait-strategies/startup-check-strategy.ts index 7ba419516..27341e3b1 100644 --- a/packages/testcontainers/src/wait-strategies/startup-check-strategy.ts +++ b/packages/testcontainers/src/wait-strategies/startup-check-strategy.ts @@ -13,7 +13,7 @@ export abstract class StartupCheckStrategy extends AbstractWaitStrategy { const startupStatus = await new IntervalRetry(1000).retryUntil( async () => await this.checkStartupState(client.container.dockerode, container.id), - (startupStatus) => startupStatus === "SUCCESS" || startupStatus === "FAIL", + (status) => status === "SUCCESS" || status === "FAIL", () => { const message = `Container not accessible after ${this.startupTimeoutMs}ms`; log.error(message, { containerId: container.id }); @@ -24,7 +24,8 @@ export abstract class StartupCheckStrategy extends AbstractWaitStrategy { if (startupStatus instanceof Error) { throw startupStatus; - } else if (startupStatus === "FAIL") { + } + if (startupStatus === "FAIL") { throw new Error(`Container failed to start for ${container.id}`); } } diff --git a/packages/testcontainers/src/wait-strategies/utils/wait-strategy-selector.ts b/packages/testcontainers/src/wait-strategies/utils/wait-strategy-selector.ts index 86e1032e2..47369ab9a 100644 --- a/packages/testcontainers/src/wait-strategies/utils/wait-strategy-selector.ts +++ b/packages/testcontainers/src/wait-strategies/utils/wait-strategy-selector.ts @@ -26,11 +26,21 @@ export const selectWaitStrategy = async ({ healthCheck, imageNames = getImageNames(inspectResult), }: WaitStrategySelectorOptions): Promise => { - if (waitStrategy) return waitStrategy; - if (hasHealthCheck(healthCheck)) return Wait.forHealthCheck(); - if (hasDisabledHealthCheckConfig(inspectResult)) return Wait.forListeningPorts(); - if (hasHealthCheckConfig(inspectResult) || hasHealthCheckStatus(inspectResult)) return Wait.forHealthCheck(); - if (await imageHasHealthCheck(client, imageNames)) return Wait.forHealthCheck(); + if (waitStrategy) { + return waitStrategy; + } + if (hasHealthCheck(healthCheck)) { + return Wait.forHealthCheck(); + } + if (hasDisabledHealthCheckConfig(inspectResult)) { + return Wait.forListeningPorts(); + } + if (hasHealthCheckConfig(inspectResult) || hasHealthCheckStatus(inspectResult)) { + return Wait.forHealthCheck(); + } + if (await imageHasHealthCheck(client, imageNames)) { + return Wait.forHealthCheck(); + } return Wait.forListeningPorts(); };