Skip to content

Commit 39b282a

Browse files
alxkmWillam2004
authored andcommitted
test: Add comprehensive edge case tests for ChatModel, ListOutputConverter, and ToolExecutionResult
Co-authored-by: Oleksandr Klymenko <[email protected]> Signed-off-by: Oleksandr Klymenko <[email protected]> Signed-off-by: 家娃 <[email protected]>
1 parent b98d232 commit 39b282a

File tree

3 files changed

+222
-0
lines changed

3 files changed

+222
-0
lines changed

spring-ai-model/src/test/java/org/springframework/ai/chat/ChatModelTests.java

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.ai.chat.prompt.Prompt;
2727

2828
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
2930
import static org.mockito.ArgumentMatchers.any;
3031
import static org.mockito.ArgumentMatchers.anyString;
3132
import static org.mockito.ArgumentMatchers.eq;
@@ -139,4 +140,139 @@ void generateWithWhitespaceOnlyStringHandlesCorrectly() {
139140
verify(mockClient, times(1)).call(eq(userMessage));
140141
}
141142

143+
@Test
144+
void generateWhenPromptCallThrowsExceptionPropagatesCorrectly() {
145+
String userMessage = "Test message";
146+
RuntimeException expectedException = new RuntimeException("API call failed");
147+
148+
ChatModel mockClient = Mockito.mock(ChatModel.class);
149+
150+
doCallRealMethod().when(mockClient).call(anyString());
151+
given(mockClient.call(any(Prompt.class))).willThrow(expectedException);
152+
153+
assertThatThrownBy(() -> mockClient.call(userMessage)).isEqualTo(expectedException);
154+
155+
verify(mockClient, times(1)).call(eq(userMessage));
156+
verify(mockClient, times(1)).call(isA(Prompt.class));
157+
}
158+
159+
@Test
160+
void generateWhenResponseIsNullHandlesGracefully() {
161+
String userMessage = "Test message";
162+
163+
ChatModel mockClient = Mockito.mock(ChatModel.class);
164+
165+
doCallRealMethod().when(mockClient).call(anyString());
166+
given(mockClient.call(any(Prompt.class))).willReturn(null);
167+
168+
assertThatThrownBy(() -> mockClient.call(userMessage)).isInstanceOf(NullPointerException.class);
169+
170+
verify(mockClient, times(1)).call(eq(userMessage));
171+
verify(mockClient, times(1)).call(isA(Prompt.class));
172+
}
173+
174+
@Test
175+
void generateWhenAssistantMessageIsNullHandlesGracefully() {
176+
String userMessage = "Test message";
177+
178+
ChatModel mockClient = Mockito.mock(ChatModel.class);
179+
180+
Generation generation = Mockito.mock(Generation.class);
181+
given(generation.getOutput()).willReturn(null);
182+
183+
ChatResponse response = Mockito.mock(ChatResponse.class);
184+
given(response.getResult()).willReturn(generation);
185+
186+
doCallRealMethod().when(mockClient).call(anyString());
187+
given(mockClient.call(any(Prompt.class))).willReturn(response);
188+
189+
assertThatThrownBy(() -> mockClient.call(userMessage)).isInstanceOf(NullPointerException.class);
190+
191+
verify(mockClient, times(1)).call(eq(userMessage));
192+
verify(generation, times(1)).getOutput();
193+
}
194+
195+
@Test
196+
void generateWhenAssistantMessageTextIsNullReturnsNull() {
197+
String userMessage = "Test message";
198+
199+
ChatModel mockClient = Mockito.mock(ChatModel.class);
200+
201+
AssistantMessage mockAssistantMessage = Mockito.mock(AssistantMessage.class);
202+
given(mockAssistantMessage.getText()).willReturn(null);
203+
204+
Generation generation = Mockito.mock(Generation.class);
205+
given(generation.getOutput()).willReturn(mockAssistantMessage);
206+
207+
ChatResponse response = Mockito.mock(ChatResponse.class);
208+
given(response.getResult()).willReturn(generation);
209+
210+
doCallRealMethod().when(mockClient).call(anyString());
211+
given(mockClient.call(any(Prompt.class))).willReturn(response);
212+
213+
String result = mockClient.call(userMessage);
214+
215+
assertThat(result).isNull();
216+
verify(mockClient, times(1)).call(eq(userMessage));
217+
verify(mockAssistantMessage, times(1)).getText();
218+
}
219+
220+
@Test
221+
void generateWithMultilineStringHandlesCorrectly() {
222+
String userMessage = "Line 1\nLine 2\r\nLine 3\rLine 4";
223+
String responseMessage = "Multiline input processed";
224+
225+
ChatModel mockClient = Mockito.mock(ChatModel.class);
226+
227+
AssistantMessage mockAssistantMessage = Mockito.mock(AssistantMessage.class);
228+
given(mockAssistantMessage.getText()).willReturn(responseMessage);
229+
230+
Generation generation = Mockito.mock(Generation.class);
231+
given(generation.getOutput()).willReturn(mockAssistantMessage);
232+
233+
ChatResponse response = Mockito.mock(ChatResponse.class);
234+
given(response.getResult()).willReturn(generation);
235+
236+
doCallRealMethod().when(mockClient).call(anyString());
237+
given(mockClient.call(any(Prompt.class))).willReturn(response);
238+
239+
String result = mockClient.call(userMessage);
240+
241+
assertThat(result).isEqualTo(responseMessage);
242+
verify(mockClient, times(1)).call(eq(userMessage));
243+
}
244+
245+
@Test
246+
void generateMultipleTimesWithSameClientMaintainsState() {
247+
ChatModel mockClient = Mockito.mock(ChatModel.class);
248+
249+
doCallRealMethod().when(mockClient).call(anyString());
250+
251+
// First call
252+
setupMockResponse(mockClient, "Response 1");
253+
String result1 = mockClient.call("Message 1");
254+
assertThat(result1).isEqualTo("Response 1");
255+
256+
// Second call
257+
setupMockResponse(mockClient, "Response 2");
258+
String result2 = mockClient.call("Message 2");
259+
assertThat(result2).isEqualTo("Response 2");
260+
261+
verify(mockClient, times(2)).call(anyString());
262+
verify(mockClient, times(2)).call(any(Prompt.class));
263+
}
264+
265+
private void setupMockResponse(ChatModel mockClient, String responseText) {
266+
AssistantMessage mockAssistantMessage = Mockito.mock(AssistantMessage.class);
267+
given(mockAssistantMessage.getText()).willReturn(responseText);
268+
269+
Generation generation = Mockito.mock(Generation.class);
270+
given(generation.getOutput()).willReturn(mockAssistantMessage);
271+
272+
ChatResponse response = Mockito.mock(ChatResponse.class);
273+
given(response.getResult()).willReturn(generation);
274+
275+
given(mockClient.call(any(Prompt.class))).willReturn(response);
276+
}
277+
142278
}

spring-ai-model/src/test/java/org/springframework/ai/converter/ListOutputConverterTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,43 @@ void csvWithOnlyWhitespace() {
178178
assertThat(list.get(0)).isBlank();
179179
}
180180

181+
@Test
182+
void csvWithCommasInQuotedValues() {
183+
String csvAsString = "\"value, with, commas\", normal, \"another, comma\"";
184+
List<String> list = this.listOutputConverter.convert(csvAsString);
185+
assertThat(list).isNotEmpty();
186+
assertThat(list).doesNotContainNull();
187+
}
188+
189+
@Test
190+
void csvWithNewlinesInQuotedValues() {
191+
String csvAsString = "\"line1\nline2\", normal, \"another\nline\"";
192+
List<String> list = this.listOutputConverter.convert(csvAsString);
193+
assertThat(list).isNotEmpty();
194+
assertThat(list).doesNotContainNull();
195+
}
196+
197+
@Test
198+
void csvWithMixedQuotingStyles() {
199+
String csvAsString = "'single quoted', \"double quoted\", `backtick quoted`, unquoted";
200+
List<String> list = this.listOutputConverter.convert(csvAsString);
201+
assertThat(list).hasSize(4);
202+
assertThat(list).doesNotContainNull();
203+
}
204+
205+
@Test
206+
void csvWithOnlyCommasAndSpaces() {
207+
String csvAsString = " , , , ";
208+
List<String> list = this.listOutputConverter.convert(csvAsString);
209+
assertThat(list).hasSize(4);
210+
assertThat(list).allMatch(String::isEmpty);
211+
}
212+
213+
@Test
214+
void csvWithMalformedQuoting() {
215+
String csvAsString = "\"unclosed quote, normal, \"properly closed\"";
216+
List<String> list = this.listOutputConverter.convert(csvAsString);
217+
assertThat(list).isNotEmpty();
218+
}
219+
181220
}

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,51 @@ void whenBuilderCalledWithoutConversationHistoryThenThrowsException() {
140140
assertThat(toolExecutionResult.conversationHistory()).isEmpty();
141141
}
142142

143+
@Test
144+
void whenMultipleToolResponseMessagesOnlyLastOneIsProcessed() {
145+
var toolExecutionResult = ToolExecutionResult.builder()
146+
.conversationHistory(List.of(new AssistantMessage("First response"),
147+
new ToolResponseMessage(
148+
List.of(new ToolResponseMessage.ToolResponse("1", "old_tool", "Old response"))),
149+
new AssistantMessage("Second response"),
150+
new ToolResponseMessage(
151+
List.of(new ToolResponseMessage.ToolResponse("2", "new_tool", "New response")))))
152+
.build();
153+
154+
var generations = ToolExecutionResult.buildGenerations(toolExecutionResult);
155+
156+
assertThat(generations).hasSize(1);
157+
assertThat(generations.get(0).getOutput().getText()).isEqualTo("New response");
158+
assertThat((String) generations.get(0).getMetadata().get(ToolExecutionResult.METADATA_TOOL_NAME))
159+
.isEqualTo("new_tool");
160+
}
161+
162+
@Test
163+
void whenToolResponseWithEmptyToolNameThenMetadataContainsEmptyString() {
164+
var toolExecutionResult = ToolExecutionResult.builder()
165+
.conversationHistory(List.of(new ToolResponseMessage(
166+
List.of(new ToolResponseMessage.ToolResponse("1", "", "Response content")))))
167+
.build();
168+
169+
var generations = ToolExecutionResult.buildGenerations(toolExecutionResult);
170+
171+
assertThat(generations).hasSize(1);
172+
assertThat((String) generations.get(0).getMetadata().get(ToolExecutionResult.METADATA_TOOL_NAME)).isEmpty();
173+
}
174+
175+
@Test
176+
void whenToolResponseWithNullToolIdThenGenerationStillCreated() {
177+
var toolExecutionResult = ToolExecutionResult.builder()
178+
.conversationHistory(List.of(new ToolResponseMessage(
179+
List.of(new ToolResponseMessage.ToolResponse(null, "tool", "Response content")))))
180+
.build();
181+
182+
var generations = ToolExecutionResult.buildGenerations(toolExecutionResult);
183+
184+
assertThat(generations).hasSize(1);
185+
assertThat(generations.get(0).getOutput().getText()).isEqualTo("Response content");
186+
assertThat((String) generations.get(0).getMetadata().get(ToolExecutionResult.METADATA_TOOL_NAME))
187+
.isEqualTo("tool");
188+
}
189+
143190
}

0 commit comments

Comments
 (0)