diff --git a/rest-api-spec/test/cluster.reroute/11_explain.yaml b/rest-api-spec/test/cluster.reroute/11_explain.yaml index 10701a57927..6cab02d143f 100644 --- a/rest-api-spec/test/cluster.reroute/11_explain.yaml +++ b/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 ] diff --git a/src/test/java/org/elasticsearch/test/rest/RestTestExecutionContext.java b/src/test/java/org/elasticsearch/test/rest/RestTestExecutionContext.java index 0f5ec14796d..03b19c1a530 100644 --- a/src/test/java/org/elasticsearch/test/rest/RestTestExecutionContext.java +++ b/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 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 params, String body) throws IOException, RestException { + public RestResponse callApi(String apiName, Map params, List> bodies) throws IOException, RestException { //makes a copy of the parameters before modifying them for this specific request HashMap requestParams = Maps.newHashMap(params); for (Map.Entry 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> bodies) throws IOException { + if (bodies.isEmpty()) { + return ""; + } + + if (bodies.size() == 1) { + return bodyAsString(stash.unstashMap(bodies.get(0))); + } + + StringBuilder bodyBuilder = new StringBuilder(); + for (Map body : bodies) { + bodyBuilder.append(bodyAsString(stash.unstashMap(body))).append("\n"); + } + return bodyBuilder.toString(); + } + + private String bodyAsString(Map 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; } /** diff --git a/src/test/java/org/elasticsearch/test/rest/Stash.java b/src/test/java/org/elasticsearch/test/rest/Stash.java new file mode 100644 index 00000000000..3179ecc55be --- /dev/null +++ b/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 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 unstashMap(Map map) { + Map 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 map = (Map) obj; + for (Map.Entry entry : map.entrySet()) { + if (isStashedValue(entry.getValue())) { + entry.setValue(unstashValue(entry.getValue().toString())); + } else { + unstashObject(entry.getValue()); + } + } + } + } +} diff --git a/src/test/java/org/elasticsearch/test/rest/parser/DoSectionParser.java b/src/test/java/org/elasticsearch/test/rest/parser/DoSectionParser.java index 2a8f89851ce..ec5aef54459 100644 --- a/src/test/java/org/elasticsearch/test/rest/parser/DoSectionParser.java +++ b/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 { 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 map = parser.mapOrdered(); - XContentBuilder contentBuilder = XContentFactory.jsonBuilder().map(map); - apiCallSection.addBody(contentBuilder.string()); + apiCallSection.addBody(parser.mapOrdered()); } } } diff --git a/src/test/java/org/elasticsearch/test/rest/section/ApiCallSection.java b/src/test/java/org/elasticsearch/test/rest/section/ApiCallSection.java index be66796cd91..2a49cd4c594 100644 --- a/src/test/java/org/elasticsearch/test/rest/section/ApiCallSection.java +++ b/src/test/java/org/elasticsearch/test/rest/section/ApiCallSection.java @@ -34,9 +34,7 @@ public class ApiCallSection { private final String api; private final Map params = Maps.newHashMap(); - private final List bodies = Lists.newArrayList(); - - private static final String EMPTY_BODY = ""; + private final List> bodies = Lists.newArrayList(); public ApiCallSection(String api) { this.api = api; @@ -59,27 +57,11 @@ public class ApiCallSection { this.params.put(key, value); } - public List getBodiesAsList() { + public List> 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 body) { this.bodies.add(body); } diff --git a/src/test/java/org/elasticsearch/test/rest/section/Assertion.java b/src/test/java/org/elasticsearch/test/rest/section/Assertion.java index 338a20ad3ca..4639732bcdb 100644 --- a/src/test/java/org/elasticsearch/test/rest/section/Assertion.java +++ b/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 map = (Map) 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); } diff --git a/src/test/java/org/elasticsearch/test/rest/section/DoSection.java b/src/test/java/org/elasticsearch/test/rest/section/DoSection.java index cc55d727a5a..3ea1ca09852 100644 --- a/src/test/java/org/elasticsearch/test/rest/section/DoSection.java +++ b/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)) { diff --git a/src/test/java/org/elasticsearch/test/rest/section/SetSection.java b/src/test/java/org/elasticsearch/test/rest/section/SetSection.java index 575b5151db7..0a52a7798b1 100644 --- a/src/test/java/org/elasticsearch/test/rest/section/SetSection.java +++ b/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 entry : stash.entrySet()) { Object actualValue = executionContext.response(entry.getKey()); - executionContext.stash(entry.getValue(), actualValue); + executionContext.stash().stashValue(entry.getValue(), actualValue); } } } diff --git a/src/test/java/org/elasticsearch/test/rest/test/DoSectionParserTests.java b/src/test/java/org/elasticsearch/test/rest/test/DoSectionParserTests.java index ea3ddad0bbc..e580f0f4e3d 100644 --- a/src/test/java/org/elasticsearch/test/rest/test/DoSectionParserTests.java +++ b/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)); + 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]); } } @@ -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)); + 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]); } } @@ -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 actualMap = JsonXContent.jsonXContent.createParser(actual).mapOrderedAndClose(); + private static void assertJsonEquals(Map actual, String expected) throws IOException { Map expectedMap = JsonXContent.jsonXContent.createParser(expected).mapOrderedAndClose(); - MatcherAssert.assertThat(actualMap, equalTo(expectedMap)); + MatcherAssert.assertThat(actual, equalTo(expectedMap)); } }