Ver código fonte

[TEST] Cover custom sorting and routing in randomized testing (#120584) (#120932)

* Cover custom sorting and routing in randomized testing

* [CI] Auto commit changes from spotless

* fix reindex tests

* fix reindex tests

* refactor classes

* comment

* more refactoring

* more refactoring

* restore tests with static mappings

* reduce diff

* reduce diff

* Restore single-element array removal in synthetic source

* Revert "Restore single-element array removal in synthetic source"

This reverts commit e8e99e1c662214c4f14e1037f37815e0666d322b.

* [CI] Auto commit changes from spotless

---------

Co-authored-by: elasticsearchmachine <infra-root+elasticsearchmachine@elastic.co>
Kostas Krikellas 8 meses atrás
pai
commit
dc124dcbd5

+ 6 - 35
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/AbstractChallengeRestTest.java

@@ -14,7 +14,6 @@ import org.elasticsearch.common.CheckedSupplier;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.core.CheckedConsumer;
-import org.elasticsearch.core.Tuple;
 import org.elasticsearch.rest.RestStatus;
 import org.elasticsearch.search.builder.SearchSourceBuilder;
 import org.elasticsearch.test.cluster.ElasticsearchCluster;
@@ -221,44 +220,16 @@ public abstract class AbstractChallengeRestTest extends ESRestTestCase {
 
     public abstract void contenderMappings(XContentBuilder builder) throws IOException;
 
-    public void baselineSettings(Settings.Builder builder) {}
+    public abstract void baselineSettings(Settings.Builder builder);
 
-    public void contenderSettings(Settings.Builder builder) {}
+    public abstract void contenderSettings(Settings.Builder builder);
 
     public void commonSettings(Settings.Builder builder) {}
 
-    private Response indexDocuments(
-        final String dataStreamName,
-        final CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier
-    ) throws IOException {
-        final StringBuilder sb = new StringBuilder();
-        int id = 0;
-        for (var document : documentsSupplier.get()) {
-            sb.append(Strings.format("{ \"create\": { \"_id\" : \"%d\" } }", id)).append("\n");
-            sb.append(Strings.toString(document)).append("\n");
-            id++;
-        }
-        var request = new Request("POST", "/" + dataStreamName + "/_bulk");
-        request.setJsonEntity(sb.toString());
-        request.addParameter("refresh", "true");
-        return client.performRequest(request);
-    }
-
-    public Response indexBaselineDocuments(final CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
-        return indexDocuments(getBaselineDataStreamName(), documentsSupplier);
-    }
-
-    public Response indexContenderDocuments(final CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier)
-        throws IOException {
-        return indexDocuments(getContenderDataStreamName(), documentsSupplier);
-    }
-
-    public Tuple<Response, Response> indexDocuments(
-        final CheckedSupplier<List<XContentBuilder>, IOException> baselineSupplier,
-        final CheckedSupplier<List<XContentBuilder>, IOException> contenderSupplier
-    ) throws IOException {
-        return new Tuple<>(indexBaselineDocuments(baselineSupplier), indexContenderDocuments(contenderSupplier));
-    }
+    public abstract void indexDocuments(
+        CheckedSupplier<List<XContentBuilder>, IOException> baselineSupplier,
+        CheckedSupplier<List<XContentBuilder>, IOException> contenderSupplier
+    ) throws IOException;
 
     public Response queryBaseline(final SearchSourceBuilder search) throws IOException {
         return query(search, this::getBaselineDataStreamName);

+ 77 - 0
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/BulkChallengeRestIT.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.logsdb.qa;
+
+import org.elasticsearch.common.CheckedSupplier;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Challenge test that uses bulk indexing for both baseline and contender sides.
+ * We index same documents into an index with standard index mode and an index with logsdb index mode.
+ * Then we verify that results of common operations are the same modulo knows differences like synthetic source
+ * modifications.
+ */
+public class BulkChallengeRestIT extends StandardVersusLogsIndexModeChallengeRestIT {
+
+    public BulkChallengeRestIT() {}
+
+    protected BulkChallengeRestIT(DataGenerationHelper dataGenerationHelper) {
+        super(dataGenerationHelper);
+    }
+
+    @Override
+    public void indexDocuments(
+        final CheckedSupplier<List<XContentBuilder>, IOException> baselineSupplier,
+        final CheckedSupplier<List<XContentBuilder>, IOException> contenderSupplier
+    ) throws IOException {
+        var contenderResponseEntity = indexContenderDocuments(contenderSupplier);
+        indexBaselineDocuments(baselineSupplier, contenderResponseEntity);
+    }
+
+    private Map<String, Object> indexContenderDocuments(final CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier)
+        throws IOException {
+        final StringBuilder sb = new StringBuilder();
+        int id = 0;
+        for (var document : documentsSupplier.get()) {
+            if (autoGenerateId()) {
+                sb.append("{ \"create\": { } }\n");
+            } else {
+                sb.append(Strings.format("{ \"create\": { \"_id\" : \"%d\" } }\n", id));
+            }
+            sb.append(Strings.toString(document)).append("\n");
+            id++;
+        }
+        return performBulkRequest(sb.toString(), false);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void indexBaselineDocuments(
+        final CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier,
+        final Map<String, Object> contenderResponseEntity
+    ) throws IOException {
+        final StringBuilder sb = new StringBuilder();
+        int id = 0;
+        final List<Map<String, Object>> items = (List<Map<String, Object>>) contenderResponseEntity.get("items");
+        for (var document : documentsSupplier.get()) {
+            if (autoGenerateId()) {
+                var contenderId = ((Map<String, Object>) items.get(id).get("create")).get("_id");
+                sb.append(Strings.format("{ \"create\": { \"_id\" : \"%s\" } }\n", contenderId));
+            } else {
+                sb.append(Strings.format("{ \"create\": { \"_id\" : \"%d\" } }\n", id));
+            }
+            sb.append(Strings.toString(document)).append("\n");
+            id++;
+        }
+        performBulkRequest(sb.toString(), true);
+    }
+}

+ 2 - 3
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeRandomDataDynamicMappingChallengeRestIT.java → x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/BulkDynamicMappingChallengeRestIT.java

@@ -9,9 +9,8 @@ package org.elasticsearch.xpack.logsdb.qa;
 
 import org.elasticsearch.common.settings.Settings;
 
-public class StandardVersusLogsIndexModeRandomDataDynamicMappingChallengeRestIT extends
-    StandardVersusLogsIndexModeRandomDataChallengeRestIT {
-    public StandardVersusLogsIndexModeRandomDataDynamicMappingChallengeRestIT() {
+public class BulkDynamicMappingChallengeRestIT extends BulkChallengeRestIT {
+    public BulkDynamicMappingChallengeRestIT() {
         super(new DataGenerationHelper(builder -> builder.withFullyDynamicMapping(true)));
     }
 

+ 121 - 0
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/BulkStaticMappingChallengeRestIT.java

@@ -0,0 +1,121 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.logsdb.qa;
+
+import org.elasticsearch.common.time.DateFormatter;
+import org.elasticsearch.common.time.FormatNames;
+import org.elasticsearch.xcontent.XContentBuilder;
+import org.elasticsearch.xcontent.XContentFactory;
+
+import java.io.IOException;
+import java.time.Instant;
+
+/**
+ * This test uses simple mapping and document structure in order to allow easier debugging of the test itself.
+ */
+public class BulkStaticMappingChallengeRestIT extends BulkChallengeRestIT {
+    public BulkStaticMappingChallengeRestIT() {}
+
+    @Override
+    public void baselineMappings(XContentBuilder builder) throws IOException {
+        if (fullyDynamicMapping == false) {
+            builder.startObject()
+                .startObject("properties")
+
+                .startObject("@timestamp")
+                .field("type", "date")
+                .endObject()
+
+                .startObject("host.name")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .startObject("message")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .startObject("method")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .startObject("memory_usage_bytes")
+                .field("type", "long")
+                .field("ignore_malformed", randomBoolean())
+                .endObject()
+
+                .endObject()
+
+                .endObject();
+        } else {
+            // We want dynamic mapping, but we need host.name to be a keyword instead of text to support aggregations.
+            builder.startObject()
+                .startObject("properties")
+
+                .startObject("host.name")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .endObject()
+                .endObject();
+        }
+    }
+
+    @Override
+    public void contenderMappings(XContentBuilder builder) throws IOException {
+        builder.startObject();
+        builder.field("subobjects", false);
+
+        if (fullyDynamicMapping == false) {
+            builder.startObject("properties")
+
+                .startObject("@timestamp")
+                .field("type", "date")
+                .endObject()
+
+                .startObject("host.name")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .startObject("message")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .startObject("method")
+                .field("type", "keyword")
+                .field("ignore_above", randomIntBetween(1000, 1200))
+                .endObject()
+
+                .startObject("memory_usage_bytes")
+                .field("type", "long")
+                .field("ignore_malformed", randomBoolean())
+                .endObject()
+
+                .endObject();
+        }
+
+        builder.endObject();
+    }
+
+    @Override
+    protected XContentBuilder generateDocument(final Instant timestamp) throws IOException {
+        return XContentFactory.jsonBuilder()
+            .startObject()
+            .field("@timestamp", DateFormatter.forPattern(FormatNames.STRICT_DATE_OPTIONAL_TIME.getName()).format(timestamp))
+            .field("host.name", randomFrom("foo", "bar", "baz"))
+            .field("message", randomFrom("a message", "another message", "still another message", "one more message"))
+            .field("method", randomFrom("put", "post", "get"))
+            .field("memory_usage_bytes", randomLongBetween(1000, 2000))
+            .endObject();
+    }
+}

+ 1 - 1
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsStoredSourceChallengeRestIT.java → x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/BulkStoredSourceChallengeRestIT.java

@@ -13,7 +13,7 @@ import org.elasticsearch.common.settings.Settings;
  * This test compares behavior of a standard mode data stream and a logsdb data stream using stored source.
  * There should be no differences between such two data streams.
  */
-public class StandardVersusLogsStoredSourceChallengeRestIT extends StandardVersusLogsIndexModeRandomDataChallengeRestIT {
+public class BulkStoredSourceChallengeRestIT extends BulkChallengeRestIT {
     @Override
     public void contenderSettings(Settings.Builder builder) {
         super.contenderSettings(builder);

+ 23 - 5
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/ReindexChallengeRestIT.java

@@ -8,8 +8,8 @@
 package org.elasticsearch.xpack.logsdb.qa;
 
 import org.elasticsearch.client.Request;
-import org.elasticsearch.client.Response;
 import org.elasticsearch.common.CheckedSupplier;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.xcontent.XContentBuilder;
 
 import java.io.IOException;
@@ -18,9 +18,29 @@ import java.util.Locale;
 
 import static org.hamcrest.Matchers.equalTo;
 
-public abstract class ReindexChallengeRestIT extends StandardVersusLogsIndexModeRandomDataChallengeRestIT {
+public abstract class ReindexChallengeRestIT extends StandardVersusLogsIndexModeChallengeRestIT {
+
     @Override
-    public Response indexContenderDocuments(CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
+    public void indexDocuments(
+        final CheckedSupplier<List<XContentBuilder>, IOException> baselineSupplier,
+        final CheckedSupplier<List<XContentBuilder>, IOException> contencontenderSupplierderSupplier
+    ) throws IOException {
+        indexBaselineDocuments(baselineSupplier);
+        indexContenderDocuments();
+    }
+
+    private void indexBaselineDocuments(final CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
+        final StringBuilder sb = new StringBuilder();
+        int id = 0;
+        for (var document : documentsSupplier.get()) {
+            sb.append(Strings.format("{ \"create\": { \"_id\" : \"%d\" } }\n", id));
+            sb.append(Strings.toString(document)).append("\n");
+            id++;
+        }
+        performBulkRequest(sb.toString(), true);
+    }
+
+    private void indexContenderDocuments() throws IOException {
         var reindexRequest = new Request("POST", "/_reindex?refresh=true");
         reindexRequest.setJsonEntity(String.format(Locale.ROOT, """
             {
@@ -38,7 +58,5 @@ public abstract class ReindexChallengeRestIT extends StandardVersusLogsIndexMode
 
         var body = entityAsMap(response);
         assertThat("encountered failures when performing reindex:\n " + body, body.get("failures"), equalTo(List.of()));
-
-        return response;
     }
 }

+ 49 - 118
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeChallengeRestIT.java

@@ -11,7 +11,6 @@ import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
 import org.elasticsearch.client.RestClient;
-import org.elasticsearch.common.CheckedSupplier;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.time.DateFormatter;
 import org.elasticsearch.common.time.FormatNames;
@@ -19,7 +18,6 @@ import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.logsdb.datageneration.matchers.MatchResult;
 import org.elasticsearch.logsdb.datageneration.matchers.Matcher;
-import org.elasticsearch.rest.RestStatus;
 import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
 import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
@@ -29,7 +27,6 @@ import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentFactory;
 import org.elasticsearch.xcontent.XContentType;
-import org.hamcrest.Matchers;
 
 import java.io.IOException;
 import java.time.Instant;
@@ -46,103 +43,34 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 
 /**
- * Basic challenge test - we index same documents into an index with standard index mode and an index with logsdb index mode.
- * Then we verify that results of common operations are the same modulo knows differences like synthetic source modifications.
- * This test uses simple mapping and document structure in order to allow easier debugging of the test itself.
+ * Challenge test (see {@link BulkStaticMappingChallengeRestIT}) that uses randomly generated
+ * mapping and documents in order to cover more code paths and permutations.
  */
-public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChallengeRestTest {
+public abstract class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChallengeRestTest {
+    protected final boolean fullyDynamicMapping = randomBoolean();
+    private final boolean useCustomSortConfig = fullyDynamicMapping == false && randomBoolean();
+    private final boolean routeOnSortFields = useCustomSortConfig && randomBoolean();
     private final int numShards = randomBoolean() ? randomIntBetween(2, 4) : 0;
     private final int numReplicas = randomBoolean() ? randomIntBetween(1, 3) : 0;
-    private final boolean fullyDynamicMapping = randomBoolean();
+    protected final DataGenerationHelper dataGenerationHelper;
 
     public StandardVersusLogsIndexModeChallengeRestIT() {
+        this(new DataGenerationHelper());
+    }
+
+    protected StandardVersusLogsIndexModeChallengeRestIT(DataGenerationHelper dataGenerationHelper) {
         super("standard-apache-baseline", "logs-apache-contender", "baseline-template", "contender-template", 101, 101);
+        this.dataGenerationHelper = dataGenerationHelper;
     }
 
     @Override
     public void baselineMappings(XContentBuilder builder) throws IOException {
-        if (fullyDynamicMapping == false) {
-            builder.startObject()
-                .startObject("properties")
-
-                .startObject("@timestamp")
-                .field("type", "date")
-                .endObject()
-
-                .startObject("host.name")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .startObject("message")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .startObject("method")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .startObject("memory_usage_bytes")
-                .field("type", "long")
-                .field("ignore_malformed", randomBoolean())
-                .endObject()
-
-                .endObject()
-
-                .endObject();
-        } else {
-            // We want dynamic mapping, but we need host.name to be a keyword instead of text to support aggregations.
-            builder.startObject()
-                .startObject("properties")
-
-                .startObject("host.name")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .endObject()
-                .endObject();
-        }
+        dataGenerationHelper.standardMapping(builder);
     }
 
     @Override
     public void contenderMappings(XContentBuilder builder) throws IOException {
-        builder.startObject();
-        builder.field("subobjects", false);
-
-        if (fullyDynamicMapping == false) {
-            builder.startObject("properties")
-
-                .startObject("@timestamp")
-                .field("type", "date")
-                .endObject()
-
-                .startObject("host.name")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .startObject("message")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .startObject("method")
-                .field("type", "keyword")
-                .field("ignore_above", randomIntBetween(1000, 1200))
-                .endObject()
-
-                .startObject("memory_usage_bytes")
-                .field("type", "long")
-                .field("ignore_malformed", randomBoolean())
-                .endObject()
-
-                .endObject();
-        }
-
-        builder.endObject();
+        dataGenerationHelper.logsDbMapping(builder);
     }
 
     @Override
@@ -159,6 +87,14 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
     @Override
     public void contenderSettings(Settings.Builder builder) {
         builder.put("index.mode", "logsdb");
+        if (useCustomSortConfig) {
+            builder.putList("index.sort.field", "host.name", "method", "@timestamp");
+            builder.putList("index.sort.order", "asc", "asc", "desc");
+            if (routeOnSortFields) {
+                builder.put("index.logsdb.route_on_sort_fields", true);
+            }
+        }
+        dataGenerationHelper.logsDbSettings(builder);
     }
 
     @Override
@@ -169,6 +105,10 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
         waitForLogs(client());
     }
 
+    protected boolean autoGenerateId() {
+        return routeOnSortFields;
+    }
+
     protected static void waitForLogs(RestClient client) throws Exception {
         assertBusy(() -> {
             try {
@@ -330,28 +270,6 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
         assertTrue(matchResult.getMessage(), matchResult.isMatch());
     }
 
-    @Override
-    public Response indexBaselineDocuments(CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
-        var response = super.indexBaselineDocuments(documentsSupplier);
-
-        assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(RestStatus.OK.getStatus()));
-        var baselineResponseBody = entityAsMap(response);
-        assertThat("errors in baseline bulk response:\n " + baselineResponseBody, baselineResponseBody.get("errors"), equalTo(false));
-
-        return response;
-    }
-
-    @Override
-    public Response indexContenderDocuments(CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
-        var response = super.indexContenderDocuments(documentsSupplier);
-
-        assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(RestStatus.OK.getStatus()));
-        var contenderResponseBody = entityAsMap(response);
-        assertThat("errors in contender bulk response:\n " + contenderResponseBody, contenderResponseBody.get("errors"), equalTo(false));
-
-        return response;
-    }
-
     private List<XContentBuilder> generateDocuments(int numberOfDocuments) throws IOException {
         final List<XContentBuilder> documents = new ArrayList<>();
         // This is static in order to be able to identify documents between test runs.
@@ -364,14 +282,12 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
     }
 
     protected XContentBuilder generateDocument(final Instant timestamp) throws IOException {
-        return XContentFactory.jsonBuilder()
-            .startObject()
-            .field("@timestamp", DateFormatter.forPattern(FormatNames.STRICT_DATE_OPTIONAL_TIME.getName()).format(timestamp))
-            .field("host.name", randomFrom("foo", "bar", "baz"))
-            .field("message", randomFrom("a message", "another message", "still another message", "one more message"))
-            .field("method", randomFrom("put", "post", "get"))
-            .field("memory_usage_bytes", randomLongBetween(1000, 2000))
-            .endObject();
+        var document = XContentFactory.jsonBuilder();
+        dataGenerationHelper.generateDocument(
+            document,
+            Map.of("@timestamp", DateFormatter.forPattern(FormatNames.STRICT_DATE_OPTIONAL_TIME.getName()).format(timestamp))
+        );
+        return document;
     }
 
     @SuppressWarnings("unchecked")
@@ -383,7 +299,7 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
         assertThat(hitsList.size(), greaterThan(0));
 
         return hitsList.stream()
-            .sorted(Comparator.comparingInt((Map<String, Object> hit) -> Integer.parseInt((String) hit.get("_id"))))
+            .sorted(Comparator.comparing((Map<String, Object> hit) -> ((String) hit.get("_id"))))
             .map(hit -> (Map<String, Object>) hit.get("_source"))
             .toList();
     }
@@ -404,7 +320,7 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
 
         // Results contain a list of [source, id] lists.
         return values.stream()
-            .sorted(Comparator.comparingInt((List<Object> value) -> Integer.parseInt((String) value.get(1))))
+            .sorted(Comparator.comparing((List<Object> value) -> ((String) value.get(1))))
             .map(value -> (Map<String, Object>) value.get(0))
             .toList();
     }
@@ -437,4 +353,19 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
     private void indexDocuments(List<XContentBuilder> documents) throws IOException {
         indexDocuments(() -> documents, () -> documents);
     }
+
+    protected final Map<String, Object> performBulkRequest(String json, boolean isBaseline) throws IOException {
+        var request = new Request("POST", "/" + (isBaseline ? getBaselineDataStreamName() : getContenderDataStreamName()) + "/_bulk");
+        request.setJsonEntity(json);
+        request.addParameter("refresh", "true");
+        var response = client.performRequest(request);
+        assertOK(response);
+        var responseBody = entityAsMap(response);
+        assertThat(
+            "errors in " + (isBaseline ? "baseline" : "contender") + " bulk response:\n " + responseBody,
+            responseBody.get("errors"),
+            equalTo(false)
+        );
+        return responseBody;
+    }
 }

+ 0 - 61
x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeRandomDataChallengeRestIT.java

@@ -1,61 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.xpack.logsdb.qa;
-
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.time.DateFormatter;
-import org.elasticsearch.common.time.FormatNames;
-import org.elasticsearch.xcontent.XContentBuilder;
-import org.elasticsearch.xcontent.XContentFactory;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.util.Map;
-
-/**
- * Challenge test (see {@link StandardVersusLogsIndexModeChallengeRestIT}) that uses randomly generated
- * mapping and documents in order to cover more code paths and permutations.
- */
-public class StandardVersusLogsIndexModeRandomDataChallengeRestIT extends StandardVersusLogsIndexModeChallengeRestIT {
-    protected final DataGenerationHelper dataGenerationHelper;
-
-    public StandardVersusLogsIndexModeRandomDataChallengeRestIT() {
-        this(new DataGenerationHelper());
-    }
-
-    protected StandardVersusLogsIndexModeRandomDataChallengeRestIT(DataGenerationHelper dataGenerationHelper) {
-        super();
-        this.dataGenerationHelper = dataGenerationHelper;
-    }
-
-    @Override
-    public void baselineMappings(XContentBuilder builder) throws IOException {
-        dataGenerationHelper.standardMapping(builder);
-    }
-
-    @Override
-    public void contenderMappings(XContentBuilder builder) throws IOException {
-        dataGenerationHelper.logsDbMapping(builder);
-    }
-
-    @Override
-    public void contenderSettings(Settings.Builder builder) {
-        super.contenderSettings(builder);
-        dataGenerationHelper.logsDbSettings(builder);
-    }
-
-    @Override
-    protected XContentBuilder generateDocument(final Instant timestamp) throws IOException {
-        var document = XContentFactory.jsonBuilder();
-        dataGenerationHelper.generateDocument(
-            document,
-            Map.of("@timestamp", DateFormatter.forPattern(FormatNames.STRICT_DATE_OPTIONAL_TIME.getName()).format(timestamp))
-        );
-        return document;
-    }
-}