Skip to content

Commit c37ed3c

Browse files
authored
Enhance OpenAiChatOptions test coverage (#3959)
Tests Added - testEqualsAndHashCode() - equals/hashCode contract validation - testBuilderWithNullValues() - builder with null inputs - testBuilderChaining() - builder method chaining - testNullAndEmptyCollections() - null/empty collection handling - testStreamUsageStreamOptionsInteraction() - stream options state sync - testStopSequencesAlias() - stop/stopSequences field sync - testFromOptionsWithWebSearchOptionsNull() - fromOptions with null webSearch - testCopyChangeIndependence() - copy mutation independence validation Signed-off-by: Alex Klimenko <[email protected]>
1 parent efd0c47 commit c37ed3c

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiChatOptionsTests.java

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,173 @@ void testFromOptions_webSearchOptions() {
280280
assertThat(target.getWebSearchOptions().userLocation().approximate().timezone()).isEqualTo("UTC+8");
281281
}
282282

283+
@Test
284+
void testEqualsAndHashCode() {
285+
OpenAiChatOptions options1 = OpenAiChatOptions.builder()
286+
.model("test-model")
287+
.temperature(0.7)
288+
.maxTokens(100)
289+
.build();
290+
291+
OpenAiChatOptions options2 = OpenAiChatOptions.builder()
292+
.model("test-model")
293+
.temperature(0.7)
294+
.maxTokens(100)
295+
.build();
296+
297+
OpenAiChatOptions options3 = OpenAiChatOptions.builder()
298+
.model("different-model")
299+
.temperature(0.7)
300+
.maxTokens(100)
301+
.build();
302+
303+
// Test equals
304+
assertThat(options1).isEqualTo(options2);
305+
assertThat(options1).isNotEqualTo(options3);
306+
assertThat(options1).isNotEqualTo(null);
307+
assertThat(options1).isEqualTo(options1);
308+
309+
// Test hashCode
310+
assertThat(options1.hashCode()).isEqualTo(options2.hashCode());
311+
assertThat(options1.hashCode()).isNotEqualTo(options3.hashCode());
312+
}
313+
314+
@Test
315+
void testBuilderWithNullValues() {
316+
OpenAiChatOptions options = OpenAiChatOptions.builder()
317+
.temperature(null)
318+
.logitBias(null)
319+
.stop(null)
320+
.tools(null)
321+
.metadata(null)
322+
.build();
323+
324+
assertThat(options.getModel()).isNull();
325+
assertThat(options.getTemperature()).isNull();
326+
assertThat(options.getLogitBias()).isNull();
327+
assertThat(options.getStop()).isNull();
328+
assertThat(options.getTools()).isNull();
329+
assertThat(options.getMetadata()).isNull();
330+
}
331+
332+
@Test
333+
void testBuilderChaining() {
334+
OpenAiChatOptions.Builder builder = OpenAiChatOptions.builder();
335+
336+
OpenAiChatOptions.Builder result = builder.model("test-model").temperature(0.7).maxTokens(100);
337+
338+
assertThat(result).isSameAs(builder);
339+
340+
OpenAiChatOptions options = result.build();
341+
assertThat(options.getModel()).isEqualTo("test-model");
342+
assertThat(options.getTemperature()).isEqualTo(0.7);
343+
assertThat(options.getMaxTokens()).isEqualTo(100);
344+
}
345+
346+
@Test
347+
void testNullAndEmptyCollections() {
348+
OpenAiChatOptions options = new OpenAiChatOptions();
349+
350+
// Test setting null collections
351+
options.setLogitBias(null);
352+
options.setStop(null);
353+
options.setTools(null);
354+
options.setMetadata(null);
355+
options.setOutputModalities(null);
356+
357+
assertThat(options.getLogitBias()).isNull();
358+
assertThat(options.getStop()).isNull();
359+
assertThat(options.getTools()).isNull();
360+
assertThat(options.getMetadata()).isNull();
361+
assertThat(options.getOutputModalities()).isNull();
362+
363+
// Test setting empty collections
364+
options.setLogitBias(new HashMap<>());
365+
options.setStop(new ArrayList<>());
366+
options.setTools(new ArrayList<>());
367+
options.setMetadata(new HashMap<>());
368+
options.setOutputModalities(new ArrayList<>());
369+
370+
assertThat(options.getLogitBias()).isEmpty();
371+
assertThat(options.getStop()).isEmpty();
372+
assertThat(options.getTools()).isEmpty();
373+
assertThat(options.getMetadata()).isEmpty();
374+
assertThat(options.getOutputModalities()).isEmpty();
375+
}
376+
377+
@Test
378+
void testStreamUsageStreamOptionsInteraction() {
379+
OpenAiChatOptions options = new OpenAiChatOptions();
380+
381+
// Initially false
382+
assertThat(options.getStreamUsage()).isFalse();
383+
assertThat(options.getStreamOptions()).isNull();
384+
385+
// Setting streamUsage to true should set streamOptions
386+
options.setStreamUsage(true);
387+
assertThat(options.getStreamUsage()).isTrue();
388+
assertThat(options.getStreamOptions()).isEqualTo(StreamOptions.INCLUDE_USAGE);
389+
390+
// Setting streamUsage to false should clear streamOptions
391+
options.setStreamUsage(false);
392+
assertThat(options.getStreamUsage()).isFalse();
393+
assertThat(options.getStreamOptions()).isNull();
394+
395+
// Setting streamOptions directly should update streamUsage
396+
options.setStreamOptions(StreamOptions.INCLUDE_USAGE);
397+
assertThat(options.getStreamUsage()).isTrue();
398+
assertThat(options.getStreamOptions()).isEqualTo(StreamOptions.INCLUDE_USAGE);
399+
400+
// Setting streamOptions to null should set streamUsage to false
401+
options.setStreamOptions(null);
402+
assertThat(options.getStreamUsage()).isFalse();
403+
assertThat(options.getStreamOptions()).isNull();
404+
}
405+
406+
@Test
407+
void testStopSequencesAlias() {
408+
OpenAiChatOptions options = new OpenAiChatOptions();
409+
List<String> stopSequences = List.of("stop1", "stop2");
410+
411+
// Setting stopSequences should also set stop
412+
options.setStopSequences(stopSequences);
413+
assertThat(options.getStopSequences()).isEqualTo(stopSequences);
414+
assertThat(options.getStop()).isEqualTo(stopSequences);
415+
416+
// Setting stop should also update stopSequences
417+
List<String> newStop = List.of("stop3", "stop4");
418+
options.setStop(newStop);
419+
assertThat(options.getStop()).isEqualTo(newStop);
420+
assertThat(options.getStopSequences()).isEqualTo(newStop);
421+
}
422+
423+
@Test
424+
void testFromOptionsWithWebSearchOptionsNull() {
425+
OpenAiChatOptions source = OpenAiChatOptions.builder()
426+
.model("test-model")
427+
.temperature(0.7)
428+
.webSearchOptions(null)
429+
.build();
430+
431+
OpenAiChatOptions result = OpenAiChatOptions.fromOptions(source);
432+
assertThat(result.getModel()).isEqualTo("test-model");
433+
assertThat(result.getTemperature()).isEqualTo(0.7);
434+
assertThat(result.getWebSearchOptions()).isNull();
435+
}
436+
437+
@Test
438+
void testCopyChangeIndependence() {
439+
OpenAiChatOptions original = OpenAiChatOptions.builder().model("original-model").temperature(0.5).build();
440+
441+
OpenAiChatOptions copied = original.copy();
442+
443+
// Modify original
444+
original.setModel("modified-model");
445+
original.setTemperature(0.9);
446+
447+
// Verify copy is unchanged
448+
assertThat(copied.getModel()).isEqualTo("original-model");
449+
assertThat(copied.getTemperature()).isEqualTo(0.5);
450+
}
451+
283452
}

0 commit comments

Comments
 (0)