diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/patterntext/PatternTextLicenseDowngradeIT.java b/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/patterntext/PatternTextLicenseDowngradeIT.java index e6f8ec12075d7..04f4f4847d4a0 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/patterntext/PatternTextLicenseDowngradeIT.java +++ b/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/patterntext/PatternTextLicenseDowngradeIT.java @@ -7,14 +7,23 @@ package org.elasticsearch.xpack.logsdb.patterntext; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.test.rest.ObjectPath; import org.elasticsearch.xpack.logsdb.DataStreamLicenseChangeTestCase; import org.junit.Before; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; public class PatternTextLicenseDowngradeIT extends DataStreamLicenseChangeTestCase { @@ -54,9 +63,25 @@ public void testLicenseDowngrade() throws IOException { assertThat(patternFieldMapping, not(hasKey("disable_templating"))); } + // Index docs while on trial license (templating enabled) + List preDowngradeDocs = List.of("foo 123 bar", "baz 456 qux", "hello 789 world"); + indexDocs(dataStreamName, preDowngradeDocs); + + // Verify search returns pre-downgrade docs + assertSearchReturnsValues(preDowngradeDocs); + startBasic(); - rolloverDataStream(client(), dataStreamName); + // Index docs after downgrade into the same backing index (still has disable_templating=false) + List postDowngradeDocs = List.of("post downgrade 111", "another post 222"); + indexDocs(dataStreamName, postDowngradeDocs); + + // Verify search returns all docs from before and after downgrade + List allDocsBeforeRollover = new ArrayList<>(preDowngradeDocs); + allDocsBeforeRollover.addAll(postDowngradeDocs); + assertSearchReturnsValues(allDocsBeforeRollover); + + // Original backing index settings remain unchanged after downgrade { assertEquals("false", getSetting(client(), backingIndex0, "index.mapping.pattern_text.disable_templating")); Map mapping = getMapping(client(), backingIndex0); @@ -66,6 +91,8 @@ public void testLicenseDowngrade() throws IOException { assertThat(patternFieldMapping, not(hasKey("disable_templating"))); } + rolloverDataStream(client(), dataStreamName); + String backingIndex1 = getDataStreamBackingIndex(client(), dataStreamName, 1); { assertEquals("true", getSetting(client(), backingIndex1, "index.mapping.pattern_text.disable_templating")); @@ -76,5 +103,76 @@ public void testLicenseDowngrade() throws IOException { assertThat(patternFieldMapping, hasEntry("disable_templating", true)); } + // Index docs into the new (downgraded) backing index + List postRolloverDocs = List.of("rolled over 333", "new index 444", "downgraded 555"); + indexDocs(dataStreamName, postRolloverDocs); + + // Verify search across all backing indices returns every doc + List allDocs = new ArrayList<>(allDocsBeforeRollover); + allDocs.addAll(postRolloverDocs); + assertSearchReturnsValues(allDocs); + + // Fetch pattern_field via the "fields" parameter, which exercises the valueFetcher() code path. + // With disabled templating, valueFetcher must load values from binary doc values or stored fields + // rather than from pattern_text template+args doc values. + assertFieldsFetchReturnsValues(allDocs); + } + + private static void indexDocs(String dataStreamName, List values) throws IOException { + StringBuilder sb = new StringBuilder(); + for (String value : values) { + sb.append("{\"create\": {}}\n"); + sb.append("{\"pattern_field\": \"").append(value).append("\"}\n"); + } + assertOK(bulkIndex(client(), dataStreamName, sb::toString)); + } + + @SuppressWarnings("unchecked") + private static void assertSearchReturnsValues(List expectedValues) throws IOException { + Request searchRequest = new Request("GET", "/" + "logs-test-pattern-text" + "/_search"); + searchRequest.setJsonEntity(""" + { + "query": {"match_all": {}}, + "size": 100 + } + """); + Response response = client().performRequest(searchRequest); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + ObjectPath objectPath = ObjectPath.createFromResponse(response); + + assertThat(objectPath.evaluate("hits.total.value"), equalTo(expectedValues.size())); + List> hits = objectPath.evaluate("hits.hits"); + Set actual = new HashSet<>(); + for (Map hit : hits) { + Map source = (Map) hit.get("_source"); + actual.add((String) source.get("pattern_field")); + } + assertEquals(new HashSet<>(expectedValues), actual); + } + + @SuppressWarnings("unchecked") + private static void assertFieldsFetchReturnsValues(List expectedValues) throws IOException { + Request searchRequest = new Request("GET", "/" + "logs-test-pattern-text" + "/_search"); + searchRequest.setJsonEntity(""" + { + "query": {"match_all": {}}, + "fields": ["pattern_field"], + "size": 100 + } + """); + Response response = client().performRequest(searchRequest); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + ObjectPath objectPath = ObjectPath.createFromResponse(response); + + assertThat(objectPath.evaluate("hits.total.value"), equalTo(expectedValues.size())); + List> hits = objectPath.evaluate("hits.hits"); + Set actual = new HashSet<>(); + for (Map hit : hits) { + Map fields = (Map) hit.get("fields"); + List values = (List) fields.get("pattern_field"); + assertThat(values.size(), equalTo(1)); + actual.add(values.get(0)); + } + assertEquals(new HashSet<>(expectedValues), actual); } }