This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new 1c0cd75af8 Fixes #8865. Add langchain4j-embeddingstore-ql4j 
integration test module
1c0cd75af8 is described below

commit 1c0cd75af84b022f5ef875e66a7fccf82e5f61a6
Author: Jomin Mathew <[email protected]>
AuthorDate: Mon Sep 7 07:06:41 2026 +0100

    Fixes #8865. Add langchain4j-embeddingstore-ql4j integration test module
    
    Add integration-tests/langchain4j-embeddingstore-ql4j/ module to verify that
    camel-quarkus-langchain4j-embeddingstore works correctly when Quarkus
    LangChain4j is on the classpath.
    
    Uses quarkus-langchain4j-ollama with a QL4J-managed EmbeddingModel (mocked
    via WireMock), Qdrant via testcontainers for the embedding store, and tests
    Camel-QL4J interop for ADD/SEARCH/REMOVE operations plus ContentRetriever
    cross-compatibility. Registered in test-categories.yaml group-07.
    
    Co-authored-by: jomin mathew <>
---
 .../langchain4j-embeddingstore-ql4j/pom.xml        | 177 +++++++++++++++++++++
 .../it/Langchain4jEmbeddingstoreQl4jProducers.java |  67 ++++++++
 .../it/Langchain4jEmbeddingstoreQl4jResource.java  | 131 +++++++++++++++
 .../it/Langchain4jEmbeddingstoreQl4jRoutes.java    |  31 ++++
 .../src/main/resources/application.properties      |  23 +++
 .../it/Langchain4jQdrantEmbeddingstoreQl4jIT.java  |  23 +++
 .../Langchain4jQdrantEmbeddingstoreQl4jTest.java   | 120 ++++++++++++++
 .../ql4j/it/OllamaWireMockTestResource.java        |  49 ++++++
 .../embeddingstore/ql4j/it/QdrantTestResource.java | 105 ++++++++++++
 .../mappings/embeddings/api_embed-camel-store.json |  22 +++
 .../mappings/embeddings/api_embed-ql4j-ingest.json |  22 +++
 integration-tests/pom.xml                          |   1 +
 tooling/scripts/test-categories.yaml               |   1 +
 13 files changed, 772 insertions(+)

diff --git a/integration-tests/langchain4j-embeddingstore-ql4j/pom.xml 
b/integration-tests/langchain4j-embeddingstore-ql4j/pom.xml
new file mode 100644
index 0000000000..c5bd0a6876
--- /dev/null
+++ b/integration-tests/langchain4j-embeddingstore-ql4j/pom.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.camel.quarkus</groupId>
+        <artifactId>camel-quarkus-build-parent-it</artifactId>
+        <version>3.40.0-SNAPSHOT</version>
+        <relativePath>../../poms/build-parent-it/pom.xml</relativePath>
+    </parent>
+
+    
<artifactId>camel-quarkus-integration-test-langchain4j-embeddingstore-ql4j</artifactId>
+    <name>Camel Quarkus :: Integration Tests :: LangChain4j Embedding Store 
QL4J</name>
+    <description>Integration tests for Camel Quarkus LangChain4j Embedding 
Store extension with Quarkus LangChain4j on the classpath</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>io.quarkiverse.langchain4j</groupId>
+                <artifactId>quarkus-langchain4j-bom</artifactId>
+                <version>${quarkiverse-langchain4j.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-langchain4j-embeddingstore</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.langchain4j</groupId>
+            <artifactId>quarkus-langchain4j-ollama</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.langchain4j</groupId>
+            <artifactId>quarkus-langchain4j-qdrant</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-rest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-rest-jackson</artifactId>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers-qdrant</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-integration-wiremock-support</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.native.enabled>true</quarkus.native.enabled>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>virtualDependencies</id>
+            <activation>
+                <property>
+                    <name>!noVirtualDependencies</name>
+                </property>
+            </activation>
+            <dependencies>
+                <!-- The following dependencies guarantee that this module is 
built after them. You can update them by running `mvn process-resources 
-Pformat -N` from the source tree root directory -->
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-direct-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    
<artifactId>camel-quarkus-langchain4j-embeddingstore-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>skip-testcontainers-tests</id>
+            <activation>
+                <property>
+                    <name>skip-testcontainers-tests</name>
+                </property>
+            </activation>
+            <properties>
+                <skipTests>true</skipTests>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jProducers.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jProducers.java
new file mode 100644
index 0000000000..04a78de3ad
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jProducers.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.rag.content.retriever.ContentRetriever;
+import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
+import dev.langchain4j.store.embedding.EmbeddingStore;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.camel.CamelContext;
+import 
org.apache.camel.component.langchain4j.embeddingstore.EmbeddingStoreFactory;
+
+@ApplicationScoped
+public class Langchain4jEmbeddingstoreQl4jProducers {
+    @Inject
+    EmbeddingStore<TextSegment> embeddingStore;
+
+    @Produces
+    @Singleton
+    EmbeddingStoreFactory embeddingStoreFactory() {
+        return new EmbeddingStoreFactory() {
+            CamelContext camelContext;
+
+            @Override
+            public EmbeddingStore<TextSegment> createEmbeddingStore() {
+                return embeddingStore;
+            }
+
+            @Override
+            public void setCamelContext(CamelContext camelContext) {
+                this.camelContext = camelContext;
+            }
+
+            @Override
+            public CamelContext getCamelContext() {
+                return this.camelContext;
+            }
+        };
+    }
+
+    @Produces
+    @Singleton
+    ContentRetriever contentRetriever(EmbeddingModel embeddingModel) {
+        return EmbeddingStoreContentRetriever.builder()
+                .embeddingStore(embeddingStore)
+                .embeddingModel(embeddingModel)
+                .build();
+    }
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jResource.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jResource.java
new file mode 100644
index 0000000000..b14e04459d
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jResource.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.rag.content.Content;
+import dev.langchain4j.rag.content.retriever.ContentRetriever;
+import dev.langchain4j.rag.query.Query;
+import dev.langchain4j.store.embedding.EmbeddingMatch;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import org.apache.camel.FluentProducerTemplate;
+import 
org.apache.camel.component.langchain4j.embeddingstore.EmbeddingStoreFactory;
+import 
org.apache.camel.component.langchain4j.embeddingstore.LangChain4jEmbeddingStoreAction;
+import 
org.apache.camel.component.langchain4j.embeddingstore.LangChain4jEmbeddingStoreHeaders;
+
+@Path("/langchain4j-embeddingstore")
+@ApplicationScoped
+public class Langchain4jEmbeddingstoreQl4jResource {
+    @Inject
+    FluentProducerTemplate fluentProducerTemplate;
+
+    @Inject
+    ContentRetriever contentRetriever;
+
+    @Inject
+    EmbeddingModel embeddingModel;
+
+    @Inject
+    EmbeddingStoreFactory embeddingStoreFactory;
+
+    @Path("/{embeddingStoreType}/add")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    public String embeddingStoreAdd(
+            @PathParam("embeddingStoreType") String embeddingStoreType,
+            String content) {
+
+        return fluentProducerTemplate.to("direct:start")
+                .withHeader("embeddingStoreType", embeddingStoreType)
+                .withHeader(LangChain4jEmbeddingStoreHeaders.ACTION, 
LangChain4jEmbeddingStoreAction.ADD)
+                .withBody(content)
+                .request(String.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Path("/{embeddingStoreType}/search")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<Map<String, String>> embeddingStoreSearch(
+            @PathParam("embeddingStoreType") String embeddingStoreType,
+            String content) {
+
+        List<EmbeddingMatch<TextSegment>> matches = 
fluentProducerTemplate.to("direct:start")
+                .withHeader("embeddingStoreType", embeddingStoreType)
+                .withHeader(LangChain4jEmbeddingStoreHeaders.ACTION, 
LangChain4jEmbeddingStoreAction.SEARCH)
+                .withBody(content)
+                .request(List.class);
+
+        List<Map<String, String>> results = new ArrayList<>();
+        matches.forEach(match -> {
+            Map<String, String> embeddingInfo = new HashMap<>();
+            embeddingInfo.put("embeddingId", match.embeddingId());
+            embeddingInfo.put("text", match.embedded().text());
+            results.add(embeddingInfo);
+        });
+
+        return results;
+    }
+
+    @Path("/{embeddingStoreType}/remove")
+    @DELETE
+    public void embeddingStoreDelete(
+            @PathParam("embeddingStoreType") String embeddingStoreType,
+            @QueryParam("embeddingId") String embeddingId) {
+        fluentProducerTemplate.to("direct:start")
+                .withHeader("embeddingStoreType", embeddingStoreType)
+                .withHeader(LangChain4jEmbeddingStoreHeaders.ACTION, 
LangChain4jEmbeddingStoreAction.REMOVE)
+                .withBody(embeddingId)
+                .request();
+    }
+
+    @Path("/qdrant/retrieve")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<String> ql4jRetrieve(String query) {
+        List<Content> results = contentRetriever.retrieve(Query.from(query));
+        return results.stream().map(c -> c.textSegment().text()).toList();
+    }
+
+    @Path("/qdrant/ingest")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    public String ql4jIngest(String text) throws Exception {
+        TextSegment segment = TextSegment.from(text);
+        return embeddingStoreFactory.createEmbeddingStore()
+                .add(embeddingModel.embed(segment).content(), segment);
+    }
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jRoutes.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jRoutes.java
new file mode 100644
index 0000000000..d635c15fe6
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jRoutes.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class Langchain4jEmbeddingstoreQl4jRoutes extends RouteBuilder {
+    @Override
+    public void configure() throws Exception {
+        from("direct:start")
+                
.choice().when().simple("${header.CamelLangchain4jEmbeddingStoreAction} != 
'REMOVE'")
+                .toD("langchain4j-embeddings:${header.embeddingStoreType}")
+                .endChoice()
+                .end()
+                
.toD("langchain4j-embeddingstore:${header.embeddingStoreType}");
+    }
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/resources/application.properties
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/resources/application.properties
new file mode 100644
index 0000000000..a23f7fddc8
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/resources/application.properties
@@ -0,0 +1,23 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+quarkus.devservices.enabled=false
+quarkus.langchain4j.devservices.enabled=false
+quarkus.langchain4j.ollama.base-url=http://localhost:11434
+quarkus.langchain4j.ollama.embedding-model.model-id=nomic-embed-text
+quarkus.langchain4j.embedding-model.provider=ollama
+quarkus.langchain4j.qdrant.collection-name=camel-quarkus-ql4j-collection
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jIT.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jIT.java
new file mode 100644
index 0000000000..068f893cb5
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jIT.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class Langchain4jQdrantEmbeddingstoreQl4jIT extends 
Langchain4jQdrantEmbeddingstoreQl4jTest {
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jTest.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jTest.java
new file mode 100644
index 0000000000..a7609ad8e6
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@QuarkusTestResource(OllamaWireMockTestResource.class)
+@QuarkusTestResource(QdrantTestResource.class)
+@QuarkusTest
+class Langchain4jQdrantEmbeddingstoreQl4jTest {
+    static final String CAMEL_MESSAGE = "Hello Camel Quarkus LangChain4j 
Embedding Store";
+    static final String QL4J_MESSAGE = "Hello from Quarkus LangChain4j direct 
ingest";
+
+    @Test
+    void qdrantEmbeddingstoreQl4jInterop() {
+        // 1. Camel ADD
+        String id = RestAssured.given()
+                .body(CAMEL_MESSAGE)
+                .post("/langchain4j-embeddingstore/qdrant/add")
+                .then()
+                .statusCode(200)
+                .extract()
+                .body()
+                .asString();
+        assertNotNull(UUID.fromString(id));
+
+        // 2. Camel SEARCH
+        RestAssured.given()
+                .body(CAMEL_MESSAGE)
+                .post("/langchain4j-embeddingstore/qdrant/search")
+                .then()
+                .statusCode(200)
+                .body(
+                        "size()", is(1),
+                        "[0].embeddingId", is(id),
+                        "[0].text", is(CAMEL_MESSAGE));
+
+        // 3. QL4J retrieves Camel-written data
+        RestAssured.given()
+                .body(CAMEL_MESSAGE)
+                .post("/langchain4j-embeddingstore/qdrant/retrieve")
+                .then()
+                .statusCode(200)
+                .body(
+                        "size()", greaterThanOrEqualTo(1),
+                        "$", hasItem(CAMEL_MESSAGE));
+
+        // 4. QL4J ingests directly (bypassing Camel)
+        String ql4jId = RestAssured.given()
+                .body(QL4J_MESSAGE)
+                .post("/langchain4j-embeddingstore/qdrant/ingest")
+                .then()
+                .statusCode(200)
+                .extract()
+                .body()
+                .asString();
+        assertNotNull(UUID.fromString(ql4jId));
+
+        // 5. Camel SEARCH finds QL4J-written data
+        Awaitility.await().pollInterval(250, TimeUnit.MILLISECONDS).atMost(10, 
TimeUnit.SECONDS).untilAsserted(() -> {
+            RestAssured.given()
+                    .body(QL4J_MESSAGE)
+                    .post("/langchain4j-embeddingstore/qdrant/search")
+                    .then()
+                    .statusCode(200)
+                    .body(
+                            "size()", greaterThanOrEqualTo(1),
+                            "[0].text", is(QL4J_MESSAGE));
+        });
+
+        // 6. Camel REMOVE both embeddings with confirmation
+        RestAssured.given()
+                .queryParam("embeddingId", id)
+                .delete("/langchain4j-embeddingstore/qdrant/remove")
+                .then()
+                .statusCode(204);
+
+        RestAssured.given()
+                .queryParam("embeddingId", ql4jId)
+                .delete("/langchain4j-embeddingstore/qdrant/remove")
+                .then()
+                .statusCode(204);
+
+        Awaitility.await().pollInterval(250, TimeUnit.MILLISECONDS).atMost(10, 
TimeUnit.SECONDS).untilAsserted(() -> {
+            RestAssured.given()
+                    .body(CAMEL_MESSAGE)
+                    .post("/langchain4j-embeddingstore/qdrant/search")
+                    .then()
+                    .statusCode(200)
+                    .body(
+                            "size()", is(0));
+        });
+    }
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/OllamaWireMockTestResource.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/OllamaWireMockTestResource.java
new file mode 100644
index 0000000000..65758d7186
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/OllamaWireMockTestResource.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import java.util.Map;
+
+import 
org.apache.camel.quarkus.test.wiremock.WireMockTestResourceLifecycleManager;
+
+public class OllamaWireMockTestResource extends 
WireMockTestResourceLifecycleManager {
+    private static final String OLLAMA_ENV_URL = "LANGCHAIN4J_OLLAMA_BASE_URL";
+
+    @Override
+    public Map<String, String> start() {
+        Map<String, String> properties = super.start();
+        String wiremockUrl = properties.get("wiremock.url");
+        String url = wiremockUrl != null ? wiremockUrl : 
getRecordTargetBaseUrl();
+        properties.put("quarkus.langchain4j.ollama.base-url", url);
+        return properties;
+    }
+
+    @Override
+    protected String getRecordTargetBaseUrl() {
+        return System.getenv(OLLAMA_ENV_URL);
+    }
+
+    @Override
+    protected boolean isMockingEnabled() {
+        return !envVarsPresent(OLLAMA_ENV_URL);
+    }
+
+    @Override
+    protected String getStubsSubdirectory() {
+        return "embeddings";
+    }
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/QdrantTestResource.java
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/QdrantTestResource.java
new file mode 100644
index 0000000000..fd21a4f68e
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/QdrantTestResource.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.langchain4j.embeddingstore.ql4j.it;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.qdrant.QdrantContainer;
+import org.testcontainers.utility.DockerImageName;
+
+public class QdrantTestResource implements QuarkusTestResourceLifecycleManager 
{
+    private static final Logger LOG = 
LoggerFactory.getLogger(QdrantTestResource.class);
+    private static final String QDRANT_IMAGE = 
ConfigProvider.getConfig().getValue("qdrant.container.image", String.class);
+    private static final int QDRANT_HTTP_PORT = 6333;
+    private static final String COLLECTION_NAME = 
"camel-quarkus-ql4j-collection";
+
+    private GenericContainer<?> qdrantContainer;
+
+    @Override
+    public Map<String, String> start() {
+        Map<String, String> properties = new HashMap<>();
+
+        DockerImageName qdrantImageName = 
DockerImageName.parse(QDRANT_IMAGE).asCompatibleSubstituteFor("qdrant/qdrant");
+        qdrantContainer = new QdrantContainer(qdrantImageName)
+                .withLogConsumer(new Slf4jLogConsumer(LOG));
+        qdrantContainer.start();
+
+        String host = qdrantContainer.getHost();
+        Integer httpPort = qdrantContainer.getMappedPort(QDRANT_HTTP_PORT);
+
+        createCollection(host, httpPort);
+
+        properties.put("quarkus.qdrant.host", host);
+        properties.put("quarkus.qdrant.port", httpPort.toString());
+
+        LOG.info("Properties: {}", properties);
+
+        return properties;
+    }
+
+    private void createCollection(String host, int port) {
+        try {
+            HttpClient httpClient = HttpClient.newHttpClient();
+            String url = String.format("http://%s:%d/collections/%s";, host, 
port, COLLECTION_NAME);
+            // Vector size must match the embedding dimension in the WireMock 
stubs under mappings/embeddings/
+            String body = """
+                    {
+                      "vectors": {
+                        "size": 5,
+                        "distance": "Cosine"
+                      }
+                    }
+                    """;
+            HttpRequest request = HttpRequest.newBuilder()
+                    .uri(URI.create(url))
+                    .PUT(HttpRequest.BodyPublishers.ofString(body))
+                    .header("Content-Type", "application/json")
+                    .build();
+            HttpResponse<String> response = httpClient.send(request, 
HttpResponse.BodyHandlers.ofString());
+            if (response.statusCode() != 200) {
+                throw new RuntimeException("Failed to create Qdrant 
collection: " + response.body());
+            }
+            LOG.info("Created Qdrant collection '{}': {}", COLLECTION_NAME, 
response.body());
+        } catch (IOException | InterruptedException e) {
+            throw new RuntimeException("Failed to create Qdrant collection", 
e);
+        }
+    }
+
+    @Override
+    public void stop() {
+        try {
+            if (qdrantContainer != null) {
+                qdrantContainer.stop();
+            }
+        } catch (Exception ex) {
+            LOG.error("An issue occurred while stopping Qdrant container", ex);
+        }
+    }
+
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-camel-store.json
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-camel-store.json
new file mode 100644
index 0000000000..0a39aebc01
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-camel-store.json
@@ -0,0 +1,22 @@
+{
+  "id" : "b1c2d3e4-f5a6-7890-abcd-ef1234567801",
+  "name" : "api_embed_camel_store",
+  "request" : {
+    "url" : "/api/embed",
+    "method" : "POST",
+    "bodyPatterns" : [ {
+      "equalToJson" : "{\"model\":\"nomic-embed-text\",\"input\":\"Hello Camel 
Quarkus LangChain4j Embedding Store\"}",
+      "ignoreArrayOrder" : true,
+      "ignoreExtraElements" : true
+    } ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : 
"{\"model\":\"nomic-embed-text\",\"embeddings\":[[0.8,0.1,0.3,0.5,0.2]]}",
+    "headers" : {
+      "Content-Type" : "application/json; charset=utf-8"
+    }
+  },
+  "uuid" : "b1c2d3e4-f5a6-7890-abcd-ef1234567801",
+  "persistent" : true
+}
diff --git 
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-ql4j-ingest.json
 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-ql4j-ingest.json
new file mode 100644
index 0000000000..6a1834712f
--- /dev/null
+++ 
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-ql4j-ingest.json
@@ -0,0 +1,22 @@
+{
+  "id" : "b1c2d3e4-f5a6-7890-abcd-ef1234567802",
+  "name" : "api_embed_ql4j_ingest",
+  "request" : {
+    "url" : "/api/embed",
+    "method" : "POST",
+    "bodyPatterns" : [ {
+      "equalToJson" : "{\"model\":\"nomic-embed-text\",\"input\":\"Hello from 
Quarkus LangChain4j direct ingest\"}",
+      "ignoreArrayOrder" : true,
+      "ignoreExtraElements" : true
+    } ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : 
"{\"model\":\"nomic-embed-text\",\"embeddings\":[[0.2,0.7,0.4,0.1,0.9]]}",
+    "headers" : {
+      "Content-Type" : "application/json; charset=utf-8"
+    }
+  },
+  "uuid" : "b1c2d3e4-f5a6-7890-abcd-ef1234567802",
+  "persistent" : true
+}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 901ecc7b62..d20a4931cf 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -167,6 +167,7 @@
         <module>langchain4j-embeddings</module>
         <module>langchain4j-embeddings-ql4j</module>
         <module>langchain4j-embeddingstore</module>
+        <module>langchain4j-embeddingstore-ql4j</module>
         <module>langchain4j-ingest</module>
         <module>langchain4j-rag-bridge-ql4j</module>
         <module>langchain4j-tokenizer</module>
diff --git a/tooling/scripts/test-categories.yaml 
b/tooling/scripts/test-categories.yaml
index 8da660ecd9..1a342ec5e3 100644
--- a/tooling/scripts/test-categories.yaml
+++ b/tooling/scripts/test-categories.yaml
@@ -262,6 +262,7 @@ group-13:
   - jdbc-grouped
   - kubernetes
   - langchain4j-embeddingstore
+  - langchain4j-embeddingstore-ql4j
   - milvus
   - salesforce
   - sap-netweaver

Reply via email to