Skip to content

Commit 0b6ab65

Browse files
alxkmWillam2004
authored andcommitted
test: Add comprehensive test coverage for runtime hints and bindings processors (spring-projects#4402)
* test: Add comprehensive test coverage for runtime hints and bindings processors Co-authored-by: Oleksandr Klymenko <[email protected]> Signed-off-by: Oleksandr Klymenko <[email protected]> Signed-off-by: 家娃 <[email protected]>
1 parent 06dbfd2 commit 0b6ab65

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/aot/AzureOpenAiRuntimeHintsTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,44 @@ void verifyClientTypesAreRegistered() {
169169
assertThat(this.runtimeHints).matches(reflection().onType(OpenAIAsyncClient.class));
170170
}
171171

172+
@Test
173+
void verifyNoSerializationHintsAreRegistered() {
174+
this.azureOpenAiRuntimeHints.registerHints(this.runtimeHints, null);
175+
176+
// Azure OpenAI should only register reflection and resource hints, not
177+
// serialization hints
178+
assertThat(this.runtimeHints.serialization().javaSerializationHints().count()).isEqualTo(0);
179+
}
180+
181+
@Test
182+
void verifyRegistrationWithDifferentRuntimeHintsInstances() {
183+
RuntimeHints hints1 = new RuntimeHints();
184+
RuntimeHints hints2 = new RuntimeHints();
185+
186+
this.azureOpenAiRuntimeHints.registerHints(hints1, null);
187+
this.azureOpenAiRuntimeHints.registerHints(hints2, null);
188+
189+
// Both instances should have same number of reflection hints
190+
long count1 = hints1.reflection().typeHints().count();
191+
long count2 = hints2.reflection().typeHints().count();
192+
193+
assertThat(count1).isEqualTo(count2);
194+
assertThat(count1).isGreaterThan(0);
195+
}
196+
197+
@Test
198+
void verifyEnumTypesInAzurePackageAreRegistered() {
199+
this.azureOpenAiRuntimeHints.registerHints(this.runtimeHints, null);
200+
201+
Set<TypeReference> registeredTypes = new HashSet<>();
202+
this.runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType()));
203+
204+
// Verify that enum types from Azure OpenAI package are registered
205+
boolean hasEnumTypes = registeredTypes.stream()
206+
.anyMatch(tr -> tr.getName().contains("com.azure.ai.openai.models")
207+
&& tr.getName().toLowerCase().contains("choice"));
208+
209+
assertThat(hasEnumTypes).as("Azure OpenAI enum types should be registered").isTrue();
210+
}
211+
172212
}

models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/aot/MistralAiRuntimeHintsTests.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.aot.hint.TypeReference;
2929

3030
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
31+
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
3132
import static org.springframework.ai.aot.AiRuntimeHints.findJsonAnnotatedClassesInPackage;
3233

3334
class MistralAiRuntimeHintsTests {
@@ -174,4 +175,71 @@ void verifyReflectionHintsIncludeConstructors() {
174175
assertThat(hasConstructorHints).as("Should register constructor hints for JSON deserialization").isTrue();
175176
}
176177

178+
@Test
179+
void verifyNoExceptionThrownWithEmptyRuntimeHints() {
180+
RuntimeHints emptyRuntimeHints = new RuntimeHints();
181+
MistralAiRuntimeHints mistralAiRuntimeHints = new MistralAiRuntimeHints();
182+
183+
// Should not throw any exception even with empty runtime hints
184+
assertThatCode(() -> mistralAiRuntimeHints.registerHints(emptyRuntimeHints, null)).doesNotThrowAnyException();
185+
186+
assertThat(emptyRuntimeHints.reflection().typeHints().count()).isGreaterThan(0);
187+
}
188+
189+
@Test
190+
void verifyProxyHintsAreNotRegistered() {
191+
RuntimeHints runtimeHints = new RuntimeHints();
192+
MistralAiRuntimeHints mistralAiRuntimeHints = new MistralAiRuntimeHints();
193+
mistralAiRuntimeHints.registerHints(runtimeHints, null);
194+
195+
// MistralAi should only register reflection hints, not proxy hints
196+
assertThat(runtimeHints.proxies().jdkProxyHints().count()).isEqualTo(0);
197+
}
198+
199+
@Test
200+
void verifySerializationHintsAreNotRegistered() {
201+
RuntimeHints runtimeHints = new RuntimeHints();
202+
MistralAiRuntimeHints mistralAiRuntimeHints = new MistralAiRuntimeHints();
203+
mistralAiRuntimeHints.registerHints(runtimeHints, null);
204+
205+
// MistralAi should only register reflection hints, not serialization hints
206+
assertThat(runtimeHints.serialization().javaSerializationHints().count()).isEqualTo(0);
207+
}
208+
209+
@Test
210+
void verifyResponseTypesAreRegistered() {
211+
RuntimeHints runtimeHints = new RuntimeHints();
212+
MistralAiRuntimeHints mistralAiRuntimeHints = new MistralAiRuntimeHints();
213+
mistralAiRuntimeHints.registerHints(runtimeHints, null);
214+
215+
Set<TypeReference> registeredTypes = new HashSet<>();
216+
runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType()));
217+
218+
// Verify response wrapper types are registered
219+
assertThat(registeredTypes.stream().anyMatch(tr -> tr.getName().contains("EmbeddingList")))
220+
.as("EmbeddingList response type should be registered")
221+
.isTrue();
222+
223+
assertThat(registeredTypes.stream().anyMatch(tr -> tr.getName().contains("ChatCompletion")))
224+
.as("ChatCompletion response type should be registered")
225+
.isTrue();
226+
}
227+
228+
@Test
229+
void verifyMultipleInstancesRegisterSameHints() {
230+
RuntimeHints runtimeHints1 = new RuntimeHints();
231+
RuntimeHints runtimeHints2 = new RuntimeHints();
232+
233+
MistralAiRuntimeHints hints1 = new MistralAiRuntimeHints();
234+
MistralAiRuntimeHints hints2 = new MistralAiRuntimeHints();
235+
236+
hints1.registerHints(runtimeHints1, null);
237+
hints2.registerHints(runtimeHints2, null);
238+
239+
long count1 = runtimeHints1.reflection().typeHints().count();
240+
long count2 = runtimeHints2.reflection().typeHints().count();
241+
242+
assertThat(count1).isEqualTo(count2);
243+
}
244+
177245
}

spring-ai-spring-cloud-bindings/src/test/java/org/springframework/ai/bindings/MistralAiBindingsPropertiesProcessorTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,35 @@ void wrongBindingTypeShouldBeIgnored() {
120120
assertThat(this.properties).isEmpty();
121121
}
122122

123+
@Test
124+
void emptyBindingsShouldNotThrowException() {
125+
Bindings emptyBindings = new Bindings();
126+
127+
new MistralAiBindingsPropertiesProcessor().process(this.environment, emptyBindings, this.properties);
128+
129+
assertThat(this.properties).isEmpty();
130+
}
131+
132+
@Test
133+
void onlyUriWithoutApiKeyShouldSetBothProperties() {
134+
Bindings bindingsWithOnlyUri = new Bindings(new Binding("test-name", Paths.get("test-path"), Map
135+
.of(Binding.TYPE, MistralAiBindingsPropertiesProcessor.TYPE, "uri", "https://custom.mistralai.com")));
136+
137+
new MistralAiBindingsPropertiesProcessor().process(this.environment, bindingsWithOnlyUri, this.properties);
138+
139+
assertThat(this.properties).containsEntry("spring.ai.mistralai.base-url", "https://custom.mistralai.com");
140+
assertThat(this.properties).containsEntry("spring.ai.mistralai.api-key", null);
141+
}
142+
143+
@Test
144+
void onlyApiKeyWithoutUriShouldSetBothProperties() {
145+
Bindings bindingsWithOnlyApiKey = new Bindings(new Binding("test-name", Paths.get("test-path"),
146+
Map.of(Binding.TYPE, MistralAiBindingsPropertiesProcessor.TYPE, "api-key", "secret-key")));
147+
148+
new MistralAiBindingsPropertiesProcessor().process(this.environment, bindingsWithOnlyApiKey, this.properties);
149+
150+
assertThat(this.properties).containsEntry("spring.ai.mistralai.api-key", "secret-key");
151+
assertThat(this.properties).containsEntry("spring.ai.mistralai.base-url", null);
152+
}
153+
123154
}

0 commit comments

Comments
 (0)