diff --git a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/RequestIndexFilteringIT.java b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/RequestIndexFilteringIT.java index 4b192b6aef948..c3e29602a8b8c 100644 --- a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/RequestIndexFilteringIT.java +++ b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/RequestIndexFilteringIT.java @@ -38,6 +38,7 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; @ThreadLeakFilters(filters = TestClustersThreadFilter.class) public class RequestIndexFilteringIT extends RequestIndexFilteringTestCase { @@ -91,6 +92,12 @@ protected String from(String... indexName) { @Override public Map runEsql(RestEsqlTestCase.RequestObjectBuilder requestObject) throws IOException { + return runEsql(requestObject, true); + } + + @Override + public Map runEsql(RestEsqlTestCase.RequestObjectBuilder requestObject, boolean checkPartialResults) + throws IOException { if (requestObject.allowPartialResults() != null) { assumeTrue( "require allow_partial_results on local cluster", @@ -98,7 +105,7 @@ public Map runEsql(RestEsqlTestCase.RequestObjectBuilder request ); } requestObject.includeCCSMetadata(true); - return super.runEsql(requestObject); + return super.runEsql(requestObject, checkPartialResults); } @After @@ -154,7 +161,30 @@ public void testIndicesDontExistRemote() throws IOException { indexTimestampData(docsTest1, "test1", "2024-11-26", "id1"); Map result = runEsql( - timestampFilter("gte", "2020-01-01").query("FROM *:foo,*:test1 METADATA _index | SORT id1 | KEEP _index, id*") + timestampFilter("gte", "2020-01-01").query("FROM *:foo,*:test1 METADATA _index | SORT id1 | KEEP _index, id*"), + false + ); + + // `foo` index doesn't exist, so the request will currently be successful, but with partial results + var isPartial = result.get("is_partial"); + assertThat(isPartial, is(true)); + assertThat( + result, + matchesMap().entry( + "_clusters", + matchesMap().entry( + "details", + matchesMap().entry( + "remote_cluster", + matchesMap().entry( + "failures", + matchesList().item( + matchesMap().entry("reason", matchesMap().entry("reason", "no such index [foo]").extraOk()).extraOk() + ) + ).extraOk() + ).extraOk() + ).extraOk() + ).extraOk() ); @SuppressWarnings("unchecked") var columns = (List>) result.get("columns"); diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java index 7df7eff0d189b..221aa17ee1979 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java @@ -276,9 +276,7 @@ protected final void doTest(String query) throws Throwable { Map prevTooks = supportsTook() ? tooks() : null; Map answer = runEsql(builder.query(query), testCase.assertWarnings(deduplicateExactWarnings())); - var clusters = answer.get("_clusters"); - var reason = "unexpected partial results" + (clusters != null ? ": _clusters=" + clusters : ""); - assertThat(reason, answer.get("is_partial"), anyOf(nullValue(), is(false))); + assertNotPartial(answer); var expectedColumnsWithValues = loadCsvSpecValues(testCase.expectedResults); @@ -303,6 +301,14 @@ protected final void doTest(String query) throws Throwable { } } + static Map assertNotPartial(Map answer) { + var clusters = answer.get("_clusters"); + var reason = "unexpected partial results" + (clusters != null ? ": _clusters=" + clusters : ""); + assertThat(reason, answer.get("is_partial"), anyOf(nullValue(), is(false))); + + return answer; + } + private Map tooks() throws IOException { Request request = new Request("GET", "/_xpack/usage"); HttpEntity entity = client().performRequest(request).getEntity(); diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java index 1cb2a6a526191..1ba4365ea3e92 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java @@ -249,6 +249,11 @@ public Map runEsql(RestEsqlTestCase.RequestObjectBuilder request return RestEsqlTestCase.runEsql(requestObject, new AssertWarnings.NoWarnings(), RestEsqlTestCase.Mode.SYNC); } + public Map runEsql(RestEsqlTestCase.RequestObjectBuilder requestObject, boolean checkPartialResults) + throws IOException { + return RestEsqlTestCase.runEsql(requestObject, new AssertWarnings.NoWarnings(), RestEsqlTestCase.Mode.SYNC, checkPartialResults); + } + protected void indexTimestampData(int docs, String indexName, String date, String differentiatorFieldName) throws IOException { indexTimestampDataForClient(client(), docs, indexName, date, differentiatorFieldName); } diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java index 0c29d7a711257..5e85c20b026ab 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java @@ -60,6 +60,7 @@ import static org.elasticsearch.test.MapMatcher.assertMap; import static org.elasticsearch.test.MapMatcher.matchesMap; import static org.elasticsearch.xpack.esql.EsqlTestUtils.as; +import static org.elasticsearch.xpack.esql.qa.rest.EsqlSpecTestCase.assertNotPartial; import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.Mode.ASYNC; import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.Mode.SYNC; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.dateTimeToString; @@ -1248,13 +1249,21 @@ public static Map runEsqlAsync(RequestObjectBuilder requestObjec return runEsqlAsync(requestObject, randomBoolean(), new AssertWarnings.NoWarnings()); } + public static Map runEsql( + RequestObjectBuilder requestObject, + AssertWarnings assertWarnings, + Mode mode, + boolean checkPartialResults + ) throws IOException { + var results = mode == ASYNC + ? runEsqlAsync(requestObject, randomBoolean(), assertWarnings) + : runEsqlSync(requestObject, assertWarnings); + return checkPartialResults ? assertNotPartial(results) : results; + } + public static Map runEsql(RequestObjectBuilder requestObject, AssertWarnings assertWarnings, Mode mode) throws IOException { - if (mode == ASYNC) { - return runEsqlAsync(requestObject, randomBoolean(), assertWarnings); - } else { - return runEsqlSync(requestObject, assertWarnings); - } + return runEsql(requestObject, assertWarnings, mode, true); } public static Map runEsqlSync(RequestObjectBuilder requestObject, AssertWarnings assertWarnings) throws IOException {