diff --git a/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016..29953ea141 100644 Binary files a/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar and b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar differ diff --git a/docker/spring-cloud-contract-docker/project/gradlew b/docker/spring-cloud-contract-docker/project/gradlew index 8e25e6c19d..cccdd3d517 100755 --- a/docker/spring-cloud-contract-docker/project/gradlew +++ b/docker/spring-cloud-contract-docker/project/gradlew @@ -1,21 +1,5 @@ #!/usr/bin/env sh -# -# Copyright 2015 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 -# -# https://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. -# - ############################################################################## ## ## Gradle start up script for UN*X @@ -44,7 +28,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/docker/spring-cloud-contract-docker/project/gradlew.bat b/docker/spring-cloud-contract-docker/project/gradlew.bat index 24467a141f..e95643d6a2 100644 --- a/docker/spring-cloud-contract-docker/project/gradlew.bat +++ b/docker/spring-cloud-contract-docker/project/gradlew.bat @@ -1,19 +1,3 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -30,7 +14,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" +set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/pom.xml b/pom.xml index ea76204bbd..ecdd64b80f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.2.0.BUILD-SNAPSHOT 5.0.4 3.2.9 - 1.0-groovy-2.4 + 1.3-groovy-2.5 0.5.1 0.2.2 1.6 @@ -53,7 +53,7 @@ 1.6.3 - 2.4.17 + 2.5.7 3.3.9 1.1.0 diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/GitStubDownloaderPropertiesSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/GitStubDownloaderPropertiesSpec.groovy index b4191438a3..6f687ecb56 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/GitStubDownloaderPropertiesSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/GitStubDownloaderPropertiesSpec.groovy @@ -44,7 +44,7 @@ class GitStubDownloaderPropertiesSpec extends Specification { props.url == URI.create("git:git@foo.com/foo") } - Resource resource(String uri) { + Resource resource(String resourceUri) { return new AbstractResource() { @Override String getDescription() { @@ -58,7 +58,8 @@ class GitStubDownloaderPropertiesSpec extends Specification { @Override URI getURI() throws IOException { - return URI.create(uri) + // Groovy resolves URI to getURI() and StackOverFlow is thrown + return java.net.URI.create(resourceUri) } } } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy index 9f1ff05cac..647aae8c20 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy @@ -148,7 +148,7 @@ class StubRunnerExecutorSpec extends Specification { def 'should generate regex values when message is to be set and it contains regex'() { given: - MessageVerifier messageVerifier = Mock(MessageVerifier) + MockMessageVerifier messageVerifier = new MockMessageVerifier() StubRunnerExecutor executor = new StubRunnerExecutor(portScanner, messageVerifier, []) when: def stubConf = new StubConfiguration('asd', 'asd', 'asd', '') @@ -158,17 +158,40 @@ class StubRunnerExecutorSpec extends Specification { boolean triggered = executor.trigger("trigger") then: triggered - 1 * messageVerifier.send({ it -> - println "Body <${it}>" - !it.toString().contains("cursor") - }, { Map map -> - println "Headers <${map}>" - !map.values().any { it.toString().contains("cursor") } - }, _) + messageVerifier.called cleanup: executor.shutdown() } + class MockMessageVerifier implements MessageVerifier { + + boolean called + + @Override + void send(Object message, String destination) { + + } + + @Override + Object receive(String destination, long timeout, TimeUnit timeUnit) { + return null + } + + @Override + Object receive(String destination) { + return null + } + + @Override + void send(Object payload, Map headers, String destination) { + this.called = true + println "Body <${payload}>" + assert !payload.toString().contains("cursor") + println "Headers <${headers}>" + assert headers.values().every { !it.toString().contains("cursor") } + } + } + Map stubIdsWithPortsFromString(String stubIdsToPortMapping) { return stubIdsToPortMapping.split(',').collectEntries { String entry -> return StubsParser.fromStringWithPort(entry) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.groovy index e305f17710..e55a486b06 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.groovy @@ -18,7 +18,6 @@ package org.springframework.cloud.contract.verifier.converter import java.util.regex.Pattern -import groovy.transform.CompileStatic import groovy.transform.PackageScope import org.springframework.cloud.contract.spec.Contract @@ -41,13 +40,11 @@ import org.springframework.cloud.contract.verifier.util.MapConverter import static org.springframework.cloud.contract.verifier.util.ContentType.XML import static org.springframework.cloud.contract.verifier.util.ContentUtils.evaluateContentType - /** * @author Marcin Grzejszczak * @author Olga Maciaszek-Sharma */ @PackageScope -@CompileStatic class ContractsToYaml { List convertTo(Collection contracts) { @@ -169,7 +166,7 @@ class ContractsToYaml { request.multipart.named << new YamlContract.Named(paramName: key, fileName: fileName instanceof String ? value.name?.serverValue as String : null, fileContent: fileContent instanceof String ? fileContent as String : null, - fileContentAsBytes: fileContent instanceof String ? fileContent as String : null, + fileContentAsBytes: fileContent instanceof FromFileProperty ? fileContent.asBytes().toString() : null, fileContentFromFileAsBytes: resolveFileNameAsBytes(fileContent), contentType: contentType instanceof String ? contentType as String : null, fileNameCommand: fileName instanceof ExecutionProperty ? fileName.toString() : null, @@ -413,6 +410,8 @@ class ContractsToYaml { return YamlContract.TestMatcherType.by_timestamp case MatchingType.REGEX: return YamlContract.TestMatcherType.by_regex + case MatchingType.NULL: + return YamlContract.TestMatcherType.by_null } return null } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy deleted file mode 100644 index c3877673d4..0000000000 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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 - * - * https://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.converter - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.ToString - -/** - * YAML representation of a {@link org.springframework.cloud.contract.spec.Contract} - * - * @since 1.2.1* @author Marcin Grzejszczak - * @author Tim Ysewyn - */ -@CompileStatic -class YamlContract { - public Request request - public Response response - public Input input - public OutputMessage outputMessage - public String description - public String label - public String name - public Integer priority - public boolean ignored - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class Request { - public String method - public String url - public String urlPath - public Map queryParameters = [:] - public Map headers = [:] - public Map cookies = [:] - public Object body - public String bodyFromFile - public String bodyFromFileAsBytes - public StubMatchers matchers = new StubMatchers() - public Multipart multipart - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class Multipart { - public Map params = [:] - public List named = [] - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class Named { - public String paramName - public String fileName - public String fileContent - public String fileContentAsBytes - public String fileContentFromFileAsBytes - public String contentType - public String fileNameCommand - public String fileContentCommand - public String contentTypeCommand - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class StubMatchers { - public KeyValueMatcher url - public List body = [] - public List headers = [] - public List queryParameters = [] - public List cookies = [] - public MultipartStubMatcher multipart - } - - @CompileStatic - enum MatchingType { - equal_to, containing, matching, not_matching, equal_to_json, - equal_to_xml, absent - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class BodyStubMatcher { - public String path - public StubMatcherType type - public String value - public PredefinedRegex predefined - public Integer minOccurrence - public Integer maxOccurrence - public RegexType regexType - } - - @CompileStatic - enum RegexType { - as_integer, as_double, as_float, as_long, as_short, as_boolean, as_string - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class MultipartStubMatcher { - public List params = [] - public List named = [] - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class MultipartNamedStubMatcher { - public String paramName - public ValueMatcher fileName - public ValueMatcher fileContent - public ValueMatcher contentType - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class ValueMatcher { - public String regex - public PredefinedRegex predefined - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class BodyTestMatcher { - public String path - public TestMatcherType type - public String value - public Integer minOccurrence - public Integer maxOccurrence - public PredefinedRegex predefined - public RegexType regexType - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class KeyValueMatcher { - public String key - public String regex - public PredefinedRegex predefined - public String command - public RegexType regexType - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class HeadersMatcher extends KeyValueMatcher { - - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class QueryParameterMatcher { - public String key - public MatchingType type - public Object value - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class TestHeaderMatcher { - public String key - public String regex - public String command - public PredefinedRegex predefined - public RegexType regexType - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class TestCookieMatcher { - public String key - public String regex - public String command - public PredefinedRegex predefined - public RegexType regexType - } - - @CompileStatic - static enum PredefinedRegex { - only_alpha_unicode, number, any_double, any_boolean, ip_address, hostname, - email, url, uuid, iso_date, iso_date_time, iso_time, - iso_8601_with_offset, non_empty, non_blank - } - - @CompileStatic - static enum StubMatcherType { - by_date, by_time, by_timestamp, by_regex, by_equality, by_type, by_null - } - - @CompileStatic - static enum TestMatcherType { - by_date, by_time, by_timestamp, by_regex, by_equality, by_type, by_command, by_null - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class Response { - public int status - public Map headers = [:] - public Map cookies = [:] - public Object body - public String bodyFromFile - public String bodyFromFileAsBytes - public TestMatchers matchers = new TestMatchers() - public Boolean async - public Integer fixedDelayMilliseconds - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class TestMatchers { - public List body = [] - public List headers = [] - public List cookies = [] - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class Input { - public String messageFrom - public String triggeredBy - public Map messageHeaders = [:] - public Object messageBody - public String messageBodyFromFile - public String messageBodyFromFileAsBytes - public String assertThat - public StubMatchers matchers = new StubMatchers() - } - - @CompileStatic - @ToString(includeFields = true) - @EqualsAndHashCode - static class OutputMessage { - public String sentTo - public Map headers = [:] - public Object body - public String bodyFromFile - public String bodyFromFileAsBytes - public String assertThat - public TestMatchers matchers = new TestMatchers() - } -} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.java new file mode 100644 index 0000000000..615ca30aa0 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.java @@ -0,0 +1,841 @@ +/* + * 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 + * + * https://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.converter; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.springframework.cloud.contract.spec.Contract; + +/** + * YAML representation of a {@link Contract}. + * + * @author Marcin Grzejszczak + * @author Tim Ysewyn + * @since 1.2.1 + */ +public class YamlContract { + + public Request request; + + public Response response; + + public Input input; + + public OutputMessage outputMessage; + + public String description; + + public String label; + + public String name; + + public Integer priority; + + public boolean ignored; + + public static class Request { + + public String method; + + public String url; + + public String urlPath; + + public Map queryParameters = new LinkedHashMap(); + + public Map headers = new LinkedHashMap(); + + public Map cookies = new LinkedHashMap(); + + public Object body; + + public String bodyFromFile; + + public String bodyFromFileAsBytes; + + public StubMatchers matchers = new StubMatchers(); + + public Multipart multipart; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Request request = (Request) o; + return Objects.equals(method, request.method) + && Objects.equals(url, request.url) + && Objects.equals(urlPath, request.urlPath) + && Objects.equals(queryParameters, request.queryParameters) + && Objects.equals(headers, request.headers) + && Objects.equals(cookies, request.cookies) + && Objects.equals(body, request.body) + && Objects.equals(bodyFromFile, request.bodyFromFile) + && Objects.equals(bodyFromFileAsBytes, request.bodyFromFileAsBytes) + && Objects.equals(matchers, request.matchers) + && Objects.equals(multipart, request.multipart); + } + + @Override + public int hashCode() { + return Objects.hash(method, url, urlPath, queryParameters, headers, cookies, + body, bodyFromFile, bodyFromFileAsBytes, matchers, multipart); + } + + @Override + public String toString() { + return "Request{" + "method='" + method + '\'' + ", url='" + url + '\'' + + ", urlPath='" + urlPath + '\'' + ", queryParameters=" + + queryParameters + ", headers=" + headers + ", cookies=" + cookies + + ", body=" + body + ", bodyFromFile='" + bodyFromFile + '\'' + + ", bodyFromFileAsBytes='" + bodyFromFileAsBytes + '\'' + + ", matchers=" + matchers + ", multipart=" + multipart + '}'; + } + + } + + public static class Multipart { + + public Map params = new LinkedHashMap(); + + public List named = new ArrayList(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Multipart multipart = (Multipart) o; + return Objects.equals(params, multipart.params) + && Objects.equals(named, multipart.named); + } + + @Override + public int hashCode() { + return Objects.hash(params, named); + } + + @Override + public String toString() { + return "Multipart{" + "params=" + params + ", named=" + named + '}'; + } + + } + + public static class Named { + + public String paramName; + + public String fileName; + + public String fileContent; + + public String fileContentAsBytes; + + public String fileContentFromFileAsBytes; + + public String contentType; + + public String fileNameCommand; + + public String fileContentCommand; + + public String contentTypeCommand; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Named named = (Named) o; + return Objects.equals(paramName, named.paramName) + && Objects.equals(fileName, named.fileName) + && Objects.equals(fileContent, named.fileContent) + && Objects.equals(fileContentAsBytes, named.fileContentAsBytes) + && Objects.equals(fileContentFromFileAsBytes, + named.fileContentFromFileAsBytes) + && Objects.equals(contentType, named.contentType) + && Objects.equals(fileNameCommand, named.fileNameCommand) + && Objects.equals(fileContentCommand, named.fileContentCommand) + && Objects.equals(contentTypeCommand, named.contentTypeCommand); + } + + @Override + public int hashCode() { + return Objects.hash(paramName, fileName, fileContent, fileContentAsBytes, + fileContentFromFileAsBytes, contentType, fileNameCommand, + fileContentCommand, contentTypeCommand); + } + + @Override + public String toString() { + return "Named{" + "paramName='" + paramName + '\'' + ", fileName='" + fileName + + '\'' + ", fileContent='" + fileContent + '\'' + + ", fileContentAsBytes='" + fileContentAsBytes + '\'' + + ", fileContentFromFileAsBytes='" + fileContentFromFileAsBytes + '\'' + + ", contentType='" + contentType + '\'' + ", fileNameCommand='" + + fileNameCommand + '\'' + ", fileContentCommand='" + + fileContentCommand + '\'' + ", contentTypeCommand='" + + contentTypeCommand + '\'' + '}'; + } + + } + + public static class StubMatchers { + + public KeyValueMatcher url; + + public List body = new ArrayList(); + + public List headers = new ArrayList(); + + public List queryParameters = new ArrayList(); + + public List cookies = new ArrayList(); + + public MultipartStubMatcher multipart; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StubMatchers that = (StubMatchers) o; + return Objects.equals(url, that.url) && Objects.equals(body, that.body) + && Objects.equals(headers, that.headers) + && Objects.equals(queryParameters, that.queryParameters) + && Objects.equals(cookies, that.cookies) + && Objects.equals(multipart, that.multipart); + } + + @Override + public int hashCode() { + return Objects.hash(url, body, headers, queryParameters, cookies, multipart); + } + + @Override + public String toString() { + return "StubMatchers{" + "url=" + url + ", body=" + body + ", headers=" + + headers + ", queryParameters=" + queryParameters + ", cookies=" + + cookies + ", multipart=" + multipart + '}'; + } + + } + + public enum MatchingType { + + equal_to, containing, matching, not_matching, equal_to_json, equal_to_xml, absent; + + } + + public static class BodyStubMatcher { + + public String path; + + public StubMatcherType type; + + public String value; + + public PredefinedRegex predefined; + + public Integer minOccurrence; + + public Integer maxOccurrence; + + public RegexType regexType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BodyStubMatcher that = (BodyStubMatcher) o; + return Objects.equals(path, that.path) && type == that.type + && Objects.equals(value, that.value) && predefined == that.predefined + && Objects.equals(minOccurrence, that.minOccurrence) + && Objects.equals(maxOccurrence, that.maxOccurrence) + && regexType == that.regexType; + } + + @Override + public int hashCode() { + return Objects.hash(path, type, value, predefined, minOccurrence, + maxOccurrence, regexType); + } + + @Override + public String toString() { + return "BodyStubMatcher{" + "path='" + path + '\'' + ", type=" + type + + ", value='" + value + '\'' + ", predefined=" + predefined + + ", minOccurrence=" + minOccurrence + ", maxOccurrence=" + + maxOccurrence + ", regexType=" + regexType + '}'; + } + + } + + public enum RegexType { + + as_integer, as_double, as_float, as_long, as_short, as_boolean, as_string; + + } + + public static class MultipartStubMatcher { + + public List params = new ArrayList(); + + public List named = new ArrayList(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MultipartStubMatcher that = (MultipartStubMatcher) o; + return Objects.equals(params, that.params) + && Objects.equals(named, that.named); + } + + @Override + public int hashCode() { + return Objects.hash(params, named); + } + + @Override + public String toString() { + return "MultipartStubMatcher{" + "params=" + params + ", named=" + named + + '}'; + } + + } + + public static class MultipartNamedStubMatcher { + + public String paramName; + + public ValueMatcher fileName; + + public ValueMatcher fileContent; + + public ValueMatcher contentType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MultipartNamedStubMatcher that = (MultipartNamedStubMatcher) o; + return Objects.equals(paramName, that.paramName) + && Objects.equals(fileName, that.fileName) + && Objects.equals(fileContent, that.fileContent) + && Objects.equals(contentType, that.contentType); + } + + @Override + public int hashCode() { + return Objects.hash(paramName, fileName, fileContent, contentType); + } + + @Override + public String toString() { + return "MultipartNamedStubMatcher{" + "paramName='" + paramName + '\'' + + ", fileName=" + fileName + ", fileContent=" + fileContent + + ", contentType=" + contentType + '}'; + } + + } + + public static class ValueMatcher { + + public String regex; + + public PredefinedRegex predefined; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ValueMatcher that = (ValueMatcher) o; + return Objects.equals(regex, that.regex) && predefined == that.predefined; + } + + @Override + public int hashCode() { + return Objects.hash(regex, predefined); + } + + @Override + public String toString() { + return "ValueMatcher{" + "regex='" + regex + '\'' + ", predefined=" + + predefined + '}'; + } + + } + + public static class BodyTestMatcher { + + public String path; + + public TestMatcherType type; + + public String value; + + public Integer minOccurrence; + + public Integer maxOccurrence; + + public PredefinedRegex predefined; + + public RegexType regexType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BodyTestMatcher that = (BodyTestMatcher) o; + return Objects.equals(path, that.path) && type == that.type + && Objects.equals(value, that.value) + && Objects.equals(minOccurrence, that.minOccurrence) + && Objects.equals(maxOccurrence, that.maxOccurrence) + && predefined == that.predefined && regexType == that.regexType; + } + + @Override + public int hashCode() { + return Objects.hash(path, type, value, minOccurrence, maxOccurrence, + predefined, regexType); + } + + @Override + public String toString() { + return "BodyTestMatcher{" + "path='" + path + '\'' + ", type=" + type + + ", value='" + value + '\'' + ", minOccurrence=" + minOccurrence + + ", maxOccurrence=" + maxOccurrence + ", predefined=" + predefined + + ", regexType=" + regexType + '}'; + } + + } + + public static class KeyValueMatcher { + + public String key; + + public String regex; + + public PredefinedRegex predefined; + + public String command; + + public RegexType regexType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + KeyValueMatcher that = (KeyValueMatcher) o; + return Objects.equals(key, that.key) && Objects.equals(regex, that.regex) + && predefined == that.predefined + && Objects.equals(command, that.command) + && regexType == that.regexType; + } + + @Override + public int hashCode() { + return Objects.hash(key, regex, predefined, command, regexType); + } + + @Override + public String toString() { + return "KeyValueMatcher{" + "key='" + key + '\'' + ", regex='" + regex + '\'' + + ", predefined=" + predefined + ", command='" + command + '\'' + + ", regexType=" + regexType + '}'; + } + + } + + public static class HeadersMatcher extends KeyValueMatcher { + + } + + public static class QueryParameterMatcher { + + public String key; + + public MatchingType type; + + public Object value; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + QueryParameterMatcher that = (QueryParameterMatcher) o; + return Objects.equals(key, that.key) && type == that.type + && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(key, type, value); + } + + @Override + public String toString() { + return "QueryParameterMatcher{" + "key='" + key + '\'' + ", type=" + type + + ", value=" + value + '}'; + } + + } + + public static class TestHeaderMatcher { + + public String key; + + public String regex; + + public String command; + + public PredefinedRegex predefined; + + public RegexType regexType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TestHeaderMatcher that = (TestHeaderMatcher) o; + return Objects.equals(key, that.key) && Objects.equals(regex, that.regex) + && Objects.equals(command, that.command) + && predefined == that.predefined && regexType == that.regexType; + } + + @Override + public int hashCode() { + return Objects.hash(key, regex, command, predefined, regexType); + } + + @Override + public String toString() { + return "TestHeaderMatcher{" + "key='" + key + '\'' + ", regex='" + regex + + '\'' + ", command='" + command + '\'' + ", predefined=" + predefined + + ", regexType=" + regexType + '}'; + } + + } + + public static class TestCookieMatcher { + + public String key; + + public String regex; + + public String command; + + public PredefinedRegex predefined; + + public RegexType regexType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TestCookieMatcher that = (TestCookieMatcher) o; + return Objects.equals(key, that.key) && Objects.equals(regex, that.regex) + && Objects.equals(command, that.command) + && predefined == that.predefined && regexType == that.regexType; + } + + @Override + public int hashCode() { + return Objects.hash(key, regex, command, predefined, regexType); + } + + @Override + public String toString() { + return "TestCookieMatcher{" + "key='" + key + '\'' + ", regex='" + regex + + '\'' + ", command='" + command + '\'' + ", predefined=" + predefined + + ", regexType=" + regexType + '}'; + } + + } + + public enum PredefinedRegex { + + only_alpha_unicode, number, any_double, any_boolean, ip_address, hostname, email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank; + + } + + public enum StubMatcherType { + + by_date, by_time, by_timestamp, by_regex, by_equality, by_type, by_null; + + } + + public enum TestMatcherType { + + by_date, by_time, by_timestamp, by_regex, by_equality, by_type, by_command, by_null; + + } + + public static class Response { + + public int status; + + public Map headers = new LinkedHashMap(); + + public Map cookies = new LinkedHashMap(); + + public Object body; + + public String bodyFromFile; + + public String bodyFromFileAsBytes; + + public TestMatchers matchers = new TestMatchers(); + + public Boolean async; + + public Integer fixedDelayMilliseconds; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Response response = (Response) o; + return status == response.status && Objects.equals(headers, response.headers) + && Objects.equals(cookies, response.cookies) + && Objects.equals(body, response.body) + && Objects.equals(bodyFromFile, response.bodyFromFile) + && Objects.equals(bodyFromFileAsBytes, response.bodyFromFileAsBytes) + && Objects.equals(matchers, response.matchers) + && Objects.equals(async, response.async) && Objects.equals( + fixedDelayMilliseconds, response.fixedDelayMilliseconds); + } + + @Override + public int hashCode() { + return Objects.hash(status, headers, cookies, body, bodyFromFile, + bodyFromFileAsBytes, matchers, async, fixedDelayMilliseconds); + } + + @Override + public String toString() { + return "Response{" + "status=" + status + ", headers=" + headers + + ", cookies=" + cookies + ", body=" + body + ", bodyFromFile='" + + bodyFromFile + '\'' + ", bodyFromFileAsBytes='" + + bodyFromFileAsBytes + '\'' + ", matchers=" + matchers + ", async=" + + async + ", fixedDelayMilliseconds=" + fixedDelayMilliseconds + '}'; + } + + } + + public static class TestMatchers { + + public List body = new ArrayList(); + + public List headers = new ArrayList(); + + public List cookies = new ArrayList(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TestMatchers that = (TestMatchers) o; + return Objects.equals(body, that.body) + && Objects.equals(headers, that.headers) + && Objects.equals(cookies, that.cookies); + } + + @Override + public int hashCode() { + return Objects.hash(body, headers, cookies); + } + + @Override + public String toString() { + return "TestMatchers{" + "body=" + body + ", headers=" + headers + + ", cookies=" + cookies + '}'; + } + + } + + public static class Input { + + public String messageFrom; + + public String triggeredBy; + + public Map messageHeaders = new LinkedHashMap(); + + public Object messageBody; + + public String messageBodyFromFile; + + public String messageBodyFromFileAsBytes; + + public String assertThat; + + public StubMatchers matchers = new StubMatchers(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Input input = (Input) o; + return Objects.equals(messageFrom, input.messageFrom) + && Objects.equals(triggeredBy, input.triggeredBy) + && Objects.equals(messageHeaders, input.messageHeaders) + && Objects.equals(messageBody, input.messageBody) + && Objects.equals(messageBodyFromFile, input.messageBodyFromFile) + && Objects.equals(messageBodyFromFileAsBytes, + input.messageBodyFromFileAsBytes) + && Objects.equals(assertThat, input.assertThat) + && Objects.equals(matchers, input.matchers); + } + + @Override + public int hashCode() { + return Objects.hash(messageFrom, triggeredBy, messageHeaders, messageBody, + messageBodyFromFile, messageBodyFromFileAsBytes, assertThat, + matchers); + } + + @Override + public String toString() { + return "Input{" + "messageFrom='" + messageFrom + '\'' + ", triggeredBy='" + + triggeredBy + '\'' + ", messageHeaders=" + messageHeaders + + ", messageBody=" + messageBody + ", messageBodyFromFile='" + + messageBodyFromFile + '\'' + ", messageBodyFromFileAsBytes='" + + messageBodyFromFileAsBytes + '\'' + ", assertThat='" + assertThat + + '\'' + ", matchers=" + matchers + '}'; + } + + } + + public static class OutputMessage { + + public String sentTo; + + public Map headers = new LinkedHashMap(); + + public Object body; + + public String bodyFromFile; + + public String bodyFromFileAsBytes; + + public String assertThat; + + public TestMatchers matchers = new TestMatchers(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OutputMessage that = (OutputMessage) o; + return Objects.equals(sentTo, that.sentTo) + && Objects.equals(headers, that.headers) + && Objects.equals(body, that.body) + && Objects.equals(bodyFromFile, that.bodyFromFile) + && Objects.equals(bodyFromFileAsBytes, that.bodyFromFileAsBytes) + && Objects.equals(assertThat, that.assertThat) + && Objects.equals(matchers, that.matchers); + } + + @Override + public int hashCode() { + return Objects.hash(sentTo, headers, body, bodyFromFile, bodyFromFileAsBytes, + assertThat, matchers); + } + + @Override + public String toString() { + return "OutputMessage{" + "sentTo='" + sentTo + '\'' + ", headers=" + headers + + ", body=" + body + ", bodyFromFile='" + bodyFromFile + '\'' + + ", bodyFromFileAsBytes='" + bodyFromFileAsBytes + '\'' + + ", assertThat='" + assertThat + '\'' + ", matchers=" + matchers + + '}'; + } + + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlToContracts.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlToContracts.groovy index 9a4a8f3356..07d5e2ee26 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlToContracts.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlToContracts.groovy @@ -21,7 +21,6 @@ import java.util.regex.Pattern import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLMapper -import groovy.transform.CompileStatic import groovy.transform.PackageScope import org.yaml.snakeyaml.Yaml @@ -41,12 +40,10 @@ import org.springframework.util.StringUtils import static java.util.stream.Collectors.toSet import static org.springframework.cloud.contract.verifier.util.ContentType.XML import static org.springframework.cloud.contract.verifier.util.ContentUtils.evaluateContentType - /** * @author Marcin Grzejszczak * @author Olga Maciaszek-Sharma */ -@CompileStatic @PackageScope class YamlToContracts { @@ -192,7 +189,7 @@ class YamlToContracts { body(fileAsBytes(yamlContract.request.bodyFromFileAsBytes)) } if (yamlContract.request.multipart) { - Map multipartMap = [:] + Map multipartMap = [:] as Map Map multiPartParams = yamlContract.request .multipart.params. collectEntries { String paramKey, String paramValue -> @@ -685,19 +682,19 @@ class YamlToContracts { } switch (matcher.type) { case YamlContract.MatchingType.equal_to: - return new DslProperty(request.equalTo(matcher.value), value) + return new DslProperty(request.equalTo(matcher.value) as Object, value) case YamlContract.MatchingType.containing: - return new DslProperty(request.containing(matcher.value), value) + return new DslProperty(request.containing(matcher.value) as Object, value) case YamlContract.MatchingType.matching: - return new DslProperty(request.matching(matcher.value), value) + return new DslProperty(request.matching(matcher.value) as Object, value) case YamlContract.MatchingType.not_matching: - return new DslProperty(request.notMatching(matcher.value), value) + return new DslProperty(request.notMatching(matcher.value) as Object, value) case YamlContract.MatchingType.equal_to_json: - return new DslProperty(request.equalToJson(matcher.value), value) + return new DslProperty(request.equalToJson(matcher.value) as Object, value) case YamlContract.MatchingType.equal_to_xml: - return new DslProperty(request.equalToXml(matcher.value), value) + return new DslProperty(request.equalToXml(matcher.value) as Object, value) case YamlContract.MatchingType.absent: - return new DslProperty(request.absent(), null) + return new DslProperty(request.absent() as Object, null) default: throw new UnsupportedOperationException("The provided matching type [" + matcher + "] is unsupported. Use on of " + YamlContract.MatchingType. diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/CloneUtils.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/CloneUtils.java new file mode 100644 index 0000000000..bb44ca0183 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/CloneUtils.java @@ -0,0 +1,43 @@ +/* + * 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 + * + * https://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; + +import org.springframework.util.SerializationUtils; + +/** + * Creates a clone. + * + * @author Marcin Grzejszczak + * @since 2.2.0 + */ +public final class CloneUtils { + + private CloneUtils() { + throw new IllegalStateException("Can't instantiate an utility class"); + } + + /** + * Clones an object if it's serializable. + * @param object to clone + * @return a clone of the object + */ + public static Object clone(Object object) { + byte[] serializedObject = SerializationUtils.serialize(object); + return SerializationUtils.deserialize(serializedObject); + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentUtils.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentUtils.groovy index 6f3caade2a..293946ab6d 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentUtils.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentUtils.groovy @@ -16,9 +16,8 @@ package org.springframework.cloud.contract.verifier.util -import java.util.function.Consumer + import java.util.function.Function -import java.util.function.Supplier import java.util.regex.Matcher import java.util.regex.Pattern @@ -248,7 +247,7 @@ class ContentUtils { bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as String[], - bodyAsValue.strings.clone() as String[] + CloneUtils.clone(bodyAsValue.strings) as String[] ) } @@ -263,7 +262,7 @@ class ContentUtils { private static String extractValueForText(GString bodyAsValue, Closure valueProvider) { GString transformedString = new GStringImpl( bodyAsValue.values.collect { valueProvider(it) } as String[], - bodyAsValue.strings.clone() as String[] + CloneUtils.clone(bodyAsValue.strings) as String[] ) return transformedString.toString() } @@ -274,7 +273,7 @@ class ContentUtils { collect { transformJSONStringValue(it, valueProvider) } as String[], - bodyAsValue.strings.clone() as String[] + CloneUtils.clone(bodyAsValue.strings) as String[] ) def parsedJson = new JsonSlurper(). parseText(transformedString.toString().replace('\\', '\\\\')) @@ -287,7 +286,7 @@ class ContentUtils { collect { transformXMLStringValue(it, valueProvider) } as String[], - bodyAsValue.strings.clone() as String[] + CloneUtils.clone(bodyAsValue.strings) as String[] ) // try to convert it to XML getXmlSlurperWithDefaultErrorHandler() @@ -523,7 +522,7 @@ class ContentUtils { it instanceof String || it instanceof GString ? it.toString() : escapeJson(it.toString()) }) as Object[], - gstring.strings.clone() as String[] + CloneUtils.clone(gstring.strings) as String[] ) try { new JsonSlurper().parseText(stringWithoutValues.toString()) @@ -541,7 +540,7 @@ class ContentUtils { it instanceof String || it instanceof GString ? it.toString() : escapeXml11(it.toString()) }) as Object[], - gString.strings.clone() as String[] + CloneUtils.clone(gString.strings) as String[] ) try { getXmlSlurperWithDefaultErrorHandler() 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 f55846f899..b92b2d5228 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 @@ -195,7 +195,7 @@ class DslToYamlContractConverterSpec extends Specification { ] yamlContract.request.matchers.headers == [ new YamlContract.KeyValueMatcher( - key: "sample", regex: "foo.*") + key: "sample", regex: "foo.*", regexType: YamlContract.RegexType.as_string) ] yamlContract.request.matchers.body == [ new YamlContract.BodyStubMatcher( @@ -208,27 +208,30 @@ class DslToYamlContractConverterSpec extends Specification { new YamlContract.BodyStubMatcher( path: '$.alpha', type: YamlContract.StubMatcherType.by_regex, - predefined: YamlContract.PredefinedRegex.only_alpha_unicode), + value: "[\\p{L}]*"), new YamlContract.BodyStubMatcher( path: '$.alpha', type: YamlContract.StubMatcherType.by_equality), new YamlContract.BodyStubMatcher( path: '$.number', type: YamlContract.StubMatcherType.by_regex, - predefined: YamlContract.PredefinedRegex.number), + value: "-?(\\d*\\.\\d+|\\d+)"), new YamlContract.BodyStubMatcher( path: '$.aBoolean', type: YamlContract.StubMatcherType.by_regex, - predefined: YamlContract.PredefinedRegex.any_boolean), + value: "(true|false)"), new YamlContract.BodyStubMatcher( path: '$.date', - type: YamlContract.StubMatcherType.by_date), + type: YamlContract.StubMatcherType.by_date, + value: "(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"), new YamlContract.BodyStubMatcher( path: '$.dateTime', - type: YamlContract.StubMatcherType.by_timestamp), + type: YamlContract.StubMatcherType.by_timestamp, + value: "([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])"), new YamlContract.BodyStubMatcher( path: '$.time', - type: YamlContract.StubMatcherType.by_time), + type: YamlContract.StubMatcherType.by_time, + value: "(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"), new YamlContract.BodyStubMatcher( path: "\$.['key'].['complex.key']", type: YamlContract.StubMatcherType.by_equality), @@ -261,9 +264,9 @@ class DslToYamlContractConverterSpec extends Specification { ] yamlContract.response.matchers.headers == [ new YamlContract.TestHeaderMatcher( - key: "Some-Header", regex: "[a-zA-Z]{9}"), + key: "Content-Type", regex: "application/json.*"), new YamlContract.TestHeaderMatcher( - key: "Content-Type", regex: "application/json.*") + key: "Some-Header", regex: "[a-zA-Z]{9}", regexType: YamlContract.RegexType.as_string) ] yamlContract.response.matchers.body == [ new YamlContract.BodyTestMatcher( @@ -302,15 +305,15 @@ class DslToYamlContractConverterSpec extends Specification { value: '(true|false)'), new YamlContract.BodyTestMatcher( path: '$.date', - type: YamlContract.TestMatcherType.by_regex, + type: YamlContract.TestMatcherType.by_date, value: '(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])'), new YamlContract.BodyTestMatcher( path: '$.dateTime', - type: YamlContract.TestMatcherType.by_regex, + type: YamlContract.TestMatcherType.by_timestamp, value: '([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])'), new YamlContract.BodyTestMatcher( path: '$.time', - type: YamlContract.TestMatcherType.by_regex, + type: YamlContract.TestMatcherType.by_time, value: '(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'), new YamlContract.BodyTestMatcher( path: '$.valueWithTypeMatch', @@ -388,13 +391,14 @@ class DslToYamlContractConverterSpec extends Specification { ] yamlContract.request.matchers.headers == [ new YamlContract.KeyValueMatcher( - key: "Content-Type", regex: "application/json.*") + key: "Content-Type", regex: "application/json.*", regexType: YamlContract.RegexType.as_string) ] yamlContract.request.matchers.body == [ new YamlContract.BodyStubMatcher( - path: '$.duck', + path: "\$.['client.id']", type: YamlContract.StubMatcherType.by_regex, - value: "[0-9]{3}"), + value: "[0-9]{10}", + regexType: YamlContract.RegexType.as_string), ] yamlContract.response.status == 200 yamlContract.response.body == [fraudCheckStatus : "FRAUD", @@ -408,9 +412,10 @@ class DslToYamlContractConverterSpec extends Specification { ] yamlContract.response.matchers.body == [ new YamlContract.BodyTestMatcher( - path: '$.fraudCheckStatus', + path: "\$.['fraudCheckStatus']", type: YamlContract.TestMatcherType.by_regex, - value: "FRAUD"), + value: "FRAUD", + regexType: YamlContract.RegexType.as_string), ] } @@ -465,11 +470,13 @@ class DslToYamlContractConverterSpec extends Specification { yamlContract.request.matchers.multipart.params == [ new YamlContract.KeyValueMatcher( key: "formParameter", - regex: '".+"' + regex: '".+"', + regexType: YamlContract.RegexType.as_string ), new YamlContract.KeyValueMatcher( key: "someBooleanParameter", - regex: '(true|false)' + regex: '(true|false)', + regexType: YamlContract.RegexType.as_string ) ] yamlContract.request.matchers.multipart.named == [ @@ -553,8 +560,8 @@ class DslToYamlContractConverterSpec extends Specification { yamlContract.request.method == 'GET' yamlContract.request.url == '/get' yamlContract.request.body.replaceAll("\n", "") - .replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "") - .replaceAll(' ', '') + .replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "") + .replaceAll(' ', '') yamlContract.request.headers == [ "Content-Type": "application/xml" ] @@ -566,8 +573,8 @@ class DslToYamlContractConverterSpec extends Specification { ] yamlContract.response.status == 200 yamlContract.response.body.replaceAll("\n", "") - .replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "") - .replaceAll(' ', '') + .replaceAll(' ', '') == xmlContractBody.replaceAll("\n", "") + .replaceAll(' ', '') yamlContract.response.matchers.body == [ new YamlContract.BodyTestMatcher( path: '/test/duck/xxx', 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 fcf6065fa8..181a8041d4 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 @@ -145,7 +145,7 @@ class YamlContractConverterSpec extends Specification { contract.response.cookies.entries.find { it.key == "fooPredefinedRegex" && ((Pattern) it.serverValue).pattern == "(true|false)" && it.clientValue == true } - contract.response.body.clientValue == ["status": "OK"] + MapConverter.getStubSideValues(contract.response.body) == ["status": "OK"] } def "should convert YAML with REST to DSL for [#yamlFile]"() { @@ -176,7 +176,7 @@ class YamlContractConverterSpec extends Specification { it.name == "fooReq" && it.serverValue == "baz" } - contract.request.body.clientValue == [foo: "bar"] + MapConverter.getStubSideValues(contract.request.body) == [foo: "bar"] contract.request.bodyMatchers.matchers[0].path() == '$.foo' contract.request.bodyMatchers.matchers[0].matchingType() == REGEX contract.request.bodyMatchers.matchers[0].value().pattern() == 'bar' @@ -200,7 +200,7 @@ class YamlContractConverterSpec extends Specification { it.name == "fooRes" && it.clientValue == "baz" } - contract.response.body.clientValue == [foo2: "bar", foo3: "baz", nullValue: null] + MapConverter.getStubSideValues(contract.response.body) == [foo2: "bar", foo3: "baz", nullValue: null] contract.response.bodyMatchers.matchers[0].path() == '$.foo2' contract.response.bodyMatchers.matchers[0].matchingType() == REGEX contract.response.bodyMatchers.matchers[0].value().pattern() == 'bar' @@ -230,8 +230,8 @@ class YamlContractConverterSpec extends Specification { url.queryParameters.parameters[1].serverValue == "bar2" contract.request.method.clientValue == "GET" contract.request.headers.entries.findAll { it.name == "Authorization" } - .collect { it.clientValue }.flatten() == ["secret", "secret2"] - contract.request.body.clientValue == [foo: "bar", baz: 5] + .collect { it.clientValue }.flatten() == ["secret", "secret2"] + MapConverter.getStubSideValues(contract.request.body) == [foo: "bar", baz: 5] and: contract.response.status.clientValue == 200 contract.response.headers.entries @@ -1113,7 +1113,7 @@ ignored: false ] yamlContract.input.matchers.headers == [ new YamlContract.KeyValueMatcher( - key: "sample", regex: "foo.*") + key: "sample", regex: "foo.*", regexType: YamlContract.RegexType.as_string) ] yamlContract.input.matchers.body == [ new YamlContract.BodyStubMatcher( @@ -1126,27 +1126,30 @@ ignored: false new YamlContract.BodyStubMatcher( path: '$.alpha', type: YamlContract.StubMatcherType.by_regex, - predefined: YamlContract.PredefinedRegex.only_alpha_unicode), + value: "[\\p{L}]*"), new YamlContract.BodyStubMatcher( path: '$.alpha', type: YamlContract.StubMatcherType.by_equality), new YamlContract.BodyStubMatcher( path: '$.number', type: YamlContract.StubMatcherType.by_regex, - predefined: YamlContract.PredefinedRegex.number), + value: "-?(\\d*\\.\\d+|\\d+)"), new YamlContract.BodyStubMatcher( path: '$.aBoolean', type: YamlContract.StubMatcherType.by_regex, - predefined: YamlContract.PredefinedRegex.any_boolean), + value: "(true|false)"), new YamlContract.BodyStubMatcher( path: '$.date', - type: YamlContract.StubMatcherType.by_date), + type: YamlContract.StubMatcherType.by_date, + value: "(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"), new YamlContract.BodyStubMatcher( path: '$.dateTime', - type: YamlContract.StubMatcherType.by_timestamp), + type: YamlContract.StubMatcherType.by_timestamp, + value: "([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])"), new YamlContract.BodyStubMatcher( path: '$.time', - type: YamlContract.StubMatcherType.by_time), + type: YamlContract.StubMatcherType.by_time, + value: "(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"), new YamlContract.BodyStubMatcher( path: "\$.['key'].['complex.key']", type: YamlContract.StubMatcherType.by_equality), @@ -1175,7 +1178,7 @@ ignored: false ] yamlContract.outputMessage.matchers.headers == [ new YamlContract.TestHeaderMatcher( - key: "Content-Type", regex: "application/json.*") + key: "Some-Header", regex: "[a-zA-Z]{9}", regexType: YamlContract.RegexType.as_string) ] yamlContract.outputMessage.matchers.body == [ new YamlContract.BodyTestMatcher( @@ -1214,15 +1217,15 @@ ignored: false value: '(true|false)'), new YamlContract.BodyTestMatcher( path: '$.date', - type: YamlContract.TestMatcherType.by_regex, + type: YamlContract.TestMatcherType.by_date, value: '(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])'), new YamlContract.BodyTestMatcher( path: '$.dateTime', - type: YamlContract.TestMatcherType.by_regex, + type: YamlContract.TestMatcherType.by_timestamp, value: '([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])'), new YamlContract.BodyTestMatcher( path: '$.time', - type: YamlContract.TestMatcherType.by_regex, + type: YamlContract.TestMatcherType.by_time, value: '(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'), new YamlContract.BodyTestMatcher( path: '$.valueWithTypeMatch', @@ -1317,26 +1320,26 @@ ignored: false contract.response.bodyMatchers.matchers[4] .value().pattern() == patterns.isoTime().pattern() contract.response.body.clientValue.replaceAll("\n", "") - .replaceAll(' ', '') == xmlContractBody + .replaceAll(' ', '') == xmlContractBody .replaceAll("\n", "").replaceAll(' ', '') contract.response.body.serverValue.replaceAll("\n", "") - .replaceAll(' ', '') == xmlContractBody + .replaceAll(' ', '') == xmlContractBody .replaceAll("\n", "").replaceAll(' ', '') } - def "should accept a yaml file that is a proper scc YAML contract"(){ + def "should accept a yaml file that is a proper scc YAML contract"() { when: - def accepted = converter.isAccepted(ymlWithRest3) + def accepted = converter.isAccepted(ymlWithRest3) then: - accepted + accepted } - def "should not accept a YAML file that is not a scc YAML contract"(){ + def "should not accept a YAML file that is not a scc YAML contract"() { when: - def accepted = converter.isAccepted(oa3File) + def accepted = converter.isAccepted(oa3File) then: - !accepted + !accepted } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy index acac2ed89a..6c5db7ba87 100755 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy @@ -2400,11 +2400,11 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie server.start() server.addStubMapping(WireMockStubMapping.buildFrom(json)) then: - String entity = callApiCategories(port) + ResponseEntity entity = callApiCategories(port) and: AssertionUtil.assertThatJsonsAreEqual((''' ["[\\"Programming\\",\\"Java\\"]","[\\"Programming\\",\\"Java\\",\\"Spring\\",\\"Boot\\"]"] - '''), entity) + '''), entity.getBody()) cleanup: server?.shutdown() } @@ -2820,12 +2820,12 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie .body(request.bytes), byte[].class) } - String callApiCategories(int port) { + ResponseEntity callApiCategories(int port) { return new TestRestTemplate().exchange( RequestEntity. post(URI.create("http://localhost:" + port + "/api/categories")) - .header("Content-Type", "application/json;charset=UTF-8") - .body(JsonOutput. - toJson([["Programming", "Java"], ["Programming", "Java", "Spring", "Boot"]])), String.class).body + .header("Content-Type", "application/json;charset=UTF-8") + .body(JsonOutput. + toJson([["Programming", "Java"], ["Programming", "Java", "Spring", "Boot"]])), String.class) } } diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index f12aaf9df6..e44b819c1e 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -41,4 +41,5 @@ +