Skip to content

Commit 882d64d

Browse files
committed
test(agentic-ai): add unit tests for AgentCore Harness converters
- Add HarnessToolConverterTest (7 tests) - Add HarnessMessageConverterTest (12 tests) - Cover tool definition conversion, message conversion, and error handling
1 parent 10c027b commit 882d64d

2 files changed

Lines changed: 357 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
3+
* under one or more contributor license agreements. Licensed under a proprietary license.
4+
* See the License.txt file for more information. You may not use this file
5+
* except in compliance with the proprietary license.
6+
*/
7+
package io.camunda.connector.agenticai.aiagent.framework.agentcoreharness;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
import io.camunda.connector.agenticai.model.message.AssistantMessage;
12+
import io.camunda.connector.agenticai.model.message.Message;
13+
import io.camunda.connector.agenticai.model.message.SystemMessage;
14+
import io.camunda.connector.agenticai.model.message.ToolCallResultMessage;
15+
import io.camunda.connector.agenticai.model.message.UserMessage;
16+
import io.camunda.connector.agenticai.model.message.content.TextContent;
17+
import io.camunda.connector.agenticai.model.tool.ToolCall;
18+
import io.camunda.connector.agenticai.model.tool.ToolCallResult;
19+
import java.util.List;
20+
import java.util.Map;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.NullAndEmptySource;
24+
import software.amazon.awssdk.core.document.Document;
25+
import software.amazon.awssdk.services.bedrockagentcore.model.HarnessConversationRole;
26+
import software.amazon.awssdk.services.bedrockagentcore.model.HarnessToolUseBlock;
27+
import software.amazon.awssdk.services.bedrockagentcore.model.HarnessToolUseStatus;
28+
29+
class HarnessMessageConverterTest {
30+
31+
private final HarnessMessageConverter converter = new HarnessMessageConverter();
32+
33+
@ParameterizedTest
34+
@NullAndEmptySource
35+
void extractSystemPromptReturnsEmptyForNullOrEmpty(List<Message> messages) {
36+
var result = converter.extractSystemPrompt(messages != null ? messages : List.of());
37+
assertThat(result).isEmpty();
38+
}
39+
40+
@Test
41+
void extractSystemPromptExtractsSystemMessages() {
42+
var messages =
43+
List.<Message>of(systemMessage("You are a helpful assistant."), userMessage("Hello"));
44+
45+
var result = converter.extractSystemPrompt(messages);
46+
47+
assertThat(result).hasSize(1);
48+
assertThat(result.get(0).text()).isEqualTo("You are a helpful assistant.");
49+
}
50+
51+
@Test
52+
void extractSystemPromptHandlesMultipleSystemMessages() {
53+
var messages =
54+
List.<Message>of(systemMessage("First instruction"), systemMessage("Second instruction"));
55+
56+
var result = converter.extractSystemPrompt(messages);
57+
58+
assertThat(result).hasSize(2);
59+
assertThat(result.get(0).text()).isEqualTo("First instruction");
60+
assertThat(result.get(1).text()).isEqualTo("Second instruction");
61+
}
62+
63+
@Test
64+
void toHarnessMessagesExcludesSystemMessages() {
65+
var messages = List.<Message>of(systemMessage("System prompt"), userMessage("User message"));
66+
67+
var result = converter.toHarnessMessages(messages);
68+
69+
assertThat(result).hasSize(1);
70+
assertThat(result.get(0).role()).isEqualTo(HarnessConversationRole.USER);
71+
}
72+
73+
@Test
74+
void toHarnessMessagesConvertsUserMessage() {
75+
var messages = List.<Message>of(userMessage("Hello, how are you?"));
76+
77+
var result = converter.toHarnessMessages(messages);
78+
79+
assertThat(result).hasSize(1);
80+
assertThat(result.get(0).role()).isEqualTo(HarnessConversationRole.USER);
81+
assertThat(result.get(0).content()).hasSize(1);
82+
assertThat(result.get(0).content().get(0).text()).isEqualTo("Hello, how are you?");
83+
}
84+
85+
@Test
86+
void toHarnessMessagesConvertsAssistantMessage() {
87+
var assistantMessage =
88+
AssistantMessage.builder()
89+
.content(List.of(TextContent.textContent("I'm doing well, thank you!")))
90+
.build();
91+
var messages = List.<Message>of(assistantMessage);
92+
93+
var result = converter.toHarnessMessages(messages);
94+
95+
assertThat(result).hasSize(1);
96+
assertThat(result.get(0).role()).isEqualTo(HarnessConversationRole.ASSISTANT);
97+
assertThat(result.get(0).content().get(0).text()).isEqualTo("I'm doing well, thank you!");
98+
}
99+
100+
@Test
101+
void toHarnessMessagesConvertsAssistantMessageWithToolCalls() {
102+
var toolCall =
103+
ToolCall.builder()
104+
.id("call_123")
105+
.name("get_weather")
106+
.arguments(Map.of("location", "Seattle"))
107+
.build();
108+
var assistantMessage =
109+
AssistantMessage.builder()
110+
.content(List.of(TextContent.textContent("Let me check the weather.")))
111+
.toolCalls(List.of(toolCall))
112+
.build();
113+
var messages = List.<Message>of(assistantMessage);
114+
115+
var result = converter.toHarnessMessages(messages);
116+
117+
assertThat(result).hasSize(1);
118+
assertThat(result.get(0).content()).hasSize(2);
119+
// First content block is text
120+
assertThat(result.get(0).content().get(0).text()).isEqualTo("Let me check the weather.");
121+
// Second content block is tool use
122+
var toolUse = result.get(0).content().get(1).toolUse();
123+
assertThat(toolUse.toolUseId()).isEqualTo("call_123");
124+
assertThat(toolUse.name()).isEqualTo("get_weather");
125+
}
126+
127+
@Test
128+
void toHarnessMessagesConvertsToolCallResultMessage() {
129+
var toolResult =
130+
ToolCallResult.builder().id("call_123").name("get_weather").content("Sunny, 72°F").build();
131+
var messages = List.<Message>of(toolCallResultMessage(toolResult));
132+
133+
var result = converter.toHarnessMessages(messages);
134+
135+
assertThat(result).hasSize(1);
136+
assertThat(result.get(0).role()).isEqualTo(HarnessConversationRole.USER);
137+
var toolResultBlock = result.get(0).content().get(0).toolResult();
138+
assertThat(toolResultBlock.toolUseId()).isEqualTo("call_123");
139+
assertThat(toolResultBlock.status()).isEqualTo(HarnessToolUseStatus.SUCCESS);
140+
}
141+
142+
@Test
143+
void toHarnessMessagesConvertsErrorToolCallResult() {
144+
var toolResult =
145+
ToolCallResult.builder()
146+
.id("call_456")
147+
.name("failing_tool")
148+
.content("Error occurred")
149+
.properties(Map.of(ToolCallResult.PROPERTY_INTERRUPTED, true))
150+
.build();
151+
var messages = List.<Message>of(toolCallResultMessage(toolResult));
152+
153+
var result = converter.toHarnessMessages(messages);
154+
155+
var toolResultBlock = result.get(0).content().get(0).toolResult();
156+
assertThat(toolResultBlock.status()).isEqualTo(HarnessToolUseStatus.ERROR);
157+
}
158+
159+
@Test
160+
void toToolCallConvertsHarnessToolUseBlock() {
161+
var toolUseBlock =
162+
HarnessToolUseBlock.builder()
163+
.toolUseId("tool_use_abc")
164+
.name("search_database")
165+
.input(
166+
Document.fromMap(
167+
Map.of(
168+
"query", Document.fromString("test query"),
169+
"limit", Document.fromNumber("10"))))
170+
.build();
171+
172+
var result = converter.toToolCall(toolUseBlock);
173+
174+
assertThat(result.id()).isEqualTo("tool_use_abc");
175+
assertThat(result.name()).isEqualTo("search_database");
176+
assertThat(result.arguments()).containsEntry("query", "test query");
177+
}
178+
179+
@Test
180+
void toHarnessMessagesHandlesConversationFlow() {
181+
var messages =
182+
List.<Message>of(
183+
systemMessage("You are helpful."),
184+
userMessage("What's the weather?"),
185+
AssistantMessage.builder()
186+
.content(List.of(TextContent.textContent("Checking...")))
187+
.toolCalls(
188+
List.of(
189+
ToolCall.builder()
190+
.id("call_1")
191+
.name("get_weather")
192+
.arguments(Map.of("location", "NYC"))
193+
.build()))
194+
.build(),
195+
toolCallResultMessage(
196+
ToolCallResult.builder()
197+
.id("call_1")
198+
.name("get_weather")
199+
.content("Rainy, 55°F")
200+
.build()),
201+
AssistantMessage.builder()
202+
.content(List.of(TextContent.textContent("It's rainy in NYC.")))
203+
.build());
204+
205+
var systemPrompt = converter.extractSystemPrompt(messages);
206+
var harnessMessages = converter.toHarnessMessages(messages);
207+
208+
assertThat(systemPrompt).hasSize(1);
209+
assertThat(harnessMessages).hasSize(4); // user, assistant+tool, tool_result, assistant
210+
assertThat(harnessMessages.get(0).role()).isEqualTo(HarnessConversationRole.USER);
211+
assertThat(harnessMessages.get(1).role()).isEqualTo(HarnessConversationRole.ASSISTANT);
212+
assertThat(harnessMessages.get(2).role()).isEqualTo(HarnessConversationRole.USER);
213+
assertThat(harnessMessages.get(3).role()).isEqualTo(HarnessConversationRole.ASSISTANT);
214+
}
215+
216+
// Helper methods to create messages
217+
private static SystemMessage systemMessage(String text) {
218+
return SystemMessage.builder().content(List.of(TextContent.textContent(text))).build();
219+
}
220+
221+
private static UserMessage userMessage(String text) {
222+
return UserMessage.builder().content(List.of(TextContent.textContent(text))).build();
223+
}
224+
225+
private static ToolCallResultMessage toolCallResultMessage(ToolCallResult result) {
226+
return ToolCallResultMessage.builder().results(List.of(result)).build();
227+
}
228+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
3+
* under one or more contributor license agreements. Licensed under a proprietary license.
4+
* See the License.txt file for more information. You may not use this file
5+
* except in compliance with the proprietary license.
6+
*/
7+
package io.camunda.connector.agenticai.aiagent.framework.agentcoreharness;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
import io.camunda.connector.agenticai.model.tool.ToolDefinition;
12+
import java.util.List;
13+
import java.util.Map;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.params.ParameterizedTest;
16+
import org.junit.jupiter.params.provider.NullAndEmptySource;
17+
import software.amazon.awssdk.services.bedrockagentcore.model.HarnessToolType;
18+
19+
class HarnessToolConverterTest {
20+
21+
private final HarnessToolConverter converter = new HarnessToolConverter();
22+
23+
@ParameterizedTest
24+
@NullAndEmptySource
25+
void toHarnessToolsReturnsEmptyListForNullOrEmpty(List<ToolDefinition> toolDefinitions) {
26+
assertThat(converter.toHarnessTools(toolDefinitions)).isEmpty();
27+
}
28+
29+
@Test
30+
void toHarnessToolConvertsBasicToolDefinition() {
31+
var toolDefinition =
32+
ToolDefinition.builder()
33+
.name("get_weather")
34+
.description("Get the current weather for a location")
35+
.inputSchema(
36+
Map.of(
37+
"type", "object",
38+
"properties", Map.of("location", Map.of("type", "string")),
39+
"required", List.of("location")))
40+
.build();
41+
42+
var result = converter.toHarnessTool(toolDefinition);
43+
44+
assertThat(result.name()).isEqualTo("get_weather");
45+
assertThat(result.type()).isEqualTo(HarnessToolType.INLINE_FUNCTION);
46+
assertThat(result.config().inlineFunction()).isNotNull();
47+
assertThat(result.config().inlineFunction().description())
48+
.isEqualTo("Get the current weather for a location");
49+
}
50+
51+
@Test
52+
void toHarnessToolsConvertsMultipleTools() {
53+
var tool1 =
54+
ToolDefinition.builder()
55+
.name("tool_one")
56+
.description("First tool")
57+
.inputSchema(Map.of("type", "object"))
58+
.build();
59+
var tool2 =
60+
ToolDefinition.builder()
61+
.name("tool_two")
62+
.description("Second tool")
63+
.inputSchema(Map.of("type", "object"))
64+
.build();
65+
66+
var result = converter.toHarnessTools(List.of(tool1, tool2));
67+
68+
assertThat(result).hasSize(2);
69+
assertThat(result.get(0).name()).isEqualTo("tool_one");
70+
assertThat(result.get(1).name()).isEqualTo("tool_two");
71+
}
72+
73+
@Test
74+
void toHarnessToolHandlesComplexInputSchema() {
75+
var toolDefinition =
76+
ToolDefinition.builder()
77+
.name("complex_tool")
78+
.description("A tool with complex schema")
79+
.inputSchema(
80+
Map.of(
81+
"type", "object",
82+
"properties",
83+
Map.of(
84+
"name", Map.of("type", "string"),
85+
"count", Map.of("type", "number"),
86+
"enabled", Map.of("type", "boolean"),
87+
"tags", Map.of("type", "array", "items", Map.of("type", "string"))),
88+
"required", List.of("name")))
89+
.build();
90+
91+
var result = converter.toHarnessTool(toolDefinition);
92+
93+
assertThat(result.name()).isEqualTo("complex_tool");
94+
assertThat(result.config().inlineFunction().inputSchema()).isNotNull();
95+
var schema = result.config().inlineFunction().inputSchema();
96+
assertThat(schema.asMap()).containsKey("type");
97+
assertThat(schema.asMap().get("type").asString()).isEqualTo("object");
98+
}
99+
100+
@Test
101+
void toHarnessToolHandlesEmptyInputSchema() {
102+
var toolDefinition =
103+
ToolDefinition.builder()
104+
.name("no_params_tool")
105+
.description("A tool with no parameters")
106+
.inputSchema(Map.of())
107+
.build();
108+
109+
var result = converter.toHarnessTool(toolDefinition);
110+
111+
assertThat(result.name()).isEqualTo("no_params_tool");
112+
assertThat(result.config().inlineFunction().inputSchema().asMap()).isEmpty();
113+
}
114+
115+
@Test
116+
void toHarnessToolHandlesNullInputSchema() {
117+
var toolDefinition =
118+
ToolDefinition.builder()
119+
.name("null_schema_tool")
120+
.description("A tool with null schema")
121+
.inputSchema(null)
122+
.build();
123+
124+
var result = converter.toHarnessTool(toolDefinition);
125+
126+
assertThat(result.name()).isEqualTo("null_schema_tool");
127+
assertThat(result.config().inlineFunction().inputSchema().asMap()).isEmpty();
128+
}
129+
}

0 commit comments

Comments
 (0)