2121import java .util .List ;
2222import org .apache .solr .client .solrj .SolrClient ;
2323import org .apache .solr .client .solrj .request .CollectionAdminRequest ;
24+ import org .apache .solr .client .solrj .request .SolrQuery ;
25+ import org .apache .solr .common .SolrInputDocument ;
2426import org .apache .solr .mcp .server .TestcontainersConfiguration ;
2527import org .junit .jupiter .api .BeforeAll ;
2628import org .junit .jupiter .api .Test ;
@@ -41,250 +43,193 @@ class CollectionServiceIntegrationTest {
4143 private static final Logger log = LoggerFactory .getLogger (CollectionServiceIntegrationTest .class );
4244
4345 private static final String TEST_COLLECTION = "test_collection" ;
46+
47+ private static final int DOC_COUNT = 50 ;
48+
4449 @ Autowired
4550 private CollectionService collectionService ;
51+
4652 @ Autowired
4753 private SolrClient solrClient ;
4854
4955 @ BeforeAll
50- void setupCollection () throws Exception {
51- CollectionAdminRequest . Create createRequest = CollectionAdminRequest . createCollection ( TEST_COLLECTION ,
52- "_default" , 1 , 1 );
53- createRequest . process ( solrClient );
56+ void setupCollectionWithData () throws Exception {
57+ // 1. Create collection
58+ CollectionAdminRequest . createCollection ( TEST_COLLECTION , "_default" , 1 , 1 ). process ( solrClient );
59+ log . debug ( "Test collection created: {}" , TEST_COLLECTION );
5460
55- // Verify collection was created successfully
56- CollectionAdminRequest .List listRequest = new CollectionAdminRequest .List ();
57- listRequest .process (solrClient );
61+ // 2. Index documents so metrics have real data
62+ for (int i = 0 ; i < DOC_COUNT ; i ++) {
63+ SolrInputDocument doc = new SolrInputDocument ();
64+ doc .addField ("id" , "doc-" + i );
65+ doc .addField ("title_s" , "Document " + i );
66+ doc .addField ("category_s" , (i % 2 == 0 ) ? "even" : "odd" );
67+ doc .addField ("count_i" , i );
68+ solrClient .add (TEST_COLLECTION , doc );
69+ }
70+ solrClient .commit (TEST_COLLECTION );
5871
59- log .debug ("Test collection created: {}" , TEST_COLLECTION );
72+ // 3. Run several queries to populate query result cache and handler stats
73+ for (int i = 0 ; i < 5 ; i ++) {
74+ solrClient .query (TEST_COLLECTION , new SolrQuery ("*:*" ).setRows (10 ));
75+ solrClient .query (TEST_COLLECTION , new SolrQuery ("title_s:Document" ).setRows (5 ));
76+ solrClient .query (TEST_COLLECTION , new SolrQuery ("*:*" ).addFilterQuery ("category_s:even" ).setRows (10 ));
77+ }
78+ log .debug ("Indexed {} documents and ran warm-up queries" , DOC_COUNT );
6079 }
6180
6281 @ Test
6382 void testListCollections () {
64- // Test listing collections
6583 List <String > collections = collectionService .listCollections ();
6684
6785 log .debug ("Collections: {}" , collections );
6886
69- // Enhanced assertions for collections list
70- assertNotNull (collections , "Collections list should not be null" );
71- assertFalse (collections .isEmpty (), "Collections list should not be empty" );
87+ assertNotNull (collections );
88+ assertFalse (collections .isEmpty ());
7289
73- // Check if the test collection exists (either as exact name or as shard)
7490 boolean testCollectionExists = collections .contains (TEST_COLLECTION )
7591 || collections .stream ().anyMatch (col -> col .startsWith (TEST_COLLECTION + "_shard" ));
7692 assertTrue (testCollectionExists ,
77- "Collections should contain the test collection: " + TEST_COLLECTION + " (found: " + collections + ")" );
78-
79- // Verify collection names are not null or empty
80- for (String collection : collections ) {
81- assertNotNull (collection , "Collection name should not be null" );
82- assertFalse (collection .trim ().isEmpty (), "Collection name should not be empty" );
83- }
93+ "Collections should contain " + TEST_COLLECTION + " (found: " + collections + ")" );
8494
85- // Verify expected collection characteristics
8695 assertEquals (collections .size (), collections .stream ().distinct ().count (), "Collection names should be unique" );
87-
88- // Verify that collections follow expected naming patterns
89- for (String collection : collections ) {
90- // Collection names should either be simple names or shard names
91- assertTrue (collection .matches ("^[a-zA-Z0-9_]+(_shard\\ d+_replica_n\\ d+)?$" ),
92- "Collection name should follow expected pattern: " + collection );
93- }
9496 }
9597
9698 @ Test
97- void testGetCollectionStats () throws Exception {
98- // Test getting collection stats
99+ void testGetCollectionStats_reflectsIndexedData () throws Exception {
99100 SolrMetrics metrics = collectionService .getCollectionStats (TEST_COLLECTION );
100101
101- // Enhanced assertions for metrics
102- assertNotNull (metrics , "Collection stats should not be null" );
103- assertNotNull (metrics .timestamp (), "Timestamp should not be null" );
102+ assertNotNull (metrics );
103+ assertNotNull (metrics .timestamp ());
104104
105- // Verify index stats
106- assertNotNull (metrics .indexStats (), "Index stats should not be null" );
105+ // Index stats should reflect the documents we indexed
107106 IndexStats indexStats = metrics .indexStats ();
108- assertNotNull (indexStats .numDocs (), "Number of documents should not be null" );
109- assertTrue (indexStats .numDocs () >= 0 , "Number of documents should be non-negative" );
107+ assertNotNull (indexStats );
108+ assertEquals (DOC_COUNT , indexStats .numDocs (), "numDocs should match indexed document count" );
109+ assertNotNull (indexStats .segmentCount ());
110+ assertTrue (indexStats .segmentCount () >= 1 , "Should have at least one segment after indexing" );
110111
111- // Verify query stats
112- assertNotNull (metrics .queryStats (), "Query stats should not be null" );
112+ // Query stats come from the *:* probe query inside getCollectionStats
113113 QueryStats queryStats = metrics .queryStats ();
114- assertNotNull (queryStats .queryTime (), "Query time should not be null" );
115- assertTrue (queryStats .queryTime () >= 0 , "Query time should be non-negative" );
116- assertNotNull (queryStats .totalResults (), "Total results should not be null" );
117- assertTrue (queryStats .totalResults () >= 0 , "Total results should be non-negative" );
118- assertNotNull (queryStats .start (), "Start should not be null" );
119- assertTrue (queryStats .start () >= 0 , "Start should be non-negative" );
120-
121- // Verify timestamp is recent (within last 10 seconds)
122- long currentTime = System .currentTimeMillis ();
123- long timestampTime = metrics .timestamp ().getTime ();
124- assertTrue (currentTime - timestampTime < 10000 , "Timestamp should be recent (within 10 seconds)" );
125-
126- // Verify optional stats (cache and handler stats may be null, which is
127- // acceptable)
128- if (metrics .cacheStats () != null ) {
129- CacheStats cacheStats = metrics .cacheStats ();
130- // Verify at least one cache type exists if cache stats are present
131- assertTrue (
132- cacheStats .queryResultCache () != null || cacheStats .documentCache () != null
133- || cacheStats .filterCache () != null ,
134- "At least one cache type should be present if cache stats exist" );
135- }
136-
137- if (metrics .handlerStats () != null ) {
138- HandlerStats handlerStats = metrics .handlerStats ();
139- // Verify at least one handler type exists if handler stats are present
140- assertTrue (handlerStats .selectHandler () != null || handlerStats .updateHandler () != null ,
141- "At least one handler type should be present if handler stats exist" );
142- }
114+ assertNotNull (queryStats );
115+ assertNotNull (queryStats .queryTime ());
116+ assertEquals ((long ) DOC_COUNT , queryStats .totalResults (), "totalResults should match indexed document count" );
117+ assertEquals (0L , queryStats .start ());
118+
119+ // Cache stats should be present after warm-up queries
120+ assertNotNull (metrics .cacheStats (), "Cache stats should not be null after queries ran" );
121+ CacheStats cacheStats = metrics .cacheStats ();
122+ assertNotNull (cacheStats .queryResultCache ());
123+ assertTrue (cacheStats .queryResultCache ().lookups () > 0 , "Query result cache should have lookups after queries" );
124+ assertNotNull (cacheStats .documentCache ());
125+ assertTrue (cacheStats .documentCache ().lookups () > 0 , "Document cache should have lookups after queries" );
126+ assertNotNull (cacheStats .filterCache ());
127+ assertTrue (cacheStats .filterCache ().lookups () > 0 , "Filter cache should have lookups after filter queries" );
128+
129+ // Handler stats should reflect actual request counts
130+ assertNotNull (metrics .handlerStats (), "Handler stats should not be null after queries ran" );
131+ HandlerStats handlerStats = metrics .handlerStats ();
132+ assertNotNull (handlerStats .selectHandler ());
133+ assertTrue (handlerStats .selectHandler ().requests () > 0 , "Select handler should have processed requests" );
134+ assertNotNull (handlerStats .updateHandler ());
135+ assertTrue (handlerStats .updateHandler ().requests () > 0 ,
136+ "Update handler should have processed requests from indexing" );
143137 }
144138
145139 @ Test
146- void testCheckHealthHealthy () {
147- // Test checking health of a valid collection
140+ void testCheckHealth_healthy () {
148141 SolrHealthStatus status = collectionService .checkHealth (TEST_COLLECTION );
149142
150- log .debug ("Health status for valid collection: {}" , status );
151-
152- // Enhanced assertions for healthy collection
153- assertNotNull (status , "Health status should not be null" );
154- assertTrue (status .isHealthy (), "Collection should be healthy" );
155-
156- // Verify response time
157- assertNotNull (status .responseTime (), "Response time should not be null" );
158- assertTrue (status .responseTime () >= 0 , "Response time should be non-negative" );
159- assertTrue (status .responseTime () < 30000 , "Response time should be reasonable (< 30 seconds)" );
160-
161- // Verify document count
162- assertNotNull (status .totalDocuments (), "Total documents should not be null" );
163- assertTrue (status .totalDocuments () >= 0 , "Total documents should be non-negative" );
143+ log .debug ("Health status: {}" , status );
164144
165- // Verify timestamp
166- assertNotNull (status .lastChecked (), "Last checked timestamp should not be null" );
167- long currentTime = System .currentTimeMillis ();
168- long lastCheckedTime = status .lastChecked ().getTime ();
169- assertTrue (currentTime - lastCheckedTime < 5000 ,
170- "Last checked timestamp should be very recent (within 5 seconds)" );
145+ assertNotNull (status );
146+ assertTrue (status .isHealthy ());
147+ assertNull (status .errorMessage ());
148+ assertEquals (TEST_COLLECTION , status .collection ());
171149
172- // Verify no error message for healthy collection
173- assertNull (status .errorMessage (), "Error message should be null for healthy collection" );
150+ assertNotNull ( status . responseTime ());
151+ assertTrue (status .responseTime () >= 0 );
174152
175- // Verify collection name is populated
176- assertEquals (TEST_COLLECTION , status .collection (), "Collection name should be populated in health status" );
153+ assertEquals ((long ) DOC_COUNT , status .totalDocuments (), "Health check should report indexed document count" );
177154
178- // Verify string representation contains meaningful information
179- String statusString = status .toString ();
180- assertTrue (statusString .contains ("healthy" ) || statusString .contains ("true" ),
181- "Status string should indicate healthy state" );
155+ assertNotNull (status .lastChecked ());
156+ assertTrue (System .currentTimeMillis () - status .lastChecked ().getTime () < 5000 );
182157 }
183158
184159 @ Test
185- void testCheckHealthUnhealthy () {
186- // Test checking health of an invalid collection
187- String nonExistentCollection = "non_existent_collection" ;
188- SolrHealthStatus status = collectionService .checkHealth (nonExistentCollection );
189-
190- log .debug ("Health status for invalid collection: {}" , status );
191-
192- // Enhanced assertions for unhealthy collection
193- assertNotNull (status , "Health status should not be null" );
194- assertFalse (status .isHealthy (), "Collection should not be healthy" );
195-
196- // Verify timestamp
197- assertNotNull (status .lastChecked (), "Last checked timestamp should not be null" );
198- long currentTime = System .currentTimeMillis ();
199- long lastCheckedTime = status .lastChecked ().getTime ();
200- assertTrue (currentTime - lastCheckedTime < 5000 ,
201- "Last checked timestamp should be very recent (within 5 seconds)" );
202-
203- // Verify error message
204- assertNotNull (status .errorMessage (), "Error message should not be null for unhealthy collection" );
205- assertFalse (status .errorMessage ().trim ().isEmpty (),
206- "Error message should not be empty for unhealthy collection" );
207-
208- // Verify that performance metrics are null for unhealthy collection
209- assertNull (status .responseTime (), "Response time should be null for unhealthy collection" );
210- assertNull (status .totalDocuments (), "Total documents should be null for unhealthy collection" );
211-
212- // Verify collection name is populated even for unhealthy collection
213- assertEquals (nonExistentCollection , status .collection (),
214- "Collection name should be populated for unhealthy collection" );
215-
216- // Verify error message contains meaningful information
217- String errorMessage = status .errorMessage ().toLowerCase ();
218- assertTrue (
219- errorMessage .contains ("collection" ) || errorMessage .contains ("not found" )
220- || errorMessage .contains ("error" ) || errorMessage .contains ("fail" ),
221- "Error message should contain meaningful error information" );
222-
223- // Verify string representation indicates unhealthy state
224- String statusString = status .toString ();
225- if (statusString != null ) {
226- assertTrue (statusString .contains ("false" ) || statusString .contains ("unhealthy" )
227- || statusString .contains ("error" ), "Status string should indicate unhealthy state" );
228- }
160+ void testCheckHealth_nonExistentCollection () {
161+ String missing = "non_existent_collection" ;
162+ SolrHealthStatus status = collectionService .checkHealth (missing );
163+
164+ assertNotNull (status );
165+ assertFalse (status .isHealthy ());
166+ assertNotNull (status .errorMessage ());
167+ assertFalse (status .errorMessage ().isBlank ());
168+ assertEquals (missing , status .collection ());
169+ assertNull (status .responseTime ());
170+ assertNull (status .totalDocuments ());
229171 }
230172
231173 @ Test
232174 void testCollectionNameExtraction () {
233- // Test collection name extraction functionality
234- assertEquals (TEST_COLLECTION , collectionService .extractCollectionName (TEST_COLLECTION ),
235- "Regular collection name should be returned as-is" );
236-
237- assertEquals ("films" , collectionService .extractCollectionName ("films_shard1_replica_n1" ),
238- "Shard name should be extracted to base collection name" );
239-
240- assertEquals ("products" , collectionService .extractCollectionName ("products_shard2_replica_n3" ),
241- "Complex shard name should be extracted correctly" );
242-
243- assertNull (collectionService .extractCollectionName (null ), "Null input should return null" );
244-
245- assertEquals ("" , collectionService .extractCollectionName ("" ), "Empty string should return empty string" );
175+ assertEquals (TEST_COLLECTION , collectionService .extractCollectionName (TEST_COLLECTION ));
176+ assertEquals ("films" , collectionService .extractCollectionName ("films_shard1_replica_n1" ));
177+ assertEquals ("products" , collectionService .extractCollectionName ("products_shard2_replica_n3" ));
178+ assertNull (collectionService .extractCollectionName (null ));
179+ assertEquals ("" , collectionService .extractCollectionName ("" ));
246180 }
247181
248182 @ Test
249- void testGetCacheMetrics () {
183+ void testGetCacheMetrics_afterQueries () {
250184 CacheStats cacheStats = collectionService .getCacheMetrics (TEST_COLLECTION );
251185
252- assertNotNull (cacheStats , "Cache stats should not be null for an existing collection " );
186+ assertNotNull (cacheStats , "Cache stats should not be null after warm-up queries " );
253187
254- // Solr registers all three caches at collection creation
255- assertNotNull (cacheStats .queryResultCache (), "Query result cache should not be null" );
256- assertTrue (cacheStats .queryResultCache ().lookups () >= 0 , "Query result cache lookups should be non-negative" );
188+ // Query result cache: warm-up queries should have generated lookups
189+ CacheInfo qrc = cacheStats .queryResultCache ();
190+ assertNotNull (qrc );
191+ assertTrue (qrc .lookups () > 0 , "queryResultCache lookups should be positive after queries" );
192+ assertNotNull (qrc .hitratio ());
193+ assertNotNull (qrc .size ());
257194
258- assertNotNull (cacheStats .documentCache (), "Document cache should not be null" );
259- assertTrue (cacheStats .documentCache ().lookups () >= 0 , "Document cache lookups should be non-negative" );
195+ // Document cache: reading documents populates this cache
196+ CacheInfo dc = cacheStats .documentCache ();
197+ assertNotNull (dc );
198+ assertTrue (dc .lookups () > 0 , "documentCache lookups should be positive after queries" );
260199
261- assertNotNull (cacheStats .filterCache (), "Filter cache should not be null" );
262- assertTrue (cacheStats .filterCache ().lookups () >= 0 , "Filter cache lookups should be non-negative" );
200+ // Filter cache: the filter queries we ran should generate lookups
201+ CacheInfo fc = cacheStats .filterCache ();
202+ assertNotNull (fc );
203+ assertTrue (fc .lookups () > 0 , "filterCache lookups should be positive after filter queries" );
263204 }
264205
265206 @ Test
266- void testGetHandlerMetrics () {
207+ void testGetHandlerMetrics_afterQueriesAndIndexing () {
267208 HandlerStats handlerStats = collectionService .getHandlerMetrics (TEST_COLLECTION );
268209
269- assertNotNull (handlerStats , "Handler stats should not be null for an existing collection " );
210+ assertNotNull (handlerStats , "Handler stats should not be null after activity " );
270211
271- assertNotNull (handlerStats .selectHandler (), "Select handler should not be null" );
272- assertTrue (handlerStats .selectHandler ().requests () >= 0 , "Select handler requests should be non-negative" );
212+ // Select handler: warm-up queries should have driven request counts > 0
213+ HandlerInfo select = handlerStats .selectHandler ();
214+ assertNotNull (select );
215+ assertTrue (select .requests () > 0 , "Select handler requests should be positive after queries" );
216+ assertNotNull (select .errors ());
217+ assertNotNull (select .timeouts ());
273218
274- assertNotNull (handlerStats .updateHandler (), "Update handler should not be null" );
275- assertTrue (handlerStats .updateHandler ().requests () >= 0 , "Update handler requests should be non-negative" );
219+ // Update handler: indexing 50 docs should have driven request counts > 0
220+ HandlerInfo update = handlerStats .updateHandler ();
221+ assertNotNull (update );
222+ assertTrue (update .requests () > 0 , "Update handler requests should be positive after indexing" );
276223 }
277224
278225 @ Test
279- void testGetCacheMetrics_NonExistentCollection () {
280- CacheStats result = collectionService .getCacheMetrics ("non_existent_collection" );
281- assertNull (result , "Cache metrics for non-existent collection should be null" );
226+ void testGetCacheMetrics_nonExistentCollection () {
227+ assertNull (collectionService .getCacheMetrics ("non_existent_collection" ));
282228 }
283229
284230 @ Test
285- void testGetHandlerMetrics_NonExistentCollection () {
286- HandlerStats result = collectionService .getHandlerMetrics ("non_existent_collection" );
287- assertNull (result , "Handler metrics for non-existent collection should be null" );
231+ void testGetHandlerMetrics_nonExistentCollection () {
232+ assertNull (collectionService .getHandlerMetrics ("non_existent_collection" ));
288233 }
289234
290235 @ Test
@@ -293,13 +238,13 @@ void createCollection_createsAndListable() throws Exception {
293238
294239 CollectionCreationResult result = collectionService .createCollection (name , null , null , null );
295240
296- assertTrue (result .success (), "Collection creation should succeed" );
297- assertEquals (name , result .name (), "Result should contain the collection name" );
298- assertNotNull (result .createdAt (), "Creation timestamp should be set" );
241+ assertTrue (result .success ());
242+ assertEquals (name , result .name ());
243+ assertNotNull (result .createdAt ());
299244
300245 List <String > collections = collectionService .listCollections ();
301- boolean collectionExists = collections .contains (name )
246+ boolean exists = collections .contains (name )
302247 || collections .stream ().anyMatch (col -> col .startsWith (name + "_shard" ));
303- assertTrue (collectionExists , "Newly created collection should appear in list (found: " + collections + ")" );
248+ assertTrue (exists , "Newly created collection should appear in list (found: " + collections + ")" );
304249 }
305250}
0 commit comments