Skip to content

Commit efd0c47

Browse files
authored
Enhance MistralAiChatOptions test coverage with comprehensive edge cases (#3968)
What's Added - Edge cases: Empty collections, boundary values, single elements - Object behavior: Copy isolation, equals/hashCode contracts - API coverage: All enum values, response formats, builder chaining - Consistency: Builder vs setter equivalence testing Impact - Improved robustness and reliability - Better coverage of untested scenarios Signed-off-by: Alex Klimenko <[email protected]>
1 parent 7d11d0d commit efd0c47

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatOptionsTests.java

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.ai.mistralai;
1818

19+
import java.util.Collections;
1920
import java.util.List;
2021
import java.util.Map;
2122

@@ -124,4 +125,158 @@ void testDefaultValues() {
124125
assertThat(options.getResponseFormat()).isNull();
125126
}
126127

128+
@Test
129+
void testBuilderWithEmptyCollections() {
130+
MistralAiChatOptions options = MistralAiChatOptions.builder()
131+
.stop(Collections.emptyList())
132+
.toolContext(Collections.emptyMap())
133+
.build();
134+
135+
assertThat(options.getStop()).isEmpty();
136+
assertThat(options.getToolContext()).isEmpty();
137+
}
138+
139+
@Test
140+
void testBuilderWithBoundaryValues() {
141+
MistralAiChatOptions options = MistralAiChatOptions.builder()
142+
.temperature(0.0)
143+
.topP(1.0)
144+
.maxTokens(1)
145+
.randomSeed(Integer.MAX_VALUE)
146+
.build();
147+
148+
assertThat(options.getTemperature()).isEqualTo(0.0);
149+
assertThat(options.getTopP()).isEqualTo(1.0);
150+
assertThat(options.getMaxTokens()).isEqualTo(1);
151+
assertThat(options.getRandomSeed()).isEqualTo(Integer.MAX_VALUE);
152+
}
153+
154+
@Test
155+
void testBuilderWithSingleElementCollections() {
156+
MistralAiChatOptions options = MistralAiChatOptions.builder()
157+
.stop(List.of("single-stop"))
158+
.toolContext(Map.of("single-key", "single-value"))
159+
.build();
160+
161+
assertThat(options.getStop()).hasSize(1).containsExactly("single-stop");
162+
assertThat(options.getToolContext()).hasSize(1).containsEntry("single-key", "single-value");
163+
}
164+
165+
@Test
166+
void testCopyWithEmptyOptions() {
167+
MistralAiChatOptions emptyOptions = new MistralAiChatOptions();
168+
MistralAiChatOptions copiedOptions = emptyOptions.copy();
169+
170+
assertThat(copiedOptions).isNotSameAs(emptyOptions).isEqualTo(emptyOptions);
171+
assertThat(copiedOptions.getModel()).isNull();
172+
assertThat(copiedOptions.getTemperature()).isNull();
173+
}
174+
175+
@Test
176+
void testCopyMutationDoesNotAffectOriginal() {
177+
MistralAiChatOptions original = MistralAiChatOptions.builder()
178+
.model("original-model")
179+
.temperature(0.5)
180+
.stop(List.of("original-stop"))
181+
.toolContext(Map.of("original", "value"))
182+
.build();
183+
184+
MistralAiChatOptions copy = original.copy();
185+
copy.setModel("modified-model");
186+
copy.setTemperature(0.8);
187+
188+
// Original should remain unchanged
189+
assertThat(original.getModel()).isEqualTo("original-model");
190+
assertThat(original.getTemperature()).isEqualTo(0.5);
191+
192+
// Copy should have new values
193+
assertThat(copy.getModel()).isEqualTo("modified-model");
194+
assertThat(copy.getTemperature()).isEqualTo(0.8);
195+
}
196+
197+
@Test
198+
void testEqualsAndHashCode() {
199+
MistralAiChatOptions options1 = MistralAiChatOptions.builder().model("test-model").temperature(0.7).build();
200+
201+
MistralAiChatOptions options2 = MistralAiChatOptions.builder().model("test-model").temperature(0.7).build();
202+
203+
MistralAiChatOptions options3 = MistralAiChatOptions.builder()
204+
.model("different-model")
205+
.temperature(0.7)
206+
.build();
207+
208+
assertThat(options1).isEqualTo(options2);
209+
assertThat(options1.hashCode()).isEqualTo(options2.hashCode());
210+
211+
assertThat(options1).isNotEqualTo(options3);
212+
assertThat(options1.hashCode()).isNotEqualTo(options3.hashCode());
213+
}
214+
215+
@Test
216+
void testAllToolChoiceEnumValues() {
217+
for (MistralAiApi.ChatCompletionRequest.ToolChoice toolChoice : MistralAiApi.ChatCompletionRequest.ToolChoice
218+
.values()) {
219+
220+
MistralAiChatOptions options = MistralAiChatOptions.builder().toolChoice(toolChoice).build();
221+
222+
assertThat(options.getToolChoice()).isEqualTo(toolChoice);
223+
}
224+
}
225+
226+
@Test
227+
void testResponseFormatTypes() {
228+
ResponseFormat jsonFormat = new ResponseFormat("json_object");
229+
ResponseFormat textFormat = new ResponseFormat("text");
230+
231+
MistralAiChatOptions jsonOptions = MistralAiChatOptions.builder().responseFormat(jsonFormat).build();
232+
233+
MistralAiChatOptions textOptions = MistralAiChatOptions.builder().responseFormat(textFormat).build();
234+
235+
assertThat(jsonOptions.getResponseFormat()).isEqualTo(jsonFormat);
236+
assertThat(textOptions.getResponseFormat()).isEqualTo(textFormat);
237+
assertThat(jsonOptions.getResponseFormat()).isNotEqualTo(textOptions.getResponseFormat());
238+
}
239+
240+
@Test
241+
void testChainedBuilderMethods() {
242+
MistralAiChatOptions options = MistralAiChatOptions.builder()
243+
.model("test-model")
244+
.temperature(0.7)
245+
.topP(0.9)
246+
.maxTokens(100)
247+
.safePrompt(true)
248+
.randomSeed(123)
249+
.internalToolExecutionEnabled(false)
250+
.build();
251+
252+
// Verify all chained methods worked
253+
assertThat(options.getModel()).isEqualTo("test-model");
254+
assertThat(options.getTemperature()).isEqualTo(0.7);
255+
assertThat(options.getTopP()).isEqualTo(0.9);
256+
assertThat(options.getMaxTokens()).isEqualTo(100);
257+
assertThat(options.getSafePrompt()).isTrue();
258+
assertThat(options.getRandomSeed()).isEqualTo(123);
259+
assertThat(options.getInternalToolExecutionEnabled()).isFalse();
260+
}
261+
262+
@Test
263+
void testBuilderAndSetterConsistency() {
264+
// Build an object using builder
265+
MistralAiChatOptions builderOptions = MistralAiChatOptions.builder()
266+
.model("test-model")
267+
.temperature(0.7)
268+
.topP(0.9)
269+
.maxTokens(100)
270+
.build();
271+
272+
// Create equivalent object using setters
273+
MistralAiChatOptions setterOptions = new MistralAiChatOptions();
274+
setterOptions.setModel("test-model");
275+
setterOptions.setTemperature(0.7);
276+
setterOptions.setTopP(0.9);
277+
setterOptions.setMaxTokens(100);
278+
279+
assertThat(builderOptions).isEqualTo(setterOptions);
280+
}
281+
127282
}

0 commit comments

Comments
 (0)