Skip to content

Commit fdef860

Browse files
committed
test: Add comprehensive test coverage for tool execution classes
Signed-off-by: Alex Klimenko <[email protected]>
1 parent e0ccc13 commit fdef860

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolCallingChatOptionsTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,38 @@ void deprecatedMethodsShouldWorkCorrectly() {
235235
assertThat(options.getInternalToolExecutionEnabled()).isTrue();
236236
}
237237

238+
@Test
239+
void defaultConstructorShouldInitializeWithEmptyCollections() {
240+
DefaultToolCallingChatOptions options = new DefaultToolCallingChatOptions();
241+
242+
assertThat(options.getToolCallbacks()).isEmpty();
243+
assertThat(options.getToolNames()).isEmpty();
244+
assertThat(options.getToolContext()).isEmpty();
245+
assertThat(options.getInternalToolExecutionEnabled()).isNull();
246+
}
247+
248+
@Test
249+
void builderShouldHandleEmptyCollections() {
250+
ToolCallingChatOptions options = DefaultToolCallingChatOptions.builder()
251+
.toolCallbacks(List.of())
252+
.toolNames(Set.of())
253+
.toolContext(Map.of())
254+
.build();
255+
256+
assertThat(options.getToolCallbacks()).isEmpty();
257+
assertThat(options.getToolNames()).isEmpty();
258+
assertThat(options.getToolContext()).isEmpty();
259+
}
260+
261+
@Test
262+
void setInternalToolExecutionEnabledShouldAcceptNullValue() {
263+
DefaultToolCallingChatOptions options = new DefaultToolCallingChatOptions();
264+
options.setInternalToolExecutionEnabled(true);
265+
assertThat(options.getInternalToolExecutionEnabled()).isTrue();
266+
267+
// Should be able to set back to null
268+
options.setInternalToolExecutionEnabled(null);
269+
assertThat(options.getInternalToolExecutionEnabled()).isNull();
270+
}
271+
238272
}

spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicateTests.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,69 @@ void whenEmptyGenerationsList() {
134134
assertThat(result).isFalse();
135135
}
136136

137+
@Test
138+
void whenMultipleGenerationsWithMixedToolCalls() {
139+
// Create a ToolCallingChatOptions with internal tool execution enabled
140+
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();
141+
142+
// Create multiple generations - some with tool calls, some without
143+
AssistantMessage.ToolCall toolCall = new AssistantMessage.ToolCall("id1", "function", "testTool", "{}");
144+
AssistantMessage messageWithToolCall = new AssistantMessage("test1", Map.of(), List.of(toolCall));
145+
AssistantMessage messageWithoutToolCall = new AssistantMessage("test2");
146+
147+
ChatResponse chatResponse = new ChatResponse(
148+
List.of(new Generation(messageWithToolCall), new Generation(messageWithoutToolCall)));
149+
150+
// Test the predicate - should return true if any generation has tool calls
151+
boolean result = this.predicate.test(options, chatResponse);
152+
assertThat(result).isTrue();
153+
}
154+
155+
@Test
156+
void whenMultipleGenerationsWithoutToolCalls() {
157+
// Create a ToolCallingChatOptions with internal tool execution enabled
158+
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();
159+
160+
// Create multiple generations without tool calls
161+
AssistantMessage message1 = new AssistantMessage("test1");
162+
AssistantMessage message2 = new AssistantMessage("test2");
163+
164+
ChatResponse chatResponse = new ChatResponse(List.of(new Generation(message1), new Generation(message2)));
165+
166+
// Test the predicate
167+
boolean result = this.predicate.test(options, chatResponse);
168+
assertThat(result).isFalse();
169+
}
170+
171+
@Test
172+
void whenAssistantMessageHasEmptyToolCallsList() {
173+
// Create a ToolCallingChatOptions with internal tool execution enabled
174+
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();
175+
176+
// Create a ChatResponse with AssistantMessage having empty tool calls list
177+
AssistantMessage assistantMessage = new AssistantMessage("test", Map.of(), List.of());
178+
ChatResponse chatResponse = new ChatResponse(List.of(new Generation(assistantMessage)));
179+
180+
// Test the predicate
181+
boolean result = this.predicate.test(options, chatResponse);
182+
assertThat(result).isFalse();
183+
}
184+
185+
@Test
186+
void whenMultipleToolCallsPresent() {
187+
// Create a ToolCallingChatOptions with internal tool execution enabled
188+
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();
189+
190+
// Create a ChatResponse with multiple tool calls
191+
AssistantMessage.ToolCall toolCall1 = new AssistantMessage.ToolCall("id1", "function", "testTool1", "{}");
192+
AssistantMessage.ToolCall toolCall2 = new AssistantMessage.ToolCall("id2", "function", "testTool2",
193+
"{\"param\": \"value\"}");
194+
AssistantMessage assistantMessage = new AssistantMessage("test", Map.of(), List.of(toolCall1, toolCall2));
195+
ChatResponse chatResponse = new ChatResponse(List.of(new Generation(assistantMessage)));
196+
197+
// Test the predicate
198+
boolean result = this.predicate.test(options, chatResponse);
199+
assertThat(result).isTrue();
200+
}
201+
137202
}

spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolExecutionResultTests.java

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,135 @@ void builder() {
5959
assertThat(result.returnDirect()).isTrue();
6060
}
6161

62+
@Test
63+
void whenBuilderWithMinimalRequiredFields() {
64+
var conversationHistory = new ArrayList<Message>();
65+
var result = DefaultToolExecutionResult.builder().conversationHistory(conversationHistory).build();
66+
67+
assertThat(result.conversationHistory()).isEqualTo(conversationHistory);
68+
assertThat(result.returnDirect()).isFalse(); // Default value should be false
69+
}
70+
71+
@Test
72+
void whenBuilderWithReturnDirectFalse() {
73+
var conversationHistory = new ArrayList<Message>();
74+
var result = DefaultToolExecutionResult.builder()
75+
.conversationHistory(conversationHistory)
76+
.returnDirect(false)
77+
.build();
78+
79+
assertThat(result.conversationHistory()).isEqualTo(conversationHistory);
80+
assertThat(result.returnDirect()).isFalse();
81+
}
82+
83+
@Test
84+
void whenConversationHistoryIsEmpty() {
85+
var conversationHistory = new ArrayList<Message>();
86+
var result = DefaultToolExecutionResult.builder()
87+
.conversationHistory(conversationHistory)
88+
.returnDirect(true)
89+
.build();
90+
91+
assertThat(result.conversationHistory()).isEmpty();
92+
assertThat(result.returnDirect()).isTrue();
93+
}
94+
95+
@Test
96+
void whenConversationHistoryHasMultipleMessages() {
97+
var conversationHistory = new ArrayList<Message>();
98+
var message1 = new org.springframework.ai.chat.messages.UserMessage("Hello");
99+
var message2 = new org.springframework.ai.chat.messages.AssistantMessage("Hi there!");
100+
conversationHistory.add(message1);
101+
conversationHistory.add(message2);
102+
103+
var result = DefaultToolExecutionResult.builder()
104+
.conversationHistory(conversationHistory)
105+
.returnDirect(false)
106+
.build();
107+
108+
assertThat(result.conversationHistory()).hasSize(2);
109+
assertThat(result.conversationHistory()).containsExactly(message1, message2);
110+
assertThat(result.returnDirect()).isFalse();
111+
}
112+
113+
@Test
114+
void whenConversationHistoryHasNullElementsInMiddle() {
115+
var history = new ArrayList<Message>();
116+
history.add(new org.springframework.ai.chat.messages.UserMessage("First message"));
117+
history.add(null);
118+
history.add(new org.springframework.ai.chat.messages.AssistantMessage("Last message"));
119+
120+
assertThatThrownBy(() -> DefaultToolExecutionResult.builder().conversationHistory(history).build())
121+
.isInstanceOf(IllegalArgumentException.class)
122+
.hasMessage("conversationHistory cannot contain null elements");
123+
}
124+
125+
@Test
126+
void whenConversationHistoryHasMultipleNullElements() {
127+
var history = new ArrayList<Message>();
128+
history.add(null);
129+
history.add(null);
130+
history.add(new org.springframework.ai.chat.messages.UserMessage("Valid message"));
131+
132+
assertThatThrownBy(() -> DefaultToolExecutionResult.builder().conversationHistory(history).build())
133+
.isInstanceOf(IllegalArgumentException.class)
134+
.hasMessage("conversationHistory cannot contain null elements");
135+
}
136+
137+
@Test
138+
void whenBuilderIsReused() {
139+
var conversationHistory1 = new ArrayList<Message>();
140+
conversationHistory1.add(new org.springframework.ai.chat.messages.UserMessage("Message 1"));
141+
142+
var conversationHistory2 = new ArrayList<Message>();
143+
conversationHistory2.add(new org.springframework.ai.chat.messages.UserMessage("Message 2"));
144+
145+
var builder = DefaultToolExecutionResult.builder();
146+
147+
var result1 = builder.conversationHistory(conversationHistory1).returnDirect(true).build();
148+
149+
var result2 = builder.conversationHistory(conversationHistory2).returnDirect(false).build();
150+
151+
assertThat(result1.conversationHistory()).isEqualTo(conversationHistory1);
152+
assertThat(result1.returnDirect()).isTrue();
153+
assertThat(result2.conversationHistory()).isEqualTo(conversationHistory2);
154+
assertThat(result2.returnDirect()).isFalse();
155+
}
156+
157+
@Test
158+
void whenConversationHistoryIsModifiedAfterBuilding() {
159+
var conversationHistory = new ArrayList<Message>();
160+
var originalMessage = new org.springframework.ai.chat.messages.UserMessage("Original");
161+
conversationHistory.add(originalMessage);
162+
163+
var result = DefaultToolExecutionResult.builder().conversationHistory(conversationHistory).build();
164+
165+
// Modify the original list after building
166+
conversationHistory.add(new org.springframework.ai.chat.messages.AssistantMessage("Added later"));
167+
168+
// The result should reflect the modification if the same list reference is used
169+
// This tests whether the builder stores a reference or creates a copy
170+
assertThat(result.conversationHistory()).hasSize(2);
171+
assertThat(result.conversationHistory().get(0)).isEqualTo(originalMessage);
172+
}
173+
174+
@Test
175+
void whenEqualsAndHashCodeAreConsistent() {
176+
var conversationHistory = new ArrayList<Message>();
177+
conversationHistory.add(new org.springframework.ai.chat.messages.UserMessage("Test message"));
178+
179+
var result1 = DefaultToolExecutionResult.builder()
180+
.conversationHistory(conversationHistory)
181+
.returnDirect(true)
182+
.build();
183+
184+
var result2 = DefaultToolExecutionResult.builder()
185+
.conversationHistory(conversationHistory)
186+
.returnDirect(true)
187+
.build();
188+
189+
assertThat(result1).isEqualTo(result2);
190+
assertThat(result1.hashCode()).isEqualTo(result2.hashCode());
191+
}
192+
62193
}

0 commit comments

Comments
 (0)