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