From a153ad6562ee7e839621c732979a86cc705a24e5 Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Thu, 21 May 2026 18:03:56 -0400 Subject: [PATCH 1/7] increase speed of test --- .../solr/handler/admin/LukeTestUtil.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/LukeTestUtil.java diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeTestUtil.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeTestUtil.java new file mode 100644 index 00000000000..308bed73714 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeTestUtil.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.handler.admin; + +import static org.junit.Assert.assertNull; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.request.LukeRequest; +import org.apache.solr.client.solrj.response.InputStreamResponseParser; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.util.BaseTestHarness; + +final class LukeTestUtil { + + private LukeTestUtil() {} + + static void assertLukeXPath( + SolrClient client, String collection, SolrParams extra, String... xpaths) throws Exception { + ModifiableSolrParams params = new ModifiableSolrParams(); + params.set("shards.info", "true"); + params.add(extra); + LukeRequest req = new LukeRequest(params); + req.setNumTerms(0); + req.setResponseParser(new InputStreamResponseParser("xml")); + NamedList raw = + collection != null ? client.request(req, collection) : client.request(req); + String xml = InputStreamResponseParser.consumeResponseToString(raw); + String failedXpath = BaseTestHarness.validateXPath(xml, xpaths); + assertNull("XPath validation failed: " + failedXpath + "\nResponse:\n" + xml, failedXpath); + } +} From 6202ed8e0ce9496716c48c14924da4dc886cbcf6 Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Thu, 21 May 2026 18:04:42 -0400 Subject: [PATCH 2/7] increase speed of test --- .../handler/admin/LukeHandlerCloudTest.java | 126 ++++++++- .../admin/LukeRequestHandlerDistribTest.java | 261 ++++-------------- 2 files changed, 185 insertions(+), 202 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java index f49ac3ccca0..d1583bf902f 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java @@ -26,7 +26,9 @@ import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.LukeRequest; import org.apache.solr.client.solrj.request.SolrQuery; +import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.client.solrj.request.schema.SchemaRequest; +import org.apache.solr.client.solrj.response.LukeResponse; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.SolrException; @@ -41,9 +43,29 @@ /** Cloud-specific Luke tests that require SolrCloud features like managed schema and Schema API. */ public class LukeHandlerCloudTest extends SolrCloudTestCase { + private static final String DISTRIB_COLLECTION = "lukeDistribTests"; + private static final int NUM_DOCS = 20; + @BeforeClass public static void setupCluster() throws Exception { - configureCluster(2).addConfig("managed", configset("cloud-managed")).configure(); + System.setProperty("managed.schema.mutable", "true"); + configureCluster(2) + .addConfig("managed", configset("cloud-managed")) + .addConfig("dynamic", configset("cloud-dynamic")) + .configure(); + + CollectionAdminRequest.createCollection(DISTRIB_COLLECTION, "dynamic", 2, 1) + .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT); + cluster.waitForActiveCollection(DISTRIB_COLLECTION, 2, 2); + + for (int i = 0; i < NUM_DOCS; i++) { + SolrInputDocument doc = new SolrInputDocument(); + doc.addField("id", String.valueOf(i)); + doc.addField("name", "name_" + i); + doc.addField("subject", "subject value " + (i % 5)); + cluster.getSolrClient().add(DISTRIB_COLLECTION, doc); + } + cluster.getSolrClient().commit(DISTRIB_COLLECTION); } private void requestLuke(String collection, SolrParams extra) throws Exception { @@ -52,6 +74,107 @@ private void requestLuke(String collection, SolrParams extra) throws Exception { req.process(cluster.getSolrClient(), collection); } + @Test + public void testLocalMode() throws Exception { + DocCollection docColl = getCollectionState(DISTRIB_COLLECTION); + Slice slice = docColl.getSlices().iterator().next(); + Replica leader = slice.getLeader(); + try (SolrClient client = getHttpSolrClient(leader)) { + LukeRequest req = new LukeRequest(params(DISTRIB, "false")); + req.setNumTerms(0); + LukeResponse rsp = req.process(client); + + assertNotNull("index info should be present", rsp.getIndexInfo()); + assertNull("shards should NOT be present in local mode", rsp.getShardResponses()); + } + } + + @Test + public void testDistributedShardError() { + SolrParams lukeParams = params("id", "0", "show", "schema"); + + Exception ex = expectThrows(Exception.class, () -> requestLuke(DISTRIB_COLLECTION, lukeParams)); + String fullMessage = SolrException.getRootCause(ex).getMessage(); + assertTrue( + "exception should mention doc style mismatch: " + fullMessage, + fullMessage.contains("missing doc param for doc style")); + } + + @Test + public void testDistributedDocIdRejected() { + SolrParams lukeParams = params("docId", "0"); + + Exception ex = expectThrows(Exception.class, () -> requestLuke(DISTRIB_COLLECTION, lukeParams)); + String fullMessage = SolrException.getRootCause(ex).getMessage(); + assertTrue( + "exception should mention docId not supported: " + fullMessage, + fullMessage.contains("docId parameter is not supported in distributed mode")); + } + + @Test + public void testShardsParamRoutesToSpecificShard() throws Exception { + String collection = "lukeShardsRouting"; + CollectionAdminRequest.createCollectionWithImplicitRouter( + collection, "dynamic", "shard1,shard2", 1) + .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT); + cluster.waitForActiveCollection(collection, 2, 2); + + try { + // Index a doc with a dynamic field only to shard1 + SolrInputDocument docWithField = new SolrInputDocument(); + docWithField.addField("id", "700"); + docWithField.addField("name", "shard1_only"); + docWithField.addField("only_on_shard1_s", "present"); + UpdateRequest addToShard1 = new UpdateRequest(); + addToShard1.add(docWithField); + addToShard1.setParam("_route_", "shard1"); + addToShard1.process(cluster.getSolrClient(), collection); + + // Index a plain doc to shard2 (no dynamic field) + SolrInputDocument docWithoutField = new SolrInputDocument(); + docWithoutField.addField("id", "701"); + docWithoutField.addField("name", "shard2_only"); + UpdateRequest addToShard2 = new UpdateRequest(); + addToShard2.add(docWithoutField); + addToShard2.setParam("_route_", "shard2"); + addToShard2.process(cluster.getSolrClient(), collection); + + cluster.getSolrClient().commit(collection); + + DocCollection docColl = getCollectionState(collection); + String shard1Url = docColl.getSlice("shard1").getLeader().getCoreUrl(); + String shard2Url = docColl.getSlice("shard2").getLeader().getCoreUrl(); + + // Query with shards= pointing only at shard2 — the dynamic field should NOT appear. + // This also tests that a single remote shard is correctly fanned out to rather than + // falling through to local-mode on the coordinating node. + LukeRequest req = new LukeRequest(params("shards", shard2Url)); + req.setNumTerms(0); + LukeResponse rsp = req.process(cluster.getSolrClient(), collection); + + Map fields = rsp.getFieldInfo(); + assertNotNull("fields should be present", fields); + assertNull( + "only_on_shard1_s should NOT be present when querying only shard2", + fields.get("only_on_shard1_s")); + assertNotNull("'name' field should still be present", fields.get("name")); + + // Now query with shards= pointing only at shard1 — the dynamic field SHOULD appear + req = new LukeRequest(params("shards", shard1Url)); + req.setNumTerms(0); + rsp = req.process(cluster.getSolrClient(), collection); + + fields = rsp.getFieldInfo(); + assertNotNull("fields should be present", fields); + assertNotNull( + "only_on_shard1_s SHOULD be present when querying shard1", + fields.get("only_on_shard1_s")); + } finally { + CollectionAdminRequest.deleteCollection(collection) + .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT); + } + } + /** * Verifies that distributed Luke detects inconsistent index flags across shards. Uses Schema API * to change a field's {@code stored} property between indexing on different shards, producing @@ -60,7 +183,6 @@ private void requestLuke(String collection, SolrParams extra) throws Exception { @Test public void testInconsistentIndexFlagsAcrossShards() throws Exception { String collection = "lukeInconsistentFlags"; - System.setProperty("managed.schema.mutable", "true"); CollectionAdminRequest.createCollection(collection, "managed", 2, 1) .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java index 15296027756..d755d1ab6e9 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java @@ -19,7 +19,6 @@ import java.util.Map; import org.apache.solr.BaseDistributedSearchTestCase; import org.apache.solr.client.solrj.request.LukeRequest; -import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.client.solrj.response.LukeResponse; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrException; @@ -30,7 +29,6 @@ import org.apache.solr.request.SolrQueryRequestBase; import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.CommitUpdateCommand; -import org.apache.solr.util.BaseTestHarness; import org.junit.Test; public class LukeRequestHandlerDistribTest extends BaseDistributedSearchTestCase { @@ -72,15 +70,8 @@ private LukeResponse requestLuke(ModifiableSolrParams extra) throws Exception { private void assertLukeXPath(ModifiableSolrParams extra, String... xpaths) throws Exception { ModifiableSolrParams params = new ModifiableSolrParams(); params.set("shards", shards); - params.set("shards.info", "true"); params.add(extra); - LukeRequest req = new LukeRequest(params); - req.setNumTerms(0); - req.setResponseParser(new InputStreamResponseParser("xml")); - NamedList raw = controlClient.request(req); - String xml = InputStreamResponseParser.consumeResponseToString(raw); - String failedXpath = BaseTestHarness.validateXPath(xml, xpaths); - assertNull("XPath validation failed: " + failedXpath + "\nResponse:\n" + xml, failedXpath); + LukeTestUtil.assertLukeXPath(controlClient, null, params, xpaths); } private void indexTestData() throws Exception { @@ -92,9 +83,10 @@ private void indexTestData() throws Exception { @Test @ShardsFixed(num = 2) - public void testDistributedAggregate() throws Exception { + public void testDistributedAggregateAndFields() throws Exception { indexTestData(); + // --- Aggregate index stats --- LukeResponse rsp = requestLuke(); assertEquals("aggregated numDocs should equal total docs", NUM_DOCS, rsp.getNumDocs()); @@ -114,15 +106,8 @@ public void testDistributedAggregate() throws Exception { } assertEquals( "sum of per-shard numDocs should equal aggregated numDocs", rsp.getNumDocs(), sumShardDocs); - } - - @Test - @ShardsFixed(num = 2) - public void testDistributedFieldsAggregate() throws Exception { - indexTestData(); - - LukeResponse rsp = requestLuke(); + // --- Field-level aggregation --- Map fields = rsp.getFieldInfo(); assertNotNull("fields should be present", fields); @@ -149,32 +134,22 @@ public void testDistributedFieldsAggregate() throws Exception { "//lst[@name='fields']/lst[@name='name']/long[@name='docs'][.='20']", "//lst[@name='fields']/lst[@name='id']/str[@name='type'][.='string']", "//lst[@name='fields']/lst[@name='id']/long[@name='docs'][.='20']"); - } - @Test - @ShardsFixed(num = 2) - public void testDetailedFieldStatsPerShard() throws Exception { - indexTestData(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("fl", "name"); - params.set("numTerms", "5"); + // --- Detailed per-shard stats (topTerms, histogram, distinct) --- + ModifiableSolrParams detailedParams = new ModifiableSolrParams(); + detailedParams.set("fl", "name"); + detailedParams.set("numTerms", "5"); - LukeResponse rsp = requestLuke(params); + LukeResponse detailedRsp = requestLuke(detailedParams); - // Top-level fields should NOT have topTerms, distinct, histogram - LukeResponse.FieldInfo nameField = rsp.getFieldInfo().get("name"); - assertNotNull("'name' field should be present", nameField); - assertNull("topTerms should NOT be in top-level fields", nameField.getTopTerms()); - assertEquals("distinct should NOT be in top-level fields", 0, nameField.getDistinct()); + LukeResponse.FieldInfo detailedNameField = detailedRsp.getFieldInfo().get("name"); + assertNotNull("'name' field should be present", detailedNameField); + assertNull("topTerms should NOT be in top-level fields", detailedNameField.getTopTerms()); + assertEquals("distinct should NOT be in top-level fields", 0, detailedNameField.getDistinct()); - // Per-shard entries should have detailed stats - Map shardResponses = rsp.getShardResponses(); - assertNotNull("shards section should be present", shardResponses); + Map detailedShardResponses = detailedRsp.getShardResponses(); + assertNotNull("shards section should be present", detailedShardResponses); - ModifiableSolrParams detailedParams = new ModifiableSolrParams(); - detailedParams.set("fl", "name"); - detailedParams.set("numTerms", "5"); assertLukeXPath( detailedParams, "/response/lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", @@ -185,39 +160,54 @@ public void testDetailedFieldStatsPerShard() throws Exception { "//lst[@name='shards']/lst/lst[@name='fields']/lst[@name='name']/lst[@name='topTerms']", "//lst[@name='shards']/lst/lst[@name='fields']/lst[@name='name']/lst[@name='histogram']/int[@name='1']", "//lst[@name='shards']/lst/lst[@name='fields']/lst[@name='name']/int[@name='distinct']"); - } - @Test - @ShardsFixed(num = 2) - public void testLocalModeDefault() throws Exception { - indexTestData(); + // --- Doc lookup not found --- + ModifiableSolrParams notFoundParams = new ModifiableSolrParams(); + notFoundParams.set("id", "999888777"); - // Query a single client without the shards param — local mode - LukeRequest req = new LukeRequest(); - req.setNumTerms(0); - LukeResponse rsp = req.process(controlClient); + LukeResponse notFoundRsp = requestLuke(notFoundParams); - assertNotNull("index info should be present", rsp.getIndexInfo()); - assertNull("shards should NOT be present in local mode", rsp.getShardResponses()); - } + NamedList notFoundRaw = notFoundRsp.getResponse(); + assertNull("doc section should NOT be present for missing ID", notFoundRaw.get("doc")); - @Test - @ShardsFixed(num = 2) - public void testExplicitDistribFalse() throws Exception { - indexTestData(); + assertLukeXPath(notFoundParams, "not(//lst[@name='doc'])"); - // Query a single client with distrib=false — no shards param - LukeRequest req = new LukeRequest(params("distrib", "false")); - req.setNumTerms(0); - LukeResponse rsp = req.process(controlClient); + // --- Doc lookup found --- + ModifiableSolrParams foundParams = new ModifiableSolrParams(); + foundParams.set("id", "0"); - assertNotNull("index info should be present", rsp.getIndexInfo()); - assertNull("shards should NOT be present with distrib=false", rsp.getShardResponses()); + assertLukeXPath( + foundParams, + "//lst[@name='doc']/int[@name='docId']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='type'][.='string']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='value'][.='0']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='type'][.='nametext']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='value'][.='name_0']", + "//lst[@name='doc']/arr[@name='solr']/str[.='0']", + "//lst[@name='doc']/arr[@name='solr']/str[.='name_0']", + "//lst[@name='index']", + "//lst[@name='info']"); + + // --- Schema view --- + ModifiableSolrParams schemaParams = new ModifiableSolrParams(); + schemaParams.set("show", "schema"); + + assertLukeXPath( + schemaParams, + "//lst[@name='schema']/lst[@name='fields']/lst[@name='id']/str[@name='type'][.='string']", + "//lst[@name='schema']/lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", + "//lst[@name='schema']/lst[@name='dynamicFields']/lst[@name='*_s']", + "//lst[@name='schema']/str[@name='uniqueKeyField'][.='id']", + "//lst[@name='schema']/lst[@name='types']/lst[@name='string']", + "//lst[@name='schema']/lst[@name='types']/lst[@name='nametext']", + "//lst[@name='schema']/lst[@name='similarity']", + "not(/response/lst[@name='fields'])", + "count(//lst[@name='shards']/lst)=2"); } @Test @ShardsFixed(num = 12) - public void testSparseShards() throws Exception { + public void testSparseShardsAndDeferredIndexFlags() throws Exception { // Index a single doc on shard 0 index_specific( 0, "id", "100", "name", "sparse test", "subject", "subject value", "cat_s", "category"); @@ -275,36 +265,11 @@ public void testSparseShards() throws Exception { "//lst[@name='fields']/lst[@name='cat_s']/str[@name='type'][.='string']", "//lst[@name='fields']/lst[@name='cat_s']/str[@name='dynamicBase'][.='*_s']", "//lst[@name='fields']/lst[@name='cat_s']/long[@name='docs'][.='1']"); - } - - @Test - @ShardsFixed(num = 2) - public void testDistribShowSchema() throws Exception { - indexTestData(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("show", "schema"); - - assertLukeXPath( - params, - "//lst[@name='schema']/lst[@name='fields']/lst[@name='id']/str[@name='type'][.='string']", - "//lst[@name='schema']/lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", - "//lst[@name='schema']/lst[@name='dynamicFields']/lst[@name='*_s']", - "//lst[@name='schema']/str[@name='uniqueKeyField'][.='id']", - "//lst[@name='schema']/lst[@name='types']/lst[@name='string']", - "//lst[@name='schema']/lst[@name='types']/lst[@name='nametext']", - "//lst[@name='schema']/lst[@name='similarity']", - "not(/response/lst[@name='fields'])", - "count(//lst[@name='shards']/lst)=2"); - } - @Test - @ShardsFixed(num = 16) - public void testDeferredIndexFlags() throws Exception { // Index docs with the target field across shards, plus anchor docs without it. // Use numeric IDs (the default test schema copies id to integer fields). // Target docs get even IDs starting at 1000, anchor docs get odd IDs. - for (int i = 0; i < 16 * 4; i++) { + for (int i = 0; i < 12 * 4; i++) { index("id", String.valueOf(1000 + i * 2), "flag_target_s", "value_" + i); index("id", String.valueOf(1001 + i * 2), "name", "anchor"); } @@ -320,94 +285,24 @@ public void testDeferredIndexFlags() throws Exception { controlClient.deleteByQuery("flag_target_s:* AND -id:1000"); controlClient.optimize(); - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("fl", "flag_target_s"); + ModifiableSolrParams flagParams = new ModifiableSolrParams(); + flagParams.set("fl", "flag_target_s"); - LukeResponse rsp = requestLuke(params); + LukeResponse flagRsp = requestLuke(flagParams); - Map fields = rsp.getFieldInfo(); - assertNotNull("fields should be present", fields); - LukeResponse.FieldInfo targetField = fields.get("flag_target_s"); + Map flagFields = flagRsp.getFieldInfo(); + assertNotNull("fields should be present", flagFields); + LukeResponse.FieldInfo targetField = flagFields.get("flag_target_s"); assertNotNull("'flag_target_s' field should be present", targetField); - ModifiableSolrParams xpathParams = new ModifiableSolrParams(); - xpathParams.set("fl", "flag_target_s"); assertLukeXPath( - xpathParams, + flagParams, "//lst[@name='fields']/lst[@name='flag_target_s']/str[@name='type'][.='string']", "//lst[@name='fields']/lst[@name='flag_target_s']/str[@name='dynamicBase'][.='*_s']", "//lst[@name='fields']/lst[@name='flag_target_s']/str[@name='index']", "//lst[@name='fields']/lst[@name='flag_target_s']/long[@name='docs'][.='1']"); } - @Test - @ShardsFixed(num = 2) - public void testDistributedShardError() throws Exception { - indexTestData(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("id", "0"); - params.set("show", "schema"); - - Exception ex = expectThrows(Exception.class, () -> requestLuke(params)); - String fullMessage = SolrException.getRootCause(ex).getMessage(); - assertTrue( - "exception should mention doc style mismatch: " + fullMessage, - fullMessage.contains("missing doc param for doc style")); - } - - @Test - @ShardsFixed(num = 2) - public void testDistributedDocIdRejected() throws Exception { - indexTestData(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("docId", "0"); - - Exception ex = expectThrows(Exception.class, () -> requestLuke(params)); - String fullMessage = SolrException.getRootCause(ex).getMessage(); - assertTrue( - "exception should mention docId not supported: " + fullMessage, - fullMessage.contains("docId parameter is not supported in distributed mode")); - } - - @Test - @ShardsFixed(num = 2) - public void testDistributedDocLookupFound() throws Exception { - indexTestData(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("id", "0"); - - assertLukeXPath( - params, - "//lst[@name='doc']/int[@name='docId']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='type'][.='string']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='value'][.='0']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='type'][.='nametext']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='value'][.='name_0']", - "//lst[@name='doc']/arr[@name='solr']/str[.='0']", - "//lst[@name='doc']/arr[@name='solr']/str[.='name_0']", - "//lst[@name='index']", - "//lst[@name='info']"); - } - - @Test - @ShardsFixed(num = 2) - public void testDistributedDocLookupNotFound() throws Exception { - indexTestData(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("id", "999888777"); - - LukeResponse rsp = requestLuke(params); - - NamedList raw = rsp.getResponse(); - assertNull("doc section should NOT be present for missing ID", raw.get("doc")); - - assertLukeXPath(params, "not(//lst[@name='doc'])"); - } - @Test @ShardsFixed(num = 2) public void testDistributedDocLookupDuplicateId() throws Exception { @@ -445,40 +340,6 @@ public void testDistributedDocLookupDuplicateId() throws Exception { fullMessage.contains("found on multiple shards")); } - @Test - @ShardsFixed(num = 2) - public void testShardsParamRoutesToSpecificShard() throws Exception { - // Index a doc with a dynamic field only to shard 0 - index_specific(0, "id", "700", "name", "shard0_only", "only_on_shard0_s", "present"); - // Index a plain doc to shard 1 (no dynamic field) - index_specific(1, "id", "701", "name", "shard1_only"); - commit(); - - // Query with shards= pointing only at shard 1 — the dynamic field should NOT appear. - // This also tests that a single remote shard is correctly fanned out to rather than - // falling through to local-mode on the coordinating node. - LukeRequest req = new LukeRequest(params("shards", shardsArr[1])); - req.setNumTerms(0); - LukeResponse rsp = req.process(controlClient); - - Map fields = rsp.getFieldInfo(); - assertNotNull("fields should be present", fields); - assertNull( - "only_on_shard0_s should NOT be present when querying only shard 1", - fields.get("only_on_shard0_s")); - assertNotNull("'name' field should still be present", fields.get("name")); - - // Now query with shards= pointing only at shard 0 — the dynamic field SHOULD appear - req = new LukeRequest(params("shards", shardsArr[0])); - req.setNumTerms(0); - rsp = req.process(controlClient); - - fields = rsp.getFieldInfo(); - assertNotNull("fields should be present", fields); - assertNotNull( - "only_on_shard0_s SHOULD be present when querying shard 0", fields.get("only_on_shard0_s")); - } - @Test @ShardsFixed(num = 1) public void testSingleShardViaParamStillDistributes() throws Exception { From 476dd8ac1e48c390f4c78e60db855ecb53b6381d Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Thu, 21 May 2026 18:07:37 -0400 Subject: [PATCH 3/7] make tests faster --- .../handler/admin/LukeHandlerCloudTest.java | 65 ------------------- .../admin/LukeRequestHandlerDistribTest.java | 34 ++++++++++ 2 files changed, 34 insertions(+), 65 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java index d1583bf902f..a75e384be74 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java @@ -26,7 +26,6 @@ import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.LukeRequest; import org.apache.solr.client.solrj.request.SolrQuery; -import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.client.solrj.request.schema.SchemaRequest; import org.apache.solr.client.solrj.response.LukeResponse; import org.apache.solr.client.solrj.response.QueryResponse; @@ -111,70 +110,6 @@ public void testDistributedDocIdRejected() { fullMessage.contains("docId parameter is not supported in distributed mode")); } - @Test - public void testShardsParamRoutesToSpecificShard() throws Exception { - String collection = "lukeShardsRouting"; - CollectionAdminRequest.createCollectionWithImplicitRouter( - collection, "dynamic", "shard1,shard2", 1) - .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT); - cluster.waitForActiveCollection(collection, 2, 2); - - try { - // Index a doc with a dynamic field only to shard1 - SolrInputDocument docWithField = new SolrInputDocument(); - docWithField.addField("id", "700"); - docWithField.addField("name", "shard1_only"); - docWithField.addField("only_on_shard1_s", "present"); - UpdateRequest addToShard1 = new UpdateRequest(); - addToShard1.add(docWithField); - addToShard1.setParam("_route_", "shard1"); - addToShard1.process(cluster.getSolrClient(), collection); - - // Index a plain doc to shard2 (no dynamic field) - SolrInputDocument docWithoutField = new SolrInputDocument(); - docWithoutField.addField("id", "701"); - docWithoutField.addField("name", "shard2_only"); - UpdateRequest addToShard2 = new UpdateRequest(); - addToShard2.add(docWithoutField); - addToShard2.setParam("_route_", "shard2"); - addToShard2.process(cluster.getSolrClient(), collection); - - cluster.getSolrClient().commit(collection); - - DocCollection docColl = getCollectionState(collection); - String shard1Url = docColl.getSlice("shard1").getLeader().getCoreUrl(); - String shard2Url = docColl.getSlice("shard2").getLeader().getCoreUrl(); - - // Query with shards= pointing only at shard2 — the dynamic field should NOT appear. - // This also tests that a single remote shard is correctly fanned out to rather than - // falling through to local-mode on the coordinating node. - LukeRequest req = new LukeRequest(params("shards", shard2Url)); - req.setNumTerms(0); - LukeResponse rsp = req.process(cluster.getSolrClient(), collection); - - Map fields = rsp.getFieldInfo(); - assertNotNull("fields should be present", fields); - assertNull( - "only_on_shard1_s should NOT be present when querying only shard2", - fields.get("only_on_shard1_s")); - assertNotNull("'name' field should still be present", fields.get("name")); - - // Now query with shards= pointing only at shard1 — the dynamic field SHOULD appear - req = new LukeRequest(params("shards", shard1Url)); - req.setNumTerms(0); - rsp = req.process(cluster.getSolrClient(), collection); - - fields = rsp.getFieldInfo(); - assertNotNull("fields should be present", fields); - assertNotNull( - "only_on_shard1_s SHOULD be present when querying shard1", - fields.get("only_on_shard1_s")); - } finally { - CollectionAdminRequest.deleteCollection(collection) - .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT); - } - } - /** * Verifies that distributed Luke detects inconsistent index flags across shards. Uses Schema API * to change a field's {@code stored} property between indexing on different shards, producing diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java index d755d1ab6e9..6f901b20068 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java @@ -340,6 +340,40 @@ public void testDistributedDocLookupDuplicateId() throws Exception { fullMessage.contains("found on multiple shards")); } + @Test + @ShardsFixed(num = 2) + public void testShardsParamRoutesToSpecificShard() throws Exception { + // Index a doc with a dynamic field only to shard 0 + index_specific(0, "id", "700", "name", "shard0_only", "only_on_shard0_s", "present"); + // Index a plain doc to shard 1 (no dynamic field) + index_specific(1, "id", "701", "name", "shard1_only"); + commit(); + + // Query with shards= pointing only at shard 1 — the dynamic field should NOT appear. + // This also tests that a single remote shard is correctly fanned out to rather than + // falling through to local-mode on the coordinating node. + LukeRequest req = new LukeRequest(params("shards", shardsArr[1])); + req.setNumTerms(0); + LukeResponse rsp = req.process(controlClient); + + Map fields = rsp.getFieldInfo(); + assertNotNull("fields should be present", fields); + assertNull( + "only_on_shard0_s should NOT be present when querying only shard 1", + fields.get("only_on_shard0_s")); + assertNotNull("'name' field should still be present", fields.get("name")); + + // Now query with shards= pointing only at shard 0 — the dynamic field SHOULD appear + req = new LukeRequest(params("shards", shardsArr[0])); + req.setNumTerms(0); + rsp = req.process(controlClient); + + fields = rsp.getFieldInfo(); + assertNotNull("fields should be present", fields); + assertNotNull( + "only_on_shard0_s SHOULD be present when querying shard 0", fields.get("only_on_shard0_s")); + } + @Test @ShardsFixed(num = 1) public void testSingleShardViaParamStillDistributes() throws Exception { From 511d664bd088633d4752361840c645798729628b Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Fri, 22 May 2026 10:21:27 -0400 Subject: [PATCH 4/7] further refinements --- .../handler/admin/LukeHandlerCloudTest.java | 16 ------- .../admin/LukeRequestHandlerDistribTest.java | 45 +++++++++++++------ .../solr/BaseDistributedSearchTestCase.java | 7 +++ 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java index a75e384be74..c9ddbabe2bb 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeHandlerCloudTest.java @@ -27,7 +27,6 @@ import org.apache.solr.client.solrj.request.LukeRequest; import org.apache.solr.client.solrj.request.SolrQuery; import org.apache.solr.client.solrj.request.schema.SchemaRequest; -import org.apache.solr.client.solrj.response.LukeResponse; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.SolrException; @@ -73,21 +72,6 @@ private void requestLuke(String collection, SolrParams extra) throws Exception { req.process(cluster.getSolrClient(), collection); } - @Test - public void testLocalMode() throws Exception { - DocCollection docColl = getCollectionState(DISTRIB_COLLECTION); - Slice slice = docColl.getSlices().iterator().next(); - Replica leader = slice.getLeader(); - try (SolrClient client = getHttpSolrClient(leader)) { - LukeRequest req = new LukeRequest(params(DISTRIB, "false")); - req.setNumTerms(0); - LukeResponse rsp = req.process(client); - - assertNotNull("index info should be present", rsp.getIndexInfo()); - assertNull("shards should NOT be present in local mode", rsp.getShardResponses()); - } - } - @Test public void testDistributedShardError() { SolrParams lukeParams = params("id", "0", "show", "schema"); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java index 6f901b20068..2981d27519f 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java @@ -18,6 +18,7 @@ import java.util.Map; import org.apache.solr.BaseDistributedSearchTestCase; +import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.request.LukeRequest; import org.apache.solr.client.solrj.response.LukeResponse; import org.apache.solr.client.solrj.response.QueryResponse; @@ -142,13 +143,15 @@ public void testDistributedAggregateAndFields() throws Exception { LukeResponse detailedRsp = requestLuke(detailedParams); - LukeResponse.FieldInfo detailedNameField = detailedRsp.getFieldInfo().get("name"); - assertNotNull("'name' field should be present", detailedNameField); - assertNull("topTerms should NOT be in top-level fields", detailedNameField.getTopTerms()); - assertEquals("distinct should NOT be in top-level fields", 0, detailedNameField.getDistinct()); + // Top-level fields should NOT have topTerms, distinct, histogram + nameField = detailedRsp.getFieldInfo().get("name"); + assertNotNull("'name' field should be present", nameField); + assertNull("topTerms should NOT be in top-level fields", nameField.getTopTerms()); + assertEquals("distinct should NOT be in top-level fields", 0, nameField.getDistinct()); - Map detailedShardResponses = detailedRsp.getShardResponses(); - assertNotNull("shards section should be present", detailedShardResponses); + // Per-shard entries should have detailed stats + shardResponses = detailedRsp.getShardResponses(); + assertNotNull("shards section should be present", shardResponses); assertLukeXPath( detailedParams, @@ -188,6 +191,22 @@ public void testDistributedAggregateAndFields() throws Exception { "//lst[@name='index']", "//lst[@name='info']"); + // Query a single client without the shards param — local mode + LukeRequest req = new LukeRequest(); + req.setNumTerms(0); + rsp = req.process(controlClient); + + assertNotNull("index info should be present", rsp.getIndexInfo()); + assertNull("shards should NOT be present in local mode", rsp.getShardResponses()); + + // Query a single client with distrib=false — no shards param + req = new LukeRequest(params("distrib", "false")); + req.setNumTerms(0); + rsp = req.process(controlClient); + + assertNotNull("index info should be present", rsp.getIndexInfo()); + assertNull("shards should NOT be present with distrib=false", rsp.getShardResponses()); + // --- Schema view --- ModifiableSolrParams schemaParams = new ModifiableSolrParams(); schemaParams.set("show", "schema"); @@ -206,7 +225,7 @@ public void testDistributedAggregateAndFields() throws Exception { } @Test - @ShardsFixed(num = 12) + @ShardsFixed(num = 4) public void testSparseShardsAndDeferredIndexFlags() throws Exception { // Index a single doc on shard 0 index_specific( @@ -222,7 +241,7 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { Map shardResponses = rsp.getShardResponses(); assertNotNull("shards section should be present", shardResponses); - assertEquals("should have 12 shard entries", 12, shardResponses.size()); + assertEquals("should have 4 shard entries", 4, shardResponses.size()); long sumShardDocs = 0; for (Map.Entry entry : shardResponses.entrySet()) { @@ -257,7 +276,7 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { new ModifiableSolrParams(), "//lst[@name='index']/long[@name='numDocs'][.='1']", "//lst[@name='index']/long[@name='deletedDocs'][.='0']", - "count(//lst[@name='shards']/lst)=12", + "count(//lst[@name='shards']/lst)=4", "//lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", "//lst[@name='fields']/lst[@name='name']/str[@name='schema']", "//lst[@name='fields']/lst[@name='name']/str[@name='index']", @@ -269,7 +288,7 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { // Index docs with the target field across shards, plus anchor docs without it. // Use numeric IDs (the default test schema copies id to integer fields). // Target docs get even IDs starting at 1000, anchor docs get odd IDs. - for (int i = 0; i < 12 * 4; i++) { + for (int i = 0; i < 4 * 4; i++) { index("id", String.valueOf(1000 + i * 2), "flag_target_s", "value_" + i); index("id", String.valueOf(1001 + i * 2), "name", "anchor"); } @@ -278,9 +297,9 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { // Delete all target docs except the first one, using per-shard deletes. // Then optimize to force segment merge — expunges soft-deleted docs so // Terms.getDocCount() (which backs docs) reflects only live docs. - for (int i = 0; i < clients.size(); i++) { - clients.get(i).deleteByQuery("flag_target_s:* AND -id:1000"); - clients.get(i).optimize(); + for (SolrClient client : clients) { + client.deleteByQuery("flag_target_s:* AND -id:1000"); + client.optimize(); } controlClient.deleteByQuery("flag_target_s:* AND -id:1000"); controlClient.optimize(); diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index e72ffaa213c..5249457ecd8 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -93,6 +93,13 @@ * without annotations in that class hierarchy. Ideally this function should be retired in favour of * better annotations. * + *

WARNING each test annotated with @Shards* will spin up its own set of Jetty servers which can + * be a substantial performance hit. Therefore, one should be mindful about the total number of + * independent tests using such annotations. One approach is to pool assertions in a single test to + * minimize jetty server construction overhead. If the test doesn't rely on the comparison features + * of this class, i.e. {@link #query} it may be wise to make it a {@link + * org.apache.solr.cloud.SolrCloudTestCase} instead. + * * @since solr 1.5 */ public abstract class BaseDistributedSearchTestCase extends SolrTestCaseJ4 { From d6d01add1659e991072976feb0da7f83e8a3081e Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Fri, 22 May 2026 10:49:35 -0400 Subject: [PATCH 5/7] rename to minimize diff --- .../admin/LukeRequestHandlerDistribTest.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java index 2981d27519f..12284539acb 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java @@ -137,24 +137,24 @@ public void testDistributedAggregateAndFields() throws Exception { "//lst[@name='fields']/lst[@name='id']/long[@name='docs'][.='20']"); // --- Detailed per-shard stats (topTerms, histogram, distinct) --- - ModifiableSolrParams detailedParams = new ModifiableSolrParams(); - detailedParams.set("fl", "name"); - detailedParams.set("numTerms", "5"); + ModifiableSolrParams params = new ModifiableSolrParams(); + params.set("fl", "name"); + params.set("numTerms", "5"); - LukeResponse detailedRsp = requestLuke(detailedParams); + rsp = requestLuke(params); // Top-level fields should NOT have topTerms, distinct, histogram - nameField = detailedRsp.getFieldInfo().get("name"); + nameField = rsp.getFieldInfo().get("name"); assertNotNull("'name' field should be present", nameField); assertNull("topTerms should NOT be in top-level fields", nameField.getTopTerms()); assertEquals("distinct should NOT be in top-level fields", 0, nameField.getDistinct()); // Per-shard entries should have detailed stats - shardResponses = detailedRsp.getShardResponses(); + shardResponses = rsp.getShardResponses(); assertNotNull("shards section should be present", shardResponses); assertLukeXPath( - detailedParams, + params, "/response/lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", "/response/lst[@name='fields']/lst[@name='name']/long[@name='docs'][.='20']", "not(/response/lst[@name='fields']/lst[@name='name']/lst[@name='topTerms'])", @@ -165,22 +165,22 @@ public void testDistributedAggregateAndFields() throws Exception { "//lst[@name='shards']/lst/lst[@name='fields']/lst[@name='name']/int[@name='distinct']"); // --- Doc lookup not found --- - ModifiableSolrParams notFoundParams = new ModifiableSolrParams(); - notFoundParams.set("id", "999888777"); + params = new ModifiableSolrParams(); + params.set("id", "999888777"); - LukeResponse notFoundRsp = requestLuke(notFoundParams); + rsp = requestLuke(params); - NamedList notFoundRaw = notFoundRsp.getResponse(); - assertNull("doc section should NOT be present for missing ID", notFoundRaw.get("doc")); + NamedList raw = rsp.getResponse(); + assertNull("doc section should NOT be present for missing ID", raw.get("doc")); - assertLukeXPath(notFoundParams, "not(//lst[@name='doc'])"); + assertLukeXPath(params, "not(//lst[@name='doc'])"); // --- Doc lookup found --- - ModifiableSolrParams foundParams = new ModifiableSolrParams(); - foundParams.set("id", "0"); + params = new ModifiableSolrParams(); + params.set("id", "0"); assertLukeXPath( - foundParams, + params, "//lst[@name='doc']/int[@name='docId']", "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='type'][.='string']", "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='value'][.='0']", @@ -208,11 +208,11 @@ public void testDistributedAggregateAndFields() throws Exception { assertNull("shards should NOT be present with distrib=false", rsp.getShardResponses()); // --- Schema view --- - ModifiableSolrParams schemaParams = new ModifiableSolrParams(); - schemaParams.set("show", "schema"); + params = new ModifiableSolrParams(); + params.set("show", "schema"); assertLukeXPath( - schemaParams, + params, "//lst[@name='schema']/lst[@name='fields']/lst[@name='id']/str[@name='type'][.='string']", "//lst[@name='schema']/lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", "//lst[@name='schema']/lst[@name='dynamicFields']/lst[@name='*_s']", From 90c37c1db7cbe8f92ea8e7ecf8f97a089ba0b2bf Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Fri, 22 May 2026 10:58:01 -0400 Subject: [PATCH 6/7] rename to minimize diff --- .../admin/LukeRequestHandlerDistribTest.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java index 12284539acb..935eabc7c1d 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java @@ -164,33 +164,6 @@ public void testDistributedAggregateAndFields() throws Exception { "//lst[@name='shards']/lst/lst[@name='fields']/lst[@name='name']/lst[@name='histogram']/int[@name='1']", "//lst[@name='shards']/lst/lst[@name='fields']/lst[@name='name']/int[@name='distinct']"); - // --- Doc lookup not found --- - params = new ModifiableSolrParams(); - params.set("id", "999888777"); - - rsp = requestLuke(params); - - NamedList raw = rsp.getResponse(); - assertNull("doc section should NOT be present for missing ID", raw.get("doc")); - - assertLukeXPath(params, "not(//lst[@name='doc'])"); - - // --- Doc lookup found --- - params = new ModifiableSolrParams(); - params.set("id", "0"); - - assertLukeXPath( - params, - "//lst[@name='doc']/int[@name='docId']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='type'][.='string']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='value'][.='0']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='type'][.='nametext']", - "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='value'][.='name_0']", - "//lst[@name='doc']/arr[@name='solr']/str[.='0']", - "//lst[@name='doc']/arr[@name='solr']/str[.='name_0']", - "//lst[@name='index']", - "//lst[@name='info']"); - // Query a single client without the shards param — local mode LukeRequest req = new LukeRequest(); req.setNumTerms(0); @@ -222,6 +195,33 @@ public void testDistributedAggregateAndFields() throws Exception { "//lst[@name='schema']/lst[@name='similarity']", "not(/response/lst[@name='fields'])", "count(//lst[@name='shards']/lst)=2"); + + // --- Doc lookup not found --- + params = new ModifiableSolrParams(); + params.set("id", "999888777"); + + rsp = requestLuke(params); + + NamedList raw = rsp.getResponse(); + assertNull("doc section should NOT be present for missing ID", raw.get("doc")); + + assertLukeXPath(params, "not(//lst[@name='doc'])"); + + // --- Doc lookup found --- + params = new ModifiableSolrParams(); + params.set("id", "0"); + + assertLukeXPath( + params, + "//lst[@name='doc']/int[@name='docId']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='type'][.='string']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='id']/str[@name='value'][.='0']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='type'][.='nametext']", + "//lst[@name='doc']/lst[@name='lucene']/lst[@name='name']/str[@name='value'][.='name_0']", + "//lst[@name='doc']/arr[@name='solr']/str[.='0']", + "//lst[@name='doc']/arr[@name='solr']/str[.='name_0']", + "//lst[@name='index']", + "//lst[@name='info']"); } @Test @@ -304,18 +304,18 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { controlClient.deleteByQuery("flag_target_s:* AND -id:1000"); controlClient.optimize(); - ModifiableSolrParams flagParams = new ModifiableSolrParams(); - flagParams.set("fl", "flag_target_s"); + ModifiableSolrParams params = new ModifiableSolrParams(); + params.set("fl", "flag_target_s"); - LukeResponse flagRsp = requestLuke(flagParams); + rsp = requestLuke(params); - Map flagFields = flagRsp.getFieldInfo(); - assertNotNull("fields should be present", flagFields); - LukeResponse.FieldInfo targetField = flagFields.get("flag_target_s"); + fields = rsp.getFieldInfo(); + assertNotNull("fields should be present", fields); + LukeResponse.FieldInfo targetField = fields.get("flag_target_s"); assertNotNull("'flag_target_s' field should be present", targetField); assertLukeXPath( - flagParams, + params, "//lst[@name='fields']/lst[@name='flag_target_s']/str[@name='type'][.='string']", "//lst[@name='fields']/lst[@name='flag_target_s']/str[@name='dynamicBase'][.='*_s']", "//lst[@name='fields']/lst[@name='flag_target_s']/str[@name='index']", From 533c3e86973a6326af11b5c3678d22d64ed74150 Mon Sep 17 00:00:00 2001 From: Luke Kot-Zaniewski Date: Fri, 22 May 2026 11:09:55 -0400 Subject: [PATCH 7/7] remove unnecessary annotation overrides --- .../handler/admin/LukeRequestHandlerDistribTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java index 935eabc7c1d..b727114572f 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerDistribTest.java @@ -83,7 +83,6 @@ private void indexTestData() throws Exception { } @Test - @ShardsFixed(num = 2) public void testDistributedAggregateAndFields() throws Exception { indexTestData(); @@ -241,7 +240,10 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { Map shardResponses = rsp.getShardResponses(); assertNotNull("shards section should be present", shardResponses); - assertEquals("should have 4 shard entries", 4, shardResponses.size()); + assertEquals( + "should have " + getShardCount() + " shard entries", + getShardCount(), + shardResponses.size()); long sumShardDocs = 0; for (Map.Entry entry : shardResponses.entrySet()) { @@ -276,7 +278,7 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { new ModifiableSolrParams(), "//lst[@name='index']/long[@name='numDocs'][.='1']", "//lst[@name='index']/long[@name='deletedDocs'][.='0']", - "count(//lst[@name='shards']/lst)=4", + "count(//lst[@name='shards']/lst)=" + getShardCount(), "//lst[@name='fields']/lst[@name='name']/str[@name='type'][.='nametext']", "//lst[@name='fields']/lst[@name='name']/str[@name='schema']", "//lst[@name='fields']/lst[@name='name']/str[@name='index']", @@ -288,7 +290,7 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { // Index docs with the target field across shards, plus anchor docs without it. // Use numeric IDs (the default test schema copies id to integer fields). // Target docs get even IDs starting at 1000, anchor docs get odd IDs. - for (int i = 0; i < 4 * 4; i++) { + for (int i = 0; i < getShardCount() * 4; i++) { index("id", String.valueOf(1000 + i * 2), "flag_target_s", "value_" + i); index("id", String.valueOf(1001 + i * 2), "name", "anchor"); } @@ -323,7 +325,6 @@ public void testSparseShardsAndDeferredIndexFlags() throws Exception { } @Test - @ShardsFixed(num = 2) public void testDistributedDocLookupDuplicateId() throws Exception { String dupId = "99999"; @@ -360,7 +361,6 @@ public void testDistributedDocLookupDuplicateId() throws Exception { } @Test - @ShardsFixed(num = 2) public void testShardsParamRoutesToSpecificShard() throws Exception { // Index a doc with a dynamic field only to shard 0 index_specific(0, "id", "700", "name", "shard0_only", "only_on_shard0_s", "present");