CACHE = new ConcurrentHashMap<>();
+
+ private XmlAssertion(Document parsedXml) {
+ this.cachedObjects = new XmlCachedObjects(parsedXml);
+ }
+
+ private XmlAssertion(String xml) {
+ XmlCachedObjects cachedObjects = CACHE.get(xml);
+ if (cachedObjects == null && !empty(xml)) {
+ try {
+ InputStream inputXml = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+ DOMLoader loader = new XercesLoader();
+ Document document = loader.load(inputXml);
+ cachedObjects = new XmlCachedObjects(document, xml);
+ } catch (Exception e) {
+ throw new IllegalStateException("Exception occurred while trying to parse the XML", e);
+ }
+ CACHE.put(xml, cachedObjects);
+ }
+ this.cachedObjects = cachedObjects;
+ }
+
+ private boolean empty(String text) {
+ return text == null || text.length() == 0 || text.matches("^\\s*$");
+ }
+
+ /**
+ * Starts assertions for the XML provided as {@link String}
+ */
+ public static XmlVerifiable assertThat(String xml) {
+ return new XmlAssertion(xml).root();
+ }
+
+ /**
+ * Starts assertions for the XML provided as {@link Document}
+ */
+ public static XmlVerifiable assertThat(Document parsedXml) {
+ return new XmlAssertion(parsedXml).root();
+ }
+
+ /**
+ * Helper method so that there are no clashes with other static methods of that name
+ *
+ * @see XmlAssertion#assertThat(String)
+ */
+ public static XmlVerifiable assertThatXml(String body) {
+ return assertThat(body);
+ }
+
+ /**
+ * Helper method so that there are no clashes with other static methods of that name
+ *
+ * @see XmlAssertion#assertThat(Document)
+ */
+ public static XmlVerifiable assertThatXml(Document parsedXml) {
+ return assertThat(parsedXml);
+ }
+
+ private XmlVerifiable root() {
+ return new FieldAssertion(cachedObjects, xPathBuffer, specialCaseXPathBuffer, "", xmlAsserterConfiguration).node("");
+ }
+
+}
diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlCachedObjects.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlCachedObjects.java
new file mode 100644
index 0000000000..951807bb25
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlCachedObjects.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.util.xml;
+
+import java.io.StringWriter;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.eclipse.wst.xml.xpath2.processor.util.StaticContextBuilder;
+import org.w3c.dom.Document;
+
+/**
+ * Contains cached objects that are memory consuming
+ *
+ * @author Marcin Grzejszczak
+ *
+ * @since 2.1.0
+ */
+class XmlCachedObjects {
+ final Document document;
+ final StaticContextBuilder xpathBuilder;
+ final String xmlAsString;
+
+ XmlCachedObjects(Document document) {
+ this.document = document;
+ this.xpathBuilder = new StaticContextBuilder();
+ this.xmlAsString = xmlAsString();
+ }
+
+ XmlCachedObjects(Document document, String xmlAsString) {
+ this.document = document;
+ this.xpathBuilder = new StaticContextBuilder();
+ this.xmlAsString = xmlAsString;
+ }
+
+ private String xmlAsString() {
+ try {
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ StringWriter writer = new StringWriter();
+ transformer.transform(new DOMSource(document), new StreamResult(writer));
+ return writer.getBuffer().toString().replaceAll("\n|\r", "");
+ } catch (TransformerException e) {
+ throw new RuntimeException("Exception occured while trying to convert XML Document to String", e);
+ }
+ }
+}
diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlReader.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlReader.java
new file mode 100644
index 0000000000..81f950fa29
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlReader.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.util.xml;
+
+/**
+ * Contract to read the value from a XML basing on it.
+ *
+ * @author Marcin Grzejszczak
+ *
+ * @since 2.1.0
+ */
+public interface XmlReader {
+
+ /**
+ * Returns the value from the XML, based on the created XPath.
+ */
+ String read();
+}
diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlVerifiable.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlVerifiable.java
new file mode 100644
index 0000000000..4ac43ff356
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/xml/XmlVerifiable.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.util.xml;
+
+/**
+ * Contract to match a parsed XML via XPath
+ *
+ * @author Marcin Grzejszczak
+ * @author Olga Maciaszek-Sharma
+ *
+ * @since 2.1.0
+ */
+public interface XmlVerifiable extends IteratingOverArray, XmlReader {
+
+ /**
+ * Field assertion. Adds a XPath entry for a single node.
+ */
+ XmlVerifiable node(String nodeName);
+
+ /**
+ * Field assertion. Adds a attribute to the currently checked node.
+ * NOTE: If you want to both check equality and attributes you have to
+ * first check the equality and then attributes. E.g. having such an XML
+ *
+ *
+ *
+ * {@code
+
+
+
+ with "val'ue
+ 4
+ foo
+
+ name1
+
+
+ name2
+
+
+ 8
+
+
+ name3
+
+
+
+ * }
+ *
+ *
+ *
+ * In order to check the values of the attributes of the {@code withlist} element
+ * with value {@code name3} you'd have to call:
+ *
+ *
+ *
+ * {@code assertThat(xml1).node("some").node("nested").array("withlist").contains("name").isEqualTo("name3").withAttribute("id", "10").withAttribute("surname", "kowalski")}
+ *
+ *
+ *
+ * The following XPath would be created:
+ {@code /some/nested/withlist[name='name3']/name[@id='10'][@surname='kowalski'] }
+ *
+ */
+ XmlVerifiable withAttribute(String attribute, String attributeValue);
+
+ /**
+ * Adds attribute query to xPath without comparing with any provided value
+ * @param attribute AttributeName
+ * @return new {@code XmlVerifiable}
+ */
+ XmlVerifiable withAttribute(String attribute);
+
+ /**
+ * Adds a {@code text()} call to xPath
+ * @return new {@code XmlVerifiable}
+ */
+ XmlVerifiable text();
+
+ /**
+ * Adds an index to xPath
+ * @return new {@code XmlVerifiable}
+ */
+ XmlVerifiable index(int index);
+
+ /**
+ * Field assertions. Traverses through the list of nodes and
+ * adds a XPath entry for each one.
+ */
+ XmlVerifiable node(String... nodeNames);
+
+ /**
+ * When you want to assert values in a array with a given name, e.g.
+ *
+ *
+ *
+ * {@code
+
+ foo
+ bar
+
+ baz
+
+
+ * }
+ *
+ *
+ * The code to check it would look like this:
+ *
+ *
+ * {@code array("list").contains("element").isEqualTo("foo")}
+ * {@code array("list").contains("complexElement").node("param").isEqualTo("baz")}
+ *
+ *
+ * The generated XPaths would be
+ *
+ *
+ * {@code /list/element[text()='foo']}
+ * {@code /list/complexElement[param='baz']}
+ */
+ XmlArrayVerifiable array(String value);
+
+ /**
+ * Equality comparison with String
+ *
+ * @throws IllegalStateException - if XPath is not matched for the parsed XML
+ */
+ XmlVerifiable isEqualTo(String value) throws IllegalStateException;
+
+ /**
+ * Equality comparison with any object
+ *
+ * @throws IllegalStateException - if XPath is not matched for the parsed XML
+ */
+ XmlVerifiable isEqualTo(Object value) throws IllegalStateException;
+
+ /**
+ * Equality comparison with a Number
+ *
+ * @throws IllegalStateException - if XPath is not matched for the parsed XML
+ */
+ XmlVerifiable isEqualTo(Number value) throws IllegalStateException;
+
+ /**
+ * Equality comparison to null
+ *
+ * @throws IllegalStateException - if XPath is not matched for the parsed XML
+ */
+ XmlVerifiable isNull() throws IllegalStateException;
+
+ /**
+ * Regex matching for strings
+ *
+ * @throws IllegalStateException - if XPath is not matched for the parsed XML
+ */
+ XmlVerifiable matches(String value) throws IllegalStateException;
+
+ /**
+ * Equality comparison with a Boolean
+ *
+ * @throws IllegalStateException - if XPath is not matched for the parsed XML
+ */
+ XmlVerifiable isEqualTo(Boolean value) throws IllegalStateException;
+
+ /**
+ * Calling this method will setup the fluent interface to ignore any XPath verification
+ */
+ XmlVerifiable withoutThrowingException();
+
+ /**
+ * Returns current XPath expression
+ */
+ String xPath();
+
+ /**
+ * Checks if the parsed document matches given XPath
+ */
+ void matchesXPath(String xPath);
+
+}
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy
index 73aae01682..5f196240a6 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy
@@ -18,18 +18,19 @@ package org.springframework.cloud.contract.verifier.builder
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
-import org.springframework.cloud.contract.spec.Contract
-import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
-import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier
-import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy
-import org.springframework.cloud.contract.verifier.file.ContractMetadata
-import org.springframework.cloud.contract.verifier.util.SyntaxChecker
import spock.lang.Issue
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy
+import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubVerifier
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
+import org.springframework.cloud.contract.verifier.util.SyntaxChecker
+
class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStubVerifier {
@Shared ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(assertJsonSize: true)
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy
index 2f78b184c0..8bbc00e2fd 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy
@@ -26,7 +26,7 @@ import spock.lang.Specification
import org.springframework.boot.test.rule.OutputCapture
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
-import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier
+import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubVerifier
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
import org.springframework.cloud.contract.verifier.util.SyntaxChecker
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy
index 293755f3d8..d991ba35cc 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy
@@ -17,15 +17,16 @@
package org.springframework.cloud.contract.verifier.builder
import org.junit.Rule
-import org.springframework.boot.test.rule.OutputCapture
-import org.springframework.cloud.contract.spec.Contract
-import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
-import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier
-import org.springframework.cloud.contract.verifier.util.SyntaxChecker
import spock.lang.Issue
import spock.lang.Shared
import spock.lang.Specification
+import org.springframework.boot.test.rule.OutputCapture
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubVerifier
+import org.springframework.cloud.contract.verifier.util.SyntaxChecker
+
class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements WireMockStubVerifier {
@Rule
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy
index 74ca558dea..3cd96db5bf 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy
@@ -184,13 +184,13 @@ class SingleTestGeneratorSpec extends Specification {
and:
asserter(clazz)
where:
- testFramework | order | mode | classStrings | asserter
- JUNIT | 2 | MOCKMVC | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER
- JUNIT | 2 | TestMode.EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER
- JUNIT5 | null | MOCKMVC | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
- JUNIT5 | null | TestMode.EXPLICIT | explicitJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
- SPOCK | 2 | MOCKMVC | spockClassRestAssured3Strings | GROOVY_ASSERTER
- SPOCK | 2 | TestMode.EXPLICIT | explicitSpockRestAssured3ClassStrings | GROOVY_ASSERTER
+ testFramework | order | mode | classStrings | asserter
+ JUNIT | 2 | MOCKMVC | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER
+ JUNIT | 2 | EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER
+ JUNIT5 | null | MOCKMVC | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
+ JUNIT5 | null | EXPLICIT | explicitJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
+ SPOCK | 2 | MOCKMVC | spockClassRestAssured3Strings | GROOVY_ASSERTER
+ SPOCK | 2 | EXPLICIT | explicitSpockRestAssured3ClassStrings | GROOVY_ASSERTER
}
def 'should build test class for #testFramework when the path contains bizarre signs'() {
@@ -214,13 +214,13 @@ class SingleTestGeneratorSpec extends Specification {
size > 0
asserter(new File(newFolder.parent, '/org/springframework/cloud/contract/verifier/tests/com_uscm/dale_api44_spec/_0_1_0_dev_1_uncommitted_d1174dd/' + testName).text)
where:
- testFramework | mode | asserter | testName
- JUNIT | MOCKMVC | JAVA_ASSERTER | 'ContractsTest.java'
- JUNIT | TestMode.EXPLICIT | JAVA_ASSERTER | 'ContractsTest.java'
- JUNIT5 | MOCKMVC | JAVA_ASSERTER | 'ContractsTest.java'
- JUNIT5 | TestMode.EXPLICIT | JAVA_ASSERTER | 'ContractsTest.java'
- SPOCK | MOCKMVC | GROOVY_ASSERTER | 'ContractsSpec.groovy'
- SPOCK | TestMode.EXPLICIT | GROOVY_ASSERTER | 'ContractsSpec.groovy'
+ testFramework | mode | asserter | testName
+ JUNIT | MOCKMVC | JAVA_ASSERTER | 'ContractsTest.java'
+ JUNIT | EXPLICIT | JAVA_ASSERTER | 'ContractsTest.java'
+ JUNIT5 | MOCKMVC | JAVA_ASSERTER | 'ContractsTest.java'
+ JUNIT5 | EXPLICIT | JAVA_ASSERTER | 'ContractsTest.java'
+ SPOCK | MOCKMVC | GROOVY_ASSERTER | 'ContractsSpec.groovy'
+ SPOCK | EXPLICIT | GROOVY_ASSERTER | 'ContractsSpec.groovy'
}
def "should build test class for #testFramework with Rest Assured 2x"() {
@@ -247,13 +247,13 @@ class SingleTestGeneratorSpec extends Specification {
!clazz.contains('io.restassured')
where:
- testFramework | order | mode | classStrings
- JUNIT | 2 | MOCKMVC | mockMvcJUnitRestAssured2ClassStrings
- JUNIT | 2 | TestMode.EXPLICIT | explicitJUnitRestAssured2ClassStrings
- JUNIT5 | null | MOCKMVC | mockMvcJUnit5RestAssured2ClassStrings
- JUNIT5 | null | TestMode.EXPLICIT | explicitJUnit5RestAssured2ClassStrings
- SPOCK | 2 | MOCKMVC | spockClassRestAssured2Strings
- SPOCK | 2 | TestMode.EXPLICIT | explicitSpockRestAssured2ClassStrings
+ testFramework | order | mode | classStrings
+ JUNIT | 2 | MOCKMVC | mockMvcJUnitRestAssured2ClassStrings
+ JUNIT | 2 | EXPLICIT | explicitJUnitRestAssured2ClassStrings
+ JUNIT5 | null | MOCKMVC | mockMvcJUnit5RestAssured2ClassStrings
+ JUNIT5 | null | EXPLICIT | explicitJUnit5RestAssured2ClassStrings
+ SPOCK | 2 | MOCKMVC | spockClassRestAssured2Strings
+ SPOCK | 2 | EXPLICIT | explicitSpockRestAssured2ClassStrings
}
def 'should build test class for #testFramework and mode #mode with two files'() {
@@ -322,17 +322,17 @@ class SingleTestGeneratorSpec extends Specification {
where:
testFramework | mode | classStrings | asserter | textAssertion
JUNIT | MOCKMVC | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tMockMvcRequestSpecification') == 2}
- JUNIT | TestMode.EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tMockMvcRequestSpecification') == 2}
+ JUNIT | EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tMockMvcRequestSpecification') == 2}
JUNIT5 | MOCKMVC | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tMockMvcRequestSpecification') == 2}
- JUNIT5 | TestMode.EXPLICIT | explicitJUnit5RestAssured3ClassStrings | JAVA_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tMockMvcRequestSpecification') == 2}
+ JUNIT5 | EXPLICIT | explicitJUnit5RestAssured3ClassStrings | JAVA_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tMockMvcRequestSpecification') == 2}
SPOCK | MOCKMVC | spockClassRestAssured3Strings | GROOVY_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tdef request') == 2}
- SPOCK | TestMode.EXPLICIT | explicitSpockRestAssured2ClassStrings | GROOVY_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tdef request') == 2}
+ SPOCK | EXPLICIT | explicitSpockRestAssured2ClassStrings | GROOVY_ASSERTER | {String test -> StringUtils.countOccurrencesOf(test, '\t\t\tdef request') == 2}
}
def 'should build JaxRs test class for #testFramework'() {
given:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
- properties.testMode = TestMode.JAXRSCLIENT
+ properties.testMode = JAXRSCLIENT
properties.testFramework =testFramework
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, null, convertAsCollection(new File('/'), file))
contract.ignored >> true
@@ -485,7 +485,7 @@ class SingleTestGeneratorSpec extends Specification {
and:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
properties.testFramework =JUNIT
- properties.testMode = TestMode.EXPLICIT
+ properties.testMode = EXPLICIT
properties.baseClassForTests = 'test.ContextPathTestingBaseClass'
and:
ContractMetadata contract = new ContractMetadata(file.toPath(), false, 1,
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy
index df92befc26..dd8d166582 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy
@@ -16,19 +16,20 @@
package org.springframework.cloud.contract.verifier.builder
+import java.util.regex.Pattern
+
import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.junit.Rule
-import org.springframework.boot.test.rule.OutputCapture
-import org.springframework.cloud.contract.spec.Contract
-import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
-import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier
-import org.springframework.cloud.contract.verifier.util.SyntaxChecker
import spock.lang.Issue
import spock.lang.Shared
import spock.lang.Specification
import spock.util.environment.RestoreSystemProperties
-import java.util.regex.Pattern
+import org.springframework.boot.test.rule.OutputCapture
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubVerifier
+import org.springframework.cloud.contract.verifier.util.SyntaxChecker
/**
* @author Jakub Kubrynski, codearte.io
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/XmlMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/XmlMethodBodyBuilderSpec.groovy
new file mode 100644
index 0000000000..03666c4b95
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/XmlMethodBodyBuilderSpec.groovy
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.builder
+
+import org.junit.Rule
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import org.springframework.boot.test.rule.OutputCapture
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import org.springframework.cloud.contract.verifier.util.SyntaxChecker
+
+/**
+ * @author Olga Maciaszek-Sharma
+ * @since 2.1.0
+ */
+class XmlMethodBodyBuilderSpec extends Specification {
+
+ @Rule
+ OutputCapture capture = new OutputCapture()
+
+ @Shared
+ GeneratedClassDataForMethod classDataForMethod = new GeneratedClassDataForMethod(
+ new SingleTestGenerator.GeneratedClassData("ClassName", "com.example",
+ new File("target/test.java").toPath()),
+ "some_method"
+ )
+
+ @Shared
+ ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(
+ assertJsonSize: true, generatedTestSourcesDir: new File("."),
+ generatedTestResourcesDir: new File(".")
+ )
+
+ @Unroll
+ def 'should generate correct verification from xml with body matchers [#methodBuilderName]'() {
+ given:
+ Contract contractDsl =
+ // tag::xmlgroovy[]
+ Contract.make {
+ request {
+ method GET()
+ urlPath '/get'
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ response {
+ status(OK())
+ headers {
+ contentType(applicationXml())
+ }
+ body """
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+foo
+"""
+ bodyMatchers {
+ xPath('/test/duck/text()', byRegex("[0-9]{3}"))
+ xPath('/test/duck/text()', byCommand('test($it)'))
+ xPath('/test/duck/xxx', byNull())
+ xPath('/test/duck/text()', byEquality())
+ xPath('/test/alpha/text()', byRegex(onlyAlphaUnicode()))
+ xPath('/test/alpha/text()', byEquality())
+ xPath('/test/number/text()', byRegex(number()))
+ xPath('/test/date/text()', byDate())
+ xPath('/test/dateTime/text()', byTimestamp())
+ xPath('/test/time/text()', byTime())
+ xPath('/test/*/complex/text()', byEquality())
+ xPath('/test/duck/@type', byEquality())
+ }
+ }
+ }
+ // end::xmlgroovy[]
+ MethodBodyBuilder builder = methodBuilder(contractDsl)
+ BlockBuilder blockBuilder = new BlockBuilder(' ')
+ when:
+ builder.appendTo(blockBuilder)
+ def test = blockBuilder.toString()
+ then:
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/list/elem/text()")).isEqualTo("abc")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/list/elem[2]/text()")).isEqualTo("def")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/list/elem[3]/text()")).isEqualTo("ghi")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/aBoolean/text()")).isEqualTo("true")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/valueWithoutAMatcher/text()")).isEqualTo("foo")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/duck/text()")).matches("[0-9]{3}")')
+ test.contains('test("123")')
+ test.contains('assertThat(nodeFromXPath(parsedXml, "/test/duck/xxx")).isNull()')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/duck/text()")).isEqualTo("123")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/alpha/text()")).matches("[\\\\p{L}]*")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/alpha/text()")).isEqualTo("abc")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/number/text()")).matches("-?(\\\\d*\\\\.\\\\d+|\\\\d+)")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/date/text()")).matches("(\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/dateTime/text()")).matches("([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/time/text()")).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/*/complex/text()")).isEqualTo("foo")')
+ test.contains('assertThat(valueFromXPath(parsedXml, "/test/duck/@type")).isEqualTo("xtype")')
+ and:
+ SyntaxChecker.tryToCompile(methodBuilderName, blockBuilder.
+ toString())
+ where:
+ methodBuilderName | methodBuilder
+ HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties, classDataForMethod) }
+ MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) }
+ JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties, classDataForMethod) }
+ JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) }
+ WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) }
+ }
+
+ def 'should throw exception for verification by type'() {
+ given:
+ Contract contractDsl = Contract.make {
+ request {
+ method GET()
+ urlPath '/get'
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ response {
+ status(OK())
+ headers {
+ contentType(applicationXml())
+ }
+ body """
+
+123
+"""
+ bodyMatchers {
+ xPath('/test/duck/text()', byType())
+ }
+ }
+ }
+ MethodBodyBuilder builder = new MockMvcJUnitMethodBodyBuilder(contractDsl, properties, classDataForMethod)
+ BlockBuilder blockBuilder = new BlockBuilder(' ')
+ when:
+ builder.appendTo(blockBuilder)
+ blockBuilder.toString()
+ then:
+ thrown UnsupportedOperationException
+ }
+}
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/YamlMockMvcMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/YamlMockMvcMethodBodyBuilderSpec.groovy
index 2bd0b654b4..1d573c539e 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/YamlMockMvcMethodBodyBuilderSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/YamlMockMvcMethodBodyBuilderSpec.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2018 the original author or authors.
+ * Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,17 +18,18 @@ package org.springframework.cloud.contract.verifier.builder
import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.junit.Rule
-import org.springframework.boot.test.rule.OutputCapture
-import org.springframework.cloud.contract.spec.Contract
-import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
-import org.springframework.cloud.contract.verifier.converter.YamlContractConverter
-import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier
-import org.springframework.cloud.contract.verifier.util.SyntaxChecker
import spock.lang.Issue
import spock.lang.Shared
import spock.lang.Specification
import spock.util.environment.RestoreSystemProperties
+import org.springframework.boot.test.rule.OutputCapture
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import org.springframework.cloud.contract.verifier.converter.YamlContractConverter
+import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubVerifier
+import org.springframework.cloud.contract.verifier.util.SyntaxChecker
+
/**
* @author Jakub Kubrynski, codearte.io
* @author Tim Ysewyn
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/DslToYamlContractConverterSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/DslToYamlContractConverterSpec.groovy
index f1faba9202..c29f23383c 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/DslToYamlContractConverterSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/DslToYamlContractConverterSpec.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2018 the original author or authors.
+ * Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,9 +23,30 @@ import org.springframework.cloud.contract.spec.Contract
/**
* @author Marcin Grzejszczak
* @author Tim Ysewyn
+ * @author Olga Maciaszek-Sharma
*/
class DslToYamlContractConverterSpec extends Specification {
+ String xmlContractBody = '''
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+
+'''
+
YamlContractConverter converter = new YamlContractConverter()
def "should convert rest DSL to YAML"() {
@@ -466,4 +487,90 @@ class DslToYamlContractConverterSpec extends Specification {
]
yamlContract.response.status == 200
}
+
+ def "should convert REST XML DSL to YAML"() {
+ given:
+ List contracts = [Contract.make {
+ request {
+ method 'GET'
+ url '/get'
+ headers {
+ contentType(applicationXml())
+ }
+ body """
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+"""
+ bodyMatchers {
+ xPath('/test/duck/text()', byRegex("[0-9]{3}"))
+ }
+ }
+ response {
+ status(OK())
+ body """
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+"""
+ bodyMatchers {
+ xPath('/test/duck/xxx', byNull())
+ }
+ }
+ }]
+ when:
+ Collection yamlContracts = converter.convertTo(contracts)
+ then:
+ yamlContracts.size() == 1
+ YamlContract yamlContract = yamlContracts.first()
+ yamlContract.request.method == 'GET'
+ yamlContract.request.url == '/get'
+ yamlContract.request.body.replaceAll("\n", "")
+ .replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "")
+ .replaceAll(' ', '')
+ yamlContract.request.headers == [
+ "Content-Type": "application/xml"
+ ]
+ yamlContract.request.matchers.body == [
+ new YamlContract.BodyStubMatcher(
+ path: '/test/duck/text()',
+ type: YamlContract.StubMatcherType.by_regex,
+ value: '[0-9]{3}'),
+ ]
+ yamlContract.response.status == 200
+ yamlContract.response.body.replaceAll("\n", "")
+ .replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "")
+ .replaceAll(' ', '')
+ yamlContract.response.matchers.body == [
+ new YamlContract.BodyTestMatcher(
+ path: '/test/duck/xxx',
+ type: YamlContract.TestMatcherType.by_null)
+ ]
+ }
}
\ No newline at end of file
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy
index a89997abb6..55fe2d679b 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2018 the original author or authors.
+ * Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,16 +26,26 @@ import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.spec.internal.MatchingStrategy
-import org.springframework.cloud.contract.spec.internal.MatchingType
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.QueryParameters
import org.springframework.cloud.contract.spec.internal.RegexPatterns
import org.springframework.cloud.contract.spec.internal.Url
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
import org.springframework.cloud.contract.verifier.util.MapConverter
+
+import static org.springframework.cloud.contract.spec.internal.MatchingType.COMMAND
+import static org.springframework.cloud.contract.spec.internal.MatchingType.DATE
+import static org.springframework.cloud.contract.spec.internal.MatchingType.EQUALITY
+import static org.springframework.cloud.contract.spec.internal.MatchingType.NULL
+import static org.springframework.cloud.contract.spec.internal.MatchingType.REGEX
+import static org.springframework.cloud.contract.spec.internal.MatchingType.TIME
+import static org.springframework.cloud.contract.spec.internal.MatchingType.TIMESTAMP
+import static org.springframework.cloud.contract.spec.internal.MatchingType.TYPE
+
/**
* @author Marcin Grzejszczak
* @author Tim Ysewyn
+ * @author Olga Maciaszek-Sharma
*/
class YamlContractConverterSpec extends Specification {
@@ -69,7 +79,29 @@ class YamlContractConverterSpec extends Specification {
File groovyBytes = new File(groovyBytesUrl.toURI())
URL ymlMessagingBytesUrl = YamlContractConverterSpec.getResource("/yml/contract_messaging_pdf.yml")
File ymlMessagingBytes = new File(ymlMessagingBytesUrl.toURI())
+ URL ymlRestXmlFile = YamlContractConverterSpec.
+ getResource("/yml/contract_rest_xml.yml")
+ File ymlRestXml = new File(ymlRestXmlFile.toURI())
YamlContractConverter converter = new YamlContractConverter()
+ String xmlContractBody = '''
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+
+'''
def "should convert YAML with Cookies to DSL"() {
given:
@@ -122,9 +154,9 @@ class YamlContractConverterSpec extends Specification {
contract.request.headers.entries.find { it.name == "fooReq" &&
it.serverValue == "baz" }
contract.request.body.clientValue == [foo: "bar"]
- contract.request.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.foo'
- contract.request.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.request.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
+ contract.request.bodyMatchers.matchers[0].path() == '$.foo'
+ contract.request.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.request.bodyMatchers.matchers[0].value().pattern() == 'bar'
and:
contract.response.status.clientValue == 200
if (yamlFile == ymlWithRest) contract.response.delay.clientValue == 1000 else !contract.response.delay
@@ -135,15 +167,15 @@ class YamlContractConverterSpec extends Specification {
contract.response.headers.entries.find { it.name == "fooRes" &&
it.clientValue == "baz" }
contract.response.body.clientValue == [foo2: "bar", foo3: "baz", nullValue: null]
- contract.response.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.foo2'
- contract.response.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.response.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
- contract.response.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.foo3'
- contract.response.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.COMMAND
- contract.response.bodyMatchers.jsonPathRegexMatchers[1].value() == new ExecutionProperty('executeMe($it)')
- contract.response.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.nullValue'
- contract.response.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.NULL
- contract.response.bodyMatchers.jsonPathRegexMatchers[2].value() == null
+ contract.response.bodyMatchers.matchers[0].path() == '$.foo2'
+ contract.response.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.response.bodyMatchers.matchers[0].value().pattern() == 'bar'
+ contract.response.bodyMatchers.matchers[1].path() == '$.foo3'
+ contract.response.bodyMatchers.matchers[1].matchingType() == COMMAND
+ contract.response.bodyMatchers.matchers[1].value() == new ExecutionProperty('executeMe($it)')
+ contract.response.bodyMatchers.matchers[2].path() == '$.nullValue'
+ contract.response.bodyMatchers.matchers[2].matchingType() == NULL
+ contract.response.bodyMatchers.matchers[2].value() == null
where:
yamlFile << [ymlWithRest, ymlWithRest2, ymlWithRest3]
}
@@ -214,93 +246,93 @@ class YamlContractConverterSpec extends Specification {
MatchingStrategy.Type.MATCHING, "John.*")
assertQueryParam(queryParameters, "hello", true,
MatchingStrategy.Type.ABSENT, null)
- contract.request.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
- contract.request.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.request.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
- contract.request.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
- contract.request.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
- contract.request.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
- contract.request.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
- contract.request.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
- contract.request.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
- contract.request.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
- contract.request.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
- contract.request.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
- contract.request.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
- contract.request.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
- contract.request.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
- contract.request.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
- contract.request.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
- contract.request.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
- contract.request.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
- contract.request.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
- contract.request.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
- contract.request.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
- contract.request.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
- contract.request.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
- contract.request.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
- contract.request.bodyMatchers.jsonPathRegexMatchers[9].path() == "\$.['key'].['complex.key']"
- contract.request.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.EQUALITY
- contract.request.bodyMatchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin'
- contract.request.bodyMatchers.jsonPathRegexMatchers[10].matchingType() == MatchingType.TYPE
- contract.request.bodyMatchers.jsonPathRegexMatchers[10].minTypeOccurrence() == 1
- contract.request.bodyMatchers.jsonPathRegexMatchers[11].path() == '$.valueWithMax'
- contract.request.bodyMatchers.jsonPathRegexMatchers[11].matchingType() == MatchingType.TYPE
- contract.request.bodyMatchers.jsonPathRegexMatchers[11].maxTypeOccurrence() == 3
- contract.request.bodyMatchers.jsonPathRegexMatchers[12].path() == '$.valueWithMinMax'
- contract.request.bodyMatchers.jsonPathRegexMatchers[12].matchingType() == MatchingType.TYPE
- contract.request.bodyMatchers.jsonPathRegexMatchers[12].minTypeOccurrence() == 1
- contract.request.bodyMatchers.jsonPathRegexMatchers[12].maxTypeOccurrence() == 3
+ contract.request.bodyMatchers.matchers[0].path() == '$.duck'
+ contract.request.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.request.bodyMatchers.matchers[0].value().pattern() == '[0-9]{3}'
+ contract.request.bodyMatchers.matchers[1].path() == '$.duck'
+ contract.request.bodyMatchers.matchers[1].matchingType() == EQUALITY
+ contract.request.bodyMatchers.matchers[2].path() == '$.alpha'
+ contract.request.bodyMatchers.matchers[2].matchingType() == REGEX
+ contract.request.bodyMatchers.matchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
+ contract.request.bodyMatchers.matchers[3].path() == '$.alpha'
+ contract.request.bodyMatchers.matchers[3].matchingType() == EQUALITY
+ contract.request.bodyMatchers.matchers[4].path() == '$.number'
+ contract.request.bodyMatchers.matchers[4].matchingType() == REGEX
+ contract.request.bodyMatchers.matchers[4].value().pattern() == patterns.number().pattern()
+ contract.request.bodyMatchers.matchers[5].path() == '$.aBoolean'
+ contract.request.bodyMatchers.matchers[5].matchingType() == REGEX
+ contract.request.bodyMatchers.matchers[5].value().pattern() == patterns.anyBoolean().pattern()
+ contract.request.bodyMatchers.matchers[6].path() == '$.date'
+ contract.request.bodyMatchers.matchers[6].matchingType() == DATE
+ contract.request.bodyMatchers.matchers[6].value().pattern() == patterns.isoDate().pattern()
+ contract.request.bodyMatchers.matchers[7].path() == '$.dateTime'
+ contract.request.bodyMatchers.matchers[7].matchingType() == TIMESTAMP
+ contract.request.bodyMatchers.matchers[7].value().pattern() == patterns.isoDateTime().pattern()
+ contract.request.bodyMatchers.matchers[8].path() == '$.time'
+ contract.request.bodyMatchers.matchers[8].matchingType() == TIME
+ contract.request.bodyMatchers.matchers[8].value().pattern() == patterns.isoTime().pattern()
+ contract.request.bodyMatchers.matchers[9].path() == "\$.['key'].['complex.key']"
+ contract.request.bodyMatchers.matchers[9].matchingType() == EQUALITY
+ contract.request.bodyMatchers.matchers[10].path() == '$.valueWithMin'
+ contract.request.bodyMatchers.matchers[10].matchingType() == TYPE
+ contract.request.bodyMatchers.matchers[10].minTypeOccurrence() == 1
+ contract.request.bodyMatchers.matchers[11].path() == '$.valueWithMax'
+ contract.request.bodyMatchers.matchers[11].matchingType() == TYPE
+ contract.request.bodyMatchers.matchers[11].maxTypeOccurrence() == 3
+ contract.request.bodyMatchers.matchers[12].path() == '$.valueWithMinMax'
+ contract.request.bodyMatchers.matchers[12].matchingType() == TYPE
+ contract.request.bodyMatchers.matchers[12].minTypeOccurrence() == 1
+ contract.request.bodyMatchers.matchers[12].maxTypeOccurrence() == 3
contract.request.cookies.entries.find { it.key == "foo" }.clientValue instanceof Pattern
contract.request.cookies.entries.find { it.key == "bar" }.serverValue == new ExecutionProperty('equals($it)')
and:
contract.response.status.clientValue == 200
- contract.response.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
- contract.response.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.response.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
- contract.response.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
- contract.response.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
- contract.response.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
- contract.response.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
- contract.response.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
- contract.response.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
- contract.response.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
- contract.response.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
- contract.response.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
- contract.response.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
- contract.response.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
- contract.response.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
- contract.response.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
- contract.response.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
- contract.response.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
- contract.response.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
- contract.response.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
- contract.response.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
- contract.response.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
- contract.response.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
- contract.response.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
- contract.response.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
- contract.response.bodyMatchers.jsonPathRegexMatchers[9].path() == '$.valueWithTypeMatch'
- contract.response.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.TYPE
- contract.response.bodyMatchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin'
- contract.response.bodyMatchers.jsonPathRegexMatchers[10].matchingType() == MatchingType.TYPE
- contract.response.bodyMatchers.jsonPathRegexMatchers[10].minTypeOccurrence() == 1
- contract.response.bodyMatchers.jsonPathRegexMatchers[11].path() == '$.valueWithMax'
- contract.response.bodyMatchers.jsonPathRegexMatchers[11].matchingType() == MatchingType.TYPE
- contract.response.bodyMatchers.jsonPathRegexMatchers[11].maxTypeOccurrence() == 3
- contract.response.bodyMatchers.jsonPathRegexMatchers[12].path() == '$.valueWithMinMax'
- contract.response.bodyMatchers.jsonPathRegexMatchers[12].matchingType() == MatchingType.TYPE
- contract.response.bodyMatchers.jsonPathRegexMatchers[12].minTypeOccurrence() == 1
- contract.response.bodyMatchers.jsonPathRegexMatchers[12].maxTypeOccurrence() == 3
- contract.response.bodyMatchers.jsonPathRegexMatchers[13].path() == '$.valueWithMinEmpty'
- contract.response.bodyMatchers.jsonPathRegexMatchers[13].matchingType() == MatchingType.TYPE
- contract.response.bodyMatchers.jsonPathRegexMatchers[13].minTypeOccurrence() == 0
- contract.response.bodyMatchers.jsonPathRegexMatchers[14].path() == '$.valueWithMaxEmpty'
- contract.response.bodyMatchers.jsonPathRegexMatchers[14].matchingType() == MatchingType.TYPE
- contract.response.bodyMatchers.jsonPathRegexMatchers[14].maxTypeOccurrence() == 0
- contract.response.bodyMatchers.jsonPathRegexMatchers[15].path() == '$.duck'
- contract.response.bodyMatchers.jsonPathRegexMatchers[15].matchingType() == MatchingType.COMMAND
- contract.response.bodyMatchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)')
+ contract.response.bodyMatchers.matchers[0].path() == '$.duck'
+ contract.response.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.response.bodyMatchers.matchers[0].value().pattern() == '[0-9]{3}'
+ contract.response.bodyMatchers.matchers[1].path() == '$.duck'
+ contract.response.bodyMatchers.matchers[1].matchingType() == EQUALITY
+ contract.response.bodyMatchers.matchers[2].path() == '$.alpha'
+ contract.response.bodyMatchers.matchers[2].matchingType() == REGEX
+ contract.response.bodyMatchers.matchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
+ contract.response.bodyMatchers.matchers[3].path() == '$.alpha'
+ contract.response.bodyMatchers.matchers[3].matchingType() == EQUALITY
+ contract.response.bodyMatchers.matchers[4].path() == '$.number'
+ contract.response.bodyMatchers.matchers[4].matchingType() == REGEX
+ contract.response.bodyMatchers.matchers[4].value().pattern() == patterns.number().pattern()
+ contract.response.bodyMatchers.matchers[5].path() == '$.aBoolean'
+ contract.response.bodyMatchers.matchers[5].matchingType() == REGEX
+ contract.response.bodyMatchers.matchers[5].value().pattern() == patterns.anyBoolean().pattern()
+ contract.response.bodyMatchers.matchers[6].path() == '$.date'
+ contract.response.bodyMatchers.matchers[6].matchingType() == DATE
+ contract.response.bodyMatchers.matchers[6].value().pattern() == patterns.isoDate().pattern()
+ contract.response.bodyMatchers.matchers[7].path() == '$.dateTime'
+ contract.response.bodyMatchers.matchers[7].matchingType() == TIMESTAMP
+ contract.response.bodyMatchers.matchers[7].value().pattern() == patterns.isoDateTime().pattern()
+ contract.response.bodyMatchers.matchers[8].path() == '$.time'
+ contract.response.bodyMatchers.matchers[8].matchingType() == TIME
+ contract.response.bodyMatchers.matchers[8].value().pattern() == patterns.isoTime().pattern()
+ contract.response.bodyMatchers.matchers[9].path() == '$.valueWithTypeMatch'
+ contract.response.bodyMatchers.matchers[9].matchingType() == TYPE
+ contract.response.bodyMatchers.matchers[10].path() == '$.valueWithMin'
+ contract.response.bodyMatchers.matchers[10].matchingType() == TYPE
+ contract.response.bodyMatchers.matchers[10].minTypeOccurrence() == 1
+ contract.response.bodyMatchers.matchers[11].path() == '$.valueWithMax'
+ contract.response.bodyMatchers.matchers[11].matchingType() == TYPE
+ contract.response.bodyMatchers.matchers[11].maxTypeOccurrence() == 3
+ contract.response.bodyMatchers.matchers[12].path() == '$.valueWithMinMax'
+ contract.response.bodyMatchers.matchers[12].matchingType() == TYPE
+ contract.response.bodyMatchers.matchers[12].minTypeOccurrence() == 1
+ contract.response.bodyMatchers.matchers[12].maxTypeOccurrence() == 3
+ contract.response.bodyMatchers.matchers[13].path() == '$.valueWithMinEmpty'
+ contract.response.bodyMatchers.matchers[13].matchingType() == TYPE
+ contract.response.bodyMatchers.matchers[13].minTypeOccurrence() == 0
+ contract.response.bodyMatchers.matchers[14].path() == '$.valueWithMaxEmpty'
+ contract.response.bodyMatchers.matchers[14].matchingType() == TYPE
+ contract.response.bodyMatchers.matchers[14].maxTypeOccurrence() == 0
+ contract.response.bodyMatchers.matchers[15].path() == '$.duck'
+ contract.response.bodyMatchers.matchers[15].matchingType() == COMMAND
+ contract.response.bodyMatchers.matchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)')
}
protected Object assertQueryParam(QueryParameters queryParameters, String queryParamName, Object serverValue,
@@ -326,80 +358,80 @@ class YamlContractConverterSpec extends Specification {
RegexPatterns patterns = new RegexPatterns()
contract.input.messageHeaders.entries.find { it.name == "contentType" &&
((Pattern) it.clientValue).pattern == "application/json.*" && it.serverValue == "application/json" }
- contract.input.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
- contract.input.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.input.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
- contract.input.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
- contract.input.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
- contract.input.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
- contract.input.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
- contract.input.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
- contract.input.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
- contract.input.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
- contract.input.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
- contract.input.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
- contract.input.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
- contract.input.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
- contract.input.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
- contract.input.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
- contract.input.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
- contract.input.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
- contract.input.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
- contract.input.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
- contract.input.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
- contract.input.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
- contract.input.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
- contract.input.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
- contract.input.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
- contract.input.bodyMatchers.jsonPathRegexMatchers[9].path() == "\$.['key'].['complex.key']"
- contract.input.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.EQUALITY
+ contract.input.bodyMatchers.matchers[0].path() == '$.duck'
+ contract.input.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.input.bodyMatchers.matchers[0].value().pattern() == '[0-9]{3}'
+ contract.input.bodyMatchers.matchers[1].path() == '$.duck'
+ contract.input.bodyMatchers.matchers[1].matchingType() == EQUALITY
+ contract.input.bodyMatchers.matchers[2].path() == '$.alpha'
+ contract.input.bodyMatchers.matchers[2].matchingType() == REGEX
+ contract.input.bodyMatchers.matchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
+ contract.input.bodyMatchers.matchers[3].path() == '$.alpha'
+ contract.input.bodyMatchers.matchers[3].matchingType() == EQUALITY
+ contract.input.bodyMatchers.matchers[4].path() == '$.number'
+ contract.input.bodyMatchers.matchers[4].matchingType() == REGEX
+ contract.input.bodyMatchers.matchers[4].value().pattern() == patterns.number().pattern()
+ contract.input.bodyMatchers.matchers[5].path() == '$.aBoolean'
+ contract.input.bodyMatchers.matchers[5].matchingType() == REGEX
+ contract.input.bodyMatchers.matchers[5].value().pattern() == patterns.anyBoolean().pattern()
+ contract.input.bodyMatchers.matchers[6].path() == '$.date'
+ contract.input.bodyMatchers.matchers[6].matchingType() == DATE
+ contract.input.bodyMatchers.matchers[6].value().pattern() == patterns.isoDate().pattern()
+ contract.input.bodyMatchers.matchers[7].path() == '$.dateTime'
+ contract.input.bodyMatchers.matchers[7].matchingType() == TIMESTAMP
+ contract.input.bodyMatchers.matchers[7].value().pattern() == patterns.isoDateTime().pattern()
+ contract.input.bodyMatchers.matchers[8].path() == '$.time'
+ contract.input.bodyMatchers.matchers[8].matchingType() == TIME
+ contract.input.bodyMatchers.matchers[8].value().pattern() == patterns.isoTime().pattern()
+ contract.input.bodyMatchers.matchers[9].path() == "\$.['key'].['complex.key']"
+ contract.input.bodyMatchers.matchers[9].matchingType() == EQUALITY
and:
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[9].path() == '$.valueWithTypeMatch'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.TYPE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[10].matchingType() == MatchingType.TYPE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[10].minTypeOccurrence() == 1
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[11].path() == '$.valueWithMax'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[11].matchingType() == MatchingType.TYPE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[11].maxTypeOccurrence() == 3
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[12].path() == '$.valueWithMinMax'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[12].matchingType() == MatchingType.TYPE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[12].minTypeOccurrence() == 1
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[12].maxTypeOccurrence() == 3
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[13].path() == '$.valueWithMinEmpty'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[13].matchingType() == MatchingType.TYPE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[13].minTypeOccurrence() == 0
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[14].path() == '$.valueWithMaxEmpty'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[14].matchingType() == MatchingType.TYPE
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[14].maxTypeOccurrence() == 0
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[15].path() == '$.duck'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[15].matchingType() == MatchingType.COMMAND
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)')
+ contract.outputMessage.bodyMatchers.matchers[0].path() == '$.duck'
+ contract.outputMessage.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.outputMessage.bodyMatchers.matchers[0].value().pattern() == '[0-9]{3}'
+ contract.outputMessage.bodyMatchers.matchers[1].path() == '$.duck'
+ contract.outputMessage.bodyMatchers.matchers[1].matchingType() == EQUALITY
+ contract.outputMessage.bodyMatchers.matchers[2].path() == '$.alpha'
+ contract.outputMessage.bodyMatchers.matchers[2].matchingType() == REGEX
+ contract.outputMessage.bodyMatchers.matchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
+ contract.outputMessage.bodyMatchers.matchers[3].path() == '$.alpha'
+ contract.outputMessage.bodyMatchers.matchers[3].matchingType() == EQUALITY
+ contract.outputMessage.bodyMatchers.matchers[4].path() == '$.number'
+ contract.outputMessage.bodyMatchers.matchers[4].matchingType() == REGEX
+ contract.outputMessage.bodyMatchers.matchers[4].value().pattern() == patterns.number().pattern()
+ contract.outputMessage.bodyMatchers.matchers[5].path() == '$.aBoolean'
+ contract.outputMessage.bodyMatchers.matchers[5].matchingType() == REGEX
+ contract.outputMessage.bodyMatchers.matchers[5].value().pattern() == patterns.anyBoolean().pattern()
+ contract.outputMessage.bodyMatchers.matchers[6].path() == '$.date'
+ contract.outputMessage.bodyMatchers.matchers[6].matchingType() == DATE
+ contract.outputMessage.bodyMatchers.matchers[6].value().pattern() == patterns.isoDate().pattern()
+ contract.outputMessage.bodyMatchers.matchers[7].path() == '$.dateTime'
+ contract.outputMessage.bodyMatchers.matchers[7].matchingType() == TIMESTAMP
+ contract.outputMessage.bodyMatchers.matchers[7].value().pattern() == patterns.isoDateTime().pattern()
+ contract.outputMessage.bodyMatchers.matchers[8].path() == '$.time'
+ contract.outputMessage.bodyMatchers.matchers[8].matchingType() == TIME
+ contract.outputMessage.bodyMatchers.matchers[8].value().pattern() == patterns.isoTime().pattern()
+ contract.outputMessage.bodyMatchers.matchers[9].path() == '$.valueWithTypeMatch'
+ contract.outputMessage.bodyMatchers.matchers[9].matchingType() == TYPE
+ contract.outputMessage.bodyMatchers.matchers[10].path() == '$.valueWithMin'
+ contract.outputMessage.bodyMatchers.matchers[10].matchingType() == TYPE
+ contract.outputMessage.bodyMatchers.matchers[10].minTypeOccurrence() == 1
+ contract.outputMessage.bodyMatchers.matchers[11].path() == '$.valueWithMax'
+ contract.outputMessage.bodyMatchers.matchers[11].matchingType() == TYPE
+ contract.outputMessage.bodyMatchers.matchers[11].maxTypeOccurrence() == 3
+ contract.outputMessage.bodyMatchers.matchers[12].path() == '$.valueWithMinMax'
+ contract.outputMessage.bodyMatchers.matchers[12].matchingType() == TYPE
+ contract.outputMessage.bodyMatchers.matchers[12].minTypeOccurrence() == 1
+ contract.outputMessage.bodyMatchers.matchers[12].maxTypeOccurrence() == 3
+ contract.outputMessage.bodyMatchers.matchers[13].path() == '$.valueWithMinEmpty'
+ contract.outputMessage.bodyMatchers.matchers[13].matchingType() == TYPE
+ contract.outputMessage.bodyMatchers.matchers[13].minTypeOccurrence() == 0
+ contract.outputMessage.bodyMatchers.matchers[14].path() == '$.valueWithMaxEmpty'
+ contract.outputMessage.bodyMatchers.matchers[14].matchingType() == TYPE
+ contract.outputMessage.bodyMatchers.matchers[14].maxTypeOccurrence() == 0
+ contract.outputMessage.bodyMatchers.matchers[15].path() == '$.duck'
+ contract.outputMessage.bodyMatchers.matchers[15].matchingType() == COMMAND
+ contract.outputMessage.bodyMatchers.matchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)')
}
def "should convert YAML with REST with response from request"() {
@@ -459,9 +491,9 @@ class YamlContractConverterSpec extends Specification {
contract.input.messageHeaders.entries.find { it.name == "foo" &&
((Pattern) it.clientValue).pattern == "bar" && it.serverValue == "bar" }
contract.input.messageBody.clientValue == [foo: "bar"]
- contract.input.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.bar'
- contract.input.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.input.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
+ contract.input.bodyMatchers.matchers[0].path() == '$.bar'
+ contract.input.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.input.bodyMatchers.matchers[0].value().pattern() == 'bar'
and:
contract.outputMessage.assertThat.toString() == "baz()"
contract.outputMessage.headers.entries.find { it.name == "foo2" &&
@@ -471,12 +503,12 @@ class YamlContractConverterSpec extends Specification {
contract.outputMessage.headers.entries.find { it.name == "fooRes" &&
it.clientValue == "baz" }
contract.outputMessage.body.clientValue == [foo2: "bar", foo3: "baz"]
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.foo2'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.foo3'
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.COMMAND
- contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].value() == new ExecutionProperty('executeMe($it)')
+ contract.outputMessage.bodyMatchers.matchers[0].path() == '$.foo2'
+ contract.outputMessage.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.outputMessage.bodyMatchers.matchers[0].value().pattern() == 'bar'
+ contract.outputMessage.bodyMatchers.matchers[1].path() == '$.foo3'
+ contract.outputMessage.bodyMatchers.matchers[1].matchingType() == COMMAND
+ contract.outputMessage.bodyMatchers.matchers[1].value() == new ExecutionProperty('executeMe($it)')
}
def "should convert YAML with messaging triggered by a method to DSL"() {
@@ -1184,4 +1216,55 @@ ignored: false
yamlContract.request.body == null
yamlContract.request.bodyFromFileAsBytes != null
}
+
+ def "should convert REST YAML with XML request and response to DSL"() {
+ given:
+ assert converter.isAccepted(ymlRestXml)
+ when:
+ Collection contracts = converter.convertFrom(ymlRestXml)
+ then:
+ contracts.size() == 1
+ Contract contract = contracts.first()
+ RegexPatterns patterns = new RegexPatterns()
+ contract.request.headers.entries.find({
+ it.name == 'Content-Type' && it.clientValue == "application/xml" && it.serverValue == "application/xml"
+ })
+ contract.request.bodyMatchers.matchers[0].path() == '/test/duck/text()'
+ contract.request.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.request.bodyMatchers.matchers[0].value().pattern() == '[0-9]{10}'
+ contract.request.bodyMatchers.matchers[1].path() == '/test/duck/text()'
+ contract.request.bodyMatchers.matchers[1].matchingType() == EQUALITY
+ contract.request.bodyMatchers.matchers[2].path() == '/test/time/text()'
+ contract.request.bodyMatchers.matchers[2].matchingType() == TIME
+ contract.request.bodyMatchers.matchers[2]
+ .value().pattern() == patterns.isoTime().pattern()
+ contract.request.body.clientValue.replaceAll("\n", "").
+ replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "").
+ replaceAll(' ', '')
+ contract.request.body.serverValue.replaceAll("\n", "").
+ replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "").
+ replaceAll(' ', '')
+ and:
+ contract.response.bodyMatchers.matchers[0].path() == '/test/duck/text()'
+ contract.response.bodyMatchers.matchers[0].matchingType() == REGEX
+ contract.response.bodyMatchers.matchers[0].value().pattern() == '[0-9]{10}'
+ contract.response.bodyMatchers.matchers[1].path() == '/test/duck/text()'
+ contract.response.bodyMatchers.matchers[1].matchingType() == COMMAND
+ contract.response.bodyMatchers.matchers[1].
+ value().executionCommand == 'test($it)'
+ contract.response.bodyMatchers.matchers[2].path() == '/test/duck/xxx'
+ contract.response.bodyMatchers.matchers[2].matchingType() == NULL
+ contract.response.bodyMatchers.matchers[3].path() == '/test/duck/text()'
+ contract.response.bodyMatchers.matchers[3].matchingType() == EQUALITY
+ contract.response.bodyMatchers.matchers[4].path() == '/test/time/text()'
+ contract.response.bodyMatchers.matchers[4].matchingType() == TIME
+ contract.response.bodyMatchers.matchers[4]
+ .value().pattern() == patterns.isoTime().pattern()
+ contract.response.body.clientValue.replaceAll("\n", "")
+ .replaceAll(' ', '') == xmlContractBody
+ .replaceAll("\n", "").replaceAll(' ', '')
+ contract.response.body.serverValue.replaceAll("\n", "")
+ .replaceAll(' ', '') == xmlContractBody
+ .replaceAll("\n", "").replaceAll(' ', '')
+ }
}
\ No newline at end of file
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockGroovyDslSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy
similarity index 96%
rename from spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockGroovyDslSpec.groovy
rename to spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy
index 27cd2d7fb3..2a1dd82006 100755
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockGroovyDslSpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.contract.verifier.dsl
+package org.springframework.cloud.contract.verifier.dsl.wiremock
import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.core.WireMockConfiguration
@@ -29,8 +29,6 @@ import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsEscapeHelper
import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsJsonPathHelper
-import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubMapping
-import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.cloud.contract.verifier.util.AssertionUtil
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
@@ -392,7 +390,7 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
stubMappingIsValidWireMockStub(json)
}
- def 'should use equalToXml when content type ends with xml'() {
+ def 'should use xml matchers when content type ends with xml'() {
given:
org.springframework.cloud.contract.spec.Contract groovyDsl = org.springframework.cloud.contract.spec.Contract.make {
request {
@@ -414,31 +412,43 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
then:
AssertionUtil.assertThatJsonsAreEqual(('''
{
- "request": {
- "method": "GET",
- "url": "/users",
- "headers": {
- "Content-Type": {
- "equalTo": "customtype/xml"
- }
- },
- "bodyPatterns": [
- {
- "equalToXml":"Jozo<test>"
- }
- ]
- },
- "response": {
- "status": 200,
- "transformers" : [ "response-template", "foo-transformer" ]
- }
- }
+ "request": {
+ "url": "/users",
+ "method": "GET",
+ "headers": {
+ "Content-Type": {
+ "equalTo": "customtype/xml"
+ }
+ },
+ "bodyPatterns": [
+ {
+ "matchesXPath": {
+ "expression": "/foo/name/text()",
+ "equalTo": "Jozo"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/foo/jobId/text()",
+ "equalTo": ""
+ }
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "transformers": [
+ "response-template",
+ "foo-transformer"
+ ]
+ }
+}
'''), json)
and:
stubMappingIsValidWireMockStub(json)
}
- def 'should use equalToXml when content type is parsable xml'() {
+ def 'should use xml matchers when content type is parsable xml'() {
given:
org.springframework.cloud.contract.spec.Contract groovyDsl = org.springframework.cloud.contract.spec.Contract.make {
request {
@@ -456,21 +466,33 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
String json = toWireMockClientJsonStub(groovyDsl)
then:
AssertionUtil.assertThatJsonsAreEqual(('''
- {
- "request": {
- "method": "GET",
- "url": "/users",
- "bodyPatterns": [
- {
- "equalToXml":"Jozo<test>"
- }
- ]
- },
- "response": {
- "status": 200,
- "transformers" : [ "response-template", "foo-transformer" ]
- }
- }
+{
+ "request": {
+ "url": "/users",
+ "method": "GET",
+ "bodyPatterns": [
+ {
+ "matchesXPath": {
+ "expression": "/user/name/text()",
+ "equalTo": "Jozo"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/user/jobId/text()",
+ "equalTo": ""
+ }
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "transformers": [
+ "response-template",
+ "foo-transformer"
+ ]
+ }
+}
'''), json)
and:
stubMappingIsValidWireMockStub(json)
@@ -2428,6 +2450,59 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
stubMappingIsValidWireMockStub(wireMockStub)
}
+ def should_generate_stubs_with_request_body_matchers() {
+ given:
+ Contract contractDsl = Contract.make {
+ request {
+ method 'GET'
+ urlPath '/get'
+ body([
+ duck : 123,
+ alpha : 'abc',
+ number : 123,
+ aBoolean : true,
+ date : '2017-01-01',
+ dateTime : '2017-01-01T01:23:45',
+ time : '01:02:34',
+ valueWithoutAMatcher: 'foo',
+ valueWithTypeMatch : 'string',
+ key : [
+ 'complex.key': 'foo'
+ ]
+ ])
+ bodyMatchers {
+ jsonPath('$.duck', byRegex("[0-9]{3}"))
+ jsonPath('$.duck', byEquality())
+ jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
+ jsonPath('$.alpha', byEquality())
+ jsonPath('$.number', byRegex(number()))
+ jsonPath('$.aBoolean', byRegex(anyBoolean()))
+ jsonPath('$.date', byDate())
+ jsonPath('$.dateTime', byTimestamp())
+ jsonPath('$.time', byTime())
+ jsonPath("\$.['key'].['complex.key']", byEquality())
+ }
+ headers {
+ contentType(applicationJson())
+ }
+ }
+ response {
+ status(200)
+ headers {
+ contentType(applicationJsonUtf8())
+ }
+ body("true")
+ }
+ }
+ when:
+ String wireMockStub = new WireMockStubStrategy("Test",
+ new ContractMetadata(null, false, 0, null, contractDsl), contractDsl)
+ .toWireMockClientStub()
+
+ then:
+ stubMappingIsValidWireMockStub(wireMockStub)
+ }
+
WireMockConfiguration config() {
return new WireMockConfiguration().extensions(responseTemplateTransformer())
}
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy
index 18a401c9ee..35c82c4695 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy
@@ -1,10 +1,27 @@
+/*
+ * Copyright 2013-2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.dsl.wiremock
import groovy.json.JsonSlurper
-import org.springframework.cloud.contract.spec.Contract
import spock.lang.Issue
import spock.lang.Specification
+import org.springframework.cloud.contract.spec.Contract
+
class WireMockResponseStubStrategySpec extends Specification {
def "should not quote floating point numbers"() {
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockStubVerifier.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubVerifier.groovy
similarity index 87%
rename from spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockStubVerifier.groovy
rename to spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubVerifier.groovy
index a0fc0ab772..ce16a4fb4a 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockStubVerifier.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubVerifier.groovy
@@ -14,16 +14,15 @@
* limitations under the License.
*/
-package org.springframework.cloud.contract.verifier.dsl
+package org.springframework.cloud.contract.verifier.dsl.wiremock
+
+import java.util.regex.Pattern
import com.github.tomakehurst.wiremock.matching.RegexPattern
import com.github.tomakehurst.wiremock.stubbing.StubMapping
-import org.springframework.cloud.contract.spec.Contract
-import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubMapping
-import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy
-import org.springframework.cloud.contract.verifier.file.ContractMetadata
-import java.util.regex.Pattern
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
trait WireMockStubVerifier {
@@ -38,5 +37,4 @@ trait WireMockStubVerifier {
void stubMappingIsValidWireMockStub(Contract contractDsl) {
stubMappingIsValidWireMockStub(new WireMockStubStrategy("Test", new ContractMetadata(null, false, 0, null, contractDsl), contractDsl).toWireMockClientStub())
}
-
}
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockXmlStubStrategySpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockXmlStubStrategySpec.groovy
new file mode 100644
index 0000000000..3c14c21ec7
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockXmlStubStrategySpec.groovy
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.dsl.wiremock
+
+
+import spock.lang.Specification
+
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
+
+/**
+ * @author Olga Maciaszek-Sharma
+ */
+class WireMockXmlStubStrategySpec extends Specification implements WireMockStubVerifier {
+
+ def 'should generate stubs with plain xml request body'() {
+ given:
+ Contract contractDsl = Contract.make {
+ request {
+ method 'GET'
+ urlPath '/get'
+ body """
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+"""
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ response {
+ status(OK())
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ }
+ when:
+ String wireMockStub = new WireMockStubStrategy("Test",
+ new ContractMetadata(null, false, 0, null, contractDsl), contractDsl)
+ .toWireMockClientStub()
+ then:
+ stubMappingIsValidWireMockStub(wireMockStub)
+ wireMockStub
+ .replaceAll("\n", "")
+ .replaceAll(' ', '')
+ .contains(
+ """
+"bodyPatterns": [
+ {
+ "matchesXPath": {
+ "expression": "/test/duck/text()",
+ "equalTo": "123"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/alpha/text()",
+ "equalTo": "abc"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/list/elem/text()",
+ "equalTo": "abc"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/list/elem[2]/text()",
+ "equalTo": "def"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/list/elem[3]/text()",
+ "equalTo": "ghi"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/number/text()",
+ "equalTo": "123"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/aBoolean/text()",
+ "equalTo": "true"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/date/text()",
+ "equalTo": "2017-01-01"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/dateTime/text()",
+ "equalTo": "2017-01-01T01:23:45"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/time/text()",
+ "equalTo": "01:02:34"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/valueWithoutAMatcher/text()",
+ "equalTo": "foo"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/valueWithTypeMatch/text()",
+ "equalTo": "string"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/key/complex/text()",
+ "equalTo": "foo"
+ }
+ },
+ {
+ "matchesXPath": {
+ "expression": "/test/duck/@type",
+ "equalTo": "xtype"
+ }
+ }]
+""".replaceAll("\n", "").replaceAll(' ', ''))
+ }
+
+ def 'should generate stubs with request body matchers'() {
+ given:
+ Contract contractDsl = Contract.make {
+ request {
+ method 'GET'
+ urlPath '/get'
+ body """
+
+123
+abc
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+"""
+ bodyMatchers {
+ xPath('/test/duck/text()', byRegex("[0-9]{3}"))
+ xPath('/test/duck/text()', byEquality())
+ xPath('/test/alpha/text()', byRegex(onlyAlphaUnicode()))
+ xPath('/test/alpha/text()', byEquality())
+ xPath('/test/number/text()', byRegex(number()))
+ xPath('/test/aBoolean/text()', byRegex(anyBoolean()))
+ xPath('/test/date/text()', byDate())
+ xPath('/test/dateTime/text()', byTimestamp())
+ xPath('/test/time/text()', byTime())
+ xPath('/test/*/complex/text()', byEquality())
+ xPath('/test/duck/@type', byEquality())
+ }
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ response {
+ status(OK())
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ }
+ when:
+ String wireMockStub = new WireMockStubStrategy("Test",
+ new ContractMetadata(null, false, 0, null, contractDsl), contractDsl)
+ .toWireMockClientStub()
+ then:
+ stubMappingIsValidWireMockStub(wireMockStub)
+ wireMockStub.replaceAll("\n", '').replaceAll(' ', '')
+ .contains("""
+ matchesXPath" : {
+ "expression" : "/test/duck/text()",
+ "matches" : "[0-9]{3}"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/duck/text()",
+ "equalTo" : "123"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/alpha/text()",
+ "matches" : "[\\\\p{L}]*"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/alpha/text()",
+ "equalTo" : "abc"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/number/text()",
+ "matches" : "-?(\\\\d*\\\\.\\\\d+|\\\\d+)"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/aBoolean/text()",
+ "matches" : "(true|false)"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/date/text()",
+ "matches" : "(\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/dateTime/text()",
+ "matches" : "([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/time/text()",
+ "matches" : "(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/*/complex/text()",
+ "equalTo" : "foo"
+ }
+ }, {
+ "matchesXPath" : {
+ "expression" : "/test/duck/@type",
+ "equalTo" : "xtype"
+ }
+ }""".replaceAll("\n",
+ "").replaceAll(' ', ''))
+ }
+
+ def 'should generate stubs with both xml and body matchers in request'() {
+ given:
+ Contract contractDsl = Contract.make {
+ request {
+ method 'GET'
+ urlPath '/get'
+ body """
+
+123
+abc
+123
+"""
+ bodyMatchers {
+ xPath('/test/duck/text()', byEquality())
+ xPath('/test/number/text()', byRegex(number()))
+ }
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ response {
+ status(OK())
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ }
+ when:
+ String wireMockStub = new WireMockStubStrategy("Test",
+ new ContractMetadata(null, false, 0, null, contractDsl), contractDsl)
+ .toWireMockClientStub()
+ then:
+ stubMappingIsValidWireMockStub(wireMockStub)
+ wireMockStub.replaceAll("\n", "")
+ .replaceAll(' ', '')
+ .contains("""
+ "bodyPatterns" : [ {
+ "matchesXPath": {
+ "expression": "/test/alpha/text()",
+ "equalTo": "abc"
+ }
+ }, {
+ "matchesXPath": {
+ "expression": "/test/duck/text()",
+ "equalTo": "123"
+ }
+ }, {
+ "matchesXPath": {
+ "expression": "/test/number/text()",
+ "matches" : "-?(\\\\d*\\\\.\\\\d+|\\\\d+)"
+ }
+ }
+ ]
+}
+""".replaceAll("\n", "").replaceAll(' ', ''))
+ }
+
+
+ def 'should generate stubs with response body matchers'() {
+ given:
+ Contract contractDsl = Contract.make {
+ request {
+ method 'GET'
+ urlPath '/get'
+ headers {
+ contentType(applicationXml())
+ }
+ }
+ response {
+ status(OK())
+ headers {
+ contentType(applicationXml())
+ }
+ body """
+
+123
+abc
+
+abc
+def
+ghi
+
+123
+true
+2017-01-01
+2017-01-01T01:23:45
+
+foo
+string
+foo
+"""
+ bodyMatchers {
+ xPath('/test/duck/text()', byRegex("[0-9]{3}"))
+ xPath('/test/duck/text()', byEquality())
+ xPath('/test/alpha/text()', byRegex(onlyAlphaUnicode()))
+ xPath('/test/alpha/text()', byEquality())
+ xPath('/test/number/text()', byRegex(number()))
+ xPath('/test/aBoolean/text()', byRegex(anyBoolean()))
+ xPath('/test/date/text()', byDate())
+ xPath('/test/dateTime/text()', byTimestamp())
+ xPath('/test/time/text()', byTime())
+ xPath('/test/*/complex/text()', byEquality())
+ xPath('/test/duck/@type', byEquality())
+ }
+ }
+ }
+ when:
+ String wireMockStub = new WireMockStubStrategy("Test",
+ new ContractMetadata(null, false, 0, null, contractDsl), contractDsl)
+ .toWireMockClientStub()
+ then:
+ wireMockStub.contains("\\n\\n123" +
+ "\\nabc\\n\\nabc\\ndef" +
+ "\\nghi\\n
\\n123" +
+ "\\ntrue\\n2017-01-01" +
+ "\\n2017-01-01T01:23:45\\n" +
+ "\\nfoo" +
+ "\\nstring" +
+ "\\nfoo\\n")
+ }
+}
+
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy
index 5dd38775dc..8b5969941a 100644
--- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy
@@ -1,5 +1,14 @@
package org.springframework.cloud.contract.verifier.util
+import java.lang.reflect.Method
+
+import javax.inject.Inject
+import javax.ws.rs.client.Entity
+import javax.ws.rs.client.WebTarget
+import javax.ws.rs.core.Response
+import javax.xml.parsers.DocumentBuilder
+import javax.xml.parsers.DocumentBuilderFactory
+
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
import com.toomuchcoding.jsonassert.JsonAssertion
@@ -16,6 +25,9 @@ import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.junit.Rule
import org.junit.Test
import org.mdkt.compiler.InMemoryJavaCompiler
+import org.w3c.dom.Document
+import org.xml.sax.InputSource
+
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage
@@ -24,19 +36,12 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
import org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil
import org.springframework.util.ReflectionUtils
-import javax.inject.Inject
-import javax.ws.rs.client.Entity
-import javax.ws.rs.client.WebTarget
-import javax.ws.rs.core.Response
-import java.lang.reflect.Method
-
/**
* checking the syntax of produced scripts
*/
@CompileStatic
class SyntaxChecker {
- WebTarget webTarget
Entity entity
private static final String[] DEFAULT_IMPORTS = [
@@ -55,7 +60,12 @@ class SyntaxChecker {
WebTarget.name,
Response.name,
WebTestClientRequestSpecification.name,
- WebTestClientResponse.name
+ WebTestClientResponse.name,
+ DocumentBuilder.name,
+ DocumentBuilderFactory.name,
+ Document.name,
+ InputSource.name,
+ StringReader.name
]
private static final String DEFAULT_IMPORTS_AS_STRING = DEFAULT_IMPORTS.collect {
@@ -67,21 +77,26 @@ class SyntaxChecker {
"${RestAssuredMockMvc.name}.when",
"${RestAssured.name}.*",
"${Entity.name}.*",
- "${ContractVerifierUtil.name}.fileToBytes",
+ "${ContractVerifierUtil.name}.*",
"${ContractVerifierMessagingUtil.name}.headers",
"${JsonAssertion.name}.assertThatJson",
- "${SpringCloudContractAssertions.name}.assertThat"
+ "${SpringCloudContractAssertions.name}.assertThat",
].collect { "import static ${it};"}.join("\n")
private static final String WEB_TEST_CLIENT_STATIC_IMPORTS = [
"${RestAssuredWebTestClient.name}.*",
"${Entity.name}.*",
- "${ContractVerifierUtil.name}.fileToBytes",
+ "${ContractVerifierUtil.name}.*",
"${ContractVerifierMessagingUtil.name}.headers",
"${JsonAssertion.name}.assertThatJson",
"${SpringCloudContractAssertions.name}.assertThat"
].collect { "import static ${it};" }.join("\n")
+ private static final String dummyMethod = '''
+private void test(String test) {
+\t\tassertThat(test).isEqualTo("123");
+\t}'''
+
static void tryToCompile(String builderName, String test) {
if (builderName.toLowerCase().contains("spock")) {
tryToCompileGroovy(builderName, test)
@@ -125,6 +140,7 @@ class SyntaxChecker {
sourceCode.append("WebTarget webTarget")
sourceCode.append("\n")
sourceCode.append(test)
+ sourceCode.append(dummyMethod)
return new GroovyShell(SyntaxChecker.classLoader, configuration).parse(sourceCode.toString())
}
@@ -150,9 +166,10 @@ class SyntaxChecker {
sourceCode.append("\n")
sourceCode.append(" WebTarget webTarget;")
sourceCode.append("\n")
- sourceCode.append(" public void method() {\n")
+ sourceCode.append(" public void method() throws Exception {\n")
sourceCode.append(" ${test}\n")
sourceCode.append(" }\n")
+ sourceCode.append(dummyMethod)
sourceCode.append("}")
return InMemoryJavaCompiler.compile(fqnClassName, sourceCode.toString())
}
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/xml/XPathSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/xml/XPathSpec.groovy
new file mode 100644
index 0000000000..6bd0349200
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/xml/XPathSpec.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.util.xml
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+/**
+ * @author Marcin Grzejszczak
+ */
+class XPathSpec extends Specification {
+
+ @Unroll
+ def "should generate [#expectedXPath] for XPath [#xPath]"() {
+ expect:
+ xPath == expectedXPath
+ where:
+ xPath || expectedXPath
+ XPathBuilder.builder().node("some").node("nested").node("anothervalue").isEqualTo(4).xPath() || '''/some/nested[anothervalue=4]'''
+ XPathBuilder.builder().node("some").node("nested").array("withlist").contains("name").isEqualTo("name1").xPath() || '''/some/nested/withlist[name='name1']'''
+ XPathBuilder.builder().node("some").node("nested").array("withlist").contains("name").isEqualTo("name2").xPath() || '''/some/nested/withlist[name='name2']'''
+ XPathBuilder.builder().node("some").node("nested").node("json").isEqualTo("with \"val'ue").xPath() || '''/some/nested[json=concat('with "val',"'",'ue')]'''
+ XPathBuilder.builder().node("some", "nested", "json").isEqualTo("with \"val'ue").xPath() || '''/some/nested[json=concat('with "val',"'",'ue')]'''
+ }
+
+}
diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/xml/XmlAssertionSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/xml/XmlAssertionSpec.groovy
new file mode 100644
index 0000000000..0d819b8ce3
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/xml/XmlAssertionSpec.groovy
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed 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.springframework.cloud.contract.verifier.util.xml
+
+import groovy.xml.MarkupBuilder
+import spock.lang.Issue
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class XmlAssertionSpec extends Specification {
+
+ @Shared
+ String xml1 = '''
+
+
+ with "val'ue
+ 4
+ foo
+
+ name1
+
+
+ name2
+
+
+ 8
+
+
+ name3
+
+
+ '''
+
+ @Unroll
+ def 'should convert an xml with a map as root to a map of path to value '() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").node("anothervalue").isEqualTo(4) || '''/some/nested[anothervalue=4]'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").node("anothervalue") || '''/some/nested/anothervalue'''
+ XmlAssertion.
+ assertThat(xml1).node("some").text() || '''/some/text()'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").node("withattr").withAttribute("id", "a").
+ withAttribute("id2", "b") || '''/some/nested/withattr[@id='a'][@id2='b']'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").node("withattr").withAttribute("id") || '''/some/nested/withattr/@id'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").node("withattr").isEqualTo("foo").
+ withAttribute("id", "a").withAttribute("id2", "b") || '''/some/nested[withattr='foo']/withattr[@id='a'][@id2='b']'''
+ XmlAssertion.assertThatXml(xml1).node("some").
+ node("nested").node("anothervalue").isEqualTo(4) || '''/some/nested[anothervalue=4]'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").array("withlist").contains("name").
+ isEqualTo("name1") || '''/some/nested/withlist[name='name1']'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").array("withlist").contains("name").
+ isEqualTo("name2") || '''/some/nested/withlist[name='name2']'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").array("withlist").contains("name").isEqualTo("name3").
+ withAttribute("id", "10").withAttribute("surname", "kowalski") || '''/some/nested/withlist[name='name3']/name[@id='10'][@surname='kowalski']'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").array("withlist").isEqualTo(8) || '''/some/nested/withlist[number()=8]'''
+ XmlAssertion.assertThat(xml1).node("some").
+ node("nested").node("json").isEqualTo("with \"val'ue") || '''/some/nested[json=concat('with "val',"'",'ue')]'''
+ XmlAssertion.assertThat(xml1).
+ node("some", "nested", "json").isEqualTo("with \"val'ue") || '''/some/nested[json=concat('with "val',"'",'ue')]'''
+ }
+
+ @Shared
+ String xml2 = '''
+
+ a
+ b
+
+'''
+
+ @Unroll
+ def "should generate assertions for simple response body"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml2).node("root").
+ node("property1").isEqualTo("a") || '''/root[property1='a']'''
+ XmlAssertion.assertThat(xml2).node("root").
+ node("property2").isEqualTo("b") || '''/root[property2='b']'''
+ }
+
+ @Shared
+ String xml3 = '''
+
+ true
+
+ false
+ 5
+
+'''
+
+ @Unroll
+ def "should generate assertions for null and boolean values"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml3).node("root").
+ node("property1").isEqualTo("true") || '''/root[property1='true']'''
+ XmlAssertion.assertThat(xml3).node("root").
+ node("property2").isNull() || '''not(boolean(/root/property2/text()[1]))'''
+ XmlAssertion.assertThat(xml3).node("root").
+ node("property3").isEqualTo(false) || '''/root[property3='false']'''
+ XmlAssertion.assertThat(xml3).node("root").
+ node("property4").isEqualTo(5) || '''/root[property4=5]'''
+ }
+
+ @Shared
+ StringWriter xml4 = new StringWriter()
+ @Shared
+ def root4 = new MarkupBuilder(xml4).root {
+ property1('a')
+ property2 {
+ a('sth')
+ b('sthElse')
+ }
+ }
+
+ @Unroll
+ def "should generate assertions for simple response body constructed from map with a list"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml4.toString()).
+ node("root").node("property1").
+ isEqualTo("a") || '''/root[property1='a']'''
+ XmlAssertion.assertThat(xml4.toString()).
+ node("root").array("property2").contains("a").
+ isEqualTo("sth") || '''/root/property2[a='sth']'''
+ XmlAssertion.assertThat(xml4.toString()).
+ node("root").array("property2").contains("b").
+ isEqualTo("sthElse") || '''/root/property2[b='sthElse']'''
+ }
+
+ @Shared
+ String xml7 = '''
+
+
+ test1
+
+
+ test2
+
+
+'''
+
+ @Unroll
+ def "should generate assertions for array inside response body element"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml7).node("root").
+ array("property1").contains("property2").isEqualTo("test1") || '''/root/property1[property2='test1']'''
+ XmlAssertion.assertThat(xml7).node("root").
+ array("property1").contains("property3").isEqualTo("test2") || '''/root/property1[property3='test2']'''
+ }
+
+ @Shared
+ String xml8 = """
+
+ a
+
+ b
+
+
+"""
+
+ def "should generate assertions for nested objects in response body"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml8).node("root").
+ node("property2").node("property3").isEqualTo("b") || '''/root/property2[property3='b']'''
+ XmlAssertion.assertThat(xml8).node("root").
+ node("property1").isEqualTo("a") || '''/root[property1='a']'''
+ }
+
+ @Shared
+ StringWriter xml9 = new StringWriter()
+ @Shared
+ def root9 = new MarkupBuilder(xml9).root {
+ property1('a')
+ property2(123)
+ }
+
+ @Unroll
+ def "should generate regex assertions for map objects in response body"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml9.toString()).
+ node("root").node("property2").matches("[0-9]{3}") || '''/root[matches(property2, '[0-9]{3}')]'''
+ XmlAssertion.assertThat(xml9.toString()).
+ node("root").node("property1").isEqualTo("a") || '''/root[property1='a']'''
+ }
+
+ def "should generate escaped regex assertions for string objects in response body"() {
+ given:
+ StringWriter xml = new StringWriter()
+ def root = new MarkupBuilder(xml).root {
+ property2(123123)
+ }
+ expect:
+ def verifiable = XmlAssertion.
+ assertThat(xml.toString()).node("root").node("property2").
+ matches("\\d+")
+ verifiable.xPath() == '''/root[matches(property2, '\\d+')]'''
+ }
+
+ @Shared
+ StringWriter xml10 = new StringWriter()
+ @Shared
+ def root10 = new MarkupBuilder(xml10).root {
+ errors {
+ property('bank_account_number')
+ message('incorrect_format')
+ }
+ }
+
+ @Unroll
+ def "should work with more complex stuff and xpaths"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml10.toString()).
+ node("root").array("errors").contains("property").
+ isEqualTo("bank_account_number") || '''/root/errors[property='bank_account_number']'''
+ XmlAssertion.assertThat(xml10.toString()).
+ node("root").array("errors").contains("message").
+ isEqualTo("incorrect_format") || '''/root/errors[message='incorrect_format']'''
+ }
+
+ @Shared
+ String xml11 = '''
+
+
+
+ -77.119759
+ 38.995548
+ -76.909393
+ 38.791645
+
+
+
+'''
+
+ @Unroll
+ def "should manage to parse a double array"() {
+ expect:
+ verifiable.xPath() == expectedXPath
+ where:
+ verifiable || expectedXPath
+ XmlAssertion.assertThat(xml11).node("root").
+ node("place").node("bounding_box").array("coordinates").
+ isEqualTo(38.995548) || '''/root/place/bounding_box/coordinates[number()=38.995548]'''
+ XmlAssertion.assertThat(xml11).node("root").
+ node("place").node("bounding_box").array("coordinates").
+ isEqualTo(-77.119759) || '''/root/place/bounding_box/coordinates[number()=-77.119759]'''
+ XmlAssertion.assertThat(xml11).node("root").
+ node("place").node("bounding_box").array("coordinates").
+ isEqualTo(-76.909393) || '''/root/place/bounding_box/coordinates[number()=-76.909393]'''
+ XmlAssertion.assertThat(xml11).node("root").
+ node("place").node("bounding_box").array("coordinates").
+ isEqualTo(38.791645) || '''/root/place/bounding_box/coordinates[number()=38.791645]'''
+
+ }
+
+ def "should run XPath when provided manually"() {
+ given:
+ String xml = """
+
+ a
+
+ b
+
+
+"""
+ and:
+ String xPath = '''/root/property2[property3='b']'''
+ expect:
+ XmlAssertion.assertThat(xml).matchesXPath(xPath)
+ }
+
+ def "should throw exception when XPath is not matched"() {
+ given:
+ String xml = """
+
+ a
+
+ b
+
+
+"""
+ and:
+ String xPath = '''/root/property2[property3='non-existing']'''
+ when:
+ XmlAssertion.assertThat(xml).matchesXPath(xPath)
+ then:
+ IllegalStateException illegalStateException = thrown(IllegalStateException)
+ illegalStateException.message.contains("Parsed XML")
+ illegalStateException.message.contains("doesn't match the XPath")
+ }
+
+ def "should not throw exception when json path is not matched and system prop overrides the check"() {
+ given:
+ String xml = """
+
+ a
+
+ b
+
+
+ """
+ and:
+ String xPath = '''/root/property2[property3='non-existing']'''
+ when:
+ XmlAssertion.assertThat(xml).
+ withoutThrowingException().matchesXPath(xPath)
+ then:
+ noExceptionThrown()
+ }
+
+ def "should generate escaped regex assertions for text with regular expression values"() {
+ given:
+ // '"<>[]()
+ String xml = """
+
+ '"<>[]()
+ """
+ expect:
+ def verifiable = XmlAssertion.assertThat(xml).
+ node("root").node("property1").matches('\'"<>\\[\\]\\(\\)')
+ verifiable.xPath() == '''/root[matches(property1, concat('',"'",'"<>\\[\\]\\(\\)'))]'''
+ }
+
+ def "should escape regular expression properly"() {
+ given:
+ String xml = """
+
+ /api/12
+ 123456
+ """
+ expect:
+ def verifiable = XmlAssertion.assertThatXml(xml).
+ node("root").node("path").matches("^/api/[0-9]{2}\$")
+ verifiable.xPath() == '''/root[matches(path, '^/api/[0-9]{2}$')]'''
+ }
+
+ def "should escape single quotes in a quoted string"() {
+ given:
+ String xml = """
+
+ text with 'quotes' inside
+
+ """
+ expect:
+ def verifiable = XmlAssertion.assertThatXml(xml).
+ node("root").node("text").isEqualTo("text with 'quotes' inside")
+ verifiable.xPath() == '''/root[text=concat('text with ',"'",'quotes',"'",' inside')]'''
+ }
+
+ def "should escape brackets in a string"() {
+ given:
+ String xml = """
+
+ <escape me>
+
+ """
+ expect:
+ def verifiable = XmlAssertion.assertThatXml(xml).
+ node("root").node("id").isEqualTo("")
+ verifiable.xPath() == '''/root[id='']'''
+ }
+
+ def "should escape double quotes in a quoted string"() {
+ given:
+ String xml = """
+
+ text with "quotes" inside
+
+ """
+ expect:
+ def verifiable = XmlAssertion.assertThatXml(xml).
+ node("root").node("text").isEqualTo('''text with "quotes" inside''')
+ verifiable.xPath() == '''/root[text='text with "quotes" inside']'''
+ }
+
+ def 'should resolve the value of XML via XPath'() {
+ given:
+ String xml =
+ '''
+
+
+
+
+ with value
+ 4
+
+ name1
+
+
+ name2
+
+
+
+ name3
+
+
+
+
+
+
+
+
+ true
+ 4
+
+ name1
+
+
+ name2
+
+ a
+ b
+
+
+
+ '''
+ expect:
+ XPathBuilder.builder(xml).node("root").
+ array("element").node("some").node("nested").node("json").
+ read() == 'with value'
+ XPathBuilder.builder(xml).node("root").
+ array("element").node("some").node("nested").node("anothervalue").
+ read() == 4.toString()
+ // assertThat(xml).node("root").array("element").node("some").node("nested").array("withlist").node("name").read() == ['name1', 'name2'].toString()
+ //assertThat(xml).node("root").array("element").node("someother").node("nested").array("withlist2").read() == ['a', 'b'].toString()
+ XmlAssertion.assertThat(xml).node("root").
+ array("element").node("someother").node("nested").node("json").
+ read() == true.toString()
+ }
+
+ def 'should match array containing an array of primitives'() {
+ given:
+ String xml = '''
+
+ existing
+
+
+ AGENT
+ BANK
+ CASH
+
+
+
+'''
+ expect:
+ def verifiable = XmlAssertion.assertThatXml(xml).
+ node("root").array("elements").array("partners").
+ contains("payment_methods").isEqualTo("BANK")
+ verifiable.xPath() == '''/root/elements/partners[payment_methods='BANK']'''
+ }
+
+ def 'should match pattern in array'() {
+ given:
+ String xml = '''
+
+ ROLE_ADMIN
+
+ '''
+
+ expect:
+ def verifiable = XmlAssertion.assertThatXml(xml).
+ node("root").array("authorities").matches("^[a-zA-Z0-9_\\- ]+\$")
+ verifiable.xPath() == '''/root/authorities[matches(text(), '^[a-zA-Z0-9_\\- ]+$')]'''
+ }
+
+ def 'should manage to parse array with string values'() {
+ given:
+ String xml = '''
+
+ name1
+ name2
+ '''
+
+ expect:
+ def v1 = XmlAssertion.assertThat(xml).
+ node("root").array("some_list").isEqualTo("name1")
+ def v2 = XmlAssertion.assertThat(xml).
+ node("root").array("some_list").isEqualTo("name2")
+ and:
+ v1.xPath() == '''/root/some_list[text()='name1']'''
+ v2.xPath() == '''/root/some_list[text()='name2']'''
+ }
+
+ @Issue("#2")
+ def 'should allow nested calls with counting the elements size'() {
+ given:
+ String xml = '''
+
+ name1
+ name2
+ '''
+
+ expect:
+ def v1 = XmlAssertion.assertThat(xml).
+ node("root").array("some_list").hasSize(2).isEqualTo("name1")
+ and:
+ v1.xPath() == '''/root/some_list[text()='name1']'''
+ }
+
+ @Issue("#2")
+ def 'should count the elements size'() {
+ given:
+ String xml = '''
+
+ name1
+ name2
+ '''
+
+ expect:
+ def v1 = XmlAssertion.assertThat(xml).
+ node("root").array("some_list").hasSize(2)
+ and:
+ v1.xPath() == '''count(/root/some_list)'''
+ }
+
+ @Issue("#2")
+ def 'should throw exception if size is wrong'() {
+ given:
+ String xml = '''
+
+ name1
+ name2
+ '''
+
+ when:
+ XmlAssertion.assertThat(xml).node("root").
+ array("some_list").hasSize(1)
+ then:
+ IllegalStateException e = thrown(IllegalStateException)
+ e.message.
+ contains("has size [2] and not [1] for XPath ")
+ }
+
+ @Issue("#2")
+ def 'should return 0 if element is missing'() {
+ given:
+ String xml = '''
+
+ name1
+ name2
+ '''
+
+ when:
+ XmlAssertion.assertThat(xml).node("root").
+ array("foo").hasSize(1)
+ then:
+ IllegalStateException e = thrown(IllegalStateException)
+ e.message.contains("has size [0] and not [1] for XPath ")
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_xml.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_xml.yml
new file mode 100644
index 0000000000..47ad9f2fe6
--- /dev/null
+++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_xml.yml
@@ -0,0 +1,68 @@
+request:
+ method: GET
+ url: /getymlResponse
+ headers:
+ Content-Type: application/xml
+ body: |
+
+ 123
+ abc
+
+ abc
+ def
+ ghi
+
+ 123
+ true
+ 2017-01-01
+ 2017-01-01T01:23:45
+
+ foo
+ string
+ foo
+
+ matchers:
+ body:
+ - path: /test/duck/text()
+ type: by_regex
+ value: "[0-9]{10}"
+ - path: /test/duck/text()
+ type: by_equality
+ - path: /test/time/text()
+ type: by_time
+response:
+ status: 200
+ headers:
+ Content-Type: application/xml
+ body: |
+
+ 123
+ abc
+
+ abc
+ def
+ ghi
+
+ 123
+ true
+ 2017-01-01
+ 2017-01-01T01:23:45
+
+ foo
+ string
+ foo
+
+ matchers:
+ body:
+ - path: /test/duck/text()
+ type: by_regex
+ value: "[0-9]{10}"
+ - path: /test/duck/text()
+ type: by_command
+ value: "test($it)"
+ - path: /test/duck/xxx
+ type: by_null
+ - path: /test/duck/text()
+ type: by_equality
+ - path: /test/time/text()
+ type: by_time
\ No newline at end of file