Skip to content

Commit b0bd718

Browse files
ES|QL: add generative tests for CHANGE_POINT command (#129758)
1 parent 539d8d4 commit b0bd718

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/EsqlQueryGenerator.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import org.elasticsearch.xpack.esql.CsvTestsDataLoader;
1111
import org.elasticsearch.xpack.esql.qa.rest.generative.command.CommandGenerator;
12+
import org.elasticsearch.xpack.esql.qa.rest.generative.command.pipe.ChangePointGenerator;
1213
import org.elasticsearch.xpack.esql.qa.rest.generative.command.pipe.DissectGenerator;
1314
import org.elasticsearch.xpack.esql.qa.rest.generative.command.pipe.DropGenerator;
1415
import org.elasticsearch.xpack.esql.qa.rest.generative.command.pipe.EnrichGenerator;
@@ -49,6 +50,7 @@ public record QueryExecuted(String query, int depth, List<Column> outputSchema,
4950
* These are downstream commands, ie. that cannot appear as the first command in a query
5051
*/
5152
static List<CommandGenerator> PIPE_COMMANDS = List.of(
53+
ChangePointGenerator.INSTANCE,
5254
DissectGenerator.INSTANCE,
5355
DropGenerator.INSTANCE,
5456
EnrichGenerator.INSTANCE,
@@ -197,6 +199,10 @@ public static String randomNumericOrDateField(List<Column> previousOutput) {
197199
return randomName(previousOutput, Set.of("long", "integer", "double", "date"));
198200
}
199201

202+
public static String randomDateField(List<Column> previousOutput) {
203+
return randomName(previousOutput, Set.of("date"));
204+
}
205+
200206
public static String randomNumericField(List<Column> previousOutput) {
201207
return randomName(previousOutput, Set.of("long", "integer", "double"));
202208
}
@@ -255,6 +261,22 @@ public static String constantExpression() {
255261

256262
}
257263

264+
/**
265+
* returns a random identifier or one of the existing names
266+
*/
267+
public static String randomAttributeOrIdentifier(List<EsqlQueryGenerator.Column> previousOutput) {
268+
String name;
269+
if (randomBoolean()) {
270+
name = EsqlQueryGenerator.randomIdentifier();
271+
} else {
272+
name = EsqlQueryGenerator.randomName(previousOutput);
273+
if (name == null) {
274+
name = EsqlQueryGenerator.randomIdentifier();
275+
}
276+
}
277+
return name;
278+
}
279+
258280
public static String randomIdentifier() {
259281
// Let's create identifiers that are long enough to avoid collisions with reserved keywords.
260282
// There could be a smarter way (introspection on the lexer class?), but probably it's not worth the effort

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/GenerativeRestTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.elasticsearch.client.Request;
1111
import org.elasticsearch.client.ResponseException;
1212
import org.elasticsearch.test.rest.ESRestTestCase;
13+
import org.elasticsearch.xpack.esql.AssertWarnings;
1314
import org.elasticsearch.xpack.esql.CsvTestsDataLoader;
1415
import org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase;
1516
import org.elasticsearch.xpack.esql.qa.rest.generative.command.CommandGenerator;
@@ -44,10 +45,6 @@ public abstract class GenerativeRestTest extends ESRestTestCase {
4445
"The field names are too complex to process", // field_caps problem
4546
"must be \\[any type except counter types\\]", // TODO refine the generation of count()
4647

47-
// warnings
48-
"Field '.*' shadowed by field at line .*",
49-
"evaluation of \\[.*\\] failed, treating result as null", // TODO investigate?
50-
5148
// Awaiting fixes for query failure
5249
"Unknown column \\[<all-fields-projected>\\]", // https://github.com/elastic/elasticsearch/issues/121741,
5350
"Plan \\[ProjectExec\\[\\[<no-fields>.* optimized incorrectly due to missing references", // https://github.com/elastic/elasticsearch/issues/125866
@@ -99,10 +96,10 @@ public void test() throws IOException {
9996
EsqlQueryGenerator.QueryExecuted result = execute(command, 0);
10097
if (result.exception() != null) {
10198
checkException(result);
102-
break;
99+
continue;
103100
}
104101
if (checkResults(List.of(), commandGenerator, desc, null, result).success() == false) {
105-
break;
102+
continue;
106103
}
107104
previousResult = result;
108105
previousCommands.add(desc);
@@ -168,7 +165,11 @@ private void checkException(EsqlQueryGenerator.QueryExecuted query) {
168165
@SuppressWarnings("unchecked")
169166
private EsqlQueryGenerator.QueryExecuted execute(String command, int depth) {
170167
try {
171-
Map<String, Object> a = RestEsqlTestCase.runEsqlSync(new RestEsqlTestCase.RequestObjectBuilder().query(command).build());
168+
Map<String, Object> a = RestEsqlTestCase.runEsql(
169+
new RestEsqlTestCase.RequestObjectBuilder().query(command).build(),
170+
new AssertWarnings.AllowedRegexes(List.of(Pattern.compile(".*"))),// we don't care about warnings
171+
RestEsqlTestCase.Mode.SYNC
172+
);
172173
List<EsqlQueryGenerator.Column> outputSchema = outputSchema(a);
173174
List<List<Object>> values = (List<List<Object>>) a.get("values");
174175
return new EsqlQueryGenerator.QueryExecuted(command, depth, outputSchema, values, null);

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/command/CommandGenerator.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,22 @@ static ValidationResult expectSameColumns(List<EsqlQueryGenerator.Column> previo
139139

140140
return VALIDATION_OK;
141141
}
142+
143+
/**
144+
* The command doesn't have to produce LESS columns than the previous query
145+
*/
146+
static ValidationResult expectAtLeastSameNumberOfColumns(
147+
List<EsqlQueryGenerator.Column> previousColumns,
148+
List<EsqlQueryGenerator.Column> columns
149+
) {
150+
if (previousColumns.stream().anyMatch(x -> x.name().contains("<all-fields-projected>"))) {
151+
return VALIDATION_OK; // known bug
152+
}
153+
154+
if (previousColumns.size() > columns.size()) {
155+
return new ValidationResult(false, "Expecting at least [" + previousColumns.size() + "] columns, got [" + columns.size() + "]");
156+
}
157+
158+
return VALIDATION_OK;
159+
}
142160
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.esql.qa.rest.generative.command.pipe;
9+
10+
import org.elasticsearch.xpack.esql.qa.rest.generative.EsqlQueryGenerator;
11+
import org.elasticsearch.xpack.esql.qa.rest.generative.command.CommandGenerator;
12+
13+
import java.util.List;
14+
import java.util.Map;
15+
16+
import static org.elasticsearch.xpack.esql.qa.rest.generative.EsqlQueryGenerator.randomAttributeOrIdentifier;
17+
18+
public class ChangePointGenerator implements CommandGenerator {
19+
public static final String CHANGE_POINT = "change_point";
20+
public static final CommandGenerator INSTANCE = new ChangePointGenerator();
21+
22+
@Override
23+
public CommandDescription generate(
24+
List<CommandDescription> previousCommands,
25+
List<EsqlQueryGenerator.Column> previousOutput,
26+
QuerySchema schema
27+
) {
28+
String timestampField = EsqlQueryGenerator.randomDateField(previousOutput);
29+
String numericField = EsqlQueryGenerator.randomNumericField(previousOutput);
30+
if (timestampField == null || numericField == null) {
31+
return EMPTY_DESCRIPTION;
32+
}
33+
String alias1 = randomAttributeOrIdentifier(previousOutput);
34+
String alias2 = randomAttributeOrIdentifier(previousOutput);
35+
while (alias1.equals(alias2)) {
36+
alias2 = randomAttributeOrIdentifier(previousOutput);
37+
}
38+
39+
String cmd = " | CHANGE_POINT " + numericField + " ON " + timestampField + " AS " + alias1 + ", " + alias2;
40+
41+
return new CommandDescription(CHANGE_POINT, this, cmd, Map.of());
42+
}
43+
44+
@Override
45+
public ValidationResult validateOutput(
46+
List<CommandDescription> previousCommands,
47+
CommandDescription command,
48+
List<EsqlQueryGenerator.Column> previousColumns,
49+
List<List<Object>> previousOutput,
50+
List<EsqlQueryGenerator.Column> columns,
51+
List<List<Object>> output
52+
) {
53+
return CommandGenerator.expectAtLeastSameNumberOfColumns(previousColumns, columns);
54+
}
55+
}

0 commit comments

Comments
 (0)