diff --git a/eslint.config.js b/eslint.config.js index e7a8f7db4..de127bf5a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -32,6 +32,7 @@ export default [ "error", { caughtErrors: "none", + varsIgnorePattern: "^_", }, ], "no-irregular-whitespace": "off", diff --git a/packages/modules/arangodb/src/arangodb-container.test.ts b/packages/modules/arangodb/src/arangodb-container.test.ts index 2bc2a3052..437c3e37c 100755 --- a/packages/modules/arangodb/src/arangodb-container.test.ts +++ b/packages/modules/arangodb/src/arangodb-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("ArangoDB", { timeout: 180_000 }, () => { // connect { it("should connect and return a query result", async () => { - const container = await new ArangoDBContainer(IMAGE).start(); + await using container = await new ArangoDBContainer(IMAGE).start(); const db = new Database({ url: container.getHttpUrl() }); db.database("_system"); @@ -20,8 +20,6 @@ describe("ArangoDB", { timeout: 180_000 }, () => { }); const returnValue = await result.next(); expect(returnValue).toBe(value); - - await container.stop(); }); // } }); diff --git a/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.test.ts b/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.test.ts index 4cf569cc7..2ee18215d 100644 --- a/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.test.ts +++ b/packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.test.ts @@ -7,21 +7,20 @@ const IMAGE = getImage(__dirname); describe("AzureCosmosDbEmulatorContainer", { timeout: 180_000 }, async () => { it("should set https protocol", async () => { - const container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("https").start(); + await using container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("https").start(); const connectionUri = container.getConnectionUri(); expect(connectionUri).toContain("AccountEndpoint=https://"); - await container.stop(); }); + it("should set http protocol if no protocol is specified", async () => { - const container = await new AzureCosmosDbEmulatorContainer(IMAGE).start(); + await using container = await new AzureCosmosDbEmulatorContainer(IMAGE).start(); const connectionUri = container.getConnectionUri(); expect(connectionUri).toContain("AccountEndpoint=http://"); - await container.stop(); }); // httpCreateDB { it("should be able to create a database using http", async () => { - const container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("http").start(); + await using container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("http").start(); const cosmosClient = new CosmosClient({ endpoint: container.getEndpoint(), key: container.getKey(), @@ -35,14 +34,12 @@ describe("AzureCosmosDbEmulatorContainer", { timeout: 180_000 }, async () => { const db = await cosmosClient.database(dbName).read(); expect(db.database.id).toBe(dbName); - - await container.stop(); }); // } // httpsCreateDB { it("should be able to create a database using https", async () => { - const container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("https").start(); + await using container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("https").start(); const cosmosClient = new CosmosClient({ endpoint: container.getEndpoint(), key: container.getKey(), @@ -59,14 +56,12 @@ describe("AzureCosmosDbEmulatorContainer", { timeout: 180_000 }, async () => { const db = await cosmosClient.database(dbName).read(); expect(db.database.id).toBe(dbName); - - await container.stop(); }); // } // createAndRead { it("should be able to create a container and store and retrieve items", async () => { - const container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("http").start(); + await using container = await new AzureCosmosDbEmulatorContainer(IMAGE).withProtocol("http").start(); const cosmosClient = new CosmosClient({ endpoint: container.getEndpoint(), key: container.getKey(), @@ -94,8 +89,6 @@ describe("AzureCosmosDbEmulatorContainer", { timeout: 180_000 }, async () => { const readItem = await containerClient.item(createResponse.item.id, "bar").read(); expect(readItem.resource.foo).toEqual("bar"); - - await container.stop(); }); // } }); diff --git a/packages/modules/azurite/src/azurite-container.test.ts b/packages/modules/azurite/src/azurite-container.test.ts index a892df646..e81b7669e 100644 --- a/packages/modules/azurite/src/azurite-container.test.ts +++ b/packages/modules/azurite/src/azurite-container.test.ts @@ -9,7 +9,7 @@ const IMAGE = getImage(__dirname); describe("Azurite", { timeout: 240_000 }, () => { // uploadAndDownloadBlob { it("should upload and download blob with default credentials", async () => { - const container = await new AzuriteContainer(IMAGE).start(); + await using container = await new AzuriteContainer(IMAGE).start(); const connectionString = container.getConnectionString(); expect(connectionString).toBeTruthy(); @@ -34,14 +34,12 @@ describe("Azurite", { timeout: 240_000 }, () => { } expect(data).toBe(content); - - await container.stop(); }); // } // sendAndReceiveQueue { it("should add to queue with default credentials", async () => { - const container = await new AzuriteContainer(IMAGE).start(); + await using container = await new AzuriteContainer(IMAGE).start(); const connectionString = container.getConnectionString(); expect(connectionString).toBeTruthy(); @@ -58,14 +56,12 @@ describe("Azurite", { timeout: 240_000 }, () => { const messages = await queueClient.receiveMessages(); expect(messages.receivedMessageItems).toHaveLength(1); expect(messages.receivedMessageItems[0].messageText).toBe(message); - - await container.stop(); }); // } // createAndInsertOnTable { it("should add to table with default credentials", async () => { - const container = await new AzuriteContainer(IMAGE).start(); + await using container = await new AzuriteContainer(IMAGE).start(); const connectionString = container.getConnectionString(); expect(connectionString).toBeTruthy(); @@ -86,8 +82,6 @@ describe("Azurite", { timeout: 240_000 }, () => { const e1 = await tableClient.listEntities().next(); expect(e1.value).toBeTruthy(); expect(e1.value.name).toBe(entity.name); - - await container.stop(); }); // } @@ -97,7 +91,10 @@ describe("Azurite", { timeout: 240_000 }, () => { // Account key must be base64 encoded const accountKey = Buffer.from("test-key").toString("base64"); - const container = await new AzuriteContainer(IMAGE).withAccountName(accountName).withAccountKey(accountKey).start(); + await using container = await new AzuriteContainer(IMAGE) + .withAccountName(accountName) + .withAccountKey(accountKey) + .start(); const credentials = new StorageSharedKeyCredential(accountName, accountKey); const serviceClient = new BlobServiceClient(container.getBlobEndpoint(), credentials); @@ -109,8 +106,6 @@ describe("Azurite", { timeout: 240_000 }, () => { const blobContainer = await serviceClient.listContainers().next(); expect(blobContainer.value).toBeTruthy(); expect(blobContainer.value.name).toBe(blobContainerName); - - await container.stop(); }); // } @@ -119,7 +114,7 @@ describe("Azurite", { timeout: 240_000 }, () => { const blobPort = 13000; const queuePort = 14000; const tablePort = 15000; - const container = await new AzuriteContainer(IMAGE) + await using container = await new AzuriteContainer(IMAGE) .withBlobPort({ container: 10001, host: blobPort }) .withQueuePort({ container: 10002, host: queuePort }) .withTablePort({ container: 10003, host: tablePort }) @@ -137,14 +132,12 @@ describe("Azurite", { timeout: 240_000 }, () => { const serviceClient = BlobServiceClient.fromConnectionString(connectionString); const containerClient = serviceClient.getContainerClient("test"); await containerClient.createIfNotExists(); - - await container.stop(); }); // } // inMemoryPersistence { it("should be able to use in-memory persistence", async () => { - const container = await new AzuriteContainer(IMAGE).withInMemoryPersistence().start(); + await using container = await new AzuriteContainer(IMAGE).withInMemoryPersistence().start(); const blobName = "hello.txt"; { diff --git a/packages/modules/cassandra/src/cassandra-container.test.ts b/packages/modules/cassandra/src/cassandra-container.test.ts index e403af9e1..019406ecb 100644 --- a/packages/modules/cassandra/src/cassandra-container.test.ts +++ b/packages/modules/cassandra/src/cassandra-container.test.ts @@ -8,7 +8,7 @@ const IMAGE = getImage(__dirname); describe.sequential("Cassandra", { timeout: 240_000 }, () => { // connectWithDefaultCredentials { it("should connect and execute a query with default credentials", async () => { - const container = await new CassandraContainer(IMAGE).start(); + await using container = await new CassandraContainer(IMAGE).start(); const client = new Client({ contactPoints: [container.getContactPoint()], @@ -22,7 +22,6 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { expect(result.rows[0].release_version).toBe(ImageName.fromString(IMAGE).tag); await client.shutdown(); - await container.stop(); }); // } @@ -31,7 +30,7 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { const username = "testUser"; const password = "testPassword"; - const container = await new CassandraContainer(IMAGE).withUsername(username).withPassword(password).start(); + await using container = await new CassandraContainer(IMAGE).withUsername(username).withPassword(password).start(); const client = new Client({ contactPoints: [container.getContactPoint()], @@ -46,7 +45,6 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { expect(result.rows.length).toBeGreaterThan(0); await client.shutdown(); - await container.stop(); }); // } @@ -54,7 +52,10 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { it("should set datacenter and rack", async () => { const customDataCenter = "customDC"; const customRack = "customRack"; - const container = await new CassandraContainer(IMAGE).withDatacenter(customDataCenter).withRack(customRack).start(); + await using container = await new CassandraContainer(IMAGE) + .withDatacenter(customDataCenter) + .withRack(customRack) + .start(); const client = new Client({ contactPoints: [container.getContactPoint()], @@ -67,13 +68,12 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { expect(result.rows[0].rack).toBe(customRack); await client.shutdown(); - await container.stop(); }); // } // createAndFetchData { it("should create keyspace, a table, insert data, and retrieve it", async () => { - const container = await new CassandraContainer(IMAGE).start(); + await using container = await new CassandraContainer(IMAGE).start(); const client = new Client({ contactPoints: [container.getContactPoint()], @@ -108,12 +108,11 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { expect(result.rows[0].name).toBe(username); await client.shutdown(); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new CassandraContainer(IMAGE).start(); + await using container = await new CassandraContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -128,6 +127,5 @@ describe.sequential("Cassandra", { timeout: 240_000 }, () => { expect(result.rows[0].release_version).toBe(ImageName.fromString(IMAGE).tag); await client.shutdown(); - await container.stop(); }); }); diff --git a/packages/modules/chromadb/src/chromadb-container.test.ts b/packages/modules/chromadb/src/chromadb-container.test.ts index ae865b1c3..8f2d74f90 100755 --- a/packages/modules/chromadb/src/chromadb-container.test.ts +++ b/packages/modules/chromadb/src/chromadb-container.test.ts @@ -11,11 +11,10 @@ const IMAGE = getImage(__dirname); describe("ChromaDB", { timeout: 360_000 }, () => { // startContainer { it("should connect", async () => { - const container = await new ChromaDBContainer(IMAGE).start(); + await using container = await new ChromaDBContainer(IMAGE).start(); const client = await connectTo(container); expect(await client.heartbeat()).toBeDefined(); // Do something with the client - await container.stop(); }); // } @@ -32,7 +31,7 @@ describe("ChromaDB", { timeout: 360_000 }, () => { // createCollection { it("should create collection and get data", async () => { - const container = await new ChromaDBContainer(IMAGE).start(); + await using container = await new ChromaDBContainer(IMAGE).start(); const client = await connectTo(container); const collection = await client.createCollection({ name: "test", metadata: { "hnsw:space": "cosine" } }); expect(collection.name).toBe("test"); @@ -44,13 +43,12 @@ describe("ChromaDB", { timeout: 360_000 }, () => { expect(getResults.documents[0]).toStrictEqual("my doc"); expect(getResults.metadatas).toBeDefined(); expect(getResults.metadatas?.[0]?.key).toStrictEqual("value"); - await container.stop(); }); // } // queryCollectionWithEmbeddingFunction { it("should create collection and query", async () => { - const container = await new ChromaDBContainer(IMAGE).start(); + await using container = await new ChromaDBContainer(IMAGE).start(); const ollama = await new GenericContainer("ollama/ollama").withExposedPorts(11434).start(); await ollama.exec(["ollama", "pull", "nomic-embed-text"]); const client = await connectTo(container); @@ -75,13 +73,12 @@ describe("ChromaDB", { timeout: 360_000 }, () => { expect(results).toBeDefined(); expect(results.ids[0]).toEqual(["1"]); expect(results.ids[0][0]).toBe("1"); - await container.stop(); }); // persistentData { it("should reconnect with volume and persistence data", async () => { const sourcePath = fs.mkdtempSync(path.join(os.tmpdir(), "chroma-temp")); - const container = await new ChromaDBContainer(IMAGE) + await using container = await new ChromaDBContainer(IMAGE) .withBindMounts([{ source: sourcePath, target: "/data" }]) .start(); const client = await connectTo(container); @@ -93,7 +90,6 @@ describe("ChromaDB", { timeout: 360_000 }, () => { const getResults = await collection.get({ ids: ["1"] }); expect(getResults.ids[0]).toBe("1"); expect(getResults.documents[0]).toStrictEqual("my doc"); - await container.stop(); expect(fs.existsSync(`${sourcePath}/chroma.sqlite3`)).toBe(true); try { fs.rmSync(sourcePath, { force: true, recursive: true }); @@ -109,7 +105,7 @@ describe("ChromaDB", { timeout: 360_000 }, () => { const tenant = "test-tenant"; const key = "test-key"; const database = "test-db"; - const container = await new ChromaDBContainer(IMAGE) + await using container = await new ChromaDBContainer(IMAGE) .withEnvironment({ CHROMA_SERVER_AUTHN_CREDENTIALS: key, CHROMA_SERVER_AUTHN_PROVIDER: "chromadb.auth.token_authn.TokenAuthenticationServerProvider", diff --git a/packages/modules/clickhouse/src/clickhouse-container.test.ts b/packages/modules/clickhouse/src/clickhouse-container.test.ts index b2773b073..f1dfdaea1 100644 --- a/packages/modules/clickhouse/src/clickhouse-container.test.ts +++ b/packages/modules/clickhouse/src/clickhouse-container.test.ts @@ -11,7 +11,7 @@ interface ClickHouseQueryResponse { describe("ClickHouseContainer", { timeout: 180_000 }, () => { // connectWithOptions { it("should connect using the client options object", async () => { - const container = await new ClickHouseContainer(IMAGE).start(); + await using container = await new ClickHouseContainer(IMAGE).start(); const client = createClient(container.getClientOptions()); const result = await client.query({ @@ -22,13 +22,12 @@ describe("ClickHouseContainer", { timeout: 180_000 }, () => { expect(data?.data?.[0]?.value).toBe(1); await client.close(); - await container.stop(); }); // } // connectWithUrl { it("should connect using the URL", async () => { - const container = await new ClickHouseContainer(IMAGE).start(); + await using container = await new ClickHouseContainer(IMAGE).start(); const client = createClient({ url: container.getConnectionUrl(), }); @@ -42,13 +41,12 @@ describe("ClickHouseContainer", { timeout: 180_000 }, () => { expect(data?.data?.[0]?.value).toBe(1); await client.close(); - await container.stop(); }); // } // connectWithUsernameAndPassword { it("should connect using the username and password", async () => { - const container = await new ClickHouseContainer(IMAGE) + await using container = await new ClickHouseContainer(IMAGE) .withUsername("customUsername") .withPassword("customPassword") .start(); @@ -68,14 +66,13 @@ describe("ClickHouseContainer", { timeout: 180_000 }, () => { expect(data?.data?.[0]?.value).toBe(1); await client.close(); - await container.stop(); }); // } // setDatabase { it("should set database", async () => { const customDatabase = "customDatabase"; - const container = await new ClickHouseContainer(IMAGE).withDatabase(customDatabase).start(); + await using container = await new ClickHouseContainer(IMAGE).withDatabase(customDatabase).start(); const client = createClient(container.getClientOptions()); @@ -88,14 +85,13 @@ describe("ClickHouseContainer", { timeout: 180_000 }, () => { expect(data?.data?.[0]?.current_database).toBe(customDatabase); await client.close(); - await container.stop(); }); // } // setUsername { it("should set username", async () => { const customUsername = "customUsername"; - const container = await new ClickHouseContainer(IMAGE).withUsername(customUsername).start(); + await using container = await new ClickHouseContainer(IMAGE).withUsername(customUsername).start(); const client = createClient(container.getClientOptions()); @@ -108,12 +104,11 @@ describe("ClickHouseContainer", { timeout: 180_000 }, () => { expect(data?.data?.[0]?.current_user).toBe(customUsername); await client.close(); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new ClickHouseContainer(IMAGE).start(); + await using container = await new ClickHouseContainer(IMAGE).start(); await container.restart(); const client = createClient(container.getClientOptions()); @@ -127,6 +122,5 @@ describe("ClickHouseContainer", { timeout: 180_000 }, () => { expect(data?.data?.[0]?.value).toBe(1); await client.close(); - await container.stop(); }); }); diff --git a/packages/modules/cockroachdb/src/cockroachdb-container.test.ts b/packages/modules/cockroachdb/src/cockroachdb-container.test.ts index 5dbbe7f8a..92561fe67 100644 --- a/packages/modules/cockroachdb/src/cockroachdb-container.test.ts +++ b/packages/modules/cockroachdb/src/cockroachdb-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("CockroachDbContainer", { timeout: 180_000 }, () => { // connect { it("should connect and return a query result", async () => { - const container = await new CockroachDbContainer(IMAGE).start(); + await using container = await new CockroachDbContainer(IMAGE).start(); const client = new Client({ host: container.getHost(), @@ -23,13 +23,12 @@ describe("CockroachDbContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": "1" }); await client.end(); - await container.stop(); }); // } // uriConnect { it("should work with database URI", async () => { - const container = await new CockroachDbContainer(IMAGE).start(); + await using container = await new CockroachDbContainer(IMAGE).start(); const client = new Client({ connectionString: container.getConnectionUri(), @@ -40,13 +39,12 @@ describe("CockroachDbContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": "1" }); await client.end(); - await container.stop(); }); // } // setDatabase { it("should set database", async () => { - const container = await new CockroachDbContainer(IMAGE).withDatabase("custom_database").start(); + await using container = await new CockroachDbContainer(IMAGE).withDatabase("custom_database").start(); const client = new Client({ host: container.getHost(), @@ -60,13 +58,12 @@ describe("CockroachDbContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ current_database: "custom_database" }); await client.end(); - await container.stop(); }); // } // setUsername { it("should set username", async () => { - const container = await new CockroachDbContainer(IMAGE).withUsername("custom_username").start(); + await using container = await new CockroachDbContainer(IMAGE).withUsername("custom_username").start(); const client = new Client({ host: container.getHost(), @@ -80,12 +77,11 @@ describe("CockroachDbContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ current_user: "custom_username" }); await client.end(); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new CockroachDbContainer(IMAGE).start(); + await using container = await new CockroachDbContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -100,7 +96,6 @@ describe("CockroachDbContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": "1" }); await client.end(); - await container.stop(); }); it("should allow custom healthcheck", async () => { diff --git a/packages/modules/couchbase/src/couchbase-container.test.ts b/packages/modules/couchbase/src/couchbase-container.test.ts index 8197e00ed..cf25d1f09 100644 --- a/packages/modules/couchbase/src/couchbase-container.test.ts +++ b/packages/modules/couchbase/src/couchbase-container.test.ts @@ -1,6 +1,6 @@ import couchbase, { Bucket, Cluster } from "couchbase"; import { BucketDefinition } from "./bucket-definition"; -import { CouchbaseContainer, StartedCouchbaseContainer } from "./couchbase-container"; +import { CouchbaseContainer } from "./couchbase-container"; import { CouchbaseService } from "./couchbase-service"; describe("CouchbaseContainer", { timeout: 180_000 }, () => { @@ -31,27 +31,14 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => { describe("Enterprise Image", () => { const COUCHBASE_IMAGE_ENTERPRISE = "couchbase/server:enterprise-7.0.3"; - let startedTestContainer: StartedCouchbaseContainer; - let cluster: Cluster; - - afterEach(async () => { - if (cluster) { - await cluster.close(); - } - - if (startedTestContainer) { - await startedTestContainer.stop(); - } - }); - // connectAndQuery { it("should connect and query using enterprise image", async () => { const bucketDefinition = new BucketDefinition("mybucket"); const container = new CouchbaseContainer(COUCHBASE_IMAGE_ENTERPRISE).withBucket(bucketDefinition); - startedTestContainer = await container.start(); + await using startedTestContainer = await container.start(); - cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { + const cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { username: startedTestContainer.getUsername(), password: startedTestContainer.getPassword(), }); @@ -60,6 +47,7 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => { const result = await upsertAndGet(bucket, "testdoc", { foo: "bar" }); expect(result.content).toEqual({ foo: "bar" }); + await cluster.close(); }); // } @@ -67,8 +55,8 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => { const bucketDefinition = new BucketDefinition("mybucket").withFlushEnabled(true); const container = new CouchbaseContainer(COUCHBASE_IMAGE_ENTERPRISE).withBucket(bucketDefinition); - startedTestContainer = await container.start(); - cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { + await using startedTestContainer = await container.start(); + const cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { username: startedTestContainer.getUsername(), password: startedTestContainer.getPassword(), }); @@ -81,31 +69,19 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => { const existResult = await flushBucketAndCheckExists(cluster, bucket, "testdoc"); expect(existResult.exists).toBe(false); + await cluster.close(); }); }); describe("Community Image", () => { const COUCHBASE_IMAGE_COMMUNITY = "couchbase/server:community-7.0.2"; - let startedTestContainer: StartedCouchbaseContainer; - let cluster: Cluster; - - afterEach(async () => { - if (cluster) { - await cluster.close(); - } - - if (startedTestContainer) { - await startedTestContainer.stop(); - } - }); - it("should connect and query using community image", async () => { const bucketDefinition = new BucketDefinition("mybucket"); const container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY).withBucket(bucketDefinition); - startedTestContainer = await container.start(); - cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { + await using startedTestContainer = await container.start(); + const cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { username: startedTestContainer.getUsername(), password: startedTestContainer.getPassword(), }); @@ -114,14 +90,15 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => { const result = await upsertAndGet(bucket, "testdoc", { foo: "bar" }); expect(result.content).toEqual({ foo: "bar" }); + await cluster.close(); }); it("should flush bucket if flushEnabled and check any document exists", async () => { const bucketDefinition = new BucketDefinition("mybucket").withFlushEnabled(true); const container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY).withBucket(bucketDefinition); - startedTestContainer = await container.start(); - cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { + await using startedTestContainer = await container.start(); + const cluster = await couchbase.Cluster.connect(startedTestContainer.getConnectionString(), { username: startedTestContainer.getUsername(), password: startedTestContainer.getPassword(), }); @@ -134,6 +111,7 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => { const existResult = await flushBucketAndCheckExists(cluster, bucket, "testdoc"); expect(existResult.exists).toBe(false); + await cluster.close(); }); it("should throw error if analytics service enabled with community version", async () => { diff --git a/packages/modules/elasticsearch/src/elasticsearch-container.test.ts b/packages/modules/elasticsearch/src/elasticsearch-container.test.ts index 15ff0399d..5803b61b9 100644 --- a/packages/modules/elasticsearch/src/elasticsearch-container.test.ts +++ b/packages/modules/elasticsearch/src/elasticsearch-container.test.ts @@ -8,7 +8,7 @@ const images = ["elasticsearch:7.17.28", "elasticsearch:8.18.1", IMAGE]; describe("ElasticsearchContainer", { timeout: 180_000 }, () => { // createIndex { it.each(images)("should create an index with %s", async (image) => { - const container = await new ElasticsearchContainer(image).start(); + await using container = await new ElasticsearchContainer(image).start(); const client = new Client({ node: container.getHttpUrl(), auth: { username: container.getUsername(), password: container.getPassword() }, @@ -17,13 +17,12 @@ describe("ElasticsearchContainer", { timeout: 180_000 }, () => { await client.indices.create({ index: "people" }); expect(await client.indices.exists({ index: "people" })).toBe(true); - await container.stop(); }); // } // indexDocument { it("should index a document", async () => { - const container = await new ElasticsearchContainer(IMAGE).start(); + await using container = await new ElasticsearchContainer(IMAGE).start(); const client = new Client({ node: container.getHttpUrl(), auth: { username: container.getUsername(), password: container.getPassword() }, @@ -40,12 +39,11 @@ describe("ElasticsearchContainer", { timeout: 180_000 }, () => { }); expect((await client.get({ index: "people", id: document.id }))._source).toStrictEqual(document); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new ElasticsearchContainer(IMAGE).start(); + await using container = await new ElasticsearchContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -56,11 +54,10 @@ describe("ElasticsearchContainer", { timeout: 180_000 }, () => { await client.indices.create({ index: "people" }); expect(await client.indices.exists({ index: "people" })).toBe(true); - await container.stop(); }); // } it("should set custom password", async () => { - const container = await new ElasticsearchContainer(IMAGE).withPassword("testPassword").start(); + await using container = await new ElasticsearchContainer(IMAGE).withPassword("testPassword").start(); const client = new Client({ node: container.getHttpUrl(), @@ -70,6 +67,5 @@ describe("ElasticsearchContainer", { timeout: 180_000 }, () => { await client.indices.create({ index: "people" }); expect(await client.indices.exists({ index: "people" })).toBe(true); - await container.stop(); }); }); diff --git a/packages/modules/etcd/src/etcd-container.test.ts b/packages/modules/etcd/src/etcd-container.test.ts index f8fbf333d..a70e0d053 100644 --- a/packages/modules/etcd/src/etcd-container.test.ts +++ b/packages/modules/etcd/src/etcd-container.test.ts @@ -1,50 +1,48 @@ import { Etcd3 } from "etcd3"; import { setTimeout } from "node:timers/promises"; import { getImage } from "../../../testcontainers/src/utils/test-helper"; -import { EtcdContainer, StartedEtcdContainer } from "./etcd-container"; +import { EtcdContainer } from "./etcd-container"; const IMAGE = getImage(__dirname); describe("etcd", () => { - it("should construct a container", { timeout: 30_000 }, async () => { - const container = await new EtcdContainer(IMAGE).start(); - expect(container).toBeInstanceOf(StartedEtcdContainer); - container.stop(); - }); - // readWrite { it("should connect and perform read/write operations", async () => { - const container = await new EtcdContainer(IMAGE).start(); + await using container = await new EtcdContainer(IMAGE).start(); + const client = new Etcd3({ hosts: container.getClientEndpoint(), }); + const key = "foo"; const value = "bar"; - await client.put(key).value(value); + const result = await client.get(key).string(); expect(result).toEqual(value); - - await container.stop(); }); // } // subscribe { it("should subscribe to key changes", async () => { const subscriber = vi.fn(); - const container = await new EtcdContainer(IMAGE).start(); + + await using container = await new EtcdContainer(IMAGE).start(); + const client = new Etcd3({ hosts: container.getClientEndpoint(), }); + const key = "foo"; const value = "bar"; const watcher = await client.watch().key(key).create(); watcher.on("put", subscriber); await client.put(key).value(value); + await setTimeout(1_000); + expect(subscriber).toHaveBeenCalled(); await watcher.cancel(); - await container.stop(); }); // } }); diff --git a/packages/modules/gcloud/src/bigquery-emulator-container.test.ts b/packages/modules/gcloud/src/bigquery-emulator-container.test.ts index e5c16e6b7..3eebfb7e0 100644 --- a/packages/modules/gcloud/src/bigquery-emulator-container.test.ts +++ b/packages/modules/gcloud/src/bigquery-emulator-container.test.ts @@ -6,11 +6,9 @@ const IMAGE = getImage(__dirname, 2); describe("BigQueryEmulatorContainer", { timeout: 240_000 }, () => { it("should work using default version", async () => { - const bigQueryEmulatorContainer = await new BigQueryEmulatorContainer(IMAGE).start(); + await using bigQueryEmulatorContainer = await new BigQueryEmulatorContainer(IMAGE).start(); await checkBigQuery(bigQueryEmulatorContainer); - - await bigQueryEmulatorContainer.stop(); }); async function checkBigQuery(bigQueryEmulatorContainer: StartedBigQueryEmulatorContainer) { diff --git a/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts b/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts index 297d30ccd..44cbb7959 100644 --- a/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts +++ b/packages/modules/gcloud/src/cloudstorage-emulator-container.test.ts @@ -49,27 +49,23 @@ describe.sequential("CloudStorageEmulatorContainer", { timeout: 240_000 }, () => // cloud-storage { it("should work using default version", async () => { - const cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE).start(); + await using cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE).start(); await checkCloudStorage(cloudstorageEmulatorContainer); - - await cloudstorageEmulatorContainer.stop(); }); // } it("should use the provided external URL", async () => { - const cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE) + await using cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE) .withExternalURL("http://cdn.company.local") .start(); expect(cloudstorageEmulatorContainer).toBeDefined(); expect(cloudstorageEmulatorContainer.getExternalUrl()).toBe("http://cdn.company.local"); - - await cloudstorageEmulatorContainer.stop(); }); it("should be able update the external URL of running instance", async () => { - const cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE) + await using cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE) .withExternalURL("http://cdn.company.local") .start(); @@ -102,8 +98,6 @@ describe.sequential("CloudStorageEmulatorContainer", { timeout: 240_000 }, () => expect(requestBodyAsJson).toEqual(expect.objectContaining({ externalUrl: "http://files.company.local" })); expect(cloudstorageEmulatorContainer.getExternalUrl()).toBe("http://files.company.local"); - - await cloudstorageEmulatorContainer.stop(); }); it("should use emulator endpoint as default external URL", async () => { @@ -113,13 +107,11 @@ describe.sequential("CloudStorageEmulatorContainer", { timeout: 240_000 }, () => if (request.url.includes("/_internal/config")) configUpdated = true; }); - const container = await new CloudStorageEmulatorContainer(IMAGE).start(); + await using container = await new CloudStorageEmulatorContainer(IMAGE).start(); expect(configUpdated).toBe(true); expect(container.getExternalUrl()).toBe(container.getEmulatorEndpoint()); expect((await fetch(`${container.getExternalUrl()}/_internal/healthcheck`)).status).toBe(200); - - await container.stop(); }); it("should allow skipping updating the external URL automatically", async () => { @@ -129,13 +121,11 @@ describe.sequential("CloudStorageEmulatorContainer", { timeout: 240_000 }, () => if (request.url.includes("/_internal/config")) configUpdated = true; }); - const container = await new CloudStorageEmulatorContainer(IMAGE).withAutoUpdateExternalUrl(false).start(); + await using container = await new CloudStorageEmulatorContainer(IMAGE).withAutoUpdateExternalUrl(false).start(); expect(configUpdated).toBe(false); expect(container.getExternalUrl()).toBe(undefined); expect((await fetch(`${container.getEmulatorEndpoint()}/_internal/healthcheck`)).status).toBe(200); - - await container.stop(); }); async function checkCloudStorage(cloudstorageEmulatorContainer: StartedCloudStorageEmulatorContainer) { diff --git a/packages/modules/gcloud/src/datastore-emulator-container.test.ts b/packages/modules/gcloud/src/datastore-emulator-container.test.ts index d3c03c1b0..074e9f7ad 100644 --- a/packages/modules/gcloud/src/datastore-emulator-container.test.ts +++ b/packages/modules/gcloud/src/datastore-emulator-container.test.ts @@ -7,23 +7,19 @@ const IMAGE = getImage(__dirname); describe("DatastoreEmulatorContainer", { timeout: 240_000 }, () => { // datastore4 { it("should work using default version", async () => { - const datastoreEmulatorContainer = await new DatastoreEmulatorContainer(IMAGE).start(); + await using datastoreEmulatorContainer = await new DatastoreEmulatorContainer(IMAGE).start(); await checkDatastore(datastoreEmulatorContainer); - - await datastoreEmulatorContainer.stop(); }); // } // datastore5 { it("should work using version 468.0.0", async () => { - const datastoreEmulatorContainer = await new DatastoreEmulatorContainer( + await using datastoreEmulatorContainer = await new DatastoreEmulatorContainer( "gcr.io/google.com/cloudsdktool/google-cloud-cli:468.0.0-emulators" ).start(); await checkDatastore(datastoreEmulatorContainer); - - await datastoreEmulatorContainer.stop(); }); // } diff --git a/packages/modules/gcloud/src/firestore-emulator-container.test.ts b/packages/modules/gcloud/src/firestore-emulator-container.test.ts index 8353ed8de..f4a05d322 100644 --- a/packages/modules/gcloud/src/firestore-emulator-container.test.ts +++ b/packages/modules/gcloud/src/firestore-emulator-container.test.ts @@ -8,23 +8,19 @@ const IMAGE = getImage(__dirname); describe("FirestoreEmulatorContainer", { timeout: 240_000 }, () => { // firestore4 { it("should work using default version", async () => { - const firestoreEmulatorContainer = await new FirestoreEmulatorContainer(IMAGE).start(); + await using firestoreEmulatorContainer = await new FirestoreEmulatorContainer(IMAGE).start(); await checkFirestore(firestoreEmulatorContainer); - - await firestoreEmulatorContainer.stop(); }); // } // firestore5 { it("should work using version 468.0.0", async () => { - const firestoreEmulatorContainer = await new FirestoreEmulatorContainer( + await using firestoreEmulatorContainer = await new FirestoreEmulatorContainer( "gcr.io/google.com/cloudsdktool/google-cloud-cli:468.0.0-emulators" ).start(); await checkFirestore(firestoreEmulatorContainer); - - await firestoreEmulatorContainer.stop(); }); // } diff --git a/packages/modules/gcloud/src/pubsub-emulator-container.test.ts b/packages/modules/gcloud/src/pubsub-emulator-container.test.ts index 2d3fcb67f..a30bc64a1 100644 --- a/packages/modules/gcloud/src/pubsub-emulator-container.test.ts +++ b/packages/modules/gcloud/src/pubsub-emulator-container.test.ts @@ -6,11 +6,9 @@ const IMAGE = getImage(__dirname); describe("PubSubEmulatorContainer", { timeout: 240_000 }, () => { it("should work using default version", async () => { - const pubsubEmulatorContainer = await new PubSubEmulatorContainer(IMAGE).start(); + await using pubsubEmulatorContainer = await new PubSubEmulatorContainer(IMAGE).start(); await checkPubSub(pubsubEmulatorContainer); - - await pubsubEmulatorContainer.stop(); }); async function checkPubSub(pubsubEmulatorContainer: StartedPubSubEmulatorContainer) { diff --git a/packages/modules/gcloud/src/spanner-emulator-container.test.ts b/packages/modules/gcloud/src/spanner-emulator-container.test.ts index 09ce87c3d..8b005959c 100644 --- a/packages/modules/gcloud/src/spanner-emulator-container.test.ts +++ b/packages/modules/gcloud/src/spanner-emulator-container.test.ts @@ -8,7 +8,7 @@ const IMAGE = getImage(__dirname, 3); describe("SpannerEmulatorContainer", { timeout: 240_000 }, () => { // startupWithExplicitClient { it("should start, expose endpoints and accept real client connections using explicitly configured client", async () => { - const container = await new SpannerEmulatorContainer(IMAGE).withProjectId("test-project").start(); + await using container = await new SpannerEmulatorContainer(IMAGE).withProjectId("test-project").start(); const client = new Spanner({ projectId: container.getProjectId(), @@ -26,8 +26,6 @@ describe("SpannerEmulatorContainer", { timeout: 240_000 }, () => { // emulator always includes "emulator-config" const expectedConfigName = admin.instanceConfigPath(container.getProjectId(), "emulator-config"); expect(configs.map((c) => c.name)).toContain(expectedConfigName); - - await container.stop(); }); // } @@ -38,7 +36,7 @@ describe("SpannerEmulatorContainer", { timeout: 240_000 }, () => { // startupWithEnvironmentVariable { it("should start, expose endpoints and accept real client connections using projectId and SPANNER_EMULATOR_HOST", async () => { - const container = await new SpannerEmulatorContainer(IMAGE).withProjectId("test-project").start(); + await using container = await new SpannerEmulatorContainer(IMAGE).withProjectId("test-project").start(); // configure the client to talk to our emulator process.env.SPANNER_EMULATOR_HOST = container.getEmulatorGrpcEndpoint(); @@ -53,8 +51,6 @@ describe("SpannerEmulatorContainer", { timeout: 240_000 }, () => { // emulator always includes "emulator-config" const expectedConfigName = admin.instanceConfigPath(container.getProjectId(), "emulator-config"); expect(configs.map((c) => c.name)).toContain(expectedConfigName); - - await container.stop(); }); // } }); diff --git a/packages/modules/gcloud/src/spanner-emulator-helper.test.ts b/packages/modules/gcloud/src/spanner-emulator-helper.test.ts index ecd47c866..e145d3c4b 100644 --- a/packages/modules/gcloud/src/spanner-emulator-helper.test.ts +++ b/packages/modules/gcloud/src/spanner-emulator-helper.test.ts @@ -8,7 +8,7 @@ const IMAGE = getImage(__dirname, 3); describe("SpannerEmulatorHelper", { timeout: 240_000 }, () => { // createAndDelete { it("should create and delete instance and database via helper", async () => { - const container = await new SpannerEmulatorContainer(IMAGE).start(); + await using container = await new SpannerEmulatorContainer(IMAGE).start(); const helper = new SpannerEmulatorHelper(container); const instanceId = "test-instance"; const databaseId = "test-db"; @@ -37,8 +37,6 @@ describe("SpannerEmulatorHelper", { timeout: 240_000 }, () => { const [instanceExistsAfter] = await client.instance(instanceId).exists(); expect(instanceExistsAfter).toBe(false); - - await container.stop(); }); // } }); diff --git a/packages/modules/hivemq/src/hivemq-container.test.ts b/packages/modules/hivemq/src/hivemq-container.test.ts index b88c63f8b..247e72984 100644 --- a/packages/modules/hivemq/src/hivemq-container.test.ts +++ b/packages/modules/hivemq/src/hivemq-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("HiveMQContainer", { timeout: 240_000 }, () => { // connect { it("should connect to HiveMQ Community Edition via MQTT.js", async () => { - const container = await new HiveMQContainer(IMAGE).start(); + await using container = await new HiveMQContainer(IMAGE).start(); const testMqttClient = mqtt.connect(container.getConnectionString()); @@ -27,7 +27,7 @@ describe("HiveMQContainer", { timeout: 240_000 }, () => { }); }); - return expect(promise).resolves.toBeUndefined(); + await expect(promise).resolves.toBeUndefined(); }); // } }); diff --git a/packages/modules/k3s/src/k3s-container.test.ts b/packages/modules/k3s/src/k3s-container.test.ts index 2d380f9da..03bdc4591 100644 --- a/packages/modules/k3s/src/k3s-container.test.ts +++ b/packages/modules/k3s/src/k3s-container.test.ts @@ -12,7 +12,7 @@ describe("K3s", { timeout: 120_000 }, () => { if (!process.env["CI_ROOTLESS"]) { it("should start and have listable node", async () => { // starting_k3s { - const container = await new K3sContainer("rancher/k3s:v1.31.2-k3s1").start(); + await using container = await new K3sContainer("rancher/k3s:v1.31.2-k3s1").start(); // } // connecting_with_client { @@ -29,13 +29,11 @@ describe("K3s", { timeout: 120_000 }, () => { // } expect(nodeList.items).toHaveLength(1); - - await container.stop(); }); it("should expose kubeconfig for a network alias", async () => { - const network = await new Network().start(); - const container = await new K3sContainer("rancher/k3s:v1.31.2-k3s1") + await using network = await new Network().start(); + await using container = await new K3sContainer("rancher/k3s:v1.31.2-k3s1") .withNetwork(network) .withNetworkAliases("k3s") .start(); @@ -43,7 +41,7 @@ describe("K3s", { timeout: 120_000 }, () => { // obtain a kubeconfig that allows us to connect on the custom network const kubeConfig = container.getAliasedKubeConfig("k3s"); - const kubectlContainer = await new GenericContainer("rancher/kubectl:v1.31.2") + await using kubectlContainer = await new GenericContainer("rancher/kubectl:v1.31.2") .withNetwork(network) .withCopyContentToContainer([{ content: kubeConfig, target: "/home/kubectl/.kube/config" }]) .withCommand(["get", "namespaces"]) @@ -56,14 +54,10 @@ describe("K3s", { timeout: 120_000 }, () => { chunks.push(chunk); } expect(chunks).toEqual(expect.arrayContaining([expect.stringContaining("kube-system")])); - - await kubectlContainer.stop(); - await container.stop(); - await network.stop(); }); it("should start a pod", async () => { - const container = await new K3sContainer("rancher/k3s:v1.31.2-k3s1").start(); + await using container = await new K3sContainer("rancher/k3s:v1.31.2-k3s1").start(); const kc = new k8s.KubeConfig(); kc.loadFromString(container.getKubeConfig()); @@ -96,8 +90,6 @@ describe("K3s", { timeout: 120_000 }, () => { // wait for pod to be ready expect(await podIsReady(client, "default", "helloworld", 60_000)).toBe(true); - - await container.stop(); }); } }); diff --git a/packages/modules/kafka/src/kafka-container-7.test.ts b/packages/modules/kafka/src/kafka-container-7.test.ts index e4df2f51a..b2989139a 100644 --- a/packages/modules/kafka/src/kafka-container-7.test.ts +++ b/packages/modules/kafka/src/kafka-container-7.test.ts @@ -9,66 +9,53 @@ const IMAGE = "confluentinc/cp-kafka:7.9.1"; describe("KafkaContainer", { timeout: 240_000 }, () => { // connectBuiltInZK { it("should connect using in-built zoo-keeper", async () => { - const kafkaContainer = await new KafkaContainer(IMAGE).start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); }); // } it("should connect using in-built zoo-keeper and custom images", async () => { - const kafkaContainer = await new KafkaContainer(IMAGE).start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); }); it("should connect using in-built zoo-keeper and custom network", async () => { - const network = await new Network().start(); + await using network = await new Network().start(); - const kafkaContainer = await new KafkaContainer(IMAGE).withNetwork(network).start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).withNetwork(network).start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); - await network.stop(); }); // connectProvidedZK { it("should connect using provided zoo-keeper and network", async () => { - const network = await new Network().start(); + await using network = await new Network().start(); const zooKeeperHost = "zookeeper"; const zooKeeperPort = 2181; - const zookeeperContainer = await new GenericContainer("confluentinc/cp-zookeeper:5.5.4") + await using _ = await new GenericContainer("confluentinc/cp-zookeeper:5.5.4") .withNetwork(network) .withNetworkAliases(zooKeeperHost) .withEnvironment({ ZOOKEEPER_CLIENT_PORT: zooKeeperPort.toString() }) .withExposedPorts(zooKeeperPort) .start(); - const kafkaContainer = await new KafkaContainer(IMAGE) + await using kafkaContainer = await new KafkaContainer(IMAGE) .withNetwork(network) .withZooKeeper(zooKeeperHost, zooKeeperPort) .start(); await testPubSub(kafkaContainer); - - await zookeeperContainer.stop(); - await kafkaContainer.stop(); - await network.stop(); }); // } it("should be reusable", async () => { - const originalKafkaContainer = await new KafkaContainer(IMAGE).withReuse().start(); + await using originalKafkaContainer = await new KafkaContainer(IMAGE).withReuse().start(); const newKafkaContainer = await new KafkaContainer(IMAGE).withReuse().start(); expect(newKafkaContainer.getId()).toBe(originalKafkaContainer.getId()); - - await originalKafkaContainer.stop(); }); describe.each([ @@ -104,7 +91,7 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { }, }); configure(kafkaContainer); - const startedKafkaContainer = await kafkaContainer.start(); + await using startedKafkaContainer = await kafkaContainer.start(); await testPubSub(startedKafkaContainer, { brokers: [`${startedKafkaContainer.getHost()}:${startedKafkaContainer.getMappedPort(9096)}`], @@ -117,14 +104,13 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { ca: [fs.readFileSync(path.resolve(certificatesDir, "kafka.client.truststore.pem"))], }, }); - await startedKafkaContainer.stop(); }); // } it(`should connect within Docker network`, async () => { - const network = await new Network().start(); + await using network = await new Network().start(); - const kafkaContainer = await new KafkaContainer(IMAGE) + await using _ = await new KafkaContainer(IMAGE) .withNetwork(network) .withNetworkAliases("kafka") .withSaslSslListener({ @@ -147,7 +133,7 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { }) .start(); - const kafkaCliContainer = await new GenericContainer(IMAGE) + await using kafkaCliContainer = await new GenericContainer(IMAGE) .withNetwork(network) .withCommand(["bash", "-c", "sleep infinity"]) .withCopyFilesToContainer([ @@ -182,19 +168,14 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { expect(exitCode).toBe(0); expect(output).toContain("test-topic"); - - await kafkaCliContainer.stop(); - await kafkaContainer.stop(); }); }); // connectKraft { it("should connect using kraft", async () => { - const kafkaContainer = await new KafkaContainer(IMAGE).withKraft().start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).withKraft().start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); }); // } @@ -205,13 +186,10 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { }); it("should connect using kraft and custom network", async () => { - const network = await new Network().start(); - const kafkaContainer = await new KafkaContainer(IMAGE).withKraft().withNetwork(network).start(); + await using network = await new Network().start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).withKraft().withNetwork(network).start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); - await network.stop(); }); it("should throw an error when using kraft wit sasl and confluence platfom below 7.5.0", async () => { diff --git a/packages/modules/kafka/src/kafka-container-latest.test.ts b/packages/modules/kafka/src/kafka-container-latest.test.ts index 96a7fb041..acb6d73aa 100644 --- a/packages/modules/kafka/src/kafka-container-latest.test.ts +++ b/packages/modules/kafka/src/kafka-container-latest.test.ts @@ -12,31 +12,24 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { // connectKafkaLatest { it("should connect", async () => { - const kafkaContainer = await new KafkaContainer(IMAGE).start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); }); // } it("should connect with custom network", async () => { - const network = await new Network().start(); - const kafkaContainer = await new KafkaContainer(IMAGE).withNetwork(network).start(); + await using network = await new Network().start(); + await using kafkaContainer = await new KafkaContainer(IMAGE).withNetwork(network).start(); await testPubSub(kafkaContainer); - - await kafkaContainer.stop(); - await network.stop(); }); it("should be reusable", async () => { - const originalKafkaContainer = await new KafkaContainer(IMAGE).withReuse().start(); + await using originalKafkaContainer = await new KafkaContainer(IMAGE).withReuse().start(); const newKafkaContainer = await new KafkaContainer(IMAGE).withReuse().start(); expect(newKafkaContainer.getId()).toBe(originalKafkaContainer.getId()); - - await originalKafkaContainer.stop(); }); // ssl { @@ -61,7 +54,7 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { }; const kafkaContainer = new KafkaContainer("confluentinc/cp-kafka:7.5.0").withSaslSslListener(saslConfig); - const startedKafkaContainer = await kafkaContainer.start(); + await using startedKafkaContainer = await kafkaContainer.start(); await testPubSub(startedKafkaContainer, { brokers: [`${startedKafkaContainer.getHost()}:${startedKafkaContainer.getMappedPort(9096)}`], @@ -74,12 +67,11 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { ca: [fs.readFileSync(path.resolve(certificatesDir, "kafka.client.truststore.pem"))], }, }); - await startedKafkaContainer.stop(); }); // } it(`should connect with SASL in custom network`, async () => { - const network = await new Network().start(); + await using network = await new Network().start(); const saslConfig: SaslSslListenerOptions = { port: 9096, @@ -100,13 +92,13 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { }, }; - const kafkaContainer = await new KafkaContainer(IMAGE) + await using _ = await new KafkaContainer(IMAGE) .withNetwork(network) .withNetworkAliases("kafka") .withSaslSslListener(saslConfig) .start(); - const kafkaCliContainer = await new GenericContainer(IMAGE) + await using kafkaCliContainer = await new GenericContainer(IMAGE) .withNetwork(network) .withCommand(["bash", "-c", "sleep infinity"]) .withCopyFilesToContainer([ @@ -141,8 +133,5 @@ describe("KafkaContainer", { timeout: 240_000 }, () => { expect(exitCode).toBe(0); expect(output).toContain("test-topic"); - - await kafkaCliContainer.stop(); - await kafkaContainer.stop(); }); }); diff --git a/packages/modules/kurrentdb/src/kurrentdb-container.test.ts b/packages/modules/kurrentdb/src/kurrentdb-container.test.ts index 544e7618b..5c2ed9a13 100644 --- a/packages/modules/kurrentdb/src/kurrentdb-container.test.ts +++ b/packages/modules/kurrentdb/src/kurrentdb-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe.sequential("KurrentDbContainer", { timeout: 240_000 }, () => { // startContainer { it("should execute write and read", async () => { - const container = await new KurrentDbContainer(IMAGE).start(); + await using container = await new KurrentDbContainer(IMAGE).start(); const client = KurrentDBClient.connectionString(container.getConnectionString()); @@ -41,14 +41,13 @@ describe.sequential("KurrentDbContainer", { timeout: 240_000 }, () => { }), ]); - await container.stop(); await client.dispose(); }); // } // usingStandardProjections { it("should use built-in projections", async () => { - const container = await new KurrentDbContainer(IMAGE).start(); + await using container = await new KurrentDbContainer(IMAGE).start(); const client = KurrentDBClient.connectionString(container.getConnectionString()); await client.appendToStream("Todo-1", [ @@ -86,7 +85,6 @@ describe.sequential("KurrentDbContainer", { timeout: 240_000 }, () => { }) ); await stream.unsubscribe(); - await container.stop(); await client.dispose(); }); // } diff --git a/packages/modules/localstack/src/localstack-container.test.ts b/packages/modules/localstack/src/localstack-container.test.ts index 797a69bfa..21cdaa4b2 100644 --- a/packages/modules/localstack/src/localstack-container.test.ts +++ b/packages/modules/localstack/src/localstack-container.test.ts @@ -19,7 +19,7 @@ const runAwsCliAgainstDockerNetworkContainer = async ( describe("LocalStackContainer", { timeout: 180_000 }, () => { // createS3Bucket { it("should create a S3 bucket", async () => { - const container = await new LocalstackContainer(IMAGE).start(); + await using container = await new LocalstackContainer(IMAGE).start(); const client = new S3Client({ endpoint: container.getConnectionUri(), @@ -39,20 +39,18 @@ describe("LocalStackContainer", { timeout: 180_000 }, () => { expect(createBucketResponse.$metadata.httpStatusCode).toEqual(200); const headBucketResponse = await client.send(new HeadBucketCommand(input)); expect(headBucketResponse.$metadata.httpStatusCode).toEqual(200); - - await container.stop(); }); // } it("should use custom network", async () => { - const network = await new Network().start(); - const container = await new LocalstackContainer(IMAGE) + await using network = await new Network().start(); + await using _ = await new LocalstackContainer(IMAGE) .withNetwork(network) .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL .withEnvironment({ SQS_ENDPOINT_STRATEGY: "path" }) .start(); - const awsCliInDockerNetwork = await new GenericContainer("amazon/aws-cli:2.7.27") + await using awsCliInDockerNetwork = await new GenericContainer("amazon/aws-cli:2.7.27") .withNetwork(network) .withEntrypoint(["bash"]) .withCommand(["-c", "sleep infinity"]) @@ -68,13 +66,10 @@ describe("LocalStackContainer", { timeout: 180_000 }, () => { awsCliInDockerNetwork ); expect(response).toContain(`http://localstack:${LOCALSTACK_PORT}`); - await container.stop(); - await awsCliInDockerNetwork.stop(); - await network.stop(); }); it("should not override LOCALSTACK_HOST assignment", async () => { - const container = await new LocalstackContainer(IMAGE) + await using container = await new LocalstackContainer(IMAGE) .withEnvironment({ LOCALSTACK_HOST: "myhost" }) .withNetworkAliases("myalias") .start(); @@ -82,32 +77,26 @@ describe("LocalStackContainer", { timeout: 180_000 }, () => { const { output, exitCode } = await container.exec(["printenv", "LOCALSTACK_HOST"]); expect(exitCode).toBe(0); expect(output).toContain("myhost"); - - await container.stop(); }); it("should override LOCALSTACK_HOST with last network alias", async () => { - const container = await new LocalstackContainer(IMAGE).withNetworkAliases("other", "myalias").start(); + await using container = await new LocalstackContainer(IMAGE).withNetworkAliases("other", "myalias").start(); const { output, exitCode } = await container.exec(["printenv", "LOCALSTACK_HOST"]); expect(exitCode).toBe(0); expect(output).toContain("myalias"); - - await container.stop(); }); it("should assign LOCALSTACK_HOST to localhost", async () => { - const container = await new LocalstackContainer(IMAGE).start(); + await using container = await new LocalstackContainer(IMAGE).start(); const { output, exitCode } = await container.exec(["printenv", "LOCALSTACK_HOST"]); expect(exitCode).toBe(0); expect(output).toContain("localhost"); - - await container.stop(); }); it("should add LAMBDA_DOCKER_FLAGS with sessionId label", async () => { - const container = await new LocalstackContainer(IMAGE).start(); + await using container = await new LocalstackContainer(IMAGE).start(); const sessionId = container.getLabels()[LABEL_TESTCONTAINERS_SESSION_ID]; const { output, exitCode } = await container.exec(["printenv", "LAMBDA_DOCKER_FLAGS"]); @@ -116,7 +105,7 @@ describe("LocalStackContainer", { timeout: 180_000 }, () => { }); it("should concatenate sessionId label to LAMBDA_DOCKER_FLAGS", async () => { - const container = await new LocalstackContainer(IMAGE) + await using container = await new LocalstackContainer(IMAGE) .withEnvironment({ LAMBDA_DOCKER_FLAGS: `-l mylabel=myvalue`, }) diff --git a/packages/modules/mariadb/src/mariadb-container.test.ts b/packages/modules/mariadb/src/mariadb-container.test.ts index 21078a817..49b5124d6 100644 --- a/packages/modules/mariadb/src/mariadb-container.test.ts +++ b/packages/modules/mariadb/src/mariadb-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("MariaDb", { timeout: 240_000 }, () => { // connect { it("should connect and execute query", async () => { - const container = await new MariaDbContainer(IMAGE).start(); + await using container = await new MariaDbContainer(IMAGE).start(); const client = await mariadb.createConnection({ host: container.getHost(), @@ -21,7 +21,6 @@ describe("MariaDb", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: 1 }]); await client.end(); - await container.stop(); }); // } @@ -32,7 +31,7 @@ describe("MariaDb", { timeout: 240_000 }, () => { const database = "testDB"; // Test non-root user - const container = await new MariaDbContainer(IMAGE) + await using container = await new MariaDbContainer(IMAGE) .withUsername(username) .withUserPassword(password) .withDatabase(database) @@ -40,20 +39,21 @@ describe("MariaDb", { timeout: 240_000 }, () => { expect(container.getConnectionUri()).toEqual( `mariadb://${username}:${password}@${container.getHost()}:${container.getPort()}/${database}` ); - await container.stop(); // Test root user - const rootContainer = await new MariaDbContainer(IMAGE).withRootPassword(password).withDatabase(database).start(); + await using rootContainer = await new MariaDbContainer(IMAGE) + .withRootPassword(password) + .withDatabase(database) + .start(); expect(rootContainer.getConnectionUri(true)).toEqual( `mariadb://root:${password}@${rootContainer.getHost()}:${rootContainer.getPort()}/${database}` ); - await rootContainer.stop(); }); // } // setDatabase { it("should set database", async () => { - const container = await new MariaDbContainer(IMAGE).withDatabase("customDatabase").start(); + await using container = await new MariaDbContainer(IMAGE).withDatabase("customDatabase").start(); const client = await mariadb.createConnection({ host: container.getHost(), @@ -67,13 +67,12 @@ describe("MariaDb", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: "customDatabase" }]); await client.end(); - await container.stop(); }); // } // setUsername { it("should set username", async () => { - const container = await new MariaDbContainer(IMAGE).withUsername("customUsername").start(); + await using container = await new MariaDbContainer(IMAGE).withUsername("customUsername").start(); const client = await mariadb.createConnection({ host: container.getHost(), @@ -87,13 +86,12 @@ describe("MariaDb", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: "customUsername@%" }]); await client.end(); - await container.stop(); }); // } // insertAndFetchData { it("should create a table, insert a row, and fetch that row", async () => { - const container = await new MariaDbContainer(IMAGE).start(); + await using container = await new MariaDbContainer(IMAGE).start(); const client = await mariadb.createConnection({ host: container.getHost(), @@ -123,12 +121,11 @@ describe("MariaDb", { timeout: 240_000 }, () => { expect(user).toEqual({ id: expect.any(Number), name, email }); await client.end(); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new MariaDbContainer(IMAGE).start(); + await using container = await new MariaDbContainer(IMAGE).start(); await container.restart(); const client = await mariadb.createConnection({ @@ -143,6 +140,5 @@ describe("MariaDb", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: 1 }]); await client.end(); - await container.stop(); }); }); diff --git a/packages/modules/minio/src/minio-container.test.ts b/packages/modules/minio/src/minio-container.test.ts index b636a444e..15650ac7a 100644 --- a/packages/modules/minio/src/minio-container.test.ts +++ b/packages/modules/minio/src/minio-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("MinIO", { timeout: 240_000 }, () => { // connectWithDefaultCredentials { it("should connect and upload a file", async () => { - const container = await new MinioContainer(IMAGE).start(); + await using container = await new MinioContainer(IMAGE).start(); const minioClient = new minio.Client({ endPoint: container.getHost(), @@ -30,14 +30,15 @@ describe("MinIO", { timeout: 240_000 }, () => { .catch(() => false); expect(objectExists).toBeTruthy(); - - await container.stop(); }); // } // connectWithCustomCredentials { it("should work with custom credentials", async () => { - const container = await new MinioContainer(IMAGE).withUsername("AzureDiamond").withPassword("hunter2!").start(); + await using container = await new MinioContainer(IMAGE) + .withUsername("AzureDiamond") + .withPassword("hunter2!") + .start(); const minioClient = new minio.Client({ endPoint: container.getHost(), @@ -54,8 +55,6 @@ describe("MinIO", { timeout: 240_000 }, () => { const bucketExits = await minioClient.bucketExists("test-bucket"); expect(bucketExits).toBeTruthy(); - - await container.stop(); }); // } }); diff --git a/packages/modules/mockserver/src/mockserver-container.test.ts b/packages/modules/mockserver/src/mockserver-container.test.ts index 392261923..8d91d6b85 100644 --- a/packages/modules/mockserver/src/mockserver-container.test.ts +++ b/packages/modules/mockserver/src/mockserver-container.test.ts @@ -8,7 +8,7 @@ const IMAGE = getImage(__dirname); describe("MockserverContainer", { timeout: 240_000 }, () => { // startContainer { it("should start and accept mocks", async () => { - const container = await new MockserverContainer(IMAGE).start(); + await using container = await new MockserverContainer(IMAGE).start(); const client = mockServerClient(container.getHost(), container.getMockserverPort()); const url = container.getUrl(); @@ -33,15 +33,14 @@ describe("MockserverContainer", { timeout: 240_000 }, () => { // } it("should return an https url", async () => { - const container = await new MockserverContainer(IMAGE).start(); + await using container = await new MockserverContainer(IMAGE).start(); const secureUrl = container.getSecureUrl(); - await container.stop(); expect(secureUrl.startsWith("https://")).to.equal(true, `${secureUrl} does not start with https://`); }); // httpsRequests { it("should respond to https requests", async () => { - const container = await new MockserverContainer(IMAGE).start(); + await using container = await new MockserverContainer(IMAGE).start(); const client = mockServerClient(container.getHost(), container.getMockserverPort()); await client.mockAnyResponse({ @@ -62,8 +61,6 @@ describe("MockserverContainer", { timeout: 240_000 }, () => { expect(response.statusCode).toBe(200); expect(response.text).toBe("bar"); - - await container.stop(); }); // } }); diff --git a/packages/modules/mongodb/src/mongodb-container.test.ts b/packages/modules/mongodb/src/mongodb-container.test.ts index b038a64dd..aed4cec34 100644 --- a/packages/modules/mongodb/src/mongodb-container.test.ts +++ b/packages/modules/mongodb/src/mongodb-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("MongodbContainer", { timeout: 240_000 }, () => { // connect4 { it("should work using default version 4.0.1", async () => { - const mongodbContainer = await new MongoDBContainer(IMAGE).start(); + await using mongodbContainer = await new MongoDBContainer(IMAGE).start(); // directConnection: true is required as the testcontainer is created as a MongoDB Replica Set. const db = mongoose.createConnection(mongodbContainer.getConnectionString(), { directConnection: true }); @@ -31,13 +31,12 @@ describe("MongodbContainer", { timeout: 240_000 }, () => { ).toEqual(obj); await mongoose.disconnect(); - await mongodbContainer.stop(); }); // } // connect6 { it("should work using version 6.0.1", async () => { - const mongodbContainer = await new MongoDBContainer("mongo:6.0.1").start(); + await using mongodbContainer = await new MongoDBContainer("mongo:6.0.1").start(); // directConnection: true is required as the testcontainer is created as a MongoDB Replica Set. const db = mongoose.createConnection(mongodbContainer.getConnectionString(), { directConnection: true }); @@ -61,7 +60,6 @@ describe("MongodbContainer", { timeout: 240_000 }, () => { ).toEqual(obj); await mongoose.disconnect(); - await mongodbContainer.stop(); }); // } }); diff --git a/packages/modules/mssqlserver/src/mssqlserver-container.test.ts b/packages/modules/mssqlserver/src/mssqlserver-container.test.ts index 813493854..9623fc42d 100644 --- a/packages/modules/mssqlserver/src/mssqlserver-container.test.ts +++ b/packages/modules/mssqlserver/src/mssqlserver-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("MSSqlServerContainer", { timeout: 180_000 }, () => { // connect { it("should connect and return a query result", async () => { - const container = await new MSSQLServerContainer(IMAGE).acceptLicense().start(); + await using container = await new MSSQLServerContainer(IMAGE).acceptLicense().start(); const sqlConfig: config = { user: container.getUsername(), @@ -31,13 +31,12 @@ describe("MSSqlServerContainer", { timeout: 180_000 }, () => { expect(recordset).toStrictEqual([{ "": 1 }]); await connection.close(); - await container.stop(); }); // } // uriConnect { it("should connect and return a query result with database URI", async () => { - const container = await new MSSQLServerContainer(IMAGE).acceptLicense().start(); + await using container = await new MSSQLServerContainer(IMAGE).acceptLicense().start(); const connectionString = container.getConnectionUri(); const connection = await sql.connect(connectionString); @@ -46,13 +45,12 @@ describe("MSSqlServerContainer", { timeout: 180_000 }, () => { expect(recordset).toStrictEqual([{ "": 1 }]); await connection.close(); - await container.stop(); }); // } // validPassword { it("should connect and return a query result with valid custom password", async () => { - const container = await new MSSQLServerContainer(IMAGE).acceptLicense().withPassword("I!@M#$eCur3").start(); + await using container = await new MSSQLServerContainer(IMAGE).acceptLicense().withPassword("I!@M#$eCur3").start(); const connectionString = container.getConnectionUri(); const connection = await sql.connect(connectionString); @@ -61,7 +59,6 @@ describe("MSSqlServerContainer", { timeout: 180_000 }, () => { expect(recordset).toStrictEqual([{ "": 1 }]); await connection.close(); - await container.stop(); }); // } @@ -76,7 +73,7 @@ describe("MSSqlServerContainer", { timeout: 180_000 }, () => { // expressEdition { it("should start db with express edition", async () => { - const container = await new MSSQLServerContainer(IMAGE) + await using container = await new MSSQLServerContainer(IMAGE) .withWaitForMessage(/.*Attribute synchronization manager initialized*/) .acceptLicense() .withEnvironment({ MSSQL_PID: "Express" }) @@ -96,8 +93,6 @@ describe("MSSqlServerContainer", { timeout: 180_000 }, () => { expect(exitCode).toBe(0); expect(output).toContain("Express Edition"); - - await container.stop(); }); // } }); diff --git a/packages/modules/mysql/src/mysql-container.test.ts b/packages/modules/mysql/src/mysql-container.test.ts index cc6b80484..a28226b67 100644 --- a/packages/modules/mysql/src/mysql-container.test.ts +++ b/packages/modules/mysql/src/mysql-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("MySqlContainer", { timeout: 240_000 }, () => { // connect { it("should connect and execute query", async () => { - const container = await new MySqlContainer(IMAGE).start(); + await using container = await new MySqlContainer(IMAGE).start(); const client = await createConnection({ host: container.getHost(), @@ -21,7 +21,6 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: 1 }]); await client.end(); - await container.stop(); }); // } @@ -32,7 +31,7 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { const database = "testDB"; // Test non-root user - const container = await new MySqlContainer(IMAGE) + await using container = await new MySqlContainer(IMAGE) .withUsername(username) .withUserPassword(password) .withDatabase(database) @@ -40,20 +39,21 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { expect(container.getConnectionUri()).toEqual( `mysql://${username}:${password}@${container.getHost()}:${container.getPort()}/${database}` ); - await container.stop(); // Test root user - const rootContainer = await new MySqlContainer(IMAGE).withRootPassword(password).withDatabase(database).start(); + await using rootContainer = await new MySqlContainer(IMAGE) + .withRootPassword(password) + .withDatabase(database) + .start(); expect(rootContainer.getConnectionUri(true)).toEqual( `mysql://root:${password}@${rootContainer.getHost()}:${rootContainer.getPort()}/${database}` ); - await rootContainer.stop(); }); // } // setDatabase { it("should set database", async () => { - const container = await new MySqlContainer(IMAGE).withDatabase("customDatabase").start(); + await using container = await new MySqlContainer(IMAGE).withDatabase("customDatabase").start(); const client = await createConnection({ host: container.getHost(), @@ -67,13 +67,12 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: "customDatabase" }]); await client.end(); - await container.stop(); }); // } // setUsername { it("should set username", async () => { - const container = await new MySqlContainer(IMAGE).withUsername("customUsername").start(); + await using container = await new MySqlContainer(IMAGE).withUsername("customUsername").start(); const client = await createConnection({ host: container.getHost(), @@ -87,22 +86,19 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: "customUsername@%" }]); await client.end(); - await container.stop(); }); // } // executeQuery { it("should execute a query and return the result", async () => { - const container = await new MySqlContainer(IMAGE).start(); + await using container = await new MySqlContainer(IMAGE).start(); const queryResult = await container.executeQuery("SELECT 1 as res"); expect(queryResult).toEqual(expect.stringContaining("res\n1\n")); - - await container.stop(); }); it("should execute a query as root user", async () => { - const container = await new MySqlContainer(IMAGE).withUsername("customUsername").start(); + await using container = await new MySqlContainer(IMAGE).withUsername("customUsername").start(); // Test non-root user const queryResult = await container.executeQuery("SELECT CURRENT_USER() as user"); @@ -111,13 +107,11 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { // Test root user const rootQueryResult = await container.executeQuery("SELECT CURRENT_USER() as user", [], true); expect(rootQueryResult).toEqual(expect.stringContaining("user\nroot")); - - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new MySqlContainer(IMAGE).start(); + await using container = await new MySqlContainer(IMAGE).start(); await container.restart(); const client = await createConnection({ @@ -132,6 +126,5 @@ describe("MySqlContainer", { timeout: 240_000 }, () => { expect(rows).toEqual([{ res: 1 }]); await client.end(); - await container.stop(); }); }); diff --git a/packages/modules/nats/src/nats-container.test.ts b/packages/modules/nats/src/nats-container.test.ts index ef73b2a5a..9a5427d30 100644 --- a/packages/modules/nats/src/nats-container.test.ts +++ b/packages/modules/nats/src/nats-container.test.ts @@ -8,7 +8,7 @@ const IMAGE = getImage(__dirname); describe("NatsContainer", { timeout: 180_000 }, () => { // connect { it("should start, connect and close", async () => { - const container = await new NatsContainer(IMAGE).start(); + await using container = await new NatsContainer(IMAGE).start(); // establish connection const nc = await connect(container.getConnectionOptions()); @@ -17,13 +17,11 @@ describe("NatsContainer", { timeout: 180_000 }, () => { // check if the close was OK const err = await nc.closed(); expect(err).toBe(undefined); - - await container.stop(); }); // } it("should start, connect and close using scratch image", async () => { - const container = await new NatsContainer("nats:2.11").start(); + await using container = await new NatsContainer("nats:2.11").start(); // establish connection const nc = await connect(container.getConnectionOptions()); @@ -32,8 +30,6 @@ describe("NatsContainer", { timeout: 180_000 }, () => { // check if the close was OK const err = await nc.closed(); expect(err).toBe(undefined); - - await container.stop(); }); // pubsub { @@ -41,7 +37,7 @@ describe("NatsContainer", { timeout: 180_000 }, () => { const SUBJECT = "HELLO"; const PAYLOAD = "WORLD"; - const container = await new NatsContainer(IMAGE).start(); + await using container = await new NatsContainer(IMAGE).start(); const nc = await connect(container.getConnectionOptions()); const TE = new TextEncoder(); const TD = new TextDecoder(); @@ -63,15 +59,13 @@ describe("NatsContainer", { timeout: 180_000 }, () => { await nc.close(); const err = await nc.closed(); expect(err).toBe(undefined); - - await container.stop(); }); // } // credentials { it("should start with alternative username and password ", async () => { // set username and password like this - const container = await new NatsContainer(IMAGE).withPass("1234").withUsername("George").start(); + await using container = await new NatsContainer(IMAGE).withPass("1234").withUsername("George").start(); const nc = await connect(container.getConnectionOptions()); // close the connection @@ -79,15 +73,13 @@ describe("NatsContainer", { timeout: 180_000 }, () => { // check if the close was OK const err = await nc.closed(); expect(err).toBe(undefined); - - await container.stop(); }); // } // jetstream { it("should start with JetStream ", async () => { // enable JetStream - const container = await new NatsContainer(IMAGE).withJetStream().start(); + await using container = await new NatsContainer(IMAGE).withJetStream().start(); const nc = await connect(container.getConnectionOptions()); @@ -99,12 +91,10 @@ describe("NatsContainer", { timeout: 180_000 }, () => { // check if the close was OK const err = await nc.closed(); expect(err).toBe(undefined); - - await container.stop(); }); it("should fail without JetStream ", async () => { - const container = await new NatsContainer(IMAGE).start(); + await using container = await new NatsContainer(IMAGE).start(); const nc = await connect(container.getConnectionOptions()); @@ -116,8 +106,6 @@ describe("NatsContainer", { timeout: 180_000 }, () => { // check if the close was OK const err = await nc.closed(); expect(err).toBe(undefined); - - await container.stop(); }); // } @@ -125,7 +113,7 @@ describe("NatsContainer", { timeout: 180_000 }, () => { // for the complete list of available arguments see: // See Command Line Options section inside [NATS docker image documentation](https://hub.docker.com/_/nats) async function outputVersionAndExit() { - const container = await new NatsContainer(IMAGE).withArg("version", "").start(); + await using container = await new NatsContainer(IMAGE).withArg("version", "").start(); await connect(container.getConnectionOptions()); } diff --git a/packages/modules/neo4j/src/neo4j-container.test.ts b/packages/modules/neo4j/src/neo4j-container.test.ts index 63d6894c4..32f31cffa 100755 --- a/packages/modules/neo4j/src/neo4j-container.test.ts +++ b/packages/modules/neo4j/src/neo4j-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("Neo4jContainer", { timeout: 180_000 }, () => { // createNode { it("should create a person node", async () => { - const container = await new Neo4jContainer(IMAGE).start(); + await using container = await new Neo4jContainer(IMAGE).start(); const driver = neo4j.driver( container.getBoltUri(), neo4j.auth.basic(container.getUsername(), container.getPassword()) @@ -22,13 +22,12 @@ describe("Neo4jContainer", { timeout: 180_000 }, () => { await session.close(); await driver.close(); - await container.stop(); }); // } // v5DefaultPassword { it("should connect to neo4j:v5 with default password", async () => { - const container = await new Neo4jContainer("neo4j:5.23.0").start(); + await using container = await new Neo4jContainer("neo4j:5.23.0").start(); const driver = neo4j.driver( container.getBoltUri(), neo4j.auth.basic(container.getUsername(), container.getPassword()) @@ -43,13 +42,12 @@ describe("Neo4jContainer", { timeout: 180_000 }, () => { await session.close(); await driver.close(); - await container.stop(); }); // } // setPassword { it("should connect with custom password", async () => { - const container = await new Neo4jContainer(IMAGE).withPassword("xyz1234@!").start(); + await using container = await new Neo4jContainer(IMAGE).withPassword("xyz1234@!").start(); const driver = neo4j.driver( container.getBoltUri(), neo4j.auth.basic(container.getUsername(), container.getPassword()) @@ -64,13 +62,12 @@ describe("Neo4jContainer", { timeout: 180_000 }, () => { await session.close(); await driver.close(); - await container.stop(); }); // } // apoc { it("should have APOC plugin installed", async () => { - const container = await new Neo4jContainer(IMAGE).withApoc().withStartupTimeout(120_000).start(); + await using container = await new Neo4jContainer(IMAGE).withApoc().withStartupTimeout(120_000).start(); const driver = neo4j.driver( container.getBoltUri(), neo4j.auth.basic(container.getUsername(), container.getPassword()) @@ -83,13 +80,12 @@ describe("Neo4jContainer", { timeout: 180_000 }, () => { await session.close(); await driver.close(); - await container.stop(); }); // } // pluginsList { it("should work with plugin list", async () => { - const container = await new Neo4jContainer("neo4j:5.26.5") + await using container = await new Neo4jContainer("neo4j:5.26.5") .withPlugins([Neo4jPlugin.APOC_EXTENDED, Neo4jPlugin.GRAPH_DATA_SCIENCE]) .withStartupTimeout(120_000) .start(); @@ -113,7 +109,6 @@ describe("Neo4jContainer", { timeout: 180_000 }, () => { await session.close(); await driver.close(); - await container.stop(); }); // } }); diff --git a/packages/modules/ollama/src/ollama-container.test.ts b/packages/modules/ollama/src/ollama-container.test.ts index c58d7cda6..55de98d2c 100644 --- a/packages/modules/ollama/src/ollama-container.test.ts +++ b/packages/modules/ollama/src/ollama-container.test.ts @@ -7,17 +7,16 @@ const IMAGE = getImage(__dirname); describe("OllamaContainer", { timeout: 180_000 }, () => { it("should run ollama with default config", async () => { // container { - const container = await new OllamaContainer(IMAGE).start(); + await using container = await new OllamaContainer(IMAGE).start(); // } const response = await fetch(`${container.getEndpoint()}/api/version`); expect(response.status).toEqual(200); const body = (await response.json()) as { version: string }; expect(body.version).toEqual(ImageName.fromString(IMAGE).tag); - await container.stop(); }); it.skip("download model and commit to image", async () => { - const container = await new OllamaContainer(IMAGE).start(); + await using container = await new OllamaContainer(IMAGE).start(); // pullModel { const execResult = await container.exec(["ollama", "pull", "all-minilm"]); // } @@ -31,15 +30,13 @@ describe("OllamaContainer", { timeout: 180_000 }, () => { // commitToImage { await container.commitToImage(newImageName); // } - await container.stop(); // substitute { - const newContainer = await new OllamaContainer(newImageName).start(); + await using newContainer = await new OllamaContainer(newImageName).start(); // } const response2 = await fetch(`${newContainer.getEndpoint()}/api/tags`); expect(response2.status).toEqual(200); const body2 = (await response2.json()) as { models: { name: string }[] }; expect(body2.models[0].name).toContain("all-minilm"); - await newContainer.stop(); }); }); diff --git a/packages/modules/opensearch/src/opensearch-container.test.ts b/packages/modules/opensearch/src/opensearch-container.test.ts index 3c3eb85fd..bff69a99d 100644 --- a/packages/modules/opensearch/src/opensearch-container.test.ts +++ b/packages/modules/opensearch/src/opensearch-container.test.ts @@ -8,7 +8,7 @@ const images = ["opensearchproject/opensearch:2.19.2", IMAGE]; describe("OpenSearchContainer", { timeout: 180_000 }, () => { // createIndex { it.each(images)("should create an index with %s", async (image) => { - const container = await new OpenSearchContainer(image).start(); + await using container = await new OpenSearchContainer(image).start(); const client = new Client({ node: container.getHttpUrl(), auth: { @@ -24,13 +24,12 @@ describe("OpenSearchContainer", { timeout: 180_000 }, () => { await client.indices.create({ index: "people" }); const existsResponse = await client.indices.exists({ index: "people" }); expect(existsResponse.body).toBe(true); - await container.stop(); }); // } // indexDocument { it("should index a document", async () => { - const container = await new OpenSearchContainer(IMAGE).start(); + await using container = await new OpenSearchContainer(IMAGE).start(); const client = new Client({ node: container.getHttpUrl(), auth: { @@ -52,12 +51,11 @@ describe("OpenSearchContainer", { timeout: 180_000 }, () => { const getResponse = await client.get({ index: "people", id: document.id }); expect(getResponse.body._source).toStrictEqual(document); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new OpenSearchContainer(IMAGE).start(); + await using container = await new OpenSearchContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -74,7 +72,6 @@ describe("OpenSearchContainer", { timeout: 180_000 }, () => { await client.indices.create({ index: "people" }); const existsResponse = await client.indices.exists({ index: "people" }); expect(existsResponse.body).toBe(true); - await container.stop(); }); it("should throw when given an invalid password", () => { @@ -83,7 +80,7 @@ describe("OpenSearchContainer", { timeout: 180_000 }, () => { // customPassword { it("should set custom password", async () => { - const container = await new OpenSearchContainer(IMAGE).withPassword("Str0ng!Passw0rd2025").start(); + await using container = await new OpenSearchContainer(IMAGE).withPassword("Str0ng!Passw0rd2025").start(); const client = new Client({ node: container.getHttpUrl(), @@ -99,7 +96,6 @@ describe("OpenSearchContainer", { timeout: 180_000 }, () => { await client.indices.create({ index: "people" }); const existsResponse = await client.indices.exists({ index: "people" }); expect(existsResponse.body).toBe(true); - await container.stop(); }); // } }); diff --git a/packages/modules/postgresql/src/pgvector-container.test.ts b/packages/modules/postgresql/src/pgvector-container.test.ts index 9b3960fa4..eaf79f210 100644 --- a/packages/modules/postgresql/src/pgvector-container.test.ts +++ b/packages/modules/postgresql/src/pgvector-container.test.ts @@ -6,7 +6,7 @@ const IMAGE = getImage(__dirname); describe("PgvectorContainer", { timeout: 180_000 }, () => { it("should work", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); const client = new Client({ host: container.getHost(), @@ -21,11 +21,10 @@ describe("PgvectorContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); it("should restart", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -41,6 +40,5 @@ describe("PgvectorContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); }); diff --git a/packages/modules/postgresql/src/postgis-container.test.ts b/packages/modules/postgresql/src/postgis-container.test.ts index 8b40e5a7e..bbee13e98 100644 --- a/packages/modules/postgresql/src/postgis-container.test.ts +++ b/packages/modules/postgresql/src/postgis-container.test.ts @@ -6,7 +6,7 @@ const IMAGE = getImage(__dirname); describe("PostgisContainer", { timeout: 180_000 }, () => { it("should work", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); const client = new Client({ host: container.getHost(), @@ -21,11 +21,10 @@ describe("PostgisContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); it("should restart", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -41,6 +40,5 @@ describe("PostgisContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); }); diff --git a/packages/modules/postgresql/src/postgresql-container-snapshot.test.ts b/packages/modules/postgresql/src/postgresql-container-snapshot.test.ts index 4636c7c03..135631340 100644 --- a/packages/modules/postgresql/src/postgresql-container-snapshot.test.ts +++ b/packages/modules/postgresql/src/postgresql-container-snapshot.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("PostgreSqlContainer snapshot and restore", { timeout: 180_000 }, () => { // createAndRestoreFromSnapshot { it("should create and restore from snapshot", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); // Connect to the database let client = new Client({ @@ -58,12 +58,11 @@ describe("PostgreSqlContainer snapshot and restore", { timeout: 180_000 }, () => expect(result.rows[0].name).toEqual("initial data"); await client.end(); - await container.stop(); }); // } it("should use custom snapshot name", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); const customSnapshotName = "my_custom_snapshot"; // Connect to the database @@ -109,11 +108,10 @@ describe("PostgreSqlContainer snapshot and restore", { timeout: 180_000 }, () => expect(result.rows[0].name).toEqual("initial data"); await client.end(); - await container.stop(); }); it("should handle multiple snapshots", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); // Connect to the database let client = new Client({ @@ -192,11 +190,10 @@ describe("PostgreSqlContainer snapshot and restore", { timeout: 180_000 }, () => expect(result.rows[0].name).toEqual("data for snapshot 2"); await client.end(); - await container.stop(); }); it("should throw an error when trying to snapshot postgres system database", async () => { - const container = await new PostgreSqlContainer(IMAGE).withDatabase("postgres").start(); + await using container = await new PostgreSqlContainer(IMAGE).withDatabase("postgres").start(); await expect(container.snapshot()).rejects.toThrow( "Snapshot feature is not supported when using the postgres system database" @@ -205,7 +202,5 @@ describe("PostgreSqlContainer snapshot and restore", { timeout: 180_000 }, () => await expect(container.restoreSnapshot()).rejects.toThrow( "Snapshot feature is not supported when using the postgres system database" ); - - await container.stop(); }); }); diff --git a/packages/modules/postgresql/src/postgresql-container.test.ts b/packages/modules/postgresql/src/postgresql-container.test.ts index 826324e56..303d3cd7e 100644 --- a/packages/modules/postgresql/src/postgresql-container.test.ts +++ b/packages/modules/postgresql/src/postgresql-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("PostgreSqlContainer", { timeout: 180_000 }, () => { // connect { it("should connect and return a query result", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); const client = new Client({ host: container.getHost(), @@ -22,13 +22,12 @@ describe("PostgreSqlContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); // } // uriConnect { it("should work with database URI", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); const client = new Client({ connectionString: container.getConnectionUri(), @@ -39,13 +38,12 @@ describe("PostgreSqlContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); // } // setDatabase { it("should set database", async () => { - const container = await new PostgreSqlContainer(IMAGE).withDatabase("customDatabase").start(); + await using container = await new PostgreSqlContainer(IMAGE).withDatabase("customDatabase").start(); const client = new Client({ host: container.getHost(), @@ -60,13 +58,12 @@ describe("PostgreSqlContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ current_database: "customDatabase" }); await client.end(); - await container.stop(); }); // } // setUsername { it("should set username", async () => { - const container = await new PostgreSqlContainer(IMAGE).withUsername("customUsername").start(); + await using container = await new PostgreSqlContainer(IMAGE).withUsername("customUsername").start(); const client = new Client({ host: container.getHost(), @@ -81,12 +78,11 @@ describe("PostgreSqlContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ current_user: "customUsername" }); await client.end(); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -102,7 +98,6 @@ describe("PostgreSqlContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); it("should allow custom healthcheck", async () => { diff --git a/packages/modules/postgresql/src/timescale-container.test.ts b/packages/modules/postgresql/src/timescale-container.test.ts index 988115a2b..253eba3df 100644 --- a/packages/modules/postgresql/src/timescale-container.test.ts +++ b/packages/modules/postgresql/src/timescale-container.test.ts @@ -6,7 +6,7 @@ const IMAGE = getImage(__dirname); describe("TimescaleContainer", { timeout: 180_000 }, () => { it("should work", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); const client = new Client({ host: container.getHost(), @@ -21,11 +21,10 @@ describe("TimescaleContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); it("should restart", async () => { - const container = await new PostgreSqlContainer(IMAGE).start(); + await using container = await new PostgreSqlContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -41,6 +40,5 @@ describe("TimescaleContainer", { timeout: 180_000 }, () => { expect(result.rows[0]).toEqual({ "?column?": 1 }); await client.end(); - await container.stop(); }); }); diff --git a/packages/modules/qdrant/src/qdrant-container.test.ts b/packages/modules/qdrant/src/qdrant-container.test.ts index 0908b5ee5..323d8429a 100644 --- a/packages/modules/qdrant/src/qdrant-container.test.ts +++ b/packages/modules/qdrant/src/qdrant-container.test.ts @@ -9,13 +9,11 @@ const IMAGE = getImage(__dirname); describe("QdrantContainer", { timeout: 100_000 }, () => { // connectQdrantSimple { it("should connect to the client", async () => { - const container = await new QdrantContainer(IMAGE).start(); + await using container = await new QdrantContainer(IMAGE).start(); const client = new QdrantClient({ url: `http://${container.getRestHostAddress()}` }); expect((await client.getCollections()).collections.length).toBe(0); - - await container.stop(); }); // } @@ -23,20 +21,18 @@ describe("QdrantContainer", { timeout: 100_000 }, () => { it("should work with valid API keys", async () => { const apiKey = crypto.randomUUID(); - const container = await new QdrantContainer(IMAGE).withApiKey(apiKey).start(); + await using container = await new QdrantContainer(IMAGE).withApiKey(apiKey).start(); const client = new QdrantClient({ url: `http://${container.getRestHostAddress()}`, apiKey }); expect((await client.getCollections()).collections.length).toBe(0); - - await container.stop(); }); // } it("should fail for invalid API keys", async () => { const apiKey = crypto.randomUUID(); - const container = await new QdrantContainer(IMAGE).withApiKey(apiKey).start(); + await using container = await new QdrantContainer(IMAGE).withApiKey(apiKey).start(); const client = new QdrantClient({ url: `http://${container.getRestHostAddress()}`, @@ -44,26 +40,22 @@ describe("QdrantContainer", { timeout: 100_000 }, () => { }); await expect(client.getCollections()).rejects.toThrow("Unauthorized"); - - await container.stop(); }); // connectQdrantWithConfig { it("should work with config files - valid API key", async () => { - const container = await new QdrantContainer(IMAGE) + await using container = await new QdrantContainer(IMAGE) .withConfigFile(path.resolve(__dirname, "test_config.yaml")) .start(); const client = new QdrantClient({ url: `http://${container.getRestHostAddress()}`, apiKey: "SOME_TEST_KEY" }); expect((await client.getCollections()).collections.length).toBe(0); - - await container.stop(); }); // } it("should work with config files - invalid API key", async () => { - const container = await new QdrantContainer(IMAGE) + await using container = await new QdrantContainer(IMAGE) .withConfigFile(path.resolve(__dirname, "test_config.yaml")) .start(); @@ -73,7 +65,5 @@ describe("QdrantContainer", { timeout: 100_000 }, () => { }); await expect(client.getCollections()).rejects.toThrow("Unauthorized"); - - await container.stop(); }); }); diff --git a/packages/modules/rabbitmq/src/rabbitmq-container.test.ts b/packages/modules/rabbitmq/src/rabbitmq-container.test.ts index 51048cc23..6a2c649c5 100644 --- a/packages/modules/rabbitmq/src/rabbitmq-container.test.ts +++ b/packages/modules/rabbitmq/src/rabbitmq-container.test.ts @@ -7,12 +7,10 @@ const IMAGE = getImage(__dirname); describe("RabbitMQContainer", { timeout: 240_000 }, () => { // start { it("should start, connect and close", async () => { - const rabbitMQContainer = await new RabbitMQContainer(IMAGE).start(); + await using rabbitMQContainer = await new RabbitMQContainer(IMAGE).start(); const connection = await amqp.connect(rabbitMQContainer.getAmqpUrl()); await connection.close(); - - await rabbitMQContainer.stop(); }); // } @@ -21,7 +19,7 @@ describe("RabbitMQContainer", { timeout: 240_000 }, () => { const USER = "user"; const PASSWORD = "password"; - const rabbitMQContainer = await new RabbitMQContainer(IMAGE) + await using rabbitMQContainer = await new RabbitMQContainer(IMAGE) .withEnvironment({ RABBITMQ_DEFAULT_USER: USER, RABBITMQ_DEFAULT_PASS: PASSWORD, @@ -35,8 +33,6 @@ describe("RabbitMQContainer", { timeout: 240_000 }, () => { }); await connection.close(); - - await rabbitMQContainer.stop(); }); // } @@ -45,7 +41,7 @@ describe("RabbitMQContainer", { timeout: 240_000 }, () => { const QUEUE = "test"; const PAYLOAD = "Hello World"; - const rabbitMQContainer = await new RabbitMQContainer(IMAGE).start(); + await using rabbitMQContainer = await new RabbitMQContainer(IMAGE).start(); const connection = await amqp.connect(rabbitMQContainer.getAmqpUrl()); const channel = await connection.createChannel(); @@ -62,8 +58,6 @@ describe("RabbitMQContainer", { timeout: 240_000 }, () => { await channel.close(); await connection.close(); - - await rabbitMQContainer.stop(); }, 20_000); // } }); diff --git a/packages/modules/redis/src/redis-container.test.ts b/packages/modules/redis/src/redis-container.test.ts index bb8df58a8..99e803d63 100644 --- a/packages/modules/redis/src/redis-container.test.ts +++ b/packages/modules/redis/src/redis-container.test.ts @@ -10,7 +10,7 @@ const IMAGE = getImage(__dirname); describe("RedisContainer", { timeout: 240_000 }, () => { // startContainer { it("should connect and execute set-get", async () => { - const container = await new RedisContainer(IMAGE).start(); + await using container = await new RedisContainer(IMAGE).start(); const client = await connectTo(container); @@ -18,12 +18,11 @@ describe("RedisContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); client.destroy(); - await container.stop(); }); // } it("should connect with password and execute set-get", async () => { - const container = await new RedisContainer(IMAGE).withPassword("test").start(); + await using container = await new RedisContainer(IMAGE).withPassword("test").start(); const client = await connectTo(container); @@ -31,13 +30,12 @@ describe("RedisContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); client.destroy(); - await container.stop(); }); // persistentData { it("should reconnect with volume and persistence data", async () => { const sourcePath = fs.mkdtempSync(path.join(os.tmpdir(), "redis-")); - const container = await new RedisContainer(IMAGE).withPassword("test").withPersistence(sourcePath).start(); + await using container = await new RedisContainer(IMAGE).withPassword("test").withPersistence(sourcePath).start(); let client = await connectTo(container); await client.set("key", "val"); @@ -47,7 +45,6 @@ describe("RedisContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); client.destroy(); - await container.stop(); try { fs.rmSync(sourcePath, { force: true, recursive: true }); } catch (e) { @@ -59,7 +56,7 @@ describe("RedisContainer", { timeout: 240_000 }, () => { // initial data import { it("should load initial data and can read it", async () => { - const container = await new RedisContainer(IMAGE) + await using container = await new RedisContainer(IMAGE) .withPassword("test") .withInitialData(path.join(__dirname, "initData.redis")) .start(); @@ -72,7 +69,6 @@ describe("RedisContainer", { timeout: 240_000 }, () => { expect(await client.get("user:002")).toBe(JSON.stringify(user)); client.destroy(); - await container.stop(); }); // } @@ -81,7 +77,7 @@ describe("RedisContainer", { timeout: 240_000 }, () => { const password = "testPassword"; // Test authentication - const container = await new RedisContainer(IMAGE).withPassword(password).start(); + await using container = await new RedisContainer(IMAGE).withPassword(password).start(); expect(container.getConnectionUrl()).toEqual(`redis://:${password}@${container.getHost()}:${container.getPort()}`); const client = await connectTo(container); @@ -90,24 +86,21 @@ describe("RedisContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); client.destroy(); - await container.stop(); }); // } // executeCommand { it("should execute container cmd and return the result", async () => { - const container = await new RedisContainer(IMAGE).start(); + await using container = await new RedisContainer(IMAGE).start(); const queryResult = await container.executeCliCmd("info", ["clients"]); expect(queryResult).toEqual(expect.stringContaining("connected_clients:1")); - - await container.stop(); }); // } // startWithRedisStack { it("should start with redis-stack-server and json module", async () => { - const container = await new RedisContainer("redis/redis-stack-server:7.4.0-v4") + await using container = await new RedisContainer("redis/redis-stack-server:7.4.0-v4") .withPassword("testPassword") .start(); const client = await connectTo(container); @@ -117,7 +110,6 @@ describe("RedisContainer", { timeout: 240_000 }, () => { expect(result).toEqual({ name: "test" }); client.destroy(); - await container.stop(); }); // } diff --git a/packages/modules/redpanda/src/redpanda-container.test.ts b/packages/modules/redpanda/src/redpanda-container.test.ts index ab0621980..3e39b6f8d 100644 --- a/packages/modules/redpanda/src/redpanda-container.test.ts +++ b/packages/modules/redpanda/src/redpanda-container.test.ts @@ -1,20 +1,20 @@ import { Kafka, KafkaConfig, logLevel } from "kafkajs"; import { getImage } from "../../../testcontainers/src/utils/test-helper"; import { RedpandaContainer, StartedRedpandaContainer } from "./redpanda-container"; + const IMAGE = getImage(__dirname); describe("RedpandaContainer", { timeout: 240_000 }, () => { // connectToKafka { it("should connect", async () => { - const redpandaContainer = await new RedpandaContainer(IMAGE).start(); + await using redpandaContainer = await new RedpandaContainer(IMAGE).start(); await testPubSub(redpandaContainer); - await redpandaContainer.stop(); }); // } // connectToSchemaRegistry { it("should connect to schema registry", async () => { - const redpandaContainer = await new RedpandaContainer(IMAGE).start(); + await using redpandaContainer = await new RedpandaContainer(IMAGE).start(); const schemaRegistryUrl = redpandaContainer.getSchemaRegistryAddress(); const response = await fetch(`${schemaRegistryUrl}/subjects`, { @@ -25,34 +25,28 @@ describe("RedpandaContainer", { timeout: 240_000 }, () => { }); expect(response.status).toBe(200); - - await redpandaContainer.stop(); }); // } // connectToAdmin { it("should connect to admin", async () => { - const redpandaContainer = await new RedpandaContainer(IMAGE).start(); + await using redpandaContainer = await new RedpandaContainer(IMAGE).start(); const adminUrl = `${redpandaContainer.getAdminAddress()}/v1`; const response = await fetch(adminUrl); expect(response.status).toBe(200); - - await redpandaContainer.stop(); }); // } // connectToRestProxy { it("should connect to rest proxy", async () => { - const redpandaContainer = await new RedpandaContainer(IMAGE).start(); + await using redpandaContainer = await new RedpandaContainer(IMAGE).start(); const restProxyUrl = `${redpandaContainer.getRestProxyAddress()}/topics`; const response = await fetch(restProxyUrl); expect(response.status).toBe(200); - - await redpandaContainer.stop(); }); // } diff --git a/packages/modules/scylladb/src/scylladb-container.test.ts b/packages/modules/scylladb/src/scylladb-container.test.ts index 0b9d177f7..1326ea35a 100644 --- a/packages/modules/scylladb/src/scylladb-container.test.ts +++ b/packages/modules/scylladb/src/scylladb-container.test.ts @@ -7,7 +7,7 @@ const IMAGE = getImage(__dirname); describe("ScyllaDB", { timeout: 240_000 }, () => { // connectWithDefaultCredentials { it("should connect and execute a query", async () => { - const container = await new ScyllaContainer(IMAGE).start(); + await using container = await new ScyllaContainer(IMAGE).start(); const client = new Client({ contactPoints: [container.getContactPoint()], @@ -21,13 +21,12 @@ describe("ScyllaDB", { timeout: 240_000 }, () => { expect(result.rows[0].cql_version).toBe("3.3.1"); await client.shutdown(); - await container.stop(); }); // } // createAndFetchData { it("should create keyspace, a table, insert data, and retrieve it", async () => { - const container = await new ScyllaContainer(IMAGE).start(); + await using container = await new ScyllaContainer(IMAGE).start(); const client = new Client({ contactPoints: [container.getContactPoint()], @@ -62,12 +61,11 @@ describe("ScyllaDB", { timeout: 240_000 }, () => { expect(result.rows[0].name).toEqual(username); await client.shutdown(); - await container.stop(); }); // } it("should work with restarted container", async () => { - const container = await new ScyllaContainer(IMAGE).start(); + await using container = await new ScyllaContainer(IMAGE).start(); await container.restart(); const client = new Client({ @@ -82,6 +80,5 @@ describe("ScyllaDB", { timeout: 240_000 }, () => { expect(result.rows[0].cql_version).toBe("3.3.1"); await client.shutdown(); - await container.stop(); }); }); diff --git a/packages/modules/selenium/src/selenium-container.test.ts b/packages/modules/selenium/src/selenium-container.test.ts index 921bfe803..961752ff3 100644 --- a/packages/modules/selenium/src/selenium-container.test.ts +++ b/packages/modules/selenium/src/selenium-container.test.ts @@ -12,14 +12,13 @@ describe("SeleniumContainer", { timeout: 180_000 }, () => { browsers.forEach(async ([browser, image]) => { it(`should work for ${browser}`, async () => { - const container = await new SeleniumContainer(image).start(); + await using container = await new SeleniumContainer(image).start(); const driver = await new Builder().forBrowser(Browser[browser]).usingServer(container.getServerUrl()).build(); await driver.get("https://testcontainers.com"); expect(await driver.getTitle()).toEqual("Testcontainers"); await driver.quit(); - await container.stop(); }); it(`should record video and save to disk for ${browser}`, async () => { @@ -33,13 +32,12 @@ describe("SeleniumContainer", { timeout: 180_000 }, () => { const videoFileName = path.basename(videoFilePath); await stoppedContainer.saveRecording(videoFilePath); - const ffmpegContainer = await new GenericContainer(SELENIUM_VIDEO_IMAGE) + await using ffmpegContainer = await new GenericContainer(SELENIUM_VIDEO_IMAGE) .withCommand(["sleep", "infinity"]) .start(); await ffmpegContainer.copyFilesToContainer([{ source: videoFilePath, target: `/tmp/${videoFileName}` }]); const { exitCode } = await ffmpegContainer.exec(["ffprobe", `/tmp/${videoFileName}`]); expect(exitCode).toBe(0); - await ffmpegContainer.stop(); }); }); }); diff --git a/packages/modules/toxiproxy/src/toxiproxy-container.test.ts b/packages/modules/toxiproxy/src/toxiproxy-container.test.ts index faa7bb2a6..e25930d37 100644 --- a/packages/modules/toxiproxy/src/toxiproxy-container.test.ts +++ b/packages/modules/toxiproxy/src/toxiproxy-container.test.ts @@ -7,14 +7,14 @@ const IMAGE = getImage(__dirname); describe("ToxiProxyContainer", { timeout: 240_000 }, () => { // create_proxy { it("should create a proxy to an endpoint", async () => { - const network = await new Network().start(); - const appContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withNetwork(network) .withNetworkAliases("app") .start(); - const toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); + await using toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); const appProxy = await toxiproxyContainer.createProxy({ name: "app", @@ -23,23 +23,19 @@ describe("ToxiProxyContainer", { timeout: 240_000 }, () => { const response = await fetch(`http://${appProxy.host}:${appProxy.port}/hello-world`); expect(response.status).toBe(200); - - await toxiproxyContainer.stop(); - await appContainer.stop(); - await network.stop(); }); // } // enabled_disabled { it("should enable and disable a proxy", async () => { - const network = await new Network().start(); - const appContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withNetwork(network) .withNetworkAliases("app") .start(); - const toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); + await using toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); const appProxy = await toxiproxyContainer.createProxy({ name: "app", @@ -52,23 +48,19 @@ describe("ToxiProxyContainer", { timeout: 240_000 }, () => { await appProxy.setEnabled(true); const response = await fetch(`http://${appProxy.host}:${appProxy.port}/hello-world`); expect(response.status).toBe(200); - - await toxiproxyContainer.stop(); - await appContainer.stop(); - await network.stop(); }); // } // adding_toxic { it("should add a toxic to a proxy and then remove", async () => { - const network = await new Network().start(); - const appContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withNetwork(network) .withNetworkAliases("app") .start(); - const toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); + await using toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); const appProxy = await toxiproxyContainer.createProxy({ name: "app", @@ -93,22 +85,18 @@ describe("ToxiProxyContainer", { timeout: 240_000 }, () => { expect(after - before).toBeGreaterThan(1000); await toxic.remove(); - - await toxiproxyContainer.stop(); - await appContainer.stop(); - await network.stop(); }); // } it("should create multiple proxies", async () => { - const network = await new Network().start(); - const appContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withNetwork(network) .withNetworkAliases("app") .start(); - const toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); + await using toxiproxyContainer = await new ToxiProxyContainer(IMAGE).withNetwork(network).start(); const appProxy = await toxiproxyContainer.createProxy({ name: "app", @@ -124,14 +112,10 @@ describe("ToxiProxyContainer", { timeout: 240_000 }, () => { const response2 = await fetch(`http://${appProxy2.host}:${appProxy2.port}/hello-world`); expect(response2.status).toBe(200); - - await toxiproxyContainer.stop(); - await appContainer.stop(); - await network.stop(); }); it("throws an error when too many proxies are created", async () => { - const toxiproxyContainer = await new ToxiProxyContainer(IMAGE).start(); + await using toxiproxyContainer = await new ToxiProxyContainer(IMAGE).start(); for (let i = 0; i < 32; i++) { await toxiproxyContainer.createProxy({ @@ -146,7 +130,5 @@ describe("ToxiProxyContainer", { timeout: 240_000 }, () => { upstream: `google.com:80`, }) ).rejects.toThrow(); - - await toxiproxyContainer.stop(); }); }); diff --git a/packages/modules/valkey/src/valkey-container.test.ts b/packages/modules/valkey/src/valkey-container.test.ts index 12e642d68..fa68dae9e 100644 --- a/packages/modules/valkey/src/valkey-container.test.ts +++ b/packages/modules/valkey/src/valkey-container.test.ts @@ -9,7 +9,7 @@ const IMAGE = getImage(__dirname); describe("ValkeyContainer", { timeout: 240_000 }, () => { it("should connect and execute set-get", async () => { - const container = await new ValkeyContainer(IMAGE).start(); + await using container = await new ValkeyContainer(IMAGE).start(); const client = await connectTo(container); @@ -17,11 +17,10 @@ describe("ValkeyContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); await client.disconnect(); - await container.stop(); }); it("should connect with password and execute set-get", async () => { - const container = await new ValkeyContainer(IMAGE).withPassword("test").start(); + await using container = await new ValkeyContainer(IMAGE).withPassword("test").start(); const client = await connectTo(container); @@ -29,12 +28,11 @@ describe("ValkeyContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); await client.disconnect(); - await container.stop(); }); it("should reconnect with volume and persistence data", async () => { const sourcePath = fs.mkdtempSync(path.join(os.tmpdir(), "valkey-")); - const container = await new ValkeyContainer(IMAGE).withPassword("test").withPersistence(sourcePath).start(); + await using container = await new ValkeyContainer(IMAGE).withPassword("test").withPersistence(sourcePath).start(); let client = await connectTo(container); await client.set("key", "val"); @@ -44,7 +42,6 @@ describe("ValkeyContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); await client.disconnect(); - await container.stop(); try { fs.rmSync(sourcePath, { force: true, recursive: true }); } catch (e) { @@ -54,7 +51,7 @@ describe("ValkeyContainer", { timeout: 240_000 }, () => { }); it("should load initial data and can read it", async () => { - const container = await new ValkeyContainer(IMAGE) + await using container = await new ValkeyContainer(IMAGE) .withPassword("test") .withInitialData(path.join(__dirname, "initData.valkey")) .start(); @@ -67,13 +64,12 @@ describe("ValkeyContainer", { timeout: 240_000 }, () => { expect(await client.get("user:002")).toBe(JSON.stringify(user)); await client.disconnect(); - await container.stop(); }); it("should start with credentials and login", async () => { const password = "testPassword"; - const container = await new ValkeyContainer(IMAGE).withPassword(password).start(); + await using container = await new ValkeyContainer(IMAGE).withPassword(password).start(); expect(container.getConnectionUrl()).toEqual(`redis://:${password}@${container.getHost()}:${container.getPort()}`); const client = await connectTo(container); @@ -82,16 +78,13 @@ describe("ValkeyContainer", { timeout: 240_000 }, () => { expect(await client.get("key")).toBe("val"); await client.disconnect(); - await container.stop(); }); it("should execute container cmd and return the result", async () => { - const container = await new ValkeyContainer(IMAGE).start(); + await using container = await new ValkeyContainer(IMAGE).start(); const queryResult = await container.executeCliCmd("info", ["clients"]); expect(queryResult).toEqual(expect.stringContaining("connected_clients:1")); - - await container.stop(); }); async function connectTo(container: StartedValkeyContainer) { diff --git a/packages/modules/vault/src/vault-container.test.ts b/packages/modules/vault/src/vault-container.test.ts index 8911286a6..2a1afdcac 100644 --- a/packages/modules/vault/src/vault-container.test.ts +++ b/packages/modules/vault/src/vault-container.test.ts @@ -8,7 +8,7 @@ const IMAGE = getImage(__dirname); describe("VaultContainer", { timeout: 180_000 }, () => { // inside_block:readWrite { it("should start Vault and allow reading/writing secrets", async () => { - const container = await new VaultContainer(IMAGE).withVaultToken(VAULT_TOKEN).start(); + await using container = await new VaultContainer(IMAGE).withVaultToken(VAULT_TOKEN).start(); const client = vault({ apiVersion: "v1", @@ -28,14 +28,12 @@ describe("VaultContainer", { timeout: 180_000 }, () => { expect(data.message).toBe("world"); expect(data.other).toBe("vault"); - - await container.stop(); }); // } // inside_block:initCommands { it("should execute init commands using vault CLI", async () => { - const container = await new VaultContainer(IMAGE) + await using container = await new VaultContainer(IMAGE) .withVaultToken(VAULT_TOKEN) .withInitCommands("secrets enable transit", "write -f transit/keys/my-key") .start(); @@ -44,8 +42,6 @@ describe("VaultContainer", { timeout: 180_000 }, () => { expect(result.exitCode).toBe(0); expect(result.output).toContain("my-key"); - - await container.stop(); }); // } }); diff --git a/packages/modules/weaviate/src/weaviate-container.test.ts b/packages/modules/weaviate/src/weaviate-container.test.ts index 1e31ffcad..96f87d9d5 100644 --- a/packages/modules/weaviate/src/weaviate-container.test.ts +++ b/packages/modules/weaviate/src/weaviate-container.test.ts @@ -8,18 +8,16 @@ const IMAGE = getImage(__dirname); describe("WeaviateContainer", { timeout: 100_000 }, () => { // connectWeaviate { it("should expose ports", async () => { - const container = await new WeaviateContainer(IMAGE).start(); + await using container = await new WeaviateContainer(IMAGE).start(); expect(container.getHttpHostAddress()).toBeDefined(); expect(container.getGrpcHostAddress()).toBeDefined(); - - await container.stop(); }); // } // connectWeaviateWithClient { it("should connect to Weaviate", async () => { - const container = await new WeaviateContainer(IMAGE).start(); + await using container = await new WeaviateContainer(IMAGE).start(); const client = weaviate.client({ scheme: "http", @@ -28,8 +26,6 @@ describe("WeaviateContainer", { timeout: 100_000 }, () => { const res = await client.misc.metaGetter().do(); expect(res.version).toBeDefined(); - - await container.stop(); }); // } @@ -46,7 +42,7 @@ describe("WeaviateContainer", { timeout: 100_000 }, () => { ENABLE_MODULES: enableModules.join(","), BACKUP_FILESYSTEM_PATH: "/tmp/backups", }; - const container = await new WeaviateContainer(IMAGE).withEnvironment(environment).start(); + await using container = await new WeaviateContainer(IMAGE).withEnvironment(environment).start(); const client = weaviate.client({ scheme: "http", @@ -59,8 +55,6 @@ describe("WeaviateContainer", { timeout: 100_000 }, () => { enableModules.forEach((module) => { expect(res.modules[module]).toBeDefined(); }); - - await container.stop(); }); // } }); 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 d97c604db..9a077642b 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 @@ -21,95 +21,84 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { }); it("should start all containers in the compose file", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml").up(); + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml").up(); await Promise.all( [await composeContainerName("container"), await composeContainerName("another_container")].map( async (containerName) => await checkEnvironmentContainerIsHealthy(startedEnvironment, containerName) ) ); - - await startedEnvironment.down(); }); it("should start container with a given name", async () => { const name = `custom_container_name_${randomUuid()}`; - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-name.yml") + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-name.yml") .withEnvironment({ CONTAINER_NAME: name }) .up(); await checkEnvironmentContainerIsHealthy(startedEnvironment, name); - - await startedEnvironment.down(); }); if (!process.env.CI_PODMAN) { it("should work with buildkit", async () => { const buildkitFixtures = path.resolve(fixtures, "docker-compose-with-buildkit"); - const startedEnvironment = await new DockerComposeEnvironment(buildkitFixtures, "docker-compose.yml").up(); + await using startedEnvironment = await new DockerComposeEnvironment(buildkitFixtures, "docker-compose.yml").up(); await checkEnvironmentContainerIsHealthy(startedEnvironment, await composeContainerName("container")); - await startedEnvironment.down(); }); } it("should use pull policy", async () => { const env = new DockerComposeEnvironment(fixtures, "docker-compose-with-many-services.yml"); - const startedEnv1 = await env.up(); - const dockerEventStream = await getDockerEventStream(); - const dockerPullEventPromise = waitForDockerEvent(dockerEventStream, "pull"); - const startedEnv2 = await env.withPullPolicy(PullPolicy.alwaysPull()).up(); - await dockerPullEventPromise; + await using _ = await env.up(); - dockerEventStream.destroy(); - await startedEnv1.stop(); - await startedEnv2.stop(); + { + await using dockerEventStream = await getDockerEventStream(); + const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); + await using _ = await env.withPullPolicy(PullPolicy.alwaysPull()).up(); + await dockerPullEventPromise; + } }); it("should use pull policy for specific service", async () => { const env = new DockerComposeEnvironment(fixtures, "docker-compose-with-many-services.yml"); - const startedEnv1 = await env.up(["service_2"]); - const dockerEventStream = await getDockerEventStream(); - const dockerPullEventPromise = waitForDockerEvent(dockerEventStream, "pull"); - const startedEnv2 = await env.withPullPolicy(PullPolicy.alwaysPull()).up(["service_2"]); - await dockerPullEventPromise; + await using _ = await env.up(["service_2"]); - dockerEventStream.destroy(); - await startedEnv1.stop(); - await startedEnv2.stop(); + { + await using dockerEventStream = await getDockerEventStream(); + const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); + await using _ = await env.withPullPolicy(PullPolicy.alwaysPull()).up(["service_2"]); + await dockerPullEventPromise; + } }); it("should start environment with multiple compose files", async () => { const overrideFixtures = path.resolve(fixtures, "docker-compose-with-override"); - const startedEnvironment = await new DockerComposeEnvironment(overrideFixtures, [ + await using startedEnvironment = await new DockerComposeEnvironment(overrideFixtures, [ "docker-compose.yml", "docker-compose-update.yml", ]).up(); - const container = startedEnvironment.getContainer(await composeContainerName("container")); + await using container = startedEnvironment.getContainer(await composeContainerName("container")); const url = `http://${container.getHost()}:${container.getMappedPort(8080)}`; const response = await fetch(`${url}/env`); const responseBody = (await response.json()) as { [key: string]: string }; expect(responseBody["IS_OVERRIDDEN"]).toBe("true"); - - await startedEnvironment.down(); }); it("should support default wait strategy", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-healthcheck.yml") + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-healthcheck.yml") .withDefaultWaitStrategy(Wait.forHealthCheck()) .up(); await checkEnvironmentContainerIsHealthy(startedEnvironment, await composeContainerName("container")); - - await startedEnvironment.down(); }); it("should support log message wait strategy", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml") + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml") .withWaitStrategy(await composeContainerName("container"), Wait.forLogMessage("Listening on port 8080")) .withWaitStrategy(await composeContainerName("another_container"), Wait.forLogMessage("Listening on port 8080")) .up(); @@ -119,8 +108,6 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { async (containerName) => await checkEnvironmentContainerIsHealthy(startedEnvironment, containerName) ) ); - - await startedEnvironment.down(); }); it("should stop the container when the log message wait strategy times out", async () => { @@ -137,13 +124,11 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { }); it("should support health check wait strategy", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-healthcheck.yml") + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-healthcheck.yml") .withWaitStrategy(await composeContainerName("container"), Wait.forHealthCheck()) .up(); await checkEnvironmentContainerIsHealthy(startedEnvironment, await composeContainerName("container")); - - await startedEnvironment.down(); }); it("should support failing health check wait strategy", async () => { @@ -175,106 +160,91 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { }); it("should not wait for non-public ports", async () => { - const startedEnvironment = await new DockerComposeEnvironment( - fixtures, - "docker-compose-with-private-port.yml" - ).up(); - - await startedEnvironment.down(); + await using _ = await new DockerComposeEnvironment(fixtures, "docker-compose-with-private-port.yml").up(); }); it("should re-build the Dockerfiles", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml").withBuild().up(); + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml") + .withBuild() + .up(); await Promise.all( [await composeContainerName("container"), await composeContainerName("another_container")].map( async (containerName) => await checkEnvironmentContainerIsHealthy(startedEnvironment, containerName) ) ); - - await startedEnvironment.down(); }); it("should bind environment variables to the docker compose file", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-env.yml") + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-env.yml") .withEnvironment({ ENV_VAR: "ENV_VAR_VALUE" }) .up(); - const container = startedEnvironment.getContainer(await composeContainerName("container")); + await using container = startedEnvironment.getContainer(await composeContainerName("container")); const response = await fetch(`http://${container.getHost()}:${container.getMappedPort(8080)}/env`); const responseBody = (await response.json()) as { [key: string]: string }; expect(responseBody["ENV_VAR"]).toBe("ENV_VAR_VALUE"); - - await startedEnvironment.down(); }); it("should throw error when you get container that does not exist", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml").up(); + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml").up(); expect(() => startedEnvironment.getContainer("non_existent_container")).toThrow( `Cannot get container "non_existent_container" as it is not running` ); - - await startedEnvironment.down(); }); it("should support starting a subset of services defined in the docker-compose file", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-many-services.yml").up( - ["service_2"] - ); + await using startedEnvironment = await new DockerComposeEnvironment( + fixtures, + "docker-compose-with-many-services.yml" + ).up(["service_2"]); await checkEnvironmentContainerIsHealthy(startedEnvironment, await composeContainerName("service_2")); expect(() => startedEnvironment.getContainer("service_1")).toThrow( `Cannot get container "service_1" as it is not running` ); - - await startedEnvironment.down(); }); it("should not recreate the containers when no recreate option is set", async () => { - const startedEnvironment1 = await new DockerComposeEnvironment(fixtures, "docker-compose-with-name.yml") - .withEnvironment({ CONTAINER_NAME: `custom_container_name_${randomUuid()}` }) - .withNoRecreate() - .up(); - const startedEnvironment2 = await new DockerComposeEnvironment(fixtures, "docker-compose-with-name.yml") + { + await using _ = await new DockerComposeEnvironment(fixtures, "docker-compose-with-name.yml") + .withEnvironment({ CONTAINER_NAME: `custom_container_name_${randomUuid()}` }) + .withNoRecreate() + .up(); + } + await using _ = await new DockerComposeEnvironment(fixtures, "docker-compose-with-name.yml") .withEnvironment({ CONTAINER_NAME: `custom_container_name_${randomUuid()}` }) .withNoRecreate() .up(); - - await startedEnvironment1.down(); - await startedEnvironment2.down(); }); it("should load .env if no environment file option given", async () => { const overrideFixtures = path.resolve(fixtures, "docker-compose-with-env-file"); - const startedEnvironment = await new DockerComposeEnvironment(overrideFixtures, "docker-compose.yml").up(); + await using startedEnvironment = await new DockerComposeEnvironment(overrideFixtures, "docker-compose.yml").up(); - const container = startedEnvironment.getContainer(await composeContainerName("container")); + await using container = startedEnvironment.getContainer(await composeContainerName("container")); const response = await fetch(`http://${container.getHost()}:${container.getMappedPort(8080)}/env`); const responseBody = (await response.json()) as { [key: string]: string }; expect(responseBody["ENV_VAR"]).toBe("default"); - - await startedEnvironment.down(); }); it("should load the values in the environment file if the environment file option is set", async () => { const overrideFixtures = path.resolve(fixtures, "docker-compose-with-env-file"); - const startedEnvironment = await new DockerComposeEnvironment(overrideFixtures, "docker-compose.yml") + await using startedEnvironment = await new DockerComposeEnvironment(overrideFixtures, "docker-compose.yml") .withEnvironmentFile(".env.override") .up(); - const container = startedEnvironment.getContainer(await composeContainerName("container")); + await using container = startedEnvironment.getContainer(await composeContainerName("container")); const response = await fetch(`http://${container.getHost()}:${container.getMappedPort(8080)}/env`); const responseBody = (await response.json()) as { [key: string]: string }; expect(responseBody["ENV_VAR"]).toBe("override"); - - await startedEnvironment.down(); }); it("should start containers with a profile if profile option is set", async () => { - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-profile.yml") + await using startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose-with-profile.yml") .withProfiles("debug") .up(); @@ -283,18 +253,14 @@ describe("DockerComposeEnvironment", { timeout: 180_000 }, () => { async (containerName) => await checkEnvironmentContainerIsHealthy(startedEnvironment, containerName) ) ); - - await startedEnvironment.down(); }); it("should use a custom project name if set", async () => { const customProjectName = `custom-${new RandomUuid().nextUuid()}`; - const startedEnvironment = await new DockerComposeEnvironment(fixtures, "docker-compose.yml") + await using _ = await new DockerComposeEnvironment(fixtures, "docker-compose.yml") .withProjectName(customProjectName) .up(); expect(await getRunningContainerNames()).toContain(`${customProjectName}-container-1`); - - await startedEnvironment.down(); }); }); diff --git a/packages/testcontainers/src/docker-compose-environment/started-docker-compose-environment.ts b/packages/testcontainers/src/docker-compose-environment/started-docker-compose-environment.ts index 4a645458c..13a3f8f51 100644 --- a/packages/testcontainers/src/docker-compose-environment/started-docker-compose-environment.ts +++ b/packages/testcontainers/src/docker-compose-environment/started-docker-compose-environment.ts @@ -4,7 +4,7 @@ import { StartedGenericContainer } from "../generic-container/started-generic-co import { DownedDockerComposeEnvironment } from "./downed-docker-compose-environment"; import { StoppedDockerComposeEnvironment } from "./stopped-docker-compose-environment"; -export class StartedDockerComposeEnvironment { +export class StartedDockerComposeEnvironment implements AsyncDisposable { constructor( private readonly startedGenericContainers: { [containerName: string]: StartedGenericContainer }, private readonly options: ComposeOptions @@ -32,4 +32,8 @@ export class StartedDockerComposeEnvironment { } return container; } + + async [Symbol.asyncDispose]() { + await this.down(); + } } diff --git a/packages/testcontainers/src/generic-container/abstract-started-container.ts b/packages/testcontainers/src/generic-container/abstract-started-container.ts index 1ccac33b7..92ce5bc4d 100644 --- a/packages/testcontainers/src/generic-container/abstract-started-container.ts +++ b/packages/testcontainers/src/generic-container/abstract-started-container.ts @@ -98,4 +98,8 @@ export class AbstractStartedContainer implements StartedTestContainer { public logs(opts?: { since?: number; tail?: number }): Promise { return this.startedTestContainer.logs(opts); } + + async [Symbol.asyncDispose]() { + await this.startedTestContainer[Symbol.asyncDispose](); + } } diff --git a/packages/testcontainers/src/generic-container/generic-container-commit.test.ts b/packages/testcontainers/src/generic-container/generic-container-commit.test.ts index 21ab42a2c..747bb9100 100644 --- a/packages/testcontainers/src/generic-container/generic-container-commit.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-commit.test.ts @@ -17,7 +17,7 @@ describe("GenericContainer commit", { timeout: 180_000 }, () => { const testComment = "test-comment"; // Start original container and make a change - const container = await new GenericContainer(`${imageName}:${imageVersion}`).withExposedPorts(8080).start(); + await using container = await new GenericContainer(`${imageName}:${imageVersion}`).withExposedPorts(8080).start(); // Make a change to the container await container.exec(["sh", "-c", `echo '${testContent}' > /test-file.txt`]); @@ -36,20 +36,16 @@ describe("GenericContainer commit", { timeout: 180_000 }, () => { expect(imageInfo.Comment).toBe(testComment); // Start a new container from the committed image - const newContainer = await new GenericContainer(imageId).withExposedPorts(8080).start(); + await using newContainer = await new GenericContainer(imageId).withExposedPorts(8080).start(); // Verify the changes exist in the new container const result = await newContainer.exec(["cat", "/test-file.txt"]); expect(result.output.trim()).toBe(testContent); - - // Cleanup - await container.stop(); - await newContainer.stop(); }); it("should add session ID label when deleteOnExit is true", async () => { const newImageTag = `test-commit-${new RandomUuid().nextUuid()}`; - const container = await new GenericContainer(`${imageName}:${imageVersion}`).withExposedPorts(8080).start(); + await using container = await new GenericContainer(`${imageName}:${imageVersion}`).withExposedPorts(8080).start(); // Commit with deleteOnExit true (default) const imageId = await container.commit({ @@ -62,13 +58,11 @@ describe("GenericContainer commit", { timeout: 180_000 }, () => { const client = await getContainerRuntimeClient(); const reaper = await getReaper(client); expect(imageInfo.Config.Labels[LABEL_TESTCONTAINERS_SESSION_ID]).toBe(reaper.sessionId); - - await container.stop(); }); it("should not add session ID label when deleteOnExit is false", async () => { const newImageTag = `test-commit-${new RandomUuid().nextUuid()}`; - const container = await new GenericContainer(`${imageName}:${imageVersion}`).withExposedPorts(8080).start(); + await using container = await new GenericContainer(`${imageName}:${imageVersion}`).withExposedPorts(8080).start(); // Commit with deleteOnExit false const imageId = await container.commit({ @@ -85,7 +79,6 @@ describe("GenericContainer commit", { timeout: 180_000 }, () => { expect(imageInfo.Config.Labels.test).toBe("test"); expect(imageInfo.Config.Env).toContain("test=test"); - await container.stop(); await deleteImageByName(imageId); }); }); diff --git a/packages/testcontainers/src/generic-container/generic-container-dockerfile.test.ts b/packages/testcontainers/src/generic-container/generic-container-dockerfile.test.ts index ec14aff4c..243b5cfc6 100644 --- a/packages/testcontainers/src/generic-container/generic-container-dockerfile.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-dockerfile.test.ts @@ -21,22 +21,18 @@ describe("GenericContainer Dockerfile", { timeout: 180_000 }, () => { it("should build and start", async () => { const context = path.resolve(fixtures, "docker"); const container = await GenericContainer.fromDockerfile(context).build(); - const startedContainer = await container.withExposedPorts(8080).start(); + await using startedContainer = await container.withExposedPorts(8080).start(); await checkContainerIsHealthy(startedContainer); - - await startedContainer.stop(); }); if (!process.env.CI_PODMAN) { it("should build with buildkit", async () => { const context = path.resolve(fixtures, "docker-with-buildkit"); const container = await GenericContainer.fromDockerfile(context).withBuildkit().build(); - const startedContainer = await container.withExposedPorts(8080).start(); + await using startedContainer = await container.withExposedPorts(8080).start(); await checkContainerIsHealthy(startedContainer); - - await startedContainer.stop(); }); } @@ -73,12 +69,10 @@ describe("GenericContainer Dockerfile", { timeout: 180_000 }, () => { const containerSpec = GenericContainer.fromDockerfile(dockerfile).withPullPolicy(PullPolicy.alwaysPull()); await containerSpec.build(); - const dockerEventStream = await getDockerEventStream(); - const dockerPullEventPromise = waitForDockerEvent(dockerEventStream, "pull"); + await using dockerEventStream = await getDockerEventStream(); + const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); await containerSpec.build(); await dockerPullEventPromise; - - dockerEventStream.destroy(); }); it("should not pull existing image without pull policy", async () => { @@ -89,46 +83,38 @@ describe("GenericContainer Dockerfile", { timeout: 180_000 }, () => { const containerSpec = GenericContainer.fromDockerfile(dockerfile); await containerSpec.build(); - const dockerEventStream = await getDockerEventStream(); - const dockerPullEventPromise = waitForDockerEvent(dockerEventStream, "pull"); + await using dockerEventStream = await getDockerEventStream(); + const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); let hasResolved = false; dockerPullEventPromise.then(() => (hasResolved = true)); await containerSpec.build(); expect(hasResolved).toBeFalsy(); - - dockerEventStream.destroy(); }); } it("should build and start with custom file name", async () => { const context = path.resolve(fixtures, "docker-with-custom-filename"); const container = await GenericContainer.fromDockerfile(context, "Dockerfile-A").build(); - const startedContainer = await container.withExposedPorts(8080).start(); + await using startedContainer = await container.withExposedPorts(8080).start(); await checkContainerIsHealthy(startedContainer); - - await startedContainer.stop(); }); it("should set build arguments", async () => { const context = path.resolve(fixtures, "docker-with-buildargs"); const container = await GenericContainer.fromDockerfile(context).withBuildArgs({ VERSION: "10-alpine" }).build(); - const startedContainer = await container.withExposedPorts(8080).start(); + await using startedContainer = await container.withExposedPorts(8080).start(); await checkContainerIsHealthy(startedContainer); - - await startedContainer.stop(); }); it("should exit immediately and stop without exception", async () => { const message = "This container will exit immediately."; const context = path.resolve(fixtures, "docker-exit-immediately"); const container = await GenericContainer.fromDockerfile(context).build(); - const startedContainer = await container.withWaitStrategy(Wait.forLogMessage(message)).start(); + await using _ = await container.withWaitStrategy(Wait.forLogMessage(message)).start(); await new Promise((resolve) => setTimeout(resolve, 1000)); - - await startedContainer.stop(); }); }); diff --git a/packages/testcontainers/src/generic-container/generic-container-lifecycle.test.ts b/packages/testcontainers/src/generic-container/generic-container-lifecycle.test.ts index d8bac8f85..da26ee617 100644 --- a/packages/testcontainers/src/generic-container/generic-container-lifecycle.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-lifecycle.test.ts @@ -34,7 +34,7 @@ describe.sequential("GenericContainer lifecycle", { timeout: 180_000 }, () => { }); it("should not call lifecycle callbacks for a reused, generic container", async () => { - const container1 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container1 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withReuse() .start(); @@ -48,8 +48,6 @@ describe.sequential("GenericContainer lifecycle", { timeout: 180_000 }, () => { expect(containerCreated).not.toHaveBeenCalled(); expect(containerStarting).not.toHaveBeenCalled(); expect(containerStarted).not.toHaveBeenCalled(); - - await container1.stop(); }); class CustomContainer extends GenericContainer { 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 85a1c565b..eff805f61 100644 --- a/packages/testcontainers/src/generic-container/generic-container-logs.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-logs.test.ts @@ -16,20 +16,20 @@ describe("GenericContainer logs", { timeout: 180_000 }, () => { }); it("should stream logs from a started container", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); const stream = await container.logs(); const log = await new Promise((resolve) => stream.on("data", (line) => resolve(line))); expect(log).toContain("Listening on port 8080"); - - await container.stop(); }); it("should stream logs with since option from a started container", async () => { const pauseMs = 5 * 1000; const logBeforeSleep = "first"; const logAfterSleep = "second"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withEntrypoint([ "/bin/sh", "-c", @@ -47,19 +47,17 @@ describe("GenericContainer logs", { timeout: 180_000 }, () => { const log: string = await new Promise((resolve) => stream.on("data", (line) => resolve(line.trim()))); expect(log).toBe(logAfterSleep); - - await container.stop(); }); it("should stream logs from a running container after restart", async () => { const containerLogTraceSpy = vi.spyOn(containerLog, "trace"); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); await container.restart(); const logs = containerLogTraceSpy.mock.calls; expect(logs.some((line) => line.includes("Listening on port 8080"))).toBe(true); - - await container.stop(); }); }); diff --git a/packages/testcontainers/src/generic-container/generic-container-network.test.ts b/packages/testcontainers/src/generic-container/generic-container-network.test.ts index 03e3af41e..412dc228c 100644 --- a/packages/testcontainers/src/generic-container/generic-container-network.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-network.test.ts @@ -5,22 +5,22 @@ import { GenericContainer } from "./generic-container"; describe("GenericContainer network", { timeout: 180_000 }, () => { it("should set network mode", async () => { const client = await getContainerRuntimeClient(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetworkMode("host").start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withNetworkMode("host") + .start(); const dockerContainer = await client.container.getById(container.getId()); const containerInfo = await dockerContainer.inspect(); expect(containerInfo.HostConfig.NetworkMode).toBe("host"); - - await container.stop(); }); it("should set network aliases", async () => { - const network = await new Network().start(); - const fooContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using fooContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withNetwork(network) .withNetworkAliases("foo") .start(); - const barContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using barContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withNetwork(network) .withNetworkAliases("bar", "baz") .start(); @@ -29,23 +29,16 @@ describe("GenericContainer network", { timeout: 180_000 }, () => { expect((await fooContainer.exec(["getent", "hosts", "baz"])).exitCode).toBe(0); expect((await barContainer.exec(["getent", "hosts", "foo"])).exitCode).toBe(0); expect((await barContainer.exec(["getent", "hosts", "unknown"])).exitCode).not.toBe(0); - - await barContainer.stop(); - await fooContainer.stop(); - await network.stop(); }); it("should set extra hosts", async () => { - const fooContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); + await using fooContainer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExtraHosts([{ host: "foo", ipAddress: fooContainer.getIpAddress(fooContainer.getNetworkNames()[0]) }]) .start(); expect((await container.exec(["getent", "hosts", "foo"])).exitCode).toBe(0); expect((await container.exec(["getent", "hosts", "unknown"])).exitCode).not.toBe(0); - - await container.stop(); - await fooContainer.stop(); }); }); diff --git a/packages/testcontainers/src/generic-container/generic-container-resources-quota.test.ts b/packages/testcontainers/src/generic-container/generic-container-resources-quota.test.ts index a99376a7e..d03e2f315 100644 --- a/packages/testcontainers/src/generic-container/generic-container-resources-quota.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-resources-quota.test.ts @@ -10,7 +10,7 @@ describe("GenericContainer resources quota", { timeout: 180_000 }, () => { if (!process.env["CI_ROOTLESS"]) { it("should set resources quota", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withResourcesQuota({ memory: 0.5, cpu: 1 }) .start(); @@ -19,25 +19,21 @@ describe("GenericContainer resources quota", { timeout: 180_000 }, () => { expect(containerInfo.HostConfig.Memory).toEqual(536870912); expect(containerInfo.HostConfig.NanoCpus).toEqual(1000000000); - - await container.stop(); }); } it("resources quota should be 0 for cpu and memory if not set by user", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); const dockerContainer = await client.container.getById(container.getId()); const containerInfo = await dockerContainer.inspect(); expect(containerInfo.HostConfig.Memory).toEqual(0); expect(containerInfo.HostConfig.NanoCpus).toEqual(0); - - await container.stop(); }); it("should set resources quota memory only, cpu should be 0", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withResourcesQuota({ memory: 0.5 }) .start(); @@ -46,13 +42,11 @@ describe("GenericContainer resources quota", { timeout: 180_000 }, () => { expect(containerInfo.HostConfig.Memory).toEqual(536870912); expect(containerInfo.HostConfig.NanoCpus).toEqual(0); - - await container.stop(); }); if (!process.env["CI_ROOTLESS"]) { it("should set resources quota cpu only, memory should be 0", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withResourcesQuota({ cpu: 1 }) .start(); @@ -61,8 +55,6 @@ describe("GenericContainer resources quota", { timeout: 180_000 }, () => { expect(containerInfo.HostConfig.Memory).toEqual(0); expect(containerInfo.HostConfig.NanoCpus).toEqual(1000000000); - - await container.stop(); }); } }); diff --git a/packages/testcontainers/src/generic-container/generic-container-restart.test.ts b/packages/testcontainers/src/generic-container/generic-container-restart.test.ts index 68e721ed0..36d7970a8 100644 --- a/packages/testcontainers/src/generic-container/generic-container-restart.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-restart.test.ts @@ -4,7 +4,7 @@ import { GenericContainer } from "./generic-container"; describe("GenericContainer restart", { timeout: 180_000 }, () => { it("should restart", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(`container-${new RandomUuid().nextUuid()}`) .withExposedPorts(8080) .start(); @@ -14,12 +14,10 @@ describe("GenericContainer restart", { timeout: 180_000 }, () => { await checkContainerIsHealthy(container); expect(container.getId()).toStrictEqual(container.getId()); expect(container.getName()).toStrictEqual(container.getName()); - - await container.stop(); }); it("should restart persisting state", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(`container-${new RandomUuid().nextUuid()}`) .withExposedPorts(8080) .start(); @@ -29,6 +27,5 @@ describe("GenericContainer restart", { timeout: 180_000 }, () => { await checkContainerIsHealthy(container); expect((await container.exec(["cat", "config.txt"])).output).toEqual(expect.stringContaining("testconfig")); - await container.stop(); }); }); diff --git a/packages/testcontainers/src/generic-container/generic-container-reuse.test.ts b/packages/testcontainers/src/generic-container/generic-container-reuse.test.ts index b54f521d7..53e63f45f 100644 --- a/packages/testcontainers/src/generic-container/generic-container-reuse.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container-reuse.test.ts @@ -5,82 +5,66 @@ import { GenericContainer } from "./generic-container"; describe("GenericContainer reuse", { timeout: 180_000 }, () => { it("should not reuse the container by default", async () => { const name = `there_can_only_be_one_${randomUuid()}`; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .start(); await checkContainerIsHealthy(container); - try { - await expect(() => - new GenericContainer("cristianrgreco/testcontainer:1.1.14").withName(name).withExposedPorts(8080).start() - ).rejects.toThrowError(); - } finally { - await container.stop(); - } + await expect(() => + new GenericContainer("cristianrgreco/testcontainer:1.1.14").withName(name).withExposedPorts(8080).start() + ).rejects.toThrowError(); }); it("should not reuse the container even when there is a candidate 1", async () => { const name = `there_can_only_be_one_${randomUuid()}`; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .withReuse() .start(); await checkContainerIsHealthy(container); - try { - await expect(() => - new GenericContainer("cristianrgreco/testcontainer:1.1.14").withName(name).withExposedPorts(8080).start() - ).rejects.toThrowError(); - } finally { - await container.stop(); - } + await expect(() => + new GenericContainer("cristianrgreco/testcontainer:1.1.14").withName(name).withExposedPorts(8080).start() + ).rejects.toThrowError(); }); it("should not reuse the container even when there is a candidate 2", async () => { const name = `there_can_only_be_one_${randomUuid()}`; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .start(); await checkContainerIsHealthy(container); - try { - await expect(() => - new GenericContainer("cristianrgreco/testcontainer:1.1.14") - .withName(name) - .withExposedPorts(8080) - .withReuse() - .start() - ).rejects.toThrowError(); - } finally { - await container.stop(); - } + await expect(() => + new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withName(name) + .withExposedPorts(8080) + .withReuse() + .start() + ).rejects.toThrowError(); }); it("should not reuse the container when TESTCONTAINERS_REUSE_ENABLE is set to false", async () => { vi.stubEnv("TESTCONTAINERS_REUSE_ENABLE", "false"); const name = `there_can_only_be_one_${randomUuid()}`; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .withReuse() .start(); await checkContainerIsHealthy(container); - try { - await expect(() => - new GenericContainer("cristianrgreco/testcontainer:1.1.14") - .withName(name) - .withExposedPorts(8080) - .withReuse() - .start() - ).rejects.toThrowError(); - } finally { - await container.stop(); - } + await expect(() => + new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withName(name) + .withExposedPorts(8080) + .withReuse() + .start() + ).rejects.toThrowError(); }); it.each(["true", undefined])( @@ -89,7 +73,7 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { vi.stubEnv("TESTCONTAINERS_REUSE_ENABLE", reuseEnable); const name = `there_can_only_be_one_${randomUuid()}`; - const container1 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container1 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .withReuse() @@ -104,8 +88,6 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { await checkContainerIsHealthy(container2); expect(container1.getId()).toBe(container2.getId()); - - await container1.stop(); } ); @@ -118,7 +100,7 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { .start(); await container1.stop(); - const container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .withReuse() @@ -126,7 +108,6 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { await checkContainerIsHealthy(container2); expect(container1.getId()).not.toBe(container2.getId()); - await container2.stop(); }); it("should reuse stopped container, if configured withAutoRemove(false)", async () => { @@ -139,7 +120,7 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { .start(); await container1.stop({ timeout: 10000 }); - const container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .withReuse() @@ -159,7 +140,7 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { .start(); await container1.stop({ remove: false, timeout: 10000 }); - const container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(name) .withExposedPorts(8080) .withReuse() @@ -171,7 +152,7 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { }); it("should keep the labels passed in when a new reusable container is created", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(`there_can_only_be_one_${randomUuid()}`) .withExposedPorts(8080) .withLabels({ test: "foo", bar: "baz" }) @@ -179,7 +160,6 @@ describe("GenericContainer reuse", { timeout: 180_000 }, () => { .start(); expect(container.getLabels()).toEqual(expect.objectContaining({ test: "foo" })); - await container.stop(); }); it("should not create multiple reusable containers if called in parallel", async () => { diff --git a/packages/testcontainers/src/generic-container/generic-container.test.ts b/packages/testcontainers/src/generic-container/generic-container.test.ts index b96cc5d90..e84f7653d 100644 --- a/packages/testcontainers/src/generic-container/generic-container.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container.test.ts @@ -15,16 +15,16 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const fixtures = path.resolve(__dirname, "..", "..", "fixtures", "docker"); it("should return first mapped port", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); expect(container.getFirstMappedPort()).toBe(container.getMappedPort(8080)); - - await container.stop(); }); it("should bind to specified host port", async () => { const hostPort = await getPort(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts({ container: 8080, host: hostPort, @@ -33,12 +33,12 @@ describe("GenericContainer", { timeout: 180_000 }, () => { await checkContainerIsHealthy(container); expect(container.getMappedPort(8080)).toBe(hostPort); - - await container.stop(); }); it("should execute a command on a running container", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); const { output, stdout, stderr, exitCode } = await container.exec(["echo", "hello", "world"]); @@ -46,12 +46,12 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(stdout).toEqual(expect.stringContaining("hello world")); expect(stderr).toBe(""); expect(output).toEqual(stdout); - - await container.stop(); }); it("should execute a command in a different working directory", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); const { output, stdout, stderr, exitCode } = await container.exec(["pwd"], { workingDir: "/var/log" }); @@ -59,12 +59,12 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(stdout).toEqual(expect.stringContaining("/var/log")); expect(stderr).toBe(""); expect(output).toEqual(stdout); - - await container.stop(); }); it("should execute a command with custom environment variables", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); const { output, stdout, stderr, exitCode } = await container.exec(["env"], { env: { TEST_ENV: "test" } }); @@ -72,13 +72,13 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(stdout).toEqual(expect.stringContaining("TEST_ENV=test")); expect(stderr).toBe(""); expect(output).toEqual(stdout); - - await container.stop(); }); it("should execute a command with a different user", async () => { // By default, node:alpine runs as root - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); const { output, stdout, stderr, exitCode } = await container.exec(["whoami"], { user: "node" }); @@ -86,12 +86,12 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(stdout).toEqual(expect.stringContaining("node")); expect(stderr).toBe(""); expect(output).toEqual(stdout); - - await container.stop(); }); it("should capture stderr when a command fails", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); const { output, stdout, stderr, exitCode } = await container.exec(["ls", "/nonexistent/path"]); @@ -99,12 +99,12 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(stdout).toBe(""); expect(stderr).toEqual(expect.stringContaining("No such file or directory")); expect(output).toEqual(stderr); - - await container.stop(); }); it("should capture stdout and stderr in the correct order", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); // The command first writes to stdout and then tries to access a nonexistent file (stderr) const { output, stdout, stderr, exitCode } = await container.exec([ @@ -118,12 +118,10 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(stderr).toEqual(expect.stringContaining("No such file or directory")); expect(output).toEqual(expect.stringContaining("This is stdout")); expect(output).toEqual(expect.stringContaining("No such file or directory")); - - await container.stop(); }); it("should set environment variables", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withEnvironment({ customKey: "customValue" }) .withExposedPorts(8080) .start(); @@ -132,12 +130,10 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const response = await fetch(`${url}/env`); const responseBody = (await response.json()) as { [key: string]: string }; expect(responseBody.customKey).toBe("customValue"); - - await container.stop(); }); it("should set command", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCommand(["node", "index.js", "one", "two", "three"]) .withExposedPorts(8080) .start(); @@ -146,12 +142,10 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const response = await fetch(`${url}/cmd`); const responseBody = await response.json(); expect(responseBody).toEqual(["/usr/local/bin/node", "/index.js", "one", "two", "three"]); - - await container.stop(); }); it("should set working directory", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withWorkingDir("/tmp") .withCommand(["node", "../index.js"]) .withExposedPorts(8080) @@ -162,7 +156,7 @@ describe("GenericContainer", { timeout: 180_000 }, () => { }); it("should set platform", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withPullPolicy(PullPolicy.alwaysPull()) .withCommand(["node", "../index.js"]) .withPlatform("linux/amd64") @@ -174,25 +168,23 @@ describe("GenericContainer", { timeout: 180_000 }, () => { }); it("should set entrypoint", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withEntrypoint(["node"]) .withCommand(["index.js"]) .withExposedPorts(8080) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should set name", async () => { const containerName = "special-test-container"; const expectedContainerName = "/special-test-container"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withName(containerName).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withName(containerName) + .start(); expect(container.getName()).toEqual(expectedContainerName); - - await container.stop(); }); it("should set labels", async () => { @@ -201,11 +193,11 @@ describe("GenericContainer", { timeout: 180_000 }, () => { ["label-2"]: "value-2", }; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withLabels(labels).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withLabels(labels) + .start(); expect(container.getLabels()).toMatchObject(labels); - - await container.stop(); }); it("should set bind mounts", async () => { @@ -213,19 +205,17 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const source = path.resolve(fixtures, "docker", filename); const target = `/tmp/${filename}`; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withBindMounts([{ source, target }]) .withExposedPorts(8080) .start(); const { output } = await container.exec(["cat", target]); expect(output).toContain("hello world"); - - await container.stop(); }); it("should set tmpfs", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withTmpFs({ "/testtmpfs": "rw" }) .withExposedPorts(8080) .start(); @@ -238,50 +228,44 @@ describe("GenericContainer", { timeout: 180_000 }, () => { await container.exec(["touch", tmpFsFile]); const { exitCode: exitCode2 } = await container.exec(["ls", tmpFsFile]); expect(exitCode2).toBe(0); - - await container.stop(); }); if (!process.env["CI_ROOTLESS"]) { it("should set ulimits", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withUlimits({ memlock: { hard: -1, soft: -1 } }) .withExposedPorts(8080) .start(); const { output } = await container.exec(["sh", "-c", "ulimit -l"]); expect(output.trim()).toBe("unlimited"); - - await container.stop(); }); } it("should add capabilities", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withAddedCapabilities("IPC_LOCK") .withExposedPorts(8080) .start(); const { output } = await container.exec(["sh", "-c", "getpcaps 1 2>&1"]); expect(output).toContain("cap_ipc_lock"); - - await container.stop(); }); it("should drop capabilities", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withDroppedCapabilities("CHOWN") .withExposedPorts(8080) .start(); const { output } = await container.exec(["sh", "-c", "getpcaps 1 2>&1"]); expect(output).not.toContain("cap_chown"); - - await container.stop(); }); it("should set default log driver", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withDefaultLogDriver().start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withDefaultLogDriver() + .start(); const client = await getContainerRuntimeClient(); const dockerContainer = client.container.getById(container.getId()); @@ -291,12 +275,10 @@ describe("GenericContainer", { timeout: 180_000 }, () => { Type: "json-file", }) ); - - await container.stop(); }); it("should set privileged mode", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withPrivilegedMode() .withExposedPorts(8080) .start(); @@ -306,37 +288,32 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const containerInfo = await dockerContainer.inspect(); expect(containerInfo.HostConfig.Privileged).toBe(true); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should use pull policy", async () => { const container = new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080); - const startedContainer1 = await container.start(); - const dockerEventStream = await getDockerEventStream(); - const dockerPullEventPromise = waitForDockerEvent(dockerEventStream, "pull"); - const startedContainer2 = await container.withPullPolicy(PullPolicy.alwaysPull()).start(); - await dockerPullEventPromise; + await using _ = await container.start(); - dockerEventStream.destroy(); - await startedContainer1.stop(); - await startedContainer2.stop(); + { + await using dockerEventStream = await getDockerEventStream(); + const dockerPullEventPromise = waitForDockerEvent(dockerEventStream.events, "pull"); + await using _ = await container.withPullPolicy(PullPolicy.alwaysPull()).start(); + await dockerPullEventPromise; + } }); it("should set the IPC mode", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withIpcMode("host") .withExposedPorts(8080) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should set the user", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withUser("node") .withExposedPorts(8080) .start(); @@ -344,22 +321,18 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const { output } = await container.exec(["whoami"]); expect(output).toEqual(expect.stringContaining("node")); - - await container.stop(); }); it("should copy file to container", async () => { const source = path.resolve(fixtures, "docker", "test.txt"); const target = "/tmp/test.txt"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCopyFilesToContainer([{ source, target }]) .withExposedPorts(8080) .start(); expect((await container.exec(["cat", target])).output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); }); it("should copy file to container with permissions", async () => { @@ -367,40 +340,36 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const target = "/tmp/test.txt"; const mode = parseInt("0777", 8); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCopyFilesToContainer([{ source, target, mode }]) .withExposedPorts(8080) .start(); expect((await container.exec(`stat -c "%a %n" ${target}`)).output).toContain("777"); - - await container.stop(); }); it("should copy file to started container", async () => { const source = path.resolve(fixtures, "docker", "test.txt"); const target = "/tmp/test.txt"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); await container.copyFilesToContainer([{ source, target }]); expect((await container.exec(["cat", target])).output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); }); it("should copy directory to container", async () => { const source = path.resolve(fixtures, "docker"); const target = "/tmp"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCopyDirectoriesToContainer([{ source, target }]) .withExposedPorts(8080) .start(); expect((await container.exec("cat /tmp/test.txt")).output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); }); it("should copy directory to container with permissions", async () => { @@ -408,40 +377,36 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const target = "/tmp/newdir"; const mode = parseInt("0777", 8); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCopyDirectoriesToContainer([{ source, target, mode }]) .withExposedPorts(8080) .start(); expect((await container.exec(`stat -c "%a %n" /tmp/newdir/test.txt`)).output).toContain("777"); - - await container.stop(); }); it("should copy directory to started container", async () => { const source = path.resolve(fixtures, "docker"); const target = "/tmp"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); await container.copyDirectoriesToContainer([{ source, target }]); expect((await container.exec("cat /tmp/test.txt")).output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); }); it("should copy content to container", async () => { const content = "hello world"; const target = "/tmp/test.txt"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCopyContentToContainer([{ content, target }]) .withExposedPorts(8080) .start(); expect((await container.exec(["cat", target])).output).toEqual(expect.stringContaining(content)); - - await container.stop(); }); it("should copy content to container with permissions", async () => { @@ -449,32 +414,30 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const target = "/tmp/test.txt"; const mode = parseInt("0777", 8); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCopyContentToContainer([{ content, target, mode }]) .withExposedPorts(8080) .start(); expect((await container.exec(`stat -c "%a %n" ${target}`)).output).toContain("777"); - - await container.stop(); }); it("should copy content to started container", async () => { const content = "hello world"; const target = "/tmp/test.txt"; - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); await container.copyContentToContainer([{ content, target }]); expect((await container.exec(["cat", target])).output).toEqual(expect.stringContaining(content)); - - await container.stop(); }); it("should honour .dockerignore file", async () => { const context = path.resolve(fixtures, "docker-with-dockerignore"); const container = await GenericContainer.fromDockerfile(context).build(); - const startedContainer = await container.withExposedPorts(8080).start(); + await using startedContainer = await container.withExposedPorts(8080).start(); const { output } = await startedContainer.exec(["find"]); @@ -489,22 +452,18 @@ describe("GenericContainer", { timeout: 180_000 }, () => { expect(output).not.toContain("example6.txt"); expect(output).not.toContain("example7.txt"); expect(output).not.toContain("Dockerfile"); - - await startedContainer.stop(); }); it("should stop the container", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(`container-${new RandomUuid().nextUuid()}`) .start(); - await container.stop(); - expect(await getRunningContainerNames()).not.toContain(container.getName()); }); it("should stop the container idempotently", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName(`container-${new RandomUuid().nextUuid()}`) .start(); @@ -551,24 +510,19 @@ describe("GenericContainer", { timeout: 180_000 }, () => { const firstContainer = await GenericContainer.fromDockerfile(context).withTarget("first").build(); const secondContainer = await GenericContainer.fromDockerfile(context).withTarget("second").build(); - const firstStartedContainer = await firstContainer.start(); - const secondStartedContainer = await secondContainer.start(); + await using firstStartedContainer = await firstContainer.start(); + await using secondStartedContainer = await secondContainer.start(); expect(firstStartedContainer.getLabels().stage).toEqual("first"); expect(secondStartedContainer.getLabels().stage).toEqual("second"); - - await firstStartedContainer.stop(); - await secondStartedContainer.stop(); }); it("should set the hostname", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withHostname("hostname") .start(); expect(container.getHostname()).toEqual("hostname"); - - await container.stop(); }); // failing to build an image hangs within the DockerImageClient.build method, diff --git a/packages/testcontainers/src/generic-container/started-generic-container.ts b/packages/testcontainers/src/generic-container/started-generic-container.ts index 7bb052d76..895faead8 100644 --- a/packages/testcontainers/src/generic-container/started-generic-container.ts +++ b/packages/testcontainers/src/generic-container/started-generic-container.ts @@ -232,4 +232,8 @@ export class StartedGenericContainer implements StartedTestContainer { return client.container.logs(this.container, opts); } + + async [Symbol.asyncDispose]() { + await this.stop(); + } } diff --git a/packages/testcontainers/src/network/network.test.ts b/packages/testcontainers/src/network/network.test.ts index e7ccb2488..21a4a10ef 100644 --- a/packages/testcontainers/src/network/network.test.ts +++ b/packages/testcontainers/src/network/network.test.ts @@ -10,42 +10,38 @@ describe("Network", { timeout: 180_000 }, () => { }); it("should start container via network mode", async () => { - const network = await new Network().start(); + await using network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withNetworkMode(network.getName()) .start(); const dockerContainer = client.container.getById(container.getId()); const containerInfo = await dockerContainer.inspect(); expect(Object.keys(containerInfo.NetworkSettings.Networks)).toContain(network.getName()); - - await container.stop(); - await network.stop(); }); it("should start container via network", async () => { - const network = await new Network().start(); + await using network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withNetwork(network) + .start(); const dockerContainer = client.container.getById(container.getId()); const containerInfo = await dockerContainer.inspect(); expect(Object.keys(containerInfo.NetworkSettings.Networks)).toContain(network.getName()); - - await container.stop(); - await network.stop(); }); it("two containers in user-defined network should be able to ping each other by name", async () => { - const network = await new Network().start(); + await using network = await new Network().start(); - const container1 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container1 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName("container1") .withNetwork(network) .start(); - const container2 = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withName("container2") .withNetwork(network) .start(); @@ -53,20 +49,15 @@ describe("Network", { timeout: 180_000 }, () => { const { exitCode } = await container1.exec(["ping", "-c", "3", "container2"]); expect(exitCode).toBe(0); - - await container1.stop(); - await container2.stop(); - await network.stop(); }); it("should expose the IP address of a container in a given network", async () => { - const network = await new Network().start(); + await using network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withNetwork(network) + .start(); expect(container.getIpAddress(network.getName())).toEqual(expect.stringMatching(/^\d+\.\d+\.\d+\.\d+$/)); - - await container.stop(); - await network.stop(); }); }); diff --git a/packages/testcontainers/src/network/network.ts b/packages/testcontainers/src/network/network.ts index ba6fcd9af..be22802f9 100644 --- a/packages/testcontainers/src/network/network.ts +++ b/packages/testcontainers/src/network/network.ts @@ -33,7 +33,7 @@ export class Network { } } -export class StartedNetwork { +export class StartedNetwork implements AsyncDisposable { constructor( private readonly client: ContainerRuntimeClient, private readonly name: string, @@ -54,6 +54,9 @@ export class StartedNetwork { log.info(`Stopped network with ID "${this.network.id}"`); return new StoppedNetwork(); } -} + async [Symbol.asyncDispose]() { + await this.stop(); + } +} export class StoppedNetwork {} diff --git a/packages/testcontainers/src/port-forwarder/port-forwarder-reuse.test.ts b/packages/testcontainers/src/port-forwarder/port-forwarder-reuse.test.ts index 905e6db3b..c300fa7d0 100644 --- a/packages/testcontainers/src/port-forwarder/port-forwarder-reuse.test.ts +++ b/packages/testcontainers/src/port-forwarder/port-forwarder-reuse.test.ts @@ -21,7 +21,7 @@ describe.sequential("Port Forwarder reuse", { timeout: 180_000 }, () => { await TC2.exposeHostPorts(port2); const portForwarder2ContainerId = (await PFI2.getInstance()).getContainerId(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); expect(portForwarder1ContainerId).toEqual(portForwarder2ContainerId); const { output: output1 } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port1}`]); @@ -31,7 +31,6 @@ describe.sequential("Port Forwarder reuse", { timeout: 180_000 }, () => { await new Promise((resolve) => server1.close(resolve)); await new Promise((resolve) => server2.close(resolve)); - await container.stop(); }); it("should reuse same ports", async () => { @@ -49,13 +48,12 @@ describe.sequential("Port Forwarder reuse", { timeout: 180_000 }, () => { await TC2.exposeHostPorts(port); const portForwarder2ContainerId = (await PFI2.getInstance()).getContainerId(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); expect(portForwarder1ContainerId).toEqual(portForwarder2ContainerId); const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${port}`]); expect(output).toEqual(expect.stringContaining("hello world")); await new Promise((resolve) => server.close(resolve)); - await container.stop(); }); }); diff --git a/packages/testcontainers/src/port-forwarder/port-forwarder.test.ts b/packages/testcontainers/src/port-forwarder/port-forwarder.test.ts index 23a97b97a..21f0defde 100644 --- a/packages/testcontainers/src/port-forwarder/port-forwarder.test.ts +++ b/packages/testcontainers/src/port-forwarder/port-forwarder.test.ts @@ -9,50 +9,41 @@ describe("PortForwarder", { timeout: 180_000 }, () => { it("should expose host ports to the container", async () => { const randomPort = await portGen.generatePort(); - const server = await createTestServer(randomPort); + await using _ = await createTestServer(randomPort); await TestContainers.exposeHostPorts(randomPort); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").start(); const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]); expect(output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); - server.close(); }); it("should expose host ports to the container with custom network", async () => { const randomPort = await portGen.generatePort(); - const server = await createTestServer(randomPort); + await using _ = await createTestServer(randomPort); await TestContainers.exposeHostPorts(randomPort); - const network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withNetwork(network).start(); + await using network = await new Network().start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withNetwork(network) + .start(); const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]); expect(output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); - await network.stop(); - server.close(); }); it("should expose host ports to the container with custom network and network alias", async () => { const randomPort = await portGen.generatePort(); - const server = await createTestServer(randomPort); + await using _ = await createTestServer(randomPort); await TestContainers.exposeHostPorts(randomPort); - const network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withNetwork(network) .withNetworkAliases("foo") .start(); const { output } = await container.exec(["curl", "-s", `http://host.testcontainers.internal:${randomPort}`]); expect(output).toEqual(expect.stringContaining("hello world")); - - await container.stop(); - await network.stop(); - server.close(); }); }); diff --git a/packages/testcontainers/src/socat/socat-container.test.ts b/packages/testcontainers/src/socat/socat-container.test.ts index b74a9f351..bc2354b35 100644 --- a/packages/testcontainers/src/socat/socat-container.test.ts +++ b/packages/testcontainers/src/socat/socat-container.test.ts @@ -4,42 +4,34 @@ import { SocatContainer } from "./socat-container"; describe("SocatContainer", { timeout: 120_000 }, () => { it("should forward requests to helloworld container", async () => { - const network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withNetwork(network) .withNetworkAliases("helloworld") .start(); - const socat = await new SocatContainer().withNetwork(network).withTarget(8080, "helloworld").start(); + await using socat = await new SocatContainer().withNetwork(network).withTarget(8080, "helloworld").start(); const socatUrl = `http://${socat.getHost()}:${socat.getMappedPort(8080)}`; const response = await fetch(`${socatUrl}/hello-world`); expect(response.status).toBe(200); expect(await response.text()).toBe("hello-world"); - - await socat.stop(); - await container.stop(); - await network.stop(); }); it("should forward requests to helloworld container in a different port", async () => { - const network = await new Network().start(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using network = await new Network().start(); + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withNetwork(network) .withNetworkAliases("helloworld") .start(); - const socat = await new SocatContainer().withNetwork(network).withTarget(8081, "helloworld", 8080).start(); + await using socat = await new SocatContainer().withNetwork(network).withTarget(8081, "helloworld", 8080).start(); const socatUrl = `http://${socat.getHost()}:${socat.getMappedPort(8081)}`; const response = await fetch(`${socatUrl}/hello-world`); expect(response.status).toBe(200); expect(await response.text()).toBe("hello-world"); - - await socat.stop(); - await container.stop(); - await network.stop(); }); }); diff --git a/packages/testcontainers/src/test-container.ts b/packages/testcontainers/src/test-container.ts index 148ef18e6..b58f0b38c 100644 --- a/packages/testcontainers/src/test-container.ts +++ b/packages/testcontainers/src/test-container.ts @@ -64,7 +64,7 @@ export interface StopOptions { removeVolumes: boolean; } -export interface StartedTestContainer { +export interface StartedTestContainer extends AsyncDisposable { stop(options?: Partial): Promise; restart(options?: Partial): Promise; commit(options: CommitOptions): Promise; diff --git a/packages/testcontainers/src/utils/test-helper.ts b/packages/testcontainers/src/utils/test-helper.ts index f3a4ae3cf..0f649dbab 100644 --- a/packages/testcontainers/src/utils/test-helper.ts +++ b/packages/testcontainers/src/utils/test-helper.ts @@ -38,11 +38,17 @@ export const checkEnvironmentContainerIsHealthy = async ( await checkContainerIsHealthy(container); }; -export const getDockerEventStream = async (opts: GetEventsOptions = {}): Promise => { +export const getDockerEventStream = async (opts: GetEventsOptions = {}): Promise<{ events: Readable } & Disposable> => { const dockerode = (await getContainerRuntimeClient()).container.dockerode; const events = (await dockerode.getEvents(opts)) as Readable; events.setEncoding("utf-8"); - return events; + + return { + events, + [Symbol.dispose]() { + events.destroy(); + }, + }; }; export const getRunningContainerNames = async (): Promise => { diff --git a/packages/testcontainers/src/wait-strategies/health-check-wait-strategy.test.ts b/packages/testcontainers/src/wait-strategies/health-check-wait-strategy.test.ts index 73a46acf5..e6fa9fbd7 100644 --- a/packages/testcontainers/src/wait-strategies/health-check-wait-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/health-check-wait-strategy.test.ts @@ -10,18 +10,16 @@ describe("HealthCheckWaitStrategy", { timeout: 180_000 }, () => { it("should wait for health check", async () => { const context = path.resolve(fixtures, "docker-with-health-check"); const customGenericContainer = await GenericContainer.fromDockerfile(context).build(); - const container = await customGenericContainer + await using container = await customGenericContainer .withExposedPorts(8080) .withWaitStrategy(Wait.forHealthCheck()) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should wait for custom health check", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withHealthCheck({ test: ["CMD-SHELL", "curl -f http://localhost:8080/hello-world || exit 1"], @@ -34,8 +32,6 @@ describe("HealthCheckWaitStrategy", { timeout: 180_000 }, () => { .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should stop the container when the health check fails", async () => { @@ -74,7 +70,7 @@ describe("HealthCheckWaitStrategy", { timeout: 180_000 }, () => { }); it("should wait for custom health check using CMD to run the command directly without a shell", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withHealthCheck({ test: ["CMD", "/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/hello-world"], @@ -87,7 +83,5 @@ describe("HealthCheckWaitStrategy", { timeout: 180_000 }, () => { .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); }); diff --git a/packages/testcontainers/src/wait-strategies/host-port-wait-strategy.test.ts b/packages/testcontainers/src/wait-strategies/host-port-wait-strategy.test.ts index 40de3bcec..242870d8d 100644 --- a/packages/testcontainers/src/wait-strategies/host-port-wait-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/host-port-wait-strategy.test.ts @@ -4,11 +4,11 @@ import { checkContainerIsHealthy, getRunningContainerNames } from "../utils/test describe("HostPortWaitStrategy", { timeout: 180_000 }, () => { it("should wait for port", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14").withExposedPorts(8080).start(); + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withExposedPorts(8080) + .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should stop the container when the host port check wait strategy times out", async () => { diff --git a/packages/testcontainers/src/wait-strategies/http-wait-strategy.test.ts b/packages/testcontainers/src/wait-strategies/http-wait-strategy.test.ts index 56c2b59f2..67a6f10b6 100644 --- a/packages/testcontainers/src/wait-strategies/http-wait-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/http-wait-strategy.test.ts @@ -5,25 +5,21 @@ import { Wait } from "./wait"; describe("HttpWaitStrategy", { timeout: 180_000 }, () => { it("should wait for 200", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forHttp("/hello-world", 8080)) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should wait for status code", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forHttp("/unknown-path", 8080).forStatusCode(404)) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should timeout for mismatching status code", async () => { @@ -37,7 +33,7 @@ describe("HttpWaitStrategy", { timeout: 180_000 }, () => { }); it("should wait for status code matching", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy( Wait.forHttp("/hello-world", 8080).forStatusCodeMatching( @@ -47,8 +43,6 @@ describe("HttpWaitStrategy", { timeout: 180_000 }, () => { .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should timeout for falsy status code matching", async () => { @@ -62,7 +56,7 @@ describe("HttpWaitStrategy", { timeout: 180_000 }, () => { }); it("should wait for response body predicate", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy( Wait.forHttp("/hello-world", 8080).forResponsePredicate((response) => response === "hello-world") @@ -70,8 +64,6 @@ describe("HttpWaitStrategy", { timeout: 180_000 }, () => { .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should timeout for falsy response body predicate", async () => { @@ -123,47 +115,39 @@ describe("HttpWaitStrategy", { timeout: 180_000 }, () => { }); it("should set method", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forHttp("/hello-world-post", 8080).withMethod("POST")) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should set headers", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forHttp("/header-or-400/custom", 8080).withHeaders({ custom: "value" })) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should set basic credentials", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forHttp("/auth", 8080).withBasicCredentials("user", "pass")) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should set read timeout", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forHttp("/hello-world-delay", 8080).withReadTimeout(5000)) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); describe("should wait for TLS", () => { @@ -178,14 +162,12 @@ describe("HttpWaitStrategy", { timeout: 180_000 }, () => { }); it("allow self-signed certificates", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8443) .withWaitStrategy(Wait.forHttp("/hello-world", 8443).usingTls().allowInsecure()) .start(); await checkContainerIsHealthyTls(container); - - await container.stop(); }); }); }); diff --git a/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts b/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts index 62e537f0c..fcac44642 100644 --- a/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts @@ -5,30 +5,26 @@ import { Wait } from "./wait"; describe("LogWaitStrategy", { timeout: 180_000 }, () => { it("should wait for log", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forLogMessage("Listening on port 8080")) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should wait for log with regex", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withExposedPorts(8080) .withWaitStrategy(Wait.forLogMessage(/Listening on port \d+/)) .start(); await checkContainerIsHealthy(container); - - await container.stop(); }); it("should wait for a new log after restart", async () => { const start = new Date(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCommand(["/bin/sh", "-c", 'sleep 2; echo "Ready"']) .withWaitStrategy(Wait.forLogMessage("Ready")) .start(); @@ -36,8 +32,6 @@ describe("LogWaitStrategy", { timeout: 180_000 }, () => { expect(new Date().getTime() - start.getTime()).toBeGreaterThanOrEqual(2_000); await container.restart(); expect(new Date().getTime() - start.getTime()).toBeGreaterThanOrEqual(4_000); - - await container.stop(); }); it("should stop the container when the log message wait strategy times out", async () => { diff --git a/packages/testcontainers/src/wait-strategies/one-shot-startup-startegy.test.ts b/packages/testcontainers/src/wait-strategies/one-shot-startup-startegy.test.ts index dc5ffc347..c478471c5 100644 --- a/packages/testcontainers/src/wait-strategies/one-shot-startup-startegy.test.ts +++ b/packages/testcontainers/src/wait-strategies/one-shot-startup-startegy.test.ts @@ -3,12 +3,10 @@ import { Wait } from "./wait"; describe("OneShotStartupCheckStrategy", { timeout: 180_000 }, () => { it("should wait for container to finish", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCommand(["/bin/sh", "-c", 'sleep 2; echo "Ready"']) .withWaitStrategy(Wait.forOneShotStartup()) .start(); - - await container.stop(); }); it("should fail if container did not finish succesfully", async () => { diff --git a/packages/testcontainers/src/wait-strategies/shell-wait-strategy.test.ts b/packages/testcontainers/src/wait-strategies/shell-wait-strategy.test.ts index 99c87137f..026a6eb5e 100644 --- a/packages/testcontainers/src/wait-strategies/shell-wait-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/shell-wait-strategy.test.ts @@ -3,12 +3,10 @@ import { Wait } from "./wait"; describe("ShellWaitStrategy", { timeout: 180_000 }, () => { it("should wait for successful command", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withCommand(["/bin/sh", "-c", "sleep 0.5; touch /tmp/test.lock; node index.js"]) .withWaitStrategy(Wait.forSuccessfulCommand("stat /tmp/test.lock")) .start(); - - await container.stop(); }); it("should timeout when command not successful", async () => { 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 99f7931a9..7e0bbd6fa 100644 --- a/packages/testcontainers/src/wait-strategies/startup-check-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/startup-check-strategy.test.ts @@ -16,11 +16,9 @@ describe("StartupCheckStrategy", { timeout: 180_000 }, () => { } })(); - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + await using _ = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withWaitStrategy(waitStrategy) .start(); - - await container.stop(); }); it("should fail when status PENDING after timeout", async () => {