1616
1717package org .springframework .ai .azure .openai .aot ;
1818
19+ import java .util .HashSet ;
1920import java .util .Set ;
2021
2122import com .azure .ai .openai .OpenAIAsyncClient ;
2223import com .azure .ai .openai .OpenAIClient ;
2324import com .azure .ai .openai .models .ChatChoice ;
2425import org .junit .jupiter .api .Test ;
26+ import org .junit .jupiter .api .BeforeEach ;
2527
2628import org .springframework .ai .aot .AiRuntimeHints ;
2729import org .springframework .aot .hint .RuntimeHints ;
2830import org .springframework .aot .hint .TypeReference ;
31+ import org .springframework .aot .hint .MemberCategory ;
2932
3033import static org .assertj .core .api .AssertionsForClassTypes .assertThat ;
3134import static org .springframework .aot .hint .predicate .RuntimeHintsPredicates .reflection ;
3235import static org .springframework .aot .hint .predicate .RuntimeHintsPredicates .resource ;
3336
3437class AzureOpenAiRuntimeHintsTests {
3538
39+ private RuntimeHints runtimeHints ;
40+
41+ private AzureOpenAiRuntimeHints azureOpenAiRuntimeHints ;
42+
43+ @ BeforeEach
44+ void setUp () {
45+ runtimeHints = new RuntimeHints ();
46+ azureOpenAiRuntimeHints = new AzureOpenAiRuntimeHints ();
47+ }
48+
3649 @ Test
3750 void registerHints () {
38- RuntimeHints runtimeHints = new RuntimeHints ();
39- AzureOpenAiRuntimeHints openAiRuntimeHints = new AzureOpenAiRuntimeHints ();
40- openAiRuntimeHints .registerHints (runtimeHints , null );
51+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
4152
4253 Set <TypeReference > azureModelTypes = AiRuntimeHints .findClassesInPackage (ChatChoice .class .getPackageName (),
4354 (metadataReader , metadataReaderFactory ) -> true );
@@ -50,4 +61,112 @@ void registerHints() {
5061 assertThat (runtimeHints ).matches (resource ().forResource ("/azure-ai-openai.properties" ));
5162 }
5263
64+ @ Test
65+ void registerHintsWithNullClassLoader () {
66+ // Test that registering hints with null ClassLoader works correctly
67+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
68+
69+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIClient .class ));
70+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIAsyncClient .class ));
71+ assertThat (runtimeHints ).matches (resource ().forResource ("/azure-ai-openai.properties" ));
72+ }
73+
74+ @ Test
75+ void registerHintsWithCustomClassLoader () {
76+ // Test that registering hints with a custom ClassLoader works correctly
77+ ClassLoader customClassLoader = Thread .currentThread ().getContextClassLoader ();
78+ azureOpenAiRuntimeHints .registerHints (runtimeHints , customClassLoader );
79+
80+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIClient .class ));
81+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIAsyncClient .class ));
82+ assertThat (runtimeHints ).matches (resource ().forResource ("/azure-ai-openai.properties" ));
83+ }
84+
85+ @ Test
86+ void allMemberCategoriesAreRegisteredForAzureTypes () {
87+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
88+
89+ Set <TypeReference > azureModelTypes = AiRuntimeHints .findClassesInPackage (ChatChoice .class .getPackageName (),
90+ (metadataReader , metadataReaderFactory ) -> true );
91+
92+ // Verify that all MemberCategory values are registered for Azure model types
93+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> {
94+ if (azureModelTypes .contains (typeHint .getType ())) {
95+ Set <MemberCategory > expectedCategories = Set .of (MemberCategory .values ());
96+ Set <MemberCategory > actualCategories = typeHint .getMemberCategories ();
97+ assertThat (actualCategories .containsAll (expectedCategories )).isTrue ();
98+ }
99+ });
100+ }
101+
102+ @ Test
103+ void verifySpecificAzureOpenAiClasses () {
104+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
105+
106+ // Verify specific Azure OpenAI classes are registered
107+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIClient .class ));
108+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIAsyncClient .class ));
109+ assertThat (runtimeHints ).matches (reflection ().onType (ChatChoice .class ));
110+ }
111+
112+ @ Test
113+ void emptyRuntimeHintsInitiallyContainsNoTypes () {
114+ // Verify that fresh RuntimeHints instance contains no reflection hints
115+ RuntimeHints emptyHints = new RuntimeHints ();
116+ Set <TypeReference > emptyRegisteredTypes = new HashSet <>();
117+ emptyHints .reflection ().typeHints ().forEach (typeHint -> emptyRegisteredTypes .add (typeHint .getType ()));
118+
119+ assertThat (emptyRegisteredTypes .size ()).isEqualTo (0 );
120+ }
121+
122+ @ Test
123+ void multipleRegistrationCallsAreIdempotent () {
124+ // Register hints multiple times and verify no duplicates
125+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
126+ int firstRegistrationCount = (int ) runtimeHints .reflection ().typeHints ().count ();
127+
128+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
129+ int secondRegistrationCount = (int ) runtimeHints .reflection ().typeHints ().count ();
130+
131+ assertThat (firstRegistrationCount ).isEqualTo (secondRegistrationCount );
132+
133+ // Verify resource hint registration is also idempotent
134+ assertThat (runtimeHints ).matches (resource ().forResource ("/azure-ai-openai.properties" ));
135+ }
136+
137+ @ Test
138+ void verifyAzureModelTypesInPackageIsNotEmpty () {
139+ Set <TypeReference > azureModelTypes = AiRuntimeHints .findClassesInPackage (ChatChoice .class .getPackageName (),
140+ (metadataReader , metadataReaderFactory ) -> true );
141+ assertThat (azureModelTypes .size ()).isGreaterThan (0 );
142+ }
143+
144+ @ Test
145+ void verifyResourceHintIsRegistered () {
146+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
147+
148+ // Verify the specific resource hint is registered
149+ assertThat (runtimeHints ).matches (resource ().forResource ("/azure-ai-openai.properties" ));
150+ }
151+
152+ @ Test
153+ void verifyAllRegisteredTypesHaveReflectionHints () {
154+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
155+
156+ // Ensure every registered type has proper reflection hints
157+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> {
158+ assertThat (typeHint .getType ()).isNotNull ();
159+ assertThat (typeHint .getMemberCategories ().size ()).isGreaterThan (0 );
160+ });
161+ }
162+
163+ @ Test
164+ void verifyClientTypesAreRegistered () {
165+ azureOpenAiRuntimeHints .registerHints (runtimeHints , null );
166+
167+ // Verify both sync and async client types are properly registered
168+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIClient .class ));
169+ assertThat (runtimeHints ).matches (reflection ().onType (OpenAIAsyncClient .class ));
170+ }
171+
53172}
0 commit comments