1
0
Эх сурвалжийг харах

[TEST] added support for replacing stashed values within objects and lists in our REST tests

javanna 11 жил өмнө
parent
commit
7d3cd89207

+ 0 - 4
rest-api-spec/test/cluster.reroute/11_explain.yaml

@@ -26,10 +26,6 @@ setup:
 ---
 "Explain API for non-existant node & shard":
 
-  - skip:
-      version: 0-999
-      reason: param substitution not implemented yet
-
   - do:
       cluster.state:
         metric: [ master_node ]

+ 32 - 41
src/test/java/org/elasticsearch/test/rest/RestTestExecutionContext.java

@@ -19,9 +19,9 @@
 package org.elasticsearch.test.rest;
 
 import com.google.common.collect.Maps;
-import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.logging.ESLogger;
 import org.elasticsearch.common.logging.Loggers;
+import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.test.rest.client.RestClient;
 import org.elasticsearch.test.rest.client.RestException;
 import org.elasticsearch.test.rest.client.RestResponse;
@@ -31,6 +31,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -47,7 +48,7 @@ public class RestTestExecutionContext implements Closeable {
 
     private final String esVersion;
 
-    private final Map<String, Object> stash = Maps.newHashMap();
+    private final Stash stash = new Stash();
 
     private RestResponse response;
 
@@ -61,18 +62,21 @@ public class RestTestExecutionContext implements Closeable {
      * Saves the obtained response in the execution context.
      * @throws RestException if the returned status code is non ok
      */
-    public RestResponse callApi(String apiName, Map<String, String> params, String body) throws IOException, RestException  {
+    public RestResponse callApi(String apiName, Map<String, String> params, List<Map<String, Object>> bodies) throws IOException, RestException  {
         //makes a copy of the parameters before modifying them for this specific request
         HashMap<String, String> requestParams = Maps.newHashMap(params);
         for (Map.Entry<String, String> entry : requestParams.entrySet()) {
-            if (isStashed(entry.getValue())) {
-                entry.setValue(unstash(entry.getValue()).toString());
+            if (stash.isStashedValue(entry.getValue())) {
+                entry.setValue(stash.unstashValue(entry.getValue()).toString());
             }
         }
+
+        String body = actualBody(bodies);
+
         try {
             response = callApiInternal(apiName, requestParams, body);
             //we always stash the last response body
-            stash("body", response.getBody());
+            stash.stashValue("body", response.getBody());
             return response;
         } catch(RestException e) {
             response = e.restResponse();
@@ -80,6 +84,26 @@ public class RestTestExecutionContext implements Closeable {
         }
     }
 
+    private String actualBody(List<Map<String, Object>> bodies) throws IOException {
+        if (bodies.isEmpty()) {
+            return "";
+        }
+
+        if (bodies.size() == 1) {
+            return bodyAsString(stash.unstashMap(bodies.get(0)));
+        }
+
+        StringBuilder bodyBuilder = new StringBuilder();
+        for (Map<String, Object> body : bodies) {
+            bodyBuilder.append(bodyAsString(stash.unstashMap(body))).append("\n");
+        }
+        return bodyBuilder.toString();
+    }
+
+    private String bodyAsString(Map<String, Object> body) throws IOException {
+        return XContentFactory.jsonBuilder().map(body).string();
+    }
+
     /**
      * Calls an elasticsearch api internally without saving the obtained response in the context.
      * Useful for internal calls (e.g. delete index during teardown)
@@ -109,41 +133,8 @@ public class RestTestExecutionContext implements Closeable {
         stash.clear();
     }
 
-    /**
-     * Tells whether a particular value needs to be looked up in the stash
-     * The stash contains fields eventually extracted from previous responses that can be reused
-     * as arguments for following requests (e.g. scroll_id)
-     */
-    public boolean isStashed(Object key) {
-        if (key == null) {
-            return false;
-        }
-        String stashKey = key.toString();
-        return Strings.hasLength(stashKey) && stashKey.startsWith("$");
-    }
-
-    /**
-     * Extracts a value from the current stash
-     * The stash contains fields eventually extracted from previous responses that can be reused
-     * as arguments for following requests (e.g. scroll_id)
-     */
-    public Object unstash(String value) {
-        Object stashedValue = stash.get(value.substring(1));
-        if (stashedValue == null) {
-            throw new IllegalArgumentException("stashed value not found for key [" + value + "]");
-        }
-        return stashedValue;
-    }
-
-    /**
-     * Allows to saved a specific field in the stash as key-value pair
-     */
-    public void stash(String key, Object value) {
-        logger.debug("stashing [{}]=[{}]", key, value);
-        Object old = stash.put(key, value);
-        if (old != null && old != value) {
-            logger.trace("replaced stashed value [{}] with same key [{}]", old, key);
-        }
+    public Stash stash() {
+        return stash;
     }
 
     /**

+ 117 - 0
src/test/java/org/elasticsearch/test/rest/Stash.java

@@ -0,0 +1,117 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.test.rest;
+
+import com.google.common.collect.Maps;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.logging.ESLogger;
+import org.elasticsearch.common.logging.Loggers;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Allows to cache the last obtained test response and or part of it within variables
+ * that can be used as input values in following requests and assertions.
+ */
+public class Stash {
+
+    private static final ESLogger logger = Loggers.getLogger(Stash.class);
+
+    private final Map<String, Object> stash = Maps.newHashMap();
+
+    /**
+     * Allows to saved a specific field in the stash as key-value pair
+     */
+    public void stashValue(String key, Object value) {
+        logger.debug("stashing [{}]=[{}]", key, value);
+        Object old = stash.put(key, value);
+        if (old != null && old != value) {
+            logger.trace("replaced stashed value [{}] with same key [{}]", old, key);
+        }
+    }
+
+    /**
+     * Clears the previously stashed values
+     */
+    public void clear() {
+        stash.clear();
+    }
+
+    /**
+     * Tells whether a particular value needs to be looked up in the stash
+     * The stash contains fields eventually extracted from previous responses that can be reused
+     * as arguments for following requests (e.g. scroll_id)
+     */
+    public boolean isStashedValue(Object key) {
+        if (key == null) {
+            return false;
+        }
+        String stashKey = key.toString();
+        return Strings.hasLength(stashKey) && stashKey.startsWith("$");
+    }
+
+    /**
+     * Extracts a value from the current stash
+     * The stash contains fields eventually extracted from previous responses that can be reused
+     * as arguments for following requests (e.g. scroll_id)
+     */
+    public Object unstashValue(String value) {
+        Object stashedValue = stash.get(value.substring(1));
+        if (stashedValue == null) {
+            throw new IllegalArgumentException("stashed value not found for key [" + value + "]");
+        }
+        return stashedValue;
+    }
+
+    /**
+     * Recursively unstashes map values if needed
+     */
+    public Map<String, Object> unstashMap(Map<String, Object> map) {
+        Map<String, Object> copy = Maps.newHashMap(map);
+        unstashObject(copy);
+        return copy;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void unstashObject(Object obj) {
+        if (obj instanceof List) {
+            List list = (List)obj;
+            for (int i = 0; i < list.size(); i++) {
+                Object o = list.get(i);
+                if (isStashedValue(o)) {
+                    list.set(i, unstashValue(o.toString()));
+                } else {
+                    unstashObject(o);
+                }
+            }
+        }
+        if (obj instanceof Map) {
+            Map<String, Object> map = (Map) obj;
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (isStashedValue(entry.getValue())) {
+                    entry.setValue(unstashValue(entry.getValue().toString()));
+                } else {
+                    unstashObject(entry.getValue());
+                }
+            }
+        }
+    }
+}

+ 9 - 6
src/test/java/org/elasticsearch/test/rest/parser/DoSectionParser.java

@@ -18,14 +18,13 @@
  */
 package org.elasticsearch.test.rest.parser;
 
-import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.test.rest.section.ApiCallSection;
 import org.elasticsearch.test.rest.section.DoSection;
 
 import java.io.IOException;
-import java.util.Map;
 
 /**
  * Parser for do sections
@@ -58,15 +57,19 @@ public class DoSectionParser implements RestTestFragmentParser<DoSection> {
                             paramName = parser.currentName();
                         } else if (token.isValue()) {
                             if ("body".equals(paramName)) {
-                                apiCallSection.addBody(parser.text());
+                                String body = parser.text();
+                                XContentType bodyContentType = XContentFactory.xContentType(body);
+                                XContentParser bodyParser = XContentFactory.xContent(bodyContentType).createParser(body);
+                                //multiple bodies are supported e.g. in case of bulk provided as a whole string
+                                while(bodyParser.nextToken() != null) {
+                                    apiCallSection.addBody(bodyParser.mapOrdered());
+                                }
                             } else {
                                 apiCallSection.addParam(paramName, parser.text());
                             }
                         } else if (token == XContentParser.Token.START_OBJECT) {
                             if ("body".equals(paramName)) {
-                                Map<String,Object> map = parser.mapOrdered();
-                                XContentBuilder contentBuilder = XContentFactory.jsonBuilder().map(map);
-                                apiCallSection.addBody(contentBuilder.string());
+                                apiCallSection.addBody(parser.mapOrdered());
                             }
                         }
                     }

+ 3 - 21
src/test/java/org/elasticsearch/test/rest/section/ApiCallSection.java

@@ -34,9 +34,7 @@ public class ApiCallSection {
 
     private final String api;
     private final Map<String, String> params = Maps.newHashMap();
-    private final List<String> bodies = Lists.newArrayList();
-
-    private static final String EMPTY_BODY = "";
+    private final List<Map<String, Object>> bodies = Lists.newArrayList();
 
     public ApiCallSection(String api) {
         this.api = api;
@@ -59,27 +57,11 @@ public class ApiCallSection {
         this.params.put(key, value);
     }
 
-    public List<String> getBodiesAsList() {
+    public List<Map<String, Object>> getBodies() {
         return ImmutableList.copyOf(bodies);
     }
 
-    public String getBody() {
-        if (bodies.size() == 0) {
-            return EMPTY_BODY;
-        }
-
-        if (bodies.size() == 1) {
-            return bodies.get(0);
-        }
-
-        StringBuilder bodyBuilder = new StringBuilder();
-        for (String body : bodies) {
-            bodyBuilder.append(body).append("\n");
-        }
-        return bodyBuilder.toString();
-    }
-
-    public void addBody(String body) {
+    public void addBody(Map<String, Object> body) {
         this.bodies.add(body);
     }
 

+ 12 - 5
src/test/java/org/elasticsearch/test/rest/section/Assertion.java

@@ -21,6 +21,7 @@ package org.elasticsearch.test.rest.section;
 import org.elasticsearch.test.rest.RestTestExecutionContext;
 
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * Base class for executable sections that hold assertions
@@ -43,16 +44,22 @@ public abstract class Assertion implements ExecutableSection {
         return expectedValue;
     }
 
-    protected final Object resolveExpectedValue(RestTestExecutionContext executionContext) {
-        if (executionContext.isStashed(expectedValue)) {
-            return executionContext.unstash(expectedValue.toString());
+    protected final Object resolveExpectedValue(RestTestExecutionContext executionContext) throws IOException {
+        if (expectedValue instanceof Map) {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> map = (Map<String, Object>) expectedValue;
+            return executionContext.stash().unstashMap(map);
+        }
+
+        if (executionContext.stash().isStashedValue(expectedValue)) {
+            return executionContext.stash().unstashValue(expectedValue.toString());
         }
         return expectedValue;
     }
 
     protected final Object getActualValue(RestTestExecutionContext executionContext) throws IOException {
-        if (executionContext.isStashed(field)) {
-            return executionContext.unstash(field);
+        if (executionContext.stash().isStashedValue(field)) {
+            return executionContext.stash().unstashValue(field);
         }
         return executionContext.response(field);
     }

+ 1 - 1
src/test/java/org/elasticsearch/test/rest/section/DoSection.java

@@ -82,7 +82,7 @@ public class DoSection implements ExecutableSection {
         }
 
         try {
-            RestResponse restResponse = executionContext.callApi(apiCallSection.getApi(), apiCallSection.getParams(), apiCallSection.getBody());
+            RestResponse restResponse = executionContext.callApi(apiCallSection.getApi(), apiCallSection.getParams(), apiCallSection.getBodies());
             if (Strings.hasLength(catchParam)) {
                 String catchStatusCode;
                 if (catches.containsKey(catchParam)) {

+ 1 - 1
src/test/java/org/elasticsearch/test/rest/section/SetSection.java

@@ -46,7 +46,7 @@ public class SetSection implements ExecutableSection {
     public void execute(RestTestExecutionContext executionContext) throws IOException {
         for (Map.Entry<String, String> entry : stash.entrySet()) {
             Object actualValue = executionContext.response(entry.getKey());
-            executionContext.stash(entry.getValue(), actualValue);
+            executionContext.stash().stashValue(entry.getValue(), actualValue);
         }
     }
 }

+ 20 - 47
src/test/java/org/elasticsearch/test/rest/test/DoSectionParserTests.java

@@ -98,8 +98,7 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getParams().get("id"), equalTo("1"));
         assertThat(apiCallSection.hasBody(), equalTo(true));
 
-        assertJsonEquals(apiCallSection.getBodiesAsList().get(0), body);
-        assertJsonEquals(apiCallSection.getBody(), body);
+        assertJsonEquals(apiCallSection.getBodies().get(0), body);
     }
 
     @Test
@@ -129,12 +128,7 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getParams().size(), equalTo(1));
         assertThat(apiCallSection.getParams().get("refresh"), equalTo("true"));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(1));
-        StringBuilder bodyBuilder = new StringBuilder();
-        for (String body : bodies) {
-            bodyBuilder.append(body);
-        }
-        assertThat(apiCallSection.getBody(), equalTo(bodyBuilder.toString()));
+        assertThat(apiCallSection.getBodies().size(), equalTo(4));
     }
 
     @Test
@@ -161,15 +155,9 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getParams().size(), equalTo(1));
         assertThat(apiCallSection.getParams().get("refresh"), equalTo("true"));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(bodies.length));
+        assertThat(apiCallSection.getBodies().size(), equalTo(bodies.length));
         for (int i = 0; i < bodies.length; i++) {
-            assertJsonEquals(apiCallSection.getBodiesAsList().get(i), bodies[i]);
-        }
-
-        String[] returnedBodies = apiCallSection.getBody().split("\n");
-        assertThat(returnedBodies.length, equalTo(bodies.length));
-        for (int i = 0; i < bodies.length; i++) {
-            assertJsonEquals(returnedBodies[i], bodies[i]);
+            assertJsonEquals(apiCallSection.getBodies().get(i), bodies[i]);
         }
     }
 
@@ -191,9 +179,8 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getApi(), equalTo("search"));
         assertThat(apiCallSection.getParams().size(), equalTo(0));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(1));
-        assertJsonEquals(apiCallSection.getBodiesAsList().get(0), body);
-        assertJsonEquals(apiCallSection.getBody(), body);
+        assertThat(apiCallSection.getBodies().size(), equalTo(1));
+        assertJsonEquals(apiCallSection.getBodies().get(0), body);
     }
 
     @Test
@@ -230,16 +217,10 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getParams().size(), equalTo(1));
         assertThat(apiCallSection.getParams().get("refresh"), equalTo("true"));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(bodies.length));
-
-        for (int i = 0; i < bodies.length; i++) {
-            assertJsonEquals(apiCallSection.getBodiesAsList().get(i), bodies[i]);
-        }
+        assertThat(apiCallSection.getBodies().size(), equalTo(bodies.length));
 
-        String[] returnedBodies = apiCallSection.getBody().split("\n");
-        assertThat(returnedBodies.length, equalTo(bodies.length));
         for (int i = 0; i < bodies.length; i++) {
-            assertJsonEquals(returnedBodies[i], bodies[i]);
+            assertJsonEquals(apiCallSection.getBodies().get(i), bodies[i]);
         }
     }
 
@@ -270,16 +251,10 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getParams().size(), equalTo(1));
         assertThat(apiCallSection.getParams().get("refresh"), equalTo("true"));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(bodies.length));
-
-        for (int i = 0; i < bodies.length; i++) {
-            assertJsonEquals(apiCallSection.getBodiesAsList().get(i), bodies[i]);
-        }
+        assertThat(apiCallSection.getBodies().size(), equalTo(bodies.length));
 
-        String[] returnedBodies = apiCallSection.getBody().split("\n");
-        assertThat(returnedBodies.length, equalTo(bodies.length));
         for (int i = 0; i < bodies.length; i++) {
-            assertJsonEquals(returnedBodies[i], bodies[i]);
+            assertJsonEquals(apiCallSection.getBodies().get(i), bodies[i]);
         }
     }
 
@@ -305,9 +280,8 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getApi(), equalTo("mget"));
         assertThat(apiCallSection.getParams().size(), equalTo(0));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(1));
-        assertJsonEquals(apiCallSection.getBodiesAsList().get(0), body);
-        assertJsonEquals(apiCallSection.getBody(), body);
+        assertThat(apiCallSection.getBodies().size(), equalTo(1));
+        assertJsonEquals(apiCallSection.getBodies().get(0), body);
     }
 
     @Test
@@ -331,9 +305,9 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getParams().get("type"), equalTo("test"));
         assertThat(apiCallSection.getParams().get("id"), equalTo("1"));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(1));
+        assertThat(apiCallSection.getBodies().size(), equalTo(1));
         //stringified body is taken as is
-        assertThat(apiCallSection.getBodiesAsList().get(0), equalTo("{ _source: true, query: { match_all: {} } }"));
+        assertJsonEquals(apiCallSection.getBodies().get(0), "{ _source: true, query: { match_all: {} } }");
     }
 
     @Test
@@ -354,10 +328,10 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(apiCallSection.getApi(), equalTo("index"));
         assertThat(apiCallSection.getParams().size(), equalTo(0));
         assertThat(apiCallSection.hasBody(), equalTo(true));
-        assertThat(apiCallSection.getBodiesAsList().size(), equalTo(2));
+        assertThat(apiCallSection.getBodies().size(), equalTo(2));
         //stringified body is taken as is
-        assertThat(apiCallSection.getBodiesAsList().get(0), equalTo("{ _source: true, query: { match_all: {} } }"));
-        assertJsonEquals(apiCallSection.getBodiesAsList().get(1), body);
+        assertJsonEquals(apiCallSection.getBodies().get(0), "{ _source: true, query: { match_all: {} } }");
+        assertJsonEquals(apiCallSection.getBodies().get(1), body);
     }
 
     @Test
@@ -409,12 +383,11 @@ public class DoSectionParserTests extends AbstractParserTests {
         assertThat(doSection.getApiCallSection().getParams().get("type"), equalTo("test_type"));
         assertThat(doSection.getApiCallSection().getParams().get("field"), equalTo("text,text1"));
         assertThat(doSection.getApiCallSection().hasBody(), equalTo(false));
-        assertThat(doSection.getApiCallSection().getBodiesAsList().size(), equalTo(0));
+        assertThat(doSection.getApiCallSection().getBodies().size(), equalTo(0));
     }
 
-    private static void assertJsonEquals(String actual, String expected) throws IOException {
-        Map<String,Object> actualMap = JsonXContent.jsonXContent.createParser(actual).mapOrderedAndClose();
+    private static void assertJsonEquals(Map<String, Object> actual, String expected) throws IOException {
         Map<String,Object> expectedMap = JsonXContent.jsonXContent.createParser(expected).mapOrderedAndClose();
-        MatcherAssert.assertThat(actualMap, equalTo(expectedMap));
+        MatcherAssert.assertThat(actual, equalTo(expectedMap));
     }
 }