1515import org .elasticsearch .client .ResponseException ;
1616import org .elasticsearch .client .RestClient ;
1717import org .elasticsearch .common .CheckedBiConsumer ;
18+ import org .elasticsearch .common .TriConsumer ;
1819import org .elasticsearch .common .UUIDs ;
1920import org .elasticsearch .common .settings .Settings ;
2021import org .elasticsearch .core .CheckedConsumer ;
3738import java .nio .charset .StandardCharsets ;
3839import java .util .ArrayList ;
3940import java .util .Base64 ;
41+ import java .util .Collection ;
4042import java .util .Collections ;
4143import java .util .List ;
4244import java .util .Map ;
@@ -1098,17 +1100,51 @@ public void testSearchesAgainstNonMatchingIndices() throws Exception {
10981100 {
10991101 String q = "FROM employees,my_remote_cluster:employees_nomatch" ;
11001102
1103+ CheckedBiConsumer <Response , Boolean , Exception > verifier = (resp , limit0 ) -> {
1104+ assertOK (resp );
1105+ Map <String , Object > map = responseAsMap (resp );
1106+ assertThat (((List <?>) map .get ("columns" )).size (), greaterThanOrEqualTo (1 ));
1107+ assertThat (((List <?>) map .get ("values" )).size (), limit0 ? equalTo (0 ) : greaterThanOrEqualTo (1 ));
1108+ assertExpectedClustersForMissingIndicesTests (
1109+ map ,
1110+ List .of (
1111+ new ExpectedCluster ("(local)" , "employees" , "successful" , limit0 ? 0 : null ),
1112+ new ExpectedCluster (
1113+ REMOTE_CLUSTER_ALIAS ,
1114+ "employees_nomatch" ,
1115+ "skipped" ,
1116+ 0 ,
1117+ new ExpectedFailure ("verification_exception" , List .of ("Unknown index" , "my_remote_cluster:employees_nomatch" ))
1118+ )
1119+ )
1120+ );
1121+ };
1122+
11011123 Request limit1 = esqlRequest (q + " | LIMIT 1" );
1102- ResponseException e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUser (limit1 ));
1103- assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1104- e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUserViaAPIKey (limit1 , remoteSearchUserAPIKey ));
1105- assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1124+ if (skipUnavailable == false ) {
1125+ ResponseException e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUser (limit1 ));
1126+ assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1127+ e = expectThrows (
1128+ ResponseException .class ,
1129+ () -> performRequestWithRemoteSearchUserViaAPIKey (limit1 , remoteSearchUserAPIKey )
1130+ );
1131+ assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1132+ } else {
1133+ verifier .accept (performRequestWithRemoteSearchUser (limit1 ), false );
1134+ }
11061135
11071136 Request limit0 = esqlRequest (q + " | LIMIT 0" );
1108- e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUser (limit0 ));
1109- assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1110- e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUserViaAPIKey (limit0 , remoteSearchUserAPIKey ));
1111- assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1137+ if (skipUnavailable == false ) {
1138+ ResponseException e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUser (limit0 ));
1139+ assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1140+ e = expectThrows (
1141+ ResponseException .class ,
1142+ () -> performRequestWithRemoteSearchUserViaAPIKey (limit0 , remoteSearchUserAPIKey )
1143+ );
1144+ assertThat (e .getMessage (), containsString ("Unknown index [my_remote_cluster:employees_nomatch]" ));
1145+ } else {
1146+ verifier .accept (performRequestWithRemoteSearchUser (limit0 ), true );
1147+ }
11121148 }
11131149
11141150 // since there is at least one matching index in the query, the missing wildcarded local index is not an error
@@ -1189,7 +1225,8 @@ public void testSearchesAgainstNonMatchingIndices() throws Exception {
11891225 String q = "FROM employees,my_remote_cluster:employees_nomatch,my_remote_cluster:employees*" ;
11901226
11911227 Request limit1 = esqlRequest (q + " | LIMIT 1" );
1192- ResponseException e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUser (limit1 ));
1228+ Request limit0 = esqlRequest (q + " | LIMIT 0" );
1229+
11931230 /* Example error:
11941231 *{"error":{"root_cause":[{"type":"security_exception","reason":"action [indices:data/read/esql/cluster] towards
11951232 * remote cluster is unauthorized for user [remote_search_user] with assigned roles [remote_search] authenticated by
@@ -1199,11 +1236,7 @@ public void testSearchesAgainstNonMatchingIndices() throws Exception {
11991236 * by API key id [zaeMK5MBeGk5jCIiFtqB] of user [test_user] on indices [employees_nomatch], this action is granted by the
12001237 * index privileges [read,all]"},"status":403}"
12011238 */
1202- assertThat (e .getMessage (), containsString ("unauthorized for user [remote_search_user]" ));
1203- assertThat (e .getMessage (), containsString ("on indices [employees_nomatch]" ));
1204- assertThat (e .getMessage (), containsString ("security_exception" ));
1205-
1206- e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUserViaAPIKey (limit1 , remoteSearchUserAPIKey ));
1239+ var userErrors = List .of ("unauthorized for user [remote_search_user]" , "of user [test_user]" , "on indices [employees_nomatch]" );
12071240 /* Example error:
12081241 * {"error":{"root_cause":[{"type":"security_exception","reason":"action [indices:data/read/esql/cluster] towards
12091242 * remote cluster is unauthorized for API key id [sxuSK5MBSfGSGj4YFLyv] of user [remote_search_user] authenticated by
@@ -1213,15 +1246,66 @@ public void testSearchesAgainstNonMatchingIndices() throws Exception {
12131246 * by API key id [cUiRK5MB5j18U5stsvQj] of user [test_user] on indices [employees_nomatch], this action is granted by the
12141247 * index privileges [read,all]"},"status":403}"
12151248 */
1216- assertThat (e .getMessage (), containsString ("unauthorized for API key id" ));
1217- assertThat (e .getMessage (), containsString ("of user [remote_search_user]" ));
1218- assertThat (e .getMessage (), containsString ("on indices [employees_nomatch]" ));
1219- assertThat (e .getMessage (), containsString ("security_exception" ));
1220-
1221- // TODO: in follow on PR, add support for throwing a VerificationException for this scenario - no exception is currently thrown
1222- // Request limit0 = esqlRequest(q + " | LIMIT 0");
1223- // e = expectThrows(ResponseException.class, () -> performRequestWithRemoteSearchUser(limit0));
1224- // assertThat(e.getMessage(), containsString("Unknown index [my_remote_cluster:employees_nomatch]"));
1249+ var keyErrors = List .of ("unauthorized for API key id" , "of user [remote_search_user]" , "on indices [employees_nomatch]" );
1250+
1251+ if (skipUnavailable == false ) {
1252+ ResponseException e = expectThrows (ResponseException .class , () -> performRequestWithRemoteSearchUser (limit1 ));
1253+ assertThat (e .getMessage (), containsString ("security_exception" ));
1254+ for (String error : userErrors ) {
1255+ assertThat (e .getMessage (), containsString (error ));
1256+ }
1257+
1258+ e = expectThrows (
1259+ ResponseException .class ,
1260+ () -> performRequestWithRemoteSearchUserViaAPIKey (limit1 , remoteSearchUserAPIKey )
1261+ );
1262+ assertThat (e .getMessage (), containsString ("security_exception" ));
1263+ for (String error : keyErrors ) {
1264+ assertThat (e .getMessage (), containsString (error ));
1265+ }
1266+
1267+ // TODO: in follow on PR, add support for throwing a VerificationException for this scenario - no exception is currently
1268+ // thrown
1269+ // Request limit0 = esqlRequest(q + " | LIMIT 0");
1270+ // e = expectThrows(ResponseException.class, () -> performRequestWithRemoteSearchUser(limit0));
1271+ // assertThat(e.getMessage(), containsString("Unknown index [my_remote_cluster:employees_nomatch]"));
1272+ } else {
1273+ TriConsumer <Response , Boolean , Collection <String >> verifier = (response , isLimit0 , errors ) -> {
1274+ assertOK (response );
1275+ Map <String , Object > map ;
1276+ try {
1277+ map = responseAsMap (response );
1278+ } catch (Exception e ) {
1279+ throw new RuntimeException (e );
1280+ }
1281+ assertThat (((List <?>) map .get ("columns" )).size (), greaterThanOrEqualTo (1 ));
1282+ if (isLimit0 ) {
1283+ assertThat (((List <?>) map .get ("values" )).size (), equalTo (0 ));
1284+ } else {
1285+ assertThat (((List <?>) map .get ("values" )).size (), greaterThanOrEqualTo (1 ));
1286+ }
1287+ assertExpectedClustersForMissingIndicesTests (
1288+ map ,
1289+ List .of (
1290+ new ExpectedCluster ("(local)" , "employees" , "successful" , isLimit0 ? 0 : null ),
1291+ // FIXME: this actually should produce SUCCESS since "employees" exists, but not implemented yet
1292+ new ExpectedCluster (
1293+ REMOTE_CLUSTER_ALIAS ,
1294+ "employees_nomatch,employees*" ,
1295+ // TODO: LIMIT 0 produces "successful" here since no runtime check is performed. This is probably wrong.
1296+ isLimit0 ? "successful" : "skipped" ,
1297+ 0 ,
1298+ isLimit0 ? null : new ExpectedFailure ("security_exception" , errors )
1299+ )
1300+ )
1301+ );
1302+ };
1303+ verifier .apply (performRequestWithRemoteSearchUser (limit1 ), false , userErrors );
1304+ verifier .apply (performRequestWithRemoteSearchUser (limit0 ), true , userErrors );
1305+ verifier .apply (performRequestWithRemoteSearchUserViaAPIKey (limit1 , remoteSearchUserAPIKey ), false , keyErrors );
1306+ verifier .apply (performRequestWithRemoteSearchUserViaAPIKey (limit0 , remoteSearchUserAPIKey ), true , keyErrors );
1307+ }
1308+
12251309 }
12261310 }
12271311
@@ -1527,7 +1611,13 @@ private void assertWithEnrich(Response response) throws IOException {
15271611 assertThat (flatList , containsInAnyOrder (2 , 3 , "usa" , "canada" ));
15281612 }
15291613
1530- record ExpectedCluster (String clusterAlias , String indexExpression , String status , Integer totalShards ) {}
1614+ record ExpectedFailure (String type , Collection <String > messages ) {}
1615+
1616+ record ExpectedCluster (String clusterAlias , String indexExpression , String status , Integer totalShards , ExpectedFailure failure ) {
1617+ ExpectedCluster (String clusterAlias , String indexExpression , String status , Integer totalShards ) {
1618+ this (clusterAlias , indexExpression , status , totalShards , null );
1619+ }
1620+ }
15311621
15321622 @ SuppressWarnings ("unchecked" )
15331623 void assertExpectedClustersForMissingIndicesTests (Map <String , Object > responseMap , List <ExpectedCluster > expected ) {
@@ -1566,10 +1656,12 @@ void assertExpectedClustersForMissingIndicesTests(Map<String, Object> responseMa
15661656 assertThat (failures .size (), is (1 ));
15671657 Map <String , ?> failure1 = (Map <String , ?>) failures .get (0 );
15681658 Map <String , ?> innerReason = (Map <String , ?>) failure1 .get ("reason" );
1569- String expectedMsg = "Unknown index [" + expectedCluster .indexExpression () + "]" ;
1570- assertThat (innerReason .get ("reason" ).toString (), containsString (expectedMsg ));
1571- assertThat (innerReason .get ("type" ).toString (), containsString ("verification_exception" ));
1572-
1659+ if (expectedCluster .failure () != null ) {
1660+ for (var f : expectedCluster .failure ().messages ()) {
1661+ assertThat (innerReason .get ("reason" ).toString (), containsString (f ));
1662+ }
1663+ assertThat (innerReason .get ("type" ).toString (), containsString (expectedCluster .failure ().type ()));
1664+ }
15731665 } else {
15741666 fail (msg + "; Unexpected status: " + expectedCluster .status ());
15751667 }
0 commit comments