Browse Source

[Rest Api Compatibility] Add transformation for simple text replaces (#71118)

This commits adds a simple textual replace transformation for Rest Api Compatibility testing.
It allows to replace simple key-value pairs. For instance used in this PR to replace is_true and is_false.
is_true: old_value with is_true: new_value
Przemyslaw Gomulka 4 years ago
parent
commit
e025272b9a

+ 18 - 0
buildSrc/src/integTest/groovy/org/elasticsearch/gradle/YamlRestCompatTestPluginFuncTest.groovy

@@ -210,6 +210,8 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
               task.addAllowedWarning("added allowed warning")
               task.addAllowedWarningRegex("added allowed warning regex .* [0-9]")
               task.removeWarning("one", "warning to remove")
+              task.replaceIsTrue("value_to_replace", "replaced_value")
+              task.replaceIsFalse("value_to_replace", "replaced_value")
             })
             // can't actually spin up test cluster from this test
            tasks.withType(Test).configureEach{ enabled = false }
@@ -229,6 +231,10 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
           - match: { _type: "_foo" }
           - match: { _source.blah: 1234 }
           - match: { _source.junk: true }
+          - is_true: "value_to_replace"
+          - is_false: "value_to_replace"
+          - is_true: "value_not_to_replace"
+          - is_false: "value_not_to_replace"
         ---
         "two":
           - do:
@@ -239,6 +245,10 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
           - match: { _type: "_foo" }
           - match: { _source.blah: 1234 }
           - match: { _source.junk: true }
+          - is_true: "value_to_replace"
+          - is_false: "value_to_replace"
+          - is_true: "value_not_to_replace"
+          - is_false: "value_not_to_replace"
 
         """.stripIndent()
         when:
@@ -288,6 +298,10 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
         - match: {}
         - match:
             _source.junk: true
+        - is_true: "replaced_value"
+        - is_false: "replaced_value"
+        - is_true: "value_not_to_replace"
+        - is_false: "value_not_to_replace"
         - match:
             _source.added:
               name: "jake"
@@ -314,6 +328,10 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
             _type: "_doc"
         - match: {}
         - match: {}
+        - is_true: "replaced_value"
+        - is_false: "replaced_value"
+        - is_true: "value_not_to_replace"
+        - is_false: "value_not_to_replace"
         """.stripIndent()).readAll()
 
         expectedAll.eachWithIndex{ ObjectNode expected, int i ->

+ 27 - 1
buildSrc/src/main/java/org/elasticsearch/gradle/internal/rest/compat/RestCompatTestTransformTask.java

@@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.ObjectReader;
 import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SequenceWriter;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
 import org.elasticsearch.gradle.Version;
@@ -24,6 +25,8 @@ import org.elasticsearch.gradle.test.rest.transform.headers.InjectHeaders;
 import org.elasticsearch.gradle.test.rest.transform.match.AddMatch;
 import org.elasticsearch.gradle.test.rest.transform.match.RemoveMatch;
 import org.elasticsearch.gradle.test.rest.transform.match.ReplaceMatch;
+import org.elasticsearch.gradle.test.rest.transform.text.ReplaceIsFalse;
+import org.elasticsearch.gradle.test.rest.transform.text.ReplaceIsTrue;
 import org.elasticsearch.gradle.test.rest.transform.warnings.InjectAllowedWarnings;
 import org.elasticsearch.gradle.test.rest.transform.warnings.InjectWarnings;
 import org.elasticsearch.gradle.test.rest.transform.warnings.RemoveWarnings;
@@ -92,7 +95,8 @@ public class RestCompatTestTransformTask extends DefaultTask {
     }
 
     /**
-     * Replaces all the values of a match assertion all project REST tests. For example "match":{"_type": "foo"} to "match":{"_type": "bar"}
+     * Replaces all the values of a match assertion for all project REST tests.
+     * For example "match":{"_type": "foo"} to "match":{"_type": "bar"}
      *
      * @param subKey the key name directly under match to replace. For example "_type"
      * @param value  the value used in the replacement. For example "bar"
@@ -101,6 +105,28 @@ public class RestCompatTestTransformTask extends DefaultTask {
         transformations.add(new ReplaceMatch(subKey, MAPPER.convertValue(value, JsonNode.class)));
     }
 
+    /**
+     * Replaces all the values of a is_true assertion for all project REST tests.
+     * For example "is_true": "value_to_replace" to "match": "value_replaced"
+     *
+     * @param oldValue the value that has to match and will be replaced
+     * @param newValue  the value used in the replacement
+     */
+    public void replaceIsTrue(String oldValue, Object newValue) {
+        transformations.add(new ReplaceIsTrue(oldValue, MAPPER.convertValue(newValue, TextNode.class)));
+    }
+
+    /**
+     * Replaces all the values of a is_true assertion for all project REST tests.
+     * For example "is_false": "value_to_replace" to "match": "value_replaced"
+     *
+     * @param oldValue the value that has to match and will be replaced
+     * @param newValue  the value used in the replacement
+     */
+    public void replaceIsFalse(String oldValue, Object newValue) {
+        transformations.add(new ReplaceIsFalse(oldValue, MAPPER.convertValue(newValue, TextNode.class)));
+    }
+
     /**
      * Replaces the values of a match assertion for the given REST test. For example "match":{"_type": "foo"} to "match":{"_type": "bar"}
      *

+ 9 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/transform/RestTestTransformByParentObject.java

@@ -8,6 +8,7 @@
 
 package org.elasticsearch.gradle.test.rest.transform;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
@@ -27,4 +28,12 @@ public interface RestTestTransformByParentObject extends RestTestTransform<Objec
     default String requiredChildKey() {
         return null;
     }
+
+    /**
+     * @param child a node on which the transformation will be applied.
+     * @return true if the transformation should be applied on child node, otherwise false.
+     */
+    default boolean matches(JsonNode child) {
+        return child.has(requiredChildKey());
+    }
 }

+ 8 - 1
buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/transform/RestTestTransformer.java

@@ -11,6 +11,7 @@ package org.elasticsearch.gradle.test.rest.transform;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
 
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -144,9 +145,15 @@ public class RestTestTransformer {
                             } else {
                                 if (entry.getValue().isObject()) {
                                     ObjectNode child = (ObjectNode) entry.getValue();
-                                    if (child.has(transform.requiredChildKey())) {
+                                    if (transform.matches(child)) {
                                         transform.transformTest((ObjectNode) currentNode);
                                     }
+                                } else if (entry.getValue().isTextual()) {
+                                    TextNode value = (TextNode) entry.getValue();
+                                    if (transform.matches(value)) {
+                                        transform.transformTest((ObjectNode) currentNode);
+                                    }
+
                                 }
                             }
                         }

+ 17 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/transform/text/ReplaceIsFalse.java

@@ -0,0 +1,17 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.test.rest.transform.text;
+
+import com.fasterxml.jackson.databind.node.TextNode;
+
+public class ReplaceIsFalse extends ReplaceTextual {
+    public ReplaceIsFalse(String valueToBeReplaced, TextNode replacementNode) {
+        super("is_false", valueToBeReplaced, replacementNode);
+    }
+}

+ 17 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/transform/text/ReplaceIsTrue.java

@@ -0,0 +1,17 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.test.rest.transform.text;
+
+import com.fasterxml.jackson.databind.node.TextNode;
+
+public class ReplaceIsTrue extends ReplaceTextual {
+    public ReplaceIsTrue(String valueToBeReplaced, TextNode replacementNode) {
+        super("is_true", valueToBeReplaced, replacementNode);
+    }
+}

+ 85 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/transform/text/ReplaceTextual.java

@@ -0,0 +1,85 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.test.rest.transform.text;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+import org.elasticsearch.gradle.test.rest.transform.RestTestContext;
+import org.elasticsearch.gradle.test.rest.transform.RestTestTransformByParentObject;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Internal;
+import org.gradle.api.tasks.Optional;
+
+/**
+ * A transformation to replace the flat textual fields.
+ */
+class ReplaceTextual implements RestTestTransformByParentObject {
+    private final String keyToReplaceName;
+    private final String valueToBeReplaced;
+    private final TextNode replacementNode;
+    private final String testName;
+
+    ReplaceTextual(String keyToReplaceName, String valueToBeReplaced, TextNode replacementNode) {
+        this.keyToReplaceName = keyToReplaceName;
+        this.valueToBeReplaced = valueToBeReplaced;
+        this.replacementNode = replacementNode;
+        this.testName = null;
+    }
+
+    ReplaceTextual(String keyToReplaceName, String valueToBeReplaced, TextNode replacementNode, String testName) {
+        this.keyToReplaceName = keyToReplaceName;
+        this.valueToBeReplaced = valueToBeReplaced;
+        this.replacementNode = replacementNode;
+        this.testName = testName;
+    }
+
+    @Override
+    @Internal
+    public String getKeyToFind() {
+        return keyToReplaceName;
+    }
+
+    @Override
+    public String requiredChildKey() {
+        return valueToBeReplaced;
+    }
+
+    @Override
+    public boolean shouldApply(RestTestContext testContext) {
+        return testName == null || testContext.getTestName().equals(testName);
+    }
+
+    @Override
+    public void transformTest(ObjectNode matchParent) {
+        matchParent.set(getKeyToFind(), replacementNode);
+    }
+
+    @Input
+    public String getValueToBeReplaced() {
+        return valueToBeReplaced;
+    }
+
+    @Input
+    public JsonNode getReplacementNode() {
+        return replacementNode;
+    }
+
+    @Input
+    @Optional
+    public String getTestName() {
+        return testName;
+    }
+
+    @Override
+    public boolean matches(JsonNode child) {
+        return child.asText().equals(requiredChildKey());
+    }
+
+}

+ 138 - 0
buildSrc/src/test/java/org/elasticsearch/gradle/test/rest/transform/text/ReplaceTextualTests.java

@@ -0,0 +1,138 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.test.rest.transform.text;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import org.elasticsearch.gradle.test.rest.transform.TransformTests;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ReplaceTextualTests extends TransformTests {
+
+    private static final YAMLFactory YAML_FACTORY = new YAMLFactory();
+    private static final ObjectMapper MAPPER = new ObjectMapper(YAML_FACTORY);
+
+    @Test
+    public void testReplaceAll() throws Exception {
+        String testName = "/rest/transform/text/text_replace.yml";
+        List<ObjectNode> tests = getTests(testName);
+        TextNode replacementNode = MAPPER.convertValue("_replaced_value", TextNode.class);
+        validateTest(tests, true, true);
+        List<ObjectNode> transformedTests = transformTests(
+            new LinkedList<>(tests),
+            Collections.singletonList(new ReplaceTextual("key_to_replace", "value_to_replace", replacementNode, null))
+        );
+        printTest(testName, transformedTests);
+        validateTest(tests, false, true);
+    }
+
+    private void validateTest(List<ObjectNode> tests, boolean beforeTransformation, boolean allTests) {
+        validateSetupAndTearDownForMatchTests(tests);
+        // first test
+        JsonNode firstTestChild = tests.get(2).get("First test");
+        assertThat(firstTestChild, CoreMatchers.instanceOf(ArrayNode.class));
+        ArrayNode firstTestParentArray = (ArrayNode) firstTestChild;
+
+        AtomicBoolean firstTestHasKeyToReplace = new AtomicBoolean(false);
+        AtomicBoolean firstTestHasValueToReplace = new AtomicBoolean(false);
+
+        firstTestParentArray.elements().forEachRemaining(node -> {
+            assertThat(node, CoreMatchers.instanceOf(ObjectNode.class));
+            ObjectNode childObject = (ObjectNode) node;
+            JsonNode matchObject = childObject.get("key_to_replace");
+            if (matchObject != null) {
+                firstTestHasKeyToReplace.set(true);
+                if (beforeTransformation && matchObject.asText().equals("value_to_replace")) {
+                    firstTestHasValueToReplace.set(true);
+                }
+                if (beforeTransformation == false && allTests) {
+                    assertThat(matchObject.asText(), CoreMatchers.is("_replaced_value"));
+                    firstTestHasValueToReplace.set(true);
+                }
+            }
+        });
+        assertTrue(firstTestHasKeyToReplace.get());
+        assertTrue(firstTestHasValueToReplace.get());
+
+        // last test
+        JsonNode lastTestChild = tests.get(tests.size() - 1).get("Last test");
+        assertThat(lastTestChild, CoreMatchers.instanceOf(ArrayNode.class));
+        ArrayNode lastTestParentArray = (ArrayNode) lastTestChild;
+
+        AtomicBoolean lastTestHasKeyToReplace = new AtomicBoolean(false);
+        AtomicBoolean lastTestHasValueToReplace = new AtomicBoolean(false);
+        lastTestParentArray.elements().forEachRemaining(node -> {
+            assertThat(node, CoreMatchers.instanceOf(ObjectNode.class));
+            ObjectNode childObject = (ObjectNode) node;
+            JsonNode matchObject = childObject.get("key_to_replace");
+            if (matchObject != null) {
+                lastTestHasKeyToReplace.set(true);
+                if (beforeTransformation && matchObject.asText().equals("value_to_replace")) {
+                    lastTestHasValueToReplace.set(true);
+                }
+                if (beforeTransformation == false && allTests) {
+                    assertThat(matchObject.asText(), CoreMatchers.is("_replaced_value"));
+                    lastTestHasValueToReplace.set(true);
+                }
+            }
+        });
+        assertTrue(lastTestHasKeyToReplace.get());
+        assertTrue(lastTestHasValueToReplace.get());
+
+        // validate test3 - with key_to_replace but without a value
+        {
+            ObjectNode otherTest = tests.get(4);
+            JsonNode otherTestChild = otherTest.get(otherTest.fields().next().getKey());
+            assertThat(otherTestChild, CoreMatchers.instanceOf(ArrayNode.class));
+            ArrayNode otherTestParentArray = (ArrayNode) otherTestChild;
+            otherTestParentArray.elements().forEachRemaining(node -> {
+                assertThat(node, CoreMatchers.instanceOf(ObjectNode.class));
+                ObjectNode childObject = (ObjectNode) node;
+                JsonNode value = childObject.get("key_to_replace");
+                if (value != null && beforeTransformation && allTests) {
+                    assertThat(value.asText(), CoreMatchers.is("value_NOT_to_replace"));
+                }
+                if (value != null && beforeTransformation == false && allTests) {
+                    assertThat(value.asText(), CoreMatchers.is("value_NOT_to_replace"));
+                }
+            });
+        }
+
+        // exclude setup, teardown, first test, second to last test (does not have value to replace), and last test
+        for (int i = 3; i <= tests.size() - 3; i++) {
+            ObjectNode otherTest = tests.get(i);
+            JsonNode otherTestChild = otherTest.get(otherTest.fields().next().getKey());
+            assertThat(otherTestChild, CoreMatchers.instanceOf(ArrayNode.class));
+            ArrayNode otherTestParentArray = (ArrayNode) otherTestChild;
+            otherTestParentArray.elements().forEachRemaining(node -> {
+                assertThat(node, CoreMatchers.instanceOf(ObjectNode.class));
+                ObjectNode childObject = (ObjectNode) node;
+                JsonNode value = childObject.get("key_to_replace");
+                if (value != null && beforeTransformation == false && allTests) {
+                    assertThat(value.asText(), CoreMatchers.is("_replaced_value"));
+                }
+            });
+        }
+    }
+
+    @Override
+    protected boolean getHumanDebug() {
+        return false;
+    }
+}

+ 102 - 0
buildSrc/src/test/resources/rest/transform/text/text_replace.yml

@@ -0,0 +1,102 @@
+---
+setup:
+  - do:
+      something:
+        here: ok
+---
+teardown:
+  - do:
+      something_else:
+        here: true
+---
+"First test":
+
+  - do:
+      something:
+        that_is: true
+
+  - do:
+      and: again
+
+  - key_not_to_replace: { copied.from.real.test.total: 1 }
+  - key_not_to_replace: { hits.hits.0._index: "single_doc_index"}
+  - key_not_to_replace: { _shards.total: 2 }
+  - key_not_to_replace: { _shards.successful: 2 }
+  - key_not_to_replace: { _shards.skipped : 0}
+  - key_not_to_replace: { _shards.failed: 0 }
+
+  - do:
+      and: again
+
+  - key_not_to_replace: { hits.total: 1 }
+  - key_not_to_replace: { hits.hits.0._index: "my_remote_cluster:single_doc_index"}
+  - key_not_to_replace: { _shards.total: 2 }
+  - key_not_to_replace: { _shards.successful: 2 }
+  - key_not_to_replace: { _shards.skipped : 0}
+  - key_not_to_replace: { _below_is_target_for_tests: 0 }
+  - key_to_replace: "value_to_replace"
+
+---
+"Also has key_value to replace":
+
+  - do:
+      something:
+        that_is: true
+
+  - do:
+      and: again
+
+  - key_not_to_replace: { hits.total.value: 0 }
+  - key_to_replace: "value_to_replace"
+  - key_not_to_replace: { _below_is_target_for_tests: 0 }
+  - key_not_to_replace: { _type: foo }
+  - key_not_to_replace: { _shards.total: 2 }
+  - key_not_to_replace: { _shards.successful: 2 }
+  - key_not_to_replace: { _shards.skipped : 1}
+  - key_not_to_replace: { _shards.failed: 0 }
+
+  - do:
+      not_random_but_representive: "of actual test"
+
+  - key_not_to_replace: { hits.total.value: 0 }
+  - key_not_to_replace: { _shards.total: 2 }
+  - key_not_to_replace: { _shards.successful: 2 }
+  - key_not_to_replace: { _shards.skipped : 1}
+  - key_not_to_replace: { _shards.failed: 0 }
+
+---
+"Does not have key_value to replace ":
+
+  - do:
+      something:
+        that_is: true
+
+  - do:
+      and: again
+
+  - key_to_replace: "value_NOT_to_replace"
+
+  - do:
+      it: again
+
+  - key_not_to_replace: { _type: 0 }
+  - key_not_to_replace: { _shards.total: 2 }
+
+---
+"Last test":
+
+  - do:
+      something:  中文
+
+  - key_not_to_replace: { _index:   test_1 }
+  - key_to_replace: "value_to_replace"
+  - key_not_to_replace: { _id:      中文      }
+  - key_not_to_replace: { _source:  { foo: "Hello: 中文" } }
+
+  - do:
+      something_else:  中文
+
+  - key_not_to_replace: { _index:   test_1 }
+  - key_not_to_replace: { _type:    "the value does not matter" }
+  - key_not_to_replace: { _id:      中文      }
+  - key_not_to_replace: { _source:  { foo: "Hello: 中文" } }