diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index 3ca93f1945f..583233766a1 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -17,6 +17,7 @@ body:
- ActiveMQ
- Azure
- Cassandra
+ - ChromaDB
- Clickhouse
- CockroachDB
- Consul
@@ -56,6 +57,7 @@ body:
- ToxiProxy
- Trino
- Vault
+ - Weaviate
- YugabyteDB
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml
index 914e59d7649..77a13321716 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.yaml
+++ b/.github/ISSUE_TEMPLATE/enhancement.yaml
@@ -17,6 +17,7 @@ body:
- ActiveMQ
- Azure
- Cassandra
+ - ChromaDB
- Clickhouse
- CockroachDB
- Consul
@@ -56,6 +57,7 @@ body:
- ToxiProxy
- Trino
- Vault
+ - Weaviate
- YugabyteDB
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml
index 6a40cd78168..e503bf4ff86 100644
--- a/.github/ISSUE_TEMPLATE/feature.yaml
+++ b/.github/ISSUE_TEMPLATE/feature.yaml
@@ -17,6 +17,7 @@ body:
- ActiveMQ
- Azure
- Cassandra
+ - ChromaDB
- Clickhouse
- CockroachDB
- CrateDB
@@ -56,6 +57,7 @@ body:
- ToxiProxy
- Trino
- Vault
+ - Weaviate
- YugabyteDB
- New Module
- type: textarea
diff --git a/.github/actions/setup-gradle/action.yml b/.github/actions/setup-gradle/action.yml
index 58499f75e57..3cb582c0cf8 100644
--- a/.github/actions/setup-gradle/action.yml
+++ b/.github/actions/setup-gradle/action.yml
@@ -4,7 +4,7 @@ runs:
using: "composite"
steps:
- name: Setup Gradle Build Action
- uses: gradle/gradle-build-action@v2
+ uses: gradle/actions/setup-gradle@v3
with:
gradle-home-cache-includes: |
caches
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 0b9eea13c94..e0f1bdfda25 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -51,6 +51,11 @@ updates:
ignore:
- dependency-name: "io.dropwizard.metrics:metrics-core"
update-types: [ "version-update:semver-major" ]
+ - package-ecosystem: "gradle"
+ directory: "/modules/chromadb"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/clickhouse"
schedule:
@@ -310,6 +315,11 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
+ - package-ecosystem: "gradle"
+ directory: "/modules/weaviate"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/yugabytedb"
schedule:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 998a0e8364a..9d0eb0a6e07 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -27,6 +27,10 @@
- changed-files:
- any-glob-to-any-file:
- modules/cassandra/**/*
+"modules/chromadb":
+ - changed-files:
+ - any-glob-to-any-file:
+ - modules/chromadb/**/*
"modules/clickhouse":
- changed-files:
- any-glob-to-any-file:
@@ -196,6 +200,10 @@
- changed-files:
- any-glob-to-any-file:
- modules/vault/**/*
+"modules/weaviate":
+ - changed-files:
+ - any-glob-to-any-file:
+ - modules/weaviate/**/*
"modules/yugabytedb":
- changed-files:
- any-glob-to-any-file:
diff --git a/.github/settings.yml b/.github/settings.yml
index 3047612d756..12629f2f355 100644
--- a/.github/settings.yml
+++ b/.github/settings.yml
@@ -115,6 +115,9 @@ labels:
- name: modules/cassandra
color: '#006b75'
+ - name: modules/chromadb
+ color: '#006b75'
+
- name: modules/clickhouse
color: '#006b75'
@@ -238,6 +241,9 @@ labels:
- name: modules/vault
color: '#006b75'
+ - name: modules/weaviate
+ color: '#006b75'
+
- name: modules/yugabytedb
color: '#006b75'
diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml
index fccaef5ede7..21ff4f793c6 100644
--- a/.github/workflows/ci-rootless.yml
+++ b/.github/workflows/ci-rootless.yml
@@ -52,7 +52,7 @@ jobs:
- name: Remove Docket root socket
run: sudo rm -rf /var/run/docker.sock
- name: Setup Gradle Build Action
- uses: gradle/gradle-build-action@v2
+ uses: gradle/actions/setup-gradle@v3
- name: Build with Gradle
run: ./gradlew --no-daemon --scan testcontainers:test --tests '*GenericContainerRuleTest'
- uses: ./.github/actions/setup-junit-report
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 15b1240904b..8e07cd76218 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -87,7 +87,7 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-java
- name: Setup Gradle Build Action
- uses: gradle/gradle-build-action@v2
+ uses: gradle/actions/setup-gradle@v3
- id: set-matrix
env:
# Since we override the tests executor,
@@ -122,7 +122,7 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-java
- name: Setup Gradle Build Action
- uses: gradle/gradle-build-action@v2
+ uses: gradle/actions/setup-gradle@v3
- id: set-matrix
working-directory: ./examples/
env:
@@ -159,7 +159,7 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-java
- name: Setup Gradle Build Action
- uses: gradle/gradle-build-action@v2
+ uses: gradle/actions/setup-gradle@v3
- id: set-matrix
env:
# Since we override the tests executor,
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0fbd63cdbd1..79a0d6856b1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,7 +21,7 @@ jobs:
run: docker image prune -af
- name: Setup Gradle Build Action
- uses: gradle/gradle-build-action@v2
+ uses: gradle/actions/setup-gradle@v3
- name: Run Gradle Build
run: ./gradlew build --scan --no-daemon -i -x test
diff --git a/core/build.gradle b/core/build.gradle
index 102d3997b8a..a318ff00b10 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -88,8 +88,8 @@ dependencies {
shaded 'org.awaitility:awaitility:4.2.0'
- api platform('com.github.docker-java:docker-java-bom:3.3.4')
- shaded platform('com.github.docker-java:docker-java-bom:3.3.4')
+ api platform('com.github.docker-java:docker-java-bom:3.3.5')
+ shaded platform('com.github.docker-java:docker-java-bom:3.3.5')
api "com.github.docker-java:docker-java-api"
diff --git a/docs/modules/chromadb.md b/docs/modules/chromadb.md
new file mode 100644
index 00000000000..9b14dfc2970
--- /dev/null
+++ b/docs/modules/chromadb.md
@@ -0,0 +1,30 @@
+# ChromaDB
+
+Testcontainers module for [ChromaDB](https://registry.hub.docker.com/r/chromadb/chroma)
+
+## ChromaDB's usage examples
+
+You can start a ChromaDB container instance from any Java application by using:
+
+
+[Default ChromaDB container](../../modules/chromadb/src/test/java/org/testcontainers/chromadb/ChromaDBContainerTest.java) inside_block:container
+
+
+## Adding this module to your project dependencies
+
+Add the following dependency to your `pom.xml`/`build.gradle` file:
+
+=== "Gradle"
+```groovy
+testImplementation "org.testcontainers:chromadb:{{latest_version}}"
+```
+
+=== "Maven"
+```xml
+
+org.testcontainers
+chromadb
+{{latest_version}}
+test
+
+```
diff --git a/docs/modules/databases/postgres.md b/docs/modules/databases/postgres.md
index b7632d78293..d07e77937b9 100644
--- a/docs/modules/databases/postgres.md
+++ b/docs/modules/databases/postgres.md
@@ -2,6 +2,28 @@
See [Database containers](./index.md) for documentation and usage that is common to all relational database container types.
+## Compatible images
+
+`PostgreSQLContainer` can also be used with the following images:
+
+* [pgvector/pgvector](https://hub.docker.com/r/pgvector/pgvector)
+
+
+[Using pgvector](../../../modules/postgresql/src/test/java/org/testcontainers/containers/CompatibleImageTest.java) inside_block:pgvectorContainer
+
+
+* [postgis/postgis](https://registry.hub.docker.com/r/postgis/postgis)
+
+
+[Using PostGIS](../../../modules/postgresql/src/test/java/org/testcontainers/containers/CompatibleImageTest.java) inside_block:postgisContainer
+
+
+* [timescale/timescaledb](https://hub.docker.com/r/timescale/timescaledb)
+
+
+[Using TimescaleDB](../../../modules/postgresql/src/test/java/org/testcontainers/containers/CompatibleImageTest.java) inside_block:timescaledbContainer
+
+
## Adding this module to your project dependencies
Add the following dependency to your `pom.xml`/`build.gradle` file:
diff --git a/docs/modules/weaviate.md b/docs/modules/weaviate.md
new file mode 100644
index 00000000000..da028cf3ce1
--- /dev/null
+++ b/docs/modules/weaviate.md
@@ -0,0 +1,30 @@
+# Weaviate
+
+Testcontainers module for [Weaviate](https://hub.docker.com/r/semitechnologies/weaviate)
+
+## WeaviateContainer's usage examples
+
+You can start a Weaviate container instance from any Java application by using:
+
+
+[Default Weaviate container](../../modules/weaviate/src/test/java/org/testcontainers/weaviate/WeaviateContainerTest.java) inside_block:container
+
+
+## Adding this module to your project dependencies
+
+Add the following dependency to your `pom.xml`/`build.gradle` file:
+
+=== "Gradle"
+```groovy
+testImplementation "org.testcontainers:weaviate:{{latest_version}}"
+```
+
+=== "Maven"
+```xml
+
+org.testcontainers
+weaviate
+{{latest_version}}
+test
+
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index 31afa5ea9e5..d912f7a0366 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -75,6 +75,7 @@ nav:
- modules/databases/yugabytedb.md
- modules/activemq.md
- modules/azure.md
+ - modules/chromadb.md
- modules/consul.md
- modules/docker_compose.md
- modules/elasticsearch.md
@@ -94,6 +95,7 @@ nav:
- modules/solr.md
- modules/toxiproxy.md
- modules/vault.md
+ - modules/weaviate.md
- modules/webdriver_containers.md
- Test framework integration:
- test_framework_integration/junit_4.md
diff --git a/modules/chromadb/build.gradle b/modules/chromadb/build.gradle
new file mode 100644
index 00000000000..2b170c21570
--- /dev/null
+++ b/modules/chromadb/build.gradle
@@ -0,0 +1,8 @@
+description = "Testcontainers :: ChromaDB"
+
+dependencies {
+ api project(':testcontainers')
+
+ testImplementation 'org.assertj:assertj-core:3.25.1'
+ testImplementation 'io.rest-assured:rest-assured:5.4.0'
+}
diff --git a/modules/chromadb/src/main/java/org/testcontainers/chromadb/ChromaDBContainer.java b/modules/chromadb/src/main/java/org/testcontainers/chromadb/ChromaDBContainer.java
new file mode 100644
index 00000000000..a1bccf3904f
--- /dev/null
+++ b/modules/chromadb/src/main/java/org/testcontainers/chromadb/ChromaDBContainer.java
@@ -0,0 +1,34 @@
+package org.testcontainers.chromadb;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * Testcontainers implementation of ChromaDB.
+ *
+ * Supported images: {@code chromadb/chroma}, {@code ghcr.io/chroma-core/chroma}
+ *
+ * Exposed ports: 8000
+ */
+public class ChromaDBContainer extends GenericContainer {
+
+ private static final DockerImageName DEFAULT_DOCKER_IMAGE = DockerImageName.parse("chromadb/chroma");
+
+ private static final DockerImageName GHCR_DOCKER_IMAGE = DockerImageName.parse("ghcr.io/chroma-core/chroma");
+
+ public ChromaDBContainer(String dockerImageName) {
+ this(DockerImageName.parse(dockerImageName));
+ }
+
+ public ChromaDBContainer(DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(DEFAULT_DOCKER_IMAGE, GHCR_DOCKER_IMAGE);
+ withExposedPorts(8000);
+ waitingFor(Wait.forHttp("/api/v1/heartbeat"));
+ }
+
+ public String getEndpoint() {
+ return "http://" + getHost() + ":" + getFirstMappedPort();
+ }
+}
diff --git a/modules/chromadb/src/test/java/org/testcontainers/chromadb/ChromaDBContainerTest.java b/modules/chromadb/src/test/java/org/testcontainers/chromadb/ChromaDBContainerTest.java
new file mode 100644
index 00000000000..945af0dcbff
--- /dev/null
+++ b/modules/chromadb/src/test/java/org/testcontainers/chromadb/ChromaDBContainerTest.java
@@ -0,0 +1,30 @@
+package org.testcontainers.chromadb;
+
+import io.restassured.http.ContentType;
+import org.junit.Test;
+
+import static io.restassured.RestAssured.given;
+
+public class ChromaDBContainerTest {
+
+ @Test
+ public void test() {
+ try ( // container {
+ ChromaDBContainer chroma = new ChromaDBContainer("chromadb/chroma:0.4.22")
+ // }
+ ) {
+ chroma.start();
+
+ given()
+ .baseUri(chroma.getEndpoint())
+ .when()
+ .body("{\"name\": \"test\"}")
+ .contentType(ContentType.JSON)
+ .post("/api/v1/databases")
+ .then()
+ .statusCode(200);
+
+ given().baseUri(chroma.getEndpoint()).when().get("/api/v1/databases/test").then().statusCode(200);
+ }
+ }
+}
diff --git a/modules/chromadb/src/test/resources/logback-test.xml b/modules/chromadb/src/test/resources/logback-test.xml
new file mode 100644
index 00000000000..83ef7a1a3ef
--- /dev/null
+++ b/modules/chromadb/src/test/resources/logback-test.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} %-5level %logger - %msg%n
+
+
+
+
+
+
+
+
+
diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/CompatibleImageTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/CompatibleImageTest.java
new file mode 100644
index 00000000000..2bb1c6c3d54
--- /dev/null
+++ b/modules/postgresql/src/test/java/org/testcontainers/containers/CompatibleImageTest.java
@@ -0,0 +1,64 @@
+package org.testcontainers.containers;
+
+import org.junit.Test;
+import org.testcontainers.db.AbstractContainerDatabaseTest;
+import org.testcontainers.utility.DockerImageName;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CompatibleImageTest extends AbstractContainerDatabaseTest {
+
+ @Test
+ public void pgvector() throws SQLException {
+ try (
+ // pgvectorContainer {
+ PostgreSQLContainer> pgvector = new PostgreSQLContainer<>(
+ DockerImageName.parse("pgvector/pgvector:pg16").asCompatibleSubstituteFor("postgres")
+ )
+ // }
+ ) {
+ pgvector.start();
+
+ ResultSet resultSet = performQuery(pgvector, "SELECT 1");
+ int resultSetInt = resultSet.getInt(1);
+ assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
+ }
+ }
+
+ @Test
+ public void postgis() throws SQLException {
+ try (
+ // postgisContainer {
+ PostgreSQLContainer> postgis = new PostgreSQLContainer<>(
+ DockerImageName.parse("postgis/postgis:16-3.4-alpine").asCompatibleSubstituteFor("postgres")
+ )
+ // }
+ ) {
+ postgis.start();
+
+ ResultSet resultSet = performQuery(postgis, "SELECT 1");
+ int resultSetInt = resultSet.getInt(1);
+ assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
+ }
+ }
+
+ @Test
+ public void timescaledb() throws SQLException {
+ try (
+ // timescaledbContainer {
+ PostgreSQLContainer> timescaledb = new PostgreSQLContainer<>(
+ DockerImageName.parse("timescale/timescaledb:2.14.2-pg16").asCompatibleSubstituteFor("postgres")
+ )
+ // }
+ ) {
+ timescaledb.start();
+
+ ResultSet resultSet = performQuery(timescaledb, "SELECT 1");
+ int resultSetInt = resultSet.getInt(1);
+ assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
+ }
+ }
+}
diff --git a/modules/weaviate/build.gradle b/modules/weaviate/build.gradle
new file mode 100644
index 00000000000..06a77aa434a
--- /dev/null
+++ b/modules/weaviate/build.gradle
@@ -0,0 +1,8 @@
+description = "Testcontainers :: Weaviate"
+
+dependencies {
+ api project(':testcontainers')
+
+ testImplementation 'org.assertj:assertj-core:3.25.1'
+ testImplementation 'io.weaviate:client:4.5.1'
+}
diff --git a/modules/weaviate/src/main/java/org/testcontainers/weaviate/WeaviateContainer.java b/modules/weaviate/src/main/java/org/testcontainers/weaviate/WeaviateContainer.java
new file mode 100644
index 00000000000..bd217b13e75
--- /dev/null
+++ b/modules/weaviate/src/main/java/org/testcontainers/weaviate/WeaviateContainer.java
@@ -0,0 +1,36 @@
+package org.testcontainers.weaviate;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * Testcontainers implementation of Weaviate.
+ *
+ * Supported image: {@code semitechnologies/weaviate}
+ *
+ * Exposed ports:
+ *
+ * - HTTP: 8080
+ * - gRPC: 50051
+ *
+ */
+public class WeaviateContainer extends GenericContainer {
+
+ private static final String WEAVIATE_IMAGE = "semitechnologies/weaviate";
+
+ public WeaviateContainer(String dockerImageName) {
+ this(DockerImageName.parse(dockerImageName));
+ }
+
+ public WeaviateContainer(DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(DockerImageName.parse(WEAVIATE_IMAGE));
+ withExposedPorts(8080, 50051);
+ withEnv("AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED", "true");
+ withEnv("PERSISTENCE_DATA_PATH", "/var/lib/weaviate");
+ }
+
+ public String getHttpHostAddress() {
+ return getHost() + ":" + getMappedPort(8080);
+ }
+}
diff --git a/modules/weaviate/src/test/java/org/testcontainers/weaviate/WeaviateContainerTest.java b/modules/weaviate/src/test/java/org/testcontainers/weaviate/WeaviateContainerTest.java
new file mode 100644
index 00000000000..562fc36de51
--- /dev/null
+++ b/modules/weaviate/src/test/java/org/testcontainers/weaviate/WeaviateContainerTest.java
@@ -0,0 +1,25 @@
+package org.testcontainers.weaviate;
+
+import io.weaviate.client.Config;
+import io.weaviate.client.WeaviateClient;
+import io.weaviate.client.base.Result;
+import io.weaviate.client.v1.misc.model.Meta;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WeaviateContainerTest {
+
+ @Test
+ public void test() {
+ try ( // container {
+ WeaviateContainer weaviate = new WeaviateContainer("semitechnologies/weaviate:1.22.4")
+ // }
+ ) {
+ weaviate.start();
+ WeaviateClient client = new WeaviateClient(new Config("http", weaviate.getHttpHostAddress()));
+ Result meta = client.misc().metaGetter().run();
+ assertThat(meta.getResult().getVersion()).isEqualTo("1.22.4");
+ }
+ }
+}
diff --git a/modules/weaviate/src/test/resources/logback-test.xml b/modules/weaviate/src/test/resources/logback-test.xml
new file mode 100644
index 00000000000..83ef7a1a3ef
--- /dev/null
+++ b/modules/weaviate/src/test/resources/logback-test.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} %-5level %logger - %msg%n
+
+
+
+
+
+
+
+
+