Skip to content

test: Add comprehensive test coverage for tool execution classes #4058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -235,4 +235,38 @@ void deprecatedMethodsShouldWorkCorrectly() {
assertThat(options.getInternalToolExecutionEnabled()).isTrue();
}

@Test
void defaultConstructorShouldInitializeWithEmptyCollections() {
DefaultToolCallingChatOptions options = new DefaultToolCallingChatOptions();

assertThat(options.getToolCallbacks()).isEmpty();
assertThat(options.getToolNames()).isEmpty();
assertThat(options.getToolContext()).isEmpty();
assertThat(options.getInternalToolExecutionEnabled()).isNull();
}

@Test
void builderShouldHandleEmptyCollections() {
ToolCallingChatOptions options = DefaultToolCallingChatOptions.builder()
.toolCallbacks(List.of())
.toolNames(Set.of())
.toolContext(Map.of())
.build();

assertThat(options.getToolCallbacks()).isEmpty();
assertThat(options.getToolNames()).isEmpty();
assertThat(options.getToolContext()).isEmpty();
}

@Test
void setInternalToolExecutionEnabledShouldAcceptNullValue() {
DefaultToolCallingChatOptions options = new DefaultToolCallingChatOptions();
options.setInternalToolExecutionEnabled(true);
assertThat(options.getInternalToolExecutionEnabled()).isTrue();

// Should be able to set back to null
options.setInternalToolExecutionEnabled(null);
assertThat(options.getInternalToolExecutionEnabled()).isNull();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,69 @@ void whenEmptyGenerationsList() {
assertThat(result).isFalse();
}

@Test
void whenMultipleGenerationsWithMixedToolCalls() {
// Create a ToolCallingChatOptions with internal tool execution enabled
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();

// Create multiple generations - some with tool calls, some without
AssistantMessage.ToolCall toolCall = new AssistantMessage.ToolCall("id1", "function", "testTool", "{}");
AssistantMessage messageWithToolCall = new AssistantMessage("test1", Map.of(), List.of(toolCall));
AssistantMessage messageWithoutToolCall = new AssistantMessage("test2");

ChatResponse chatResponse = new ChatResponse(
List.of(new Generation(messageWithToolCall), new Generation(messageWithoutToolCall)));

// Test the predicate - should return true if any generation has tool calls
boolean result = this.predicate.test(options, chatResponse);
assertThat(result).isTrue();
}

@Test
void whenMultipleGenerationsWithoutToolCalls() {
// Create a ToolCallingChatOptions with internal tool execution enabled
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();

// Create multiple generations without tool calls
AssistantMessage message1 = new AssistantMessage("test1");
AssistantMessage message2 = new AssistantMessage("test2");

ChatResponse chatResponse = new ChatResponse(List.of(new Generation(message1), new Generation(message2)));

// Test the predicate
boolean result = this.predicate.test(options, chatResponse);
assertThat(result).isFalse();
}

@Test
void whenAssistantMessageHasEmptyToolCallsList() {
// Create a ToolCallingChatOptions with internal tool execution enabled
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();

// Create a ChatResponse with AssistantMessage having empty tool calls list
AssistantMessage assistantMessage = new AssistantMessage("test", Map.of(), List.of());
ChatResponse chatResponse = new ChatResponse(List.of(new Generation(assistantMessage)));

// Test the predicate
boolean result = this.predicate.test(options, chatResponse);
assertThat(result).isFalse();
}

@Test
void whenMultipleToolCallsPresent() {
// Create a ToolCallingChatOptions with internal tool execution enabled
ToolCallingChatOptions options = ToolCallingChatOptions.builder().internalToolExecutionEnabled(true).build();

// Create a ChatResponse with multiple tool calls
AssistantMessage.ToolCall toolCall1 = new AssistantMessage.ToolCall("id1", "function", "testTool1", "{}");
AssistantMessage.ToolCall toolCall2 = new AssistantMessage.ToolCall("id2", "function", "testTool2",
"{\"param\": \"value\"}");
AssistantMessage assistantMessage = new AssistantMessage("test", Map.of(), List.of(toolCall1, toolCall2));
ChatResponse chatResponse = new ChatResponse(List.of(new Generation(assistantMessage)));

// Test the predicate
boolean result = this.predicate.test(options, chatResponse);
assertThat(result).isTrue();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,135 @@ void builder() {
assertThat(result.returnDirect()).isTrue();
}

@Test
void whenBuilderWithMinimalRequiredFields() {
var conversationHistory = new ArrayList<Message>();
var result = DefaultToolExecutionResult.builder().conversationHistory(conversationHistory).build();

assertThat(result.conversationHistory()).isEqualTo(conversationHistory);
assertThat(result.returnDirect()).isFalse(); // Default value should be false
}

@Test
void whenBuilderWithReturnDirectFalse() {
var conversationHistory = new ArrayList<Message>();
var result = DefaultToolExecutionResult.builder()
.conversationHistory(conversationHistory)
.returnDirect(false)
.build();

assertThat(result.conversationHistory()).isEqualTo(conversationHistory);
assertThat(result.returnDirect()).isFalse();
}

@Test
void whenConversationHistoryIsEmpty() {
var conversationHistory = new ArrayList<Message>();
var result = DefaultToolExecutionResult.builder()
.conversationHistory(conversationHistory)
.returnDirect(true)
.build();

assertThat(result.conversationHistory()).isEmpty();
assertThat(result.returnDirect()).isTrue();
}

@Test
void whenConversationHistoryHasMultipleMessages() {
var conversationHistory = new ArrayList<Message>();
var message1 = new org.springframework.ai.chat.messages.UserMessage("Hello");
var message2 = new org.springframework.ai.chat.messages.AssistantMessage("Hi there!");
conversationHistory.add(message1);
conversationHistory.add(message2);

var result = DefaultToolExecutionResult.builder()
.conversationHistory(conversationHistory)
.returnDirect(false)
.build();

assertThat(result.conversationHistory()).hasSize(2);
assertThat(result.conversationHistory()).containsExactly(message1, message2);
assertThat(result.returnDirect()).isFalse();
}

@Test
void whenConversationHistoryHasNullElementsInMiddle() {
var history = new ArrayList<Message>();
history.add(new org.springframework.ai.chat.messages.UserMessage("First message"));
history.add(null);
history.add(new org.springframework.ai.chat.messages.AssistantMessage("Last message"));

assertThatThrownBy(() -> DefaultToolExecutionResult.builder().conversationHistory(history).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("conversationHistory cannot contain null elements");
}

@Test
void whenConversationHistoryHasMultipleNullElements() {
var history = new ArrayList<Message>();
history.add(null);
history.add(null);
history.add(new org.springframework.ai.chat.messages.UserMessage("Valid message"));

assertThatThrownBy(() -> DefaultToolExecutionResult.builder().conversationHistory(history).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("conversationHistory cannot contain null elements");
}

@Test
void whenBuilderIsReused() {
var conversationHistory1 = new ArrayList<Message>();
conversationHistory1.add(new org.springframework.ai.chat.messages.UserMessage("Message 1"));

var conversationHistory2 = new ArrayList<Message>();
conversationHistory2.add(new org.springframework.ai.chat.messages.UserMessage("Message 2"));

var builder = DefaultToolExecutionResult.builder();

var result1 = builder.conversationHistory(conversationHistory1).returnDirect(true).build();

var result2 = builder.conversationHistory(conversationHistory2).returnDirect(false).build();

assertThat(result1.conversationHistory()).isEqualTo(conversationHistory1);
assertThat(result1.returnDirect()).isTrue();
assertThat(result2.conversationHistory()).isEqualTo(conversationHistory2);
assertThat(result2.returnDirect()).isFalse();
}

@Test
void whenConversationHistoryIsModifiedAfterBuilding() {
var conversationHistory = new ArrayList<Message>();
var originalMessage = new org.springframework.ai.chat.messages.UserMessage("Original");
conversationHistory.add(originalMessage);

var result = DefaultToolExecutionResult.builder().conversationHistory(conversationHistory).build();

// Modify the original list after building
conversationHistory.add(new org.springframework.ai.chat.messages.AssistantMessage("Added later"));

// The result should reflect the modification if the same list reference is used
// This tests whether the builder stores a reference or creates a copy
assertThat(result.conversationHistory()).hasSize(2);
assertThat(result.conversationHistory().get(0)).isEqualTo(originalMessage);
}

@Test
void whenEqualsAndHashCodeAreConsistent() {
var conversationHistory = new ArrayList<Message>();
conversationHistory.add(new org.springframework.ai.chat.messages.UserMessage("Test message"));

var result1 = DefaultToolExecutionResult.builder()
.conversationHistory(conversationHistory)
.returnDirect(true)
.build();

var result2 = DefaultToolExecutionResult.builder()
.conversationHistory(conversationHistory)
.returnDirect(true)
.build();

assertThat(result1).isEqualTo(result2);
assertThat(result1.hashCode()).isEqualTo(result2.hashCode());
}

}