diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/aot/OllamaRuntimeHintsTests.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/aot/OllamaRuntimeHintsTests.java index ae56cd033d3..26c5833c529 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/aot/OllamaRuntimeHintsTests.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/aot/OllamaRuntimeHintsTests.java @@ -53,4 +53,101 @@ void registerHints() { assertThat(registeredTypes.contains(TypeReference.of(OllamaOptions.class))).isTrue(); } + @Test + void registerHintsWithNullClassLoader() { + RuntimeHints runtimeHints = new RuntimeHints(); + OllamaRuntimeHints ollamaRuntimeHints = new OllamaRuntimeHints(); + + // Should not throw exception with null ClassLoader + org.assertj.core.api.Assertions.assertThatCode(() -> ollamaRuntimeHints.registerHints(runtimeHints, null)) + .doesNotThrowAnyException(); + } + + @Test + void ensureReflectionHintsAreRegistered() { + RuntimeHints runtimeHints = new RuntimeHints(); + OllamaRuntimeHints ollamaRuntimeHints = new OllamaRuntimeHints(); + ollamaRuntimeHints.registerHints(runtimeHints, null); + + // Ensure reflection hints are properly registered + assertThat(runtimeHints.reflection().typeHints().spliterator().estimateSize()).isGreaterThan(0); + } + + @Test + void verifyMultipleRegistrationCallsAreIdempotent() { + RuntimeHints runtimeHints = new RuntimeHints(); + OllamaRuntimeHints ollamaRuntimeHints = new OllamaRuntimeHints(); + + // Register hints multiple times + ollamaRuntimeHints.registerHints(runtimeHints, null); + long firstCount = runtimeHints.reflection().typeHints().spliterator().estimateSize(); + + ollamaRuntimeHints.registerHints(runtimeHints, null); + long secondCount = runtimeHints.reflection().typeHints().spliterator().estimateSize(); + + // Should not register duplicate hints + assertThat(firstCount).isEqualTo(secondCount); + } + + @Test + void verifyMainApiClassesRegistered() { + RuntimeHints runtimeHints = new RuntimeHints(); + OllamaRuntimeHints ollamaRuntimeHints = new OllamaRuntimeHints(); + ollamaRuntimeHints.registerHints(runtimeHints, null); + + Set registeredTypes = new HashSet<>(); + runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType())); + + // Verify that the main classes we already know exist are registered + assertThat(registeredTypes.contains(TypeReference.of(OllamaApi.ChatRequest.class))).isTrue(); + assertThat(registeredTypes.contains(TypeReference.of(OllamaApi.Message.class))).isTrue(); + assertThat(registeredTypes.contains(TypeReference.of(OllamaOptions.class))).isTrue(); + } + + @Test + void verifyJsonAnnotatedClassesFromCorrectPackage() { + Set jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage("org.springframework.ai.ollama"); + + // Ensure we found some JSON annotated classes in the expected package + assertThat(jsonAnnotatedClasses.spliterator().estimateSize()).isGreaterThan(0); + + // Verify all found classes are from the expected package + for (TypeReference classRef : jsonAnnotatedClasses) { + assertThat(classRef.getName()).startsWith("org.springframework.ai.ollama"); + } + } + + @Test + void verifyNoUnnecessaryHintsRegistered() { + RuntimeHints runtimeHints = new RuntimeHints(); + OllamaRuntimeHints ollamaRuntimeHints = new OllamaRuntimeHints(); + ollamaRuntimeHints.registerHints(runtimeHints, null); + + Set jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage("org.springframework.ai.ollama"); + + Set registeredTypes = new HashSet<>(); + runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType())); + + // Ensure we don't register significantly more types than needed + // Allow for some additional utility types but prevent hint bloat + assertThat(registeredTypes.size()).isLessThanOrEqualTo(jsonAnnotatedClasses.size() + 15); + } + + @Test + void verifyNestedClassHintsAreRegistered() { + RuntimeHints runtimeHints = new RuntimeHints(); + OllamaRuntimeHints ollamaRuntimeHints = new OllamaRuntimeHints(); + ollamaRuntimeHints.registerHints(runtimeHints, null); + + Set registeredTypes = new HashSet<>(); + runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType())); + + // Verify nested classes that we know exist from the original test + assertThat(registeredTypes.contains(TypeReference.of(OllamaApi.ChatRequest.Tool.class))).isTrue(); + + // Count nested classes to ensure comprehensive registration + long nestedClassCount = registeredTypes.stream().filter(typeRef -> typeRef.getName().contains("$")).count(); + assertThat(nestedClassCount).isGreaterThan(0); + } + }