-
Notifications
You must be signed in to change notification settings - Fork 804
SOLR-18068: POC of keeping existing assertQ logic with solrTestRule #4042
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7ac0d89
f33120e
f704ece
f6a3f42
7f244b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,16 +17,41 @@ | |
| package org.apache.solr.spelling.suggest; | ||
|
|
||
| import org.apache.solr.SolrTestCaseJ4; | ||
| import org.apache.solr.SolrTestCaseJ4Test; | ||
| import org.apache.solr.SolrXPathTestCase; | ||
| import org.apache.solr.client.solrj.SolrClient; | ||
| import org.apache.solr.common.params.SpellingParams; | ||
| import org.apache.solr.util.EmbeddedSolrServerTestRule; | ||
| import org.junit.BeforeClass; | ||
| import org.junit.ClassRule; | ||
|
|
||
| public class TestPhraseSuggestions extends SolrTestCaseJ4 { | ||
| public class TestPhraseSuggestions extends SolrXPathTestCase { | ||
| static final String URI = "/suggest_wfst"; | ||
|
|
||
| @ClassRule | ||
| public static final EmbeddedSolrServerTestRule solrTestRule = new EmbeddedSolrServerTestRule(); | ||
|
|
||
| @BeforeClass | ||
| public static void beforeClass() throws Exception { | ||
| initCore("solrconfig-phrasesuggest.xml", "schema-phrasesuggest.xml"); | ||
| assertQ(req("qt", URI, "q", "", SpellingParams.SPELLCHECK_BUILD, "true")); | ||
| // This was part of the SolrTestCaseJ4.setupTestCases method and appears to be needed. Ugh. | ||
| // Is this a direction we want, this randomness and need in SolrTestCase? | ||
| SolrTestCaseJ4Test.newRandomConfig(); | ||
|
Comment on lines
+36
to
+38
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, this is a struggle/tension. On one hand, I want to see SolrTestCase leaner; not a kitchen sink that STCJ4 became. And I want it to be usable by 3rd parties as well. Including logic for newRandomConfig definitely fails the latter. Maybe we need another subclass for first-party. Or have STC be able to recognize 1P from 3P and be graceful... which I think for some specific methods it may (and/or STCJ4 does. TEST_HOME() is one). Or maybe put certain functionality as static methods in other classes, like TEST_HOME() -- like a 1PTest.TEST_Home(). |
||
|
|
||
| solrTestRule.startSolr(SolrTestCaseJ4.TEST_HOME()); | ||
| solrTestRule | ||
| .newCollection() | ||
| .withConfigSet("../collection1") | ||
| .withConfigFile("conf/solrconfig-phrasesuggest.xml") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is that leading "conf" really necessary? I hope not. Something to improve... the presence of those intermediate conf directories is a sad accident of history. |
||
| .withSchemaFile("conf/schema-phrasesuggest.xml") | ||
| .create(); | ||
|
|
||
| assertQ( | ||
| solrTestRule.getSolrClient(), | ||
| req("qt", URI, "q", "", SpellingParams.SPELLCHECK_BUILD, "true")); | ||
| } | ||
|
|
||
| public SolrClient getSolrClient() { | ||
| return solrTestRule.getSolrClient(); | ||
| } | ||
|
|
||
| public void test() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,277 @@ | ||
| /* | ||
| * 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; | ||
|
|
||
| import java.io.StringWriter; | ||
| import org.apache.solr.client.solrj.SolrClient; | ||
| import org.apache.solr.client.solrj.SolrRequest; | ||
| import org.apache.solr.client.solrj.request.GenericSolrRequest; | ||
| import org.apache.solr.client.solrj.request.QueryRequest; | ||
| import org.apache.solr.client.solrj.response.InputStreamResponseParser; | ||
| import org.apache.solr.client.solrj.response.QueryResponse; | ||
| import org.apache.solr.client.solrj.response.XMLResponseParser; | ||
| 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; | ||
|
|
||
| /** | ||
| * Helper methods for using assertQ/req style testing with SolrClient-based tests. This bridges the | ||
| * gap between TestHarness-style tests and EmbeddedSolrServerTestRule tests. | ||
| * | ||
| * <p>Example usage: | ||
| * | ||
| * <pre> | ||
| * import static org.apache.solr.SolrClientTestHelpers.assertQClient; | ||
| * import static org.apache.solr.SolrClientTestHelpers.params; | ||
| * | ||
| * SolrClient client = solrTestRule.getSolrClient(); | ||
| * assertQClient(client, params("q", "*:*"), | ||
| * "/response/lst[@name='responseHeader']/int[@name='status'][.='0']"); | ||
| * </pre> | ||
| */ | ||
| public class SolrClientTestHelpers { | ||
|
|
||
| /** | ||
| * Creates a SolrParams object from key-value pairs, similar to req() in SolrTestCaseJ4. Use this | ||
| * with assertQClient(SolrClient, SolrParams, xpaths...). | ||
| * | ||
| * @param params alternating key-value pairs for query parameters | ||
| * @return SolrParams object | ||
| */ | ||
| public static SolrParams params(String... params) { | ||
| ModifiableSolrParams solrParams = new ModifiableSolrParams(); | ||
| for (int i = 0; i < params.length; i += 2) { | ||
| if (i + 1 < params.length) { | ||
| solrParams.add(params[i], params[i + 1]); | ||
| } | ||
| } | ||
| return solrParams; | ||
| } | ||
|
|
||
| public static GenericSolrRequest req3(String... q) { | ||
| ModifiableSolrParams params = new ModifiableSolrParams(); | ||
|
|
||
| if (q.length == 1) { | ||
| params.set("q", q); | ||
| } | ||
|
|
||
| params.set("wt", "xml"); | ||
|
|
||
| params.set("indent", params.get("indent", "off")); | ||
|
|
||
| var req = | ||
| new GenericSolrRequest( | ||
| SolrRequest.METHOD.GET, "/select", params // .add("indent", "true") | ||
| ); | ||
| // Using the "smart" solr parsers won't work, because they decode into Solr objects. | ||
| // When trying to re-write into JSON, the JSONWriter doesn't have the right info to print it | ||
| // correctly. | ||
| // All we want to do is pass the JSON response to the user, so do that. | ||
| req.setResponseParser(new XMLResponseParser()); | ||
|
|
||
| return req; | ||
| } | ||
|
|
||
| public static QueryRequest req2(String... q) { | ||
| ModifiableSolrParams params = new ModifiableSolrParams(); | ||
|
|
||
| if (q.length == 1) { | ||
| params.set("q", q); | ||
| } | ||
| if (q.length % 2 != 0) { | ||
| throw new RuntimeException( | ||
| "The length of the string array (query arguments) needs to be even"); | ||
| } | ||
| for (int i = 0; i < q.length; i += 2) { | ||
| params.set(q[i], q[i + 1]); | ||
| } | ||
|
|
||
| params.set("wt", "xml"); | ||
| params.set("indent", params.get("indent", "off")); | ||
|
|
||
| QueryRequest req = new QueryRequest(params); | ||
| String path = params.get("qt"); | ||
| if (path != null) { | ||
| req.setPath(path); | ||
| } | ||
| req.setResponseParser(new InputStreamResponseParser("xml")); | ||
| return req; | ||
| } | ||
|
|
||
| public static ModifiableSolrParams params2(String... params) { | ||
| if (params.length % 2 != 0) throw new RuntimeException("Params length should be even"); | ||
| ModifiableSolrParams msp = new ModifiableSolrParams(); | ||
| for (int i = 0; i < params.length; i += 2) { | ||
| msp.add(params[i], params[i + 1]); | ||
| } | ||
| return msp; | ||
| } | ||
|
|
||
| /** | ||
| * Executes a query using SolrClient with a custom message and validates against XPath | ||
| * expressions. | ||
| * | ||
| * @param message custom error message prefix | ||
| * @param client the SolrClient to execute the query against | ||
| * @param params the query parameters | ||
| * @param tests XPath expressions to validate against the response | ||
| */ | ||
| public static void assertQ2( | ||
| String message, SolrClient client, SolrParams params, String... tests) { | ||
| try { | ||
| // Ensure we request XML format for XPath validation | ||
| ModifiableSolrParams xmlParams = new ModifiableSolrParams(params); | ||
| if (xmlParams.get("wt") == null) { | ||
| xmlParams.set("wt", "xml"); | ||
| } | ||
| xmlParams.set("indent", xmlParams.get("indent", "off")); | ||
|
|
||
| // Execute the query | ||
| QueryRequest req = new QueryRequest(xmlParams); | ||
| QueryResponse rsp = req.process(client); | ||
|
|
||
| // Convert response to XML for XPath validation | ||
| String xml = toXML(rsp.getResponse()); | ||
|
|
||
| // Validate using BaseTestHarness XPath validation | ||
| String results = BaseTestHarness.validateXPath(xml, tests); | ||
|
|
||
| if (results != null) { | ||
| String msg = | ||
| (message == null ? "" : message + " ") | ||
| + "REQUEST FAILED: xpath=" | ||
| + results | ||
| + "\n\txml response was: " | ||
| + xml | ||
| + "\n\trequest was: " | ||
| + params; | ||
| throw new RuntimeException(msg); | ||
| } | ||
| } catch (RuntimeException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| throw new RuntimeException("Error executing query with SolrClient: " + params, e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Converts a NamedList response to XML string for XPath validation. This recreates the XML format | ||
| * that Solr produces. | ||
| * | ||
| * @param response the NamedList response to convert | ||
| * @return XML string representation | ||
| */ | ||
| private static String toXML(NamedList<Object> response) { | ||
| try { | ||
| StringWriter writer = new StringWriter(); | ||
|
|
||
| // Write XML manually since we don't have a full XMLWriter context | ||
| writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||
| writeNamedListAsXML(writer, response, "response", 0); | ||
|
|
||
| return writer.toString(); | ||
| } catch (Exception e) { | ||
| throw new RuntimeException("Error converting response to XML", e); | ||
| } | ||
| } | ||
|
|
||
| /** Recursively writes a NamedList as XML. */ | ||
| private static void writeNamedListAsXML( | ||
| StringWriter writer, NamedList<?> nl, String rootName, int indent) { | ||
| String indentStr = " ".repeat(indent); | ||
|
|
||
| if (rootName != null) { | ||
| writer.write(indentStr + "<" + rootName + ">\n"); | ||
| } | ||
|
|
||
| for (int i = 0; i < nl.size(); i++) { | ||
| String name = nl.getName(i); | ||
| Object value = nl.getVal(i); | ||
| writeValueAsXML(writer, name, value, indent + 1); | ||
| } | ||
|
|
||
| if (rootName != null) { | ||
| writer.write(indentStr + "</" + rootName + ">\n"); | ||
| } | ||
| } | ||
|
|
||
| /** Writes a value as XML. */ | ||
| private static void writeValueAsXML(StringWriter writer, String name, Object value, int indent) { | ||
| String indentStr = " ".repeat(indent); | ||
|
|
||
| if (value == null) { | ||
| writer.write(indentStr + "<null name=\"" + xmlEscape(name) + "\"/>\n"); | ||
| } else if (value instanceof NamedList) { | ||
| writer.write(indentStr + "<lst name=\"" + xmlEscape(name) + "\">\n"); | ||
| NamedList<?> nl = (NamedList<?>) value; | ||
| for (int i = 0; i < nl.size(); i++) { | ||
| writeValueAsXML(writer, nl.getName(i), nl.getVal(i), indent + 1); | ||
| } | ||
| writer.write(indentStr + "</lst>\n"); | ||
| } else if (value instanceof Iterable) { | ||
| writer.write(indentStr + "<arr name=\"" + xmlEscape(name) + "\">\n"); | ||
| for (Object item : (Iterable<?>) value) { | ||
| writeValueAsXML(writer, null, item, indent + 1); | ||
| } | ||
| writer.write(indentStr + "</arr>\n"); | ||
| } else { | ||
| String type = getXMLType(value); | ||
| if (name != null) { | ||
| writer.write( | ||
| indentStr | ||
| + "<" | ||
| + type | ||
| + " name=\"" | ||
| + xmlEscape(name) | ||
| + "\">" | ||
| + xmlEscape(String.valueOf(value)) | ||
| + "</" | ||
| + type | ||
| + ">\n"); | ||
| } else { | ||
| writer.write( | ||
| indentStr + "<" + type + ">" + xmlEscape(String.valueOf(value)) + "</" + type + ">\n"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** Determines XML type tag for a value. */ | ||
| private static String getXMLType(Object value) { | ||
| if (value instanceof Integer || value instanceof Long) { | ||
| return "int"; | ||
| } else if (value instanceof Float || value instanceof Double) { | ||
| return "float"; | ||
| } else if (value instanceof Boolean) { | ||
| return "bool"; | ||
| } else if (value instanceof java.util.Date) { | ||
| return "date"; | ||
| } else { | ||
| return "str"; | ||
| } | ||
| } | ||
|
|
||
| /** Escapes XML special characters. */ | ||
| private static String xmlEscape(String str) { | ||
| if (str == null) return ""; | ||
| return str.replace("&", "&") | ||
| .replace("<", "<") | ||
| .replace(">", ">") | ||
| .replace("\"", """) | ||
| .replace("'", "'"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to move the assertQ into the public void test() because appparently this is static and assertQ and req couldn't be called from a static method.