@@ -189,4 +189,127 @@ void verifyNestedClassesAreRegistered() {
189189		assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .FunctionTool .Function .class ))).isTrue ();
190190	}
191191
192+ 	@ Test 
193+ 	void  verifyPackageSpecificity () {
194+ 		Set <TypeReference > jsonAnnotatedClasses  = findJsonAnnotatedClassesInPackage ("org.springframework.ai.openai" );
195+ 
196+ 		// All found classes should be from the openai package specifically 
197+ 		for  (TypeReference  classRef  : jsonAnnotatedClasses ) {
198+ 			assertThat (classRef .getName ()).startsWith ("org.springframework.ai.openai" );
199+ 		}
200+ 
201+ 		// Should not include classes from other AI packages 
202+ 		for  (TypeReference  classRef  : jsonAnnotatedClasses ) {
203+ 			assertThat (classRef .getName ()).doesNotContain ("anthropic" );
204+ 			assertThat (classRef .getName ()).doesNotContain ("vertexai" );
205+ 			assertThat (classRef .getName ()).doesNotContain ("ollama" );
206+ 		}
207+ 	}
208+ 
209+ 	@ Test 
210+ 	void  verifyConsistencyAcrossInstances () {
211+ 		RuntimeHints  hints1  = new  RuntimeHints ();
212+ 		RuntimeHints  hints2  = new  RuntimeHints ();
213+ 
214+ 		OpenAiRuntimeHints  openaiHints1  = new  OpenAiRuntimeHints ();
215+ 		OpenAiRuntimeHints  openaiHints2  = new  OpenAiRuntimeHints ();
216+ 
217+ 		openaiHints1 .registerHints (hints1 , null );
218+ 		openaiHints2 .registerHints (hints2 , null );
219+ 
220+ 		// Different instances should register the same hints 
221+ 		Set <TypeReference > types1  = new  HashSet <>();
222+ 		Set <TypeReference > types2  = new  HashSet <>();
223+ 
224+ 		hints1 .reflection ().typeHints ().forEach (hint  -> types1 .add (hint .getType ()));
225+ 		hints2 .reflection ().typeHints ().forEach (hint  -> types2 .add (hint .getType ()));
226+ 
227+ 		assertThat (types1 ).isEqualTo (types2 );
228+ 	}
229+ 
230+ 	@ Test 
231+ 	void  verifySpecificApiClassDetails () {
232+ 		openAiRuntimeHints .registerHints (runtimeHints , null );
233+ 
234+ 		Set <TypeReference > registeredTypes  = new  HashSet <>();
235+ 		runtimeHints .reflection ().typeHints ().forEach (typeHint  -> registeredTypes .add (typeHint .getType ()));
236+ 
237+ 		// Verify critical OpenAI API classes are registered 
238+ 		assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .class ))).isTrue ();
239+ 		assertThat (registeredTypes .contains (TypeReference .of (OpenAiAudioApi .class ))).isTrue ();
240+ 		assertThat (registeredTypes .contains (TypeReference .of (OpenAiImageApi .class ))).isTrue ();
241+ 
242+ 		// Verify important nested/inner classes 
243+ 		boolean  containsChatCompletion  = registeredTypes .stream ()
244+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("ChatCompletion" ));
245+ 		assertThat (containsChatCompletion ).isTrue ();
246+ 
247+ 		boolean  containsFunctionTool  = registeredTypes .stream ()
248+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("FunctionTool" ));
249+ 		assertThat (containsFunctionTool ).isTrue ();
250+ 	}
251+ 
252+ 	@ Test 
253+ 	void  verifyClassLoaderIndependence () {
254+ 		RuntimeHints  hintsWithNull  = new  RuntimeHints ();
255+ 		RuntimeHints  hintsWithClassLoader  = new  RuntimeHints ();
256+ 
257+ 		ClassLoader  customClassLoader  = Thread .currentThread ().getContextClassLoader ();
258+ 
259+ 		openAiRuntimeHints .registerHints (hintsWithNull , null );
260+ 		openAiRuntimeHints .registerHints (hintsWithClassLoader , customClassLoader );
261+ 
262+ 		// Both should register the same types regardless of ClassLoader 
263+ 		Set <TypeReference > typesWithNull  = new  HashSet <>();
264+ 		Set <TypeReference > typesWithClassLoader  = new  HashSet <>();
265+ 
266+ 		hintsWithNull .reflection ().typeHints ().forEach (hint  -> typesWithNull .add (hint .getType ()));
267+ 		hintsWithClassLoader .reflection ().typeHints ().forEach (hint  -> typesWithClassLoader .add (hint .getType ()));
268+ 
269+ 		assertThat (typesWithNull ).isEqualTo (typesWithClassLoader );
270+ 	}
271+ 
272+ 	@ Test 
273+ 	void  verifyAllApiModulesAreIncluded () {
274+ 		openAiRuntimeHints .registerHints (runtimeHints , null );
275+ 
276+ 		Set <TypeReference > registeredTypes  = new  HashSet <>();
277+ 		runtimeHints .reflection ().typeHints ().forEach (typeHint  -> registeredTypes .add (typeHint .getType ()));
278+ 
279+ 		// Verify all main OpenAI API modules are represented 
280+ 		boolean  hasMainApi  = registeredTypes .stream ().anyMatch (typeRef  -> typeRef .getName ().contains ("OpenAiApi" ));
281+ 		boolean  hasAudioApi  = registeredTypes .stream ()
282+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("OpenAiAudioApi" ));
283+ 		boolean  hasImageApi  = registeredTypes .stream ()
284+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("OpenAiImageApi" ));
285+ 		boolean  hasChatOptions  = registeredTypes .stream ()
286+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("OpenAiChatOptions" ));
287+ 
288+ 		assertThat (hasMainApi ).isTrue ();
289+ 		assertThat (hasAudioApi ).isTrue ();
290+ 		assertThat (hasImageApi ).isTrue ();
291+ 		assertThat (hasChatOptions ).isTrue ();
292+ 	}
293+ 
294+ 	@ Test 
295+ 	void  verifyJsonAnnotatedClassesContainCriticalTypes () {
296+ 		Set <TypeReference > jsonAnnotatedClasses  = findJsonAnnotatedClassesInPackage ("org.springframework.ai.openai" );
297+ 
298+ 		// Verify that critical OpenAI types are found 
299+ 		boolean  containsApiClass  = jsonAnnotatedClasses .stream ()
300+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("OpenAiApi" ) || typeRef .getName ().contains ("ChatCompletion" )
301+ 					|| typeRef .getName ().contains ("OpenAiChatOptions" ));
302+ 
303+ 		assertThat (containsApiClass ).isTrue ();
304+ 
305+ 		// Verify audio and image API classes are found 
306+ 		boolean  containsAudioApi  = jsonAnnotatedClasses .stream ()
307+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("AudioApi" ));
308+ 		boolean  containsImageApi  = jsonAnnotatedClasses .stream ()
309+ 			.anyMatch (typeRef  -> typeRef .getName ().contains ("ImageApi" ));
310+ 
311+ 		assertThat (containsAudioApi ).isTrue ();
312+ 		assertThat (containsImageApi ).isTrue ();
313+ 	}
314+ 
192315}
0 commit comments