From 07befc839fc3ba525c0517e883bb9a42b83a3132 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 19 Jul 2016 15:56:32 +0100 Subject: [PATCH] Re-instate integration tests for stub runner messaging --- pom.xml | 1 + .../camel/StubRunnerCamelConfiguration.java | 2 + .../StubRunnerIntegrationConfiguration.java | 2 + .../stream/StubRunnerStreamConfiguration.java | 7 + .../spring/StubRunnerConfigurationSpec.groovy | 2 +- ...nerSpringCloudAutoConfigurationSpec.groovy | 4 +- tests/pom.xml | 37 +++ .../README.adoc | 123 +++++++++ .../pom.xml | 94 +++++++ .../messaging/camel/BookReturned.groovy | 32 +++ .../camel/CamelStubRunnerSpec.groovy | 250 ++++++++++++++++++ .../camel/StubRunnerCamelProcessorSpec.groovy | 98 +++++++ .../src/test/resources/application.yml | 2 + .../camelService-0.0.1-SNAPSHOT-stubs.jar | Bin 0 -> 1773 bytes .../camelService-0.0.1-SNAPSHOT.pom | 25 ++ .../0.0.1-SNAPSHOT/maven-metadata-local.xml | 28 ++ .../camelService/maven-metadata-local.xml | 28 ++ .../stubs/camelService/maven-metadata.xml | 28 ++ .../.jdk8 | 0 .../README.adoc | 131 +++++++++ .../pom.xml | 82 ++++++ .../messaging/integration/BookReturned.groovy | 32 +++ .../IntegrationStubRunnerSpec.groovy | 229 ++++++++++++++++ ...tubRunnerIntegrationTransformerSpec.groovy | 95 +++++++ .../src/test/resources/application.yml | 2 + .../test/resources/integration-context.xml | 35 +++ ...ntegrationService-0.0.1-SNAPSHOT-stubs.jar | Bin 0 -> 1762 bytes .../integrationService-0.0.1-SNAPSHOT.pom | 25 ++ .../0.0.1-SNAPSHOT/maven-metadata-local.xml | 28 ++ .../maven-metadata-local.xml | 28 ++ .../integrationService/maven-metadata.xml | 28 ++ .../README.adoc | 132 +++++++++ .../pom.xml | 85 ++++++ .../messaging/stream/BookReturned.groovy | 32 +++ .../stream/StreamStubRunnerSpec.groovy | 227 ++++++++++++++++ .../StubRunnerStreamTransformerSpec.groovy | 132 +++++++++ .../src/test/resources/application.yml | 11 + .../0.0.1-SNAPSHOT/maven-metadata-local.xml | 28 ++ .../streamService-0.0.1-SNAPSHOT-stubs.jar | Bin 0 -> 1769 bytes .../streamService-0.0.1-SNAPSHOT.pom | 25 ++ .../streamService/maven-metadata-local.xml | 28 ++ .../stubs/streamService/maven-metadata.xml | 28 ++ 42 files changed, 2203 insertions(+), 3 deletions(-) create mode 100644 tests/pom.xml create mode 100644 tests/spring-cloud-contract-stub-runner-camel/README.adoc create mode 100644 tests/spring-cloud-contract-stub-runner-camel/pom.xml create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/BookReturned.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessorSpec.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/resources/application.yml create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/camelService-0.0.1-SNAPSHOT-stubs.jar create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/camelService-0.0.1-SNAPSHOT.pom create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/maven-metadata-local.xml create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata-local.xml create mode 100644 tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata.xml create mode 100644 tests/spring-cloud-contract-stub-runner-integration/.jdk8 create mode 100644 tests/spring-cloud-contract-stub-runner-integration/README.adoc create mode 100644 tests/spring-cloud-contract-stub-runner-integration/pom.xml create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/BookReturned.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformerSpec.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/application.yml create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/integration-context.xml create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT-stubs.jar create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT.pom create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/maven-metadata-local.xml create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata-local.xml create mode 100644 tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata.xml create mode 100644 tests/spring-cloud-contract-stub-runner-stream/README.adoc create mode 100644 tests/spring-cloud-contract-stub-runner-stream/pom.xml create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/BookReturned.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformerSpec.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/resources/application.yml create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/maven-metadata-local.xml create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT-stubs.jar create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT.pom create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata-local.xml create mode 100644 tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata.xml diff --git a/pom.xml b/pom.xml index 5913b04342..be51896375 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ spring-cloud-contract-stub-runner spring-cloud-contract-starters spring-cloud-contract-tools + tests samples diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelConfiguration.java index d7a5e821f3..538fec5a70 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelConfiguration.java @@ -22,6 +22,7 @@ import java.util.Map; import org.apache.camel.RoutesBuilder; import org.apache.camel.spring.SpringRouteBuilder; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.stubrunner.BatchStubRunner; import org.springframework.cloud.contract.stubrunner.StubConfiguration; @@ -36,6 +37,7 @@ import org.springframework.context.annotation.Configuration; */ @Configuration @ConditionalOnClass(RoutesBuilder.class) +@ConditionalOnProperty(name="stubrunner.camel.enabled", havingValue="true", matchIfMissing=true) public class StubRunnerCamelConfiguration { @Bean diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.java index 78f74d7dc0..e16ec51b05 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.java @@ -23,6 +23,7 @@ import java.util.Map.Entry; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.stubrunner.BatchStubRunner; import org.springframework.cloud.contract.stubrunner.StubConfiguration; @@ -45,6 +46,7 @@ import org.springframework.messaging.Message; */ @Configuration @ConditionalOnClass(IntegrationFlowBuilder.class) +@ConditionalOnProperty(name="stubrunner.integration.enabled", havingValue="true", matchIfMissing=true) public class StubRunnerIntegrationConfiguration { @Bean diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamConfiguration.java index 9c715c4bc6..e2c9f89431 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamConfiguration.java @@ -23,11 +23,15 @@ import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.stubrunner.BatchStubRunner; import org.springframework.cloud.contract.stubrunner.StubConfiguration; +import org.springframework.cloud.contract.stubrunner.messaging.integration.StubRunnerIntegrationConfiguration; import org.springframework.cloud.contract.stubrunner.messaging.stream.StubRunnerStreamConfiguration.FlowRegistrar; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.config.BindingProperties; @@ -52,6 +56,8 @@ import org.springframework.util.StringUtils; */ @Configuration @ConditionalOnClass({FlowRegistrar.class, EnableBinding.class}) +@ConditionalOnProperty(name="stubrunner.stream.enabled", havingValue="true", matchIfMissing=true) +@AutoConfigureBefore(StubRunnerIntegrationConfiguration.class) public class StubRunnerStreamConfiguration { private static final Logger log = LoggerFactory @@ -59,6 +65,7 @@ public class StubRunnerStreamConfiguration { @Bean @ConditionalOnMissingBean(name="stubFlowRegistrar") + @ConditionalOnBean(ChannelBindingServiceProperties.class) public FlowRegistrar stubFlowRegistrar(AutowireCapableBeanFactory beanFactory, BatchStubRunner batchStubRunner) { Map> contracts = batchStubRunner 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 2460562296..292411d0b8 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 @@ -35,7 +35,7 @@ import spock.lang.Specification // tag::test[] @ContextConfiguration(classes = Config, loader = SpringBootContextLoader) // Not necessary if Spring Cloud is used. TODO: make it work without this. -@IntegrationTest("stubrunner.cloud.enabled=false") +@IntegrationTest(["stubrunner.cloud.enabled=false", "stubrunner.camel.enabled=false"]) @AutoConfigureStubRunner @DirtiesContext 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 99667475c3..de4fb64f54 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 @@ -45,7 +45,7 @@ import spock.lang.Specification */ @ContextConfiguration(classes = Config, loader = SpringBootContextLoader) @WebIntegrationTest(randomPort = true) -@IntegrationTest +@IntegrationTest("stubrunner.camel.enabled=false") @AutoConfigureStubRunner @DirtiesContext class StubRunnerSpringCloudAutoConfigurationSpec extends Specification { @@ -77,7 +77,7 @@ class StubRunnerSpringCloudAutoConfigurationSpec extends Specification { } def cleanup() { - zookeeperServiceDiscovery.serviceDiscovery.close() + zookeeperServiceDiscovery?.serviceDiscovery?.close() } @Configuration diff --git a/tests/pom.xml b/tests/pom.xml new file mode 100644 index 0000000000..df55cadd69 --- /dev/null +++ b/tests/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + org.springframework.cloud + spring-cloud-contract-parent + 1.0.0.BUILD-SNAPSHOT + .. + + + spring-cloud-contract-tests + pom + + Spring Cloud Contract Tests + Spring Cloud Contract Tests + + + spring-cloud-contract-stub-runner-camel + spring-cloud-contract-stub-runner-integration + spring-cloud-contract-stub-runner-stream + + + + + + maven-deploy-plugin + + true + + + + + + diff --git a/tests/spring-cloud-contract-stub-runner-camel/README.adoc b/tests/spring-cloud-contract-stub-runner-camel/README.adoc new file mode 100644 index 0000000000..0a3ef49f3a --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/README.adoc @@ -0,0 +1,123 @@ +:input_name: jms:input +:output_name: jms:output + +=== Stub Runner Camel + +Spring Cloud Contract Verifier Stub Runner's messaging module gives you an easy way to integrate with Apache Camel. +For the provided artifacts it will automatically download the stubs and register the required +routes. + +==== Adding it to the project + +To use it you have to add the following dependency to your project (example for Gradle): + +[source,groovy,indent=0] +---- +testCompile "org.springframework.cloud:stub-runner-camel:${verifierVersion}" +---- + +==== Examples + +===== Stubs structure + +Let us assume that we have the following Maven repository with a deployed stubs for the +`camelService` application. + +[source,bash,indent=0] +---- +└── .m2 + └── repository + └── io + └── codearte + └── accurest + └── stubs + └── camelService + ├── 0.0.1-SNAPSHOT + │   ├── camelService-0.0.1-SNAPSHOT.pom + │   ├── camelService-0.0.1-SNAPSHOT-stubs.jar + │   └── maven-metadata-local.xml + └── maven-metadata-local.xml +---- + +And the stubs contain the following structure: + +[source,bash,indent=0] +---- +├── META-INF +│   └── MANIFEST.MF +└── repository + ├── accurest + │   ├── bookDeleted.groovy + │   ├── bookReturned1.groovy + │   └── bookReturned2.groovy + └── mappings +---- + +Let's consider the following contracts (let' number it with *1*): + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl,indent=0] +---- + +and number *2* + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] +---- + +===== Scenario 1 (no input message) + +So as to trigger a message via the `return_book_1` label we'll use the `StubTigger` interface as follows + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger,indent=0] +---- + +Next we'll want to listen to the output of the message sent to `{output_name}` + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] +---- + +And the received message would pass the following assertions + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] +---- + +===== Scenario 2 (output triggered by input) + +Since the route is set for you it's enough to just send a message to the `{output_name}` destination. + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_send,indent=0] +---- + +Next we'll want to listen to the output of the message sent to `{output_name}` + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive,indent=0] +---- + +And the received message would pass the following assertions + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive_message,indent=0] +---- + +===== Scenario 3 (input with no output) + +Since the route is set for you it's enough to just send a message to the `{output_name}` destination. + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] +---- diff --git a/tests/spring-cloud-contract-stub-runner-camel/pom.xml b/tests/spring-cloud-contract-stub-runner-camel/pom.xml new file mode 100644 index 0000000000..82ded95784 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-contract-stub-runner-build + 1.0.0.BUILD-SNAPSHOT + .. + + spring-cloud-contract-stub-runner-camel + jar + Spring Cloud Contract Stub Runner Camel + Spring Cloud Contract Stub Runner Camel + + + org.springframework.cloud + spring-cloud-contract-stub-runner + test + + + org.springframework.cloud + spring-cloud-contract-stub-runner-jetty + test + + + org.apache.camel + camel-spring-boot-starter + + + org.apache.camel + camel-jackson + + + junit + junit + test + + + org.codehaus.groovy + groovy + + + org.spockframework + spock-core + test + + + org.spockframework + spock-spring + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.apache.activemq + activemq-camel + test + + + org.apache.activemq + activemq-pool + test + + + org.springframework.boot + spring-boot-starter-web + test + + + info.solidsoft.spock + spock-global-unroll + test + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + testCompile + + + + + + + diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/BookReturned.groovy b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/BookReturned.groovy new file mode 100644 index 0000000000..72a689a659 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/BookReturned.groovy @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.messaging.camel + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode + +@CompileStatic +@EqualsAndHashCode +class BookReturned implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy new file mode 100644 index 0000000000..fa6a8a5922 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy @@ -0,0 +1,250 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.messaging.camel + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper + +import org.apache.activemq.camel.component.ActiveMQComponent +import org.apache.activemq.spring.ActiveMQConnectionFactory +import org.apache.camel.CamelContext +import org.apache.camel.Exchange +import org.apache.camel.component.jms.JmsConfiguration +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.IntegrationTest +import org.springframework.boot.test.context.SpringBootContextLoader +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.stubrunner.StubFinder +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.test.context.ContextConfiguration + +import spock.lang.Specification + +/** + * @author Marcin Grzejszczak + */ +@ContextConfiguration(classes = Config, loader = SpringBootContextLoader) +@IntegrationTest("debug=true") +@AutoConfigureStubRunner +class CamelStubRunnerSpec extends Specification { + + @Autowired StubFinder stubFinder + @Autowired CamelContext camelContext + + def setup() { + // ensure that message were taken from the queue + camelContext.createConsumerTemplate().receive('jms:output', 100) + } + + def 'should download the stub and register a route for it'() { + when: + // tag::client_send[] + camelContext.createProducerTemplate().sendBodyAndHeaders('jms:input', new BookReturned('foo'), [sample: 'header']) + // end::client_send[] + then: + // tag::client_receive[] + Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000) + // end::client_receive[] + and: + // tag::client_receive_message[] + receivedMessage != null + assertThatBodyContainsBookNameFoo(receivedMessage.in.body) + receivedMessage.in.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[] + then: + // tag::client_trigger_receive[] + Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000) + // end::client_trigger_receive[] + and: + // tag::client_trigger_message[] + receivedMessage != null + assertThatBodyContainsBookNameFoo(receivedMessage.in.body) + receivedMessage.in.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:camelService', 'return_book_1') + // end::trigger_group_artifact[] + then: + Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000) + and: + receivedMessage != null + assertThatBodyContainsBookNameFoo(receivedMessage.in.body) + receivedMessage.in.headers.get('BOOK-NAME') == 'foo' + } + + def 'should trigger a label for the existing artifactId'() { + when: + // tag::trigger_artifact[] + stubFinder.trigger('camelService', 'return_book_1') + // end::trigger_artifact[] + then: + Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000) + and: + receivedMessage != null + assertThatBodyContainsBookNameFoo(receivedMessage.in.body) + receivedMessage.in.headers.get('BOOK-NAME') == 'foo' + } + + def 'should throw an exception when missing label is passed'() { + when: + stubFinder.trigger('missing label') + then: + thrown(IllegalArgumentException) + } + + def 'should throw an exception when missing label and artifactid is passed'() { + when: + stubFinder.trigger('some:service', 'return_book_1') + then: + thrown(IllegalArgumentException) + } + + def 'should trigger messages by running all triggers'() { + when: + // tag::trigger_all[] + stubFinder.trigger() + // end::trigger_all[] + then: + Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000) + and: + receivedMessage != null + assertThatBodyContainsBookNameFoo(receivedMessage.in.body) + receivedMessage.in.headers.get('BOOK-NAME') == 'foo' + } + + def 'should trigger a label with no output message'() { + when: + // tag::trigger_no_output[] + camelContext.createProducerTemplate().sendBodyAndHeaders('jms:delete', new BookReturned('foo'), [sample: 'header']) + // end::trigger_no_output[] + then: + noExceptionThrown() + } + + def 'should not trigger a message that does not match input'() { + when: + camelContext.createProducerTemplate().sendBodyAndHeaders('jms:input', new BookReturned('notmatching'), [wrong: 'header_value']) + then: + Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 100) + and: + receivedMessage == null + } + + private boolean assertThatBodyContainsBookNameFoo(Object payload) { + String objectAsString = payload instanceof String ? payload : + JsonOutput.toJson(payload) + def json = new JsonSlurper().parseText(objectAsString) + return json.bookName == 'foo' + } + + @Configuration + @ComponentScan + @EnableAutoConfiguration + static class Config { + + @Bean + ActiveMQConnectionFactory activeMQConnectionFactory(@Value('${activemq.url:vm://localhost?broker.persistent=false}') String url) { + return new ActiveMQConnectionFactory(brokerURL: url, trustAllPackages: true) + } + + @Bean + JmsConfiguration jmsConfiguration(ActiveMQConnectionFactory activeMQConnectionFactory) { + return new JmsConfiguration(connectionFactory: activeMQConnectionFactory) + } + + @Bean + ActiveMQComponent activeMQComponent(JmsConfiguration jmsConfiguration) { + return new ActiveMQComponent(configuration: jmsConfiguration) + } + } + + + Contract dsl = + // tag::sample_dsl[] + Contract.make { + label 'return_book_1' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('jms:output') + 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('jms:input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('jms:output') + 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('jms:delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + // end::sample_dsl_3[] +} diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessorSpec.groovy b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessorSpec.groovy new file mode 100644 index 0000000000..279782c65e --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessorSpec.groovy @@ -0,0 +1,98 @@ +package org.springframework.cloud.contract.stubrunner.messaging.camel + +import org.apache.camel.CamelContext +import org.apache.camel.Exchange +import org.apache.camel.builder.ExchangeBuilder +import org.apache.camel.spring.SpringCamelContext +import org.springframework.cloud.contract.spec.Contract +import spock.lang.Specification + +class StubRunnerCamelProcessorSpec extends Specification { + + CamelContext camelContext = new SpringCamelContext() + Exchange message = ExchangeBuilder.anExchange(camelContext).build() + + def noOutputMessageContract = Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookId: $(consumer(regex('[0-9]+')), producer('123')) + ]) + messageHeaders { + header('sample', 'header') + } + } + } + + def 'should not process the message if there is no output message'() { + given: + StubRunnerCamelProcessor processor = new StubRunnerCamelProcessor(noOutputMessageContract) + when: + processor.process(message) + then: + noExceptionThrown() + } + + def dsl = Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookId: $(consumer(regex('[0-9]+')), producer('123')) + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('returnBook') + body([ + responseId: $(producer(regex('[0-9]+')), consumer('123')) + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + def 'should process message when it has an output message section'() { + given: + StubRunnerCamelProcessor processor = new StubRunnerCamelProcessor(dsl) + when: + processor.process(message) + then: + message.getIn().getBody(String) == '{"responseId":"123"}' + } + + def dslWithRegexInGString = Contract.make { + // Human readable description + description 'Should produce valid sensor data' + // Label by means of which the output message can be triggered + label 'sensor1' + // input to the contract + input { + // the contract will be triggered by a method + triggeredBy('createSensorData()') + } + // output message of the contract + outputMessage { + // destination to which the output message will be sent + sentTo 'sensor-data' + headers { + header('contentType': 'application/json') + } + // the body of the output message + body("""{"id":"${value(producer(regex('[0-9]+')), consumer('99'))}","temperature":"123.45"}""") + } + } + + def 'should convert dsl into message with regex in GString'() { + given: + StubRunnerCamelProcessor processor = new StubRunnerCamelProcessor(dslWithRegexInGString) + when: + processor.process(message) + then: + message.getIn().getBody(String) == '''{"id":"99","temperature":"123.45"}''' + } +} diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/application.yml b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/application.yml new file mode 100644 index 0000000000..4eef5ea578 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/application.yml @@ -0,0 +1,2 @@ +stubrunner.stubs.repositoryRoot: classpath:m2repo/repository/ +stubrunner.stubs.ids: org.springframework.cloud.contract.verifier.stubs:camelService \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/camelService-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/camelService-0.0.1-SNAPSHOT-stubs.jar new file mode 100644 index 0000000000000000000000000000000000000000..b27b155a3eeb01c61674ce74417991e9ce662191 GIT binary patch literal 1773 zcmWIWW@h1HVBp|j&|{wF!2kqIAOZ+Df!NnI#8KDN&rP41Apk|;rh2A#(m(~0KrDi+ z(AUw=)6F$FM9W7$;qWfsl_EwJs?>QhW@s@9wxibJlF-~F*7nSXfViN)svK; zpBvJATF)oW?*Fb4m5%VWU^`BL9RmvJPzmY zhEIt#a%7L&yRu@E<%GLSqm$>}mh!ZAN%@~={Z-=LY3~l-cXD^4ic?J=Ox|&N_B^+_ z<|($TkFodo?)&fG{3>9d@Ws_q)to(bDb*`Gcb%3Jx_ImO3uW22tNA$%Uqv9BgE&CSnylcK)lNsXbYjuR-o>A}7;2#!KrDw)<23fKS zES1*kee>*G{)vR}X~(Z-M$05q?e~OY1?w~+fSxU77_e-*EeVnPt$~_ z=fB7E1xdc{%L)}!QHhy2%OZQ@`Nup{6(>q|D|U{`+vM>7CMuE@nY7on7Z`u>5BRSx%}3(lGhcA6y9b> zK6O2^bwVt6jm)Qq$Ib1TzHcxu%d&2%^=dX^mw6|#V*B!X{S|e->c9V{ZJM1wJ@4+$ z`yU#$%j?s@ zOKx*}$Q=lBU9*OTTkz%+Gy7+H(s}+aD(~N^I&QgYA%T_$a}x^+GV{`l!FiC8NrV}9(EjkZM?&1}a@3K!D+|V;PW%RDj^q2=XTcfHW=y zGLbaGOCFF3a?Ft2g<0}QfDHg2}* zB4-0_1LbTywqfQ8WZT4 + + + + 4.0.0 + org.springframework.cloud.contract.verifier.stubs + camelService + 0.0.1-SNAPSHOT + pom + diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/maven-metadata-local.xml b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 0000000000..8ac2715bc8 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/0.0.1-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + camelService + 0.0.1-SNAPSHOT + + + true + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata-local.xml b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata-local.xml new file mode 100644 index 0000000000..237fd747a9 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata-local.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + camelService + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata.xml new file mode 100644 index 0000000000..237fd747a9 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + camelService + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-integration/.jdk8 b/tests/spring-cloud-contract-stub-runner-integration/.jdk8 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/spring-cloud-contract-stub-runner-integration/README.adoc b/tests/spring-cloud-contract-stub-runner-integration/README.adoc new file mode 100644 index 0000000000..ce3b3bff49 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/README.adoc @@ -0,0 +1,131 @@ +:input_name: input +:output_name: output + +=== Stub Runner Integration + +Spring Cloud Contract Verifier Stub Runner's messaging module gives you an easy way to integrate with Spring Integration. +For the provided artifacts it will automatically download the stubs and register the required +routes. + +==== Adding it to the project + +To use it you have to add the following dependency to your project (example for Gradle): + +[source,groovy,indent=0] +---- +testCompile "org.springframework.cloud:stub-runner-integration:${verifierVersion}" +---- + +==== Examples + +===== Stubs structure + +Let us assume that we have the following Maven repository with a deployed stubs for the +`integrationService` application. + +[source,bash,indent=0] +---- +└── .m2 + └── repository + └── io + └── codearte + └── accurest + └── stubs + └── integrationService + ├── 0.0.1-SNAPSHOT + │   ├── integrationService-0.0.1-SNAPSHOT.pom + │   ├── integrationService-0.0.1-SNAPSHOT-stubs.jar + │   └── maven-metadata-local.xml + └── maven-metadata-local.xml +---- + +And the stubs contain the following structure: + +[source,bash,indent=0] +---- +├── META-INF +│   └── MANIFEST.MF +└── repository + ├── accurest + │   ├── bookDeleted.groovy + │   ├── bookReturned1.groovy + │   └── bookReturned2.groovy + └── mappings +---- + +Let's consider the following contracts (let' number it with *1*): + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl,indent=0] +---- + +and number *2* + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] +---- + +and the following Spring Integration Route: + +[source,xml] +---- +include::src/test/resources/integration-context.xml[] +---- + + +===== Scenario 1 (no input message) + +So as to trigger a message via the `return_book_1` label we'll use the `StubTigger` interface as follows + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger,indent=0] +---- + +Next we'll want to listen to the output of the message sent to `{output_name}` + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] +---- + +And the received message would pass the following assertions + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] +---- + +===== Scenario 2 (output triggered by input) + +Since the route is set for you it's enough to just send a message to the `{output_name}` destination. + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_send,indent=0] +---- + +Next we'll want to listen to the output of the message sent to `{output_name}` + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive,indent=0] +---- + +And the received message would pass the following assertions + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive_message,indent=0] +---- + +===== Scenario 3 (input with no output) + +Since the route is set for you it's enough to just send a message to the `{input_name}` destination. + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] +---- diff --git a/tests/spring-cloud-contract-stub-runner-integration/pom.xml b/tests/spring-cloud-contract-stub-runner-integration/pom.xml new file mode 100644 index 0000000000..e0c5a76850 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-contract-stub-runner-build + 1.0.0.BUILD-SNAPSHOT + .. + + spring-cloud-contract-stub-runner-integration + jar + Spring Cloud Contract Stub Runner Integration + Spring Cloud Contract Stub Runner Integration + + 1.8 + + + + org.springframework.cloud + spring-cloud-contract-stub-runner + + + org.springframework.cloud + spring-cloud-contract-stub-runner-jetty + test + + + org.springframework.cloud + spring-cloud-contract-verifier + + + org.springframework.integration + spring-integration-java-dsl + + + junit + junit + test + + + org.spockframework + spock-core + test + + + org.spockframework + spock-spring + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + test + + + info.solidsoft.spock + spock-global-unroll + test + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + testCompile + + + + + + + diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/BookReturned.groovy b/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/BookReturned.groovy new file mode 100644 index 0000000000..bbb47bbb60 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/BookReturned.groovy @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.messaging.integration + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode + +@CompileStatic +@EqualsAndHashCode +class BookReturned implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy b/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy new file mode 100644 index 0000000000..bfa4a2c3be --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy @@ -0,0 +1,229 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.messaging.integration + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper + +import java.util.concurrent.TimeUnit + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.context.SpringBootContextLoader +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.stubrunner.StubFinder +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner +import org.springframework.cloud.contract.verifier.messaging.integration.SpringIntegrationStubMessages +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.ImportResource +import org.springframework.messaging.Message +import org.springframework.test.context.ContextConfiguration + +import spock.lang.Specification + +/** + * @author Marcin Grzejszczak + */ +@Configuration +@ComponentScan +@EnableAutoConfiguration +@ContextConfiguration(classes = IntegrationStubRunnerSpec, loader = SpringBootContextLoader) +@ImportResource("classpath*:integration-context.xml") +@AutoConfigureStubRunner +class IntegrationStubRunnerSpec extends Specification { + + @Autowired StubFinder stubFinder + @Autowired SpringIntegrationStubMessages messaging + + def setup() { + // ensure that message were taken from the queue + messaging.receive('outputTest', 100, TimeUnit.MILLISECONDS) + } + + def 'should download the stub and register a route for it'() { + when: + // tag::client_send[] + messaging.send(new BookReturned('foo'), [sample: 'header'], 'input') + // end::client_send[] + then: + // tag::client_receive[] + Message receivedMessage = messaging.receive('outputTest') + // end::client_receive[] + and: + // 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[] + then: + // tag::client_trigger_receive[] + Message receivedMessage = messaging.receive('outputTest') + // end::client_trigger_receive[] + and: + // 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:integrationService', 'return_book_1') + // end::trigger_group_artifact[] + then: + Message receivedMessage = messaging.receive('outputTest') + and: + 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('integrationService', 'return_book_1') + // end::trigger_artifact[] + then: + Message receivedMessage = messaging.receive('outputTest') + and: + 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') + then: + thrown(IllegalArgumentException) + } + + def 'should throw exception when missing label and artifactid is passed'() { + when: + stubFinder.trigger('some:service', 'return_book_1') + then: + thrown(IllegalArgumentException) + } + + def 'should trigger messages by running all triggers'() { + when: + // tag::trigger_all[] + stubFinder.trigger() + // end::trigger_all[] + then: + Message receivedMessage = messaging.receive('outputTest') + and: + 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[] + then: + noExceptionThrown() + } + + def 'should not trigger a message that does not match input'() { + when: + messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'input') + then: + Message receivedMessage = messaging.receive('outputTest', 100, TimeUnit.MILLISECONDS) + and: + receivedMessage == null + } + + private boolean assertJsons(Object payload) { + String objectAsString = payload instanceof String ? payload : + JsonOutput.toJson(payload) + def json = new JsonSlurper().parseText(objectAsString) + return json.bookName == 'foo' + } + + Contract dsl = + // tag::sample_dsl[] + Contract.make { + label 'return_book_1' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('output') + 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('input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('output') + 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()') + } + } + // end::sample_dsl_3[] + +} diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformerSpec.groovy b/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformerSpec.groovy new file mode 100644 index 0000000000..31b4d19ab4 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformerSpec.groovy @@ -0,0 +1,95 @@ +package org.springframework.cloud.contract.stubrunner.messaging.integration + +import org.springframework.cloud.contract.spec.Contract +import org.springframework.messaging.Message +import org.springframework.messaging.support.MessageBuilder +import spock.lang.Specification + +class StubRunnerIntegrationTransformerSpec extends Specification { + + Message message = MessageBuilder.withPayload("hello").build() + + def noOutputMessageContract = Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookId: $(consumer(regex('[0-9]+')), producer('123')) + ]) + messageHeaders { + header('sample', 'header') + } + } + } + + def 'should not transform the message if there is no output message'() { + given: + StubRunnerIntegrationTransformer transformer = new StubRunnerIntegrationTransformer(noOutputMessageContract) + when: + def result = transformer.transform(message) + then: + result.is(message) + } + + def dsl = Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookId: $(consumer(regex('[0-9]+')), producer('123')) + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('returnBook') + body([ + responseId: $(producer(regex('[0-9]+')), consumer('123')) + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + def 'should convert dsl into message'() { + given: + StubRunnerIntegrationTransformer transformer = new StubRunnerIntegrationTransformer(dsl) + when: + def result = transformer.transform(message) + then: + result.payload == '{"responseId":"123"}' + } + + def dslWithRegexInGString = Contract.make { + // Human readable description + description 'Should produce valid sensor data' + // Label by means of which the output message can be triggered + label 'sensor1' + // input to the contract + input { + // the contract will be triggered by a method + triggeredBy('createSensorData()') + } + // output message of the contract + outputMessage { + // destination to which the output message will be sent + sentTo 'sensor-data' + headers { + header('contentType': 'application/json') + } + // the body of the output message + body("""{"id":"${value(producer(regex('[0-9]+')), consumer('99'))}","temperature":"123.45"}""") + } + } + + def 'should convert dsl into message with regex in GString'() { + given: + StubRunnerIntegrationTransformer transformer = new StubRunnerIntegrationTransformer(dslWithRegexInGString) + when: + def result = transformer.transform(message) + then: + result.payload == '''{"id":"99","temperature":"123.45"}''' + } +} diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/application.yml b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/application.yml new file mode 100644 index 0000000000..32d7427875 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/application.yml @@ -0,0 +1,2 @@ +stubrunner.stubs.repositoryRoot: classpath:m2repo/repository/ +stubrunner.stubs.ids: org.springframework.cloud.contract.verifier.stubs:integrationService:0.0.1-SNAPSHOT \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/integration-context.xml b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/integration-context.xml new file mode 100644 index 0000000000..1fdb1bb847 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/integration-context.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT-stubs.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cb1d97128317912625c1aa09877b4e7431e6119 GIT binary patch literal 1762 zcmWIWW@h1HVBp|j&|{wF!2kqIAOZ+Df!NnI#8KDN&rP41Apk|;rh2A#(m(~0KrDi+ z(AUw=)6F$FM9W7$;qWfsl_EwJs?>QhBa+>J(j9HyT2XC`wzq#3^G{tB<1I4 z2c?#j7UiX;80w`L<>!}GhK6u5Fh6-v9DWywODnh;7+JnEGBB`!Og8O1$aTno$Km|l z@F}rIj_h%JS5{2Y?D&3h?Z&>`vmVwiDgX1V?;7rzK3!n;9o{<;pA)V=?%r|sb)VbZ z>nXOYZ?X53*2OG~+p1yT`68rsE{mJ}CSC1C+wM3iNm=*U6+PT($R_#q;atZj+aq0T z;~lm&g(@v`x$xp}SmQZOm;uw;Ys-DeK%njUm++F^T(=Hh^mP?k# zet+CKX4$f*-FoMBMB_SNo8+(1zBFe`>dff$_-({|qeZ_= z+B{-PZb!c7ZasEFXu6M9`?jkKHJWRK2OY!eAM3I6sa)VKy#IX zkU`K^-a`g$|AeQ>Pqcrv#yR&_QHJD|GJno5ejRD`FR$vq$=M#=zwPqbLz8QN_14-j zB0^{dFoc%$XGyIBhR{bKR>PBYTvBsVOHxyi!{-h#Y_4L4k8kg3V8Rh_z5X&hWx9xp z{DUCZn4Ll~9Vbj?+GSkQ^z7>Ra^H9HqVC8$$LDzO3rzfS_reu9nR_R{dqvOcxn$zP zZ?#Nf>#e$}&2m3CCx~fniILbM@i9#B>!O6a27ZN}GY<dGae(82s7^T1Q?7!Ai(g}5k$jFmjG{6ZRn*7 zNHGX3Y19OgaBWCs4Y~;+)v$sLRHQ(F0K;F$G9VMF)WD|^UWKufW?7uhyMjt1HW%F%di z!^{uJwh1ufwhcXZ0POKGUR#6&9_ literal 0 HcmV?d00001 diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT.pom b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT.pom new file mode 100644 index 0000000000..0581ac022d --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/integrationService-0.0.1-SNAPSHOT.pom @@ -0,0 +1,25 @@ + + + + + 4.0.0 + org.springframework.cloud.contract.verifier.stubs + integrationService + 0.0.1-SNAPSHOT + pom + diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/maven-metadata-local.xml b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 0000000000..e6c62c5897 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/0.0.1-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + integrationService + 0.0.1-SNAPSHOT + + + true + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata-local.xml b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata-local.xml new file mode 100644 index 0000000000..f619bafb54 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata-local.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + integrationService + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata.xml new file mode 100644 index 0000000000..f619bafb54 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-integration/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/integrationService/maven-metadata.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + integrationService + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-stream/README.adoc b/tests/spring-cloud-contract-stub-runner-stream/README.adoc new file mode 100644 index 0000000000..2d6a41522c --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/README.adoc @@ -0,0 +1,132 @@ +=== Stub Runner Stream + +Spring Cloud Contract Verifier Stub Runner's messaging module gives you an easy way to integrate with Spring Stream. +For the provided artifacts it will automatically download the stubs and register the required +routes. + +WARNING: In Stub Runner's integration with Stream the `messageFrom` or `sentTo` Strings are resolved +first as a `destination` of a channel, and then if there is no such `destination` it's resolved as a +channel name. + +==== Adding it to the project + +To use it you have to add the following dependency to your project (example for Gradle): + +[source,groovy,indent=0] +---- +testCompile "org.springframework.cloud:stub-runner-stream:${verifierVersion}" +---- + +==== Examples + +===== Stubs structure + +Let us assume that we have the following Maven repository with a deployed stubs for the +`streamService` application. + +[source,bash,indent=0] +---- +└── .m2 + └── repository + └── io + └── codearte + └── accurest + └── stubs + └── streamService + ├── 0.0.1-SNAPSHOT + │   ├── streamService-0.0.1-SNAPSHOT.pom + │   ├── streamService-0.0.1-SNAPSHOT-stubs.jar + │   └── maven-metadata-local.xml + └── maven-metadata-local.xml +---- + +And the stubs contain the following structure: + +[source,bash,indent=0] +---- +├── META-INF +│   └── MANIFEST.MF +└── repository + ├── accurest + │   ├── bookDeleted.groovy + │   ├── bookReturned1.groovy + │   └── bookReturned2.groovy + └── mappings +---- + +Let's consider the following contracts (let' number it with *1*): + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl,indent=0] +---- + +and number *2* + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] +---- + +and the following Spring configuration: + +[source,yaml] +---- +include::src/test/resources/application.yml[] +---- + + +===== Scenario 1 (no input message) + +So as to trigger a message via the `return_book_1` label we'll use the `StubTrigger` interface as follows + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger,indent=0] +---- + +Next we'll want to listen to the output of the message sent to a channel whose `destination` is `returnBook` + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] +---- + +And the received message would pass the following assertions + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] +---- + +===== Scenario 2 (output triggered by input) + +Since the route is set for you it's enough to just send a message to the `bookStorage` `destination`. + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_send,indent=0] +---- + +Next we'll want to listen to the output of the message sent to `returnBook` + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive,indent=0] +---- + +And the received message would pass the following assertions + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive_message,indent=0] +---- + +===== Scenario 3 (input with no output) + +Since the route is set for you it's enough to just send a message to the `{output_name}` destination. + +[source,groovy] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] +---- diff --git a/tests/spring-cloud-contract-stub-runner-stream/pom.xml b/tests/spring-cloud-contract-stub-runner-stream/pom.xml new file mode 100644 index 0000000000..c6b411380b --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-contract-stub-runner-build + 1.0.0.BUILD-SNAPSHOT + .. + + spring-cloud-contract-stub-runner-stream + jar + Spring Cloud Contract Stub Runner Stream + Spring Cloud Contract Stub Runner Stream + + + org.springframework.cloud + spring-cloud-starter-stream-rabbit + + + org.springframework.cloud + spring-cloud-contract-stub-runner + test + + + org.springframework.cloud + spring-cloud-contract-stub-runner-jetty + test + + + org.springframework.cloud + spring-cloud-contract-verifier + test + + + org.springframework.cloud + spring-cloud-stream-test-support + test + + + org.springframework.integration + spring-integration-java-dsl + + + junit + junit + test + + + org.spockframework + spock-core + test + + + org.spockframework + spock-spring + test + + + org.springframework.boot + spring-boot-starter-test + test + + + info.solidsoft.spock + spock-global-unroll + test + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + testCompile + + + + + + + diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/BookReturned.groovy b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/BookReturned.groovy new file mode 100644 index 0000000000..49dea202eb --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/BookReturned.groovy @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.messaging.stream + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode + +@CompileStatic +@EqualsAndHashCode +class BookReturned implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} 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 new file mode 100644 index 0000000000..28564fa7be --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy @@ -0,0 +1,227 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.messaging.stream + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper + +import java.util.concurrent.TimeUnit + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.context.SpringBootContextLoader +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.stubrunner.StubFinder +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner +import org.springframework.cloud.contract.verifier.messaging.MessageVerifier +import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier; +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.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.messaging.Message +import org.springframework.test.context.ContextConfiguration + +import spock.lang.Specification + +/** + * @author Marcin Grzejszczak + */ +@ContextConfiguration(classes = Config, loader = SpringBootContextLoader) +@IntegrationTest("debug=true") +@AutoConfigureStubRunner +@AutoConfigureMessageVerifier +class StreamStubRunnerSpec extends Specification { + + @Autowired StubFinder stubFinder + @Autowired MessageVerifier> messaging + + def setup() { + // ensure that message were taken from the queue + messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS) + } + + 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[] + then: + // 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[] + } + + def 'should trigger a message by label'() { + when: + // 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[] + and: + // 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[] + then: + Message receivedMessage = messaging.receive('returnBook') + and: + 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[] + then: + Message receivedMessage = messaging.receive('returnBook') + and: + 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') + then: + thrown(IllegalArgumentException) + } + + def 'should throw exception when missing label and artifactid is passed'() { + when: + stubFinder.trigger('some:service', 'return_book_1') + then: + thrown(IllegalArgumentException) + } + + def 'should trigger messages by running all triggers'() { + when: + // tag::trigger_all[] + stubFinder.trigger() + // end::trigger_all[] + then: + Message receivedMessage = messaging.receive('returnBook') + and: + 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[] + then: + noExceptionThrown() + } + + def 'should not trigger a message that does not match input'() { + when: + messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'bookStorage') + then: + Message receivedMessage = messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS) + and: + receivedMessage == null + } + + private boolean assertJsons(Object payload) { + String objectAsString = payload instanceof String ? payload : + JsonOutput.toJson(payload) + def json = new JsonSlurper().parseText(objectAsString) + return json.bookName == 'foo' + } + + 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') } + } + } + // 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') } + } + } + // 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()') + } + } + // end::sample_dsl_3[] + + + @EnableBinding([Sink, Source]) + @Configuration + @EnableAutoConfiguration + protected static class Config {} + +} diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformerSpec.groovy b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformerSpec.groovy new file mode 100644 index 0000000000..a948936dad --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformerSpec.groovy @@ -0,0 +1,132 @@ +package org.springframework.cloud.contract.stubrunner.messaging.stream + +import org.springframework.cloud.contract.spec.Contract +import org.springframework.messaging.Message +import org.springframework.messaging.support.MessageBuilder +import spock.lang.Specification + +class StubRunnerStreamTransformerSpec extends Specification { + + Message message = MessageBuilder.withPayload("hello").build() + + def noOutputMessageContract = Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookId: $(consumer(regex('[0-9]+')), producer('123')) + ]) + messageHeaders { + header('sample', 'header') + } + } + } + + def 'should not transform the message if there is no output message'() { + given: + StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(noOutputMessageContract) + when: + def result = streamTransformer.transform(message) + then: + result.is(message) + } + + def dsl = Contract.make { + label 'return_book_2' + input { + messageFrom('bookStorage') + messageBody([ + bookId: $(consumer(regex('[0-9]+')), producer('123')) + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('returnBook') + body([ + responseId: $(producer(regex('[0-9]+')), consumer('123')) + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + def 'should convert dsl into message'() { + given: + StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dsl) + when: + def result = streamTransformer.transform(message) + then: + result.payload == '{"responseId":"123"}' + } + + def dslWithRegexInGString = Contract.make { + // Human readable description + description 'Should produce valid sensor data' + // Label by means of which the output message can be triggered + label 'sensor1' + // input to the contract + input { + // the contract will be triggered by a method + triggeredBy('createSensorData()') + } + // output message of the contract + outputMessage { + // destination to which the output message will be sent + sentTo 'sensor-data' + headers { + header('contentType': 'application/json') + } + // the body of the output message + body("""{"id":"${value(producer(regex('[0-9]+')), consumer('99'))}","temperature":"123.45"}""") + } + } + + def 'should convert dsl into message with regex in GString'() { + given: + StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dslWithRegexInGString) + when: + def result = streamTransformer.transform(message) + then: + result.payload == '''{"id":"99","temperature":"123.45"}''' + } + + def 'should parse dsl without DslProperty'() { + given: + Contract contract = Contract.make { + // Human readable description + description 'Sends an order message' + // Label by means of which the output message can be triggered + label 'send_order' + // input to the contract + input { + // the contract will be triggered by a method + triggeredBy('orderTrigger()') + } + // output message of the contract + outputMessage { + // destination to which the output message will be sent + sentTo('orders') + // any headers for the output message + headers { + header('contentType': 'application/json') + } + // the body of the output message + body( + orderId: value( + consumer('40058c70-891c-4176-a033-f70bad0c5f77'), + producer(regex('([0-9|a-f]*-*)*'))), + description: "This is the order description" + ) + } + } + StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(contract) + when: + def result = streamTransformer.transform(message) + then: + result.payload == '''{"orderId":"40058c70-891c-4176-a033-f70bad0c5f77","description":"This is the order description"}''' + } + +} diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/application.yml b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/application.yml new file mode 100644 index 0000000000..baaaa1d38e --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/application.yml @@ -0,0 +1,11 @@ +stubrunner.stubs.repositoryRoot: classpath:m2repo/repository/ +stubrunner.stubs.ids: org.springframework.cloud.contract.verifier.stubs:streamService:0.0.1-SNAPSHOT:stubs + +spring: + cloud: + stream: + bindings: + output: + destination: returnBook + input: + destination: bookStorage \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/maven-metadata-local.xml b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 0000000000..f14e885df3 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + streamService + 0.0.1-SNAPSHOT + + + true + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT-stubs.jar new file mode 100644 index 0000000000000000000000000000000000000000..b3daab26c4f8c8050b86f1d46fad15f2afdd17d3 GIT binary patch literal 1769 zcmWIWW@h1HVBp|j&|{wF!2kqIAOZ+Df!NnI#8KDN&rP41Apk|;rh2A#(m(~0KrDi+ z(AUw=)6F$FM9W7$;qWfsl_EwJs?>QhVyNAJuY)U(B28;F*7nSXfViN)svK; zpBh)XND85mi<1C3w-nQYp3kn4~EkHh)9 z;ZtIb9NFXcT7Fas7I?onI$8GitchhEC;r_nJCj_YudisfQ+nstXD71`m={hrxAnEk zKT#Fd%P%qe&zJdZR~P}~0fXry5Ub%yK`yB|sU@i?$blpU3?OdIK=SQ94GJXJ>o3Do zri-Y^KL~P-*(ns$al&M#UB)F%&#sOy_k9;H>W;i~e2(|Nz{D?iFIO+sAHxK{E=ss-;8*B5^RU2zgL~E0?NtN+{o$S2 zzDMQkjZalRm$UV<7O^B=-~Wj5-~#@cCvWL2;dtQIopki*?UPR|Wm_8T=0$Y`>D)1H ziM?mGDaNbVa6eL3)j}&E(DGkyVnIP>UV1S&|1mO&Fyk&(fWZg^0t{~*K{ULO3GhbM zhF-{k6obH$Mol0I*M?N&pql_v4J*$;B?|-yF#L5a12T~c4}2Oy{)7OK#)Uv8l16x` z12RF58IrXyOC1TY0l-|w(9n1lhY8rrAXZ2jgjW6_+lHBuk!||`v=p0rk!?d{YoKkQ zY>me@%>00Cn>aIW+t70d&@NE!z+x9H?O;oB;D7|CoxhHXEGTxt(_er$D;r1^2N2c) LV?PwAj)4IHo`NA~ literal 0 HcmV?d00001 diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT.pom b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT.pom new file mode 100644 index 0000000000..8613d57be0 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/0.0.1-SNAPSHOT/streamService-0.0.1-SNAPSHOT.pom @@ -0,0 +1,25 @@ + + + + + 4.0.0 + org.springframework.cloud.contract.verifier.stubs + streamService + 0.0.1-SNAPSHOT + pom + diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata-local.xml b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata-local.xml new file mode 100644 index 0000000000..001cf26899 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata-local.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + streamService + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + 20160409062112 + + diff --git a/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata.xml new file mode 100644 index 0000000000..001cf26899 --- /dev/null +++ b/tests/spring-cloud-contract-stub-runner-stream/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/streamService/maven-metadata.xml @@ -0,0 +1,28 @@ + + + + + org.springframework.cloud.contract.verifier.stubs + streamService + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + 20160409062112 + +