From 07840ac6adaaee8c55ce2fe4cc19ec926980155b Mon Sep 17 00:00:00 2001 From: Alex Klimenko Date: Wed, 6 Aug 2025 16:01:27 +0200 Subject: [PATCH] test: Add comprehensive test coverage for SpringAI core runtime hints registration Signed-off-by: Alex Klimenko --- .../ai/aot/SpringAiCoreRuntimeHintsTest.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java b/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java index ea7badcc0af..2beee955d96 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java @@ -21,8 +21,13 @@ import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.TypeReference; + +import java.util.HashSet; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.resource; @@ -42,4 +47,97 @@ void core() { assertThat(runtimeHints).matches(reflection().onType(ToolDefinition.class)); } + @Test + void registerHintsWithNullClassLoader() { + var runtimeHints = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + + // Should not throw exception with null ClassLoader + assertThatCode(() -> springAiCore.registerHints(runtimeHints, null)).doesNotThrowAnyException(); + } + + @Test + void verifyEmbeddingResourceIsRegistered() { + var runtimeHints = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + springAiCore.registerHints(runtimeHints, null); + + // Verify the specific embedding properties file is registered + assertThat(runtimeHints).matches(resource().forResource("embedding/embedding-model-dimensions.properties")); + } + + @Test + void verifyToolReflectionHintsAreRegistered() { + var runtimeHints = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + springAiCore.registerHints(runtimeHints, null); + + Set registeredTypes = new HashSet<>(); + runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType())); + + // Verify core tool classes are registered + assertThat(registeredTypes.contains(TypeReference.of(ToolCallback.class))).isTrue(); + assertThat(registeredTypes.contains(TypeReference.of(ToolDefinition.class))).isTrue(); + } + + @Test + void verifyResourceAndReflectionHintsSeparately() { + var runtimeHints = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + springAiCore.registerHints(runtimeHints, null); + + // Test resource hints + assertThat(runtimeHints).matches(resource().forResource("embedding/embedding-model-dimensions.properties")); + + // Test reflection hints + assertThat(runtimeHints).matches(reflection().onType(ToolCallback.class)); + assertThat(runtimeHints).matches(reflection().onType(ToolDefinition.class)); + } + + @Test + void verifyMultipleRegistrationCallsAreIdempotent() { + var runtimeHints1 = new RuntimeHints(); + var runtimeHints2 = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + + // Register hints on two separate RuntimeHints instances + springAiCore.registerHints(runtimeHints1, null); + springAiCore.registerHints(runtimeHints2, null); + + // Both should have the same hints registered + assertThat(runtimeHints1).matches(resource().forResource("embedding/embedding-model-dimensions.properties")); + assertThat(runtimeHints2).matches(resource().forResource("embedding/embedding-model-dimensions.properties")); + + assertThat(runtimeHints1).matches(reflection().onType(ToolCallback.class)); + assertThat(runtimeHints2).matches(reflection().onType(ToolCallback.class)); + } + + @Test + void verifyResourceHintsForIncorrectPaths() { + var runtimeHints = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + springAiCore.registerHints(runtimeHints, null); + + // Verify the exact resource path is registered + assertThat(runtimeHints).matches(resource().forResource("embedding/embedding-model-dimensions.properties")); + + // Verify that similar but incorrect paths are not matched + assertThat(runtimeHints).doesNotMatch(resource().forResource("embedding-model-dimensions.properties")); + assertThat(runtimeHints).doesNotMatch(resource().forResource("embedding/model-dimensions.properties")); + } + + @Test + void ensureBothResourceAndReflectionHintsArePresent() { + var runtimeHints = new RuntimeHints(); + var springAiCore = new SpringAiCoreRuntimeHints(); + springAiCore.registerHints(runtimeHints, null); + + // Ensure both resource and reflection hints are registered + boolean hasResourceHints = runtimeHints.resources() != null; + boolean hasReflectionHints = runtimeHints.reflection().typeHints().spliterator().estimateSize() > 0; + + assertThat(hasResourceHints).isTrue(); + assertThat(hasReflectionHints).isTrue(); + } + }