@@ -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