diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc index 44766dc571a..2f8b59d4182 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc @@ -54,7 +54,7 @@ The following service connection factories are provided in the `spring-ai-spring | Containers of type `QdrantContainer` | `TypesenseConnectionDetails` -| Containers named "typesense/typesense" +| Containers of type `TypesenseContainer` | `WeaviateConnectionDetails` | Containers of type `WeaviateContainer` diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml index 92ac01362c6..1ed0b73b640 100644 --- a/spring-ai-spring-boot-autoconfigure/pom.xml +++ b/spring-ai-spring-boot-autoconfigure/pom.xml @@ -545,6 +545,13 @@ test + + org.testcontainers + typesense + 1.20.4 + test + + org.testcontainers weaviate diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/typesense/TypesenseVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/typesense/TypesenseVectorStoreAutoConfigurationIT.java index 0911534efc7..2c297ee7748 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/typesense/TypesenseVectorStoreAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/typesense/TypesenseVectorStoreAutoConfigurationIT.java @@ -16,15 +16,14 @@ package org.springframework.ai.autoconfigure.vectorstore.typesense; -import java.time.Duration; import java.util.List; import java.util.Map; import io.micrometer.observation.tck.TestObservationRegistry; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.typesense.TypesenseContainer; import org.springframework.ai.ResourceUtils; import org.springframework.ai.document.Document; @@ -53,10 +52,7 @@ public class TypesenseVectorStoreAutoConfigurationIT { @Container - private static final GenericContainer typesenseContainer = new GenericContainer<>("typesense/typesense:26.0") - .withExposedPorts(8108) - .withCommand("--data-dir", "/tmp", "--api-key=xyz", "--enable-cors") - .withStartupTimeout(Duration.ofSeconds(100)); + private static final TypesenseContainer typesense = new TypesenseContainer("typesense/typesense:26.0"); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(TypesenseVectorStoreAutoConfiguration.class)) @@ -73,10 +69,10 @@ public void addAndSearch() { .withPropertyValues("spring.ai.vectorstore.typesense.embeddingDimension=384", "spring.ai.vectorstore.typesense.collectionName=myTestCollection", "spring.ai.vectorstore.typesense.initialize-schema=true", - "spring.ai.vectorstore.typesense.client.apiKey=xyz", + "spring.ai.vectorstore.typesense.client.apiKey=" + typesense.getApiKey(), "spring.ai.vectorstore.typesense.client.protocol=http", - "spring.ai.vectorstore.typesense.client.host=" + typesenseContainer.getHost(), - "spring.ai.vectorstore.typesense.client.port=" + typesenseContainer.getMappedPort(8108).toString()) + "spring.ai.vectorstore.typesense.client.host=" + typesense.getHost(), + "spring.ai.vectorstore.typesense.client.port=" + typesense.getHttpPort()) .run(context -> { VectorStore vectorStore = context.getBean(VectorStore.class); TestObservationRegistry observationRegistry = context.getBean(TestObservationRegistry.class); diff --git a/spring-ai-spring-boot-testcontainers/pom.xml b/spring-ai-spring-boot-testcontainers/pom.xml index ed7bef8c268..72db035a181 100644 --- a/spring-ai-spring-boot-testcontainers/pom.xml +++ b/spring-ai-spring-boot-testcontainers/pom.xml @@ -285,6 +285,13 @@ ollama true + + + org.testcontainers + typesense + 1.20.4 + true + org.opensearch diff --git a/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactory.java b/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactory.java index 68769925a9d..f2bc175c026 100644 --- a/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactory.java +++ b/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactory.java @@ -16,7 +16,7 @@ package org.springframework.ai.testcontainers.service.connection.typesense; -import org.testcontainers.containers.Container; +import org.testcontainers.typesense.TypesenseContainer; import org.springframework.ai.autoconfigure.vectorstore.typesense.TypesenseConnectionDetails; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; @@ -26,24 +26,21 @@ * @author Eddú Meléndez */ class TypesenseContainerConnectionDetailsFactory - extends ContainerConnectionDetailsFactory, TypesenseConnectionDetails> { - - TypesenseContainerConnectionDetailsFactory() { - super("typesense/typesense"); - } + extends ContainerConnectionDetailsFactory { @Override - protected TypesenseConnectionDetails getContainerConnectionDetails(ContainerConnectionSource> source) { + protected TypesenseConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { return new TypesenseContainerConnectionDetails(source); } /** * {@link TypesenseConnectionDetails} backed by a {@link ContainerConnectionSource}. */ - private static final class TypesenseContainerConnectionDetails extends ContainerConnectionDetails> - implements TypesenseConnectionDetails { + private static final class TypesenseContainerConnectionDetails + extends ContainerConnectionDetails implements TypesenseConnectionDetails { - private TypesenseContainerConnectionDetails(ContainerConnectionSource> source) { + private TypesenseContainerConnectionDetails(ContainerConnectionSource source) { super(source); } @@ -59,12 +56,12 @@ public String getProtocol() { @Override public int getPort() { - return getContainer().getMappedPort(8108); + return Integer.parseInt(getContainer().getHttpPort()); } @Override public String getApiKey() { - return getContainer().getEnvMap().get("TYPESENSE_API_KEY"); + return getContainer().getApiKey(); } } diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactoryIT.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactoryIT.java index 9ba47d474b2..b6b8c3b3f1a 100644 --- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactoryIT.java +++ b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/typesense/TypesenseContainerConnectionDetailsFactoryIT.java @@ -16,14 +16,13 @@ package org.springframework.ai.testcontainers.service.connection.typesense; -import java.time.Duration; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.typesense.TypesenseContainer; import org.springframework.ai.ResourceUtils; import org.springframework.ai.autoconfigure.vectorstore.typesense.TypesenseVectorStoreAutoConfiguration; @@ -51,11 +50,7 @@ class TypesenseContainerConnectionDetailsFactoryIT { @Container @ServiceConnection - private static final GenericContainer typesense = new GenericContainer<>(TypesenseImage.DEFAULT_IMAGE) - .withExposedPorts(8108) - .withCommand("--data-dir", "/tmp", "--enable-cors") - .withEnv("TYPESENSE_API_KEY", "secret") - .withStartupTimeout(Duration.ofSeconds(100)); + private static final TypesenseContainer typesense = new TypesenseContainer(TypesenseImage.DEFAULT_IMAGE); List documents = List.of( new Document(ResourceUtils.getText("classpath:/test/data/spring.ai.txt"), Map.of("spring", "great")), diff --git a/vector-stores/spring-ai-typesense-store/pom.xml b/vector-stores/spring-ai-typesense-store/pom.xml index 83d13e75f2d..dc4e20e8d97 100644 --- a/vector-stores/spring-ai-typesense-store/pom.xml +++ b/vector-stores/spring-ai-typesense-store/pom.xml @@ -82,6 +82,13 @@ test + + org.testcontainers + typesense + 1.20.4 + test + + io.micrometer diff --git a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java index bfa8374ee0b..f275d4b2f9f 100644 --- a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java +++ b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java @@ -26,9 +26,10 @@ import java.util.UUID; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; + import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.typesense.TypesenseContainer; import org.typesense.api.Client; import org.typesense.api.Configuration; import org.typesense.resources.Node; @@ -58,9 +59,7 @@ public class TypesenseVectorStoreIT { @Container - private static GenericContainer typesenseContainer = new GenericContainer<>(TypesenseImage.DEFAULT_IMAGE) - .withExposedPorts(8108) - .withCommand("--data-dir", "/tmp", "--api-key=xyz", "--enable-cors"); + private static TypesenseContainer typesense = new TypesenseContainer(TypesenseImage.DEFAULT_IMAGE); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(TestApplication.class); @@ -262,10 +261,9 @@ public VectorStore vectorStore(Client client, EmbeddingModel embeddingModel) { @Bean public Client typesenseClient() { List nodes = new ArrayList<>(); - nodes - .add(new Node("http", typesenseContainer.getHost(), typesenseContainer.getMappedPort(8108).toString())); + nodes.add(new Node("http", typesense.getHost(), typesense.getMappedPort(8108).toString())); - Configuration configuration = new Configuration(nodes, Duration.ofSeconds(5), "xyz"); + Configuration configuration = new Configuration(nodes, Duration.ofSeconds(5), typesense.getApiKey()); return new Client(configuration); } diff --git a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreObservationIT.java b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreObservationIT.java index 308291c05a5..7b8b97b0f16 100644 --- a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreObservationIT.java @@ -27,9 +27,9 @@ import io.micrometer.observation.tck.TestObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistryAssert; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.typesense.TypesenseContainer; import org.typesense.api.Client; import org.typesense.api.Configuration; import org.typesense.resources.Node; @@ -57,6 +57,7 @@ /** * @author Christian Tzolov * @author Thomas Vitale + * @author Eddú Meléndez */ @Testcontainers public class TypesenseVectorStoreObservationIT { @@ -64,9 +65,7 @@ public class TypesenseVectorStoreObservationIT { private static final String TEST_COLLECTION_NAME = "test_vector_store"; @Container - private static GenericContainer typesenseContainer = new GenericContainer<>(TypesenseImage.DEFAULT_IMAGE) - .withExposedPorts(8108) - .withCommand("--data-dir", "/tmp", "--api-key=xyz", "--enable-cors"); + private static TypesenseContainer typesense = new TypesenseContainer(TypesenseImage.DEFAULT_IMAGE); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class); @@ -183,10 +182,9 @@ public VectorStore vectorStore(Client client, EmbeddingModel embeddingModel, @Bean public Client typesenseClient() { List nodes = new ArrayList<>(); - nodes - .add(new Node("http", typesenseContainer.getHost(), typesenseContainer.getMappedPort(8108).toString())); + nodes.add(new Node("http", typesense.getHost(), typesense.getMappedPort(8108).toString())); - Configuration configuration = new Configuration(nodes, Duration.ofSeconds(5), "xyz"); + Configuration configuration = new Configuration(nodes, Duration.ofSeconds(5), typesense.getApiKey()); return new Client(configuration); }