diff --git a/.gitignore b/.gitignore index 439afc42b8..5eed16c58b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ bin/ # Package Files # *.war *.ear +*.versionsBackup # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* @@ -32,6 +33,7 @@ hs_err_pid* .gradle/ target/ build/ +out/ hs_err_pid* .DS_Store diff --git a/README.adoc b/README.adoc index 4bb695ffdc..ed626744c5 100644 --- a/README.adoc +++ b/README.adoc @@ -39,7 +39,7 @@ produced by Spring Cloud Contract Verifier. * Messaging routes, if you're using a messaging service. We integrate with Spring Integration, Spring Cloud Stream, Spring AMQP, and Apache Camel. You can also set your own integrations. -* Acceptance tests (in JUnit or Spock) are used to verify if server-side implementation +* Acceptance tests (in JUnit 4, JUnit 5 or Spock) are used to verify if server-side implementation of the API is compliant with the contract (__server tests__). A full test is generated by Spring Cloud Contract Verifier. @@ -234,7 +234,6 @@ run the collaborators' stubs for you, as shown in the following example: @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { ---- @@ -401,8 +400,8 @@ mode for HTTP contracts. However, JAX-RX client and explicit HTTP invocations ca used. (To do so, change the `testMode` property of the plugin to `JAX-RS` or `EXPLICIT`, respectively.) -Apart from the default JUnit, you can instead use Spock tests, by setting the plugin -`testFramework` property to `Spock`. +Apart from the default JUnit 4, you can instead use JUnit 5 or Spock tests, by setting the plugin +`testFramework` property to either `JUNIT5` or `Spock`. TIP: You can now also generate WireMock scenarios based on the contracts, by including an order number followed by an underscore at the beginning of the contract file names. @@ -519,7 +518,6 @@ the collaborators' stubs for you, as shown in the following example: @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { ---- @@ -693,7 +691,6 @@ Annotate your test class with `@AutoConfigureStubRunner`. In the annotation prov @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { ---- @@ -1035,8 +1032,7 @@ of an identifier or a timestamp, you need not hardcode a value. You want to allo different ranges of values. To enable ranges of values, you can set regular expressions matching those values for the consumer side. You can provide the body by means of either a map notation or String with interpolations. -https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#_contract_dsl[Consult the docs -for more information.] We highly recommend using the map notation! +Consult the <> section for more information. We highly recommend using the map notation! TIP: You must understand the map notation in order to set up contracts. Please read the http://groovy-lang.org/json.html[Groovy docs regarding JSON]. @@ -1069,7 +1065,7 @@ First, add the `Spring Cloud Contract` BOM. org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release.version} pom import @@ -1150,7 +1146,7 @@ Add the `Spring Cloud Contract` BOM: org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release-train.version} pom import @@ -1180,7 +1176,6 @@ can also provide the offline work switch (`StubRunnerProperties.StubsMode.LOCAL` @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { ---- @@ -1447,23 +1442,13 @@ https://github.com/spring-cloud-samples/spring-cloud-contract-samples[samples]. == Links -The following links may be helpful when working with Spring Cloud Contract Verifier: +The following links may be helpful when working with Spring Cloud Contract: * https://github.com/spring-cloud/spring-cloud-contract/[Spring Cloud Contract Github Repository] * https://github.com/spring-cloud-samples/spring-cloud-contract-samples/[Spring Cloud Contract Samples] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html[Spring Cloud -Contract Documentation] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/deprecated[Accurest -Legacy Documentation] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#spring-cloud-contract-stub-runner[Spring -Cloud Contract Stub Runner Documentation] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#stub-runner-for-messaging[Spring -Cloud Contract Stub Runner Messaging Documentation] * https://gitter.im/spring-cloud/spring-cloud-contract[Spring Cloud Contract Gitter] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/[Spring -Cloud Contract Maven Plugin] * https://www.youtube.com/watch?v=sAAklvxmPmk[Spring Cloud Contract WJUG Presentation by Marcin Grzejszczak] @@ -1857,8 +1842,7 @@ methods to create request matchers, but you can't use both approaches. On the consumer side, you can make the `resource.json` generated earlier in this section available on the classpath (by -https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_publishing_stubs_as_jars[publishing -stubs as JARs], for example). After that, you can create a stub using WireMock in a +< org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-docker-parent pom - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT Spring Cloud Contract Docker Parent Spring Cloud Contract Docker Parent diff --git a/docker/spring-cloud-contract-docker/get_dependencies.sh b/docker/spring-cloud-contract-docker/get_dependencies.sh index 520e770abb..3da068eb59 100755 --- a/docker/spring-cloud-contract-docker/get_dependencies.sh +++ b/docker/spring-cloud-contract-docker/get_dependencies.sh @@ -2,7 +2,7 @@ set -e -WRAPPER_VERSION="4.4.1" +WRAPPER_VERSION="4.10" GRADLE_BIN_DIR="gradle-${WRAPPER_VERSION}-bin" GRADLE_WRAPPER_DIR="${HOME}/.gradle/wrapper/dists/${GRADLE_BIN_DIR}" CURRENT_DIR="$( pwd )" diff --git a/docker/spring-cloud-contract-docker/pom.xml b/docker/spring-cloud-contract-docker/pom.xml index b904088e6f..06db33b9d1 100644 --- a/docker/spring-cloud-contract-docker/pom.xml +++ b/docker/spring-cloud-contract-docker/pom.xml @@ -7,20 +7,20 @@ org.springframework.cloud spring-cloud-contract-docker-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-docker pom - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT Spring Cloud Contract Docker Spring Cloud Contract Docker org.codehaus.groovy - groovy-all + groovy compile diff --git a/docker/spring-cloud-contract-docker/project/build.gradle b/docker/spring-cloud-contract-docker/project/build.gradle index 12a7e0b935..e7975be1ed 100644 --- a/docker/spring-cloud-contract-docker/project/build.gradle +++ b/docker/spring-cloud-contract-docker/project/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { - classpath "io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE" + classpath "io.spring.gradle:dependency-management-plugin:1.0.6.RELEASE" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifierVersion}" } } @@ -83,7 +83,7 @@ contracts { } task wrapper(type: Wrapper) { - gradleVersion = '4.4.1' + gradleVersion = '4.9' } task cleanOutput(type: Delete) { diff --git a/docker/spring-cloud-contract-docker/project/gradle.properties b/docker/spring-cloud-contract-docker/project/gradle.properties index 02d46236c4..183e436e72 100644 --- a/docker/spring-cloud-contract-docker/project/gradle.properties +++ b/docker/spring-cloud-contract-docker/project/gradle.properties @@ -1,2 +1,2 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT +verifierVersion=2.1.0.BUILD-SNAPSHOT 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 99340b4ad1..28861d273a 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/gradle/wrapper/gradle-wrapper.properties b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties index 2c2bbe5f9a..115e6ac0aa 100644 --- a/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties +++ b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip diff --git a/docker/spring-cloud-contract-stub-runner-docker/pom.xml b/docker/spring-cloud-contract-stub-runner-docker/pom.xml index 1305d85edb..225819da6e 100644 --- a/docker/spring-cloud-contract-stub-runner-docker/pom.xml +++ b/docker/spring-cloud-contract-stub-runner-docker/pom.xml @@ -7,13 +7,13 @@ org.springframework.cloud spring-cloud-contract-docker-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-docker pom - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT Spring Cloud Contract Stub Runner Docker Spring Cloud Contract Stub Runner Docker diff --git a/docs/pom.xml b/docs/pom.xml index 644d4e4222..5d8c5fb915 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-docs diff --git a/docs/src/main/asciidoc/links.adoc b/docs/src/main/asciidoc/links.adoc index 35a5f9dcfe..e6104db048 100644 --- a/docs/src/main/asciidoc/links.adoc +++ b/docs/src/main/asciidoc/links.adoc @@ -1,21 +1,11 @@ == Links -The following links may be helpful when working with Spring Cloud Contract Verifier: +The following links may be helpful when working with Spring Cloud Contract: * https://github.com/spring-cloud/spring-cloud-contract/[Spring Cloud Contract Github Repository] * https://github.com/spring-cloud-samples/spring-cloud-contract-samples/[Spring Cloud Contract Samples] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html[Spring Cloud -Contract Documentation] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/deprecated[Accurest -Legacy Documentation] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#spring-cloud-contract-stub-runner[Spring -Cloud Contract Stub Runner Documentation] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#stub-runner-for-messaging[Spring -Cloud Contract Stub Runner Messaging Documentation] * https://gitter.im/spring-cloud/spring-cloud-contract[Spring Cloud Contract Gitter] -* https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/[Spring -Cloud Contract Maven Plugin] * https://www.youtube.com/watch?v=sAAklvxmPmk[Spring Cloud Contract WJUG Presentation by Marcin Grzejszczak] diff --git a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc index 0cb52f37ba..ba88b8f67c 100644 --- a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc +++ b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc @@ -324,8 +324,7 @@ methods to create request matchers, but you can't use both approaches. On the consumer side, you can make the `resource.json` generated earlier in this section available on the classpath (by -https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_publishing_stubs_as_jars[publishing -stubs as JARs], for example). After that, you can create a stub using WireMock in a +< + org.springframework.cloud + spring-cloud-contract-maven-plugin + ${spring-cloud-contract.version} + true + + EXPLICIT + + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +contracts { + testMode = 'EXPLICIT' +} +---- + +The following example shows how to set up a base class and Rest Assured for Web Flux: + +[source,groovy,indent=0] +---- +include::{samples_url}/producer_webflux/src/test/java/com/example/BeerRestBase.java[tags=annotations,indent=0] + + // your tests go here + + // in this config class you define all controllers and mocked services +include::{samples_url}/producer_webflux/src/test/java/com/example/BeerRestBase.java[tags=config,indent=0] + +} +---- + === Messaging Top-Level Elements The DSL for messaging looks a little bit different than the one that focuses on HTTP. The diff --git a/docs/src/main/asciidoc/verifier_faq.adoc b/docs/src/main/asciidoc/verifier_faq.adoc index a15f8f8170..974c25c768 100644 --- a/docs/src/main/asciidoc/verifier_faq.adoc +++ b/docs/src/main/asciidoc/verifier_faq.adoc @@ -1,5 +1,5 @@ :introduction_url: https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/{branch} -:samples_branch: 2.0.x +:samples_branch: 2.1.x == Spring Cloud Contract FAQ @@ -127,7 +127,7 @@ $(stub(...), test(...)) $(client(...), server(...)) ---- -You can read more about this in the https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#_contract_dsl[Contract DSL section]. +You can read more about this in the <> section. Calling `value()` or `$()` tells Spring Cloud Contract that you will be passing a dynamic value. Inside the `consumer()` method you pass the value that should be used on the consumer side (in the generated stub). @@ -344,84 +344,10 @@ contracts in the folder per topic. ===== For Maven Project -To make it possible to work on the producer side we could do the following things (all via Maven plugins): +To make it possible to work on the producer side we should specify an inclusion pattern for +filtering common repository jar by messaging topics we are interested in. ```includedFiles``` property of ```Maven Spring Cloud Contract plugin``` +allows us to do that. Also ```contractsPath``` need to be specified since the default path would be the common repository ```groupid/artifactid```. -- Add common repo dependency to your classpath: - -[source,xml,indent=0] ----- - - com.example - common-repo - ${common-repo.version} - ----- - -- Download the JAR with the contracts and unpack the JAR to target: - -[source,xml,indent=0] ----- - - org.apache.maven.plugins - maven-dependency-plugin - 3.0.0 - - - unpack-dependencies - process-resources - - unpack - - - - - com.example - common-repo - jar - false - ${project.build.directory}/contracts - - - - - - ----- - -- Rip out all the folders we're not interested in: - -[source,xml,indent=0] ----- - - org.apache.maven.plugins - maven-antrun-plugin - 1.8 - - - process-resources - - run - - - - - - - - - - - - - - - - - - ----- - -- Run the contract plugin by pointing to the contracts to the folder under target: [source,xml,indent=0] ---- @@ -429,16 +355,30 @@ To make it possible to work on the producer side we could do the following thing org.springframework.cloud spring-cloud-contract-maven-plugin ${spring-cloud-contract.version} - true - com.example + REMOTE + http://link/to/your/nexus/or/artifactory/or/sth + + com.example + common-repo-with-contracts + + + + / - .*intoxication.* - com.example.intoxication.BeerIntoxicationBase + .*messaging.* + com.example.services.MessagingBase + + + .*rest.* + com.example.services.TestBase - ${project.build.directory}/contracts + + **/${project.artifactId}/** + **/${first-topic}/** + **/${second-topic}/** + ---- diff --git a/docs/src/main/asciidoc/verifier_introduction.adoc b/docs/src/main/asciidoc/verifier_introduction.adoc index ba81ef9acb..30302124b3 100644 --- a/docs/src/main/asciidoc/verifier_introduction.adoc +++ b/docs/src/main/asciidoc/verifier_introduction.adoc @@ -1,8 +1,5 @@ == Spring Cloud Contract Verifier Introduction -TIP: The Accurest project was initially started by Marcin Grzejszczak and Jakub Kubrynski -(http://codearte.io[codearte.io]) - Spring Cloud Contract Verifier enables Consumer Driven Contract (CDC) development of JVM-based applications. It moves TDD to the level of software architecture. @@ -15,10 +12,18 @@ produced by Spring Cloud Contract Verifier. * Messaging routes, if you're using a messaging service. We integrate with Spring Integration, Spring Cloud Stream, Spring AMQP, and Apache Camel. You can also set your own integrations. -* Acceptance tests (in JUnit or Spock) are used to verify if server-side implementation +* Acceptance tests (in JUnit 4, JUnit 5 or Spock) are used to verify if server-side implementation of the API is compliant with the contract (__server tests__). A full test is generated by Spring Cloud Contract Verifier. +=== History + +Before becoming Spring Cloud Contract, this project was called https://github.com/Codearte/accurest[Accurest]. +It was created by https://twitter.com/mgrzejszczak[Marcin Grzejszczak] and https://twitter.com/jkubrynski[Jakub Kubrynski] +from (http://codearte.io[codearte.io]. + +The `0.1.0` release took place on 26 Jan 2015 and it became stable with `1.0.0` release on 29 Feb 2016. + === Why a Contract Verifier? Assume that we have a system consisting of multiple microservices: @@ -200,7 +205,6 @@ run the collaborators' stubs for you, as shown in the following example: @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { ---- @@ -363,8 +367,8 @@ mode for HTTP contracts. However, JAX-RX client and explicit HTTP invocations ca used. (To do so, change the `testMode` property of the plugin to `JAX-RS` or `EXPLICIT`, respectively.) -Apart from the default JUnit, you can instead use Spock tests, by setting the plugin -`testFramework` property to `Spock`. +Apart from the default JUnit 4, you can instead use JUnit 5 or Spock tests, by setting the plugin +`testFramework` property to either `JUNIT5` or `Spock`. TIP: You can now also generate WireMock scenarios based on the contracts, by including an order number followed by an underscore at the beginning of the contract file names. @@ -475,7 +479,6 @@ the collaborators' stubs for you, as shown in the following example: @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { ---- @@ -698,8 +701,7 @@ of an identifier or a timestamp, you need not hardcode a value. You want to allo different ranges of values. To enable ranges of values, you can set regular expressions matching those values for the consumer side. You can provide the body by means of either a map notation or String with interpolations. -https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#_contract_dsl[Consult the docs -for more information.] We highly recommend using the map notation! +Consult the <> section for more information. We highly recommend using the map notation! TIP: You must understand the map notation in order to set up contracts. Please read the http://groovy-lang.org/json.html[Groovy docs regarding JSON]. diff --git a/docs/src/main/asciidoc/verifier_messaging.adoc b/docs/src/main/asciidoc/verifier_messaging.adoc index ec9736890b..811353d1ff 100644 --- a/docs/src/main/asciidoc/verifier_messaging.adoc +++ b/docs/src/main/asciidoc/verifier_messaging.adoc @@ -69,8 +69,8 @@ messaging configuration, so you only need the one annotation. === Publisher-Side Test Generation Having the `input` or `outputMessage` sections in your DSL results in creation of tests -on the publisher's side. By default, JUnit tests are created. However, there is also a -possibility to create Spock tests. +on the publisher's side. By default, JUnit 4 tests are created. However, there is also a +possibility to create JUnit 5 or Spock tests. There are 3 main scenarios that we should take into consideration: @@ -87,7 +87,7 @@ it is resolved as a channel name. For *Camel*, that's a certain component (for e ==== Scenario 1: No Input Message -Here is an example for Camel. For the given contract: +For the given contract: .Groovy DSL [source,groovy] @@ -117,7 +117,7 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract ==== Scenario 2: Output Triggered by Input -Here is an example for Camel. For the given contract: +For the given contract: .Groovy DSL [source,groovy] @@ -147,7 +147,7 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract ==== Scenario 3: No Output Message -Here is an example for Camel. For the given contract: +For the given contract: .Groovy DSL [source,groovy] @@ -180,9 +180,7 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract Unlike the HTTP part, in messaging, we need to publish the Groovy DSL inside the JAR with a stub. Then it is parsed on the consumer side and proper stubbed routes are created. -For more information, see -https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#stub-runner-for-messaging[the -Stub Runner Messaging sections]. +For more information, see <> section. [source,xml,indent=0,subs="verbatim,attributes",role="primary"] .Maven diff --git a/docs/src/main/asciidoc/verifier_setup.adoc b/docs/src/main/asciidoc/verifier_setup.adoc index aca67ea259..d755b539ee 100644 --- a/docs/src/main/asciidoc/verifier_setup.adoc +++ b/docs/src/main/asciidoc/verifier_setup.adoc @@ -245,13 +245,14 @@ from the Groovy DSL should be placed. By default its value is `$buildDir/generated-test-sources/contractVerifier`. * *stubsOutputDir*: Specifies the directory where the generated WireMock stubs from the Groovy DSL should be placed. -* *targetFramework*: Specifies the target test framework to be used. Currently, Spock and -JUnit are supported with JUnit being the default framework. +* *targetFramework*: Specifies the target test framework to be used. Currently, Spock, JUnit 4 (`TestFramework.JUNIT` and +JUnit 5 are supported with JUnit 4 being the default framework. * *contractsProperties*: a map containing properties to be passed to Spring Cloud Contract components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders. The following properties are used when you want to specify the location of the JAR containing the contracts: + * *contractDependency*: Specifies the Dependency that provides `groupid:artifactid:version:classifier` coordinates. You can use the `contractDependency` closure to set it up. @@ -260,8 +261,6 @@ closure to set it up. separated. Otherwise, it scans contracts under the provided directory. * *contractsMode*: Specifies the mode of downloading contracts (whether the JAR is available offline, remotely etc.) -* *contractsSnapshotCheckSkip*: If set to `true` will not assert whether the -downloaded stubs / contract JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact). * *deleteStubsAfterTest*: If set to `false` will not remove any downloaded contracts from temporary directories @@ -438,8 +437,8 @@ include::{standalone_samples_path}/http-server/pom.xml[tags=contract_maven_plugi ---- You can read more in the -https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/[Spring -Cloud Contract Maven Plugin Documentation]. +https://cloud.spring.io/spring-cloud-static/spring-cloud-contract/2.0.0.RELEASE/spring-cloud-contract-maven-plugin/[Spring +Cloud Contract Maven Plugin Documentation (example for `2.0.0.RELEASE` version)]. [[maven-rest-assured]] ==== Maven and Rest Assured 2.0 @@ -582,8 +581,8 @@ classes. use Spock classes, the class is `spock.lang.Specification`. * *contractsDirectory*: Specifies a directory containing contracts written with the GroovyDSL. The default directory is `/src/test/resources/contracts`. -* *testFramework*: Specifies the target test framework to be used. Currently, Spock and -JUnit are supported with JUnit being the default framework +* *testFramework*: Specifies the target test framework to be used. Currently, Spock, JUnit 4 (`TestFramework.JUNIT` and +6JUnit 5 are supported with JUnit 4 being the default framework. * *packageWithBaseClasses*: Defines a package where all the base classes reside. This setting takes precedence over *baseClassForTests*. The convention is such that, if you have a contract under (for example) `src/test/resources/contract/foo/bar/baz/` and set @@ -609,8 +608,6 @@ the following options: * *contractsPath*: The path to the concrete contracts in the JAR with packaged contracts. Defaults to `groupid/artifactid` where `gropuid` is slash separated. * *contractsMode*: Picks the mode in which stubs will be found and registered -* *contractsSnapshotCheckSkip*: If `true` then will not assert whether a stub / contract -JAR was downloaded from local or remote location * *deleteStubsAfterTest*: If set to `false` will not remove any downloaded contracts from temporary directories * *contractsRepositoryUrl*: URL to a repo with the artifacts that have contracts. If it is not provided, @@ -944,27 +941,6 @@ For example, you might decide to have no dependencies at all. As a consumer, if you add the stub dependency to your classpath, you can explicitly exclude the unwanted dependencies. -=== CI Server setup - -When fetching stubs / contracts in a CI, shared environment, what might happen is that -both the producer and the consumer reuse the same local Maven repository. Due to this, -the framework, responsible for downloading a stub JAR from remote location, -can't decide which JAR should be picked, local or remote one. That caused -the `"The artifact was found in the local repository but you have explicitly -stated that it should be downloaded from a remote one"` exception -and failed the build. - -For such cases we're introducing the property and plugin setup mechanism: - -- via `stubrunner.snapshot-check-skip` system property -- via `STUBRUNNER_SNAPSHOT_CHECK_SKIP` environment variable - -if either of these values is set to `true`, then the stub downloader will not -verify the origin of the downloaded JAR. - -For the plugins you need to set the `contractsSnapshotSkipCheck` property -to `true`. - === Scenarios You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to @@ -989,7 +965,7 @@ name of `scenario1` and the three following steps: . logout marked as `Step2` which will close the scenario. More details about WireMock scenarios can be found at -http://wiremock.org/stateful-behaviour.html[http://wiremock.org/stateful-behaviour.html] +http://wiremock.org/docs/stateful-behaviour/[http://wiremock.org/docs/stateful-behaviour/] Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution. diff --git a/docs/src/main/asciidoc/verifier_stubrunner.adoc b/docs/src/main/asciidoc/verifier_stubrunner.adoc index b637b07a91..a29dfc2b69 100644 --- a/docs/src/main/asciidoc/verifier_stubrunner.adoc +++ b/docs/src/main/asciidoc/verifier_stubrunner.adoc @@ -25,6 +25,7 @@ include::{standalone_samples_path}/http-server/pom.xml[tags=repos,indent=0] include::{standalone_samples_path}/http-server/build.gradle[tags=repos,indent=0] ---- +[[publishing-stubs-as-jars]] === Publishing Stubs as JARs The easiest approach would be to centralize the way stubs are kept. For example, you can @@ -154,7 +155,7 @@ $ STUBRUNNER_PORT="8083" $ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876" $ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local" # Run the docker with Stub Runner Boot -$ docker run --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}" +$ docker run --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}" ``` What's happening is that @@ -174,4 +175,8 @@ $ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", " # Now time for the second request $ curl -X GET http://localhost:9876/api/books # You will receive contents of the JSON -``` \ No newline at end of file +``` + +IMPORTANT: If you want use the stubs that you have built locally, on your host, +then you should pass the environment variable `-e STUBRUNNER_STUBS_MODE=LOCAL` and mount +the volume of your local m2 `-v "${HOME}/.m2/:/root/.m2:ro"` \ No newline at end of file diff --git a/docs/src/main/asciidoc/verifier_stubrunner_msg.adoc b/docs/src/main/asciidoc/verifier_stubrunner_msg.adoc index 5402d775ec..725c4e8db9 100644 --- a/docs/src/main/asciidoc/verifier_stubrunner_msg.adoc +++ b/docs/src/main/asciidoc/verifier_stubrunner_msg.adoc @@ -1,3 +1,4 @@ +[[stub-runner-for-messaging]] == Stub Runner for Messaging Stub Runner can run the published stubs in memory. It can integrate with the following diff --git a/pom.xml b/pom.xml index 4d79ad5990..cf35a25857 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,13 @@ org.springframework.cloud spring-cloud-build - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT spring-cloud-contract-parent pom - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT Spring Cloud Contract Spring Cloud Contract @@ -26,22 +26,24 @@ 2.17 3.5.13 0.0.9 - 2.0.0.BUILD-SNAPSHOT - 2.0.0.BUILD-SNAPSHOT - Elmhurst.BUILD-SNAPSHOT - 2.0.0.BUILD-SNAPSHOT - 2.0.0.BUILD-SNAPSHOT - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + Fishtown.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT 5.0.3 3.2.4 1.0-groovy-2.4 0.5.1 - 2.0.0.RELEASE + 2.1.0.M2 0.2.2 1.1 4.0.6 4.6.0.201612231935-r 1 + + 2.4.15 @@ -83,11 +85,31 @@ org.spockframework spock-spring ${spock-spring.version} + + + org.codehaus.groovy + groovy-all + + + org.codehaus.groovy + groovy + + org.spockframework spock-core ${spock-spring.version} + + + org.codehaus.groovy + groovy-all + + + org.codehaus.groovy + groovy + + info.solidsoft.spock @@ -99,10 +121,6 @@ spring-rabbit ${spring-rabbit.version} - - org.mockito - mockito-core - io.specto hoverfly-junit @@ -122,6 +140,10 @@ org.codehaus.groovy groovy-all + + org.codehaus.groovy + groovy + @@ -133,6 +155,10 @@ org.codehaus.groovy groovy-all + + org.codehaus.groovy + groovy + @@ -204,6 +230,97 @@ pom import + + + org.codehaus.groovy + groovy + ${groovy.version} + + + org.codehaus.groovy + groovy-ant + ${groovy.version} + + + org.codehaus.groovy + groovy-bsf + ${groovy.version} + + + org.codehaus.groovy + groovy-console + ${groovy.version} + + + org.codehaus.groovy + groovy-docgenerator + ${groovy.version} + + + org.codehaus.groovy + groovy-groovydoc + ${groovy.version} + + + org.codehaus.groovy + groovy-groovysh + ${groovy.version} + + + org.codehaus.groovy + groovy-jmx + ${groovy.version} + + + org.codehaus.groovy + groovy-json + ${groovy.version} + + + org.codehaus.groovy + groovy-jsr223 + ${groovy.version} + + + org.codehaus.groovy + groovy-nio + ${groovy.version} + + + org.codehaus.groovy + groovy-servlet + ${groovy.version} + + + org.codehaus.groovy + groovy-sql + ${groovy.version} + + + org.codehaus.groovy + groovy-swing + ${groovy.version} + + + org.codehaus.groovy + groovy-templates + ${groovy.version} + + + org.codehaus.groovy + groovy-test + ${groovy.version} + + + org.codehaus.groovy + groovy-testng + ${groovy.version} + + + org.codehaus.groovy + groovy-xml + ${groovy.version} + diff --git a/samples/pom.xml b/samples/pom.xml index 507f0b65c8..ed713758fd 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-samples pom - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT Spring Cloud Contract Samples Spring Cloud Contract Samples diff --git a/samples/standalone/contracts/com/example/server/pom.xml b/samples/standalone/contracts/com/example/server/pom.xml index 28ae7a8852..46e83e1cb4 100644 --- a/samples/standalone/contracts/com/example/server/pom.xml +++ b/samples/standalone/contracts/com/example/server/pom.xml @@ -13,15 +13,15 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT - Finchley.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT true @@ -30,7 +30,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release.version} pom import diff --git a/samples/standalone/dsl/http-client/build.gradle b/samples/standalone/dsl/http-client/build.gradle index d94f4e1306..632fc1e96d 100644 --- a/samples/standalone/dsl/http-client/build.gradle +++ b/samples/standalone/dsl/http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } diff --git a/samples/standalone/dsl/http-client/gradle.properties b/samples/standalone/dsl/http-client/gradle.properties index 57ed1c5339..3e2eb84942 100644 --- a/samples/standalone/dsl/http-client/gradle.properties +++ b/samples/standalone/dsl/http-client/gradle.properties @@ -1,2 +1,2 @@ org.gradle.daemon=false -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..1e5e33908b 100644 Binary files a/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..7ef53d35d1 100644 --- a/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/dsl/http-client/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:41:49 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/dsl/http-client/gradlew b/samples/standalone/dsl/http-client/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/dsl/http-client/gradlew +++ b/samples/standalone/dsl/http-client/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/dsl/http-client/pom.xml b/samples/standalone/dsl/http-client/pom.xml index 438f237aca..d260444499 100644 --- a/samples/standalone/dsl/http-client/pom.xml +++ b/samples/standalone/dsl/http-client/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE UTF-8 1.8 - Finchley.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT @@ -59,7 +59,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release-train.version} pom import diff --git a/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java b/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java index 13fe27ed58..1eb7e0a364 100644 --- a/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java +++ b/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java @@ -1,7 +1,5 @@ package com.example.loan; -import static org.assertj.core.api.Assertions.assertThat; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -9,7 +7,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.example.loan.model.Client; @@ -17,11 +14,12 @@ import com.example.loan.model.LoanApplication; import com.example.loan.model.LoanApplicationResult; import com.example.loan.model.LoanApplicationStatus; +import static org.assertj.core.api.Assertions.assertThat; + @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE, properties="server.context-path=/app") @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceContextPathTests { @Autowired diff --git a/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java index 3ce01ce1a9..ab8eeff9a8 100644 --- a/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java +++ b/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -1,11 +1,5 @@ package com.example.loan; -import com.example.loan.model.Client; -import com.example.loan.model.LoanApplication; -import com.example.loan.model.LoanApplicationResult; -import com.example.loan.model.LoanApplicationStatus; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; import io.restassured.RestAssured; import io.restassured.response.ResponseOptions; import io.restassured.specification.RequestSpecification; @@ -16,9 +10,15 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; +import com.example.loan.model.Client; +import com.example.loan.model.LoanApplication; +import com.example.loan.model.LoanApplicationResult; +import com.example.loan.model.LoanApplicationStatus; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; + import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; @@ -27,7 +27,6 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { // end::autoconfigure_stubrunner[] diff --git a/samples/standalone/dsl/http-client/src/test/resources/application.yaml b/samples/standalone/dsl/http-client/src/test/resources/application.yaml index 79ba03fbb4..fcdfd10d16 100644 --- a/samples/standalone/dsl/http-client/src/test/resources/application.yaml +++ b/samples/standalone/dsl/http-client/src/test/resources/application.yaml @@ -1,3 +1,5 @@ server: port: 6565 -logging.level.org.springframework.cloud.contract: DEBUG \ No newline at end of file +logging: + level: + org.springframework.cloud.contract: debug \ No newline at end of file diff --git a/samples/standalone/dsl/http-server/build.gradle b/samples/standalone/dsl/http-server/build.gradle index 596cdefcc2..cb9fdbcf70 100644 --- a/samples/standalone/dsl/http-server/build.gradle +++ b/samples/standalone/dsl/http-server/build.gradle @@ -10,7 +10,7 @@ buildscript { // end::repos[] dependencies { classpath 'org.codehaus.groovy:groovy-all:2.5.0-beta-1' - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" } } diff --git a/samples/standalone/dsl/http-server/gradle.properties b/samples/standalone/dsl/http-server/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/dsl/http-server/gradle.properties +++ b/samples/standalone/dsl/http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..258e37c4e6 100644 Binary files a/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..68ec43b5c6 100644 --- a/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/dsl/http-server/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:41:57 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/dsl/http-server/gradlew b/samples/standalone/dsl/http-server/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/dsl/http-server/gradlew +++ b/samples/standalone/dsl/http-server/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/dsl/http-server/pom.xml b/samples/standalone/dsl/http-server/pom.xml index 463ac693bc..692483290d 100644 --- a/samples/standalone/dsl/http-server/pom.xml +++ b/samples/standalone/dsl/http-server/pom.xml @@ -13,15 +13,15 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT - Finchley.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT @@ -55,7 +55,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release.version} pom import @@ -105,7 +105,7 @@ - org.eclipse.m2e diff --git a/samples/standalone/dsl/pom.xml b/samples/standalone/dsl/pom.xml index 0515f233b6..76f79cf082 100644 --- a/samples/standalone/dsl/pom.xml +++ b/samples/standalone/dsl/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/messaging/pom.xml b/samples/standalone/messaging/pom.xml index bf67ee7ecc..ea244d7730 100644 --- a/samples/standalone/messaging/pom.xml +++ b/samples/standalone/messaging/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/messaging/stream-sink/build.gradle b/samples/standalone/messaging/stream-sink/build.gradle index 434697f360..4ed306b40d 100644 --- a/samples/standalone/messaging/stream-sink/build.gradle +++ b/samples/standalone/messaging/stream-sink/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } diff --git a/samples/standalone/messaging/stream-sink/gradle.properties b/samples/standalone/messaging/stream-sink/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/messaging/stream-sink/gradle.properties +++ b/samples/standalone/messaging/stream-sink/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..3fc01f4742 100644 Binary files a/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..e9f0600549 100644 --- a/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/messaging/stream-sink/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:42:27 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/messaging/stream-sink/gradlew b/samples/standalone/messaging/stream-sink/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/messaging/stream-sink/gradlew +++ b/samples/standalone/messaging/stream-sink/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/messaging/stream-sink/pom.xml b/samples/standalone/messaging/stream-sink/pom.xml index bbb152bbf8..b1ed1d69d6 100644 --- a/samples/standalone/messaging/stream-sink/pom.xml +++ b/samples/standalone/messaging/stream-sink/pom.xml @@ -14,14 +14,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT @@ -48,7 +48,7 @@ org.springframework.cloud spring-cloud-dependencies - Finchley.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT pom import diff --git a/samples/standalone/messaging/stream-source/build.gradle b/samples/standalone/messaging/stream-source/build.gradle index 99d89d5d6e..3bc8fbd40a 100644 --- a/samples/standalone/messaging/stream-source/build.gradle +++ b/samples/standalone/messaging/stream-source/build.gradle @@ -7,10 +7,11 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" - classpath "com.jayway.restassured:rest-assured:2.9.0" - classpath "com.jayway.restassured:spring-mock-mvc:2.9.0" +// Example of using Rest Assured 3.x. If you don't add this section Rest Assured 2.9.0 will be used +// classpath "com.jayway.restassured:rest-assured:2.9.0" +// classpath "com.jayway.restassured:spring-mock-mvc:2.9.0" } } @@ -55,8 +56,9 @@ dependencies { testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier' testCompile "org.springframework.cloud:spring-cloud-stream-test-support" - testCompile "com.jayway.restassured:rest-assured:2.9.0" - testCompile "com.jayway.restassured:spring-mock-mvc:2.9.0" + // Example of using Rest Assured 3.x. If you don't add this section Rest Assured 2.9.0 will be used + // testCompile "com.jayway.restassured:rest-assured:2.9.0" + // testCompile "com.jayway.restassured:spring-mock-mvc:2.9.0" } task stubsJar(type: Jar, dependsOn: "generateClientStubs") { diff --git a/samples/standalone/messaging/stream-source/gradle.properties b/samples/standalone/messaging/stream-source/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/messaging/stream-source/gradle.properties +++ b/samples/standalone/messaging/stream-source/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..275158bd43 100644 Binary files a/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..862babf24f 100644 --- a/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/messaging/stream-source/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:42:35 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/messaging/stream-source/gradlew b/samples/standalone/messaging/stream-source/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/messaging/stream-source/gradlew +++ b/samples/standalone/messaging/stream-source/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/messaging/stream-source/pom.xml b/samples/standalone/messaging/stream-source/pom.xml index 2ea0dfef8e..c45a710c42 100644 --- a/samples/standalone/messaging/stream-source/pom.xml +++ b/samples/standalone/messaging/stream-source/pom.xml @@ -14,17 +14,21 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-web + org.springframework.cloud spring-cloud-starter-stream-rabbit @@ -41,23 +45,12 @@ org.springframework.cloud - spring-cloud-contract-verifier - ${spring-cloud-contract.version} + spring-cloud-starter-contract-verifier test - - - io.rest-assured - rest-assured - - - io.rest-assured - spring-mock-mvc - - - + @@ -76,7 +69,7 @@ org.springframework.cloud spring-cloud-dependencies - Finchley.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT pom import @@ -104,7 +97,7 @@ - + diff --git a/samples/standalone/messaging/stream-source/src/test/java/com/example/source/SensorSourceTestBase.java b/samples/standalone/messaging/stream-source/src/test/java/com/example/source/SensorSourceTestBase.java index 7070d24c91..63d504e5f8 100644 --- a/samples/standalone/messaging/stream-source/src/test/java/com/example/source/SensorSourceTestBase.java +++ b/samples/standalone/messaging/stream-source/src/test/java/com/example/source/SensorSourceTestBase.java @@ -16,7 +16,7 @@ package com.example.source; -import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc; +import io.restassured.module.mockmvc.RestAssuredMockMvc; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/samples/standalone/pact/pact-http-client/build.gradle b/samples/standalone/pact/pact-http-client/build.gradle index edfc27350c..5f80e75981 100644 --- a/samples/standalone/pact/pact-http-client/build.gradle +++ b/samples/standalone/pact/pact-http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } diff --git a/samples/standalone/pact/pact-http-client/gradle.properties b/samples/standalone/pact/pact-http-client/gradle.properties index 57ed1c5339..3e2eb84942 100644 --- a/samples/standalone/pact/pact-http-client/gradle.properties +++ b/samples/standalone/pact/pact-http-client/gradle.properties @@ -1,2 +1,2 @@ org.gradle.daemon=false -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..a281a3f7d8 100644 Binary files a/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..60a50fd5c3 100644 --- a/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/pact/pact-http-client/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:42:45 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/pact/pact-http-client/gradlew b/samples/standalone/pact/pact-http-client/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/pact/pact-http-client/gradlew +++ b/samples/standalone/pact/pact-http-client/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/pact/pact-http-client/pom.xml b/samples/standalone/pact/pact-http-client/pom.xml index 3341e4d201..4fef2721c1 100644 --- a/samples/standalone/pact/pact-http-client/pom.xml +++ b/samples/standalone/pact/pact-http-client/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE UTF-8 1.8 - Finchley.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT @@ -57,7 +57,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release-train.version} pom import diff --git a/samples/standalone/pact/pact-http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/samples/standalone/pact/pact-http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java index 56d33ae6de..3bf935987d 100644 --- a/samples/standalone/pact/pact-http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java +++ b/samples/standalone/pact/pact-http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -11,7 +11,6 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.core.env.Environment; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.example.loan.model.Client; @@ -23,7 +22,6 @@ import com.example.loan.model.LoanApplicationStatus; @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:pact-http-server:+:stubs"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { @Autowired diff --git a/samples/standalone/pact/pact-http-server/build.gradle b/samples/standalone/pact/pact-http-server/build.gradle index 28d2a09cba..77f0c962e7 100644 --- a/samples/standalone/pact/pact-http-server/build.gradle +++ b/samples/standalone/pact/pact-http-server/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" //tag::pact_dependency[] classpath "org.springframework.cloud:spring-cloud-contract-pact:${findProperty('verifierVersion') ?: verifierVersion}" diff --git a/samples/standalone/pact/pact-http-server/gradle.properties b/samples/standalone/pact/pact-http-server/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/pact/pact-http-server/gradle.properties +++ b/samples/standalone/pact/pact-http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..9689b2f157 100644 Binary files a/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..45f69f9cb3 100644 --- a/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/pact/pact-http-server/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:42:54 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/pact/pact-http-server/gradlew b/samples/standalone/pact/pact-http-server/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/pact/pact-http-server/gradlew +++ b/samples/standalone/pact/pact-http-server/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/pact/pact-http-server/pom.xml b/samples/standalone/pact/pact-http-server/pom.xml index 6ee769851f..ee2b7cbeda 100644 --- a/samples/standalone/pact/pact-http-server/pom.xml +++ b/samples/standalone/pact/pact-http-server/pom.xml @@ -13,15 +13,15 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT - Finchley.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT @@ -52,7 +52,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release.version} pom import @@ -108,7 +108,7 @@ - org.eclipse.m2e diff --git a/samples/standalone/pact/pom.xml b/samples/standalone/pact/pom.xml index 2035c0bc75..fc17d64ea8 100644 --- a/samples/standalone/pact/pom.xml +++ b/samples/standalone/pact/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests with Pact - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/pom.xml b/samples/standalone/pom.xml index c44d15f4fe..30047e0104 100644 --- a/samples/standalone/pom.xml +++ b/samples/standalone/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-samples - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-samples-standalone pom - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT Spring Cloud Contract Standalone Test Samples Spring Cloud Contract Standalone Test Samples used for end to end tests diff --git a/samples/standalone/restdocs/http-client/build.gradle b/samples/standalone/restdocs/http-client/build.gradle index bf8d56ccc5..843d6d68ae 100644 --- a/samples/standalone/restdocs/http-client/build.gradle +++ b/samples/standalone/restdocs/http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } diff --git a/samples/standalone/restdocs/http-client/gradle.properties b/samples/standalone/restdocs/http-client/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/restdocs/http-client/gradle.properties +++ b/samples/standalone/restdocs/http-client/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..64b0894d0e 100644 Binary files a/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..024f10d7e4 100644 --- a/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/restdocs/http-client/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:43:04 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/restdocs/http-client/gradlew b/samples/standalone/restdocs/http-client/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/restdocs/http-client/gradlew +++ b/samples/standalone/restdocs/http-client/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/restdocs/http-client/pom.xml b/samples/standalone/restdocs/http-client/pom.xml index 58ec98488f..87282005b2 100644 --- a/samples/standalone/restdocs/http-client/pom.xml +++ b/samples/standalone/restdocs/http-client/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/ServiceProperties.java b/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/ServiceProperties.java new file mode 100644 index 0000000000..0dff3bd2da --- /dev/null +++ b/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/ServiceProperties.java @@ -0,0 +1,17 @@ +package com.example.loan; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("service") +public class ServiceProperties { + + private int port = 8080; + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return this.port; + } +} diff --git a/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java index fef4e71669..ab17861df9 100644 --- a/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java +++ b/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -29,10 +29,10 @@ public class LoanApplicationServiceTests { @Autowired private LoanApplicationService service; - @Value("classpath:META-INF/com.example/http-server-restdocs/0.0.1-SNAPSHOT/mappings/markClientAsFraud.json") + @Value("classpath:META-INF/com.example/http-server-restdocs/0.0.1-SNAPSHOT/mappings/standalone/shouldMarkClientAsFraud.json") private Resource markClientAsFraud; - @Value("classpath:META-INF/com.example/http-server-restdocs/0.0.1-SNAPSHOT/mappings/markClientAsNotFraud.json") + @Value("classpath:META-INF/com.example/http-server-restdocs/0.0.1-SNAPSHOT/mappings/webapp/shouldMarkClientAsNotFraud.json") private Resource markClientAsNotFraud; @Autowired diff --git a/samples/standalone/restdocs/http-server/build.gradle b/samples/standalone/restdocs/http-server/build.gradle index 5797f5c1f7..61478d402d 100644 --- a/samples/standalone/restdocs/http-server/build.gradle +++ b/samples/standalone/restdocs/http-server/build.gradle @@ -10,7 +10,7 @@ buildscript { maven { url "http://repo.spring.io/plugins-staging-local/" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${project.findProperty('verifierVersion') ?: verifierVersion}" } } @@ -70,7 +70,7 @@ contracts { generateClientStubs.enabled = false task wrapper(type: Wrapper) { - gradleVersion = '4.0.2' + gradleVersion = '4.10' } task stubsJar(type: Jar, dependsOn: ['copySnippets', 'copySources', 'copyClasses']) { diff --git a/samples/standalone/restdocs/http-server/gradle.properties b/samples/standalone/restdocs/http-server/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/restdocs/http-server/gradle.properties +++ b/samples/standalone/restdocs/http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..b1d61a8bce 100644 Binary files a/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..79022e9646 100644 --- a/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/restdocs/http-server/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:43:13 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/restdocs/http-server/gradlew b/samples/standalone/restdocs/http-server/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/restdocs/http-server/gradlew +++ b/samples/standalone/restdocs/http-server/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/restdocs/http-server/pom.xml b/samples/standalone/restdocs/http-server/pom.xml index db18d0aa9d..7fdd22744d 100644 --- a/samples/standalone/restdocs/http-server/pom.xml +++ b/samples/standalone/restdocs/http-server/pom.xml @@ -13,15 +13,15 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT - true + 2.1.0.BUILD-SNAPSHOT + true diff --git a/samples/standalone/restdocs/http-server/settings.gradle b/samples/standalone/restdocs/http-server/settings.gradle index a27eed8db1..06780ec1e9 100644 --- a/samples/standalone/restdocs/http-server/settings.gradle +++ b/samples/standalone/restdocs/http-server/settings.gradle @@ -1 +1 @@ -rootProject.name = 'http-server-restdocs-gradle' +rootProject.name = 'http-server-restdocs' diff --git a/samples/standalone/restdocs/http-server/src/assembly/stub.xml b/samples/standalone/restdocs/http-server/src/assembly/stub.xml index 914f265c70..b459d2a636 100644 --- a/samples/standalone/restdocs/http-server/src/assembly/stub.xml +++ b/samples/standalone/restdocs/http-server/src/assembly/stub.xml @@ -29,5 +29,12 @@ **/* + + ${project.build.directory}/stubs + / + + **/* + + \ No newline at end of file diff --git a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java index 8e8a1462bf..60cb3ab6f5 100644 --- a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java +++ b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java @@ -12,7 +12,6 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.json.JacksonTester; import org.springframework.http.MediaType; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -25,10 +24,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) -@AutoConfigureRestDocs(outputDir = "target/snippets") +@AutoConfigureRestDocs @AutoConfigureMockMvc @AutoConfigureJsonTesters -@DirtiesContext public class StubGeneratorTests { @Autowired diff --git a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/XmlStubGeneratorTests.java b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/XmlStubGeneratorTests.java index b36e22bad5..1180334a06 100644 --- a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/XmlStubGeneratorTests.java +++ b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/XmlStubGeneratorTests.java @@ -15,7 +15,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.json.JacksonTester; import org.springframework.http.MediaType; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -31,7 +30,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(classes = Application.class) @AutoConfigureRestDocs(outputDir = "target/snippets") @AutoConfigureMockMvc -@DirtiesContext public class XmlStubGeneratorTests { @Autowired diff --git a/samples/standalone/restdocs/pom.xml b/samples/standalone/restdocs/pom.xml index ac78d0ad0d..1015d252f9 100644 --- a/samples/standalone/restdocs/pom.xml +++ b/samples/standalone/restdocs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/webclient/http-client/build.gradle b/samples/standalone/webclient/http-client/build.gradle index 3bbcea3bea..69bcfe6ad2 100644 --- a/samples/standalone/webclient/http-client/build.gradle +++ b/samples/standalone/webclient/http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } diff --git a/samples/standalone/webclient/http-client/gradle.properties b/samples/standalone/webclient/http-client/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/webclient/http-client/gradle.properties +++ b/samples/standalone/webclient/http-client/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..36df4eded6 100644 Binary files a/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..d456add56b 100644 --- a/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/webclient/http-client/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:43:22 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/webclient/http-client/gradlew b/samples/standalone/webclient/http-client/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/webclient/http-client/gradlew +++ b/samples/standalone/webclient/http-client/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/webclient/http-client/pom.xml b/samples/standalone/webclient/http-client/pom.xml index c5a8c45e32..cd865fd7b8 100644 --- a/samples/standalone/webclient/http-client/pom.xml +++ b/samples/standalone/webclient/http-client/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/webclient/http-server/build.gradle b/samples/standalone/webclient/http-server/build.gradle index a033d304e7..f31960b114 100644 --- a/samples/standalone/webclient/http-server/build.gradle +++ b/samples/standalone/webclient/http-server/build.gradle @@ -10,7 +10,7 @@ buildscript { maven { url "http://repo.spring.io/plugins-staging-local/" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } diff --git a/samples/standalone/webclient/http-server/gradle.properties b/samples/standalone/webclient/http-server/gradle.properties index cb360cc45f..88a97742d7 100644 --- a/samples/standalone/webclient/http-server/gradle.properties +++ b/samples/standalone/webclient/http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..48cdc59515 100644 Binary files a/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.jar and b/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..155630a64b 100644 --- a/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/webclient/http-server/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:43:29 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/webclient/http-server/gradlew b/samples/standalone/webclient/http-server/gradlew index 4453ccea33..cccdd3d517 100755 --- a/samples/standalone/webclient/http-server/gradlew +++ b/samples/standalone/webclient/http-server/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/samples/standalone/webclient/http-server/pom.xml b/samples/standalone/webclient/http-server/pom.xml index eb12881204..4fa059f70d 100644 --- a/samples/standalone/webclient/http-server/pom.xml +++ b/samples/standalone/webclient/http-server/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/webclient/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java b/samples/standalone/webclient/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java index 5363234e1d..01367f7c04 100644 --- a/samples/standalone/webclient/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java +++ b/samples/standalone/webclient/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java @@ -16,7 +16,6 @@ import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWeb import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.json.JacksonTester; import org.springframework.http.MediaType; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.BodyInserters; @@ -28,7 +27,6 @@ import static org.springframework.cloud.contract.wiremock.restdocs.WireMockWebTe @AutoConfigureRestDocs(outputDir = "target/snippets") @AutoConfigureWebTestClient @AutoConfigureJsonTesters -@DirtiesContext public class StubGeneratorTests { @Autowired diff --git a/samples/standalone/webclient/pom.xml b/samples/standalone/webclient/pom.xml index 91c397521a..2797844013 100644 --- a/samples/standalone/webclient/pom.xml +++ b/samples/standalone/webclient/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/standalone/yml/http-client/build.gradle b/samples/standalone/yml/http-client/build.gradle index 42387d97af..0e035eaeee 100644 --- a/samples/standalone/yml/http-client/build.gradle +++ b/samples/standalone/yml/http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" } } @@ -48,7 +48,7 @@ test { } task wrapper(type: Wrapper) { - gradleVersion = '2.14' + gradleVersion = '4.10' } task resolveDependencies { diff --git a/samples/standalone/yml/http-client/gradle.properties b/samples/standalone/yml/http-client/gradle.properties index d765e1c4aa..72d5eb58b7 100644 --- a/samples/standalone/yml/http-client/gradle.properties +++ b/samples/standalone/yml/http-client/gradle.properties @@ -1,2 +1,2 @@ org.gradle.daemon=false -BOM_VERSION=Finchley.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT diff --git a/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties index 2c2bbe5f9a..c1714705ed 100644 --- a/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/yml/http-client/pom.xml b/samples/standalone/yml/http-client/pom.xml index 532799d20b..36cc7fd03a 100644 --- a/samples/standalone/yml/http-client/pom.xml +++ b/samples/standalone/yml/http-client/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE UTF-8 1.8 - Finchley.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT @@ -52,7 +52,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release.version} pom import diff --git a/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java index 4a62dad689..e418990149 100644 --- a/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java +++ b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java @@ -9,7 +9,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.example.loan.model.Client; @@ -21,7 +20,6 @@ import com.example.loan.model.LoanApplicationStatus; @SpringBootTest(webEnvironment=WebEnvironment.NONE, properties="server.context-path=/app") @AutoConfigureStubRunner(ids = {"com.example:http-server-yml:+:stubs:6569"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceContextPathTests { @Autowired diff --git a/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java index 7fa34bb0dc..a0949d753c 100644 --- a/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java +++ b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -7,7 +7,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.example.loan.model.Client; @@ -22,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment=WebEnvironment.NONE) @AutoConfigureStubRunner(ids = {"com.example:http-server-yml:+:stubs:6569"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) -@DirtiesContext public class LoanApplicationServiceTests { // end::autoconfigure_stubrunner[] diff --git a/samples/standalone/yml/http-server/build.gradle b/samples/standalone/yml/http-server/build.gradle index 00149b07a1..49c98fa6cd 100644 --- a/samples/standalone/yml/http-server/build.gradle +++ b/samples/standalone/yml/http-server/build.gradle @@ -9,7 +9,7 @@ buildscript { } // end::repos[] dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.M2" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" } } @@ -59,7 +59,7 @@ test { } task wrapper(type: Wrapper) { - gradleVersion = '4.4.1' + gradleVersion = '4.10' } clean.doFirst { diff --git a/samples/standalone/yml/http-server/gradle.properties b/samples/standalone/yml/http-server/gradle.properties index 4aa503e80a..c3a4cd72dc 100644 --- a/samples/standalone/yml/http-server/gradle.properties +++ b/samples/standalone/yml/http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=2.0.0.BUILD-SNAPSHOT -BOM_VERSION=Finchley.BUILD-SNAPSHOT +verifierVersion=2.1.0.BUILD-SNAPSHOT +BOM_VERSION=Greenwich.BUILD-SNAPSHOT diff --git a/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties index 2c2bbe5f9a..c1714705ed 100644 --- a/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties +++ b/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/samples/standalone/yml/http-server/pom.xml b/samples/standalone/yml/http-server/pom.xml index 0e98af9be2..6b982b9ef7 100644 --- a/samples/standalone/yml/http-server/pom.xml +++ b/samples/standalone/yml/http-server/pom.xml @@ -13,15 +13,15 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.1.0.M2 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT - Finchley.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT + Greenwich.BUILD-SNAPSHOT @@ -55,7 +55,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud-dependencies.version} + ${spring-cloud-release.version} pom import diff --git a/samples/standalone/yml/pom.xml b/samples/standalone/yml/pom.xml index 85c4bb6199..068892b4b6 100644 --- a/samples/standalone/yml/pom.xml +++ b/samples/standalone/yml/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/wiremock-jetty/pom.xml b/samples/wiremock-jetty/pom.xml index 8fbf7d65f3..11ac98b3be 100644 --- a/samples/wiremock-jetty/pom.xml +++ b/samples/wiremock-jetty/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-jetty - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT jar wiremock-jetty @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.RELEASE + 2.1.0.M1 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsClassRuleTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsClassRuleTests.java index 94aff4b974..be39bc68f2 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsClassRuleTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsClassRuleTests.java @@ -13,14 +13,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @ActiveProfiles("classrule") -@DirtiesContext //tag::wiremock_test1[] @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java index 8bbcec20ff..ff698af3a0 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java @@ -8,12 +8,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockRestServiceServer; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; -@DirtiesContext // tag::wiremock_test[] @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.NONE) diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsTests.java index 80ee38dd34..60e5a7d37d 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockForDocsTests.java @@ -8,7 +8,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.core.env.Environment; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; @@ -19,7 +18,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; @ActiveProfiles("docs") -@DirtiesContext //tag::wiremock_test1[] @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java index 93b83c12d5..f720c6faed 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java @@ -10,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.contract.wiremock.AutoConfigureHttpClient; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -21,7 +20,6 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest("app.baseUrl=https://localhost:8443") -@DirtiesContext @AutoConfigureHttpClient public class WiremockHttpsServerApplicationTests { diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockImportApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockImportApplicationTests.java index 121f8e2d50..acc0215f92 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockImportApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockImportApplicationTests.java @@ -12,13 +12,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6060", webEnvironment=WebEnvironment.NONE) -@DirtiesContext @AutoConfigureWireMock(port = 6060) public class WiremockImportApplicationTests { diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockMockServerApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockMockServerApplicationTests.java index 6044625d39..f06509d3da 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockMockServerApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockMockServerApplicationTests.java @@ -8,14 +8,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockRestServiceServer; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockMockServerApplicationTests { @Autowired diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java index 9bbb1671d6..2a3f475fdd 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java @@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -28,7 +27,6 @@ import static org.hamcrest.CoreMatchers.instanceOf; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6061", webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockServerApplicationTests { @ClassRule diff --git a/samples/wiremock-native/pom.xml b/samples/wiremock-native/pom.xml index 213e758593..caab291dcf 100644 --- a/samples/wiremock-native/pom.xml +++ b/samples/wiremock-native/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-native - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT jar wiremock-native @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.RELEASE + 2.1.0.M1 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/wiremock-native/src/test/java/com/example/WiremockServerApplicationTests.java b/samples/wiremock-native/src/test/java/com/example/WiremockServerApplicationTests.java index faef1e5ff2..46caa3a55f 100644 --- a/samples/wiremock-native/src/test/java/com/example/WiremockServerApplicationTests.java +++ b/samples/wiremock-native/src/test/java/com/example/WiremockServerApplicationTests.java @@ -12,14 +12,12 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6063", webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockServerApplicationTests { @ClassRule diff --git a/samples/wiremock-tomcat/pom.xml b/samples/wiremock-tomcat/pom.xml index 63489e2b00..bbbba762b9 100644 --- a/samples/wiremock-tomcat/pom.xml +++ b/samples/wiremock-tomcat/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-tomcat - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT jar wiremock-tomcat @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.RELEASE + 2.1.0.M1 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java index 9e30002fdf..0eed2de134 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java @@ -13,7 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.contract.wiremock.AutoConfigureHttpClient; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @@ -21,7 +20,6 @@ import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @RunWith(SpringRunner.class) @SpringBootTest("app.baseUrl=https://localhost:6443") -@DirtiesContext @AutoConfigureHttpClient public class WiremockHttpsServerApplicationTests { diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportApplicationTests.java index 7169d26908..5d6e1081ef 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportApplicationTests.java @@ -12,13 +12,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6065", webEnvironment=WebEnvironment.NONE) -@DirtiesContext @AutoConfigureWireMock(port = 6065) public class WiremockImportApplicationTests { diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportContextPathApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportContextPathApplicationTests.java index c46ffe2873..d0205aa2f2 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportContextPathApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockImportContextPathApplicationTests.java @@ -12,13 +12,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(properties = { "app.baseUrl=http://localhost:6066", "server.context-path=/app" }, webEnvironment = WebEnvironment.NONE) -@DirtiesContext @AutoConfigureWireMock(port = 6066) public class WiremockImportContextPathApplicationTests { diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockMockServerApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockMockServerApplicationTests.java index bf75089652..c719a6de49 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockMockServerApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockMockServerApplicationTests.java @@ -8,14 +8,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockRestServiceServer; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockMockServerApplicationTests { @Autowired diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java index d4c5656527..fd10183cd3 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java @@ -17,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -29,7 +28,6 @@ import static org.hamcrest.CoreMatchers.instanceOf; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6067", webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockServerApplicationTests { @ClassRule diff --git a/samples/wiremock-undertow-ssl/pom.xml b/samples/wiremock-undertow-ssl/pom.xml index 2509769d4d..c7871b2402 100644 --- a/samples/wiremock-undertow-ssl/pom.xml +++ b/samples/wiremock-undertow-ssl/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow-ssl - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT jar wiremock-undertow-ssl @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.RELEASE + 2.1.0.M1 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/wiremock-undertow-ssl/src/test/java/com/example/WiremockHttpsServerApplicationTests.java b/samples/wiremock-undertow-ssl/src/test/java/com/example/WiremockHttpsServerApplicationTests.java index 4bd913d950..4064803f9f 100644 --- a/samples/wiremock-undertow-ssl/src/test/java/com/example/WiremockHttpsServerApplicationTests.java +++ b/samples/wiremock-undertow-ssl/src/test/java/com/example/WiremockHttpsServerApplicationTests.java @@ -12,7 +12,6 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.SocketUtils; @@ -22,7 +21,6 @@ import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @RunWith(SpringRunner.class) @SpringBootTest("app.baseUrl=https://localhost:7443") -@DirtiesContext @ActiveProfiles("ssl") public class WiremockHttpsServerApplicationTests { diff --git a/samples/wiremock-undertow/pom.xml b/samples/wiremock-undertow/pom.xml index 5a56c2956e..b801ff7b97 100644 --- a/samples/wiremock-undertow/pom.xml +++ b/samples/wiremock-undertow/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT jar wiremock-undertow @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.RELEASE + 2.1.0.M1 UTF-8 1.8 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT diff --git a/samples/wiremock-undertow/src/test/java/com/example/WiremockImportApplicationTests.java b/samples/wiremock-undertow/src/test/java/com/example/WiremockImportApplicationTests.java index fd6c29e060..9c6501404f 100644 --- a/samples/wiremock-undertow/src/test/java/com/example/WiremockImportApplicationTests.java +++ b/samples/wiremock-undertow/src/test/java/com/example/WiremockImportApplicationTests.java @@ -12,13 +12,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:7070", webEnvironment=WebEnvironment.NONE) -@DirtiesContext @AutoConfigureWireMock(port = 7070) public class WiremockImportApplicationTests { diff --git a/samples/wiremock-undertow/src/test/java/com/example/WiremockMockServerApplicationTests.java b/samples/wiremock-undertow/src/test/java/com/example/WiremockMockServerApplicationTests.java index f53438fc5a..3cc15b627d 100644 --- a/samples/wiremock-undertow/src/test/java/com/example/WiremockMockServerApplicationTests.java +++ b/samples/wiremock-undertow/src/test/java/com/example/WiremockMockServerApplicationTests.java @@ -8,14 +8,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockRestServiceServer; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockMockServerApplicationTests { @Autowired diff --git a/samples/wiremock-undertow/src/test/java/com/example/WiremockServerApplicationTests.java b/samples/wiremock-undertow/src/test/java/com/example/WiremockServerApplicationTests.java index 9fd429e5b1..e669ff19fc 100644 --- a/samples/wiremock-undertow/src/test/java/com/example/WiremockServerApplicationTests.java +++ b/samples/wiremock-undertow/src/test/java/com/example/WiremockServerApplicationTests.java @@ -13,14 +13,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:7071", webEnvironment=WebEnvironment.NONE) -@DirtiesContext public class WiremockServerApplicationTests { @ClassRule diff --git a/scripts/compileOnly.sh b/scripts/compileOnly.sh new file mode 100755 index 0000000000..d335cc8d7b --- /dev/null +++ b/scripts/compileOnly.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +source common.sh || source scripts/common.sh || echo "No common.sh script found..." + +FOLDER=`pwd` + +set -e + +./mvnw clean install -DskipTests -Pfast -fae diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml index c4d2312890..8d175ccd91 100644 --- a/spring-cloud-contract-dependencies/pom.xml +++ b/spring-cloud-contract-dependencies/pom.xml @@ -5,17 +5,17 @@ spring-cloud-dependencies-parent org.springframework.cloud - 2.0.0.BUILD-SNAPSHOT - + 2.1.0.BUILD-SNAPSHOT + spring-cloud-contract-dependencies - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT pom spring-cloud-contract-dependencies Spring Cloud Contract Dependencies - 2.17.0 - 0.4.12 + 2.18.0 + 0.4.13 1.0.2.v20150114 3.0.7 diff --git a/spring-cloud-contract-shade/pom.xml b/spring-cloud-contract-shade/pom.xml index d9801e51b6..b08de75774 100644 --- a/spring-cloud-contract-shade/pom.xml +++ b/spring-cloud-contract-shade/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-shade diff --git a/spring-cloud-contract-spec/pom.xml b/spring-cloud-contract-spec/pom.xml index 57b84600ff..acea8210ff 100644 --- a/spring-cloud-contract-spec/pom.xml +++ b/spring-cloud-contract-spec/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-spec diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/Contract.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/Contract.groovy index 8f2a9389d8..7a7de8e34a 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/Contract.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/Contract.groovy @@ -37,7 +37,7 @@ class Contract { /** * You can set the level of priority of this contract. If there are two contracts * mapped for example to the same endpoint, then the one with greater priority should - * take precedence + * take precedence. A priority of 1 is highest and takes precedence over a priority of 2. */ Integer priority /** diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractTemplate.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractTemplate.groovy deleted file mode 100644 index 47aa1c269c..0000000000 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractTemplate.groovy +++ /dev/null @@ -1,89 +0,0 @@ -package org.springframework.cloud.contract.spec - -/** - * Contract for defining templated responses. - * - * If no implementation is provided then Handlebars will be picked as a default implementation. - * - * @author Marcin Grzejszczak - * @since 1.1.0 - */ -interface ContractTemplate { - - /** - * Handlebars is using the Mustache template thus it looks like this - * {{{ Mustache }}}. In this case the opening template would return {{{ - */ - String openingTemplate() - - /** - * Handlebars is using the Mustache template thus it looks like this - * {{{ Mustache }}}. In this case the closing template would return }}} - */ - String closingTemplate() - - /** - * Returns the template for retrieving a URL path and query from request - */ - String url() - - /** - * Returns the template for retrieving first value of a query parameter e.g. {{{ request.query.search }}} - * @param key - */ - String query(String key) - - /** - * Returns the template for retrieving nth value of a query parameter (zero indexed) e.g. {{{ request.query.search.[5] }}} - * @param key - * @param index - */ - String query(String key, int index) - - /** - * Returns the template for retrieving a URL path - */ - String path() - - /** - * Returns the template for retrieving nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}} - * @param index - */ - String path(int index) - - /** - * Returns the template for retrieving the first value of a request header e.g. {{{ request.headers.X-Request-Id }}} - * @param key - */ - String header(String key) - - /** - * Returns the template for retrieving the nth value of a request header (zero indexed) e.g. {{{ request.headers.X-Request-Id.[5] }}} - * @param key - * @param index - */ - String header(String key, int index) - - /** - * Retruns the tempalte for retrieving the first value of a cookie with certain key - * @param key - */ - String cookie(String key) - - /** - * Request body text (avoid for non-text bodies) e.g. {{{ request.body }}} . The body will not be escaped - * so you won't be able to directly embed it in a JSON for example. - */ - String body() - - /** - * Request body text (avoid for non-text bodies) e.g. {{{ escapejsonbody }}} . The body will not be escaped - * so you will be able to embed it - */ - String escapedBody() - - /** - * Request body text for the given JsonPath. e.g. {{{ jsonpath this '$.a.b.c' }}} - */ - String body(String jsonPath) -} \ No newline at end of file diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractTemplate.java b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractTemplate.java new file mode 100644 index 0000000000..bafcec195b --- /dev/null +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractTemplate.java @@ -0,0 +1,204 @@ +package org.springframework.cloud.contract.spec; + +/** + * Contract for defining templated responses. + *

+ * If no implementation is provided then Handlebars will be picked as a default implementation. + * + * @author Marcin Grzejszczak + * @since 1.1.0 + */ +public interface ContractTemplate { + /** + * Asserts whether the given text starts with proper opening template + */ + default boolean startsWithTemplate(String text) { + return text.startsWith(openingTemplate()); + } + + /** + * Asserts whether the given text starts with proper opening template for escaped value + */ + default boolean startsWithEscapedTemplate(String text) { + return text.startsWith(escapedOpeningTemplate()); + } + + /** + * Handlebars is using the Mustache template thus it looks like this + * {{ Mustache }}. In this case the opening template would return {{ + */ + String openingTemplate(); + + /** + * Handlebars is using the Mustache template thus it looks like this + * {{ Mustache }}. In this case the closing template would return }} + */ + String closingTemplate(); + + /** + * Handlebars is using the Mustache template thus it looks like this + * {{{ Mustache }}}. In this case the opening template would return {{{ + */ + default String escapedOpeningTemplate() { + return openingTemplate(); + } + + /** + * Handlebars is using the Mustache template thus it looks like this + * {{{ Mustache }}}. In this case the closing template would return }}} + */ + default String escapedClosingTemplate() { + return closingTemplate(); + } + + /** + * Returns the template for retrieving a URL path and query from request + */ + String url(); + + /** + * Returns the template for retrieving first value of a query parameter e.g. {{ request.query.search }} + * + * @param key + */ + String query(String key); + + /** + * Returns the template for retrieving nth value of a query parameter (zero indexed) e.g. {{ request.query.search.[5] }} + * + * @param key + * @param index + */ + String query(String key, int index); + + /** + * Returns the template for retrieving a URL path + */ + String path(); + + /** + * Returns the template for retrieving nth value of a URL path (zero indexed) e.g. {{ request.path.[2] }} + * + * @param index + */ + String path(int index); + + /** + * Returns the template for retrieving the first value of a request header e.g. {{ request.headers.X-Request-Id }} + * + * @param key + */ + String header(String key); + + /** + * Returns the template for retrieving the nth value of a request header (zero indexed) e.g. {{ request.headers.X-Request-Id.[5] }} + * + * @param key + * @param index + */ + String header(String key, int index); + + /** + * Retruns the template for retrieving the first value of a cookie with certain key + * + * @param key + */ + String cookie(String key); + + /** + * Request body text (avoid for non-text bodies) e.g. {{ request.body }} . The body will not be escaped + * so you won't be able to directly embed it in a JSON for example. + */ + String body(); + + /** + * Request body text for the given JsonPath. e.g. {{ jsonPath request.body '$.a.b.c' }} + */ + String body(String jsonPath); + + /** + * Returns the template for retrieving a escaped URL path and query from request + */ + default String escapedUrl() { + return url(); + } + + /** + * Returns the template for retrieving escaped first value of a query parameter e.g. {{{ request.query.search }}} + * + * @param key + */ + default String escapedQuery(String key) { + return query(key); + } + + /** + * Returns the template for retrieving esacped nth value of a query parameter (zero indexed) e.g. {{{ request.query.search.[5] }}} + * + * @param key + * @param index + */ + default String escapedQuery(String key, int index) { + return query(key, index); + } + + /** + * Returns the template for retrieving a escaped URL path + */ + default String escapedPath() { + return path(); + } + + /** + * Returns the template for retrieving escaped nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}} + * + * @param index + */ + default String escapedPath(int index) { + return path(index); + } + + /** + * Returns the template for retrieving the escaped first value of a request header e.g. {{{ request.headers.X-Request-Id }}} + * + * @param key + */ + default String escapedHeader(String key) { + return header(key); + } + + /** + * Returns the template for retrieving the esacaped nth value of a request header (zero indexed) e.g. {{{ request.headers.X-Request-Id.[5] }}} + * + * @param key + * @param index + */ + default String escapedHeader(String key, int index) { + return header(key, index); + } + + /** + * Retruns the template for retrieving the escaped first value of a cookie with certain key + * + * @param key + */ + default String escapedCookie(String key) { + return cookie(key); + } + + /** + * Request body text (avoid for non-text bodies) e.g. {{{ request.body }}} . The body will not be escaped + * so you won't be able to directly embed it in a JSON for example. + */ + default String escapedBody() { + return body(); + } + + /** + * Request body text for the given JsonPath. e.g. {{{ jsonPath request.body '$.a.b.c' }}} + */ + default String escapedBody(String jsonPath) { + return body(jsonPath); + } + +} diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy index 100210ca99..407a37500f 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy @@ -53,6 +53,10 @@ class Body extends DslProperty { super(bodyAsValue) } + Body(String bodyAsValue) { + super(bodyAsValue, bodyAsValue) + } + Body(GString bodyAsValue) { super(bodyAsValue, bodyAsValue) } diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/CompositeContractTemplate.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/CompositeContractTemplate.groovy new file mode 100644 index 0000000000..08397e4438 --- /dev/null +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/CompositeContractTemplate.groovy @@ -0,0 +1,155 @@ +package org.springframework.cloud.contract.spec.internal + +import groovy.transform.CompileStatic + +import org.springframework.cloud.contract.spec.ContractTemplate + +/** + * For backward compatibility when assertions take place, + * first checks the custom setup. Writes in a new format, can read + * the old format. + * + * @author Marcin Grzejszczak + * @since 2.1.0 + */ +@CompileStatic +class CompositeContractTemplate implements ContractTemplate { + + private final CustomHandlebarsContractTemplate custom = new CustomHandlebarsContractTemplate() + private final HandlebarsContractTemplate template = new HandlebarsContractTemplate() + + @Override + boolean startsWithTemplate(String text) { + if (this.custom.startsWithTemplate(text)) { + return true + } + return template.startsWithTemplate(text) + } + + @Override + boolean startsWithEscapedTemplate(String text) { + return this.template.startsWithEscapedTemplate(text) + } + + @Override + String openingTemplate() { + return this.template.openingTemplate() + } + + @Override + String closingTemplate() { + return this.template.closingTemplate() + } + + @Override + String escapedOpeningTemplate() { + return this.template.escapedOpeningTemplate() + } + + @Override + String escapedClosingTemplate() { + return this.template.escapedClosingTemplate() + } + + @Override + String url() { + return this.template.url() + } + + @Override + String query(String key) { + return this.template.query(key) + } + + @Override + String query(String key, int index) { + return this.template.query(key, index) + } + + @Override + String path() { + return this.template.path() + } + + @Override + String path(int index) { + return this.template.path(index) + } + + @Override + String header(String key) { + return this.template.header(key) + } + + @Override + String header(String key, int index) { + return this.template.header(key, index) + } + + @Override + String cookie(String key) { + return this.template.cookie(key) + } + + @Override + String body() { + return this.template.body() + } + + @Override + String escapedBody() { + // WireMock doesn't support proper escaping of JSON body + // that's why we need to use our custom handlebars extension + return this.custom.escapedBody() + } + + @Override + String escapedBody(String jsonPath) { + return this.template.escapedBody(jsonPath) + } + + @Override + String body(String jsonPath) { + return this.template.body(jsonPath) + } + + @Override + String escapedUrl() { + return this.template.escapedUrl() + } + + @Override + String escapedQuery(String key) { + return this.template.escapedQuery(key) + } + + @Override + String escapedQuery(String key, int index) { + return this.template.escapedQuery(key, index) + } + + @Override + String escapedPath() { + return this.template.escapedPath() + } + + @Override + String escapedPath(int index) { + return this.template.escapedPath(index) + } + + @Override + String escapedHeader(String key) { + return this.template.escapedHeader(key) + } + + @Override + String escapedHeader(String key, int index) { + return this.template.escapedHeader(key, index) + } + + @Override + String escapedCookie(String key) { + return this.template.escapedCookie(key) + } +} diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/CustomHandlebarsContractTemplate.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/CustomHandlebarsContractTemplate.groovy new file mode 100644 index 0000000000..2598aace44 --- /dev/null +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/CustomHandlebarsContractTemplate.groovy @@ -0,0 +1,94 @@ +package org.springframework.cloud.contract.spec.internal + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope + +import org.springframework.cloud.contract.spec.ContractTemplate + +/** + * Represents the structure of templates using Handlebars compatible with + * WireMock template model requirements. + * + * @author Marcin Grzejszczak + * @since 1.1.0 + * + * @deprecated use {@link HandlebarsContractTemplate} + */ +@CompileStatic +@PackageScope +class CustomHandlebarsContractTemplate implements ContractTemplate { + + @Override + String openingTemplate() { + return "{{{" + } + + @Override + String closingTemplate() { + return "}}}" + } + + @Override + String url() { + return wrapped("request.url") + } + + @Override + String query(String key) { + return query(key, 0) + } + + @Override + String query(String key, int index) { + return wrapped("request.query.${key}.[${index}]") + } + + @Override + String path() { + return wrapped("request.path") + } + + @Override + String path(int index) { + return wrapped("request.path.[${index}]") + } + + @Override + String header(String key) { + return header(key, 0) + } + + @Override + String header(String key, int index) { + return wrapped("request.headers.${key}.[${index}]") + } + + @Override + String cookie(String key) { + return wrapped("request.cookies.${key}") + } + + @Override + String body() { + return wrapped("request.body") + } + + @Override + String escapedBody() { + return wrapped("escapejsonbody") + } + + @Override + String escapedBody(String jsonPath) { + return body(jsonPath) + } + + @Override + String body(String jsonPath) { + return wrapped("jsonpath this '${jsonPath}'") + } + + private String wrapped(String text) { + return openingTemplate() + text + closingTemplate() + } +} diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/FromRequest.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/FromRequest.groovy index b56add4ee6..289e1f8d1c 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/FromRequest.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/FromRequest.groovy @@ -19,14 +19,14 @@ class FromRequest { } private ContractTemplate template() { - return new HandlebarsContractTemplate() + return new CompositeContractTemplate() } /** * URL path and query */ DslProperty url() { - return new DslProperty(template.url()) + return new DslProperty(template.escapedUrl()) } /** @@ -34,7 +34,7 @@ class FromRequest { * @param key */ DslProperty query(String key) { - return new DslProperty(template.query(key)) + return new DslProperty(template.escapedQuery(key)) } /** @@ -43,14 +43,14 @@ class FromRequest { * @param index */ DslProperty query(String key, int index) { - return new DslProperty(template.query(key, index)) + return new DslProperty(template.escapedQuery(key, index)) } /** * URL path */ DslProperty path() { - return new DslProperty(template.path()) + return new DslProperty(template.escapedPath()) } /** @@ -58,7 +58,7 @@ class FromRequest { * @param index */ DslProperty path(int index) { - return new DslProperty(template.path(index)) + return new DslProperty(template.escapedPath(index)) } /** @@ -66,7 +66,7 @@ class FromRequest { * @param key */ DslProperty header(String key) { - return new DslProperty(template.header(key)) + return new DslProperty(template.escapedHeader(key)) } /** @@ -74,7 +74,7 @@ class FromRequest { * @param key */ DslProperty header(String key, int index) { - return new DslProperty(template.header(key, index)) + return new DslProperty(template.escapedHeader(key, index)) } /** @@ -82,20 +82,97 @@ class FromRequest { * @param key */ DslProperty cookie(String key) { - return new DslProperty(template.cookie(key)) + return new DslProperty(template.escapedCookie(key)) } /** * Request body text (avoid for non-text bodies) */ DslProperty body() { - return new DslProperty(template.body()) + return new DslProperty(template.escapedBody()) } /** * Request body text for the given JsonPath */ DslProperty body(String jsonPath) { + return new DslProperty(template.escapedBody(jsonPath)) + } + + /** + * Unescaped URL path and query + */ + DslProperty rawUrl() { + return new DslProperty(template.url()) + } + + /** + * Unescaped First value of a query parameter e.g. request.query.search + * @param key + */ + DslProperty rawQuery(String key) { + return new DslProperty(template.query(key)) + } + + /** + * Unescaped nth value of a query parameter (zero indexed) e.g. request.query.search.[5] + * @param key + * @param index + */ + DslProperty rawQuery(String key, int index) { + return new DslProperty(template.query(key, index)) + } + + /** + * Unescaped URL path + */ + DslProperty rawPath() { + return new DslProperty(template.path()) + } + + /** + * Unescaped nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}} + * @param index + */ + DslProperty rawPath(int index) { + return new DslProperty(template.path(index)) + } + + /** + * Unescaped First value of a request header e.g. request.headers.X-Request-Id + * @param key + */ + DslProperty rawHeader(String key) { + return new DslProperty(template.header(key)) + } + + /** + * Unescaped nth value of a request header (zero indexed) e.g. request.headers.X-Request-Id + * @param key + */ + DslProperty rawHeader(String key, int index) { + return new DslProperty(template.header(key, index)) + } + + /** + * Unescaped Retruns the tempalte for retrieving the first value of a cookie with certain key + * @param key + */ + DslProperty rawCookie(String key) { + return new DslProperty(template.cookie(key)) + } + + /** + * Unescaped Request body text (avoid for non-text bodies) + */ + DslProperty rawBody() { + return new DslProperty(template.body()) + } + + /** + * Unescaped Request body text for the given JsonPath + */ + DslProperty rawBody(String jsonPath) { return new DslProperty(template.body(jsonPath)) } diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/HandlebarsContractTemplate.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/HandlebarsContractTemplate.groovy index 9a5acb6614..73b5270e2a 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/HandlebarsContractTemplate.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/HandlebarsContractTemplate.groovy @@ -15,11 +15,21 @@ class HandlebarsContractTemplate implements ContractTemplate { @Override String openingTemplate() { - return "{{{" + return "{{" } @Override String closingTemplate() { + return "}}" + } + + @Override + String escapedOpeningTemplate() { + return "{{{" + } + + @Override + String escapedClosingTemplate() { return "}}}" } @@ -70,15 +80,64 @@ class HandlebarsContractTemplate implements ContractTemplate { @Override String escapedBody() { - return wrapped("escapejsonbody") + return escapedWrapped("request.body") + } + + @Override + String escapedBody(String jsonPath) { + return escapedWrapped("jsonPath request.body '${jsonPath}'") } @Override String body(String jsonPath) { - return wrapped("jsonpath this '${jsonPath}'") + return wrapped("jsonPath request.body '${jsonPath}'") + } + + @Override + String escapedUrl() { + return escapedWrapped("request.url") + } + + @Override + String escapedQuery(String key) { + return escapedQuery(key, 0) + } + + @Override + String escapedQuery(String key, int index) { + return escapedWrapped("request.query.${key}.[${index}]") + } + + @Override + String escapedPath() { + return escapedWrapped("request.path") + } + + @Override + String escapedPath(int index) { + return escapedWrapped("request.path.[${index}]") + } + + @Override + String escapedHeader(String key) { + return escapedHeader(key, 0) + } + + @Override + String escapedHeader(String key, int index) { + return escapedWrapped("request.headers.${key}.[${index}]") + } + + @Override + String escapedCookie(String key) { + return escapedWrapped("request.cookies.${key}") } private String wrapped(String text) { return openingTemplate() + text + closingTemplate() } + + private String escapedWrapped(String text) { + return escapedOpeningTemplate() + text + escapedClosingTemplate() + } } diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Url.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Url.groovy index be464280a2..d28d952aa7 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Url.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Url.groovy @@ -58,7 +58,7 @@ class Url extends DslProperty { url.values.collect { it instanceof Pattern ? new Xeger(it.pattern()).generate() : it } as String[], - url.strings.clone() as String[] + Arrays.copyOf(url.strings, url.strings.length) as String[] ).toString() return new Url(newUrl) } diff --git a/spring-cloud-contract-starters/pom.xml b/spring-cloud-contract-starters/pom.xml index c30ea485dc..13311eb1e5 100644 --- a/spring-cloud-contract-starters/pom.xml +++ b/spring-cloud-contract-starters/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml index 8b72e0b72b..85b739e87e 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-starter-contract-stub-runner-jetty diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml index d2e0db15b9..a4ec1457db 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-starter-contract-stub-runner @@ -28,6 +28,7 @@ org.springframework.cloud spring-cloud-stream-test-support + true org.springframework.cloud diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml index 96270fd5bc..e2df9aa5c6 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-starter-contract-verifier diff --git a/spring-cloud-contract-stub-runner-boot/pom.xml b/spring-cloud-contract-stub-runner-boot/pom.xml index e3cdfc5cb5..4eacdd9de9 100644 --- a/spring-cloud-contract-stub-runner-boot/pom.xml +++ b/spring-cloud-contract-stub-runner-boot/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot diff --git a/spring-cloud-contract-stub-runner/README.adoc b/spring-cloud-contract-stub-runner/README.adoc index 1b3eec708d..c2b1b3fe70 100644 --- a/spring-cloud-contract-stub-runner/README.adoc +++ b/spring-cloud-contract-stub-runner/README.adoc @@ -148,14 +148,6 @@ HTTP stubs without the need to download artifacts. ==== Running stubs -===== Limitations - -IMPORTANT: There might be a problem with StubRunner shutting down ports between tests. You might - have a situation in which you get port conflicts. As long as you use the same context across tests - everything works fine. But when the context are different (e.g. different stubs or different profiles) - then you have to either use `@DirtiesContext` to shut down the stub servers, or else run them on - different ports per test. - ===== Running using main app You can set the following options to the main class: @@ -498,11 +490,11 @@ For the properties check the *Stub Runner Spring* section. ===== Stub Runner Server Fat Jar -You can download a standalone JAR from Maven (for example, for version 1.2.3.RELEASE), as follows: +You can download a standalone JAR from Maven (e.g. for version 2.0.1.RELEASE), as follows: [source,bash,indent=0] ---- -$ wget -O stub-runner.jar 'https://search.maven.org/remote_content?g=org.springframework.cloud&a=spring-cloud-contract-stub-runner-boot&v=1.2.3.RELEASE' +$ wget -O stub-runner.jar 'https://search.maven.org/remotecontent?filepath=org/springframework/cloud/spring-cloud-contract-stub-runner-boot/2.0.1.RELEASE/spring-cloud-contract-stub-runner-boot-2.0.1.RELEASE.jar' $ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=... ---- diff --git a/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/pom.xml index 36c0f44aad..45ebe635a4 100644 --- a/spring-cloud-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner @@ -82,6 +82,16 @@ com.jcraft jsch.agentproxy.usocket-jna + + org.springframework + spring-test + true + + + org.springframework.boot + spring-boot-autoconfigure-processor + true + io.specto diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java index aa9245c334..39f5707026 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java @@ -17,7 +17,12 @@ package org.springframework.cloud.contract.stubrunner; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Random; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; @@ -41,10 +46,14 @@ import shaded.org.eclipse.aether.transport.http.HttpTransporterFactory; class AetherFactories { + private static final Log log = LogFactory.getLog(AetherFactories.class); + private static final String MAVEN_LOCAL_REPOSITORY_LOCATION = "maven.repo.local"; private static final String MAVEN_USER_SETTINGS_LOCATION = "org.apache.maven.user-settings"; private static final String MAVEN_GLOBAL_SETTINGS_LOCATION = "org.apache.maven.global-settings"; + private static final Random RANDOM = new Random(); + public static RepositorySystem newRepositorySystem() { DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); @@ -60,14 +69,34 @@ class AetherFactories { session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS); } session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN); - LocalRepository localRepo = new LocalRepository(localRepositoryDirectory()); + String localRepositoryDirectory = localRepositoryDirectory(workOffline); + if (log.isDebugEnabled()) { + log.debug("Local Repository Directory set to [" + localRepositoryDirectory + "]. Work offline: [" + workOffline + "]"); + } + LocalRepository localRepo = new LocalRepository(localRepositoryDirectory); session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)); return session; } - private static String localRepositoryDirectory() { + protected static String localRepositoryDirectory(boolean workOffline) { String localRepoLocationFromSettings = settings().getLocalRepository(); - return readPropertyFromSystemProps(localRepoLocationFromSettings); + String currentLocalRepo = readPropertyFromSystemProps(localRepoLocationFromSettings); + if (workOffline) { + return currentLocalRepo; + } + return temporaryDirectory(); + } + + private static String temporaryDirectory() { + try { + return Files.createTempDirectory("aether-local").toString(); + } + catch (IOException e) { + if (log.isDebugEnabled()) { + log.debug("Failed to create a new temporary directory, will generate a new one under temp dir"); + } + return System.getProperty("java.io.tmpdir") + File.separator + RANDOM.nextInt(); + } } private static String readPropertyFromSystemProps( @@ -87,16 +116,19 @@ class AetherFactories { return System.getenv(prop); } + private static File userSettings() { + String user = fromSystemPropOrEnv(MAVEN_USER_SETTINGS_LOCATION); + if (user == null) { + return new File(new File(System.getProperty("user.home")).getAbsoluteFile(), + File.separator + ".m2" + File.separator + "settings.xml"); + } + return new File(user); + } + private static Settings settings() { SettingsBuilder builder = new DefaultSettingsBuilderFactory().newInstance(); SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); - String user = fromSystemPropOrEnv(MAVEN_USER_SETTINGS_LOCATION); - if (user == null) { - request.setUserSettingsFile(new File(new File(System.getProperty("user.home")).getAbsoluteFile(), - File.separator + ".m2" + File.separator + "settings.xml")); - } else { - request.setUserSettingsFile(new File(user)); - } + request.setUserSettingsFile(userSettings()); String global = fromSystemPropOrEnv(MAVEN_GLOBAL_SETTINGS_LOCATION); if (global != null) { request.setGlobalSettingsFile(new File(global)); diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java index 2206af6d9b..fef67e6742 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java @@ -57,7 +57,6 @@ public class AetherStubDownloader implements StubDownloader { private static final String ARTIFACT_EXTENSION = "jar"; private static final String LATEST_ARTIFACT_VERSION = "(,]"; private static final String LATEST_VERSION_IN_IVY = "+"; - private static final String STUBRUNNER_SNAPSHOT_CHECK_SKIP_SYSTEM_PROP = "stubrunner.snapshot-check-skip"; // Preloading class for the shutdown hook not to throw ClassNotFound private static final Class CLAZZ = TemporaryFileStorage.class; @@ -65,7 +64,6 @@ public class AetherStubDownloader implements StubDownloader { private final RepositorySystem repositorySystem; private final RepositorySystemSession session; private final boolean workOffline; - private final boolean snapshotCheckSkip; private final boolean deleteStubsAfterTest; public AetherStubDownloader(StubRunnerOptions stubRunnerOptions) { @@ -92,8 +90,6 @@ public class AetherStubDownloader implements StubDownloader { this.repositorySystem = newRepositorySystem(); this.workOffline = stubRunnerOptions.stubsMode == StubRunnerProperties.StubsMode.LOCAL; this.session = newSession(this.repositorySystem, this.workOffline); - this.snapshotCheckSkip = - stubRunnerOptions.isSnapshotCheckSkip() || skipSnapshotCheck(); registerShutdownHook(); } @@ -118,7 +114,6 @@ public class AetherStubDownloader implements StubDownloader { log.error("Remote repositories for stubs are not specified and work offline flag wasn't passed"); } this.workOffline = false; - this.snapshotCheckSkip = skipSnapshotCheck(); registerShutdownHook(); } @@ -150,28 +145,24 @@ public class AetherStubDownloader implements StubDownloader { private File unpackedJar(String resolvedVersion, String stubsGroup, String stubsModule, String classifier) { - log.info("Resolved version is [" + resolvedVersion + "]"); - if (StringUtils.isEmpty(resolvedVersion)) { - log.warn("Stub for group [" + stubsGroup + "] module [" + stubsModule - + "] and classifier [" + classifier + "] not found in " - + this.remoteRepos); - return null; - } - Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, - ARTIFACT_EXTENSION, resolvedVersion); - ArtifactRequest request = new ArtifactRequest(artifact, this.remoteRepos, null); - if (log.isDebugEnabled()) { - log.debug("Resolving artifact [" + artifact - + "] using remote repositories " + this.remoteRepos); - } try { + log.info("Resolved version is [" + resolvedVersion + "]"); + if (StringUtils.isEmpty(resolvedVersion)) { + log.warn("Stub for group [" + stubsGroup + "] module [" + stubsModule + + "] and classifier [" + classifier + "] not found in " + + this.remoteRepos); + return null; + } + Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, + ARTIFACT_EXTENSION, resolvedVersion); + ArtifactRequest request = new ArtifactRequest(artifact, this.remoteRepos, null); + if (log.isDebugEnabled()) { + log.debug("Resolving artifact [" + artifact + + "] using remote repositories " + this.remoteRepos); + } ArtifactResult result = this.repositorySystem.resolveArtifact(this.session, request); log.info("Resolved artifact [" + artifact + "] to " + result.getArtifact().getFile()); - if (!this.snapshotCheckSkip && resolvedFromLocalRepo(result) && shouldDownloadFromRemote()) { - throw new IllegalStateException("The artifact was found in the local repository " - + "but you have explicitly stated that it should be downloaded from a remote one"); - } File temporaryFile = unpackStubJarToATemporaryFolder( result.getArtifact().getFile().toURI()); log.info("Unpacked file to [" + temporaryFile + "]"); @@ -189,10 +180,6 @@ public class AetherStubDownloader implements StubDownloader { } } - private boolean skipSnapshotCheck() { - return StubRunnerPropertyUtils.isPropertySet(STUBRUNNER_SNAPSHOT_CHECK_SKIP_SYSTEM_PROP); - } - private boolean resolvedFromLocalRepo(ArtifactResult result) { return result.getRepository() instanceof LocalRepository; } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ClasspathStubProvider.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ClasspathStubProvider.java index 2d8b1baf57..a553f59ac0 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ClasspathStubProvider.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ClasspathStubProvider.java @@ -87,10 +87,17 @@ public class ClasspathStubProvider implements StubDownloaderBuilder { for (Resource resource : resources) { try { String relativePath = relativePathPicker(resource, groupAndArtifactPattern); - int lastIndexOf = relativePath.lastIndexOf(File.separator); + if (log.isDebugEnabled()) { + log.debug("Relative path for resource is [" + relativePath + "]"); + } + // the relative path is OS agnostic and contains / only + int lastIndexOf = relativePath.lastIndexOf("/"); String relativePathWithoutFile = lastIndexOf > -1 ? relativePath.substring(0, lastIndexOf) : relativePath; + if (log.isDebugEnabled()) { + log.debug("Relative path without file name is [" + relativePathWithoutFile + "]"); + } Path directory = Files.createDirectories( new File(tmp, relativePathWithoutFile).toPath()); File newFile = new File(directory.toFile(), resource.getFilename()); @@ -132,7 +139,7 @@ public class ClasspathStubProvider implements StubDownloaderBuilder { if (groupAndArtifactMatcher.matches()) { MatchResult groupAndArtifactResult = groupAndArtifactMatcher .toMatchResult(); - return groupAndArtifactResult.group(2) + File.separator + return groupAndArtifactResult.group(2) + groupAndArtifactResult.group(3); } else { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java index 6c27dbafac..f9b4878590 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java @@ -63,8 +63,10 @@ public class ContractProjectUpdater { public void updateContractProject(String projectName, Path rootStubsFolder) { File clonedRepo = this.gitContractsRepo .clonedRepo(this.stubRunnerOptions.stubRepositoryRoot); + GitStubDownloaderProperties properties = new GitStubDownloaderProperties( + this.stubRunnerOptions.stubRepositoryRoot, this.stubRunnerOptions); copyStubs(projectName, rootStubsFolder, clonedRepo); - GitRepo gitRepo = new GitRepo(clonedRepo); + GitRepo gitRepo = new GitRepo(clonedRepo, properties); String msg = StubRunnerPropertyUtils.getProperty(this.stubRunnerOptions.getProperties(), GIT_COMMIT_MESSAGE); GitRepo.CommitResult commit = gitRepo diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GitRepo.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GitRepo.java index d90aaa71ed..1588e30cad 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GitRepo.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GitRepo.java @@ -69,7 +69,7 @@ class GitRepo { private static final Logger log = LoggerFactory.getLogger(GitRepo.class); - private final JGitFactory gitFactory; + final JGitFactory gitFactory; private final File basedir; @@ -79,12 +79,14 @@ class GitRepo { } // for tests + @Deprecated GitRepo(File basedir) { this.basedir = basedir; this.gitFactory = new JGitFactory(); } // for tests + @Deprecated GitRepo(File basedir, JGitFactory factory) { this.basedir = basedir; this.gitFactory = factory; @@ -330,7 +332,7 @@ class GitRepo { } }; - private final CredentialsProvider provider; + final CredentialsProvider provider; JGitFactory(GitStubDownloaderProperties properties) { if (org.springframework.util.StringUtils.hasText(properties.username)) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java index fcb87ab830..4e9ea34d64 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java @@ -169,10 +169,10 @@ class StubRepository { if (isContractDescriptor(file)) { contractDescriptors .addAll(ContractVerifierDslConverter.convertAsCollection(file.getParentFile(), file)); + } else if (converter != null && converter.isAccepted(file)) { + contractDescriptors.addAll(converter.convertFrom(file)); } else if (YamlContractConverter.INSTANCE.isAccepted(file)) { contractDescriptors.addAll(YamlContractConverter.INSTANCE.convertFrom(file)); - } else if (converter != null) { - contractDescriptors.addAll(converter.convertFrom(file)); } } return super.visitFile(path, attrs); @@ -190,7 +190,13 @@ class StubRepository { return true; } String consumerName = this.options.getConsumerName(); - return file.getAbsolutePath().contains(File.separator + consumerName + File.separator); + String searchedConsumerName = File.separator + consumerName + File.separator; + String absolutePath = file.getAbsolutePath(); + boolean stubPerConsumerMatching = absolutePath.contains(searchedConsumerName); + if (log.isDebugEnabled()) { + log.debug("Absolute path [" + absolutePath + "] contains [" + searchedConsumerName + "] in its path [" + stubPerConsumerMatching + "]"); + } + return stubPerConsumerMatching; } private static boolean isContractDescriptor(File file) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java index 551076cf13..80825968fe 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java @@ -25,6 +25,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import groovy.json.JsonOutput; import org.slf4j.Logger; @@ -38,12 +39,15 @@ import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockH import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessages; import org.springframework.cloud.contract.verifier.util.BodyExtractor; +import wiremock.org.eclipse.jetty.util.ConcurrentHashSet; /** * Runs stubs for a particular {@link StubServer} */ class StubRunnerExecutor implements StubFinder { + static final Set STUB_SERVERS = new ConcurrentHashSet<>(); + private static final Logger log = LoggerFactory.getLogger(StubRunnerExecutor.class); private final AvailablePortScanner portScanner; private final MessageVerifier contractVerifierMessaging; @@ -261,6 +265,7 @@ class StubRunnerExecutor implements StubFinder { } }); } + STUB_SERVERS.add(this.stubServer); } private boolean hasRequest(Collection contracts) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java index eb8511445f..b7569140f5 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java @@ -101,12 +101,6 @@ public class StubRunnerOptions { final StubRunnerProperties.StubsMode stubsMode; - /** - * If set to {@code true} will not assert whether the downloaded stubs / contract - * JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact) - */ - private boolean snapshotCheckSkip; - /** * If set to {@code false} will NOT delete stubs from a temporary * folder after running tests @@ -118,13 +112,12 @@ public class StubRunnerOptions { */ private Map properties = new HashMap<>(); - StubRunnerOptions(Integer minPortValue, Integer maxPortValue, Resource stubRepositoryRoot, StubRunnerProperties.StubsMode stubsMode, String stubsClassifier, Collection dependencies, Map stubIdsToPortMapping, String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions, - boolean stubsPerConsumer, String consumerName, String mappingsOutputFolder, boolean snapshotCheckSkip, + boolean stubsPerConsumer, String consumerName, String mappingsOutputFolder, boolean deleteStubsAfterTest, Map properties) { this.minPortValue = minPortValue; this.maxPortValue = maxPortValue; @@ -139,7 +132,6 @@ public class StubRunnerOptions { this.stubsPerConsumer = stubsPerConsumer; this.consumerName = consumerName; this.mappingsOutputFolder = mappingsOutputFolder; - this.snapshotCheckSkip = snapshotCheckSkip; this.deleteStubsAfterTest = deleteStubsAfterTest; this.properties = properties; } @@ -167,7 +159,6 @@ public class StubRunnerOptions { .withStubPerConsumer(Boolean.parseBoolean(System.getProperty("stubrunner.stubs-per-consumer", "false"))) .withConsumerName(System.getProperty("stubrunner.consumer-name")) .withMappingsOutputFolder(System.getProperty("stubrunner.mappings-output-folder")) - .withSnapshotCheckSkip(Boolean.parseBoolean(System.getProperty("stubrunner.snapshot-check-skip", "false"))) .withDeleteStubsAfterTest(Boolean.parseBoolean(System.getProperty("stubrunner.delete-stubs-after-test", "true"))) .withProperties(stubRunnerProps()); String proxyHost = System.getProperty("stubrunner.proxy.host"); @@ -277,14 +268,6 @@ public class StubRunnerOptions { this.mappingsOutputFolder = mappingsOutputFolder; } - public boolean isSnapshotCheckSkip() { - return this.snapshotCheckSkip; - } - - public void setSnapshotCheckSkip(boolean snapshotCheckSkip) { - this.snapshotCheckSkip = snapshotCheckSkip; - } - public boolean isDeleteStubsAfterTest() { return this.deleteStubsAfterTest; } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java index 8c03973b01..5d60221106 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java @@ -48,7 +48,6 @@ public class StubRunnerOptionsBuilder { private String consumerName; private String mappingsOutputFolder; private StubRunnerProperties.StubsMode stubsMode; - private boolean snapshotCheckSkip = false; private boolean deleteStubsAfterTest = true; private Map properties = new HashMap<>(); @@ -133,10 +132,9 @@ public class StubRunnerOptionsBuilder { this.consumerName = options.getConsumerName(); this.mappingsOutputFolder = options.getMappingsOutputFolder(); this.stubConfigurations = options.dependencies != null ? - options.dependencies : new ArrayList(); + options.dependencies : new ArrayList<>(); this.stubIdsToPortMapping = options.stubIdsToPortMapping != null ? - options.stubIdsToPortMapping : new LinkedHashMap(); - this.snapshotCheckSkip = options.isSnapshotCheckSkip(); + options.stubIdsToPortMapping : new LinkedHashMap<>(); this.deleteStubsAfterTest = options.isDeleteStubsAfterTest(); this.properties = options.getProperties(); return this; @@ -147,11 +145,6 @@ public class StubRunnerOptionsBuilder { return this; } - public StubRunnerOptionsBuilder withSnapshotCheckSkip(boolean snapshotCheckSkip) { - this.snapshotCheckSkip = snapshotCheckSkip; - return this; - } - public StubRunnerOptionsBuilder withDeleteStubsAfterTest(boolean deleteStubsAfterTest) { this.deleteStubsAfterTest = deleteStubsAfterTest; return this; @@ -166,7 +159,7 @@ public class StubRunnerOptionsBuilder { return new StubRunnerOptions(this.minPortValue, this.maxPortValue, this.stubRepositoryRoot, this.stubsMode, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping, this.username, this.password, this.stubRunnerProxyOptions, this.stubsPerConsumer, this.consumerName, - this.mappingsOutputFolder, this.snapshotCheckSkip, this.deleteStubsAfterTest, this.properties); + this.mappingsOutputFolder, this.deleteStubsAfterTest, this.properties); } private Collection buildDependencies() { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java index 73c7ded42f..429edb6ac3 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java @@ -20,6 +20,7 @@ import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,7 +60,14 @@ class StubServer { return this; } + public boolean isRunning() { + return this.httpServerStub.isRunning(); + } + public void stop() { + if (log.isDebugEnabled()) { + log.debug("Stopping the server at port [" + this.getPort() + "]"); + } this.httpServerStub.stop(); } @@ -95,4 +103,22 @@ class StubServer { return this.httpServerStub.registeredMappings(); } + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + StubServer that = (StubServer) o; + return Objects.equals(this.stubConfiguration, that.stubConfiguration) && Objects + .equals(this.contracts, that.contracts); + } + + @Override public int hashCode() { + return Objects.hash(this.stubConfiguration, this.contracts); + } + + @Override public String toString() { + return "StubServer{" + "stubConfiguration=" + this.stubConfiguration + ", mappingsSize=" + + this.mappings.size() + '}'; + } } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java index eb62e0bcde..6e440b3537 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java @@ -154,11 +154,6 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio return this.delegate; } - @Override public StubRunnerRule withSnapshotCheckSkip(boolean snapshotCheckSkip) { - builder().withSnapshotCheckSkip(snapshotCheckSkip); - return this.delegate; - } - @Override public StubRunnerRule withDeleteStubsAfterTest( boolean deleteStubsAfterTest) { builder().withDeleteStubsAfterTest(deleteStubsAfterTest); diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java index 90bc1fecc7..4981ca334d 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java @@ -95,12 +95,6 @@ interface StubRunnerRuleOptions { */ StubRunnerRule withMappingsOutputFolder(String mappingsOutputFolder); - /** - * If set to {@code true} will not assert whether the downloaded stubs / contract - * JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact) - */ - StubRunnerRule withSnapshotCheckSkip(boolean snapshotCheckSkip); - /** * If set to {@code false} will NOT delete stubs from a temporary * folder after running tests diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java new file mode 100644 index 0000000000..7bd3005ffa --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java @@ -0,0 +1,71 @@ +package org.springframework.cloud.contract.stubrunner.provider.wiremock; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.github.tomakehurst.wiremock.stubbing.StubMapping; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.contract.stubrunner.HttpServerStub; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.support.AbstractTestExecutionListener; +import org.springframework.util.Assert; +import org.springframework.web.client.RestTemplate; + +/** + * Stops the {@link HttpServerStub} after each test class + * + * @author Marcin Grzejszczak + * @since 1.2.6 + */ +public final class StubRunnerWireMockTestExecutionListener extends AbstractTestExecutionListener { + + private static final Log log = LogFactory.getLog(StubRunnerWireMockTestExecutionListener.class); + + private static Map> STUBS = new ConcurrentHashMap<>(); + + @Override public void beforeTestClass(TestContext testContext) { + Map stubs = STUBS + .get(testContext.getApplicationContext()); + if (stubs != null) { + if (log.isDebugEnabled()) { + log.debug("Found a matching application context from [" + testContext.getTestClass().getName() + "]"); + } + for (Map.Entry entry : stubs.entrySet()) { + while (entry.getKey().isRunning()) { + entry.getKey().stop(); + } + List mappings = entry.getValue().mappings; + if (log.isDebugEnabled()) { + log.debug("Stopped a running WireMock instance at " + + "port [" + entry.getValue().port + "] with stub mappings " + + "size [" + mappings.size() + "]. Restarting the stub."); + } + entry.getKey().start(entry.getValue().port); + entry.getKey().registerDescriptors(mappings); + /* + Thanks to Tom Akehurst: + I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a + previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it + isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet. + Calling the admin endpoint just happened to remove the dead connection from the pool. + This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false"); + */ + Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + entry.getValue().port + "/__admin/mappings", String.class) + .getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible"); + } + } + } + + @Override public void afterTestClass(TestContext testContext) { + STUBS.put(testContext.getApplicationContext(), WireMockHttpServerStub.SERVERS); + if (log.isDebugEnabled()) { + log.debug("Stopping servers " + WireMockHttpServerStub.SERVERS); + } + for (HttpServerStub serverStub : WireMockHttpServerStub.SERVERS.keySet()) { + serverStub.stop(); + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java index 8413c934dd..82c0e13fad 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; @@ -43,6 +44,8 @@ public class WireMockHttpServerStub implements HttpServerStub { private static final Logger log = LoggerFactory.getLogger(WireMockHttpServerStub.class); private static final int INVALID_PORT = -1; + static final Map SERVERS = new ConcurrentHashMap<>(); + private WireMockServer wireMockServer; private WireMockConfiguration config() { @@ -107,6 +110,12 @@ public class WireMockHttpServerStub implements HttpServerStub { this.wireMockServer = new WireMockServer(config().port(port) .notifier(new Slf4jNotifier(true))); this.wireMockServer.start(); + if (log.isDebugEnabled()) { + log.debug("Started WireMock at port [" + port + "]"); + } + if (!SERVERS.containsKey(this)) { + SERVERS.put(this, new PortAndMappings(port, new ArrayList())); + } return this; } @@ -159,20 +168,25 @@ public class WireMockHttpServerStub implements HttpServerStub { } private void registerStubMappings(Collection stubFiles) { - WireMock wireMock = new WireMock("localhost", port(), ""); + WireMock wireMock = wireMock(); registerDefaultHealthChecks(wireMock); registerStubs(stubFiles, wireMock); } + private WireMock wireMock() { + return new WireMock("localhost", port(), ""); + } + private void registerDefaultHealthChecks(WireMock wireMock) { registerHealthCheck(wireMock, "/ping"); registerHealthCheck(wireMock, "/health"); } private void registerStubs(Collection sortedMappings, WireMock wireMock) { + List stubMappings = new ArrayList<>(); for (File mappingDescriptor : sortedMappings) { try { - wireMock.register(getMapping(mappingDescriptor)); + stubMappings.add(registerDescriptor(wireMock, mappingDescriptor)); if (log.isDebugEnabled()) { log.debug("Registered stub mappings from [" + mappingDescriptor + "]"); } @@ -183,6 +197,23 @@ public class WireMockHttpServerStub implements HttpServerStub { } } } + PortAndMappings portAndMappings = SERVERS.get(this); + SERVERS.put(this, new PortAndMappings(portAndMappings.port, stubMappings)); + } + + private StubMapping registerDescriptor(WireMock wireMock, File mappingDescriptor) { + StubMapping mapping = getMapping(mappingDescriptor); + wireMock.register(mapping); + return mapping; + } + + void registerDescriptors(List stubMappings) { + if (log.isDebugEnabled()) { + log.debug("Registering stub mappings size [" + stubMappings.size() + "] at port [" + port() + "]"); + } + for (StubMapping mapping : stubMappings) { + wireMock().register(mapping); + } } private void registerHealthCheck(WireMock wireMock, String url) { @@ -194,3 +225,17 @@ public class WireMockHttpServerStub implements HttpServerStub { WireMock.get(WireMock.urlEqualTo(url)).willReturn(WireMock.aResponse().withBody(body).withStatus(200))); } } + +class PortAndMappings { + final Integer port; + final List mappings; + + PortAndMappings(Integer port, List mappings) { + this.port = port; + this.mappings = mappings; + } + + @Override public String toString() { + return "PortAndMappings{" + "port=" + this.port + ", mappings=" + this.mappings.size() + '}'; + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/EnableStubRunnerServer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/EnableStubRunnerServer.java index fc7713d8f3..c50223e419 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/EnableStubRunnerServer.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/EnableStubRunnerServer.java @@ -34,7 +34,7 @@ import org.springframework.context.annotation.Import; @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Import({HttpStubsController.class, TriggerController.class, StreamConfiguration.class, StubRunnerConfiguration.class}) +@Import({HttpStubsController.class, TriggerController.class, StubRunnerConfiguration.class}) public @interface EnableStubRunnerServer { } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StreamConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StreamConfiguration.java deleted file mode 100644 index 3d337159c4..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StreamConfiguration.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.springframework.cloud.contract.stubrunner.server; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.cloud.stream.test.binder.MessageCollector; -import org.springframework.cloud.stream.test.binder.TestSupportBinder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -/** - * Configuration required to make Stub Runner server be executed at compile time - * - * @author Marcin Grzejszczak - * - * @since 1.0.0 - */ -@Configuration -class StreamConfiguration { - - @Bean - @Primary - @ConditionalOnClass(name = "org.springframework.cloud.stream.test.binder.TestSupportBinder") - @ConditionalOnMissingBean - public MessageCollector messageCollector() { - return new TestSupportBinder().messageCollector(); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java index d68dd87906..a250d2de6c 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java @@ -17,13 +17,16 @@ package org.springframework.cloud.contract.stubrunner.server; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.config.BindingServiceConfiguration; /** * @author Marcin Grzejszczak */ @SpringBootApplication @EnableStubRunnerServer +@AutoConfigureBefore(BindingServiceConfiguration.class) public class StubRunnerBoot { public static void main(String[] args) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java index 6ea080971e..c5aba14dd0 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java @@ -109,12 +109,6 @@ public @interface AutoConfigureStubRunner { */ StubRunnerProperties.StubsMode stubsMode() default StubRunnerProperties.StubsMode.CLASSPATH; - /** - * If set to {@code true} will not assert whether the downloaded stubs / contract - * JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact) - */ - boolean snapshotCheckSkip() default false; - /** * Properties in form {@literal key=value} * @return the properties to add diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java index 0fe7249658..af6db728e1 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java @@ -94,7 +94,6 @@ public class StubRunnerConfiguration { .withStubPerConsumer(this.props.isStubsPerConsumer()) .withConsumerName(consumerName()) .withMappingsOutputFolder(this.props.getMappingsOutputFolder()) - .withSnapshotCheckSkip(this.props.isSnapshotCheckSkip()) .withDeleteStubsAfterTest(this.props.isDeleteStubsAfterTest()) .withProperties(this.props.getProperties()); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java index 9b6fcc6146..926ad8486a 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java @@ -99,12 +99,6 @@ public class StubRunnerProperties { */ private StubsMode stubsMode; - /** - * If set to {@code true} will not assert whether the downloaded stubs / contract - * JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact) - */ - private boolean snapshotCheckSkip; - /** * If set to {@code false} will NOT delete stubs from a temporary * folder after running tests @@ -241,14 +235,6 @@ public class StubRunnerProperties { this.stubsMode = stubsMode; } - public boolean isSnapshotCheckSkip() { - return this.snapshotCheckSkip; - } - - public void setSnapshotCheckSkip(boolean snapshotCheckSkip) { - this.snapshotCheckSkip = snapshotCheckSkip; - } - public boolean isDeleteStubsAfterTest() { return this.deleteStubsAfterTest; } @@ -278,7 +264,6 @@ public class StubRunnerProperties { + ", ids=" + Arrays.toString(this.ids) + ", classifier='" + this.classifier + '\'' + ", setStubsPerConsumer='" + this.stubsPerConsumer + "', consumerName='" + this.consumerName + '\'' + ", stubsMode='" + this.stubsMode + '\'' - + ", snapshotCheckSkip='" + this.snapshotCheckSkip + '\'' + ", size of properties=" + this.properties.size() + '}'; } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java index b15e3c1366..e1ff0f1905 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java @@ -140,7 +140,6 @@ public class EurekaStubsRegistrar implements StubsRegistrar { public void close() throws Exception { for (EurekaRegistration registration : this.registrations) { this.serviceRegistry.deregister(registration); - registration.close(); } } } diff --git a/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring.factories b/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring.factories index 3fec992554..0f3c598395 100644 --- a/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring.factories @@ -3,9 +3,13 @@ org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner=\ org.springframework.cloud.contract.stubrunner.spring.StubRunnerConfiguration,\ org.springframework.cloud.contract.stubrunner.spring.cloud.StubRunnerSpringCloudAutoConfiguration,\ org.springframework.cloud.contract.stubrunner.spring.cloud.ribbon.StubRunnerRibbonAutoConfiguration,\ -org.springframework.cloud.contract.stubrunner.messaging.stream.StubRunnerStreamConfiguration,\ org.springframework.cloud.contract.stubrunner.messaging.integration.StubRunnerIntegrationConfiguration,\ +org.springframework.cloud.contract.stubrunner.messaging.stream.StubRunnerStreamConfiguration,\ org.springframework.cloud.contract.stubrunner.spring.cloud.zookeeper.StubRunnerSpringCloudZookeeperAutoConfiguration,\ org.springframework.cloud.contract.stubrunner.spring.cloud.eureka.StubRunnerSpringCloudEurekaAutoConfiguration,\ org.springframework.cloud.contract.stubrunner.spring.cloud.consul.StubRunnerSpringCloudConsulAutoConfiguration,\ -org.springframework.cloud.contract.stubrunner.messaging.StubRunnerStreamsIntegrationAutoConfiguration \ No newline at end of file +org.springframework.cloud.contract.stubrunner.messaging.StubRunnerStreamsIntegrationAutoConfiguration + +# Test Execution Listeners +org.springframework.test.context.TestExecutionListener=\ +org.springframework.cloud.contract.stubrunner.provider.wiremock.StubRunnerWireMockTestExecutionListener \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy index f98d130ac9..ccd7e8707d 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy @@ -3,6 +3,8 @@ package org.springframework.cloud.contract.stubrunner import io.specto.hoverfly.junit.HoverflyRule import org.eclipse.aether.RepositorySystemSession import org.junit.Rule +import org.junit.rules.TemporaryFolder + import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties import org.springframework.util.ResourceUtils import spock.lang.Specification @@ -13,7 +15,10 @@ class AetherStubDownloaderSpec extends Specification { @Rule HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode("simulation.json") - def 'Should throw an exception when artifact not found'() { + @Rule + TemporaryFolder folder = new TemporaryFolder() + + def 'should throw an exception when artifact not found in local m2'() { given: StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() .withStubsMode(StubRunnerProperties.StubsMode.LOCAL) @@ -22,126 +27,33 @@ class AetherStubDownloaderSpec extends Specification { AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) when: - def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("non.existing.group", "missing-artifact-id", "1.0-SNAPSHOT")) + aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("non.existing.group", "missing-artifact-id", "1.0-SNAPSHOT")) then: IllegalStateException e = thrown(IllegalStateException) e.message.contains("Exception occurred while trying to download a stub for group") } - def 'Should throw an exception when a jar is in local m2 and not in remote repo'() { + def 'should throw an exception when local m2 gets replaced with a temp dir and a jar is not found in remote'() { given: StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) - .withStubRepositoryRoot("https://test.jfrog.io/test/libs-snapshot-local") + .withStubRepositoryRoot("file://" + folder.newFolder().absolutePath) .build() - - AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) - - when: - def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("org.springframework.cloud", "spring-cloud-contract-spec", "+", "")) - - then: - IllegalStateException e = thrown(IllegalStateException) - e.message.contains("The artifact was found in the local repository but you have explicitly stated that it should be downloaded from a remote one") - } - - @RestoreSystemProperties - def 'Should not throw an exception when a jar is in local m2 and not in remote repo and system property disabled snapshot check'() { - given: - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() - .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) - .withStubRepositoryRoot("https://test.jfrog.io/test/libs-snapshot-local") - .build() - System.properties.setProperty("stubrunner.snapshot-check-skip", "true") + and: + String localRepo = AetherFactories.localRepositoryDirectory(true) + new File(localRepo, "org/springframework/cloud/spring-cloud-contract-spec" + .replaceAll("/", File.separator)).list().size() > 0 and: AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) when: - def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("org.springframework.cloud", "spring-cloud-contract-spec", "+", "")) + aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("org.springframework.cloud", "spring-cloud-contract-spec", "+", "")) then: - jar != null - } - - @RestoreSystemProperties - def 'Should throw an exception when a jar is in local m2 and not in remote repo and system property disabled takes precedence over env'() { - given: - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() - .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) - .withStubRepositoryRoot("https://test.jfrog.io/test/libs-snapshot-local") - .build() - System.properties.setProperty("stubrunner.snapshot-check-skip", "false") - - and: - StubRunnerPropertyUtils.FETCHER = new PropertyFetcher() { - @Override - String systemProp(String prop) { - return super.systemProp(prop) - } - - @Override - String envVar(String prop) { - return "true" - } - } - AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) - - when: - def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("org.springframework.cloud", "spring-cloud-contract-spec", "+", "")) - - then: - IllegalStateException e = thrown(IllegalStateException) - e.message.contains("The artifact was found in the local repository but you have explicitly stated that it should be downloaded from a remote one") - } - - def 'Should not throw an exception when a jar is in local m2 and not in remote repo and env property disabled snapshot check'() { - given: - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() - .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) - .withStubRepositoryRoot("https://test.jfrog.io/test/libs-snapshot-local") - .build() - - and: - StubRunnerPropertyUtils.FETCHER = new PropertyFetcher() { - @Override - String systemProp(String prop) { - return super.systemProp(prop) - } - - @Override - String envVar(String prop) { - return "true" - } - } - AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) - - when: - def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("org.springframework.cloud", "spring-cloud-contract-spec", "+", "")) - - then: - jar != null - - cleanup: - StubRunnerPropertyUtils.FETCHER = new PropertyFetcher() - } - - def 'Should not throw an exception when a jar is in local m2 and not in remote repo and option disabled snapshot check'() { - given: - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() - .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) - .withStubRepositoryRoot("https://test.jfrog.io/test/libs-snapshot-local") - .withSnapshotCheckSkip(true) - .build() - - AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) - - when: - def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("org.springframework.cloud", "spring-cloud-contract-spec", "+", "")) - - then: - jar != null + IllegalArgumentException e = thrown(IllegalArgumentException) + e.message.contains("Could not find metadata org.springframework.cloud:spring-cloud-contract-spec/maven-metadata.xml in remote0") } @RestoreSystemProperties diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy index 63493ca351..bc5f79f859 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy @@ -231,7 +231,7 @@ class StubRunnerOptionsBuilderSpec extends Specification { given: StubRunnerOptionsBuilder builder = builder.withOptions(new StubRunnerOptions(1, 2, new FileSystemResource("root"), StubRunnerProperties.StubsMode.LOCAL, "classifier", [new StubConfiguration("a:b:c")], [(new StubConfiguration("a:b:c")): 3], "foo", "bar", - new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", true, false, [foo: "bar"])) + new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", false, [foo: "bar"])) builder.withStubs("foo:bar:baz") when: StubRunnerOptions options = builder.build() @@ -250,7 +250,6 @@ class StubRunnerOptionsBuilderSpec extends Specification { options.stubsPerConsumer == true options.consumerName == "consumer" options.mappingsOutputFolder == "folder" - options.snapshotCheckSkip == true options.deleteStubsAfterTest == false options.properties == [foo: "bar"] } @@ -260,7 +259,7 @@ class StubRunnerOptionsBuilderSpec extends Specification { StubRunnerOptionsBuilder builder = builder.withOptions(new StubRunnerOptions(1, 2, new FileSystemResource("root"), StubRunnerProperties.StubsMode.CLASSPATH, "classifier", [new StubConfiguration("a:b:c")], [(new StubConfiguration("a:b:c")): 3], "username123", "password123", - new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", true, false, [:])) + new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", false, [:])) builder.withStubs("foo:bar:baz") when: String options = builder.build().toString() @@ -287,7 +286,6 @@ class StubRunnerOptionsBuilderSpec extends Specification { System.setProperty("stubrunner.proxy.host", "host") System.setProperty("stubrunner.proxy.port", "4") System.setProperty("stubrunner.mappings-output-folder", "folder") - System.setProperty("stubrunner.snapshot-check-skip", "true") System.setProperty("stubrunner.properties.foo-bar", "bar") System.setProperty("stubrunner.properties.foo-baz", "baz") System.setProperty("stubrunner.properties.bar.bar", "foo") @@ -307,7 +305,6 @@ class StubRunnerOptionsBuilderSpec extends Specification { options.stubsPerConsumer == true options.consumerName == "consumer" options.mappingsOutputFolder == "folder" - options.snapshotCheckSkip == true options.properties == ["foo-bar": "bar", "foo-baz": "baz", "bar.bar": "foo"] } } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy index 30a48e4f2d..9dab153375 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy @@ -29,7 +29,6 @@ import org.springframework.cloud.contract.stubrunner.StubFinder import org.springframework.cloud.contract.stubrunner.StubNotFoundException import org.springframework.context.annotation.Configuration import org.springframework.core.env.Environment -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @@ -44,7 +43,6 @@ import spock.lang.Specification 'foo=${stubrunner.runningstubs.fraudDetectionServer.port}', 'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}']) @AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/") -@DirtiesContext @ActiveProfiles("test") class StubRunnerConfigurationSpec extends Specification { diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy index f056625880..c18fb30147 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy @@ -31,7 +31,6 @@ import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration import org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import org.springframework.web.client.RestTemplate @@ -50,7 +49,6 @@ import spock.lang.Specification stubsMode = StubRunnerProperties.StubsMode.REMOTE, repositoryRoot = "classpath:m2repo/repository/") // end::autoconfigure[] -@DirtiesContext class StubRunnerSpringCloudAutoConfigurationSpec extends Specification { @Autowired StubFinder stubFinder diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy index 1a207ac896..21ca1a3efb 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy @@ -29,11 +29,11 @@ import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties import org.springframework.cloud.contract.verifier.messaging.MessageVerifier import org.springframework.cloud.stream.annotation.EnableBinding import org.springframework.cloud.stream.messaging.Sink +import org.springframework.cloud.stream.messaging.Source import org.springframework.context.annotation.Configuration import org.springframework.core.env.Environment import org.springframework.http.ResponseEntity import org.springframework.messaging.Message -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration /** * @author Marcin Grzejszczak @@ -45,7 +45,6 @@ import org.springframework.test.context.ContextConfiguration repositoryRoot = "classpath:m2repo/repository/", stubsMode = StubRunnerProperties.StubsMode.REMOTE, stubsPerConsumer = true) -@DirtiesContext class StubRunnerStubsPerConsumerSpec extends Specification { // end::test[] @@ -88,6 +87,6 @@ class StubRunnerStubsPerConsumerSpec extends Specification { @Configuration @EnableAutoConfiguration - @EnableBinding(Sink) + @EnableBinding([Sink, Source]) static class Config {} } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy index 8b2f2e4557..e9b9c47604 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy @@ -29,11 +29,11 @@ import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties import org.springframework.cloud.contract.verifier.messaging.MessageVerifier import org.springframework.cloud.stream.annotation.EnableBinding import org.springframework.cloud.stream.messaging.Sink +import org.springframework.cloud.stream.messaging.Source import org.springframework.context.annotation.Configuration import org.springframework.core.env.Environment import org.springframework.http.ResponseEntity import org.springframework.messaging.Message -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration /** * @author Marcin Grzejszczak @@ -46,7 +46,6 @@ import org.springframework.test.context.ContextConfiguration consumerName = "foo-consumer", stubsMode = StubRunnerProperties.StubsMode.REMOTE, stubsPerConsumer = true) -@DirtiesContext class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification { // end::test[] @@ -89,6 +88,6 @@ class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification { @Configuration @EnableAutoConfiguration - @EnableBinding(Sink) + @EnableBinding([Sink, Source]) static class Config {} } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/consul/StubRunnerSpringCloudConsulAutoConfigurationSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/consul/StubRunnerSpringCloudConsulAutoConfigurationSpec.groovy index d9896e3f91..7a2550c394 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/consul/StubRunnerSpringCloudConsulAutoConfigurationSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/consul/StubRunnerSpringCloudConsulAutoConfigurationSpec.groovy @@ -33,7 +33,6 @@ import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRun import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import static org.mockito.BDDMockito.then @@ -58,7 +57,6 @@ import static org.mockito.Mockito.mock "org.springframework.cloud.contract.verifier.stubs:bootService"], stubsMode = StubRunnerProperties.StubsMode.REMOTE, repositoryRoot = "classpath:m2repo/repository/") -@DirtiesContext class StubRunnerSpringCloudConsulAutoConfigurationSpec extends Specification { @Autowired ConsulClient client diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java index 7cf6e526cc..2392696061 100644 --- a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java @@ -17,13 +17,17 @@ package org.springframework.cloud.contract.stubrunner; import java.io.File; +import java.util.HashMap; import org.assertj.core.api.BDDAssertions; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.springframework.boot.test.rule.OutputCapture; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import static org.assertj.core.api.BDDAssertions.then; @@ -38,6 +42,8 @@ public class ContractProjectUpdaterTest extends AbstractGitTest { GitRepo gitRepo; File origin; + @Rule public OutputCapture outputCapture = new OutputCapture(); + @Before public void setup() throws Exception { GitContractsRepo.CACHED_LOCATIONS.clear(); @@ -69,6 +75,60 @@ public class ContractProjectUpdaterTest extends AbstractGitTest { git.reset().setMode(ResetCommand.ResetType.HARD).call(); } BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).exists(); + BDDAssertions.then(gitRepo.gitFactory.provider).isNull(); + BDDAssertions.then(outputCapture.toString()).contains("No custom credentials provider will be set"); + } + + @Test + public void should_push_changes_to_current_branch_using_credentials() throws Exception { + StubRunnerOptions options = new StubRunnerOptionsBuilder() + .withStubRepositoryRoot("file://" + this.project.getAbsolutePath() + "/") + .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) + .withProperties(new HashMap() { + { + put("git.username", "foo"); + put("git.password", "bar"); + } + } ) + .build(); + ContractProjectUpdater updater = new ContractProjectUpdater(options); + File stubs = new File(GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI()); + + updater.updateContractProject("hello-world", stubs.toPath()); + + // project, not origin, cause we're making one more clone of the local copy + try(Git git = openGitProject(this.project)) { + RevCommit revCommit = git.log().call().iterator().next(); + then(revCommit.getShortMessage()).isEqualTo("Updating project [hello-world] with stubs"); + // I have no idea but the file gets deleted after pushing + git.reset().setMode(ResetCommand.ResetType.HARD).call(); + } + BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).exists(); + BDDAssertions.then(outputCapture.toString()).contains("Passed username and password - will set a custom credentials provider"); + } + + @Test + public void should_push_changes_to_current_branch_using_root_credentials() throws Exception { + StubRunnerOptions options = new StubRunnerOptionsBuilder() + .withStubRepositoryRoot("file://" + this.project.getAbsolutePath() + "/") + .withStubsMode(StubRunnerProperties.StubsMode.REMOTE) + .withUsername("foo") + .withPassword("bar") + .build(); + ContractProjectUpdater updater = new ContractProjectUpdater(options); + File stubs = new File(GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI()); + + updater.updateContractProject("hello-world", stubs.toPath()); + + // project, not origin, cause we're making one more clone of the local copy + try(Git git = openGitProject(this.project)) { + RevCommit revCommit = git.log().call().iterator().next(); + then(revCommit.getShortMessage()).isEqualTo("Updating project [hello-world] with stubs"); + // I have no idea but the file gets deleted after pushing + git.reset().setMode(ResetCommand.ResetType.HARD).call(); + } + BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).exists(); + BDDAssertions.then(outputCapture.toString()).contains("Passed username and password - will set a custom credentials provider"); } @Test diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java new file mode 100644 index 0000000000..a17916b10c --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java @@ -0,0 +1,30 @@ +package org.springframework.cloud.contract.stubrunner; + +import org.junit.Test; +import org.springframework.cloud.contract.spec.Contract; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +/** + * @author Sven Bayer + */ +public class StubRepositoryTest { + private static final File YAML_REPOSITORY_LOCATION = new File("src/test/resources/customYamlRepository"); + + @Test + public void should_prefer_custom_yaml_converter_over_standard() { + //given: + StubRepository repository = new StubRepository(YAML_REPOSITORY_LOCATION, new ArrayList<>(), new StubRunnerOptionsBuilder().build()); + int expectedDescriptorsSize = 1; + + //when: + Collection descriptors = repository.getContracts(); + + //then: + assertEquals(expectedDescriptorsSize, descriptors.size()); + } +} diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRunnerSliceTests.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRunnerSliceTests.java index 0e49703aab..a250d83608 100644 --- a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRunnerSliceTests.java +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRunnerSliceTests.java @@ -24,7 +24,6 @@ import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; @@ -47,7 +46,6 @@ import static org.assertj.core.api.Assertions.assertThat; maxPort = 10020, mappingsOutputFolder = "target/outputmappings/", properties = {"hello=world", "foo=bar"}) -@DirtiesContext @ActiveProfiles("test") public class StubRunnerSliceTests { diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/TestCustomYamlContractConverter.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/TestCustomYamlContractConverter.java new file mode 100644 index 0000000000..65ce6e4f38 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/TestCustomYamlContractConverter.java @@ -0,0 +1,42 @@ +package org.springframework.cloud.contract.stubrunner; + +import groovy.lang.Closure; +import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.spec.ContractConverter; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +/** + * @author Sven Bayer + */ +public class TestCustomYamlContractConverter implements ContractConverter { + + @Override + public boolean isAccepted(File file) { + if (!file.getName().endsWith(".yml") && !file.getName().endsWith(".yaml")) { + return false; + } + Optional line; + try { + line = Files.lines(file.toPath()).findFirst(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + return line.isPresent() && line.get().startsWith("custom_format: 1.0"); + } + + @Override + public Collection convertFrom(File file) { + return Collections.singleton(Contract.make(Closure.IDENTITY)); + } + + @Override + public Object convertTo(Collection contract) { + return new Object(); + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories b/spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories index b149be0c5b..7b88e22c0b 100644 --- a/spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories +++ b/spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories @@ -1,2 +1,4 @@ org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\ -org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions \ No newline at end of file +org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions +org.springframework.cloud.contract.spec.ContractConverter=\ +org.springframework.cloud.contract.stubrunner.TestCustomYamlContractConverter \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/test/resources/customYamlRepository/contracts/custom.yml b/spring-cloud-contract-stub-runner/src/test/resources/customYamlRepository/contracts/custom.yml new file mode 100644 index 0000000000..fce56b04db --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/resources/customYamlRepository/contracts/custom.yml @@ -0,0 +1,16 @@ +custom_format: 1.0 +outbound: + request: + url: /foo + method: PUT + headers: + foo: bar + body: + foo: bar +inbound: + response: + status: 200 + headers: + foo2: bar + body: + foo2: bar \ No newline at end of file diff --git a/spring-cloud-contract-tools/pom.xml b/spring-cloud-contract-tools/pom.xml index 0f54ab7bc3..df5bf4bd6f 100644 --- a/spring-cloud-contract-tools/pom.xml +++ b/spring-cloud-contract-tools/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml index d46fbd95d0..9cdc74f4ac 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-converters @@ -41,6 +41,11 @@ com.github.tomakehurst wiremock-standalone + + org.springframework.boot + spring-boot-autoconfigure-processor + true + org.spockframework spock-core @@ -66,6 +71,11 @@ spring-boot-starter-web test + + io.rest-assured + rest-assured + test + diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverter.groovy index 043002d9c7..1dbfad196b 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverter.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverter.groovy @@ -58,7 +58,7 @@ class RecursiveFilesConverter { void processFiles() { ContractFileScanner scanner = new ContractFileScanner(properties.contractsDslDir, - properties.excludedFiles as Set, [] as Set, properties.includedContracts) + properties.excludedFiles as Set, [] as Set, [] as Set, properties.includedContracts) ListMultimap contracts = scanner.findContracts() if (log.isDebugEnabled()) { log.debug("Found the following contracts $contracts") diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle index d289534e48..7790251c72 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle @@ -23,7 +23,7 @@ ext { ] } -project.version = findProperty('verifierVersion') ?: '2.0.0.BUILD-SNAPSHOT' +project.version = findProperty('verifierVersion') apply plugin: 'groovy' apply from: "$rootDir/gradle/release.gradle" apply plugin: 'eclipse' @@ -32,8 +32,8 @@ apply plugin: 'checkstyle' group = 'org.springframework.cloud' -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.8 +targetCompatibility = 1.8 repositories { mavenLocal() @@ -75,7 +75,7 @@ dependencies { } testCompile 'info.solidsoft.spock:spock-global-unroll:0.5.0' testCompile gradleTestKit() - checkstyle 'org.springframework.cloud:spring-cloud-build:1.0.2.RELEASE' + checkstyle 'org.springframework.cloud:spring-cloud-build:2.0.3.RELEASE' } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties index 1946c9108a..befbc3599e 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties @@ -16,6 +16,6 @@ nexusUsername = nexusPassword = -verifierVersion=2.0.0.BUILD-SNAPSHOT +verifierVersion=2.1.0.BUILD-SNAPSHOT org.gradle.daemon=false aetherVersion=1.0.2.v20150114 \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.jar index 96d36c23c8..0ab4ff82ef 100644 Binary files a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.jar and b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.properties index 64ffcab3bf..dc3996502f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 31 11:13:36 CEST 2017 +#Tue Aug 28 16:43:49 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradlew b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradlew index 4453ccea33..cccdd3d517 100755 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradlew +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml index f6aa31661c..824cfdc05f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy index 5cabf8516e..3554305263 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy @@ -175,7 +175,10 @@ class ContractVerifierExtension { /** * If set to {@code true} will not assert whether the downloaded stubs / contract * JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact) + * + * @deprecated - with 2.1.0 this option is redundant */ + @Deprecated boolean contractsSnapshotCheckSkip = false /** diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy index 527f5ece93..29c3efc7a9 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy @@ -86,7 +86,6 @@ class GradleContractsDownloader { .withStubsMode(extension.contractsMode) .withUsername(extension.contractRepository.username) .withPassword(extension.contractRepository.password) - .withSnapshotCheckSkip(extension.contractsSnapshotCheckSkip) .withDeleteStubsAfterTest(extension.deleteStubsAfterTest) .withProperties(extension.contractsProperties) if (extension.contractRepository.proxyPort) { diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index 2495415709..401ee34d3c 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -36,7 +36,9 @@ dependencies { testCompile "com.github.tomakehurst:wiremock:${wiremockVersion}" testCompile "com.toomuchcoding.jsonassert:jsonassert:${jsonAssertVersion}" - testCompile "org.spockframework:spock-spring:1.0-groovy-2.4" + testCompile("org.spockframework:spock-spring:1.0-groovy-2.4") { + exclude(group: 'org.codehaus.groovy') + } testCompile "io.restassured:rest-assured:$restAssuredVersion" testCompile "io.restassured:spring-mock-mvc:$restAssuredVersion" testCompile "ch.qos.logback:logback-classic:1.1.2" diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 60b93febf8..63e1b05224 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -wiremockVersion=2.17.0 -jsonAssertVersion=0.4.12 -verifierVersion=2.0.0.BUILD-SNAPSHOT - +wiremockVersion=2.18.0 +jsonAssertVersion=0.4.13 +verifierVersion=2.1.0.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle index 3f1c06b451..de113ce1f2 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle @@ -52,7 +52,9 @@ subprojects { dependencies { testCompile 'org.codehaus.groovy:groovy-all:2.5.0-beta-1' - testCompile "org.spockframework:spock-core:$spockVersion" + testCompile("org.spockframework:spock-core:$spockVersion") { + exclude(group: 'org.codehaus.groovy') + } testCompile 'junit:junit:4.12' testCompile "com.github.tomakehurst:wiremock:${wiremockVersion}" testCompile "com.toomuchcoding.jsonassert:jsonassert:${jsonAssertVersion}" @@ -81,7 +83,9 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') compile 'org.springframework.boot:spring-boot-starter-jersey' compile 'org.springframework.boot:spring-boot-starter-jetty' - testRuntime "org.spockframework:spock-spring:$spockVersion" + testRuntime("org.spockframework:spock-spring:$spockVersion") { + exclude(group: 'org.codehaus.groovy') + } testCompile "org.mockito:mockito-core" testCompile "org.springframework:spring-test" diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties index c98aae7429..63e1b05224 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties @@ -13,6 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -wiremockVersion=2.17.0 -jsonAssertVersion=0.4.12 -verifierVersion=2.0.0.BUILD-SNAPSHOT +wiremockVersion=2.18.0 +jsonAssertVersion=0.4.13 +verifierVersion=2.1.0.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index 62f4407ffe..497a687518 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -48,7 +48,9 @@ subprojects { } dependencies { - testCompile "org.codehaus.groovy:groovy-all:2.5.0-beta-1" + testCompile("org.codehaus.groovy:groovy-all:2.5.0-beta-1") { + exclude(group: 'org.codehaus.groovy') + } testCompile "org.spockframework:spock-core:$spockVersion" testCompile("junit:junit:4.12") testCompile "com.github.tomakehurst:wiremock:${wiremockVersion}" @@ -89,7 +91,9 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') compile("org.springframework.boot:spring-boot-starter-jetty") compile("org.springframework.boot:spring-boot-starter-actuator") - testRuntime "org.spockframework:spock-spring:$spockVersion" + testRuntime("org.spockframework:spock-spring:$spockVersion") { + exclude(group: 'org.codehaus.groovy') + } testCompile "org.mockito:mockito-core" testCompile "org.springframework:spring-test" testCompile "org.springframework.boot:spring-boot-test" diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties index 2ccc777609..63e1b05224 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties @@ -13,6 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -wiremockVersion=2.17.0 -jsonAssertVersion=0.4.12 -verifierVersion=2.0.0.BUILD-SNAPSHOT \ No newline at end of file +wiremockVersion=2.18.0 +jsonAssertVersion=0.4.13 +verifierVersion=2.1.0.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle index f54d7b18b0..52611a7a90 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle @@ -50,7 +50,9 @@ subprojects { dependencies { testCompile "org.codehaus.groovy:groovy-all:2.5.0-beta-1" - testCompile "org.spockframework:spock-core:$spockVersion" + testCompile("org.spockframework:spock-core:$spockVersion") { + exclude(group: 'org.codehaus.groovy') + } testCompile "junit:junit:4.12" testCompile "com.github.tomakehurst:wiremock:${wiremockVersion}" testCompile "com.toomuchcoding.jsonassert:jsonassert:${jsonAssertVersion}" @@ -113,7 +115,9 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') compile("org.springframework.boot:spring-boot-starter-jetty") compile("org.springframework.boot:spring-boot-starter-actuator") - testRuntime "org.spockframework:spock-spring:$spockVersion" + testRuntime("org.spockframework:spock-spring:$spockVersion") { + exclude(group: 'org.codehaus.groovy') + } testCompile "org.mockito:mockito-core" testCompile "org.springframework:spring-test" testCompile "org.springframework.boot:spring-boot-test" diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties index 60b93febf8..63e1b05224 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -wiremockVersion=2.17.0 -jsonAssertVersion=0.4.12 -verifierVersion=2.0.0.BUILD-SNAPSHOT - +wiremockVersion=2.18.0 +jsonAssertVersion=0.4.13 +verifierVersion=2.1.0.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml index 71bc3b0a02..e400b4d0af 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. @@ -33,7 +33,7 @@ 1.7 - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT 2016 @@ -208,10 +208,10 @@ true true - ${project.basedir}/src/main/java,${project.basedir}/src/main/groovy + ${project.basedir}/src/main/java,${project.basedir}/src/main/groovy - ${project.basedir}/src/test/java,${project.basedir}/src/test/groovy + ${project.basedir}/src/test/java,${project.basedir}/src/test/groovy diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java index 639953ba16..1328801a79 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java @@ -145,8 +145,11 @@ public class ConvertMojo extends AbstractMojo { /** * If {@code true} then will not assert whether a stub / contract * JAR was downloaded from local or remote location + * + * @deprecated - with 2.1.0 this option is redundant */ @Parameter(property = "contractsSnapshotCheckSkip", defaultValue = "false") + @Deprecated private boolean contractsSnapshotCheckSkip; @@ -230,7 +233,7 @@ public class ConvertMojo extends AbstractMojo { this.contractsPath, this.contractsRepositoryUrl, this.contractsMode, getLog(), this.contractsRepositoryUsername, this.contractsRepositoryPassword, this.contractsRepositoryProxyHost, this.contractsRepositoryProxyPort, - this.contractsSnapshotCheckSkip, this.deleteStubsAfterTest, this.contractsProperties) + this.deleteStubsAfterTest, this.contractsProperties) .downloadAndUnpackContractsIfRequired(config, this.contractsDirectory); } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java index 4c466b7405..99cd483dbc 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java @@ -21,6 +21,7 @@ import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; + import javax.inject.Inject; import org.apache.maven.model.Dependency; @@ -33,6 +34,7 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.eclipse.aether.RepositorySystemSession; + import org.springframework.cloud.contract.maven.verifier.stubrunner.AetherStubDownloaderFactory; import org.springframework.cloud.contract.spec.ContractVerifierException; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; @@ -96,6 +98,12 @@ public class GenerateTestsMojo extends AbstractMojo { @Parameter private List excludedFiles; + /** + * Patterns that should be taken into account for processing + */ + @Parameter(property = "includedFiles") + private List includedFiles; + /** * Incubating feature. You can check the size of JSON arrays. If not turned on * explicitly will be disabled. @@ -199,8 +207,11 @@ public class GenerateTestsMojo extends AbstractMojo { /** * If {@code true} then will not assert whether a stub / contract * JAR was downloaded from local or remote location + * + * @deprecated - with 2.1.0 this option is redundant */ @Parameter(property = "contractsSnapshotCheckSkip", defaultValue = "false") + @Deprecated private boolean contractsSnapshotCheckSkip; /** @@ -238,7 +249,7 @@ public class GenerateTestsMojo extends AbstractMojo { this.contractsPath, this.contractsRepositoryUrl, this.contractsMode, getLog(), this.contractsRepositoryUsername, this.contractsRepositoryPassword, this.contractsRepositoryProxyHost, this.contractsRepositoryProxyPort, - this.contractsSnapshotCheckSkip, this.deleteStubsAfterTest, this.contractsProperties).downloadAndUnpackContractsIfRequired(config, this.contractsDirectory); + this.deleteStubsAfterTest, this.contractsProperties).downloadAndUnpackContractsIfRequired(config, this.contractsDirectory); getLog().info("Directory with contract is present at [" + contractsDirectory + "]"); setupConfig(config, contractsDirectory); this.project.addTestCompileSourceRoot(this.generatedTestSourcesDir.getAbsolutePath()); @@ -277,6 +288,7 @@ public class GenerateTestsMojo extends AbstractMojo { config.setStaticImports(this.staticImports); config.setIgnoredFiles(this.ignoredFiles); config.setExcludedFiles(this.excludedFiles); + config.setIncludedFiles(this.includedFiles); config.setAssertJsonSize(this.assertJsonSize); config.setPackageWithBaseClasses(this.packageWithBaseClasses); if (this.baseClassMappings != null) { diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java index 338ba8b5ab..bd1027495a 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java @@ -54,7 +54,6 @@ class MavenContractsDownloader { private final String repositoryPassword; private final String repositoryProxyHost; private final Integer repositoryProxyPort; - private final boolean contractsSnapshotCheckSkip; private final boolean deleteStubsAfterTest; private final Map contractsProperties; @@ -62,8 +61,8 @@ class MavenContractsDownloader { String contractsPath, String contractsRepositoryUrl, StubRunnerProperties.StubsMode stubsMode, Log log, String repositoryUsername, String repositoryPassword, String repositoryProxyHost, - Integer repositoryProxyPort, boolean contractsSnapshotCheckSkip, - boolean deleteStubsAfterTest, Map contractsProperties) { + Integer repositoryProxyPort, boolean deleteStubsAfterTest, + Map contractsProperties) { this.project = project; this.contractDependency = contractDependency; this.contractsPath = contractsPath; @@ -75,7 +74,6 @@ class MavenContractsDownloader { this.repositoryProxyHost = repositoryProxyHost; this.repositoryProxyPort = repositoryProxyPort; this.stubDownloaderBuilderProvider = new StubDownloaderBuilderProvider(); - this.contractsSnapshotCheckSkip = contractsSnapshotCheckSkip; this.deleteStubsAfterTest = deleteStubsAfterTest; this.contractsProperties = contractsProperties; } @@ -122,7 +120,6 @@ class MavenContractsDownloader { .withStubsMode(this.stubsMode) .withUsername(this.repositoryUsername) .withPassword(this.repositoryPassword) - .withSnapshotCheckSkip(this.contractsSnapshotCheckSkip) .withDeleteStubsAfterTest(this.deleteStubsAfterTest) .withProperties(this.contractsProperties); if (StringUtils.hasText(this.contractsRepositoryUrl)) { diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java index 54beac401b..70fa7c69bc 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java @@ -305,4 +305,19 @@ public class PluginUnitTest { then(this.capture.toString()).contains("Skipping pushing stubs to scm since your"); } + + @Test + public void shouldGenerateContractTestsForIncludedFilesPattern() throws Exception { + File basedir = this.resources.getBasedir("complex-common-repo-with-messaging"); + + this.maven.executeMojo(basedir, "generateTests", defaultPackageForTests(), + newParameter("contractsRepositoryUrl", + "file://" + PluginUnitTest.class.getClassLoader() + .getResource("m2repo/repository").getFile() + .replace("/", File.separator))); + assertFilesPresent(basedir, + "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/common_repo_with_inclusion/kafka_topics/coupon_sent/src/main/resources/contracts/rule_engine_daemon/MessagingTest.java"); + assertFilesPresent(basedir, + "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/common_repo_with_inclusion/reward_rules/src/main/resources/contracts/reward_rules/rest/admin/V1Test.java"); + } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-common-repo-with-messaging/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-common-repo-with-messaging/pom.xml new file mode 100644 index 0000000000..6c71dbfd26 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-common-repo-with-messaging/pom.xml @@ -0,0 +1,61 @@ + + + + 4.0.0 + + com.example + social-service + 0.1.BUILD-SNAPSHOT + + + + + org.springframework.cloud + spring-cloud-contract-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + REMOTE + + com.example + services-contracts + + + + / + + + .*messaging.* + com.example.services.TestBase + + + .*rest.* + com.example.services.TestBase + + + + **/coupon-sent/** + **/reward-rules/** + + + + + + + \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-common-repo-with-messaging/src/test/java/com/example/services/TestBase.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-common-repo-with-messaging/src/test/java/com/example/services/TestBase.java new file mode 100644 index 0000000000..0ad45b3dcf --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-common-repo-with-messaging/src/test/java/com/example/services/TestBase.java @@ -0,0 +1,30 @@ +/** + * + * Copyright 2013-2017 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 com.example.services; + +import io.restassured.module.mockmvc.RestAssuredMockMvc; + +import org.junit.Before; + +public class TestBase { + + @Before + public void setup() { + RestAssuredMockMvc.standaloneSetup(new FraudDetectionController()); + } + +} \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml index 12e3f170b5..9b9c0000e6 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE @@ -63,7 +63,7 @@ com.toomuchcoding.jsonassert jsonassert - 0.4.12 + 0.4.13 test diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/different-module-configuration/module/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/different-module-configuration/module/pom.xml index 41d696374a..fe0933073e 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/different-module-configuration/module/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/different-module-configuration/module/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE @@ -62,7 +62,7 @@ com.toomuchcoding.jsonassert jsonassert - 0.4.12 + 0.4.13 test diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml index 88ecd59ebe..784986df59 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml index c229356fed..f645fb0d56 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml index 211d29fc5e..801cb2ecb5 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.1.RELEASE + 2.0.4.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/1.0.4-SNAPSHOT/contracts-0.0.1-SNAPSHOT.pom b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/1.0.4-SNAPSHOT/contracts-0.0.1-SNAPSHOT.pom new file mode 100644 index 0000000000..1369bcaf20 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/1.0.4-SNAPSHOT/contracts-0.0.1-SNAPSHOT.pom @@ -0,0 +1,25 @@ + + + + + 4.0.0 + com.example + contracts + 0.0.1-SNAPSHOT + pom + diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/maven-metadata-local.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 0000000000..5b45432a6e --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,24 @@ + + + com.example + services-contracts + 1.0.4-SNAPSHOT + + + true + + 20160916125313 + + + jar + 1.0.4-SNAPSHOT + 20160916125313 + + + pom + 1.0.4-SNAPSHOT + 20160916125313 + + + + diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/services-contracts-1.0.4-SNAPSHOT.jar b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/services-contracts-1.0.4-SNAPSHOT.jar new file mode 100644 index 0000000000..54c268d26c Binary files /dev/null and b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/services-contracts-1.0.4-SNAPSHOT.jar differ diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/services-contracts-1.0.4-SNAPSHOT.pom b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/services-contracts-1.0.4-SNAPSHOT.pom new file mode 100644 index 0000000000..a807f11aac --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/1.0.4-SNAPSHOT/services-contracts-1.0.4-SNAPSHOT.pom @@ -0,0 +1,25 @@ + + + + + 4.0.0 + com.example + services-contracts + 1.0.4-SNAPSHOT + pom + diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/maven-metadata.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/maven-metadata.xml new file mode 100644 index 0000000000..278b59b02a --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/services-contracts/maven-metadata.xml @@ -0,0 +1,28 @@ + + + + + com.example + services-contracts + 1.0.4-SNAPSHOT + + + 1.0.4-SNAPSHOT + + 20160409062112 + + diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-pact/pom.xml index 6b5ccca6d9..127bf49a5d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-pact/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-pact @@ -45,6 +45,11 @@ au.com.dius pact-jvm-provider-junit_2.12 + + org.springframework.boot + spring-boot-autoconfigure-processor + true + org.spockframework spock-core diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy index 8c2a5184e2..fb83d7a008 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy @@ -64,14 +64,14 @@ class PactContractConverter implements ContractConverter> { @Override Collection convertTo(Collection contracts) { List pactContracts = new ArrayList<>() - contracts.collect({ Contract contract -> + for (Contract contract : contracts) { if (contract.request) { pactContracts.add(requestResponsePactCreator.createFromContract(contract)) } if (contract.input) { pactContracts.add(messagePactCreator.createFromContract(contract)) } - }) + } return pactContracts } } diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index 4192af5a78..398a96c486 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-verifier @@ -89,6 +89,11 @@ org.yaml snakeyaml + + org.springframework.boot + spring-boot-autoconfigure-processor + true + cglib cglib @@ -123,6 +128,10 @@ spring-mock-mvc test + + org.junit.jupiter + junit-jupiter-api + org.springframework.boot spring-boot-starter-jersey diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/TestGenerator.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/TestGenerator.groovy index df4ef6d63e..7cdb9f9059 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/TestGenerator.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/TestGenerator.groovy @@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets import java.nio.file.Path import java.util.concurrent.atomic.AtomicInteger +import groovy.transform.CompileStatic import groovy.transform.PackageScope import org.apache.commons.logging.Log import org.apache.commons.logging.LogFactory @@ -39,14 +40,16 @@ import static org.springframework.cloud.contract.verifier.util.NamesUtil.beforeL import static org.springframework.cloud.contract.verifier.util.NamesUtil.convertIllegalPackageChars import static org.springframework.cloud.contract.verifier.util.NamesUtil.directoryToPackage import static org.springframework.cloud.contract.verifier.util.NamesUtil.toLastDot + /** * @author Jakub Kubrynski, codearte.io */ +@CompileStatic class TestGenerator { - private static final Log log = LogFactory.getLog(TestGenerator) private static final String DEFAULT_CLASS_PREFIX = "ContractVerifier" private static final String DEFAULT_TEST_PACKAGE = "org.springframework.cloud.contract.verifier.tests" + private static final Log log = LogFactory.getLog(TestGenerator) private final ContractVerifierConfigProperties configProperties private AtomicInteger counter = new AtomicInteger() @@ -77,6 +80,7 @@ class TestGenerator { contractFileScanner = new ContractFileScanner(configProperties.contractsDslDir, configProperties.excludedFiles as Set, configProperties.ignoredFiles as Set, + configProperties.includedFiles as Set, this.configProperties.includedContracts) } @@ -104,7 +108,7 @@ class TestGenerator { } } - private String relativizeContractPath(Map.Entry> entry) { + private String relativizeContractPath(Map.Entry> entry) { Path relativePath = configProperties.contractsDslDir.toPath().relativize(entry.getKey()) if (StringUtils.isEmpty(relativePath.toString())) { return DEFAULT_CLASS_PREFIX @@ -115,7 +119,7 @@ class TestGenerator { private void processIncludedDirectory( final String includedDirectoryRelativePath, Collection contracts, final String basePackageNameForClass) { if (log.isDebugEnabled()) { - log.debug("Collected contracts with metadata ${contracts}") + log.debug("Collected contracts with metadata ${contracts} relative path is [${includedDirectoryRelativePath}]") } if (contracts.size()) { def className = afterLast(includedDirectoryRelativePath.toString(), File.separator) + resolveNameSuffix() diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilder.groovy index 12a6f66799..5b6b5cb61b 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilder.groovy @@ -18,6 +18,9 @@ package org.springframework.cloud.contract.verifier.builder import groovy.transform.CompileStatic import groovy.transform.PackageScope +import org.apache.commons.logging.Log +import org.apache.commons.logging.LogFactory + import org.springframework.cloud.contract.verifier.config.TestFramework import org.springframework.cloud.contract.verifier.util.NamesUtil import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties @@ -33,6 +36,8 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig @PackageScope class ClassBuilder { + private static final Log log = LogFactory.getLog(ClassBuilder) + private static final String SEPARATOR = "_REPLACEME_" private final String className @@ -72,11 +77,15 @@ class ClassBuilder { } protected static String retrieveBaseClass(ContractVerifierConfigProperties properties, String includedDirectoryRelativePath) { + String contractPathAsPackage = includedDirectoryRelativePath.replace(File.separator, ".") String contractPackage = includedDirectoryRelativePath.replace(File.separator, SEPARATOR) // package mapping takes super precedence if (properties.baseClassMappings) { Map.Entry mapping = properties.baseClassMappings.find { String pattern, String fqn -> - return contractPackage.matches(pattern) + return contractPathAsPackage.matches(pattern) + } + if (log.isDebugEnabled()) { + log.debug("Matching pattern for contract package [${contractPathAsPackage}] with setup ${properties.baseClassMappings} is [${mapping}]") } if (mapping) { return mapping.value @@ -104,11 +113,16 @@ class ClassBuilder { return this } - ClassBuilder addImport(List importsToAdd) { + ClassBuilder addImports(List importsToAdd) { imports.addAll(importsToAdd) return this } + ClassBuilder addStaticImports(List importsToAdd) { + staticImports.addAll(importsToAdd) + return this + } + ClassBuilder addStaticImport(String importToAdd) { staticImports << importToAdd return this @@ -124,13 +138,6 @@ class ClassBuilder { return this } - private String appendColonIfJUniTest(String field) { - if (lang == TestFramework.JUNIT && !field.endsWith(';')) { - return "$field;" - } - return field - } - ClassBuilder addField(List fieldsToAdd) { fields.addAll(fieldsToAdd.collect { appendColonIfJUniTest(it) }) return this @@ -202,4 +209,15 @@ class ClassBuilder { void addClassLevelAnnotation(String annotation) { classLevelAnnotations << annotation } + + private String appendColonIfJUniTest(String field) { + if (isJUnitType(field)) { + return "$field;" + } + return field + } + + private boolean isJUnitType(String field) { + TestFramework.JUNIT == lang || TestFramework.JUNIT5 == lang && !field.endsWith(';') + } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy index 20a0d34a12..36b81107ed 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy @@ -16,9 +16,12 @@ package org.springframework.cloud.contract.verifier.builder +import java.util.regex.Pattern + import groovy.json.StringEscapeUtils import groovy.transform.PackageScope import groovy.transform.TypeChecked + import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.internal.Cookie import org.springframework.cloud.contract.spec.internal.ExecutionProperty @@ -29,8 +32,6 @@ import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import org.springframework.cloud.contract.verifier.util.MapConverter -import java.util.regex.Pattern - import static groovy.json.StringEscapeUtils.escapeJava import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT @@ -195,7 +196,7 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { protected String getInputString() { String request = 'ContractVerifierMessage inputMessage = contractVerifierMessaging.create(' if (inputMessage.messageBody) { - request = "${request}\n\t\t\t\t\"${StringEscapeUtils.escapeJava(bodyAsString)}\"\n" + request = "${request}\n\t\t\t\t\"${escapeJava(bodyAsString)}\"\n" } if (inputMessage.messageHeaders) { request = "${request}\t\t\t\t, headers()" diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy index 0d1dbd64fb..b8ff6f9e10 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy @@ -26,12 +26,14 @@ import org.springframework.cloud.contract.spec.internal.Header import org.springframework.cloud.contract.spec.internal.NamedProperty import org.springframework.cloud.contract.spec.internal.Request import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.util.RegexpBuilders import java.util.regex.Pattern import static groovy.json.StringEscapeUtils.escapeJava import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT import static org.springframework.cloud.contract.verifier.util.ContentUtils.getJavaMultipartFileParameterContent + /** * Root class for JUnit method building * @@ -78,7 +80,7 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder @Override protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return "assertThat(responseBody${property}).${buildEscapedMatchesMethod(value)}" + return "assertThat(responseBody${property}).${createBodyComparison(value)}" } @Override @@ -183,6 +185,11 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder return buildEscapedMatchesMethod(headerValue) + ";" } + protected String createBodyComparison(Pattern bodyValue) { + String patternAsString = bodyValue.pattern() + return createMatchesMethod(RegexpBuilders.buildGStringRegexpForTestSide(patternAsString)) + ";" + } + protected String createCookieComparison(Object cookieValue) { String escapedCookie = convertUnicodeEscapesIfRequired("$cookieValue") return "isEqualTo(\"$escapedCookie\");" @@ -197,8 +204,7 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder return createMatchesMethod(escapedHeader) } - protected String createMatchesMethod(String escapedHeader) { - return "matches(\"$escapedHeader\")" + protected String createMatchesMethod(String pattern) { + return "matches(\"$pattern\")" } - } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy index c3de1c34a7..b6c09026c6 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy @@ -16,20 +16,25 @@ package org.springframework.cloud.contract.verifier.builder +import java.lang.invoke.MethodHandles + import groovy.transform.Canonical import groovy.transform.EqualsAndHashCode import groovy.transform.PackageScope import org.apache.commons.logging.Log import org.apache.commons.logging.LogFactory + import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.verifier.builder.imports.HttpImportProvider +import org.springframework.cloud.contract.verifier.builder.imports.MessagingImportProvider import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties -import org.springframework.cloud.contract.verifier.config.TestFramework -import org.springframework.cloud.contract.verifier.config.TestMode import org.springframework.cloud.contract.verifier.file.ContractMetadata -import java.lang.invoke.MethodHandles - +import static org.springframework.cloud.contract.verifier.builder.imports.BaseImportProvider.getImports +import static org.springframework.cloud.contract.verifier.builder.imports.BaseImportProvider.getRuleImport +import static org.springframework.cloud.contract.verifier.builder.imports.BaseImportProvider.getStaticImports import static org.springframework.cloud.contract.verifier.util.NamesUtil.capitalize + /** * Builds a single test for the given {@link ContractVerifierConfigProperties properties} * @@ -48,61 +53,39 @@ class JavaTestGenerator implements SingleTestGenerator { @Override String buildClass(ContractVerifierConfigProperties configProperties, Collection listOfFiles, String className, String classPackage, String includedDirectoryRelativePath) { ClassBuilder clazz = ClassBuilder.createClass(capitalize(className), classPackage, configProperties, includedDirectoryRelativePath) - if (configProperties.imports) { configProperties.imports.each { clazz.addImport(it) } } - if (configProperties.staticImports) { configProperties.staticImports.each { clazz.addStaticImport(it) } } - if (isScenarioClass(listOfFiles)) { - clazz.addImport(configProperties.targetFramework.getOrderAnnotationImport()) + clazz.addImports(configProperties.targetFramework.getOrderAnnotationImports()) clazz.addClassLevelAnnotation(configProperties.targetFramework.getOrderAnnotation()) } - addJsonPathRelatedImports(clazz) + processContractFiles(listOfFiles, configProperties, clazz) + return clazz.build() + } + private void processContractFiles(Collection listOfFiles, + ContractVerifierConfigProperties configProperties, ClassBuilder clazz) { Map contracts = mapContractsToTheirTestTypes(listOfFiles) - boolean restAssured2Present = this.checker.isClassPresent(REST_ASSURED_2_0_CLASS) - String restAssuredPackage = restAssured2Present ? 'com.jayway.restassured' : 'io.restassured' - if (log.isDebugEnabled()) { - log.debug("Rest Assured version 2.x found [${restAssured2Present}]") - } boolean conditionalImportsAdded = false - boolean toIgnore = listOfFiles.ignored.find { it } - contracts.each { ParsedDsl key, TestType value -> + boolean toIgnore = listOfFiles.ignored.find {it} + contracts.each {ParsedDsl key, TestType value -> if (!conditionalImportsAdded) { + clazz.addImports(getImports(configProperties.targetFramework)) + clazz.addStaticImports(getStaticImports(configProperties.targetFramework)) if (contracts.values().contains(TestType.HTTP)) { - if (configProperties.testMode == TestMode.JAXRSCLIENT) { - clazz.addStaticImport('javax.ws.rs.client.Entity.*') - if (configProperties.targetFramework == TestFramework.JUNIT) { - clazz.addImport('javax.ws.rs.core.Response') - } - } else if (configProperties.testMode == TestMode.MOCKMVC) { - clazz.addStaticImport("${restAssuredPackage}.module.mockmvc.RestAssuredMockMvc.*") - } else { - clazz.addStaticImport("${restAssuredPackage}.RestAssured.*") - } + addHttpRelatedEntries(clazz, configProperties) } - if (configProperties.targetFramework == TestFramework.JUNIT) { - if (contracts.values().contains(TestType.HTTP) && configProperties.testMode == TestMode.MOCKMVC) { - clazz.addImport("${restAssuredPackage}.module.mockmvc.specification.MockMvcRequestSpecification") - clazz.addImport("${restAssuredPackage}.response.ResponseOptions") - } else if (contracts.values().contains(TestType.HTTP) && configProperties.testMode == TestMode.EXPLICIT) { - clazz.addImport("${restAssuredPackage}.specification.RequestSpecification") - clazz.addImport("${restAssuredPackage}.response.Response") - } - clazz.addImport('org.junit.Test') - } - clazz.addStaticImport('org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat') if (configProperties.ruleClassForTests) { - clazz.addImport('org.junit.Rule').addRule(configProperties.ruleClassForTests) + addRule(configProperties, clazz) } if (contracts.values().contains(TestType.MESSAGING)) { addMessagingRelatedEntries(clazz) @@ -116,8 +99,31 @@ class JavaTestGenerator implements SingleTestGenerator { if (toIgnore) { clazz.addImport(configProperties.targetFramework.getIgnoreClass()) } + } - return clazz.build() + private void addRule(ContractVerifierConfigProperties configProperties, ClassBuilder clazz) { + clazz.addImport(getRuleImport()) + if (configProperties.targetFramework.annotationLevelRules()) { + clazz.addClassLevelAnnotation(configProperties.targetFramework + .getRuleAnnotation(configProperties.ruleClassForTests)) + } else { + clazz.addRule(configProperties.ruleClassForTests) + } + } + + private void addHttpRelatedEntries(ClassBuilder clazz, ContractVerifierConfigProperties configProperties) { + HttpImportProvider httpImportProvider = new HttpImportProvider(getRestAssuredPackage()) + clazz.addImports(httpImportProvider.getImports(configProperties.targetFramework, configProperties.testMode)) + clazz.addStaticImports(httpImportProvider.getStaticImports(configProperties.targetFramework, configProperties.testMode)) + } + + private String getRestAssuredPackage() { + boolean restAssured2Present = this.checker.isClassPresent(REST_ASSURED_2_0_CLASS) + String restAssuredPackage = restAssured2Present ? 'com.jayway.restassured' : 'io.restassured' + if (log.isDebugEnabled()) { + log.debug("Rest Assured version 2.x found [${restAssured2Present}]") + } + return restAssuredPackage } @Override @@ -159,8 +165,8 @@ class JavaTestGenerator implements SingleTestGenerator { } private void addJsonPathRelatedImports(ClassBuilder clazz) { - clazz.addImport(['com.jayway.jsonpath.DocumentContext', - 'com.jayway.jsonpath.JsonPath', + clazz.addImports(['com.jayway.jsonpath.DocumentContext', + 'com.jayway.jsonpath.JsonPath', ]) if (this.checker.isClassPresent(JSON_ASSERT_CLASS)) { clazz.addStaticImport(JSON_ASSERT_STATIC_IMPORT) @@ -169,16 +175,11 @@ class JavaTestGenerator implements SingleTestGenerator { private void addMessagingRelatedEntries(ClassBuilder clazz) { clazz.addField(['@Inject ContractVerifierMessaging contractVerifierMessaging', - '@Inject ContractVerifierObjectMapper contractVerifierObjectMapper' + '@Inject ContractVerifierObjectMapper contractVerifierObjectMapper' ]) - clazz.addImport([ 'javax.inject.Inject', - 'org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper', - 'org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage', - 'org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging', - ]) - clazz.addStaticImport('org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers') + clazz.addImports(MessagingImportProvider.getImports()) + clazz.addStaticImports(MessagingImportProvider.getStaticImports()) } - } class ClassPresenceChecker { @@ -189,7 +190,7 @@ class ClassPresenceChecker { try { Class.forName(className) return true - } catch (ClassNotFoundException e) { + } catch (ClassNotFoundException ignored) { if (log.isDebugEnabled()) { log.debug("[${className}] is not present on classpath. Will not add a static import.") } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy index 439fda17e0..aa02e58844 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy @@ -69,7 +69,7 @@ abstract class MethodBodyBuilder { private static final Closure GET_SERVER_VALUE = { it instanceof DslProperty ? it.serverValue : it } private static final String FROM_REQUEST_PREFIX = "request." - private static final String FROM_REQUEST_BODY = "body" + private static final String FROM_REQUEST_BODY = "escapejsonbody" private static final String FROM_REQUEST_PATH = "path" protected final ContractVerifierConfigProperties configProperties @@ -367,6 +367,14 @@ abstract class MethodBodyBuilder { private void addJsonResponseBodyCheck(BlockBuilder bb, convertedResponseBody, BodyMatchers bodyMatchers) { appendJsonPath(bb, getResponseAsString()) + DocumentContext parsedRequestBody + if (contract.request?.body) { + def testSideRequestBody = MapConverter.getTestSideValues(contract.request.body) + parsedRequestBody = JsonPath.parse(testSideRequestBody) + if (convertedResponseBody instanceof String && !textContainsJsonPathTemplate(convertedResponseBody)) { + convertedResponseBody = templateProcessor.transform(contract.request, convertedResponseBody.toString()) + } + } Object copiedBody = cloneBody(convertedResponseBody) convertedResponseBody = JsonToJsonPathsConverter.removeMatchingJsonPaths(convertedResponseBody, bodyMatchers) // remove quotes from fromRequest objects before picking json paths @@ -374,11 +382,6 @@ abstract class MethodBodyBuilder { TestSideRequestTemplateModel.from(contract.request) : null convertedResponseBody = MapConverter.transformValues(convertedResponseBody, returnReferencedEntries(templateModel)) JsonPaths jsonPaths = new JsonToJsonPathsConverter(configProperties).transformToJsonPathWithTestsSideValues(convertedResponseBody) - DocumentContext parsedRequestBody - if (contract.request?.body) { - def requestBody = MapConverter.getTestSideValues(contract.request.body) - parsedRequestBody = JsonPath.parse(requestBody) - } jsonPaths.each { String method = it.method() method = processIfTemplateIsPresent(method, parsedRequestBody) @@ -422,11 +425,15 @@ abstract class MethodBodyBuilder { String entryAsString = (String) entry if (templateProcessor.containsTemplateEntry(entryAsString) && !templateProcessor.containsJsonPathTemplateEntry(entryAsString)) { - String justEntry = entryAsString - contractTemplate.openingTemplate() - + // TODO: HANDLEBARS LEAKING VIA request. + String justEntry = entryAsString - contractTemplate.escapedOpeningTemplate() - + contractTemplate.openingTemplate() - + contractTemplate.escapedClosingTemplate() - contractTemplate.closingTemplate() - FROM_REQUEST_PREFIX if (justEntry == FROM_REQUEST_BODY) { // the body should be transformed by standard mechanism - return entry + return contractTemplate.escapedOpeningTemplate() + FROM_REQUEST_PREFIX + + "escapedBody" + contractTemplate.escapedClosingTemplate() } try { Object result = new PropertyUtilsBean().getProperty(templateModel, justEntry) @@ -444,13 +451,14 @@ abstract class MethodBodyBuilder { } protected String processIfTemplateIsPresent(String method, DocumentContext parsedRequestBody) { - if (templateProcessor.containsTemplateEntry(method) && - templateProcessor.containsJsonPathTemplateEntry(method) && contract.request?.body) { + if (textContainsJsonPathTemplate(method) && contract.request?.body) { // Unquoting the values of non strings String jsonPathEntry = templateProcessor.jsonPathFromTemplateEntry(method) Object object = parsedRequestBody.read(jsonPathEntry) if (!(object instanceof String)) { return method + .replace('"' + contractTemplate.escapedOpeningTemplate(), contractTemplate.escapedOpeningTemplate()) + .replace(contractTemplate.escapedClosingTemplate() + '"', contractTemplate.escapedClosingTemplate()) .replace('"' + contractTemplate.openingTemplate(), contractTemplate.openingTemplate()) .replace(contractTemplate.closingTemplate() + '"', contractTemplate.closingTemplate()) } @@ -458,6 +466,11 @@ abstract class MethodBodyBuilder { return method } + protected boolean textContainsJsonPathTemplate(String method) { + return templateProcessor.containsTemplateEntry(method) && + templateProcessor.containsJsonPathTemplateEntry(method) + } + protected void methodForEqualityCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object copiedBody) { String path = quotedAndEscaped(bodyMatcher.path()) Object retrievedValue = value(copiedBody, bodyMatcher) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy index 4984a2e0a0..81641b06ec 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy @@ -19,13 +19,17 @@ package org.springframework.cloud.contract.verifier.builder import groovy.transform.CompileStatic import groovy.transform.PackageScope import groovy.util.logging.Slf4j + import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties -import org.springframework.cloud.contract.verifier.config.TestFramework import org.springframework.cloud.contract.verifier.config.TestMode import org.springframework.cloud.contract.verifier.file.ContractMetadata import org.springframework.cloud.contract.verifier.util.NamesUtil +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5 +import static org.springframework.cloud.contract.verifier.config.TestFramework.SPOCK + /** * Builds a test method. Adds an ignore annotation on a method if necessary. * @@ -57,7 +61,7 @@ class MethodBuilder { if (log.isDebugEnabled()) { log.debug("Stub content Groovy DSL [$stubContent]") } - String methodName = methodName(contract, stubsFile, stubContent) + String methodName = MethodBuilder.methodName(contract, stubsFile, stubContent) return new MethodBuilder(methodName, stubContent, configProperties, contract.ignored || stubContent.ignored) } @@ -91,11 +95,11 @@ class MethodBuilder { * Appends to the {@link BlockBuilder} the contents of the test */ void appendTo(BlockBuilder blockBuilder) { - if (configProperties.targetFramework == TestFramework.JUNIT) { + if (isJUnitType()) { blockBuilder.addLine('@Test') } if (ignored) { - blockBuilder.addLine('@Ignore') + blockBuilder.addLine(configProperties.targetFramework.ignoreAnnotation) } blockBuilder.addLine(configProperties.targetFramework.methodModifier + "validate_$methodName() throws Exception {") getMethodBodyBuilder().appendTo(blockBuilder) @@ -104,26 +108,29 @@ class MethodBuilder { private MethodBodyBuilder getMethodBodyBuilder() { if (stubContent.input || stubContent.outputMessage) { - if (configProperties.targetFramework == TestFramework.JUNIT) { + if (isJUnitType()) { return new JUnitMessagingMethodBodyBuilder(stubContent, configProperties) } return new SpockMessagingMethodBodyBuilder(stubContent, configProperties) } if (configProperties.testMode == TestMode.JAXRSCLIENT) { - if (configProperties.targetFramework == TestFramework.JUNIT) { + if (isJUnitType()) { return new JaxRsClientJUnitMethodBodyBuilder(stubContent, configProperties) } return new JaxRsClientSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) } else if (configProperties.testMode == TestMode.EXPLICIT) { - if (configProperties.targetFramework == TestFramework.JUNIT) { + if (isJUnitType()) { return new ExplicitJUnitMethodBodyBuilder(stubContent, configProperties) } // in Groovy we're using def so we don't have to update the imports return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) - } else if (configProperties.targetFramework == TestFramework.SPOCK) { + } else if (configProperties.targetFramework == SPOCK) { return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) } return new MockMvcJUnitMethodBodyBuilder(stubContent, configProperties) } + private boolean isJUnitType() { + return JUNIT == configProperties.targetFramework || JUNIT5 == configProperties.targetFramework + } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy index fdfabf0c72..249f260867 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy @@ -72,7 +72,7 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { @Override protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return """assertThat(responseBody).${createHeaderComparison(value)}""" + return """assertThat(responseBody).${createBodyComparison(value)}""" } @Override diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy index 0e4f0022e6..3cef2d4487 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy @@ -27,6 +27,7 @@ import org.springframework.cloud.contract.spec.internal.Request import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import org.springframework.cloud.contract.verifier.util.ContentUtils +import org.springframework.cloud.contract.verifier.util.RegexpBuilders import java.util.regex.Pattern @@ -60,7 +61,7 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing @Override protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return "responseBody$property ${patternComparison(value)}" + return "responseBody$property ${createBodyComparison(value)}" } @Override @@ -173,6 +174,11 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing return "== '$cookieValue'" } + protected String createBodyComparison(Pattern bodyValue) { + String patternAsString = bodyValue.pattern() + return patternComparison(RegexpBuilders.buildGStringRegexpForTestSide(patternAsString)) + ";" + } + protected String convertCookieComparison(Pattern cookieValue) { return patternComparison(cookieValue) } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TestSideRequestTemplateModel.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TestSideRequestTemplateModel.groovy index fcc7b5c37a..e66bff6ecd 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TestSideRequestTemplateModel.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TestSideRequestTemplateModel.groovy @@ -17,7 +17,6 @@ import org.springframework.cloud.contract.verifier.util.MapConverter * @author Marcin Grzejszczak * @since 1.1.0 */ -@Immutable @CompileStatic class TestSideRequestTemplateModel { /** @@ -41,14 +40,23 @@ class TestSideRequestTemplateModel { final Map> headers /** - * Escaped request body that can be put into test + * Request body as it would be sent to the controller */ final String body /** - * Request body as it would be sent to the controller + * Escaped request body that can be put into test */ - final String rawBody + final String escapedBody + + private TestSideRequestTemplateModel(String url, Map> query, Path path, Map> headers, String body, String escapedBody) { + this.url = url + this.query = query + this.path = path + this.headers = headers + this.body = body + this.escapedBody = escapedBody + } static TestSideRequestTemplateModel from(final Request request) { String url = MapConverter.getTestSideValues(request.url ?: request.urlPath) @@ -63,11 +71,15 @@ class TestSideRequestTemplateModel { Map> headers = (Map>) (request.headers?.entries?.groupBy { it.name }?.collectEntries { - [(it.key): it.value.collect { MapConverter.getTestSideValues(it) }] + List headerValues = [] + for (Object value : it.value) { + headerValues.add(MapConverter.getTestSideValues(value)) + } + [(it.key): headerValues] }) - String body = trimmedAndEscapedBody(request.body) - String rawBody = getBodyAsRawJson(request.body) - return new TestSideRequestTemplateModel(fullUrl, query, paths, headers, body, rawBody) + String escapedBody = trimmedAndEscapedBody(request.body) + String body = getBodyAsRawJson(request.body) + return new TestSideRequestTemplateModel(fullUrl, query, paths, headers, body, escapedBody) } private static List buildPathsFromUrl(String url) { @@ -86,12 +98,15 @@ class TestSideRequestTemplateModel { private static String getBodyAsRawJson(Object body) { Object bodyValue = extractServerValueFromBody(body) + if (bodyValue instanceof GString || bodyValue instanceof String) { + return bodyValue.toString() + } return bodyValue != null ? new JsonOutput().toJson(bodyValue) : bodyValue } protected static Object extractServerValueFromBody(bodyValue) { if (bodyValue instanceof GString) { - bodyValue = ContentUtils.extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) + bodyValue = ContentUtils.extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue } as Closure) } else { bodyValue = MapConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsEscapeHelper.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsEscapeHelper.groovy index 72c6007c23..b2bec99730 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsEscapeHelper.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsEscapeHelper.groovy @@ -34,7 +34,7 @@ class HandlebarsEscapeHelper implements Helper> { } private Object returnObjectForTest(Object model) { - return ((TestSideRequestTemplateModel) model).rawBody + return ((TestSideRequestTemplateModel) model).escapedBody } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy index ff29a72e22..351a5c73e3 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy @@ -1,5 +1,7 @@ package org.springframework.cloud.contract.verifier.builder.handlebars +import com.github.tomakehurst.wiremock.extension.responsetemplating.helpers.WireMockHelpers +import org.apache.commons.text.StringEscapeUtils import wiremock.com.github.jknack.handlebars.Helper import wiremock.com.github.jknack.handlebars.Options import com.github.tomakehurst.wiremock.extension.responsetemplating.RequestTemplateModel @@ -15,21 +17,41 @@ import org.springframework.cloud.contract.verifier.builder.TestSideRequestTempla * @since 1.1.0 */ @CompileStatic -class HandlebarsJsonPathHelper implements Helper> { +class HandlebarsJsonPathHelper implements Helper { public static final String NAME = "jsonpath" public static final String REQUEST_MODEL_NAME = "request" @Override - Object apply(Map context, Options options) throws IOException { - String jsonPath = options.param(0) - Object model = context.get(REQUEST_MODEL_NAME) - if (model instanceof TestSideRequestTemplateModel) { - return returnObjectForTest(model, jsonPath) - } else if (model instanceof RequestTemplateModel) { - return returnObjectForStub(model, jsonPath) + Object apply(Object context, Options options) throws IOException { + if (context instanceof Map) { + // legacy + Map oldContext = (Map) context + String jsonPath = options.param(0) + Object model = oldContext.get(REQUEST_MODEL_NAME) + if (model instanceof TestSideRequestTemplateModel) { + return returnObjectForTest(model, jsonPath) + } else if (model instanceof RequestTemplateModel) { + return returnObjectForStub(model, jsonPath) + } + throw new IllegalArgumentException("Unsupported model") + } else if (context instanceof String) { + Object value = WireMockHelpers.jsonPath.apply(context, options) + if (testSideModel(options)) { + return processTestResponseValue(value) + } + return value } - throw new IllegalArgumentException("Unsupported model") + throw new IllegalArgumentException("Unsupported context") + } + + private boolean testSideModel(Options options) { + Object model = options.context.model() + if (!(model instanceof Map)) { + return false + } + Map map = (Map) model + return map.values().any { it instanceof TestSideRequestTemplateModel } } private Object returnObjectForStub(Object model, String jsonPath) { @@ -37,10 +59,18 @@ class HandlebarsJsonPathHelper implements Helper> { return documentContext.read(jsonPath) } - private Object returnObjectForTest(Object model, String jsonPath) { - String body = removeSurroundingQuotes(((TestSideRequestTemplateModel) model).rawBody).replace('\\"', '"') + private Object returnObjectForTest(TestSideRequestTemplateModel model, String jsonPath) { + String body = removeSurroundingQuotes(model.escapedBody).replace('\\"', '"') DocumentContext documentContext = JsonPath.parse(body) - return documentContext.read(jsonPath) + Object value = documentContext.read(jsonPath) + return processTestResponseValue(value) + } + + private Object processTestResponseValue(Object value) { + if (value instanceof Long) { + return String.valueOf(value) + "L" + } + return value } private String removeSurroundingQuotes(String body) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/BaseImportProvider.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/BaseImportProvider.groovy new file mode 100644 index 0000000000..8bb12a0e49 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/BaseImportProvider.groovy @@ -0,0 +1,62 @@ +package org.springframework.cloud.contract.verifier.builder.imports + +import groovy.transform.CompileStatic + +import org.springframework.cloud.contract.verifier.config.TestFramework + +import static org.springframework.cloud.contract.verifier.config.TestFramework.CUSTOM +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5 +import static org.springframework.cloud.contract.verifier.config.TestFramework.SPOCK + +/** + * Provides imports based on test framework. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +class BaseImportProvider { + + private static final ImportDefinitions GENERAL_IMPORTS = new ImportDefinitions([], [ + 'org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat' + ]) + + private static + final Map TEST_FRAMEWORK_SPECIFIC_IMPORTS = [ + (JUNIT) : new ImportDefinitions(['org.junit.Test']), + (JUNIT5): new ImportDefinitions(['org.junit.jupiter.api.Test']), + (SPOCK) : new ImportDefinitions([]), + (CUSTOM): new ImportDefinitions([])] + + private static final Map RULE_IMPORT = [ + (JUNIT) : 'org.junit.Rule', + (JUNIT5): 'org.junit.jupiter.api.extension.ExtendWith', + (SPOCK) : 'org.junit.Rule', + (CUSTOM): 'org.junit.Rule' + ] + + /** + * Returns list of imports for provided test framework. + * @param testFramework + * @return list of imports + */ + static List getImports(TestFramework testFramework) { + return GENERAL_IMPORTS.imports + TEST_FRAMEWORK_SPECIFIC_IMPORTS.get(testFramework).imports + } + + /** + * Returns list of static imports for provided test framework. + * @param testFramework + * @return + */ + static List getStaticImports(TestFramework testFramework) { + return GENERAL_IMPORTS.staticImports + TEST_FRAMEWORK_SPECIFIC_IMPORTS.get(testFramework).staticImports + } + + static String getRuleImport(TestFramework testFramework) { + return RULE_IMPORT.get(testFramework) + } +} + diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/HttpImportProvider.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/HttpImportProvider.groovy new file mode 100644 index 0000000000..879f204303 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/HttpImportProvider.groovy @@ -0,0 +1,76 @@ +package org.springframework.cloud.contract.verifier.builder.imports + +import org.springframework.cloud.contract.verifier.config.TestFramework +import org.springframework.cloud.contract.verifier.config.TestMode + +import static org.springframework.cloud.contract.verifier.config.TestFramework.CUSTOM +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5 +import static org.springframework.cloud.contract.verifier.config.TestFramework.SPOCK +import static org.springframework.cloud.contract.verifier.config.TestMode.EXPLICIT +import static org.springframework.cloud.contract.verifier.config.TestMode.JAXRSCLIENT +import static org.springframework.cloud.contract.verifier.config.TestMode.MOCKMVC + +/** + * Provides imports based on test framework and test mode. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +class HttpImportProvider { + + private final Map TEST_MODE_SPECIFIC_IMPORTS = [ + (JAXRSCLIENT): new ImportDefinitions([], ['javax.ws.rs.client.Entity.*']), + (MOCKMVC) : new ImportDefinitions([], ["${restAssuredPackage}.module.mockmvc.RestAssuredMockMvc.*"]), + (EXPLICIT) : new ImportDefinitions([], ["${restAssuredPackage}.RestAssured.*"])] + + private final Map, ImportDefinitions> FRAMEWORK_AND_TEST_MODE_SPECIFIC_IMPORTS = [ + (new Tuple2(JUNIT, JAXRSCLIENT)) : new ImportDefinitions(['javax.ws.rs.core.Response']), + (new Tuple2(JUNIT5, JAXRSCLIENT)): new ImportDefinitions(['javax.ws.rs.core.Response']), + (new Tuple2(JUNIT, MOCKMVC)) : new ImportDefinitions([ + "${restAssuredPackage}.module.mockmvc.specification.MockMvcRequestSpecification", + "${restAssuredPackage}.response.ResponseOptions"]), + (new Tuple2(JUNIT5, MOCKMVC)) : new ImportDefinitions([ + "${restAssuredPackage}.module.mockmvc.specification.MockMvcRequestSpecification", + "${restAssuredPackage}.response.ResponseOptions"]), + (new Tuple2(JUNIT, EXPLICIT)) : new ImportDefinitions(["${restAssuredPackage}.specification.RequestSpecification", + "${restAssuredPackage}.response.Response"]), + (new Tuple2(JUNIT5, EXPLICIT)) : new ImportDefinitions(["${restAssuredPackage}.specification.RequestSpecification", + "${restAssuredPackage}.response.Response"]), + (new Tuple2(SPOCK, JAXRSCLIENT)) : new ImportDefinitions([]), + (new Tuple2(CUSTOM, JAXRSCLIENT)): new ImportDefinitions([]), + (new Tuple2(SPOCK, MOCKMVC)) : new ImportDefinitions([]), + (new Tuple2(CUSTOM, MOCKMVC)) : new ImportDefinitions([]), + (new Tuple2(SPOCK, EXPLICIT)) : new ImportDefinitions([]), + (new Tuple2(CUSTOM, EXPLICIT)) : new ImportDefinitions([]) + ] + + private final String restAssuredPackage + + HttpImportProvider(String restAssuredPackage) { + this.restAssuredPackage = restAssuredPackage + } + + /** + * Returns list of imports for http test contracts for provided test framework and test mode. + * @param testFramework + * @param testMode + * @return list of imports + */ + List getImports(TestFramework testFramework, TestMode testMode) { + return TEST_MODE_SPECIFIC_IMPORTS.get(testMode).imports + + FRAMEWORK_AND_TEST_MODE_SPECIFIC_IMPORTS.get(new Tuple2(testFramework, testMode)).imports + } + + /** + * Returns list of static imports for http test contracts for provided test framework and test mode. + * @param testFramework + * @param testMode + * @return list of static imports + */ + List getStaticImports(TestFramework testFramework, TestMode testMode) { + return TEST_MODE_SPECIFIC_IMPORTS.get(testMode).staticImports + + FRAMEWORK_AND_TEST_MODE_SPECIFIC_IMPORTS.get(new Tuple2(testFramework, testMode)).staticImports + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/ImportDefinitions.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/ImportDefinitions.groovy new file mode 100644 index 0000000000..7fb4fe1f8f --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/ImportDefinitions.groovy @@ -0,0 +1,22 @@ +package org.springframework.cloud.contract.verifier.builder.imports + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope + +/** + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +@PackageScope +class ImportDefinitions { + + final List imports + final List staticImports + + ImportDefinitions(List imports, List staticImports = []) { + this.imports = imports + this.staticImports = staticImports + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/MessagingImportProvider.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/MessagingImportProvider.groovy new file mode 100644 index 0000000000..49897b3f0a --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/imports/MessagingImportProvider.groovy @@ -0,0 +1,32 @@ +package org.springframework.cloud.contract.verifier.builder.imports + +import groovy.transform.CompileStatic + +/** + * Provides imports based on test framework and test mode. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +class MessagingImportProvider { + + /** + * @return list of imports for messaging test contracts. + */ + static List getImports() { + return ['javax.inject.Inject', + 'org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper', + 'org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage', + 'org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging', + ] + } + + /** + * @return list of static imports for messaging test contracts. + */ + static List getStaticImports() { + return ['org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers'] + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy index 7093690e8f..60985f17ac 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy @@ -60,6 +60,11 @@ class ContractVerifierConfigProperties { */ List excludedFiles = [] + /** + * Patterns that should be taken into account for processing + */ + List includedFiles = [] + /** * Patterns for which generated tests should be @Ignored */ diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/TestFramework.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/TestFramework.groovy index c223bcb6db..c68035fc5a 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/TestFramework.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/TestFramework.groovy @@ -16,68 +16,105 @@ package org.springframework.cloud.contract.verifier.config +import org.springframework.cloud.contract.verifier.config.framework.CustomDefinition +import org.springframework.cloud.contract.verifier.config.framework.JUnit5Definition +import org.springframework.cloud.contract.verifier.config.framework.JUnitDefinition +import org.springframework.cloud.contract.verifier.config.framework.SpockDefinition +import org.springframework.cloud.contract.verifier.config.framework.TestFrameworkDefinition + /** * Contains main building blocks for a test class for the given framework * * @author Jakub Kubrynski, codearte.io + * @author Olga Maciaszek-Sharma * * @since 1.0.0 */ enum TestFramework { - JUNIT("public ", "public void ", ";", ".java", "Test", "org.junit.Ignore", ["org.junit.FixMethodOrder", "org.junit.runners.MethodSorters"], "@FixMethodOrder(MethodSorters.NAME_ASCENDING)"), - SPOCK("", "def ", "", ".groovy", "Spec", "spock.lang.Ignore", ["spock.lang.Stepwise"], "@Stepwise"), - CUSTOM("", "", "", "", "", "", [], "") - private final String classModifier - private final String methodModifier - private final String lineSuffix - private final String classExtension - private final String classNameSuffix - private final String ignoreClass - private final List orderAnnotationImports - private final String orderAnnotation + SPOCK(new SpockDefinition()), + JUNIT(new JUnitDefinition()), + JUNIT5(new JUnit5Definition()), + CUSTOM(new CustomDefinition()) + @Delegate + private final TestFrameworkDefinition testFrameworkDefinition + + + TestFramework(TestFrameworkDefinition testFrameworkDefinition) { + this.testFrameworkDefinition = testFrameworkDefinition + } + + /** + * @deprecated use {@link #TestFramework(TestFrameworkDefinition)} + * @param classModifier + * @param methodModifier + * @param lineSuffix + * @param classExtension + * @param classNameSuffix + * @param ignoreClass + * @param orderAnnotationImports + * @param orderAnnotation + */ + @Deprecated TestFramework(String classModifier, String methodModifier, String lineSuffix, String classExtension, String classNameSuffix, String ignoreClass, List orderAnnotationImports, String orderAnnotation) { - this.classModifier = classModifier - this.lineSuffix = lineSuffix - this.methodModifier = methodModifier - this.classExtension = classExtension - this.classNameSuffix = classNameSuffix - this.ignoreClass = ignoreClass - this.orderAnnotationImports = orderAnnotationImports - this.orderAnnotation = orderAnnotation - } + testFrameworkDefinition = new TestFrameworkDefinition() { - String getClassModifier() { - return classModifier - } + @Override + String getClassModifier() { + return classModifier + } - String getMethodModifier() { - return methodModifier - } + @Override + String getMethodModifier() { + return methodModifier + } - String getLineSuffix() { - return lineSuffix - } + @Override + String getLineSuffix() { + return lineSuffix + } - String getClassExtension() { - return classExtension - } + @Override + String getClassExtension() { + return classExtension + } - String getClassNameSuffix() { - return classNameSuffix - } + @Override + String getClassNameSuffix() { + return classNameSuffix + } - String getIgnoreClass() { - return ignoreClass - } + @Override + String getIgnoreClass() { + return ignoreClass + } - List getOrderAnnotationImport() { - return orderAnnotationImports - } + @Override + List getOrderAnnotationImports() { + return orderAnnotationImports + } - String getOrderAnnotation() { - return orderAnnotation + @Override + String getOrderAnnotation() { + return orderAnnotation + } + + @Override + String getIgnoreAnnotation() { + return '@Ignore' + } + + @Override + boolean annotationLevelRules() { + return false + } + + @Override + String getRuleAnnotation(String annotationValue) { + throw new UnsupportedOperationException('Not available in framework.') + } + } } } \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/CustomDefinition.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/CustomDefinition.groovy new file mode 100644 index 0000000000..b22ba212ce --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/CustomDefinition.groovy @@ -0,0 +1,69 @@ +package org.springframework.cloud.contract.verifier.config.framework + +import groovy.transform.CompileStatic + +/** + * Defines elements characteristic of Custom test framework to be used during test class construction. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +class CustomDefinition implements TestFrameworkDefinition { + + @Override + String getClassModifier() { + return '' + } + + @Override + String getMethodModifier() { + return '' + } + + @Override + String getLineSuffix() { + return '' + } + + @Override + String getClassExtension() { + return '' + } + + @Override + String getClassNameSuffix() { + return '' + } + + @Override + String getIgnoreClass() { + return '' + } + + @Override + List getOrderAnnotationImports() { + return [] + } + + @Override + String getOrderAnnotation() { + return '' + } + + @Override + String getIgnoreAnnotation() { + return '@Ignore' + } + + @Override + boolean annotationLevelRules() { + return false + } + + @Override + String getRuleAnnotation(String annotationValue) { + throw new UnsupportedOperationException('Not available in framework.') + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/JUnit5Definition.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/JUnit5Definition.groovy new file mode 100644 index 0000000000..79f6d5eec9 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/JUnit5Definition.groovy @@ -0,0 +1,69 @@ +package org.springframework.cloud.contract.verifier.config.framework + +import groovy.transform.CompileStatic + +/** + * Defines elements characteristic of JUnit5 test framework to be used during test class construction. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +class JUnit5Definition implements TestFrameworkDefinition { + + @Override + String getClassModifier() { + return 'public ' + } + + @Override + String getMethodModifier() { + return 'public void ' + } + + @Override + String getLineSuffix() { + return ';' + } + + @Override + String getClassExtension() { + '.java' + } + + @Override + String getClassNameSuffix() { + return 'Test' + } + + @Override + String getIgnoreClass() { + return 'org.junit.jupiter.api.Disabled' + } + + @Override + List getOrderAnnotationImports() { + throw new UnsupportedOperationException('Not implemented yet in JUnit5 - https://github.com/junit-team/junit5/issues/48') + } + + @Override + String getOrderAnnotation() { + throw new UnsupportedOperationException('Not implemented yet in JUnit5 - https://github.com/junit-team/junit5/issues/48') + } + + @Override + String getIgnoreAnnotation() { + return '@Disabled' + } + + @Override + boolean annotationLevelRules() { + return true + } + + @Override + String getRuleAnnotation(String annotationValue) { + return ("@ExtendWith(${annotationValue}.class)") + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/JUnitDefinition.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/JUnitDefinition.groovy new file mode 100644 index 0000000000..f2b6d474fe --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/JUnitDefinition.groovy @@ -0,0 +1,69 @@ +package org.springframework.cloud.contract.verifier.config.framework + +import groovy.transform.CompileStatic + +/** + * Defines elements characteristic of JUnit test framework to be used during test class construction. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +class JUnitDefinition implements TestFrameworkDefinition { + + @Override + String getClassModifier() { + return 'public ' + } + + @Override + String getMethodModifier() { + return 'public void ' + } + + @Override + String getLineSuffix() { + return ';' + } + + @Override + String getClassExtension() { + return '.java' + } + + @Override + String getClassNameSuffix() { + return 'Test' + } + + @Override + String getIgnoreClass() { + return 'org.junit.Ignore' + } + + @Override + List getOrderAnnotationImports() { + return ['org.junit.FixMethodOrder', 'org.junit.runners.MethodSorters'] + } + + @Override + String getOrderAnnotation() { + return '@FixMethodOrder(MethodSorters.NAME_ASCENDING)' + } + + @Override + String getIgnoreAnnotation() { + return '@Ignore' + } + + @Override + boolean annotationLevelRules() { + return false + } + + @Override + String getRuleAnnotation(String annotationValue) { + throw new UnsupportedOperationException('Not available in JUnit.') + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/SpockDefinition.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/SpockDefinition.groovy new file mode 100644 index 0000000000..32384a7587 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/SpockDefinition.groovy @@ -0,0 +1,69 @@ +package org.springframework.cloud.contract.verifier.config.framework + +import groovy.transform.CompileStatic + +/** + * Defines elements characteristic of Spock test framework to be used during test class construction. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +class SpockDefinition implements TestFrameworkDefinition { + + @Override + String getClassModifier() { + return '' + } + + @Override + String getMethodModifier() { + return 'def ' + } + + @Override + String getLineSuffix() { + return '' + } + + @Override + String getClassExtension() { + return '.groovy' + } + + @Override + String getClassNameSuffix() { + return 'Spec' + } + + @Override + String getIgnoreClass() { + return 'spock.lang.Ignore' + } + + @Override + List getOrderAnnotationImports() { + return ['spock.lang.Stepwise'] + } + + @Override + String getOrderAnnotation() { + return '@Stepwise' + } + + @Override + String getIgnoreAnnotation() { + return '@Ignore' + } + + @Override + boolean annotationLevelRules() { + return false + } + + @Override + String getRuleAnnotation(String annotationValue) { + throw new UnsupportedOperationException('Not available in Spock.') + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/TestFrameworkDefinition.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/TestFrameworkDefinition.groovy new file mode 100644 index 0000000000..94788716b4 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/framework/TestFrameworkDefinition.groovy @@ -0,0 +1,75 @@ +package org.springframework.cloud.contract.verifier.config.framework + +import groovy.transform.CompileStatic + +/** + * Defines elements characteristic of a given test framework to be used during test class construction. + * + * @author Olga Maciaszek-Sharma + * + * @since 2.1.0 + */ +@CompileStatic +interface TestFrameworkDefinition { + + /** + * Returns the class access level modifier. E.g. for Java tests that would mean {@code public} + **/ + String getClassModifier() + + /** + * Returns the method access level modifier along with the return type. + * E.g. for Java tests that would mean {@code public void} + **/ + String getMethodModifier() + + /** + * Returns the characters that should end each line. E.g. for Java tests that would mean {@code ;} + **/ + String getLineSuffix() + + /** + * Returns the file extension. E.g. for Java tests that would be {@code .java} + **/ + String getClassExtension() + + /** + * Returns the test class name suffix. E.g. for JUnit tests that would be {@code Test} + **/ + String getClassNameSuffix() + + /** + * Returns the qualified name of the class used to ignore or disable tests. E.g. for JUnit 4 tests that would + * be {@code org.junit.Ignore} + **/ + String getIgnoreClass() + + /** + * Returns the qualified names of the classes that are used for arranging tests into scenarios. + * E.g. for JUnit 4 tests that would be {@code 'org.junit.FixMethodOrder'}, {@code 'org.junit.runners.MethodSorters'} + **/ + List getOrderAnnotationImports() + + /** + * Returns the annotation used for arranging tests into scenarios. + * E.g. for JUnit test that would be {@code @FixMethodOrder(MethodSorters.NAME_ASCENDING)} + **/ + String getOrderAnnotation() + + /** + * Returns the annotation used for ignoring or disabling tests. E.g. for JUnit tests that would mean {@code @Ignore} + **/ + String getIgnoreAnnotation() + + /** + * Returns a boolean indicating whether an annotation-type rule or extension is being used or not. + * E.g. for JUnit 5 tests that would return {@code true} + **/ + boolean annotationLevelRules() + + /** + * Returns the test rule or extension annotation with the {@annotationValue} passed as an argument. + * E.g. for JUnit 5 tests that could be {@code @ExtendWith(Example.class)} + **/ + String getRuleAnnotation(String annotationValue) +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy index 042ff77db4..74abe2d6ee 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy @@ -73,172 +73,295 @@ class YamlContractConverter implements ContractConverter> { ClassLoader classLoader = YamlContractConverter.getClassLoader() ObjectMapper mapper = new ObjectMapper() try { - return new Yaml().loadAll(Files.newInputStream(contractFile.toPath())).collect { - YamlContract yamlContract = mapper.convertValue(it, YamlContract.class); - Thread.currentThread().setContextClassLoader(updatedClassLoader(contractFile.getParentFile(), classLoader)) - return Contract.make { - if (yamlContract.description) description(yamlContract.description) - if (yamlContract.label) label(yamlContract.label) - if (yamlContract.name) name(yamlContract.name) - if (yamlContract.priority) priority(yamlContract.priority) - if (yamlContract.ignored) ignored() - if (yamlContract.request?.method) { - request { - method(yamlContract.request?.method) - if (yamlContract.request?.url) { - url(yamlContract.request?.url) { - if (yamlContract.request.queryParameters) { - queryParameters { - yamlContract.request.queryParameters.each { String key, Object value -> - if (value instanceof List) { - ((List) value).each { - parameter(key, it) - } - } else { - parameter(key, value) - } - } - } - } - } - } - if (yamlContract.request?.urlPath) { - urlPath(yamlContract.request?.urlPath) { - if (yamlContract.request.queryParameters) { - queryParameters { - yamlContract.request.queryParameters.each { String key, Object value -> - if (value instanceof List) { - ((List) value).each { - parameter(key, it) - } - } else { - parameter(key, value) - } - } - } - } - } - } - if (yamlContract.request?.headers) { - headers { - yamlContract.request?.headers?.each { String key, Object value -> - KeyValueMatcher matcher = yamlContract.request.matchers.headers.find { it.key == key } - if (value instanceof List) { - ((List) value).each { - Object clientValue = clientValue(it, matcher, key) - header(key, new DslProperty(clientValue, it)) - } - } else { - Object clientValue = clientValue(value, matcher, key) - header(key, new DslProperty(clientValue, value)) - } - } - } - } - if (yamlContract.request?.cookies) { - cookies { - yamlContract.request?.cookies?.each { String key, Object value -> - KeyValueMatcher matcher = yamlContract.request.matchers.cookies.find { it.key == key } - Object clientValue = clientValue(value, matcher, key) + Iterable iterables = new Yaml().loadAll(Files.newInputStream(contractFile.toPath())) + Collection contracts = [] + for (Object o : iterables) { + Closure processYaml = processYaml(mapper, classLoader, contractFile) + contracts.add(processYaml(o)) + } + return contracts + } + catch (FileNotFoundException e) { + throw new IllegalStateException(e) + } + catch (IllegalStateException ise) { + throw ise + } + catch (Exception e1) { + throw new IllegalStateException("Exception occurred while processing the file [" + contractFile + "]", e1) + } finally { + Thread.currentThread().setContextClassLoader(classLoader) + } + } - cookie(key, new DslProperty(clientValue, value)) - } - } - } - if (yamlContract.request.body) body(yamlContract.request.body) - if (yamlContract.request.bodyFromFile) body(file(yamlContract.request.bodyFromFile)) - if (yamlContract.request.multipart) { - Map multipartMap = [:] - Map multiPartParams = yamlContract.request.multipart.params.collectEntries { String paramKey, String paramValue -> - KeyValueMatcher matcher = yamlContract.request.matchers.multipart.params.find { - it.key == paramKey - } - Object value = paramValue - if (matcher) { - value = matcher.regex ? Pattern.compile(matcher.regex) : - predefinedToPattern(matcher.predefined) - } - return [(paramKey), new DslProperty<>(value, paramValue)] - } as Map - multipartMap.putAll(multiPartParams) - yamlContract.request.multipart.named.each { Named namedParam -> - YamlContract.MultipartNamedStubMatcher matcher = yamlContract.request.matchers.multipart.named.find { - it.paramName == namedParam.paramName - } - Object fileNameValue = namedParam.fileName - Object fileContentValue = namedParam.fileContent - if (matcher && matcher.fileName) { - fileNameValue = matcher.fileName.regex ? Pattern.compile(matcher.fileName.regex) : - predefinedToPattern(matcher.fileName.predefined) - } - if (matcher && matcher.fileContent) { - fileContentValue = matcher.fileContent.regex ? Pattern.compile(matcher.fileContent.regex) : - predefinedToPattern(matcher.fileContent.predefined) - } - multipartMap.put(namedParam.paramName, new NamedProperty(new DslProperty<>(fileNameValue, namedParam.fileName), - new DslProperty<>(fileContentValue, namedParam.fileContent))) - } - multipart(multipartMap) - } - bodyMatchers { - yamlContract.request.matchers?.body?.each { BodyStubMatcher matcher -> - MatchingTypeValue value = null - switch (matcher.type) { - case StubMatcherType.by_date: - value = byDate() - break - case StubMatcherType.by_time: - value = byTime() - break - case StubMatcherType.by_timestamp: - value = byTimestamp() - break - case StubMatcherType.by_regex: - String regex = matcher.value - if (matcher.predefined) { - regex = predefinedToPattern(matcher.predefined).pattern() + protected Closure processYaml(ObjectMapper mapper, ClassLoader classLoader, File contractFile) { + return { + YamlContract yamlContract = mapper.convertValue(it, YamlContract.class) + Thread.currentThread().setContextClassLoader(updatedClassLoader(contractFile.getParentFile(), classLoader)) + return Contract.make { + if (yamlContract.description) description(yamlContract.description) + if (yamlContract.label) label(yamlContract.label) + if (yamlContract.name) name(yamlContract.name) + if (yamlContract.priority) priority(yamlContract.priority) + if (yamlContract.ignored) ignored() + if (yamlContract.request?.method) { + request { + method(yamlContract.request?.method) + if (yamlContract.request?.url) { + url(yamlContract.request?.url) { + if (yamlContract.request.queryParameters) { + queryParameters { + yamlContract.request.queryParameters.each { String key, Object value -> + if (value instanceof List) { + ((List) value).each { + parameter(key, it) + } + } else { + parameter(key, value) } - value = byRegex(regex) - break - case StubMatcherType.by_equality: - value = byEquality() - break + } } - jsonPath(matcher.path, value) } } } - response { - status(yamlContract.response.status) + if (yamlContract.request?.urlPath) { + urlPath(yamlContract.request?.urlPath) { + if (yamlContract.request.queryParameters) { + queryParameters { + yamlContract.request.queryParameters.each { String key, Object value -> + if (value instanceof List) { + ((List) value).each { + parameter(key, it) + } + } else { + parameter(key, value) + } + } + } + } + } + } + if (yamlContract.request?.headers) { headers { - yamlContract.response?.headers?.each { String key, Object value -> - TestHeaderMatcher matcher = yamlContract.response.matchers.headers.find { it.key == key } + yamlContract.request?.headers?.each { String key, Object value -> + KeyValueMatcher matcher = yamlContract.request.matchers.headers.find { it.key == key } if (value instanceof List) { ((List) value).each { - Object serverValue = serverValue(it, matcher, key) - header(key, new DslProperty(it, serverValue)) + Object clientValue = clientValue(it, matcher, key) + header(key, new DslProperty(clientValue, it)) } } else { - Object serverValue = serverValue(value, matcher, key) - header(key, new DslProperty(value, serverValue)) + Object clientValue = clientValue(value, matcher, key) + header(key, new DslProperty(clientValue, value)) } } } - if (yamlContract.response?.cookies) { - cookies { - yamlContract.response?.cookies?.each { String key, Object value -> - TestCookieMatcher matcher = yamlContract.response.matchers.cookies.find { it.key == key } - Object serverValue = serverCookieValue(value, matcher, key) + } + if (yamlContract.request?.cookies) { + cookies { + yamlContract.request?.cookies?.each { String key, Object value -> + KeyValueMatcher matcher = yamlContract.request.matchers.cookies.find { it.key == key } + Object clientValue = clientValue(value, matcher, key) - cookie(key, new DslProperty(value, serverValue)) - } + cookie(key, new DslProperty(clientValue, value)) } } - if (yamlContract.response.body) body(yamlContract.response.body) - if (yamlContract.response.bodyFromFile) body(file(yamlContract.response.bodyFromFile)) - if (yamlContract.response.async) async() + } + if (yamlContract.request.body) body(yamlContract.request.body) + if (yamlContract.request.bodyFromFile) body(file(yamlContract.request.bodyFromFile)) + if (yamlContract.request.multipart) { + Map multipartMap = [:] + Map multiPartParams = yamlContract.request.multipart.params.collectEntries { String paramKey, String paramValue -> + KeyValueMatcher matcher = yamlContract.request.matchers.multipart.params.find { + it.key == paramKey + } + Object value = paramValue + if (matcher) { + value = matcher.regex ? Pattern.compile(matcher.regex) : + predefinedToPattern(matcher.predefined) + } + return [(paramKey), new DslProperty<>(value, paramValue)] + } as Map + multipartMap.putAll(multiPartParams) + yamlContract.request.multipart.named.each { Named namedParam -> + YamlContract.MultipartNamedStubMatcher matcher = yamlContract.request.matchers.multipart.named.find { + it.paramName == namedParam.paramName + } + Object fileNameValue = namedParam.fileName + Object fileContentValue = namedParam.fileContent + if (matcher && matcher.fileName) { + fileNameValue = matcher.fileName.regex ? Pattern.compile(matcher.fileName.regex) : + predefinedToPattern(matcher.fileName.predefined) + } + if (matcher && matcher.fileContent) { + fileContentValue = matcher.fileContent.regex ? Pattern.compile(matcher.fileContent.regex) : + predefinedToPattern(matcher.fileContent.predefined) + } + multipartMap.put(namedParam.paramName, new NamedProperty(new DslProperty<>(fileNameValue, namedParam.fileName), + new DslProperty<>(fileContentValue, namedParam.fileContent))) + } + multipart(multipartMap) + } + bodyMatchers { + yamlContract.request.matchers?.body?.each { BodyStubMatcher matcher -> + MatchingTypeValue value = null + switch (matcher.type) { + case StubMatcherType.by_date: + value = byDate() + break + case StubMatcherType.by_time: + value = byTime() + break + case StubMatcherType.by_timestamp: + value = byTimestamp() + break + case StubMatcherType.by_regex: + String regex = matcher.value + if (matcher.predefined) { + regex = predefinedToPattern(matcher.predefined).pattern() + } + value = byRegex(regex) + break + case StubMatcherType.by_equality: + value = byEquality() + break + } + jsonPath(matcher.path, value) + } + } + } + response { + status(yamlContract.response.status) + headers { + yamlContract.response?.headers?.each { String key, Object value -> + TestHeaderMatcher matcher = yamlContract.response.matchers.headers.find { it.key == key } + if (value instanceof List) { + ((List) value).each { + Object serverValue = serverValue(it, matcher, key) + header(key, new DslProperty(it, serverValue)) + } + } else { + Object serverValue = serverValue(value, matcher, key) + header(key, new DslProperty(value, serverValue)) + } + } + } + if (yamlContract.response?.cookies) { + cookies { + yamlContract.response?.cookies?.each { String key, Object value -> + TestCookieMatcher matcher = yamlContract.response.matchers.cookies.find { it.key == key } + Object serverValue = serverCookieValue(value, matcher, key) + + cookie(key, new DslProperty(value, serverValue)) + } + } + } + if (yamlContract.response.body) body(yamlContract.response.body) + if (yamlContract.response.bodyFromFile) body(file(yamlContract.response.bodyFromFile)) + if (yamlContract.response.async) async() + bodyMatchers { + yamlContract.response?.matchers?.body?.each { BodyTestMatcher testMatcher -> + MatchingTypeValue value = null + switch (testMatcher.type) { + case TestMatcherType.by_date: + value = byDate() + break + case TestMatcherType.by_time: + value = byTime() + break + case TestMatcherType.by_timestamp: + value = byTimestamp() + break + case TestMatcherType.by_regex: + String regex = testMatcher.value + if (testMatcher.predefined) { + regex = predefinedToPattern(testMatcher.predefined).pattern() + } + value = byRegex(regex) + break + case TestMatcherType.by_equality: + value = byEquality() + break + case TestMatcherType.by_type: + value = byType() { + if (testMatcher.minOccurrence != null) minOccurrence(testMatcher.minOccurrence) + if (testMatcher.maxOccurrence != null) maxOccurrence(testMatcher.maxOccurrence) + } + break + case TestMatcherType.by_command: + value = byCommand(testMatcher.value) + break + case TestMatcherType.by_null: + value = byNull() + break + } + jsonPath(testMatcher.path, value) + } + } + } + } + if (yamlContract.input) { + input { + if (yamlContract.input.messageFrom) messageFrom(yamlContract.input.messageFrom) + if (yamlContract.input.assertThat) assertThat(yamlContract.input.assertThat) + if (yamlContract.input.triggeredBy) triggeredBy(yamlContract.input.triggeredBy) + messageHeaders { + yamlContract.input?.messageHeaders?.each { String key, Object value -> + KeyValueMatcher matcher = yamlContract.input.matchers?.headers?.find { it.key == key } + Object clientValue = clientValue(value, matcher, key) + header(key, new DslProperty(clientValue, value)) + } + } + if (yamlContract.input.messageBody) messageBody(yamlContract.input.messageBody) + if (yamlContract.input.messageBodyFromFile) messageBody(file(yamlContract.input.messageBodyFromFile)) + bodyMatchers { + yamlContract.input.matchers.body?.each { BodyStubMatcher matcher -> + MatchingTypeValue value = null + switch (matcher.type) { + case StubMatcherType.by_date: + value = byDate() + break + case StubMatcherType.by_time: + value = byTime() + break + case StubMatcherType.by_timestamp: + value = byTimestamp() + break + case StubMatcherType.by_regex: + String regex = matcher.value + if (matcher.predefined) { + regex = predefinedToPattern(matcher.predefined).pattern() + } + value = byRegex(regex) + break + case StubMatcherType.by_equality: + value = byEquality() + break + default: + throw new UnsupportedOperationException("The type [" + matcher.type + "] is unsupported. Hint: If you're using remember to pass ") + } + jsonPath(matcher.path, value) + } + } + } + } + OutputMessage outputMsg = yamlContract.outputMessage + if (outputMsg) { + outputMessage { + if (outputMsg.assertThat) assertThat(outputMsg.assertThat) + if (outputMsg.sentTo) sentTo(outputMsg.sentTo) + headers { + outputMsg.headers?.each { String key, Object value -> + TestHeaderMatcher matcher = outputMsg.matchers?.headers?.find { it.key == key } + Object serverValue = serverValue(value, matcher, key) + header(key, new DslProperty(value, serverValue)) + } + } + if (outputMsg.body) body(outputMsg.body) + if (outputMsg.bodyFromFile) body(file(outputMsg.bodyFromFile)) + if (outputMsg.matchers) { bodyMatchers { - yamlContract.response?.matchers?.body?.each { BodyTestMatcher testMatcher -> + yamlContract.outputMessage?.matchers?.body?.each { BodyTestMatcher testMatcher -> MatchingTypeValue value = null switch (testMatcher.type) { case TestMatcherType.by_date: @@ -272,130 +395,17 @@ class YamlContractConverter implements ContractConverter> { case TestMatcherType.by_null: value = byNull() break + default: + throw new UnsupportedOperationException("The type [" + testMatcher.type + "] is unsupported. Hint: If you're using remember to pass ") } jsonPath(testMatcher.path, value) } } } } - if (yamlContract.input) { - input { - if (yamlContract.input.messageFrom) messageFrom(yamlContract.input.messageFrom) - if (yamlContract.input.assertThat) assertThat(yamlContract.input.assertThat) - if (yamlContract.input.triggeredBy) triggeredBy(yamlContract.input.triggeredBy) - messageHeaders { - yamlContract.input?.messageHeaders?.each { String key, Object value -> - KeyValueMatcher matcher = yamlContract.input.matchers?.headers?.find { it.key == key } - Object clientValue = clientValue(value, matcher, key) - header(key, new DslProperty(clientValue, value)) - } - } - if (yamlContract.input.messageBody) messageBody(yamlContract.input.messageBody) - if (yamlContract.input.messageBodyFromFile) messageBody(file(yamlContract.input.messageBodyFromFile)) - bodyMatchers { - yamlContract.input.matchers.body?.each { BodyStubMatcher matcher -> - MatchingTypeValue value = null - switch (matcher.type) { - case StubMatcherType.by_date: - value = byDate() - break - case StubMatcherType.by_time: - value = byTime() - break - case StubMatcherType.by_timestamp: - value = byTimestamp() - break - case StubMatcherType.by_regex: - String regex = matcher.value - if (matcher.predefined) { - regex = predefinedToPattern(matcher.predefined).pattern() - } - value = byRegex(regex) - break - case StubMatcherType.by_equality: - value = byEquality() - break - default: - throw new UnsupportedOperationException("The type [" + matcher.type + "] is unsupported. Hint: If you're using remember to pass ") - } - jsonPath(matcher.path, value) - } - } - } - } - OutputMessage outputMsg = yamlContract.outputMessage - if (outputMsg) { - outputMessage { - if (outputMsg.assertThat) assertThat(outputMsg.assertThat) - if (outputMsg.sentTo) sentTo(outputMsg.sentTo) - headers { - outputMsg.headers?.each { String key, Object value -> - TestHeaderMatcher matcher = outputMsg.matchers?.headers?.find { it.key == key } - Object serverValue = serverValue(value, matcher, key) - header(key, new DslProperty(value, serverValue)) - } - } - if (outputMsg.body) body(outputMsg.body) - if (outputMsg.bodyFromFile) body(file(outputMsg.bodyFromFile)) - if (outputMsg.matchers) { - bodyMatchers { - yamlContract.outputMessage?.matchers?.body?.each { BodyTestMatcher testMatcher -> - MatchingTypeValue value = null - switch (testMatcher.type) { - case TestMatcherType.by_date: - value = byDate() - break - case TestMatcherType.by_time: - value = byTime() - break - case TestMatcherType.by_timestamp: - value = byTimestamp() - break - case TestMatcherType.by_regex: - String regex = testMatcher.value - if (testMatcher.predefined) { - regex = predefinedToPattern(testMatcher.predefined).pattern() - } - value = byRegex(regex) - break - case TestMatcherType.by_equality: - value = byEquality() - break - case TestMatcherType.by_type: - value = byType() { - if (testMatcher.minOccurrence != null) minOccurrence(testMatcher.minOccurrence) - if (testMatcher.maxOccurrence != null) maxOccurrence(testMatcher.maxOccurrence) - } - break - case TestMatcherType.by_command: - value = byCommand(testMatcher.value) - break - case TestMatcherType.by_null: - value = byNull() - break - default: - throw new UnsupportedOperationException("The type [" + testMatcher.type + "] is unsupported. Hint: If you're using remember to pass ") - } - jsonPath(testMatcher.path, value) - } - } - } - } - } } } } - catch (FileNotFoundException e) { - throw new IllegalStateException(e) - } - catch (IllegalStateException ise) { - throw ise - } - catch (Exception e1) { - throw new IllegalStateException("Exception occurred while processing the file [" + contractFile + "]", e1) - } finally { - Thread.currentThread().setContextClassLoader(classLoader) - } } protected Object serverValue(Object value, TestHeaderMatcher matcher, String key) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy index cd20b20572..6f9422bc19 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy @@ -34,6 +34,7 @@ import org.springframework.cloud.contract.verifier.util.ContentUtils import org.springframework.cloud.contract.verifier.util.MapConverter import static org.springframework.cloud.contract.verifier.util.ContentUtils.extractValue +import static org.springframework.cloud.contract.verifier.util.ContentUtils.getClientContentType import static org.springframework.cloud.contract.verifier.util.MapConverter.transformValues /** * Common abstraction over WireMock Request / Response conversion implementations @@ -116,7 +117,7 @@ abstract class BaseWireMockStubStrategy { return processEntriesForTemplating(transformedMap, context) } - private Object processEntriesForTemplating(transformedMap, DocumentContext context) { + private Object processEntriesForTemplating(Object transformedMap, DocumentContext context) { return transformValues(transformedMap, { if (it instanceof String && processor.containsJsonPathTemplateEntry(it)) { String jsonPath = processor.jsonPathFromTemplateEntry(it) @@ -128,7 +129,8 @@ abstract class BaseWireMockStubStrategy { return it } return "${WRAPPER}${it}${WRAPPER}" - } else if (it instanceof String && processor.containsTemplateEntry(it) && template.body() == it) { + } else if (it instanceof String && processor.containsTemplateEntry(it) && + template.escapedBody() == it) { return template.escapedBody() } return it diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy index 8ca4be4e25..2ebd11a740 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy @@ -31,6 +31,7 @@ import java.nio.file.FileSystems import java.nio.file.Path import java.nio.file.PathMatcher import java.util.regex.Pattern + /** * Scans the provided file path for the DSLs. There's a possibility to provide * inclusion and exclusion filters. @@ -52,25 +53,31 @@ class ContractFileScanner { private final File baseDir private final Set excludeMatchers private final Set ignoreMatchers + private final Set includeMatchers private final String includeMatcher - ContractFileScanner(File baseDir, Set excluded, Set ignored, String includeMatcher = "") { + ContractFileScanner(File baseDir, Set excluded, Set ignored, + Set included = [], + String includeMatcher = "") { this.baseDir = baseDir this.excludeMatchers = processPatterns(excluded ?: [] as Set) this.ignoreMatchers = processPatterns(ignored ?: [] as Set) + this.includeMatchers = processPatterns(included ?: [] as Set) this.includeMatcher = includeMatcher } private Set processPatterns(Set patterns) { FileSystem fileSystem = FileSystems.getDefault() - return patterns.collect({ - String syntaxAndPattern = MATCH_PREFIX + '**' + File.separator + it + Set pathMatchers = new HashSet() + for (String pattern : patterns) { + String syntaxAndPattern = MATCH_PREFIX + '**' + File.separator + pattern // FIXME: This looks strange, need to be checked on windows if (IS_OS_WINDOWS) { syntaxAndPattern = syntaxAndPattern.replace("\\", "\\\\") } - fileSystem.getPathMatcher(syntaxAndPattern) - }) as Set + pathMatchers.add(fileSystem.getPathMatcher(syntaxAndPattern)) + } + return pathMatchers } /** @@ -88,6 +95,7 @@ class ContractFileScanner { */ private void appendRecursively(File baseDir, ListMultimap result) { List converters = converters() + converters.add(YamlContractConverter.INSTANCE) if (log.isTraceEnabled()) { log.trace("Found the following contract converters ${converters}") } @@ -102,10 +110,9 @@ class ContractFileScanner { if (!excluded) { boolean contractFile = isContractFile(file) boolean included = includeMatcher ? file.absolutePath.matches(includeMatcher) : true + included = includeMatchers ? matchesPattern(file, includeMatchers) : included if (contractFile && included) { addContractToTestGeneration(result, files, file, i, ContractVerifierDslConverter.convertAsCollection(baseDir, file)) - } else if (YamlContractConverter.INSTANCE.isAccepted(file) && included) { - addContractToTestGeneration(result, files, file, i, YamlContractConverter.INSTANCE.convertFrom(file)) } else if (!contractFile && included) { addContractToTestGeneration(converters, result, files, file, i) } else { @@ -127,7 +134,7 @@ class ContractFileScanner { } private void addContractToTestGeneration(List converters, ListMultimap result, - File[] files, File file, int index) { + File[] files, File file, int index) { boolean converted = false if (!file.isDirectory()) { for (ContractConverter converter : converters) { @@ -155,15 +162,12 @@ class ContractFileScanner { try { return converter.convertFrom(file) } catch (Exception e) { - if (log.isDebugEnabled()) { - log.debug("Exception occurred while trying to convert the file", e) - } - return null + throw new IllegalStateException("Failed to convert file [" + file + "]", e) } } private void addContractToTestGeneration(ListMultimap result, File[] files, File file, - int index, Collection convertedContract) { + int index, Collection convertedContract) { Path path = file.toPath() Integer order = null if (hasScenarioFilenamePattern(path)) { @@ -198,7 +202,7 @@ class ContractFileScanner { private boolean isContractFile(File file) { return file.isFile() && getFilenameExtension(file.toString())?.equalsIgnoreCase("groovy") } - + private static String getFilenameExtension(String path) { if (path == null) { return null diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/HandlebarsTemplateProcessor.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/HandlebarsTemplateProcessor.groovy index fa9c5d575a..f7c0192526 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/HandlebarsTemplateProcessor.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/HandlebarsTemplateProcessor.groovy @@ -1,16 +1,19 @@ package org.springframework.cloud.contract.verifier.template -import wiremock.com.github.jknack.handlebars.Handlebars -import wiremock.com.github.jknack.handlebars.Template -import groovy.transform.CompileStatic -import org.springframework.cloud.contract.spec.ContractTemplate -import org.springframework.cloud.contract.spec.internal.HandlebarsContractTemplate -import org.springframework.cloud.contract.spec.internal.Request -import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsJsonPathHelper -import org.springframework.cloud.contract.verifier.builder.TestSideRequestTemplateModel - import java.util.regex.Matcher import java.util.regex.Pattern + +import com.github.tomakehurst.wiremock.extension.responsetemplating.helpers.WireMockHelpers +import groovy.transform.CompileStatic +import wiremock.com.github.jknack.handlebars.Handlebars +import wiremock.com.github.jknack.handlebars.Template + +import org.springframework.cloud.contract.spec.ContractTemplate +import org.springframework.cloud.contract.spec.internal.CompositeContractTemplate +import org.springframework.cloud.contract.spec.internal.Request +import org.springframework.cloud.contract.verifier.builder.TestSideRequestTemplateModel +import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsJsonPathHelper + /** * Default Handlebars template processor * @@ -20,10 +23,17 @@ import java.util.regex.Pattern @CompileStatic class HandlebarsTemplateProcessor implements TemplateProcessor, ContractTemplate { - private static final Pattern JSON_PATH_PATTERN = Pattern.compile("^.*\\{\\{\\{jsonpath this '(.*)'}}}.*\$") + private static final Pattern ESCAPED_LEGACY_JSON_PATH_PATTERN = Pattern.compile("^.*\\{\\{\\{jsonpath this '(.*)'}}}.*\$") + private static final Pattern ESCAPED_JSON_PATH_PATTERN = Pattern.compile("^.*\\{\\{\\{jsonPath request.body '(.*)'}}}.*\$") + private static final Pattern LEGACY_JSON_PATH_PATTERN = Pattern.compile("^.*\\{\\{jsonpath this '(.*)'}}.*\$") + private static final Pattern JSON_PATH_PATTERN = Pattern.compile("^.*\\{\\{jsonPath request.body '(.*)'}}.*\$") + private static final List PATTERNS = [ESCAPED_LEGACY_JSON_PATH_PATTERN, + ESCAPED_JSON_PATH_PATTERN, LEGACY_JSON_PATH_PATTERN, JSON_PATH_PATTERN] + private static final String LEGACY_JSON_PATH_TEMPLATE_NAME = HandlebarsJsonPathHelper.NAME + private static final String JSON_PATH_TEMPLATE_NAME = WireMockHelpers.jsonPath.name() @Delegate - private final ContractTemplate contractTemplate = new HandlebarsContractTemplate() + private final ContractTemplate contractTemplate = new CompositeContractTemplate() @Override String transform(Request request, String testContents) { @@ -35,12 +45,16 @@ class HandlebarsTemplateProcessor implements TemplateProcessor, ContractTemplate @Override boolean containsTemplateEntry(String line) { - return line.matches('^.*\\{\\{\\{.*}}}.*$') + return (line.contains(contractTemplate.openingTemplate()) && line.contains(contractTemplate.closingTemplate())) || + (line.contains(contractTemplate.escapedOpeningTemplate()) && line.contains(contractTemplate.escapedClosingTemplate())) } @Override boolean containsJsonPathTemplateEntry(String line) { - return line.contains(openingTemplate() + HandlebarsJsonPathHelper.NAME) + return line.contains(openingTemplate() + LEGACY_JSON_PATH_TEMPLATE_NAME) || + line.contains(openingTemplate() + JSON_PATH_TEMPLATE_NAME) || + line.contains(escapedOpeningTemplate() + LEGACY_JSON_PATH_TEMPLATE_NAME) || + line.contains(escapedOpeningTemplate() + JSON_PATH_TEMPLATE_NAME) } @Override @@ -48,11 +62,13 @@ class HandlebarsTemplateProcessor implements TemplateProcessor, ContractTemplate if (!containsJsonPathTemplateEntry(line)) { return "" } - Matcher matcher = JSON_PATH_PATTERN.matcher(line) - if (!matcher.matches()) { - return "" + for (Pattern pattern : PATTERNS) { + Matcher matcher = pattern.matcher(line) + if (matcher.matches()) { + return matcher.group(1) + } } - return matcher.group(1) + return "" } private String templatedResponseBody(Map< String, TestSideRequestTemplateModel> model, Template bodyTemplate) { @@ -71,6 +87,12 @@ class HandlebarsTemplateProcessor implements TemplateProcessor, ContractTemplate try { Handlebars handlebars = new Handlebars() handlebars.registerHelper(HandlebarsJsonPathHelper.NAME, new HandlebarsJsonPathHelper()) + handlebars.registerHelper(WireMockHelpers.jsonPath.name(), new HandlebarsJsonPathHelper()) + WireMockHelpers.values() + .findAll { it != WireMockHelpers.jsonPath} + .each { WireMockHelpers helper -> + handlebars.registerHelper(helper.name(), helper) + } return handlebars.compileInline(content) } catch (IOException e) { throw new RuntimeException(e) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/TemplateProcessor.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/TemplateProcessor.groovy index 0d768437fc..94c38f24c9 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/TemplateProcessor.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/template/TemplateProcessor.groovy @@ -26,14 +26,14 @@ interface TemplateProcessor { /** * Returns {@code true} if the current line contains template related entry for json path processing. - * E.g. for Handlebars if a line contains {{{jsonpath ...}}} then + * E.g. for Handlebars if a line contains {{{jsonpath ...}}} or {{{jsonPath ...}}} then * it's considered to contain template related entry for json path processing */ boolean containsJsonPathTemplateEntry(String line) /** * Returns the json path entry from the current line that contains template related entry for json path processing. - * E.g. for Handlebars if a line contains {{{jsonpath this '$.a.b.c'}}} then + * E.g. for Handlebars if a line contains {{{jsonpath this '$.a.b.c'}}} or {{{jsonPath request.body ...}}} then * the te method would return {@code $.a.b.c}. Returns empty string if there's no matching * json path entry */ 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 2d6180ba80..b2ef40c79b 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 @@ -184,10 +184,13 @@ class ContentUtils { } private static GStringImpl extractValueForXML(GString bodyAsValue, Closure valueProvider) { - return new GStringImpl( + GStringImpl impl = new GStringImpl( bodyAsValue.values.collect { transformXMLStringValue(it, valueProvider) } as String[], bodyAsValue.strings.clone() as String[] ) + // try to convert it to XML + new XmlSlurper().parseText(impl.toString()) + return impl } protected static Object transformJSONStringValue(Object obj, Closure valueProvider) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy index 6e2529f8b8..1bea23558a 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy @@ -20,6 +20,9 @@ import groovy.json.JsonSlurper import org.springframework.cloud.contract.spec.internal.DslProperty import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor import org.springframework.cloud.contract.verifier.template.TemplateProcessor + +import static org.springframework.cloud.contract.verifier.util.ContentUtils.getClientContentType + /** * Converts an object into either client or server side representation. * Iterates over the structure of an object (depending on whether it's an @@ -66,6 +69,8 @@ class MapConverter { def json = new JsonSlurper().parseText(value) if (json instanceof Map) { return convert(json, closure) + } else if (json instanceof List) { + return transformValues(json, closure) } } catch (Exception ignore) { } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy index f1593eb2f4..74f5f915ce 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy @@ -28,7 +28,7 @@ import static ContentType.* import static ContentUtils.extractValue /** - * Useful utility methods to work with regular expresisons + * Useful utility methods to work with regular expressions * * Do not change to {@code @CompileStatic} since it's using double dispatch. * diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/CollectionAssert.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/CollectionAssert.java index 4e93b12359..648af6cf01 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/CollectionAssert.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/CollectionAssert.java @@ -30,14 +30,21 @@ public class CollectionAssert extends IterableAssert { isNotNull(); isNotEmpty(); for (Object anActual : this.actual) { + if (anActual == null) { + failWithMessageRelatedToRegex(regex, anActual); + } String value = anActual.toString(); if (!value.matches(regex)) { - failWithMessage("The value <%s> doesn't match the regex <%s>", value, regex); + failWithMessageRelatedToRegex(regex, value); } } return this; } + private void failWithMessageRelatedToRegex(String regex, Object value) { + failWithMessage("The value <%s> doesn't match the regex <%s>", value, regex); + } + /** * Flattens the collection and checks whether size is greater than or equal to the provided value * @param size - the flattened collection should have size greater than or equal to this value diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/RabbitMockConnectionFactoryAutoConfiguration.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/RabbitMockConnectionFactoryAutoConfiguration.java index e92f02bbf6..e7ef0691fc 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/RabbitMockConnectionFactoryAutoConfiguration.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/RabbitMockConnectionFactoryAutoConfiguration.java @@ -1,5 +1,6 @@ package org.springframework.cloud.contract.verifier.messaging.amqp; +import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; @@ -32,7 +33,7 @@ public class RabbitMockConnectionFactoryAutoConfiguration { @Bean public ConnectionFactory connectionFactory() { final Connection mockConnection = mock(Connection.class); - final Channel mockChannel = mock(Channel.class); + final AMQP.Queue.DeclareOk mockDeclareOk = mock(AMQP.Queue.DeclareOk.class); com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class, new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { @@ -44,12 +45,20 @@ public class RabbitMockConnectionFactoryAutoConfiguration { } }); try { + final Channel mockChannel = mock(Channel.class, invocationOnMock -> { + if ("queueDeclare".equals(invocationOnMock.getMethod().getName())) { + return mockDeclareOk; + } + return Mockito.RETURNS_DEFAULTS.answer(invocationOnMock); + }); when(mockConnection.isOpen()).thenReturn(true); when(mockConnection.createChannel()).thenReturn(mockChannel); when(mockConnection.createChannel(Mockito.anyInt())).thenReturn(mockChannel); } catch (Exception e) { throw new RuntimeException(e); } - return new CachingConnectionFactory(mockConnectionFactory); + return new CachingConnectionFactory(mockConnectionFactory) { + + }; } } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java index b3e4dcbb51..8c923121c7 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; @@ -28,13 +29,12 @@ import org.springframework.amqp.core.MessageListener; import org.springframework.amqp.core.MessagePropertiesBuilder; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; -import org.springframework.amqp.rabbit.support.CorrelationData; +import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.util.Assert; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mockingDetails; @@ -98,8 +98,18 @@ public class SpringAmqpStubMessages implements throw new IllegalStateException("no listeners found for destination " + destination); } for (SimpleMessageListenerContainer listenerContainer : listenerContainers) { - MessageListener messageListener = (MessageListener) listenerContainer.getMessageListener(); - messageListener.onMessage(message); + Object messageListener = listenerContainer.getMessageListener(); + if (messageListener instanceof ChannelAwareMessageListener && listenerContainer.getConnectionFactory() != null) { + try { + ((ChannelAwareMessageListener) messageListener).onMessage(message, + listenerContainer.getConnectionFactory().createConnection().createChannel(true)); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } else { + ((MessageListener) messageListener).onMessage(message); + } } } @@ -108,7 +118,7 @@ public class SpringAmqpStubMessages implements ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); ArgumentCaptor routingKeyCaptor = ArgumentCaptor.forClass(String.class); verify(this.rabbitTemplate, atLeastOnce()).send(eq(destination), routingKeyCaptor.capture(), - messageCaptor.capture(), any(CorrelationData.class)); + messageCaptor.capture(), ArgumentMatchers.any()); if (messageCaptor.getAllValues().isEmpty()) { log.info("no messages found on destination {}", destination); return null; @@ -117,6 +127,10 @@ public class SpringAmqpStubMessages implements return messageCaptor.getValue(); } Message message = messageCaptor.getValue(); + if (message == null) { + log.info("no messages found on destination {}", destination); + return null; + } if (!routingKeyCaptor.getValue().isEmpty()) { log.info("routing key passed {}", routingKeyCaptor.getValue()); message.getMessageProperties() diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessages.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessages.java index fdb7a8b043..17f6b88339 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessages.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessages.java @@ -16,6 +16,7 @@ package org.springframework.cloud.contract.verifier.messaging.stream; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -28,6 +29,7 @@ import org.springframework.cloud.stream.test.binder.MessageCollector; import org.springframework.context.ApplicationContext; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; +import org.springframework.util.StringUtils; /** * @author Marcin Grzejszczak @@ -54,7 +56,7 @@ public class StreamStubMessages implements MessageVerifier> { public void send(Message message, String destination) { try { MessageChannel messageChannel = this.context - .getBean(resolvedDestination(destination), MessageChannel.class); + .getBean(resolvedDestination(destination, DefaultChannels.OUTPUT), MessageChannel.class); messageChannel.send(message); } catch (Exception e) { @@ -68,7 +70,7 @@ public class StreamStubMessages implements MessageVerifier> { public Message receive(String destination, long timeout, TimeUnit timeUnit) { try { MessageChannel messageChannel = this.context - .getBean(resolvedDestination(destination), MessageChannel.class); + .getBean(resolvedDestination(destination, DefaultChannels.INPUT), MessageChannel.class); return this.messageCollector.forChannel(messageChannel).poll(timeout, timeUnit); } catch (Exception e) { @@ -78,10 +80,11 @@ public class StreamStubMessages implements MessageVerifier> { } } - private String resolvedDestination(String destination) { + private String resolvedDestination(String destination, DefaultChannels defaultChannel) { try { BindingServiceProperties channelBindingServiceProperties = this.context .getBean(BindingServiceProperties.class); + Map channels = new HashMap<>(); for (Map.Entry entry : channelBindingServiceProperties .getBindings().entrySet()) { if (destination.equals(entry.getValue().getDestination())) { @@ -89,9 +92,24 @@ public class StreamStubMessages implements MessageVerifier> { log.debug("Found a channel named [{}] with destination [{}]", entry.getKey(), destination); } - return entry.getKey(); + channels.put(entry.getKey().toLowerCase(), destination); } } + if (channels.size() == 1) { + return channels.keySet().iterator().next(); + } else if (channels.size() > 0) { + if (log.isDebugEnabled()) { + log.debug("Found following channels [{}] for destination [{}]. " + + "Will pick the one that matches the default channel name or the first one if none is matching", + channels, destination); + } + String defaultChannelName = channels.get(defaultChannel.name().toLowerCase()); + String matchingChannelName = StringUtils.hasText(defaultChannelName) ? defaultChannel.name().toLowerCase() : channels.keySet().iterator().next(); + if (log.isDebugEnabled()) { + log.debug("Picked channel name is [{}]", matchingChannelName); + } + return matchingChannelName; + } } catch (Exception e) { log.error("Exception took place while trying to resolve the destination. Will assume the name [" + destination + "]", e); } @@ -109,3 +127,8 @@ public class StreamStubMessages implements MessageVerifier> { } } + + +enum DefaultChannels { + INPUT, OUTPUT +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/GeneratorScannerSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/GeneratorScannerSpec.groovy index 300ca595b3..24947a19da 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/GeneratorScannerSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/GeneratorScannerSpec.groovy @@ -16,10 +16,12 @@ package org.springframework.cloud.contract.verifier +import spock.lang.Specification + import org.springframework.cloud.contract.verifier.builder.JavaTestGenerator import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties -import org.springframework.cloud.contract.verifier.config.TestFramework -import spock.lang.Specification + +import static org.springframework.cloud.contract.verifier.config.TestFramework.SPOCK class GeneratorScannerSpec extends Specification { @@ -39,7 +41,7 @@ class GeneratorScannerSpec extends Specification { def "should create class with full package"() { given: - ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(targetFramework: TestFramework.SPOCK) + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(targetFramework: SPOCK) properties.contractsDslDir = new File(this.getClass().getResource("/directory/with/stubs/package").toURI()) TestGenerator testGenerator = new TestGenerator(properties, classGenerator, Stub(FileSaver)) when: diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertTests.java b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertTests.java index 213b03c42d..91e183da07 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertTests.java +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertTests.java @@ -33,6 +33,18 @@ public class CollectionAssertTests { } } + @Test + public void should_throw_an_exception_when_element_is_null() { + Collection collection = collectionWithNulls(); + + try { + SpringCloudContractAssertions.assertThat(collection).allElementsMatch("[0-9]"); + Assertions.fail("should throw exception"); + } catch (AssertionError e) { + Assertions.assertThat(e).hasMessageContaining("The value doesn't match the regex <[0-9]>"); + } + } + @Test public void should_throw_an_exception_when_collection_is_null() { Collection collection = null; @@ -277,6 +289,12 @@ public class CollectionAssertTests { return list; } + private Collection collectionWithNulls() { + List list = new ArrayList<>(); + list.add(null); + return list; + } + private Collection nestedCollection() { List list = new ArrayList<>(); List list1 = new ArrayList<>(); diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilderSpec.groovy index 5c7020876e..7181fe45c3 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ClassBuilderSpec.groovy @@ -1,5 +1,7 @@ package org.springframework.cloud.contract.verifier.builder +import spock.lang.Issue + import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import spock.lang.Specification @@ -49,6 +51,16 @@ class ClassBuilderSpec extends Specification { ClassBuilder.retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperClass' } + @Issue("701") + def "should match base class when mapping regex has multiple folders"() { + given: + ContractVerifierConfigProperties props = new ContractVerifierConfigProperties( + baseClassMappings: ['.*bar.baz.some.*' : 'com.example.base.SuperClass']) + String contractRelativeFolder = 'foo/bar/baz/some/package'.split("/").join(File.separator) + expect: + ClassBuilder.retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperClass' + } + def "should return the first matching base class when provided mapping doesn't match"() { given: ContractVerifierConfigProperties props = new ContractVerifierConfigProperties( 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 437773ee97..525e3d9746 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 @@ -491,6 +491,58 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + @Issue("#702") + def "should generate proper type for large numbers [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method 'PUT' + urlPath '/example/create' + headers { + contentType applicationJson() + } + body( + [ + "name" : $(consumer(~/.+/), producer("string-1")), + "updatedTs" : $(consumer(~/\d{13}/), producer(1531916906000L)), + "isDisabled": $(consumer(regex(anyBoolean())), producer(true)) + ] + ) + } + + response { + status 200 + headers { + contentType applicationJsonUtf8() + } + body( + [ + "id" : $(consumer(2222L), producer(~/\d+/)), + "name" : fromRequest().body("name"), + "updatedTs" : fromRequest().body("updatedTs"), + "isDisabled": fromRequest().body("isDisabled") + ] + ) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + test.contains('''assertThatJson(parsedJson).field("['updatedTs']").isEqualTo(1531916906000L)''') + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + @Issue("#465") def "should work for '/' url for [#methodBuilderName]"() { given: @@ -653,6 +705,57 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + @Issue("#493") + def "should not escape a form URL encoded request body another try [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method POST() + urlPath('/oauth/token') + headers { + header(authorization(), anyNonBlankString()) + header(contentType(), 'application/x-www-form-urlencoded; charset=UTF-8') + header(accept(), anyNonBlankString()) + } + body('username=user&password=password&grant_type=password') + } + response { + status 200 + headers { + header(contentType(), applicationJsonUtf8()) + } + body([ + refresh_token: 'RANDOM_REFRESH_TOKEN', + access_token : 'RANDOM_ACCESS_TOKEN', + token_type : 'bearer', + expires_in : 3600, + scope : ['task'], + user : [ + id : 1, + username: 'user', + name : 'User' + ] + ]) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + !test.contains("&") + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + @Issue("#509") def "classToCheck() should return class of object"() { given: @@ -716,4 +819,34 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + def "should not escape a regex pattern when matching raw body value [#methodBuilderName]"() { + def pattern = "\\d+\\w?" + def escapedPattern = "\\\\d+\\\\w?" + + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + url '/api/arbitrary-url' + } + response { + status OK() + body(value(stub("1"), test(regex(pattern)))) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + test.contains(escapedPattern) + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy index 1b96f457be..2b3d8d4f39 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy @@ -447,7 +447,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } } - def "should generate assertions for array inside response body element with #methodBuilderName"() { + def "should generate assertions for array inside response body element with #methodBuilderName"() { given: Contract contractDsl = Contract.make { request { @@ -1515,8 +1515,7 @@ World.'''""" method "GET" urlPath('/auth/oauth/check_token') { queryParameters { - parameter 'token': - value( + parameter 'token': value( consumer(regex('^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')), producer('6973b31d-7140-402a-bca6-1cdb954e03a7') ) @@ -1551,8 +1550,7 @@ World.'''""" method "GET" urlPath('/auth/oauth/check_token') { queryParameters { - parameter 'token': - value( + parameter 'token': value( consumer(regex('^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')), producer('6973b31d-7140-402a-bca6-1cdb954e03a7') ) @@ -2594,7 +2592,17 @@ DocumentContext parsedJson = JsonPath.parse(json); fullBody: fromRequest().body(), responseFoo: fromRequest().body('$.foo'), responseBaz: fromRequest().body('$.baz'), - responseBaz2: "Bla bla ${fromRequest().body('$.foo')} bla bla" + responseBaz2: "Bla bla ${fromRequest().body('$.foo')} bla bla", + rawUrl: fromRequest().rawUrl(), + rawPath: fromRequest().rawPath(), + rawPathIndex: fromRequest().rawPath(1), + rawParam: fromRequest().rawQuery("foo"), + rawParamIndex: fromRequest().rawQuery("foo", 1), + rawAuthorization: fromRequest().rawHeader("Authorization"), + rawAuthorization2: fromRequest().rawHeader("Authorization", 1), + rawResponseFoo: fromRequest().rawBody('$.foo'), + rawResponseBaz: fromRequest().rawBody('$.baz'), + rawResponseBaz2: "Bla bla ${fromRequest().rawBody('$.foo')} bla bla" ) } } @@ -2608,6 +2616,7 @@ DocumentContext parsedJson = JsonPath.parse(json); SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) then: !test.contains('''DslProperty''') + !test.contains('''ERROR: ''') test.contains('''assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2")''') test.contains('''assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx")''') test.contains('''assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1")''') @@ -2619,6 +2628,16 @@ DocumentContext parsedJson = JsonPath.parse(json); test.contains('''assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5)''') test.contains('''assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla")''') test.contains('''assertThatJson(parsedJson).field("['param']").isEqualTo("bar")''') + test.contains('''assertThatJson(parsedJson).field("['rawUrl']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2")''') + test.contains('''assertThatJson(parsedJson).field("['rawPath']").isEqualTo("/api/v1/xxxx")''') + test.contains('''assertThatJson(parsedJson).field("['rawPathIndex']").isEqualTo("v1")''') + test.contains('''assertThatJson(parsedJson).field("['rawParamIndex']").isEqualTo("bar2")''') + test.contains('''assertThatJson(parsedJson).field("['rawResponseFoo']").isEqualTo("bar")''') + test.contains('''assertThatJson(parsedJson).field("['rawAuthorization']").isEqualTo("secret")''') + test.contains('''assertThatJson(parsedJson).field("['rawAuthorization2']").isEqualTo("secret2")''') + test.contains('''assertThatJson(parsedJson).field("['rawResponseBaz']").isEqualTo(5)''') + test.contains('''assertThatJson(parsedJson).field("['rawResponseBaz2']").isEqualTo("Bla bla bar bla bla")''') + test.contains('''assertThatJson(parsedJson).field("['rawParam']").isEqualTo("bar")''') responseAssertion(test) where: methodBuilderName | methodBuilder | responseAssertion @@ -2628,6 +2647,60 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String body -> body.contains('assertThat(response.getHeaderString("Authorization")).isEqualTo("foo secret bar");') } } + @Issue("#230") + def "should manage to reference request in response via WireMock native entries [#methodBuilderName]"() { + given: + //tag::template_contract[] + Contract contractDsl = Contract.make { + request { + method 'GET' + url('/api/v1/xxxx') { + queryParameters { + parameter("foo", "bar") + parameter("foo", "bar2") + } + } + headers { + header(authorization(), "secret") + header(authorization(), "secret2") + } + body(foo: "bar", baz: 5) + } + response { + status OK() + headers { + contentType(applicationJson()) + } + body(''' + { + "responseFoo": "{{{ jsonPath request.body '$.foo' }}}", + "responseBaz": {{{ jsonPath request.body '$.baz' }}}, + "responseBaz2": "Bla bla {{{ jsonPath request.body '$.foo' }}} bla bla" + } + '''.toString()) + } + } + //end::template_contract[] + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + and: + builder.appendTo(blockBuilder) + String test = blockBuilder.toString() + when: + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + then: + !test.contains('''DslProperty''') + test.contains('''assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar")''') + test.contains('''assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5)''') + test.contains('''assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla")''') + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + def "should generate JUnit assertions with cookies"() { given: MethodBodyBuilder builder = new MockMvcJUnitMethodBodyBuilder(contractDslWithCookiesValue, properties) 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 6d72082b7c..c4f01db2eb 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 @@ -18,17 +18,23 @@ package org.springframework.cloud.contract.verifier.builder import org.junit.Rule import org.junit.rules.TemporaryFolder +import spock.lang.Issue +import spock.lang.Specification + import org.springframework.cloud.contract.verifier.TestGenerator import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.config.TestFramework import org.springframework.cloud.contract.verifier.config.TestMode import org.springframework.cloud.contract.verifier.file.ContractMetadata import org.springframework.cloud.contract.verifier.util.SyntaxChecker import org.springframework.util.StringUtils -import spock.lang.Issue -import spock.lang.Specification import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT +import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5 import static org.springframework.cloud.contract.verifier.config.TestFramework.SPOCK +import static org.springframework.cloud.contract.verifier.config.TestMode.EXPLICIT +import static org.springframework.cloud.contract.verifier.config.TestMode.JAXRSCLIENT +import static org.springframework.cloud.contract.verifier.config.TestMode.MOCKMVC import static org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter.convertAsCollection class SingleTestGeneratorSpec extends Specification { @@ -49,7 +55,6 @@ class SingleTestGeneratorSpec extends Specification { '@FixMethodOrder(MethodSorters.NAME_ASCENDING)', '@Test', '@Ignore', 'import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;', 'import io.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat'] - private static final List explicitJUnitRestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;', 'import org.junit.FixMethodOrder;', 'import org.junit.Ignore;', 'import org.junit.Test;', 'import org.junit.runners.MethodSorters;', 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.RestAssured.*;', @@ -62,6 +67,34 @@ class SingleTestGeneratorSpec extends Specification { '@FixMethodOrder(MethodSorters.NAME_ASCENDING)', '@Test', '@Ignore', 'import io.restassured.specification.RequestSpecification;', 'import io.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat'] + private static + final List mockMvcJUnit5RestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;', + 'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;', + '@Test', '@Disabled', 'import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;', + 'import com.jayway.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat'] + private static + final List mockMvcJUnit5RestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;', + 'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;', + '@Test', '@Disabled', 'import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;', + 'import io.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat'] + + private static + final List explicitJUnit5RestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;', + 'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.RestAssured.*;', + '@Test', '@Disabled', 'import com.jayway.restassured.specification.RequestSpecification;', + 'import com.jayway.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat'] + + private static + final List explicitJUnit5RestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;', + 'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static io.restassured.RestAssured.*;', + '@Test', '@Disabled', 'import io.restassured.specification.RequestSpecification;', + 'import io.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat'] + + private static final List spockClassRestAssured2Strings = ['import com.jayway.jsonpath.DocumentContext', 'import com.jayway.jsonpath.JsonPath', 'import spock.lang.Ignore', 'import spock.lang.Specification', 'import spock.lang.Stepwise', 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*', @@ -85,23 +118,23 @@ class SingleTestGeneratorSpec extends Specification { public static final Closure JAVA_ASSERTER = { String classToTest -> String name = Math.abs(new Random().nextInt()) String changedTest = classToTest - .replace("public class Test", "public class Test${name}") - .replace("public class ContractsTest", "public class Test${name}") + .replace('public class Test', "public class Test${name}") + .replace('public class ContractsTest', "public class Test${name}") String fqn = FQN(classToTest) SyntaxChecker.tryToCompileJavaWithoutImports("${fqn}${name}", changedTest) } static String FQN(String classToTest) { - return classToTest.contains("0_1_0_dev_1_uncommitted_d1174dd") ? - "org.springframework.cloud.contract.verifier.tests.com_uscm.dale_api44_spec._0_1_0_dev_1_uncommitted_d1174dd.Test" : - "test.Test" + return classToTest.contains('0_1_0_dev_1_uncommitted_d1174dd') ? + 'org.springframework.cloud.contract.verifier.tests.com_uscm.dale_api44_spec._0_1_0_dev_1_uncommitted_d1174dd.Test' : + 'test.Test' } public static final Closure JAVA_JAXRS_ASSERTER = { String classToTest -> String name = Math.abs(new Random().nextInt()) String changedTest = classToTest - .replace("public class Test {", "public class Test${name} {\njavax.ws.rs.client.WebTarget webTarget;\n") - .replace("public class ContractsTest {", "public class Test${name} {\njavax.ws.rs.client.WebTarget webTarget;\n") + .replace('public class Test {', "public class Test${name} {\njavax.ws.rs.client.WebTarget webTarget;\n") + .replace('public class ContractsTest {', "public class Test${name} {\njavax.ws.rs.client.WebTarget webTarget;\n") String fqn = FQN(classToTest) SyntaxChecker.tryToCompileJavaWithoutImports("${fqn}${name}", changedTest) } @@ -112,11 +145,11 @@ class SingleTestGeneratorSpec extends Specification { def setup() { file = tmpFolder.newFile() - wiriteContract(file) + writeContract(file) } - private wiriteContract(File file) { - file.write(""" + private writeContract(File file) { + file.write(''' org.springframework.cloud.contract.spec.Contract.make { request { method 'PUT' @@ -126,70 +159,72 @@ class SingleTestGeneratorSpec extends Specification { status OK() } } -""") +''') } - def "should build test class for #testFramework"() { + def 'should build test class for #testFramework'() { given: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework properties.testMode = mode - ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convertAsCollection(new File("/"),file)) + ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, order, convertAsCollection(new File('/'), file)) contract.ignored >> true - contract.order >> 2 JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: classStrings.each { assert clazz.contains(it) } and: asserter(clazz) where: - testFramework | mode | classStrings | asserter - JUNIT | TestMode.MOCKMVC | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER - JUNIT | TestMode.EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER - SPOCK | TestMode.MOCKMVC | spockClassRestAssured3Strings | GROOVY_ASSERTER - SPOCK | TestMode.EXPLICIT | explicitSpockRestAssured3ClassStrings | GROOVY_ASSERTER + 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 } - def "should build test class for #testFramework when the path contains bizarre signs"() { + def 'should build test class for #testFramework when the path contains bizarre signs'() { given: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework - properties.basePackageForTests = "org.springframework.cloud.contract.verifier.tests" + properties.basePackageForTests = 'org.springframework.cloud.contract.verifier.tests' and: - File newFolder = tmpFolder.newFolder("META_INF") - File subfolders = new File(newFolder, "/com.uscm/dale_api44_spec/0.1.0_dev.1.uncommitted+d1174dd/contracts/") + File newFolder = tmpFolder.newFolder('META_INF') + File subfolders = new File(newFolder, '/com.uscm/dale_api44_spec/0.1.0_dev.1.uncommitted+d1174dd/contracts/') subfolders.mkdirs() - File newFile = new File(subfolders, "contract.groovy") + File newFile = new File(subfolders, 'contract.groovy') newFile.createNewFile() - wiriteContract(newFile) + writeContract(newFile) properties.contractsDslDir = newFolder properties.generatedTestSourcesDir = newFolder.parentFile when: int size = new TestGenerator(properties).generate() then: 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) + 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 | TestMode.MOCKMVC | JAVA_ASSERTER | "ContractsTest.java" - JUNIT | TestMode.EXPLICIT | JAVA_ASSERTER | "ContractsTest.java" - SPOCK | TestMode.MOCKMVC | GROOVY_ASSERTER | "ContractsSpec.groovy" - SPOCK | TestMode.EXPLICIT | GROOVY_ASSERTER | "ContractsSpec.groovy" + 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' } - def "should build test class for #testFramework with Rest Assured 2.x"() { + def 'should build test class for #testFramework with Rest Assured 2.x'() { given: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework properties.testMode = mode - ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, - convertAsCollection(new File("/"),file) ) + ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, order, + convertAsCollection(new File('/'), file)) contract.ignored >> true - contract.order >> 2 JavaTestGenerator testGenerator = new JavaTestGenerator(checker: new ClassPresenceChecker() { @Override boolean isClassPresent(String className) { @@ -198,25 +233,27 @@ class SingleTestGeneratorSpec extends Specification { }) when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: classStrings.each { assert clazz.contains(it) } - clazz.contains("com.jayway.restassured") - !clazz.contains("io.restassured") + clazz.contains('com.jayway.restassured') + !clazz.contains('io.restassured') where: - testFramework | mode | classStrings - JUNIT | TestMode.MOCKMVC | mockMvcJUnitRestAssured2ClassStrings - JUNIT | TestMode.EXPLICIT | explicitJUnitRestAssured2ClassStrings - SPOCK | TestMode.MOCKMVC | spockClassRestAssured2Strings - SPOCK | TestMode.EXPLICIT | explicitSpockRestAssured2ClassStrings + 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 } - def "should build test class for #testFramework and mode #mode with two files"() { + def 'should build test class for #testFramework and mode #mode with two files'() { given: File file = tmpFolder.newFile() - file.write(""" + file.write(''' org.springframework.cloud.contract.spec.Contract.make { request { method 'PUT' @@ -233,10 +270,10 @@ class SingleTestGeneratorSpec extends Specification { } } } - """) + ''') and: File file2 = tmpFolder.newFile() - file2.write(""" + file2.write(''' org.springframework.cloud.contract.spec.Contract.make { request { method 'PUT' @@ -253,22 +290,22 @@ class SingleTestGeneratorSpec extends Specification { } } } -""") +''') and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework ContractMetadata contract = new ContractMetadata(file.toPath(), false, 1, null, - convertAsCollection(new File("/"),file) ) + convertAsCollection(new File('/'), file)) contract.ignored >> false and: ContractMetadata contract2 = new ContractMetadata(file2.toPath(), false, 1, null, - convertAsCollection(new File("/"),file2) ) + convertAsCollection(new File('/'), file2)) contract2.ignored >> false and: JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract, contract2], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract, contract2], 'test', 'test', 'com/foo') then: classStrings.each { clazz.contains(it) } @@ -277,25 +314,26 @@ class SingleTestGeneratorSpec extends Specification { and: textAssertion(clazz) where: - testFramework | mode | classStrings | asserter | textAssertion - JUNIT | TestMode.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 } - SPOCK | TestMode.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 } + 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} + 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} + 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} } - def "should build JaxRs test class for #testFramework"() { + def 'should build JaxRs test class for #testFramework'() { given: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.testMode = TestMode.JAXRSCLIENT properties.targetFramework = testFramework - ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convertAsCollection(new File("/"),file)) + ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, null, convertAsCollection(new File('/'), file)) contract.ignored >> true - contract.order >> 2 JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: classStrings.each { clazz.contains(it) } @@ -304,15 +342,16 @@ class SingleTestGeneratorSpec extends Specification { asserter(clazz) where: - testFramework | classStrings | asserter - JUNIT | ['import static javax.ws.rs.client.Entity.*', 'import javax.ws.rs.core.Response'] | JAVA_JAXRS_ASSERTER - SPOCK | ['import static javax.ws.rs.client.Entity.*'] | GROOVY_ASSERTER + testFramework | classStrings | asserter + JUNIT | ['import static javax.ws.rs.client.Entity.*', 'import javax.ws.rs.core.Response'] | JAVA_JAXRS_ASSERTER + JUNIT5 | ['import static javax.ws.rs.client.Entity.*', 'import javax.ws.rs.core.Response'] | JAVA_JAXRS_ASSERTER + SPOCK | ['import static javax.ws.rs.client.Entity.*'] | GROOVY_ASSERTER } - def "should work if there is messaging and rest in one folder #testFramework"() { + def 'should work if there is messaging and rest in one folder #testFramework'() { given: File secondFile = tmpFolder.newFile() - secondFile.write(""" + secondFile.write(''' org.springframework.cloud.contract.spec.Contract.make { ignored() label 'some_label' @@ -327,22 +366,20 @@ class SingleTestGeneratorSpec extends Specification { assertThat('hashCode()') } } - """) + ''') and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework - ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convertAsCollection(new File("/"),file)) + ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, order, convertAsCollection(new File('/'), file)) contract.ignored >> true - contract.order >> 2 and: - ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2, convertAsCollection(new File("/"),secondFile)) + ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, order, convertAsCollection(new File('/'), secondFile)) contract2.ignored >> true - contract2.order >> 2 and: JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract, contract2], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract, contract2], 'test', 'test', 'com/foo') then: classStrings.each { clazz.contains(it) } @@ -352,16 +389,17 @@ class SingleTestGeneratorSpec extends Specification { asserter(clazz) where: - testFramework | classStrings | asserter - JUNIT | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER - SPOCK | spockClassRestAssured3Strings | GROOVY_ASSERTER + testFramework | order | classStrings | asserter + JUNIT | 2 | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER + JUNIT5 | null | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER + SPOCK | 2 | spockClassRestAssured3Strings | GROOVY_ASSERTER } @Issue('#30') - def "should ignore a test if the contract is ignored in the dsl"() { + def 'should ignore a test if the contract is ignored in the dsl'() { given: File secondFile = tmpFolder.newFile() - secondFile.write(""" + secondFile.write(''' org.springframework.cloud.contract.spec.Contract.make { ignored() request { @@ -372,37 +410,37 @@ class SingleTestGeneratorSpec extends Specification { status OK() } } - """) + ''') and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework and: - ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2, convertAsCollection(new File("/"),file)) + ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, order, convertAsCollection(new File('/'), file)) contract2.ignored >> false - contract2.order >> 2 and: JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract2], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract2], 'test', 'test', 'com/foo') then: classStrings.each { clazz.contains(it) } - clazz.contains('@Ignore') + clazz.contains(ignoreAnnotation) and: asserter(clazz) where: - testFramework | classStrings | asserter - JUNIT | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER - SPOCK | spockClassRestAssured3Strings | GROOVY_ASSERTER + testFramework | order | classStrings | ignoreAnnotation | asserter + JUNIT | 2 | mockMvcJUnitRestAssured3ClassStrings | '@Ignore' | JAVA_ASSERTER + JUNIT5 | null | mockMvcJUnit5RestAssured3ClassStrings | '@Disabled' | JAVA_ASSERTER + SPOCK | 2 | spockClassRestAssured3Strings | '@Ignore' | GROOVY_ASSERTER } @Issue('#117') - def "should generate test in explicit test mode using JUnit"() { + def 'should generate test in explicit test mode using JUnit'() { given: - String baseClass = """ + String baseClass = ''' // tag::context_path_baseclass[] import io.restassured.RestAssured; import org.junit.Before; @@ -421,11 +459,11 @@ class SingleTestGeneratorSpec extends Specification { } } // end::context_path_baseclass[] - """ - SyntaxChecker.tryToCompileJavaWithoutImports("test.ContextPathTestingBaseClass", "package test;\n${baseClass}") + ''' + SyntaxChecker.tryToCompileJavaWithoutImports('test.ContextPathTestingBaseClass', "package test;\n${baseClass}") and: File secondFile = tmpFolder.newFile() - secondFile.write(""" + secondFile.write(''' // tag::context_path_contract[] org.springframework.cloud.contract.spec.Contract.make { request { @@ -437,28 +475,28 @@ class SingleTestGeneratorSpec extends Specification { } } // end::context_path_contract[] - """) + ''') and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = JUNIT properties.testMode = TestMode.EXPLICIT - properties.baseClassForTests = "test.ContextPathTestingBaseClass" + properties.baseClassForTests = 'test.ContextPathTestingBaseClass' and: ContractMetadata contract = new ContractMetadata(file.toPath(), false, 1, - null, convertAsCollection(new File("/"),file)) + null, convertAsCollection(new File('/'), file)) and: SingleTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: - clazz.contains("RequestSpecification request = given();") - clazz.contains("Response response = given().spec(request)") + clazz.contains('RequestSpecification request = given();') + clazz.contains('Response response = given().spec(request)') } def "should pick the contract's name as the test method"() { given: File secondFile = tmpFolder.newFile() - secondFile.write(""" + secondFile.write(''' org.springframework.cloud.contract.spec.Contract.make { name("MySuperMethod") request { @@ -469,18 +507,18 @@ class SingleTestGeneratorSpec extends Specification { status OK() } } - """) + ''') and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); properties.targetFramework = testFramework - ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(new File("/"),secondFile)) + ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(new File('/'), secondFile)) JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: - clazz.contains("validate_mySuperMethod()") + clazz.contains('validate_mySuperMethod()') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } def "should pick the contract's name as the test method when there are multiple contracts"() { @@ -505,18 +543,18 @@ class SingleTestGeneratorSpec extends Specification { and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); properties.targetFramework = testFramework - ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(new File("/"),secondFile)) + ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(new File('/'), secondFile)) JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: - clazz.contains("validate_shouldHaveIndex1()") - clazz.contains("validate_shouldHaveIndex2()") + clazz.contains('validate_shouldHaveIndex1()') + clazz.contains('validate_shouldHaveIndex2()') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } - def "should generate the test method when there are multiple contracts without name field"() { + def 'should generate the test method when there are multiple contracts without name field'() { given: File secondFile = tmpFolder.newFile() secondFile.write(''' @@ -537,26 +575,26 @@ class SingleTestGeneratorSpec extends Specification { and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); properties.targetFramework = testFramework - ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(new File("/"),secondFile)) + ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(new File('/'), secondFile)) JavaTestGenerator testGenerator = new JavaTestGenerator() when: - String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo') + String clazz = testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') then: - clazz.contains("_0() throws Exception") - clazz.contains("_1() throws Exception") + clazz.contains('_0() throws Exception') + clazz.contains('_1() throws Exception') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } - @Issue("#359") - def "should generate tests from a contract that references a file for [#testFramework]"() { + @Issue('#359') + def 'should generate tests from a contract that references a file for [#testFramework]'() { given: - File contractLocation = new File(SingleTestGeneratorSpec.class.getResource("/classpath/readFromFile.groovy").toURI()) + File contractLocation = new File(SingleTestGeneratorSpec.class.getResource('/classpath/readFromFile.groovy').toURI()) File temp = tmpFolder.newFolder() and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties( targetFramework: testFramework, contractsDslDir: contractLocation.parentFile, - basePackageForTests: "a.b", + basePackageForTests: 'a.b', generatedTestSourcesDir: temp ) TestGenerator testGenerator = new TestGenerator(properties) @@ -565,22 +603,22 @@ class SingleTestGeneratorSpec extends Specification { then: count == 1 and: - String test = new File(temp, "a/b/ContractVerifier" + (testFramework == JUNIT ? "Test.java" : "Spec.groovy")).text - test.contains("REQUEST") - test.contains("RESPONSE") + String test = new File(temp, "a/b/ContractVerifier${getTestName(testFramework)}").text + test.contains('REQUEST') + test.contains('RESPONSE') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } - @Issue("#260") - def "should generate tests in a folder taken from basePackageForTests when it is set for [#testFramework]"() { + @Issue('#260') + def 'should generate tests in a folder taken from basePackageForTests when it is set for [#testFramework]'() { given: - File contractLocation = new File(SingleTestGeneratorSpec.class.getResource("/classpath/readFromFile.groovy").toURI()) + File contractLocation = new File(SingleTestGeneratorSpec.class.getResource('/classpath/readFromFile.groovy').toURI()) File temp = tmpFolder.newFolder() and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties( targetFramework: testFramework, contractsDslDir: contractLocation.parentFile, - basePackageForTests: "a.b", generatedTestSourcesDir: temp + basePackageForTests: 'a.b', generatedTestSourcesDir: temp ) TestGenerator testGenerator = new TestGenerator(properties) when: @@ -588,22 +626,22 @@ class SingleTestGeneratorSpec extends Specification { then: count == 1 and: - String test = new File(temp, "a/b/ContractVerifier" + (testFramework == JUNIT ? "Test.java" : "Spec.groovy")).text - test.contains("REQUEST") - test.contains("RESPONSE") + String test = new File(temp, "a/b/ContractVerifier${getTestName(testFramework)}").text + test.contains('REQUEST') + test.contains('RESPONSE') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } - @Issue("#260") + @Issue('#260') def "should generate tests in a folder taken from baseClassForTests's package when it is set for [#testFramework]"() { given: - File contractLocation = new File(SingleTestGeneratorSpec.class.getResource("/classpath/readFromFile.groovy").toURI()) + File contractLocation = new File(SingleTestGeneratorSpec.class.getResource('/classpath/readFromFile.groovy').toURI()) File temp = tmpFolder.newFolder() and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties( targetFramework: testFramework, contractsDslDir: contractLocation.parentFile, - baseClassForTests: "a.b.SomeClass", generatedTestSourcesDir: temp + baseClassForTests: 'a.b.SomeClass', generatedTestSourcesDir: temp ) TestGenerator testGenerator = new TestGenerator(properties) when: @@ -611,22 +649,22 @@ class SingleTestGeneratorSpec extends Specification { then: count == 1 and: - String test = new File(temp, "a/b/ContractVerifier" + (testFramework == JUNIT ? "Test.java" : "Spec.groovy")).text - test.contains("REQUEST") - test.contains("RESPONSE") + String test = new File(temp, "a/b/ContractVerifier${getTestName(testFramework)}").text + test.contains('REQUEST') + test.contains('RESPONSE') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } - @Issue("#260") - def "should generate tests in a folder taken from packageWithBaseClasses when it is set for [#testFramework]"() { + @Issue('#260') + def 'should generate tests in a folder taken from packageWithBaseClasses when it is set for [#testFramework]'() { given: - File contractLocation = new File(SingleTestGeneratorSpec.class.getResource("/classpath/readFromFile.groovy").toURI()) + File contractLocation = new File(SingleTestGeneratorSpec.class.getResource('/classpath/readFromFile.groovy').toURI()) File temp = tmpFolder.newFolder() and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties( targetFramework: testFramework, contractsDslDir: contractLocation.parentFile, - packageWithBaseClasses: "a.b", generatedTestSourcesDir: temp + packageWithBaseClasses: 'a.b', generatedTestSourcesDir: temp ) TestGenerator testGenerator = new TestGenerator(properties) when: @@ -634,17 +672,17 @@ class SingleTestGeneratorSpec extends Specification { then: count == 1 and: - String test = new File(temp, "a/b/ContractVerifier" + (testFramework == JUNIT ? "Test.java" : "Spec.groovy")).text - test.contains("REQUEST") - test.contains("RESPONSE") + String test = new File(temp, "a/b/ContractVerifier${getTestName(testFramework)}").text + test.contains('REQUEST') + test.contains('RESPONSE') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } - @Issue("#260") - def "should generate tests in a default folder when no property was passed for [#testFramework]"() { + @Issue('#260') + def 'should generate tests in a default folder when no property was passed for [#testFramework]'() { given: - File contractLocation = new File(SingleTestGeneratorSpec.class.getResource("/classpath/readFromFile.groovy").toURI()) + File contractLocation = new File(SingleTestGeneratorSpec.class.getResource('/classpath/readFromFile.groovy').toURI()) File temp = tmpFolder.newFolder() and: ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties( @@ -657,14 +695,29 @@ class SingleTestGeneratorSpec extends Specification { then: count == 1 and: - String test = new File(temp, "org/springframework/cloud/contract/verifier/tests/ContractVerifier" + (testFramework == JUNIT ? "Test.java" : "Spec.groovy")).text - test.contains("REQUEST") - test.contains("RESPONSE") + String test = new File(temp, "org/springframework/cloud/contract/verifier/tests/ContractVerifier${getTestName(testFramework)}").text + test.contains('REQUEST') + test.contains('RESPONSE') where: - testFramework << [JUNIT, SPOCK] + testFramework << [JUNIT, JUNIT5, SPOCK] } + def 'should throw exception in JUnit5 when contract belongs to scenario'() { + given: + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() + properties.targetFramework = JUNIT5 + properties.testMode = mode + ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 1, convertAsCollection(new File('/'), file)) + JavaTestGenerator testGenerator = new JavaTestGenerator() + when: + testGenerator.buildClass(properties, [contract], 'test', 'test', 'com/foo') + then: + thrown(UnsupportedOperationException) + where: + mode << [MOCKMVC, EXPLICIT, JAXRSCLIENT] + } - - + private static String getTestName(TestFramework testFramework) { + testFramework == SPOCK ? 'Spec.groovy' : 'Test.java' + } } 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/WireMockGroovyDslSpec.groovy index ef4cd771e1..565336dedd 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/WireMockGroovyDslSpec.groovy @@ -1736,7 +1736,73 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie } @Issue('#237') - def "should generate a stub with response template"() { + def "should work with legacy mappings"() { + given: + def oldJsonMapping = ''' + { + "request" : { + "urlPath" : "/api/v1/xxxx", + "method" : "POST", + "headers" : { + "Authorization" : { + "equalTo" : "secret2" + } + }, + "cookies" : { + "foo" : { + "equalTo" : "bar" + } + }, + "queryParameters" : { + "foo" : { + "equalTo" : "bar2" + } + }, + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.['baz'] == 5)]" + }, { + "matchesJsonPath" : "$[?(@.['foo'] == 'bar')]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"authorization\\":\\"{{{request.headers.Authorization.[0]}}}\\",\\"path\\":\\"{{{request.path}}}\\",\\"responseBaz\\":{{{jsonpath this '$.baz'}}} ,\\"param\\":\\"{{{request.query.foo.[0]}}}\\",\\"pathIndex\\":\\"{{{request.path.[1]}}}\\",\\"responseBaz2\\":\\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\\",\\"responseFoo\\":\\"{{{jsonpath this '$.foo'}}}\\",\\"authorization2\\":\\"{{{request.headers.Authorization.[1]}}}\\",\\"fullBody\\":\\"{{{escapejsonbody}}}\\",\\"url\\":\\"{{{request.url}}}\\",\\"paramIndex\\":\\"{{{request.query.foo.[1]}}}\\"}", + "headers" : { + "Authorization" : "{{{request.headers.Authorization.[0]}}};foo" + }, + "transformers" : [ "response-template", "foo-transformer" ] + } + } + ''' + and: + int port = SocketUtils.findAvailableTcpPort() + WireMockServer server = new WireMockServer(config().port(port)) + server.start() + and: + server.addStubMapping(WireMockStubMapping.buildFrom(oldJsonMapping)) + when: + ResponseEntity entity = call(port) + then: + entity.headers.find { it.key == "Authorization" && it.value.contains("secret;foo") } + AssertionUtil.assertThatJsonsAreEqual((''' + { + "url" : "/api/v1/xxxx?foo=bar&foo=bar2", + "param" : "bar", + "paramIndex" : "bar2", + "authorization" : "secret", + "authorization2" : "secret2", + "fullBody" : "{\\"foo\\":\\"bar\\",\\"baz\\":5}", + "responseFoo" : "bar", + "responseBaz" : 5, + "responseBaz2" : "Bla bla bar bla bla" + } + '''), entity.body) + cleanup: + server?.shutdown() + } + + @Issue('#540') + def "should generate a stub with standard WireMock request template"() { given: org.springframework.cloud.contract.spec.Contract groovyDsl = org.springframework.cloud.contract.spec.Contract.make { request { @@ -1772,7 +1838,17 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie fullBody: fromRequest().body(), responseFoo: fromRequest().body('$.foo'), responseBaz: fromRequest().body('$.baz'), - responseBaz2: "Bla bla ${fromRequest().body('$.foo')} bla bla" + responseBaz2: "Bla bla ${fromRequest().body('$.foo')} bla bla", + rawUrl: fromRequest().rawUrl(), + rawPath: fromRequest().rawPath(), + rawPathIndex: fromRequest().rawPath(1), + rawParam: fromRequest().rawQuery("foo"), + rawParamIndex: fromRequest().rawQuery("foo", 1), + rawAuthorization: fromRequest().rawHeader("Authorization"), + rawAuthorization2: fromRequest().rawHeader("Authorization", 1), + rawResponseFoo: fromRequest().rawBody('$.foo'), + rawResponseBaz: fromRequest().rawBody('$.baz'), + rawResponseBaz2: "Bla bla ${fromRequest().rawBody('$.foo')} bla bla" ) } } @@ -1807,7 +1883,7 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie }, "response" : { "status" : 200, - "body" : "{\\"authorization\\":\\"{{{request.headers.Authorization.[0]}}}\\",\\"path\\":\\"{{{request.path}}}\\",\\"responseBaz\\":{{{jsonpath this '$.baz'}}} ,\\"param\\":\\"{{{request.query.foo.[0]}}}\\",\\"pathIndex\\":\\"{{{request.path.[1]}}}\\",\\"responseBaz2\\":\\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\\",\\"responseFoo\\":\\"{{{jsonpath this '$.foo'}}}\\",\\"authorization2\\":\\"{{{request.headers.Authorization.[1]}}}\\",\\"fullBody\\":\\"{{{escapejsonbody}}}\\",\\"url\\":\\"{{{request.url}}}\\",\\"paramIndex\\":\\"{{{request.query.foo.[1]}}}\\"}", + "body" : "{\\"rawAuthorization2\\":\\"{{request.headers.Authorization.[1]}}\\",\\"responseBaz\\":{{{jsonPath request.body '$.baz'}}} ,\\"pathIndex\\":\\"{{{request.path.[1]}}}\\",\\"rawAuthorization\\":\\"{{request.headers.Authorization.[0]}}\\",\\"authorization2\\":\\"{{{request.headers.Authorization.[1]}}}\\",\\"rawParam\\":\\"{{request.query.foo.[0]}}\\",\\"url\\":\\"{{{request.url}}}\\",\\"paramIndex\\":\\"{{{request.query.foo.[1]}}}\\",\\"authorization\\":\\"{{{request.headers.Authorization.[0]}}}\\",\\"path\\":\\"{{{request.path}}}\\",\\"rawUrl\\":\\"{{request.url}}\\",\\"rawPath\\":\\"{{request.path}}\\",\\"rawResponseBaz2\\":\\"Bla bla {{jsonPath request.body '$.foo'}} bla bla\\",\\"param\\":\\"{{{request.query.foo.[0]}}}\\",\\"rawResponseBaz\\":{{jsonPath request.body '$.baz'}} ,\\"responseBaz2\\":\\"Bla bla {{{jsonPath request.body '$.foo'}}} bla bla\\",\\"rawResponseFoo\\":\\"{{jsonPath request.body '$.foo'}}\\",\\"responseFoo\\":\\"{{{jsonPath request.body '$.foo'}}}\\",\\"rawPathIndex\\":\\"{{request.path.[1]}}\\",\\"fullBody\\":\\"{{{escapejsonbody}}}\\",\\"rawParamIndex\\":\\"{{request.query.foo.[1]}}\\"}", "headers" : { "Authorization" : "{{{request.headers.Authorization.[0]}}};foo" }, @@ -1827,16 +1903,28 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie entity.headers.find { it.key == "Authorization" && it.value.contains("secret;foo") } and: AssertionUtil.assertThatJsonsAreEqual((''' - { - "url" : "/api/v1/xxxx?foo=bar&foo=bar2", - "param" : "bar", - "paramIndex" : "bar2", - "authorization" : "secret", - "authorization2" : "secret2", - "fullBody" : "{\\"foo\\":\\"bar\\",\\"baz\\":5}", - "responseFoo" : "bar", - "responseBaz" : 5, - "responseBaz2" : "Bla bla bar bla bla" + { + "rawAuthorization2":"secret2", + "responseBaz":5, + "pathIndex":"v1", + "rawAuthorization":"secret", + "authorization2":"secret2", + "rawParam":"bar", + "url":"/api/v1/xxxx?foo=bar&foo=bar2", + "paramIndex":"bar2", + "authorization":"secret", + "path":"/api/v1/xxxx", + "rawUrl":"/api/v1/xxxx?foo=bar&foo=bar2", + "rawPath":"/api/v1/xxxx", + "rawResponseBaz2":"Bla bla bar bla bla", + "param":"bar", + "rawResponseBaz":5, + "responseBaz2":"Bla bla bar bla bla", + "rawResponseFoo":"bar", + "responseFoo":"bar", + "rawPathIndex":"v1", + "fullBody":"{\\"foo\\":\\"bar\\",\\"baz\\":5}", + "rawParamIndex":"bar2" } '''), entity.body) cleanup: @@ -2155,6 +2243,79 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie } + @Issue('#493') + def "should not escape &"() { + given: + Contract groovyDsl = Contract.make { + request { + method POST() + urlPath('/oauth/token') + headers { + header(authorization(), anyNonBlankString()) + header(contentType(), 'application/x-www-form-urlencoded; charset=UTF-8') + header(accept(), anyNonBlankString()) + } + body('username=user&password=password&grant_type=password') + } + response { + status 200 + headers { + header(contentType(), applicationJsonUtf8()) + } + body([ + refresh_token: 'RANDOM_REFRESH_TOKEN', + access_token : 'RANDOM_ACCESS_TOKEN', + token_type : 'bearer', + expires_in : 3600, + scope : ['task'], + user : [ + id : 1, + username: 'user', + name : 'User' + ] + ]) + } + } + when: + String wireMockStub = new WireMockStubStrategy("Test", new ContractMetadata(null, false, 0, null, groovyDsl), groovyDsl).toWireMockClientStub() + then: + !wireMockStub.contains("&") + and: + AssertionUtil.assertThatJsonsAreEqual(''' + { + "request" : { + "urlPath" : "/oauth/token", + "method" : "POST", + "headers" : { + "Authorization" : { + "matches" : "^\\\\s*\\\\S[\\\\S\\\\s]*" + }, + "Content-Type" : { + "equalTo" : "application/x-www-form-urlencoded; charset=UTF-8" + }, + "Accept" : { + "matches" : "^\\\\s*\\\\S[\\\\S\\\\s]*" + } + }, + "bodyPatterns" : [ { + "equalTo" : "username=user&password=password&grant_type=password" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"access_token\\":\\"RANDOM_ACCESS_TOKEN\\",\\"refresh_token\\":\\"RANDOM_REFRESH_TOKEN\\",\\"scope\\":[\\"task\\"],\\"token_type\\":\\"bearer\\",\\"expires_in\\":3600,\\"user\\":{\\"name\\":\\"User\\",\\"id\\":1,\\"username\\":\\"user\\"}}", + "headers" : { + "Content-Type" : "application/json;charset=UTF-8" + }, + "transformers" : [ "response-template", "foo-transformer" ] + } + } + ''', wireMockStub) + and: + stubMappingIsValidWireMockStub(wireMockStub) + + } + WireMockConfiguration config() { return new WireMockConfiguration().extensions(responseTemplateTransformer()) } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy index d4ee0cdf48..2d09bbc5d9 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy @@ -34,7 +34,7 @@ class ContractFileScannerSpec extends Specification { File baseDir = new File(this.getClass().getResource("/directory/with/stubs").toURI()) Set excluded = ["package/**"] as Set Set ignored = ["other/different/**"] as Set - ContractFileScanner scanner = new ContractFileScanner(baseDir, excluded, ignored) + ContractFileScanner scanner = new ContractFileScanner(baseDir, excluded, ignored, [] as Set) when: ListMultimap result = scanner.findContracts() then: @@ -52,7 +52,7 @@ class ContractFileScannerSpec extends Specification { File baseDir = new File(this.getClass().getResource("/strange_[3.3.3]_directory").toURI()) Set excluded = ["foo/**"] as Set Set ignored = ["bar/**"] as Set - ContractFileScanner scanner = new ContractFileScanner(baseDir, excluded, ignored) + ContractFileScanner scanner = new ContractFileScanner(baseDir, excluded, ignored, [] as Set) when: ListMultimap result = scanner.findContracts() then: @@ -66,7 +66,7 @@ class ContractFileScannerSpec extends Specification { def "should find contracts group in scenario"() { given: File baseDir = new File(this.getClass().getResource("/directory/with/scenario").toURI()) - ContractFileScanner scanner = new ContractFileScanner(baseDir, [] as Set, [] as Set) + ContractFileScanner scanner = new ContractFileScanner(baseDir, [] as Set, [] as Set, [] as Set) when: ListMultimap contracts = scanner.findContracts() then: @@ -80,7 +80,7 @@ class ContractFileScannerSpec extends Specification { def "should find contract files with converters"() { given: File baseDir = new File(this.getClass().getResource("/directory/with/mixed").toURI()) - ContractFileScanner scanner = new ContractFileScanner(baseDir, null, null) { + ContractFileScanner scanner = new ContractFileScanner(baseDir, null, null, null) { @Override protected List converters() { return [new ContractConverter() { @@ -101,11 +101,67 @@ class ContractFileScannerSpec extends Specification { }] } } + when: + scanner.findContracts() + then: + IllegalStateException e = thrown(IllegalStateException) + e.cause.message == "boom" + e.message.matches(".*Failed to convert file .*invalid.json.*") + } + + def "should prefer custom yaml converter over standard yaml converter"() { + given: + File baseDir = new File(this.getClass().getResource("/directory/with/custom/yml").toURI()) + ContractFileScanner scanner = new ContractFileScanner(baseDir, null, null) { + @Override + protected List converters() { + return [new ContractConverter() { + @Override + boolean isAccepted(File file) { + if (!file.name.endsWith(".yml") && !file.name.endsWith(".yaml")) { + return false + } + String line + file.withReader { + line = it.readLine() + } + return line != null && line.startsWith("custom_format: 1.0") + } + + @Override + Collection convertFrom(File file) { + return Collections.singleton(Contract.newInstance()) + } + + @Override + Object convertTo(Collection contract) { + return new Object() + } + }] + } + } when: ListMultimap result = scanner.findContracts() then: result.keySet().size() == 1 result.entries().every { it.value.convertedContract } - result.entries().find { it.value.convertedContract.any { it.request.method.clientValue == "PUT" } } } + + def "should find contracts for include pattern"() { + given: + File baseDir = new File(this.getClass().getResource("/directory/with/common-messaging").toURI()) + Set included = ["social-service/**","**/coupon-collected/**/*V1*"] as Set + ContractFileScanner scanner = new ContractFileScanner(baseDir, [] as Set, [] as Set, included) + when: + ListMultimap result = scanner.findContracts() + then: + result.keySet().size() == 3 + result.values().find { (it.path.fileName.toString() == 'couponCollectedEventV1.groovy') }.groupSize==2 + result.values().find { (it.convertedContract.first().label == 'couponCollectedV1') } + result.values().findAll { (it.path.fileName.toString() == 'couponCollectedEventV2.groovy') }.isEmpty() + result.values().find { (it.path.fileName.toString() == 'shouldUpdateUserInfo.groovy') }.groupSize==1 + result.values().find { (it.path.fileName.toString() == 'shouldReturnEmptyFriendsWhenGetFriends.groovy') }.groupSize==1 + result.get(baseDir.toPath().resolve("coupon-sent")).size() == 0 + result.get(baseDir.toPath().resolve("reward-rules")).size() == 0 + } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesSpec.groovy index e15205d30b..87aa2ac429 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesSpec.groovy @@ -1,10 +1,12 @@ package org.springframework.cloud.contract.verifier.messaging.amqp +import org.mockito.exceptions.verification.WantedButNotInvoked import wiremock.com.google.common.collect.ImmutableMap import org.springframework.amqp.core.Binding import org.springframework.amqp.core.BindingBuilder import org.springframework.amqp.core.DirectExchange import org.springframework.amqp.core.Message +import org.springframework.amqp.core.MessageProperties import org.springframework.amqp.core.Queue import org.springframework.amqp.rabbit.core.RabbitTemplate import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer @@ -22,6 +24,7 @@ class SpringAmqpStubMessagesSpec extends Specification { RabbitTemplate rabbitTemplate = mock(RabbitTemplate.class) SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer() MessageListenerAdapter messageListenerAdapter = Mock(MessageListenerAdapter.class) + Message message = Mock(Message.class) String queueName = "test.queue" String exchange = "test-exchange" @@ -51,4 +54,45 @@ class SpringAmqpStubMessagesSpec extends Specification { msg.getMessageProperties().getHeaders().get(DEFAULT_CLASSID_FIELD_NAME) == "org.example.Some" }) } + + def "should fail to receive a message if rabbit template wasn't called"() { + given: + listenerContainer.setMessageListener(messageListenerAdapter) + listenerContainer.setQueueNames(queueName) + Binding binding = BindingBuilder.bind(new Queue(queueName)).to(new DirectExchange(exchange)).with(routingKey) + MessageListenerAccessor messageListenerAccessor = new MessageListenerAccessor(null, [listenerContainer], [binding]) + SpringAmqpStubMessages messageVerifier = new SpringAmqpStubMessages(rabbitTemplate, messageListenerAccessor) + when: + messageVerifier.receive("foo") + then: + thrown(WantedButNotInvoked) + } + + def "should return null if received called and message was sent without any body"() { + given: + listenerContainer.setMessageListener(messageListenerAdapter) + listenerContainer.setQueueNames(queueName) + Binding binding = BindingBuilder.bind(new Queue(queueName)).to(new DirectExchange(exchange)).with(routingKey) + MessageListenerAccessor messageListenerAccessor = new MessageListenerAccessor(null, [listenerContainer], [binding]) + SpringAmqpStubMessages messageVerifier = new SpringAmqpStubMessages(rabbitTemplate, messageListenerAccessor) + + and: + rabbitTemplate.send("foo", "bar", null, null) + expect: + messageVerifier.receive("foo") == null + } + + def "should return match the message if received called and message was sent with a message with null payload"() { + given: + listenerContainer.setMessageListener(messageListenerAdapter) + listenerContainer.setQueueNames(queueName) + Binding binding = BindingBuilder.bind(new Queue(queueName)).to(new DirectExchange(exchange)).with(routingKey) + MessageListenerAccessor messageListenerAccessor = new MessageListenerAccessor(null, [listenerContainer], [binding]) + SpringAmqpStubMessages messageVerifier = new SpringAmqpStubMessages(rabbitTemplate, messageListenerAccessor) + message.getMessageProperties() >> new MessageProperties() + and: + rabbitTemplate.send("foo", "bar", message, null) + expect: + messageVerifier.receive("foo") is message + } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessagesSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessagesSpec.groovy new file mode 100644 index 0000000000..8cfcd09210 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/StreamStubMessagesSpec.groovy @@ -0,0 +1,109 @@ +package org.springframework.cloud.contract.verifier.messaging.stream + +import spock.lang.Issue +import spock.lang.Specification + +import org.springframework.cloud.stream.config.BindingProperties +import org.springframework.cloud.stream.config.BindingServiceProperties +import org.springframework.cloud.stream.test.binder.MessageCollector +import org.springframework.context.ApplicationContext +import org.springframework.messaging.MessageChannel + +/** + * @author Marcin Grzejszczak + */ +class StreamStubMessagesSpec extends Specification { + + @Issue("694") + def "should resolve input channel if input and output have same destination and receive is called"() { + given: + ApplicationContext applicationContext = Mock(ApplicationContext) + BindingServiceProperties properties = new BindingServiceProperties( + bindings: [ + input: new BindingProperties(destination: "verifications"), + output: new BindingProperties(destination: "verifications"), + ] + ) + MessageCollector collector = Stub(MessageCollector) + and: + applicationContext.getBean(BindingServiceProperties) >> properties + applicationContext.getBean(MessageCollector) >> collector + and: + StreamStubMessages messages = new StreamStubMessages(applicationContext) + when: + messages.receive("verifications") + then: + 1 * applicationContext.getBean("input", MessageChannel) >> null + } + + @Issue("694") + def "should resolve output channel if input and output have same destination and send is called"() { + given: + ApplicationContext applicationContext = Mock(ApplicationContext) + BindingServiceProperties properties = new BindingServiceProperties( + bindings: [ + input: new BindingProperties(destination: "verifications"), + output: new BindingProperties(destination: "verifications"), + ] + ) + MessageCollector collector = Stub(MessageCollector) + MessageChannel channel = Stub(MessageChannel) + and: + applicationContext.getBean(BindingServiceProperties) >> properties + applicationContext.getBean(MessageCollector) >> collector + and: + StreamStubMessages messages = new StreamStubMessages(applicationContext) + when: + messages.send("foo", [:], "verifications") + then: + 1 * applicationContext.getBean("output", MessageChannel) >> channel + } + + def "should resolve channel via destination for send and receive"() { + given: + ApplicationContext applicationContext = Mock(ApplicationContext) + BindingServiceProperties properties = new BindingServiceProperties( + bindings: [ + foo: new BindingProperties(destination: "verifications") + ] + ) + MessageCollector collector = Stub(MessageCollector) + MessageChannel channel = Stub(MessageChannel) + and: + applicationContext.getBean(BindingServiceProperties) >> properties + applicationContext.getBean(MessageCollector) >> collector + and: + StreamStubMessages messages = new StreamStubMessages(applicationContext) + when: + messageInteraction(messages) + then: + 1 * applicationContext.getBean("foo", MessageChannel) >> channel + where: + messageInteraction << [ { StreamStubMessages stream -> stream.send("foo", [:], "verifications")}, + { StreamStubMessages stream -> stream.receive("verifications")}] + } + + def "should resolve channel via channel name for send and receive"() { + given: + ApplicationContext applicationContext = Mock(ApplicationContext) + BindingServiceProperties properties = new BindingServiceProperties( + bindings: [ + verifications: new BindingProperties(destination: "bar") + ] + ) + MessageCollector collector = Stub(MessageCollector) + MessageChannel channel = Stub(MessageChannel) + and: + applicationContext.getBean(BindingServiceProperties) >> properties + applicationContext.getBean(MessageCollector) >> collector + and: + StreamStubMessages messages = new StreamStubMessages(applicationContext) + when: + messageInteraction(messages) + then: + 1 * applicationContext.getBean("verifications", MessageChannel) >> channel + where: + messageInteraction << [ { StreamStubMessages stream -> stream.send("foo", [:], "verifications")}, + { StreamStubMessages stream -> stream.receive("verifications")}] + } +} diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-collected/src/main/resources/contracts/rule-engine-daemon/messaging/couponCollectedEventV1.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-collected/src/main/resources/contracts/rule-engine-daemon/messaging/couponCollectedEventV1.groovy new file mode 100644 index 0000000000..b61731fbcf --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-collected/src/main/resources/contracts/rule-engine-daemon/messaging/couponCollectedEventV1.groovy @@ -0,0 +1,27 @@ +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + + description("""Should send a message in topic coupon_collected""") + + label 'couponCollectedV1' + + input { + triggeredBy('couponCollectedSm()') + } + + outputMessage { + sentTo('coupon_collected') + + body([ + receiverSnId: value(consumer("receiver-sn-id"), producer(regex('([^\\W]|-)+'))), + sessionId: value(consumer(7928568413097907541), producer(regex('\\d+'))), + createdTs: value(consumer(1504688949158), producer(regex('\\d+'))), + couponToken: value(consumer("440006-6-1504688949139-xyuzzrx5"), producer(regex('([^\\W]|-)+'))) + ]) + + headers { + messagingContentType(applicationJsonUtf8()) + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-collected/src/main/resources/contracts/rule-engine-daemon/messaging/couponCollectedEventV2.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-collected/src/main/resources/contracts/rule-engine-daemon/messaging/couponCollectedEventV2.groovy new file mode 100644 index 0000000000..529f1c93f7 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-collected/src/main/resources/contracts/rule-engine-daemon/messaging/couponCollectedEventV2.groovy @@ -0,0 +1,27 @@ +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + + description("""Should send a message in topic coupon_collected""") + + label 'couponCollectedSm' + + input { + triggeredBy('couponCollectedSm()') + } + + outputMessage { + sentTo('coupon_collected') + + body([ + receiverSnId: value(consumer("receiver-sn-id"), producer(regex('([^\\W]|-)+'))), + sessionId: value(consumer(7928568413097907541), producer(regex('\\d+'))), + createdTs: value(consumer(1504688949158), producer(regex('\\d+'))), + couponToken: value(consumer("440006-6-1504688949139-xyuzzrx5"), producer(regex('([^\\W]|-)+'))) + ]) + + headers { + messagingContentType(applicationJsonUtf8()) + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-sent/src/main/resources/contracts/rule-engine-daemon/messaging/couponSentEventSm.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-sent/src/main/resources/contracts/rule-engine-daemon/messaging/couponSentEventSm.groovy new file mode 100644 index 0000000000..24772ce212 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/kafka-topics/coupon-sent/src/main/resources/contracts/rule-engine-daemon/messaging/couponSentEventSm.groovy @@ -0,0 +1,27 @@ +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + + description("""Should send a message in topic coupon_sent""") + + label 'couponSentSm' + + input { + triggeredBy('couponSentSm()') + } + + outputMessage { + sentTo('coupon_sent') + + body([ + senderUserId: value(consumer(123), producer(regex('\\d+'))), + sessionId: value(consumer(7928568413097907541), producer(regex('\\d+'))), + createdTs: value(consumer(1504688949158), producer(regex('\\d+'))), + couponToken: value(consumer("440006-6-1504688949139-xyuzzrx5"), producer(regex('([^\\W]|-)+'))) + ]) + + headers { + messagingContentType(applicationJsonUtf8()) + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/reward-rules/src/main/resources/contracts/reward_rules/rest/admin/v1/shouldReturnBetRanges.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/reward-rules/src/main/resources/contracts/reward_rules/rest/admin/v1/shouldReturnBetRanges.groovy new file mode 100644 index 0000000000..f70af82308 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/reward-rules/src/main/resources/contracts/reward_rules/rest/admin/v1/shouldReturnBetRanges.groovy @@ -0,0 +1,38 @@ +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + description('Should return bet ranges array') + request { + method 'GET' + url('/admin/v1/spin/betRanges') + } + + response { + status 200 + body( + betRanges: [ + [ + betRangeId : 3, + fromBetPercent: -1 + ], + [ + betRangeId : 4, + fromBetPercent: 0 + ], + [ + betRangeId : 1, + fromBetPercent: 90 + ], + [ + betRangeId : 2, + fromBetPercent: 130 + ] + ] + ) + + headers { + contentType(applicationJsonUtf8()) + } + } +} + diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/social-service/src/main/resources/contracts/coupons_new_backend/rest/shouldReturnEmptyFriendsWhenGetFriends.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/social-service/src/main/resources/contracts/coupons_new_backend/rest/shouldReturnEmptyFriendsWhenGetFriends.groovy new file mode 100644 index 0000000000..5c33f16f4f --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/social-service/src/main/resources/contracts/coupons_new_backend/rest/shouldReturnEmptyFriendsWhenGetFriends.groovy @@ -0,0 +1,30 @@ +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + description(""" +Should return empty array if user has no friends +""") + request { + method 'GET' + urlPath('/SocialServer/social/getFriends') + url('/social/getFriends') { + queryParameters { + parameter 'snId': $(consumer(regex('([^\\W]|-)+')), producer('12345')) + parameter 'snType': $(consumer(regex('\\d+')), producer(2)) + } + } + } + + response { + status 200 + body( + """ + {"friends" : []} + """) + + headers { + contentType(applicationJsonUtf8()) + } + } +} + diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/social-service/src/main/resources/contracts/facebook_client/rest/shouldUpdateUserInfo.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/social-service/src/main/resources/contracts/facebook_client/rest/shouldUpdateUserInfo.groovy new file mode 100644 index 0000000000..411bbe2496 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/common-messaging/social-service/src/main/resources/contracts/facebook_client/rest/shouldUpdateUserInfo.groovy @@ -0,0 +1,44 @@ +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + description(""" +User's information should be update if has appropriate age +""") + request { + method 'POST' + url '/test/updateUserInfo' + body([ + userId: 123, + age: 25, + firstName: "asd", + lastName: "asd" + ]) + stubMatchers { + jsonPath('$.userId', byRegex("[1-9]{1}([0-9]{7})")) + jsonPath('$.age', byRegex("(1[89]|[2-9][0-9])")) + jsonPath('$.firstName', byRegex("[a-zA-Z]{2,20}")) + jsonPath('$.lastName', byRegex("[a-zA-Z]{2,20}")) + } + headers { + contentType(applicationJson()) + } + } + response { + status 200 + body([ + userId: fromRequest().body("userId"), + age: fromRequest().body("age"), + firstName: fromRequest().body("firstName"), + lastName: fromRequest().body("lastName") + ]) + testMatchers { + jsonPath('$.userId', byEquality()) + jsonPath('$.age', byEquality()) + jsonPath('$.firstName', byEquality()) + jsonPath('$.lastName', byEquality()) + } + headers { + contentType(applicationJson()) + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/custom/yml/custom.yml b/spring-cloud-contract-verifier/src/test/resources/directory/with/custom/yml/custom.yml new file mode 100644 index 0000000000..fce56b04db --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/custom/yml/custom.yml @@ -0,0 +1,16 @@ +custom_format: 1.0 +outbound: + request: + url: /foo + method: PUT + headers: + foo: bar + body: + foo: bar +inbound: + response: + status: 200 + headers: + foo2: bar + body: + foo2: bar \ No newline at end of file diff --git a/spring-cloud-contract-wiremock/pom.xml b/spring-cloud-contract-wiremock/pom.xml index b9b983d31b..17f69d42ea 100644 --- a/spring-cloud-contract-wiremock/pom.xml +++ b/spring-cloud-contract-wiremock/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-wiremock @@ -35,6 +35,11 @@ org.springframework.boot spring-boot-starter + + org.springframework.boot + spring-boot-autoconfigure-processor + true + org.springframework.boot spring-boot-starter-tomcat diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java index d01c428ac4..b75f391fee 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java @@ -16,17 +16,18 @@ package org.springframework.cloud.contract.wiremock; -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.common.Slf4jNotifier; -import com.github.tomakehurst.wiremock.core.Options; - import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.common.Slf4jNotifier; +import com.github.tomakehurst.wiremock.core.Options; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -37,8 +38,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.util.Assert; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; /** * Configuration and lifecycle for a Spring Application context that wants to run a @@ -55,6 +58,10 @@ import org.springframework.util.StringUtils; @EnableConfigurationProperties(WireMockProperties.class) public class WireMockConfiguration implements SmartLifecycle { + private static final Log log = LogFactory.getLog(WireMockConfiguration.class); + + static final String WIREMOCK_SERVER_BEAN_NAME = "wireMockServer"; + private volatile boolean running; private WireMockServer server; @@ -91,10 +98,15 @@ public class WireMockConfiguration implements SmartLifecycle { this.customizer.customize(factory); } } - this.server = new WireMockServer(this.options); + if (this.server == null) { + this.server = new WireMockServer(this.options); + } registerStubs(); - if (!this.beanFactory.containsBean("wireMockServer")) { - this.beanFactory.registerSingleton("wireMockServer", this.server); + if (log.isDebugEnabled()) { + log.debug("WireMock server has [" + this.server.getStubMappings().size() + "] registered"); + } + if (!this.beanFactory.containsBean(WIREMOCK_SERVER_BEAN_NAME)) { + this.beanFactory.registerSingleton(WIREMOCK_SERVER_BEAN_NAME, this.server); } } @@ -118,6 +130,10 @@ public class WireMockConfiguration implements SmartLifecycle { } } + void reset() { + this.server.resetAll(); + } + private void registerFiles(com.github.tomakehurst.wiremock.core.WireMockConfiguration factory) throws IOException { List resources = new ArrayList<>(); for (String files : this.wireMock.getServer().getFiles()) { @@ -142,6 +158,19 @@ public class WireMockConfiguration implements SmartLifecycle { this.server.start(); WireMock.configureFor("localhost", this.server.port()); this.running = true; + if (log.isDebugEnabled()) { + log.debug("Started WireMock at port [" + this.server.port() + "]. It has [" + this.server.getStubMappings().size() + "] mappings registered"); + } + /* + Thanks to Tom Akehurst: + I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a + previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it + isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet. + Calling the admin endpoint just happened to remove the dead connection from the pool. + This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false"); + */ + Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + this.server.port() + "/__admin/mappings", String.class) + .getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible"); } @Override @@ -149,6 +178,9 @@ public class WireMockConfiguration implements SmartLifecycle { if (this.running) { this.server.stop(); this.running = false; + if (log.isDebugEnabled()) { + log.debug("Stopped WireMock instance"); + } } } diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java new file mode 100644 index 0000000000..77947f65bf --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java @@ -0,0 +1,73 @@ +package org.springframework.cloud.contract.wiremock; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.support.AbstractTestExecutionListener; + +/** + * Stops the WireMock server after each test class and restarts it before every class + * + * @author Marcin Grzejszczak + * @since 1.2.6 + */ +public final class WireMockTestExecutionListener extends AbstractTestExecutionListener { + + private static final Log log = LogFactory.getLog(WireMockTestExecutionListener.class); + + @Override public void beforeTestClass(TestContext testContext) { + try { + if (wireMockConfigMissing(testContext)) { + return; + } + WireMockConfiguration wireMockConfiguration = wireMockConfiguration(testContext); + if (log.isDebugEnabled()) { + log.debug("WireMock configuration is running [" + wireMockConfiguration.isRunning() + "]"); + } + if (!wireMockConfiguration.isRunning()) { + wireMockConfiguration.reset(); + wireMockConfiguration.init(); + wireMockConfiguration.start(); + } + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Exception occurred while trying to init WireMock configuration", e); + } + } + } + + private boolean wireMockConfigMissing(TestContext testContext) { + boolean missing = !testContext.getApplicationContext().containsBean(WireMockConfiguration.class.getName()); + if (log.isDebugEnabled()) { + log.debug("WireMockConfig is missing [" + missing + "]"); + } + return missing; + } + + @Override public void afterTestClass(TestContext testContext) { + try { + if (wireMockConfigMissing(testContext)) { + return; + } + stopWireMockConfiguration(testContext); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Exception occurred while trying to init WireMock configuration", e); + } + } + } + + private void stopWireMockConfiguration(TestContext testContext) { + WireMockConfiguration wireMockConfiguration = wireMockConfiguration(testContext); + if (wireMockConfiguration.isRunning()) { + if (log.isDebugEnabled()) { + log.debug("WireMock is running, will stop it"); + } + wireMockConfiguration.stop(); + } + } + + private WireMockConfiguration wireMockConfiguration(TestContext testContext) { + return testContext.getApplicationContext().getBean(WireMockConfiguration.class); + } +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/BasicMappingBuilder.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/BasicMappingBuilder.java index a9ebaa1db5..92ced4f27a 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/BasicMappingBuilder.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/BasicMappingBuilder.java @@ -7,6 +7,7 @@ import java.util.UUID; import com.github.tomakehurst.wiremock.client.BasicCredentials; import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; import com.github.tomakehurst.wiremock.client.ScenarioMappingBuilder; +import com.github.tomakehurst.wiremock.common.Metadata; import com.github.tomakehurst.wiremock.extension.Parameters; import com.github.tomakehurst.wiremock.http.Request; import com.github.tomakehurst.wiremock.http.RequestMethod; @@ -142,6 +143,18 @@ class BasicMappingBuilder implements ScenarioMappingBuilder { return this; } + @Override public ScenarioMappingBuilder withMetadata(Map map) { + throw new UnsupportedOperationException("Metadata not supported"); + } + + @Override public ScenarioMappingBuilder withMetadata(Metadata metadata) { + throw new UnsupportedOperationException("Metadata not supported"); + } + + @Override public ScenarioMappingBuilder withMetadata(Metadata.Builder builder) { + throw new UnsupportedOperationException("Metadata not supported"); + } + @Override public StubMapping build() { if (this.scenarioName == null && (this.requiredScenarioState != null || this.newScenarioState != null)) { diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractExchangeHandler.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractExchangeHandler.java index 6e84cd2077..669f8b66f2 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractExchangeHandler.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractExchangeHandler.java @@ -131,6 +131,18 @@ class WireMockHttpRequestAdapter implements Request { return new RequestMethod(this.result.getMethod().name()); } + @Override public String getScheme() { + return this.result.getUrl().getScheme(); + } + + @Override public String getHost() { + return this.result.getUrl().getHost(); + } + + @Override public int getPort() { + return this.result.getUrl().getPort(); + } + @Override public String getClientIp() { return "127.0.0.1"; } @@ -182,7 +194,11 @@ class WireMockHttpRequestAdapter implements Request { return null; } List values = new ArrayList<>(); - for (String name : StringUtils.split(query, "&")) { + String[] strings = StringUtils.split(query, "&"); + if (strings == null) { + return null; + } + for (String name : strings) { if (name.equals(key)) { values.add(""); } diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractResultHandler.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractResultHandler.java index db1f8cbb4b..2c35665f58 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractResultHandler.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractResultHandler.java @@ -16,35 +16,72 @@ package org.springframework.cloud.contract.wiremock.restdocs; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.common.Gzip; +import com.github.tomakehurst.wiremock.http.ContentTypeHeader; +import com.github.tomakehurst.wiremock.http.Cookie; +import com.github.tomakehurst.wiremock.http.HttpHeader; +import com.github.tomakehurst.wiremock.http.HttpHeaders; +import com.github.tomakehurst.wiremock.http.QueryParameter; import com.github.tomakehurst.wiremock.http.Request; -import com.github.tomakehurst.wiremock.servlet.WireMockHttpServletRequestAdapter; - +import com.github.tomakehurst.wiremock.http.RequestMethod; +import com.github.tomakehurst.wiremock.servlet.WireMockHttpServletMultipartAdapter; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultHandler; -import org.springframework.util.StreamUtils; +import wiremock.com.google.common.base.Function; +import wiremock.com.google.common.base.Joiner; +import wiremock.com.google.common.base.Optional; +import wiremock.com.google.common.collect.FluentIterable; +import wiremock.com.google.common.collect.ImmutableList; +import wiremock.com.google.common.collect.ImmutableMultimap; +import wiremock.com.google.common.collect.Maps; +import wiremock.org.eclipse.jetty.util.MultiPartInputStreamParser; -public class ContractResultHandler extends - WireMockVerifyHelper implements ResultHandler { +import static com.github.tomakehurst.wiremock.common.Encoding.encodeBase64; +import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked; +import static com.github.tomakehurst.wiremock.common.Strings.stringFromBytes; +import static com.github.tomakehurst.wiremock.common.Urls.splitQuery; +import static java.util.Collections.list; +import static wiremock.com.google.common.base.Charsets.UTF_8; +import static wiremock.com.google.common.base.MoreObjects.firstNonNull; +import static wiremock.com.google.common.base.Strings.isNullOrEmpty; +import static wiremock.com.google.common.collect.FluentIterable.from; +import static wiremock.com.google.common.collect.Lists.newArrayList; +import static wiremock.com.google.common.io.ByteStreams.toByteArray; + +public class ContractResultHandler + extends WireMockVerifyHelper + implements ResultHandler { static final String ATTRIBUTE_NAME_CONFIGURATION = "org.springframework.restdocs.configuration"; - @Override - public void handle(MvcResult result) throws Exception { + @Override public void handle(MvcResult result) throws Exception { configure(result); MockMvcRestDocumentation.document(getName()).handle(result); } - @Override - protected ResponseDefinitionBuilder getResponseDefinition(MvcResult result) { + @Override protected ResponseDefinitionBuilder getResponseDefinition( + MvcResult result) { MockHttpServletResponse response = result.getResponse(); ResponseDefinitionBuilder definition; try { @@ -66,11 +103,9 @@ public class ContractResultHandler extends } } - @Override - protected Map getConfiguration(MvcResult result) { - @SuppressWarnings("unchecked") - Map map = (Map) result.getRequest() - .getAttribute(ATTRIBUTE_NAME_CONFIGURATION); + @Override protected Map getConfiguration(MvcResult result) { + @SuppressWarnings("unchecked") Map map = (Map) result + .getRequest().getAttribute(ATTRIBUTE_NAME_CONFIGURATION); if (map == null) { map = new HashMap<>(); result.getRequest().setAttribute(ATTRIBUTE_NAME_CONFIGURATION, map); @@ -78,24 +113,272 @@ public class ContractResultHandler extends return map; } - @Override - protected Request getWireMockRequest(MvcResult result) { + @Override protected Request getWireMockRequest(MvcResult result) { return new WireMockHttpServletRequestAdapter(result.getRequest()); } - @Override - protected MediaType getContentType(MvcResult result) { + @Override protected MediaType getContentType(MvcResult result) { return MediaType.valueOf(result.getRequest().getContentType()); } - @Override - protected byte[] getRequestBodyContent(MvcResult result) { - try { - return StreamUtils.copyToByteArray(result.getRequest().getInputStream()); - } - catch (IOException e) { - throw new IllegalStateException("Cannot create request body", e); - } + @Override protected byte[] getRequestBodyContent(MvcResult result) { + return new WireMockHttpServletRequestAdapter(result.getRequest()).getBody(); } } + +// COPIED FROM WIREMOCK +class WireMockHttpServletRequestAdapter implements Request { + + private static final String ORIGINAL_REQUEST_KEY = "wiremock.ORIGINAL_REQUEST"; + + private final HttpServletRequest request; + private byte[] cachedBody; + private Collection cachedMultiparts; + + WireMockHttpServletRequestAdapter(HttpServletRequest request) { + this.request = request; + } + + @Override public String getUrl() { + String url = this.request.getRequestURI(); + String contextPath = this.request.getContextPath(); + if (!isNullOrEmpty(contextPath) && url.startsWith(contextPath)) { + url = url.substring(contextPath.length()); + } + return withQueryStringIfPresent(url); + } + + @Override public String getAbsoluteUrl() { + return withQueryStringIfPresent(this.request.getRequestURL().toString()); + } + + private String withQueryStringIfPresent(String url) { + return url + (isNullOrEmpty(this.request.getQueryString()) ? + "" : + "?" + this.request.getQueryString()); + } + + @Override public RequestMethod getMethod() { + return RequestMethod.fromString(this.request.getMethod().toUpperCase()); + } + + @Override public String getScheme() { + return this.request.getScheme(); + } + + @Override public String getHost() { + return this.request.getServerName(); + } + + @Override public int getPort() { + return this.request.getServerPort(); + } + + @Override public String getClientIp() { + String forwardedForHeader = this.getHeader("X-Forwarded-For"); + + if (forwardedForHeader != null && forwardedForHeader.length() > 0) { + return forwardedForHeader; + } + + return this.request.getRemoteAddr(); + } + + // Something's wrong with reading the body from request + @Override public byte[] getBody() { + if (this.cachedBody == null || this.cachedBody.length == 0) { + try { + if (this.request instanceof MockHttpServletRequest) { + this.cachedBody = ((MockHttpServletRequest) this.request).getContentAsByteArray(); + return this.cachedBody; + } + byte[] body = toByteArray(this.request.getInputStream()); + boolean isGzipped = hasGzipEncoding() || Gzip.isGzipped(body); + this.cachedBody = isGzipped ? Gzip.unGzip(body) : body; + } + catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + return this.cachedBody; + } + + private Charset encodingFromContentTypeHeaderOrUtf8() { + ContentTypeHeader contentTypeHeader = contentTypeHeader(); + if (contentTypeHeader != null) { + return contentTypeHeader.charset(); + } + return UTF_8; + } + + private boolean hasGzipEncoding() { + String encodingHeader = this.request.getHeader("Content-Encoding"); + return encodingHeader != null && encodingHeader.contains("gzip"); + } + + @Override public String getBodyAsString() { + return stringFromBytes(getBody(), encodingFromContentTypeHeaderOrUtf8()); + } + + @Override public String getBodyAsBase64() { + return encodeBase64(getBody()); + } + + @SuppressWarnings("unchecked") @Override public String getHeader(String key) { + List headerNames = list(this.request.getHeaderNames()); + for (String currentKey : headerNames) { + if (currentKey.toLowerCase().equals(key.toLowerCase())) { + return this.request.getHeader(currentKey); + } + } + return null; + } + + @Override @SuppressWarnings("unchecked") public HttpHeader header(String key) { + List headerNames = list(this.request.getHeaderNames()); + for (String currentKey : headerNames) { + if (currentKey.toLowerCase().equals(key.toLowerCase())) { + List valueList = list(this.request.getHeaders(currentKey)); + if (valueList.isEmpty()) { + return HttpHeader.empty(key); + } + return new HttpHeader(key, valueList); + } + } + + return HttpHeader.absent(key); + } + + @Override public ContentTypeHeader contentTypeHeader() { + return getHeaders().getContentTypeHeader(); + } + + @Override public boolean containsHeader(String key) { + return header(key).isPresent(); + } + + @Override public HttpHeaders getHeaders() { + List headerList = newArrayList(); + for (String key : getAllHeaderKeys()) { + headerList.add(header(key)); + } + + return new HttpHeaders(headerList); + } + + @SuppressWarnings("unchecked") @Override public Set getAllHeaderKeys() { + LinkedHashSet headerKeys = new LinkedHashSet<>(); + for (Enumeration headerNames = this.request.getHeaderNames(); headerNames + .hasMoreElements(); ) { + headerKeys.add(headerNames.nextElement()); + } + + return headerKeys; + } + + @Override public Map getCookies() { + ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); + javax.servlet.http.Cookie[] cookies = firstNonNull(this.request.getCookies(), + new javax.servlet.http.Cookie[0]); + for (javax.servlet.http.Cookie cookie : cookies) { + builder.put(cookie.getName(), cookie.getValue()); + } + return Maps.transformValues(builder.build().asMap(), + input -> new Cookie(null, ImmutableList.copyOf(input))); + } + + @Override public QueryParameter queryParameter(String key) { + return firstNonNull((splitQuery(this.request.getQueryString()).get(key)), + QueryParameter.absent(key)); + } + + @Override public boolean isBrowserProxyRequest() { + if (!isJetty()) { + return false; + } + return false; + } + + @Override @SuppressWarnings("unchecked") public Collection getParts() { + if (!isMultipart()) { + return null; + } + if (this.cachedMultiparts == null) { + try { + String contentTypeHeaderValue = FluentIterable + .from(contentTypeHeader().values()).join(Joiner.on(" ")); + InputStream inputStream = new ByteArrayInputStream(getBody()); + MultiPartInputStreamParser inputStreamParser = new MultiPartInputStreamParser( + inputStream, contentTypeHeaderValue, null, null); + this.cachedMultiparts = from(safelyGetRequestParts()) + .transform( + (Function) WireMockHttpServletMultipartAdapter::from).toList(); + } + catch (IOException | ServletException exception) { + return throwUnchecked(exception, Collection.class); + } + } + return (this.cachedMultiparts.size() > 0) ? this.cachedMultiparts : null; + } + + private Collection safelyGetRequestParts() + throws IOException, ServletException { + try { + return this.request.getParts(); + } + catch (IOException ioe) { + if (ioe.getMessage().contains("Missing content for multipart")) { + return Collections.emptyList(); + } + + throw ioe; + } + } + + @Override public boolean isMultipart() { + String header = getHeader("Content-Type"); + return (header != null && header.contains("multipart")); + } + + @Override public Part getPart(final String name) { + if (name == null || name.length() == 0) { + return null; + } + if (this.cachedMultiparts == null) { + if (getParts() == null) { + return null; + } + } + return from(this.cachedMultiparts).firstMatch( + input -> name.equals(input.getName())).get(); + } + + @Override public Optional getOriginalRequest() { + Request originalRequest = (Request) this.request.getAttribute(ORIGINAL_REQUEST_KEY); + return Optional.fromNullable(originalRequest); + } + + private boolean isJetty() { + try { + getClass("org.eclipse.jetty.server.Request"); + return true; + } + catch (Exception e) { + } + return false; + } + + private void getClass(String type) throws ClassNotFoundException { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + ClassLoader loader = contextCL == null ? + com.github.tomakehurst.wiremock.servlet.WireMockHttpServletRequestAdapter.class + .getClassLoader() : + contextCL; + Class.forName(type, false, loader); + } + + @Override public String toString() { + return this.request.toString() + getBodyAsString(); + } +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockVerifyHelper.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockVerifyHelper.java index 9caee5b0b7..1e39534f70 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockVerifyHelper.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockVerifyHelper.java @@ -70,8 +70,8 @@ public abstract class WireMockVerifyHelper org.springframework.cloud spring-cloud-contract-parent - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. diff --git a/tests/samples-messaging-amqp/pom.xml b/tests/samples-messaging-amqp/pom.xml index 6d99636ccb..72208fe766 100644 --- a/tests/samples-messaging-amqp/pom.xml +++ b/tests/samples-messaging-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-sample-amqp diff --git a/tests/samples-messaging-integration/pom.xml b/tests/samples-messaging-integration/pom.xml index e586adc71f..6134fb9a22 100644 --- a/tests/samples-messaging-integration/pom.xml +++ b/tests/samples-messaging-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-sample-integration diff --git a/tests/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy b/tests/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy index a50b850083..b3a5ec34e3 100644 --- a/tests/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy +++ b/tests/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy @@ -25,7 +25,6 @@ import org.springframework.cloud.contract.verifier.messaging.MessageVerifier import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; import org.springframework.messaging.Message -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @@ -35,9 +34,8 @@ import com.jayway.jsonpath.JsonPath import com.toomuchcoding.jsonassert.JsonAssertion // Context configuration would end up in base class @ContextConfiguration(classes = [IntegrationMessagingApplication], loader = SpringBootContextLoader) -@DirtiesContext @AutoConfigureMessageVerifier -public class IntegrationMessagingApplicationSpec extends Specification { +class IntegrationMessagingApplicationSpec extends Specification { // ALL CASES @Inject MessageVerifier> contractVerifierMessaging diff --git a/tests/samples-messaging-spring/pom.xml b/tests/samples-messaging-spring/pom.xml index 79a18e3fcd..476a4ddec5 100644 --- a/tests/samples-messaging-spring/pom.xml +++ b/tests/samples-messaging-spring/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-sample-spring diff --git a/tests/samples-messaging-spring/src/test/groovy/com/example/SpringApplicationSpec.groovy b/tests/samples-messaging-spring/src/test/groovy/com/example/SpringApplicationSpec.groovy index fcb16bc994..402fd0e8dd 100644 --- a/tests/samples-messaging-spring/src/test/groovy/com/example/SpringApplicationSpec.groovy +++ b/tests/samples-messaging-spring/src/test/groovy/com/example/SpringApplicationSpec.groovy @@ -25,8 +25,7 @@ import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.verifier.messaging.MessageVerifier import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.messaging.Message; -import org.springframework.test.annotation.DirtiesContext +import org.springframework.messaging.Message import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @@ -40,9 +39,8 @@ import com.toomuchcoding.jsonassert.JsonAssertion */ // Context configuration would end up in base class @ContextConfiguration(classes = [SpringMessagingApplication], loader = SpringBootContextLoader) -@DirtiesContext @AutoConfigureMessageVerifier -public class SpringApplicationSpec extends Specification { +class SpringApplicationSpec extends Specification { // ALL CASES @Inject MessageVerifier> contractVerifierMessaging diff --git a/tests/samples-messaging-stream/pom.xml b/tests/samples-messaging-stream/pom.xml index 9ad4fa8a07..e3ec0afcc2 100644 --- a/tests/samples-messaging-stream/pom.xml +++ b/tests/samples-messaging-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-sample-stream diff --git a/tests/samples-messaging-stream/src/test/groovy/com/example/StreamMessagingApplicationSpec.groovy b/tests/samples-messaging-stream/src/test/groovy/com/example/StreamMessagingApplicationSpec.groovy index 71f804412d..ca305ee7ee 100644 --- a/tests/samples-messaging-stream/src/test/groovy/com/example/StreamMessagingApplicationSpec.groovy +++ b/tests/samples-messaging-stream/src/test/groovy/com/example/StreamMessagingApplicationSpec.groovy @@ -27,7 +27,6 @@ import org.springframework.cloud.contract.verifier.messaging.MessageVerifier import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper import org.springframework.messaging.Message -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @@ -37,10 +36,9 @@ import javax.inject.Inject */ // Context configuration would end up in base class @ContextConfiguration(classes = [StreamMessagingApplication], loader = SpringBootContextLoader) -@DirtiesContext @SpringBootTest(properties = "debug=true") @AutoConfigureMessageVerifier -public class StreamMessagingApplicationSpec extends Specification { +class StreamMessagingApplicationSpec extends Specification { // ALL CASES @Inject MessageVerifier> contractVerifierMessaging diff --git a/tests/spring-cloud-contract-stub-runner-amqp/README.adoc b/tests/spring-cloud-contract-stub-runner-amqp/README.adoc index ab5fb80b95..033e4fa094 100644 --- a/tests/spring-cloud-contract-stub-runner-amqp/README.adoc +++ b/tests/spring-cloud-contract-stub-runner-amqp/README.adoc @@ -20,6 +20,9 @@ match this exchange. Then it collects the queues from the Spring exchanges and t find message listeners bound to these queues. The message is triggered for all matching message listeners. +If you need to work with routing keys, it's enough to pass them via the `amqp_receivedRoutingKey` +messaging header. + ==== Adding the Runner to the Project You can have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and diff --git a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml index 19e2e84ac2..5ecf019a23 100644 --- a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-amqp diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml index bd05d85268..3ac791fac9 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot-eureka diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy index 5bb90fd8a8..076628cb8a 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy +++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy @@ -32,7 +32,6 @@ import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.HttpMethod import org.springframework.http.client.ClientHttpResponse -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import org.springframework.web.client.* import spock.lang.AutoCleanup @@ -58,7 +57,6 @@ import spock.util.concurrent.PollingConditions "org.springframework.cloud.contract.verifier.stubs:bootService"], repositoryRoot = "classpath:m2repo/repository/", stubsMode = StubRunnerProperties.StubsMode.REMOTE) -@DirtiesContext class StubRunnerSpringCloudEurekaAutoConfigurationSpec extends Specification { @Autowired StubFinder stubFinder diff --git a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml index dc43e82d6d..a52d733f07 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot-zookeeper diff --git a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/zookeeper/StubRunnerSpringCloudZookeeperAutoConfigurationSpec.groovy b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/zookeeper/StubRunnerSpringCloudZookeeperAutoConfigurationSpec.groovy index 42b5574a45..a1ead5b9d7 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/zookeeper/StubRunnerSpringCloudZookeeperAutoConfigurationSpec.groovy +++ b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/zookeeper/StubRunnerSpringCloudZookeeperAutoConfigurationSpec.groovy @@ -32,7 +32,6 @@ import org.springframework.cloud.zookeeper.ZookeeperProperties import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import org.springframework.util.SocketUtils import org.springframework.web.client.RestTemplate @@ -51,7 +50,6 @@ import spock.lang.Specification "org.springframework.cloud.contract.verifier.stubs:bootService"], repositoryRoot = "classpath:m2repo/repository/", stubsMode = StubRunnerProperties.StubsMode.REMOTE) -@DirtiesContext class StubRunnerSpringCloudZookeeperAutoConfigurationSpec extends Specification { @Autowired StubFinder stubFinder diff --git a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml index fb29de0bb2..70f469f621 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-context-path diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java index 821ecf0878..f3ed2d3174 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java +++ b/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -10,7 +10,6 @@ import org.springframework.boot.web.server.LocalServerPort; import org.springframework.cloud.contract.stubrunner.StubFinder; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestTemplate; @@ -27,7 +26,6 @@ import static org.assertj.core.api.Assertions.assertThat; @AutoConfigureStubRunner(repositoryRoot = "classpath:m2repo/repository/", ids = { "org.springframework.cloud.contract.verifier.stubs:contextPathFraudDetectionServer" }, stubsMode = StubRunnerProperties.StubsMode.REMOTE) -@DirtiesContext public class LoanApplicationServiceTests { // end::autoconfigure_stubrunner[] diff --git a/tests/spring-cloud-contract-stub-runner-integration/README.adoc b/tests/spring-cloud-contract-stub-runner-integration/README.adoc index baba7e3ab3..ccae044b9a 100644 --- a/tests/spring-cloud-contract-stub-runner-integration/README.adoc +++ b/tests/spring-cloud-contract-stub-runner-integration/README.adoc @@ -102,7 +102,7 @@ The received message would pass the following assertions: include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] ---- -[[integration-scenario-1]] +[[integration-scenario-2]] ===== Scenario 2 (output triggered by input) Since the route is set for you, you can send a message to the `{output_name}` diff --git a/tests/spring-cloud-contract-stub-runner-integration/pom.xml b/tests/spring-cloud-contract-stub-runner-integration/pom.xml index ed4fe4a69b..2c1ca90901 100644 --- a/tests/spring-cloud-contract-stub-runner-integration/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-integration diff --git a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml index 40a8270173..c2b6a8b8e6 100644 --- a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-moco-contract-jar diff --git a/tests/spring-cloud-contract-stub-runner-moco/pom.xml b/tests/spring-cloud-contract-stub-runner-moco/pom.xml index 536e2b67d4..9b132ee2d5 100644 --- a/tests/spring-cloud-contract-stub-runner-moco/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-moco diff --git a/tests/spring-cloud-contract-stub-runner-moco/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/moco/MocoHttpServerStubSpec.groovy b/tests/spring-cloud-contract-stub-runner-moco/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/moco/MocoHttpServerStubSpec.groovy index 69550ed7be..0289147173 100644 --- a/tests/spring-cloud-contract-stub-runner-moco/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/moco/MocoHttpServerStubSpec.groovy +++ b/tests/spring-cloud-contract-stub-runner-moco/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/moco/MocoHttpServerStubSpec.groovy @@ -29,7 +29,6 @@ import org.springframework.cloud.stream.messaging.Sink import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.stereotype.Component -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @@ -43,7 +42,6 @@ import spock.lang.Specification @AutoConfigureStubRunner( ids = ["com.example:fraudDetectionServerMoco"], stubsMode = StubRunnerProperties.StubsMode.CLASSPATH) // end::[classpath_stub_runner] -@DirtiesContext @ActiveProfiles("test") class MocoHttpServerStubSpec extends Specification { diff --git a/tests/spring-cloud-contract-stub-runner-stream/pom.xml b/tests/spring-cloud-contract-stub-runner-stream/pom.xml index b514f41d0d..86f60e482d 100644 --- a/tests/spring-cloud-contract-stub-runner-stream/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-stream diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy index 2ee278ee21..32a71d28db 100644 --- a/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy @@ -58,107 +58,107 @@ class StreamStubRunnerSpec extends Specification { def 'should download the stub and register a route for it'() { when: - // tag::client_send[] - messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage') - // end::client_send[] + // tag::client_send[] + messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage') + // end::client_send[] then: - // tag::client_receive[] - Message receivedMessage = messaging.receive('returnBook') - // end::client_receive[] + // tag::client_receive[] + Message receivedMessage = messaging.receive('returnBook') + // end::client_receive[] and: - // tag::client_receive_message[] - receivedMessage != null - assertJsons(receivedMessage.payload) - receivedMessage.headers.get('BOOK-NAME') == 'foo' - // end::client_receive_message[] + // tag::client_receive_message[] + receivedMessage != null + assertJsons(receivedMessage.payload) + receivedMessage.headers.get('BOOK-NAME') == 'foo' + // end::client_receive_message[] } def 'should trigger a message by label'() { when: - // tag::client_trigger[] - stubFinder.trigger('return_book_1') - // end::client_trigger[] + // tag::client_trigger[] + stubFinder.trigger('return_book_1') + // end::client_trigger[] then: - // tag::client_trigger_receive[] - Message receivedMessage = messaging.receive('returnBook') - // end::client_trigger_receive[] + // tag::client_trigger_receive[] + Message receivedMessage = messaging.receive('returnBook') + // end::client_trigger_receive[] and: - // tag::client_trigger_message[] - receivedMessage != null - assertJsons(receivedMessage.payload) - receivedMessage.headers.get('BOOK-NAME') == 'foo' - // end::client_trigger_message[] + // tag::client_trigger_message[] + receivedMessage != null + assertJsons(receivedMessage.payload) + receivedMessage.headers.get('BOOK-NAME') == 'foo' + // end::client_trigger_message[] } def 'should trigger a label for the existing groupId:artifactId'() { when: - // tag::trigger_group_artifact[] - stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:streamService', 'return_book_1') - // end::trigger_group_artifact[] + // tag::trigger_group_artifact[] + stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:streamService', 'return_book_1') + // end::trigger_group_artifact[] then: - Message receivedMessage = messaging.receive('returnBook') + Message receivedMessage = messaging.receive('returnBook') and: - receivedMessage != null - assertJsons(receivedMessage.payload) - receivedMessage.headers.get('BOOK-NAME') == 'foo' + receivedMessage != null + assertJsons(receivedMessage.payload) + receivedMessage.headers.get('BOOK-NAME') == 'foo' } def 'should trigger a label for the existing artifactId'() { when: - // tag::trigger_artifact[] - stubFinder.trigger('streamService', 'return_book_1') - // end::trigger_artifact[] + // tag::trigger_artifact[] + stubFinder.trigger('streamService', 'return_book_1') + // end::trigger_artifact[] then: - Message receivedMessage = messaging.receive('returnBook') + Message receivedMessage = messaging.receive('returnBook') and: - receivedMessage != null - assertJsons(receivedMessage.payload) - receivedMessage.headers.get('BOOK-NAME') == 'foo' + receivedMessage != null + assertJsons(receivedMessage.payload) + receivedMessage.headers.get('BOOK-NAME') == 'foo' } def 'should throw exception when missing label is passed'() { when: - stubFinder.trigger('missing label') + stubFinder.trigger('missing label') then: - thrown(IllegalArgumentException) + thrown(IllegalArgumentException) } def 'should throw exception when missing label and artifactid is passed'() { when: - stubFinder.trigger('some:service', 'return_book_1') + stubFinder.trigger('some:service', 'return_book_1') then: - thrown(IllegalArgumentException) + thrown(IllegalArgumentException) } def 'should trigger messages by running all triggers'() { when: - // tag::trigger_all[] - stubFinder.trigger() - // end::trigger_all[] + // tag::trigger_all[] + stubFinder.trigger() + // end::trigger_all[] then: - Message receivedMessage = messaging.receive('returnBook') + Message receivedMessage = messaging.receive('returnBook') and: - receivedMessage != null - assertJsons(receivedMessage.payload) - receivedMessage.headers.get('BOOK-NAME') == 'foo' + receivedMessage != null + assertJsons(receivedMessage.payload) + receivedMessage.headers.get('BOOK-NAME') == 'foo' } def 'should trigger a label with no output message'() { when: - // tag::trigger_no_output[] - messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete') - // end::trigger_no_output[] + // tag::trigger_no_output[] + messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete') + // end::trigger_no_output[] then: - noExceptionThrown() + noExceptionThrown() } def 'should not trigger a message that does not match input'() { when: - messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'bookStorage') + messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'bookStorage') then: - Message receivedMessage = messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS) + Message receivedMessage = messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS) and: - receivedMessage == null + receivedMessage == null } private boolean assertJsons(Object payload) { @@ -170,52 +170,52 @@ class StreamStubRunnerSpec extends Specification { } Contract dsl = - // tag::sample_dsl[] - Contract.make { - label 'return_book_1' - input { triggeredBy('bookReturnedTriggered()') } - outputMessage { - sentTo('returnBook') - body('''{ "bookName" : "foo" }''') - headers { header('BOOK-NAME', 'foo') } - } - } + // tag::sample_dsl[] + Contract.make { + label 'return_book_1' + input { triggeredBy('bookReturnedTriggered()') } + outputMessage { + sentTo('returnBook') + body('''{ "bookName" : "foo" }''') + headers { header('BOOK-NAME', 'foo') } + } + } // end::sample_dsl[] Contract dsl2 = - // tag::sample_dsl_2[] - Contract.make { - label 'return_book_2' - input { - messageFrom('bookStorage') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { header('sample', 'header') } - } - outputMessage { - sentTo('returnBook') - body([ - bookName: 'foo' - ]) - headers { header('BOOK-NAME', 'foo') } - } - } + // tag::sample_dsl_2[] + Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { header('sample', 'header') } + } + outputMessage { + sentTo('returnBook') + body([ + bookName: 'foo' + ]) + headers { header('BOOK-NAME', 'foo') } + } + } // end::sample_dsl_2[] Contract dsl3 = - // tag::sample_dsl_3[] - Contract.make { - label 'delete_book' - input { - messageFrom('delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { header('sample', 'header') } - assertThat('bookWasDeleted()') - } - } + // tag::sample_dsl_3[] + Contract.make { + label 'delete_book' + input { + messageFrom('delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { header('sample', 'header') } + assertThat('bookWasDeleted()') + } + } // end::sample_dsl_3[]