From 31d45b3c956d1fbca9d4c7b68255bb0db7649b49 Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Thu, 12 Mar 2020 10:58:07 -0400 Subject: [PATCH] EQL: Improve query folder test suite (#53187) (#53476) Related to https://github.com/elastic/elasticsearch/issues/52775 --- .../planner/AbstractQueryFolderTestCase.java | 38 +++++++ .../eql/planner/QueryFolderFailTests.java | 25 +++++ .../xpack/eql/planner/QueryFolderOkTests.java | 105 ++++++++++++++++++ .../xpack/eql/planner/QueryFolderTests.java | 56 ---------- 4 files changed, 168 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/AbstractQueryFolderTestCase.java create mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java create mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderOkTests.java delete mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderTests.java diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/AbstractQueryFolderTestCase.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/AbstractQueryFolderTestCase.java new file mode 100644 index 00000000000..7c54e31db79 --- /dev/null +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/AbstractQueryFolderTestCase.java @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.planner; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.eql.analysis.Analyzer; +import org.elasticsearch.xpack.eql.analysis.PreAnalyzer; +import org.elasticsearch.xpack.eql.analysis.Verifier; +import org.elasticsearch.xpack.eql.expression.function.EqlFunctionRegistry; +import org.elasticsearch.xpack.eql.optimizer.Optimizer; +import org.elasticsearch.xpack.eql.parser.EqlParser; +import org.elasticsearch.xpack.eql.plan.physical.PhysicalPlan; +import org.elasticsearch.xpack.ql.index.EsIndex; +import org.elasticsearch.xpack.ql.index.IndexResolution; + +import static org.elasticsearch.xpack.ql.type.TypesTests.loadMapping; + +public abstract class AbstractQueryFolderTestCase extends ESTestCase { + protected EqlParser parser = new EqlParser(); + protected PreAnalyzer preAnalyzer = new PreAnalyzer(); + protected Analyzer analyzer = new Analyzer(new EqlFunctionRegistry(), new Verifier()); + protected Optimizer optimizer = new Optimizer(); + protected Planner planner = new Planner(); + + protected IndexResolution index = IndexResolution.valid(new EsIndex("test", loadMapping("mapping-default.json"))); + + protected PhysicalPlan plan(IndexResolution resolution, String eql) { + return planner.plan(optimizer.optimize(analyzer.analyze(preAnalyzer.preAnalyze(parser.createStatement(eql), resolution)))); + } + + protected PhysicalPlan plan(String eql) { + return plan(index, eql); + } +} diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java new file mode 100644 index 00000000000..471ee4c5694 --- /dev/null +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.planner; + +import org.elasticsearch.xpack.ql.QlIllegalArgumentException; + +public class QueryFolderFailTests extends AbstractQueryFolderTestCase { + public void testPropertyEquationFilterUnsupported() { + QlIllegalArgumentException e = expectThrows(QlIllegalArgumentException.class, + () -> plan("process where (serial_event_id<9 and serial_event_id >= 7) or (opcode == pid)")); + String msg = e.getMessage(); + assertEquals("Line 1:74: Comparisons against variables are not (currently) supported; offender [pid] in [==]", msg); + } + + public void testPropertyEquationInClauseFilterUnsupported() { + QlIllegalArgumentException e = expectThrows(QlIllegalArgumentException.class, + () -> plan("process where opcode in (1,3) and process_name in (parent_process_name, \"SYSTEM\")")); + String msg = e.getMessage(); + assertEquals("Line 1:52: Comparisons against variables are not (currently) supported; offender [parent_process_name] in [==]", msg); + } +} diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderOkTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderOkTests.java new file mode 100644 index 00000000000..acbe328a474 --- /dev/null +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderOkTests.java @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.planner; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.elasticsearch.xpack.eql.plan.physical.EsQueryExec; +import org.elasticsearch.xpack.eql.plan.physical.PhysicalPlan; + +import java.util.Arrays; + +import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; +import static org.hamcrest.Matchers.containsString; + +public class QueryFolderOkTests extends AbstractQueryFolderTestCase { + private static Object[][] specs = { + {"basic", "process where true", null}, + {"singleNumericFilterEquals", "process where serial_event_id = 1", "\"term\":{\"serial_event_id\":{\"value\":1"}, + {"singleNumericFilterLess", "process where serial_event_id < 4", + "\"range\":{\"serial_event_id\":{\"from\":null,\"to\":4,\"include_lower\":false,\"include_upper\":false" + }, + {"singleNumericFilterLessSymmetry", "process where 4 > serial_event_id", + "\"range\":{\"serial_event_id\":{\"from\":null,\"to\":4,\"include_lower\":false,\"include_upper\":false" + }, + {"singleNumericFilterLessEquals", "process where serial_event_id <= 4", + "\"range\":{\"serial_event_id\":{\"from\":null,\"to\":4,\"include_lower\":false,\"include_upper\":true" + }, + {"singleNumericFilterGreater", "process where serial_event_id > 4", + "\"range\":{\"serial_event_id\":{\"from\":4,\"to\":null,\"include_lower\":false,\"include_upper\":false" + }, + {"singleNumericFilterGreaterEquals", "process where serial_event_id >= 4", + "\"range\":{\"serial_event_id\":{\"from\":4,\"to\":null,\"include_lower\":true,\"include_upper\":false" + }, + {"mixedTypeFilter", "process where process_name == \"notepad.exe\" or (serial_event_id < 4.5 and serial_event_id >= 3.1)", + new Object[]{ + "\"term\":{\"process_name\":{\"value\":\"notepad.exe\"", + "\"range\":{\"serial_event_id\":{\"from\":3.1,\"to\":4.5,\"include_lower\":true,\"include_upper\":false" + } + }, + {"notFilter", "process where not (exit_code > -1)", + "\"range\":{\"exit_code\":{\"from\":null,\"to\":-1,\"include_lower\":false,\"include_upper\":true" + }, + {"inFilter", "process where process_name in (\"python.exe\", \"SMSS.exe\", \"explorer.exe\")", + new Object[]{ + "\"term\":{\"process_name\":{\"value\":\"python.exe\"", + "\"term\":{\"process_name\":{\"value\":\"SMSS.exe\"", + "\"term\":{\"process_name\":{\"value\":\"explorer.exe\"", + } + }, + {"equalsAndInFilter", "process where process_path == \"*\\\\red_ttp\\\\wininit.*\" and opcode in (0,1,2,3)", + new Object[]{ + "\"wildcard\":{\"process_path\":{\"wildcard\":\"*\\\\\\\\red_ttp\\\\\\\\wininit.*\"", + "\"term\":{\"opcode\":{\"value\":0", + "\"term\":{\"opcode\":{\"value\":1", + "\"term\":{\"opcode\":{\"value\":2", + "\"term\":{\"opcode\":{\"value\":3", + } + }, + }; + + private final String name; + private final String query; + private final Object expect; + + public QueryFolderOkTests(String name, String query, Object expect) { + this.name = name; + this.query = query; + this.expect = expect; + } + + @ParametersFactory(shuffle = false, argumentFormatting = "%1$s.test") + public static Iterable parameters() { + return Arrays.asList(specs); + } + + public void test() { + PhysicalPlan p = plan(query); + assertEquals(EsQueryExec.class, p.getClass()); + EsQueryExec eqe = (EsQueryExec) p; + assertEquals(23, eqe.output().size()); + assertEquals(KEYWORD, eqe.output().get(0).dataType()); + + final String query = eqe.queryContainer().toString().replaceAll("\\s+", ""); + + // test query term + if (expect != null) { + if (expect instanceof Object[]) { + for (Object item : (Object[]) expect) { + assertThat(query, containsString((String) item)); + } + } else { + assertThat(query, containsString((String) expect)); + } + } + + // test common term + assertThat(query, containsString("\"term\":{\"event.category\":{\"value\":\"process\"")); + + // test field source extraction + assertThat(query, containsString("\"_source\":{\"includes\":[],\"excludes\":[]")); + } +} diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderTests.java deleted file mode 100644 index 1c0664ee7ac..00000000000 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderTests.java +++ /dev/null @@ -1,56 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.eql.planner; - -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.eql.analysis.Analyzer; -import org.elasticsearch.xpack.eql.analysis.PreAnalyzer; -import org.elasticsearch.xpack.eql.analysis.Verifier; -import org.elasticsearch.xpack.eql.expression.function.EqlFunctionRegistry; -import org.elasticsearch.xpack.eql.optimizer.Optimizer; -import org.elasticsearch.xpack.eql.parser.EqlParser; -import org.elasticsearch.xpack.eql.plan.physical.EsQueryExec; -import org.elasticsearch.xpack.eql.plan.physical.PhysicalPlan; -import org.elasticsearch.xpack.ql.index.EsIndex; -import org.elasticsearch.xpack.ql.index.IndexResolution; - -import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; -import static org.elasticsearch.xpack.ql.type.TypesTests.loadMapping; -import static org.hamcrest.Matchers.containsString; - -public class QueryFolderTests extends ESTestCase { - - private EqlParser parser = new EqlParser(); - private PreAnalyzer preAnalyzer = new PreAnalyzer(); - private Analyzer analyzer = new Analyzer(new EqlFunctionRegistry(), new Verifier()); - private Optimizer optimizer = new Optimizer(); - private Planner planner = new Planner(); - - private IndexResolution index = IndexResolution.valid(new EsIndex("test", loadMapping("mapping-default.json"))); - - - private PhysicalPlan plan(IndexResolution resolution, String eql) { - return planner.plan(optimizer.optimize(analyzer.analyze(preAnalyzer.preAnalyze(parser.createStatement(eql), resolution)))); - } - - private PhysicalPlan plan(String eql) { - return plan(index, eql); - } - - public void testBasicPlan() { - PhysicalPlan p = plan("process where true"); - assertEquals(EsQueryExec.class, p.getClass()); - EsQueryExec eqe = (EsQueryExec) p; - assertEquals(23, eqe.output().size()); - assertEquals(KEYWORD, eqe.output().get(0).dataType()); - String query = eqe.queryContainer().toString().replaceAll("\\s+", ""); - // test query term - assertThat(query, containsString("\"term\":{\"event.category\":{\"value\":\"process\"")); - // test field source extraction - assertThat(query, containsString("\"_source\":{\"includes\":[],\"excludes\":[]")); - } -}