Skip to content

Commit ab4a8f6

Browse files
committed
Add a simple integration test + input validation robustness
1 parent 3772599 commit ab4a8f6

File tree

5 files changed

+176
-4
lines changed

5 files changed

+176
-4
lines changed

components/camel-ai/camel-openai/pom.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
<name>Camel :: AI :: OpenAI</name>
3434
<description>Camel OpenAI component for chat completion using OpenAI API</description>
3535

36+
<properties>
37+
<failsafe.rerunFailingTestsCount>3</failsafe.rerunFailingTestsCount>
38+
</properties>
39+
3640
<dependencies>
3741
<dependency>
3842
<groupId>org.apache.camel</groupId>
@@ -66,5 +70,17 @@
6670
<artifactId>camel-jackson</artifactId>
6771
<scope>test</scope>
6872
</dependency>
73+
<dependency>
74+
<groupId>org.apache.camel</groupId>
75+
<artifactId>camel-test-infra-ollama</artifactId>
76+
<version>${project.version}</version>
77+
<type>test-jar</type>
78+
<scope>test</scope>
79+
</dependency>
80+
<dependency>
81+
<groupId>org.assertj</groupId>
82+
<artifactId>assertj-core</artifactId>
83+
<scope>test</scope>
84+
</dependency>
6985
</dependencies>
7086
</project>

components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,14 @@ private List<ChatCompletionMessageParam> buildMessages(Exchange exchange, OpenAI
206206
addConversationHistory(messages, in, config);
207207

208208
ChatCompletionMessageParam userMessage = buildUserMessage(in, config);
209-
messages.add(userMessage);
209+
if (userMessage != null) {
210+
messages.add(userMessage);
211+
}
212+
213+
if (messages.isEmpty()) {
214+
throw new IllegalArgumentException(
215+
"No input provided to LLM. At least one message (user, system, or developer) must be provided");
216+
}
210217

211218
return messages;
212219
}
@@ -243,9 +250,8 @@ private ChatCompletionMessageParam buildUserMessage(Message in, OpenAIConfigurat
243250

244251
private ChatCompletionMessageParam buildTextMessage(Message in, String userPrompt, OpenAIConfiguration config) {
245252
String prompt = userPrompt != null ? userPrompt : in.getBody(String.class);
246-
if (prompt == null || prompt.isEmpty()) {
247-
throw new IllegalArgumentException(
248-
"Message body or user message configuration must contain the prompt text");
253+
if (prompt == null || prompt.trim().isEmpty()) {
254+
return null;
249255
}
250256
return createTextMessage(prompt);
251257
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.component.openai.integration;
18+
19+
import org.apache.camel.builder.RouteBuilder;
20+
import org.apache.camel.component.mock.MockEndpoint;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.junit.jupiter.api.Assertions.assertThrows;
26+
27+
@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*",
28+
disabledReason = "Requires too much network resources")
29+
public class OpenAIChatCompletionIT extends OpenAITestSupport {
30+
31+
@Override
32+
protected RouteBuilder createRouteBuilder() {
33+
return new RouteBuilder() {
34+
@Override
35+
public void configure() {
36+
// Route for simple message test
37+
from("direct:send-simple-message")
38+
.toF("openai:chat-completion?apiKey=%s&baseUrl=%s&model=%s", apiKey, baseUrl, model)
39+
.to("mock:response");
40+
}
41+
};
42+
}
43+
44+
@Test
45+
public void testSendSimpleStringMessage() throws Exception {
46+
// Setup mock endpoint expectations
47+
MockEndpoint mockResponse = getMockEndpoint("mock:response");
48+
mockResponse.expectedMessageCount(1);
49+
50+
// Send a test message to the OpenAI endpoint
51+
String response = template.requestBody("direct:send-simple-message",
52+
"What is Apache Camel?",
53+
String.class);
54+
55+
// Verify the mock endpoint received the message
56+
mockResponse.assertIsSatisfied();
57+
58+
// Verify response is not null and contains meaningful content
59+
assertThat(response).isNotNull();
60+
assertThat(response).isNotEmpty();
61+
assertThat(response.length()).isGreaterThan(10);
62+
63+
assertThat(response).contains("Camel");
64+
assertThat(response).contains("Apache");
65+
assertThat(response).contains("integration");
66+
}
67+
68+
@Test
69+
public void testEmptyMessageThrowsException() {
70+
// Verify that empty messages result in an IllegalArgumentException
71+
Exception exception = assertThrows(Exception.class, () -> {
72+
template.requestBody("direct:send-simple-message", "", String.class);
73+
});
74+
75+
// Verify the exception is an IllegalArgumentException about empty input
76+
assertThat(exception.getCause()).isInstanceOf(IllegalArgumentException.class);
77+
assertThat(exception.getCause().getMessage()).contains("No input provided to LLM");
78+
}
79+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.component.openai.integration;
18+
19+
import org.apache.camel.test.infra.ollama.services.OllamaService;
20+
import org.apache.camel.test.infra.ollama.services.OllamaServiceFactory;
21+
import org.apache.camel.test.junit5.CamelTestSupport;
22+
23+
public class OpenAITestSupport extends CamelTestSupport {
24+
25+
protected String apiKey;
26+
protected String baseUrl;
27+
protected String model;
28+
29+
static OllamaService OLLAMA = hasEnvironmentConfiguration()
30+
? null
31+
: OllamaServiceFactory.createSingletonService();
32+
33+
@Override
34+
protected void setupResources() throws Exception {
35+
super.setupResources();
36+
37+
if (OLLAMA != null) {
38+
// Use Ollama service
39+
baseUrl = OLLAMA.baseUrlV1();
40+
model = OLLAMA.modelName();
41+
apiKey = "dummy"; // Ollama doesn't require API key
42+
} else {
43+
// Use environment variables
44+
apiKey = System.getenv("OPENAI_API_KEY");
45+
baseUrl = System.getenv("OPENAI_BASE_URL"); // Optional
46+
model = System.getenv("OPENAI_MODEL"); // Optional
47+
}
48+
}
49+
50+
protected static boolean hasEnvironmentConfiguration() {
51+
String apiKey = System.getenv("OPENAI_API_KEY");
52+
return apiKey != null && !apiKey.trim().isEmpty();
53+
}
54+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## Test execution
2+
3+
### MacOS or Linux without nvidia graphic card
4+
If ollama is already installed on the system execute the test with
5+
6+
```bash
7+
mvn verify -Dollama.endpoint=http://localhost:11434/ -Dollama.model=granite4:3b -Dollama.instance.type=remote
8+
```
9+
10+
The Ollama docker image is really slow on macbook without nvidia hardware acceleration
11+
12+
### Linux with Nvidia graphic card
13+
The hardware acceleration can be used, and the test can be executed with
14+
15+
```bash
16+
mvn verify -Dollama.container.enable.gpu=enabled
17+
```

0 commit comments

Comments
 (0)