20
20
import java .util .Set ;
21
21
22
22
import org .junit .jupiter .api .Test ;
23
+ import org .junit .jupiter .api .BeforeEach ;
23
24
24
25
import org .springframework .ai .openai .OpenAiChatOptions ;
25
26
import org .springframework .ai .openai .api .OpenAiApi ;
26
27
import org .springframework .ai .openai .api .OpenAiAudioApi ;
27
28
import org .springframework .ai .openai .api .OpenAiImageApi ;
28
29
import org .springframework .aot .hint .RuntimeHints ;
29
30
import org .springframework .aot .hint .TypeReference ;
31
+ import org .springframework .aot .hint .MemberCategory ;
30
32
31
33
import static org .assertj .core .api .AssertionsForClassTypes .assertThat ;
32
34
import static org .springframework .ai .aot .AiRuntimeHints .findJsonAnnotatedClassesInPackage ;
33
35
34
36
class OpenAiRuntimeHintsTests {
35
37
38
+ private RuntimeHints runtimeHints ;
39
+
40
+ private OpenAiRuntimeHints openAiRuntimeHints ;
41
+
42
+ @ BeforeEach
43
+ void setUp () {
44
+ runtimeHints = new RuntimeHints ();
45
+ openAiRuntimeHints = new OpenAiRuntimeHints ();
46
+ }
47
+
36
48
@ Test
37
49
void registerHints () {
38
- RuntimeHints runtimeHints = new RuntimeHints ();
39
- OpenAiRuntimeHints openAiRuntimeHints = new OpenAiRuntimeHints ();
40
50
openAiRuntimeHints .registerHints (runtimeHints , null );
41
51
42
52
Set <TypeReference > jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage ("org.springframework.ai.openai" );
@@ -61,4 +71,122 @@ void registerHints() {
61
71
assertThat (registeredTypes .contains (TypeReference .of (OpenAiChatOptions .class ))).isTrue ();
62
72
}
63
73
74
+ @ Test
75
+ void registerHintsWithNullClassLoader () {
76
+ // Test that registering hints with null ClassLoader works correctly
77
+ openAiRuntimeHints .registerHints (runtimeHints , null );
78
+
79
+ Set <TypeReference > registeredTypes = new HashSet <>();
80
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> registeredTypes .add (typeHint .getType ()));
81
+
82
+ assertThat (registeredTypes .size ()).isGreaterThan (0 );
83
+ }
84
+
85
+ @ Test
86
+ void registerHintsWithCustomClassLoader () {
87
+ // Test that registering hints with a custom ClassLoader works correctly
88
+ ClassLoader customClassLoader = Thread .currentThread ().getContextClassLoader ();
89
+ openAiRuntimeHints .registerHints (runtimeHints , customClassLoader );
90
+
91
+ Set <TypeReference > registeredTypes = new HashSet <>();
92
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> registeredTypes .add (typeHint .getType ()));
93
+
94
+ assertThat (registeredTypes .size ()).isGreaterThan (0 );
95
+ }
96
+
97
+ @ Test
98
+ void allMemberCategoriesAreRegistered () {
99
+ openAiRuntimeHints .registerHints (runtimeHints , null );
100
+
101
+ Set <TypeReference > jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage ("org.springframework.ai.openai" );
102
+
103
+ // Verify that all MemberCategory values are registered for each type
104
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> {
105
+ if (jsonAnnotatedClasses .contains (typeHint .getType ())) {
106
+ Set <MemberCategory > expectedCategories = Set .of (MemberCategory .values ());
107
+ Set <MemberCategory > actualCategories = typeHint .getMemberCategories ();
108
+ assertThat (actualCategories .containsAll (expectedCategories )).isTrue ();
109
+ }
110
+ });
111
+ }
112
+
113
+ @ Test
114
+ void verifySpecificOpenAiApiClasses () {
115
+ openAiRuntimeHints .registerHints (runtimeHints , null );
116
+
117
+ Set <TypeReference > registeredTypes = new HashSet <>();
118
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> registeredTypes .add (typeHint .getType ()));
119
+
120
+ // Verify specific OpenAI API classes are registered
121
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .class ))).isTrue ();
122
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiAudioApi .class ))).isTrue ();
123
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiImageApi .class ))).isTrue ();
124
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiChatOptions .class ))).isTrue ();
125
+ }
126
+
127
+ @ Test
128
+ void emptyRuntimeHintsInitiallyContainsNoTypes () {
129
+ // Verify that fresh RuntimeHints instance contains no reflection hints
130
+ RuntimeHints emptyHints = new RuntimeHints ();
131
+ Set <TypeReference > emptyRegisteredTypes = new HashSet <>();
132
+ emptyHints .reflection ().typeHints ().forEach (typeHint -> emptyRegisteredTypes .add (typeHint .getType ()));
133
+
134
+ assertThat (emptyRegisteredTypes .size ()).isEqualTo (0 );
135
+ }
136
+
137
+ @ Test
138
+ void multipleRegistrationCallsAreIdempotent () {
139
+ // Register hints multiple times and verify no duplicates
140
+ openAiRuntimeHints .registerHints (runtimeHints , null );
141
+ int firstRegistrationCount = (int ) runtimeHints .reflection ().typeHints ().count ();
142
+
143
+ openAiRuntimeHints .registerHints (runtimeHints , null );
144
+ int secondRegistrationCount = (int ) runtimeHints .reflection ().typeHints ().count ();
145
+
146
+ assertThat (firstRegistrationCount ).isEqualTo (secondRegistrationCount );
147
+ }
148
+
149
+ @ Test
150
+ void verifyJsonAnnotatedClassesInPackageIsNotEmpty () {
151
+ Set <TypeReference > jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage ("org.springframework.ai.openai" );
152
+ assertThat (jsonAnnotatedClasses .size ()).isGreaterThan (0 );
153
+ }
154
+
155
+ @ Test
156
+ void verifyAllRegisteredTypesHaveReflectionHints () {
157
+ openAiRuntimeHints .registerHints (runtimeHints , null );
158
+
159
+ // Ensure every registered type has proper reflection hints
160
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> {
161
+ assertThat (typeHint .getType ()).isNotNull ();
162
+ assertThat (typeHint .getMemberCategories ().size ()).isGreaterThan (0 );
163
+ });
164
+ }
165
+
166
+ @ Test
167
+ void verifyEnumTypesAreRegistered () {
168
+ openAiRuntimeHints .registerHints (runtimeHints , null );
169
+
170
+ Set <TypeReference > registeredTypes = new HashSet <>();
171
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> registeredTypes .add (typeHint .getType ()));
172
+
173
+ // Verify enum types are properly registered
174
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .ChatCompletionFinishReason .class ))).isTrue ();
175
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .OutputModality .class ))).isTrue ();
176
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiAudioApi .TtsModel .class ))).isTrue ();
177
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiAudioApi .WhisperModel .class ))).isTrue ();
178
+ }
179
+
180
+ @ Test
181
+ void verifyNestedClassesAreRegistered () {
182
+ openAiRuntimeHints .registerHints (runtimeHints , null );
183
+
184
+ Set <TypeReference > registeredTypes = new HashSet <>();
185
+ runtimeHints .reflection ().typeHints ().forEach (typeHint -> registeredTypes .add (typeHint .getType ()));
186
+
187
+ // Verify nested classes are properly registered
188
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .FunctionTool .class ))).isTrue ();
189
+ assertThat (registeredTypes .contains (TypeReference .of (OpenAiApi .FunctionTool .Function .class ))).isTrue ();
190
+ }
191
+
64
192
}
0 commit comments