Skip to content

Commit 76eb16e

Browse files
authored
Borrow more tests for runtime fields (#61113)
This "borrows" 150 more tests from core for runtime fields, extending the work done in #60931. More precisely, it adds a template to every test that forces dynamic mapping updates to build runtime fields where possible. In particular, `long` and `double` field are created as runtime fields. `string`-typed fields are mimick the out of the box behavior and create a top level `text` field with a `.keyword` multi-field, but this `keyword` multi-field executes a script and loads from source.
1 parent f3b65eb commit 76eb16e

File tree

5 files changed

+101
-21
lines changed

5 files changed

+101
-21
lines changed

test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSection.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ public static ClientYamlTestSection parse(XContentParser parser) throws IOExcept
6060
private final SkipSection skipSection;
6161
private final List<ExecutableSection> executableSections;
6262

63-
ClientYamlTestSection(XContentLocation location, String name, SkipSection skipSection, List<ExecutableSection> executableSections) {
63+
public ClientYamlTestSection(
64+
XContentLocation location,
65+
String name,
66+
SkipSection skipSection,
67+
List<ExecutableSection> executableSections
68+
) {
6469
this.location = location;
6570
this.name = name;
6671
this.skipSection = Objects.requireNonNull(skipSection, "skip section cannot be null");

test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSuite.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public static ClientYamlTestSuite parse(String api, String suiteName, XContentPa
111111
private final TeardownSection teardownSection;
112112
private final List<ClientYamlTestSection> testSections;
113113

114-
ClientYamlTestSuite(String api, String name, SetupSection setupSection, TeardownSection teardownSection,
114+
public ClientYamlTestSuite(String api, String name, SetupSection setupSection, TeardownSection teardownSection,
115115
List<ClientYamlTestSection> testSections) {
116116
this.api = api;
117117
this.name = name;

test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/SetupSection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public static SetupSection parse(XContentParser parser) throws IOException {
7070
private final SkipSection skipSection;
7171
private final List<ExecutableSection> executableSections;
7272

73-
SetupSection(SkipSection skipSection, List<ExecutableSection> executableSections) {
73+
public SetupSection(SkipSection skipSection, List<ExecutableSection> executableSections) {
7474
this.skipSection = Objects.requireNonNull(skipSection, "skip section cannot be null");
7575
this.executableSections = Collections.unmodifiableList(executableSections);
7676
}

x-pack/plugin/runtime-fields/qa/rest/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ yamlRestTest {
3030
'search/115_multiple_field_collapsing/two levels fields collapsing', // Broken. Gotta fix.
3131
'field_caps/30_filter/Field caps with index filter', // We don't support filtering field caps on runtime fields. What should we do?
3232
'search.aggregation/10_histogram/*', // runtime_script doesn't support sub-fields. Maybe it should?
33+
'search/10_source_filtering/docvalue_fields with explicit format',
34+
'search.aggregation/200_top_hits_metric/top_hits aggregation with sequence numbers',
35+
'search.aggregation/200_top_hits_metric/top_hits aggregation with nested documents',
3336
/////// TO FIX ///////
3437

3538
/////// NOT SUPPORTED ///////

x-pack/plugin/runtime-fields/qa/rest/src/yamlRestTest/java/org/elasticsearch/xpack/runtimefields/rest/CoreTestsWithRuntimeFieldsIT.java

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,29 @@
99
import com.carrotsearch.randomizedtesting.annotations.Name;
1010
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
1111

12+
import org.elasticsearch.common.xcontent.XContentLocation;
1213
import org.elasticsearch.index.mapper.IpFieldMapper;
1314
import org.elasticsearch.index.mapper.KeywordFieldMapper;
1415
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
1516
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
17+
import org.elasticsearch.test.rest.yaml.ClientYamlTestExecutionContext;
18+
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
1619
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
20+
import org.elasticsearch.test.rest.yaml.section.ClientYamlTestSection;
21+
import org.elasticsearch.test.rest.yaml.section.ClientYamlTestSuite;
1722
import org.elasticsearch.test.rest.yaml.section.DoSection;
1823
import org.elasticsearch.test.rest.yaml.section.ExecutableSection;
24+
import org.elasticsearch.test.rest.yaml.section.SetupSection;
1925

26+
import java.io.IOException;
2027
import java.util.ArrayList;
2128
import java.util.HashMap;
2229
import java.util.List;
2330
import java.util.Map;
2431
import java.util.Objects;
2532

33+
import static org.hamcrest.Matchers.equalTo;
34+
2635
public class CoreTestsWithRuntimeFieldsIT extends ESClientYamlSuiteTestCase {
2736
public CoreTestsWithRuntimeFieldsIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
2837
super(testCandidate);
@@ -37,34 +46,49 @@ public CoreTestsWithRuntimeFieldsIT(@Name("yaml") ClientYamlTestCandidate testCa
3746
*/
3847
@ParametersFactory
3948
public static Iterable<Object[]> parameters() throws Exception {
40-
/*
41-
* Map of "setup"s that we've seen - from path to whether or
42-
* not we the setup was modified to include a runtime_script
43-
*/
44-
Map<String, Boolean> seenSetups = new HashMap<>();
49+
Map<String, ClientYamlTestSuite> suites = new HashMap<>();
4550
List<Object[]> result = new ArrayList<>();
4651
for (Object[] orig : ESClientYamlSuiteTestCase.createParameters()) {
4752
assert orig.length == 1;
4853
ClientYamlTestCandidate candidate = (ClientYamlTestCandidate) orig[0];
49-
boolean modifiedSetup = seenSetups.computeIfAbsent(
50-
candidate.getName(),
51-
k -> modifySection(candidate.getSuitePath() + "/setup", candidate.getSetupSection().getExecutableSections())
54+
ClientYamlTestSuite suite = suites.computeIfAbsent(candidate.getTestPath(), k -> modifiedSuite(candidate));
55+
modifySection(candidate.getTestPath(), candidate.getTestSection().getExecutableSections());
56+
ClientYamlTestSection modified = new ClientYamlTestSection(
57+
candidate.getTestSection().getLocation(),
58+
candidate.getTestSection().getName(),
59+
candidate.getTestSection().getSkipSection(),
60+
candidate.getTestSection().getExecutableSections()
5261
);
53-
boolean modifiedTest = modifySection(candidate.getTestPath(), candidate.getTestSection().getExecutableSections());
54-
if (modifiedSetup || modifiedTest) {
55-
result.add(new Object[] { candidate });
56-
}
62+
result.add(new Object[] { new ClientYamlTestCandidate(suite, modified) });
5763
}
5864
return result;
5965
}
6066

6167
/**
62-
* Replace property configuration in {@code indices.create} with scripts
68+
* Modify the setup section to setup a dynamic template that replaces
69+
* field configurations with scripts that load from source
70+
* <strong>and</strong> replaces field configurations in {@code incides.create}
71+
* with scripts that load from source.
72+
*/
73+
private static ClientYamlTestSuite modifiedSuite(ClientYamlTestCandidate candidate) {
74+
modifySection(candidate.getSuitePath() + "/setup", candidate.getSetupSection().getExecutableSections());
75+
List<ExecutableSection> setup = new ArrayList<>(candidate.getSetupSection().getExecutableSections().size() + 1);
76+
setup.add(ADD_TEMPLATE);
77+
setup.addAll(candidate.getSetupSection().getExecutableSections());
78+
return new ClientYamlTestSuite(
79+
candidate.getApi(),
80+
candidate.getName(),
81+
new SetupSection(candidate.getSetupSection().getSkipSection(), setup),
82+
candidate.getTeardownSection(),
83+
List.of()
84+
);
85+
}
86+
87+
/**
88+
* Replace field configuration in {@code indices.create} with scripts
6389
* that load from the source.
64-
* @return {@code true} if any fields were rewritten into runtime_scripts, {@code false} otherwise.
6590
*/
66-
private static boolean modifySection(String sectionName, List<ExecutableSection> executables) {
67-
boolean include = false;
91+
private static void modifySection(String sectionName, List<ExecutableSection> executables) {
6892
for (ExecutableSection section : executables) {
6993
if (false == (section instanceof DoSection)) {
7094
continue;
@@ -129,11 +153,9 @@ private static boolean modifySection(String sectionName, List<ExecutableSection>
129153
propertyMap.remove("store");
130154
propertyMap.remove("index");
131155
propertyMap.remove("doc_values");
132-
include = true;
133156
}
134157
}
135158
}
136-
return include;
137159
}
138160

139161
private static String painlessToLoadFromSource(String name, String type) {
@@ -173,4 +195,54 @@ private static String painlessToLoadFromSource(String name, String type) {
173195
)
174196
);
175197

198+
private static final ExecutableSection ADD_TEMPLATE = new ExecutableSection() {
199+
@Override
200+
public XContentLocation getLocation() {
201+
return new XContentLocation(-1, -1);
202+
}
203+
204+
@Override
205+
public void execute(ClientYamlTestExecutionContext executionContext) throws IOException {
206+
Map<String, String> params = Map.of("name", "convert_to_source_only", "create", "true");
207+
List<Map<String, Object>> dynamicTemplates = new ArrayList<>();
208+
for (String type : PAINLESS_TO_EMIT.keySet()) {
209+
if (type.equals("ip")) {
210+
// There isn't a dynamic template to pick up ips. They'll just look like strings.
211+
continue;
212+
}
213+
Map<String, Object> mapping = Map.ofEntries(
214+
Map.entry("type", "runtime_script"),
215+
Map.entry("runtime_type", type),
216+
Map.entry("script", painlessToLoadFromSource("{name}", type))
217+
);
218+
Map<String, Object> body = Map.ofEntries(
219+
Map.entry("match_mapping_type", type.equals("keyword") ? "string" : type),
220+
Map.entry("mapping", mapping)
221+
);
222+
if (type.contentEquals("keyword")) {
223+
/*
224+
* For "string"-type dynamic mappings emulate our default
225+
* behavior with a top level text field and a `.keyword`
226+
* multi-field. But instead of the default, use a runtime
227+
* field for the multi-field.
228+
*/
229+
mapping = Map.of("type", "text", "fields", Map.of("keyword", mapping));
230+
dynamicTemplates.add(Map.of(type, Map.of("match_mapping_type", "string", "mapping", mapping)));
231+
} else {
232+
dynamicTemplates.add(Map.of(type, Map.of("match_mapping_type", type, "mapping", mapping)));
233+
}
234+
dynamicTemplates.add(Map.of(type, body));
235+
}
236+
List<Map<String, Object>> bodies = List.of(
237+
Map.ofEntries(
238+
Map.entry("index_patterns", "*"),
239+
Map.entry("priority", Integer.MAX_VALUE),
240+
Map.entry("template", Map.of("settings", Map.of(), "mappings", Map.of("dynamic_templates", dynamicTemplates)))
241+
)
242+
);
243+
ClientYamlTestResponse response = executionContext.callApi("indices.put_index_template", params, bodies, Map.of());
244+
assertThat(response.getStatusCode(), equalTo(200));
245+
// There are probably some warning about overlapping templates. Ignore them.
246+
}
247+
};
176248
}

0 commit comments

Comments
 (0)