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