diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ContainsAssertion.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ContainsAssertion.java new file mode 100644 index 0000000000000..2f7e74750a247 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ContainsAssertion.java @@ -0,0 +1,80 @@ +/* + * 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.yaml.section; + +import org.apache.logging.log4j.Logger; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.xcontent.XContentLocation; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class ContainsAssertion extends Assertion { + public static ContainsAssertion parse(XContentParser parser) throws IOException { + XContentLocation location = parser.getTokenLocation(); + Tuple stringObjectTuple = ParserUtils.parseTuple(parser); + return new ContainsAssertion(location, stringObjectTuple.v1(), stringObjectTuple.v2()); + } + + private static final Logger logger = Loggers.getLogger(ContainsAssertion.class); + + public ContainsAssertion(XContentLocation location, String field, Object expectedValue) { + super(location, field, expectedValue); + } + + @Override + protected void doAssert(Object actualValue, Object expectedValue) { + // add support for matching objects ({a:b}) against list of objects ([ {a:b, c:d} ]) + if (expectedValue instanceof Map && actualValue instanceof List) { + logger.trace("assert that [{}] contains [{}]", actualValue, expectedValue); + Map expectedMap = (Map) expectedValue; + List actualList = (List) actualValue; + List> actualValues = actualList.stream() + .filter(each -> each instanceof Map) + .map((each -> (Map) each)) + .filter(each -> each.keySet().containsAll(expectedMap.keySet())) + .collect(Collectors.toList()); + assertThat( + getField() + " expected to be a list with at least one object that has keys: " + + expectedMap.keySet() + " but it was " + actualList, + actualValues, + is(not(empty())) + ); + assertTrue( + getField() + " expected to be a list with at least on object that matches " + expectedMap + + " but was " + actualValues, + actualValues.stream() + .anyMatch(each -> each.entrySet().containsAll(expectedMap.entrySet())) + ); + } else { + fail("'contains' only supports checking an object against a list of objects"); + } + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java index ce5ea1c1cde06..ff02d6d16aa4a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java @@ -47,6 +47,7 @@ public interface ExecutableSection { new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("gte"), GreaterThanEqualToAssertion::parse), new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("lt"), LessThanAssertion::parse), new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("lte"), LessThanOrEqualToAssertion::parse), + new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("contains"), ContainsAssertion::parse), new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("length"), LengthAssertion::parse))); /** diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java index fcef74678359e..ddf153ff44f5c 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java @@ -134,6 +134,22 @@ public void testParseMatchArray() throws Exception { assertThat(strings.get(1).toString(), equalTo("test_percolator_2")); } + @SuppressWarnings("unchecked") + public void testParseContains() throws Exception { + parser = createParser(YamlXContent.yamlXContent, + "{testKey: { someKey: someValue } }" + ); + + ContainsAssertion containsAssertion = ContainsAssertion.parse(parser); + assertThat(containsAssertion, notNullValue()); + assertThat(containsAssertion.getField(), equalTo("testKey")); + assertThat(containsAssertion.getExpectedValue(), instanceOf(Map.class)); + assertThat( + ((Map) containsAssertion.getExpectedValue()).get("someKey"), + equalTo("someValue") + ); + } + @SuppressWarnings("unchecked") public void testParseMatchSourceValues() throws Exception { parser = createParser(YamlXContent.yamlXContent,