diff --git a/docker/spring-cloud-contract-docker/get_dependencies.sh b/docker/spring-cloud-contract-docker/get_dependencies.sh index f7d58dc64a..d552ef8717 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="7.4.1" +WRAPPER_VERSION="7.5.1" 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/project/gradle/wrapper/gradle-wrapper.properties b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties index 00e33edef6..ae04661ee7 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-7.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/docker/spring-cloud-contract-docker/project/src/test/java/contracts/ContractTestsBase.java b/docker/spring-cloud-contract-docker/project/src/test/java/contracts/ContractTestsBase.java index 86980e5383..a2a49e9fef 100644 --- a/docker/spring-cloud-contract-docker/project/src/test/java/contracts/ContractTestsBase.java +++ b/docker/spring-cloud-contract-docker/project/src/test/java/contracts/ContractTestsBase.java @@ -120,9 +120,6 @@ public abstract class ContractTestsBase { AmqpMetadata amqpMetadata = AmqpMetadata.fromMetadata(contract.metadata); if (isMessagingType("rabbit") && hasDeclaredOutputQueue(amqpMetadata) || isMessagingType("kafka")) { log.info("First will try to receive a message to setup the connection with the broker"); - if (contract.input != null && StringUtils.hasText(contract.input.messageFrom)) { - setupConnection(contract.input.messageFrom, contract); - } if (contract.outputMessage != null && StringUtils.hasText(contract.outputMessage.sentTo)){ setupConnection(contract.outputMessage.sentTo, contract); } diff --git a/docker/spring-cloud-contract-docker/project/src/test/java/contracts/MessagingAutoConfig.java b/docker/spring-cloud-contract-docker/project/src/test/java/contracts/MessagingAutoConfig.java index c3049a34ff..5495907605 100644 --- a/docker/spring-cloud-contract-docker/project/src/test/java/contracts/MessagingAutoConfig.java +++ b/docker/spring-cloud-contract-docker/project/src/test/java/contracts/MessagingAutoConfig.java @@ -30,6 +30,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.contract.verifier.converter.YamlContract; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; +import org.springframework.cloud.contract.verifier.messaging.MessageVerifierReceiver; +import org.springframework.cloud.contract.verifier.messaging.MessageVerifierSender; import org.springframework.cloud.contract.verifier.messaging.amqp.AmqpMetadata; import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessageMetadata; @@ -68,8 +70,8 @@ public class MessagingAutoConfig { @Bean public ContractVerifierMessaging contractVerifierMessaging( - MessageVerifier exchange) { - return new ContractVerifierCamelHelper(exchange); + MessageVerifierSender sender, MessageVerifierReceiver receiver) { + return new ContractVerifierCamelHelper(sender, receiver); } @Bean @@ -185,8 +187,8 @@ public class MessagingAutoConfig { class ContractVerifierCamelHelper extends ContractVerifierMessaging { - ContractVerifierCamelHelper(MessageVerifier exchange) { - super(exchange); + ContractVerifierCamelHelper(MessageVerifierSender sender, MessageVerifierReceiver receiver) { + super(sender, receiver); } @Override @@ -197,4 +199,4 @@ class ContractVerifierCamelHelper extends ContractVerifierMessaging { return new ContractVerifierMessage(receive.getBody(), receive.getHeaders()); } -} \ No newline at end of file +} diff --git a/docs/src/main/asciidoc/_project-features-messaging.adoc b/docs/src/main/asciidoc/_project-features-messaging.adoc index 9ac2539e80..d8f40b9915 100644 --- a/docs/src/main/asciidoc/_project-features-messaging.adoc +++ b/docs/src/main/asciidoc/_project-features-messaging.adoc @@ -13,7 +13,6 @@ The DSL for messaging looks a little bit different than the one that focuses on following sections explain the differences: * <> -* <> * <> * <> @@ -42,31 +41,6 @@ In the previous example case, the output message is sent to `output` if a method test that calls that method to trigger the message. On the consumer side, you can use `some_label` to trigger the message. -[[contract-dsl-output-triggered-message]] -==== Output Triggered by a Message - -The output message can be triggered by receiving a message, as shown in the following -example: - -==== -[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] -.Groovy ----- -include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=message_trigger,indent=0] ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.YAML ----- -include::{verifier_core_path}/src/test/resources/yml/contract_message_input_message.yml[indent=0] ----- -==== - -In the preceding example, the output message is sent to `output` if a proper message is -received on the `input` destination. On the message publisher's side, the engine -generates a test that sends the input message to the defined destination. On the -consumer side, you can either send a message to the input destination or use a label -(`some_label` in the example) to trigger the message. [[contract-dsl-consumer-producer]] ==== Consumer/Producer @@ -75,16 +49,9 @@ IMPORTANT: This section is valid only for the Groovy DSL. In HTTP, you have a notion of `client`/`stub and `server`/`test` notation. You can also use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also -provides the `consumer` and `producer` methods, as presented in the following example +provides the `consumer` and `producer` methods (note that you can use either `$` or `value` methods to provide `consumer` and `producer` -parts): - -==== -[source,groovy] ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=consumer_producer] ----- -==== +parts). [[contract-dsl-messaging-common]] ==== Common @@ -97,14 +64,12 @@ in the generated test. [[features-messaging-integrations]] === Integrations -You can use one of the following four integration configurations: +You can use one of the following integration configurations: * Apache Camel * Spring Integration * Spring Cloud Stream -* Spring AMQP -* Spring JMS (requires embedded broker) -* Spring Kafka (requires embedded broker) +* Spring JMS Since we use Spring Boot, if you have added one of these libraries to the classpath, all the messaging configuration is automatically set up. @@ -143,8 +108,8 @@ testImplementation(group: 'org.springframework.cloud', name: 'spring-cloud-strea ==== Manual Integration Testing The main interface used by the tests is -`org.springframework.cloud.contract.verifier.messaging.MessageVerifier`. -It defines how to send and receive messages. You can create your own implementation to +`org.springframework.cloud.contract.verifier.messaging.MessageVerifierSender` and `org.springframework.cloud.contract.verifier.messaging.MessageVerifierReceiver`. +It defines how to send and receive messages. If you need both sender and receiver capabilities you can use `org.springframework.cloud.contract.verifier.messaging.MessageVerifier` interface. You can create your own implementation to achieve the same goal. In a test, you can inject a `ContractVerifierMessageExchange` to send and receive @@ -176,22 +141,12 @@ Having the `input` or `outputMessage` sections in your DSL results in creation o on the publisher's side. By default, JUnit 4 tests are created. However, there is also a possibility to create JUnit 5, TestNG, or Spock tests. -There are three main scenarios that we should take into consideration: - -* Scenario 1: There is no input message that produces an output message. The output -message is triggered by a component inside the application (for example, a scheduler). -* Scenario 2: The input message triggers an output message. -* Scenario 3: The input message is consumed, and there is no output message. - IMPORTANT: The destination passed to `messageFrom` or `sentTo` can have different meanings for different messaging implementations. For Stream and Integration, it is first resolved as a `destination` of a channel. Then, if there is no such `destination`, it is resolved as a channel name. For Camel, that's a certain component (for example, `jms`). -[[features-messaging-scenario1]] -==== Scenario 1: No Input Message - Consider the following contract: ===== @@ -225,76 +180,6 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract ---- ==== -[[features-messaging-scenario2]] -==== Scenario 2: Output Triggered by Input - -Consider the following contract: - -===== -[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] -.Groovy ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_dsl] ----- - -[source,yml,indent=0,subs="verbatim,attributes",role="secondary"] -.YAML ----- -include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario2.yml[indent=0] ----- -===== - -For the preceding contract, the following test would be created: - -==== -[source,java,indent=0,subs="verbatim,attributes",role="primary"] -.JUnit ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_junit] ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Spock ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_spock] ----- -==== - -[[features-messaging-scenario3]] -==== Scenario 3: No Output Message - -Consider the following contract: - -==== -[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] -.Groovy ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl] ----- - -[source,yml,indent=0,subs="verbatim,attributes",role="secondary"] -.YAML ----- -include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0] ----- -==== - -For the preceding contract, the following test would be created: - -==== -[source,java,indent=0,subs="verbatim,attributes",role="primary"] -.JUnit ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_junit] ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Spock ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_spock] ----- -==== - [[features-messaging-consumer]] === Consumer Stub Generation @@ -418,7 +303,7 @@ Assume that we have the following Maven repository with deployed stubs for the ---- ==== -Further assume that the stubs contain the following structure: +Further, assume that the stubs contain the following structure: ==== [source,bash,indent=0] @@ -427,107 +312,33 @@ Further assume that the stubs contain the following structure: │   └── MANIFEST.MF └── repository ├── accurest - │   ├── bookDeleted.groovy - │   ├── bookReturned1.groovy - │   └── bookReturned2.groovy + │   └── bookReturned1.groovy └── mappings ---- ==== -Now consider the following contracts (we number them 1 and 2): +Now consider the following contract: ==== [source,groovy] ---- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl,indent=0] ----- - -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] +include::{tests_path}/samples-messaging-camel/src/test/groovy/com/example/CamelMessagingApplicationSpec.groovy[tags=sample_dsl,indent=0] ---- ==== -These examples lend themselves to three scenarios: - -. <> -. <> -. <> - -[[features-messaging-stub-runner-camel-scenario1]] -===== Scenario 1 (No Input Message) - To trigger a message from the `return_book_1` label, we use the `StubTrigger` interface, as follows: ==== [source,groovy] ---- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger,indent=0] +stubFinder.trigger("return_book_1") ---- ==== -Next, we want to listen to the output of the message sent to `{output_name}`: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] ----- -==== - -The received message would then pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-camel-scenario2]] -===== Scenario 2 (Output Triggered by Input) - -Since the route is set for you, you can send a message to the `{output_name}` destination. - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_send,indent=0] ----- -==== - -Next, we want to listen to the output of the message sent to `{output_name}`, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive,indent=0] ----- -==== - -The received message would pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-camel-scenario3]] -===== Scenario 3 (Input with No Output) - -Since the route is set for you, you can send a message to the `{output_name}` destination, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] ----- +That will send out a message to the destination described in the output message of the contract. :input_name: input :output_name: output -==== [[features-messaging-stub-runner-integration]] === Consumer Side Messaging with Spring Integration @@ -581,25 +392,18 @@ Further assume the stubs contain the following structure: │   └── MANIFEST.MF └── repository ├── accurest - │   ├── bookDeleted.groovy - │   ├── bookReturned1.groovy - │   └── bookReturned2.groovy + │   └── bookReturned1.groovy └── mappings ---- ==== -Consider the following contracts (numbered 1 and 2): +Consider the following contract: ==== [source,groovy] ---- include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl,indent=0] ---- - -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] ----- ==== Now consider the following Spring Integration Route: @@ -611,15 +415,6 @@ include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/res ---- ==== -These examples lend themselves to three scenarios: - -. <> -. <> -. <> - -[[features-messaging-stub-runner-integration-scenario1]] -===== Scenario 1 (No Input Message) - To trigger a message from the `return_book_1` label, use the `StubTrigger` interface, as follows: @@ -630,66 +425,7 @@ include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/gro ---- ==== -The following listing shows how to listen to the output of the message sent to `{output_name}`: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] ----- -==== - -The received message would pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-integration-scenario2]] -===== Scenario 2 (Output Triggered by Input) - -Since the route is set for you, you can send a message to the `{output_name}` -destination, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_send,indent=0] ----- -==== - -The following listing shows how to listen to the output of the message sent to `{output_name}`: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive,indent=0] ----- -==== - -The received message passes the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-integration-scenario3]] -===== Scenario 3 (Input with No Output) - -Since the route is set for you, you can send a message to the `{input_name}` destination, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] ----- -==== +That will send out a message to the destination described in the output message of the contract. [[features-messaging-stub-runner-stream]] === Consumer Side Messaging With Spring Cloud Stream @@ -773,24 +509,26 @@ Further assume the stubs contain the following structure: │   └── MANIFEST.MF └── repository ├── accurest - │   ├── bookDeleted.groovy - │   ├── bookReturned1.groovy - │   └── bookReturned2.groovy + │   └── bookReturned1.groovy └── mappings ---- ==== -Consider the following contracts (numbered 1 and 2): +Consider the following contract: ==== [source,groovy] ---- include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl,indent=0] ---- +==== -[source,groovy] +Now consider the following Spring Cloud Stream function configuration: + +==== +[source,yaml] ---- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] +include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=setup,indent=0] ---- ==== @@ -803,15 +541,6 @@ include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/resource ---- ==== -These examples lend themselves to three scenarios: - -* <> -* <> -* <> - -[[features-messaging-stub-runner-stream-scenario1]] -===== Scenario 1 (No Input Message) - To trigger a message from the `return_book_1` label, use the `StubTrigger` interface as follows: @@ -822,220 +551,8 @@ include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/o ---- ==== -The following example shows how to listen to the output of the message sent to a channel whose `destination` is -`returnBook`: -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] ----- -==== - -The received message passes the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-stream-scenario2]] -===== Scenario 2 (Output Triggered by Input) - -Since the route is set for you, you can send a message to the `bookStorage` -`destination`, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_send,indent=0] ----- -==== - -The following example shows how to listen to the output of the message sent to `returnBook`: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive,indent=0] ----- -==== - -The received message passes the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-stream-scenario3]] -===== Scenario 3 (Input with No Output) - -Since the route is set for you, you can send a message to the `{output_name}` -destination, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] ----- -==== - -[[features-messaging-stub-runner-amqp]] -=== Consumer Side Messaging With Spring AMQP - -Spring Cloud Contract Stub Runner's messaging module provides an easy way to -integrate with Spring AMQP's Rabbit Template. For the provided artifacts, it -automatically downloads the stubs and registers the required routes. - -The integration tries to work standalone (that is, without interaction with a running -RabbitMQ message broker). It expects a `RabbitTemplate` on the application context and -uses it as a Spring Boot test named `@SpyBean`. As a result, it can use the Mockito spy -functionality to verify and inspect messages sent by the application. - -On the message consumer side, the stub runner considers all `@RabbitListener`-annotated -endpoints and all `SimpleMessageListenerContainer` objects on the application context. - -As messages are usually sent to exchanges in AMQP, the message contract contains the -exchange name as the destination. Message listeners on the other side are bound to -queues. Bindings connect an exchange to a queue. If message contracts are triggered, the -Spring AMQP stub runner integration looks for bindings on the application context that -matches this exchange. Then it collects the queues from the Spring exchanges and tries to -find message listeners bound to these queues. The message is triggered for all matching -message listeners. - -If you need to work with routing keys, you can pass them by using the `amqp_receivedRoutingKey` -messaging header. - -[[features-messaging-stub-runner-amqp-adding]] -==== Adding the Runner to the Project - -You can have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and -set the property `stubrunner.amqp.enabled=true`. Remember to annotate your test class -with `@AutoConfigureStubRunner`. - -IMPORTANT: If you already have Stream and Integration on the classpath, you need -to explicitly disable them by setting the `stubrunner.stream.enabled=false` and -`stubrunner.integration.enabled=false` properties. - -[[features-messaging-stub-runner-amqp-example]] -==== Examples - -Assume that you have the following Maven repository with a deployed stubs for the -`spring-cloud-contract-amqp-test` application: - -==== -[source,bash,indent=0] ----- -└── .m2 - └── repository - └── com - └── example - └── spring-cloud-contract-amqp-test - ├── 0.4.0-SNAPSHOT - │   ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT.pom - │   ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT-stubs.jar - │   └── maven-metadata-local.xml - └── maven-metadata-local.xml ----- -==== - -Further assume that the stubs contain the following structure: - -==== -[source,bash,indent=0] ----- -├── META-INF -│   └── MANIFEST.MF -└── contracts - └── shouldProduceValidPersonData.groovy ----- -==== - -Then consider the following contract: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpStubRunnerSpec.groovy[tags=amqp_contract,indent=0] ----- -==== - -Now consider the following Spring configuration: - -==== -[source,yaml] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/test/resources/application.yml[] ----- -==== - -[[features-messaging-stub-runner-amqp-triggering]] -===== Triggering the Message - -To trigger a message using the contract in the preceding section, use the `StubTrigger` interface, as -follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpStubRunnerSpec.groovy[tags=client_trigger,indent=0] ----- -==== - -The message has a destination of `contract-test.exchange`, so the Spring AMQP stub runner -integration looks for bindings related to this exchange, as the following example shows: - -==== -[source,java] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpMessagingApplication.java[tags=amqp_binding,indent=0] ----- -==== - -The binding definition binds the queue called `test.queue`. As a result, the following listener -definition is matched and invoked with the contract message: - -==== -[source,java] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpMessagingApplication.java[tags=amqp_listener,indent=0] ----- -==== - -Also, the following annotated listener matches and is invoked: - -==== -[source,java] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/MessageSubscriberRabbitListener.java[tags=amqp_annotated_listener,indent=0] ----- -==== - -NOTE: The message is directly handed over to the `onMessage` method of the -`MessageListener` associated with the matching `SimpleMessageListenerContainer`. - -[[features-messaging-stub-runner-amqp-configuration]] -===== Spring AMQP Test Configuration - -To avoid Spring AMQP trying to connect to a running broker during our tests, we -configure a mock `ConnectionFactory`. - -To disable the mocked `ConnectionFactory`, set the following property: -`stubrunner.amqp.mockConnection=false`, as follows: - -==== -[source,yaml] ----- -stubrunner: - amqp: - mockConnection: false ----- -==== +That will send out a message to the destination described in the output message of the contract. [[features-messaging-stub-runner-jms]] === Consumer Side Messaging With Spring JMS @@ -1043,7 +560,7 @@ stubrunner: Spring Cloud Contract Stub Runner's messaging module provides an easy way to integrate with Spring JMS. -The integration assumes that you have a running instance of a JMS broker (such as an `activemq` embedded broker). +The integration assumes that you have a running instance of a JMS broker. [[features-messaging-stub-runner-jms-adding]] ==== Adding the Runner to the Project @@ -1063,9 +580,7 @@ Assume that the stub structure looks as follows: [source,bash,indent=0] ---- ├── stubs - ├── bookDeleted.groovy - ├── bookReturned1.groovy - └── bookReturned2.groovy + └── bookReturned1.groovy ---- ==== @@ -1088,23 +603,15 @@ spring: ---- ==== -Now consider the following contracts (we number them 1 and 2): +Now consider the following contract: ==== [source,groovy] ---- include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=sample_dsl,indent=0] ---- - -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] ----- ==== -[[features-messaging-stub-runner-jms-scenario1]] -===== Scenario 1 (No Input Message) - To trigger a message from the `return_book_1` label, we use the `StubTrigger` interface, as follows: ==== @@ -1114,213 +621,4 @@ include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/ ---- ==== -Next, we want to listen to the output of the message sent to `{output_name}`: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] ----- -==== - -The received message would then pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-jms-scenario2]] -===== Scenario 2 (Output Triggered by Input) - -Since the route is set for you, you can send a message to the `{output_name}` destination. - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=client_send,indent=0] ----- -==== - -Next, we want to listen to the output of the message sent to `{output_name}`, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=client_receive,indent=0] ----- -==== - -The received message would pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=client_receive_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-jms-scenario3]] -===== Scenario 3 (Input with No Output) - -Since the route is set for you, you can send a message to the `{output_name}` destination, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] ----- -==== - -[[features-messaging-stub-runner-kafka]] -=== Consumer Side Messaging With Spring Kafka - -Spring Cloud Contract Stub Runner's messaging module provides an easy way to -integrate with Spring Kafka. - -The integration assumes that you have a running instance of an embedded Kafka broker (through the `spring-kafka-test` dependency). - -[[features-messaging-stub-runner-kafka-adding]] -==== Adding the Runner to the Project - -You need to have Spring Kafka, Spring Kafka Test (to run the `@EmbeddedBroker`), and Spring Cloud Contract Stub Runner on the classpath. Remember to annotate your test class -with `@AutoConfigureStubRunner`. - -With Kafka integration, in order to poll for a single message, we need to register a consumer upon Spring context startup. That may lead to a situation that, when you are on the consumer side, Stub Runner can register an additional consumer for the same group ID and topic. That could lead to a situation that only one of the components would actually poll for the message. Since, on the consumer side, you have both the Spring Cloud Contract Stub Runner and Spring Cloud Contract Verifier classpath, we need to be able to switch off such behavior. That is done automatically through the `stubrunner.kafka.initializer.enabled` flag, which disables the Contact Verifier consumer registration. If your application is both the consumer and the producer of a Kafka message, you might need to manually toggle that property to `false` in the base class of your generated tests. - -If you have multiple `KafkaTemplate` beans, you can provide your own bean of `Supplier` type that returns the `KafkaTemplate` of your chosing. - -:input_name: input -:output_name: output - -[[features-messaging-stub-runner-kafka-example]] -==== Examples - -Assume that the stub structure looks as follows: - -==== -[source,bash,indent=0] ----- -├── stubs - ├── bookDeleted.groovy - ├── bookReturned1.groovy - └── bookReturned2.groovy - ----- -==== - -Further assume the following test configuration (notice the `spring.kafka.bootstrap-servers` pointing to the embedded broker's IP via `${spring.embedded.kafka.brokers}`): - -==== -[source,yml,indent=0] ----- -stubrunner: - repository-root: stubs:classpath:/stubs/ - ids: my:stubs - stubs-mode: remote -spring: - kafka: - bootstrap-servers: ${spring.embedded.kafka.brokers} - producer: - value-serializer: org.springframework.kafka.support.serializer.JsonSerializer - properties: - "spring.json.trusted.packages": "*" - consumer: - value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer - properties: - "spring.json.trusted.packages": "*" - group-id: groupId ----- -==== - -NOTE: If your application uses non-integer record keys you will need to set the `spring.kafka.producer.key-serializer` -and `spring.kafka.consumer.key-deserializer` properties accordingly because the Kafka de/serialization expects non-null -record keys to be of integer type. - -Now consider the following contracts (we number them 1 and 2): - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=sample_dsl,indent=0] ----- - -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0] ----- -==== - -[[features-messaging-stub-runner-kafka-scenario1]] -===== Scenario 1 (No Input Message) - -To trigger a message from the `return_book_1` label, we use the `StubTrigger` interface, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=client_trigger,indent=0] ----- -==== - -Next, we want to listen to the output of the message sent to `{output_name}`: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0] ----- -==== - -The received message would then pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=client_trigger_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-kafka-scenario2]] -===== Scenario 2 (Output Triggered by Input) - -Since the route is set for you, you can send a message to the `{output_name}` destination. - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=client_send,indent=0] ----- -==== - -Next, we want to listen to the output of the message sent to `{output_name}`, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=client_receive,indent=0] ----- -==== - -The received message would pass the following assertions: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=client_receive_message,indent=0] ----- -==== - -[[features-messaging-stub-runner-kafka-scenario3]] -===== Scenario 3 (Input with No Output) - -Since the route is set for you, you can send a message to the `{output_name}` destination, as follows: - -==== -[source,groovy] ----- -include::{tests_path}/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy[tags=trigger_no_output,indent=0] ----- -==== +That will send out a message to the destination described in the output message of the contract. diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc index ceacee8d3d..4782bd6c8e 100644 --- a/docs/src/main/asciidoc/getting-started.adoc +++ b/docs/src/main/asciidoc/getting-started.adoc @@ -446,13 +446,13 @@ The following example shows a Camel messaging contract: [source,groovy,indent=0,role="primary"] .groovy ---- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl] +include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_dsl] ---- [source,yaml,indent=0,role="secondary"] .yaml ---- -include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0] +include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario1.yml[indent=0] ---- ==== diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/messaging/shouldWorkWithInputOutputBinary.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/messaging/shouldWorkWithInputOutputBinary.groovy index df911e8517..9d7027acdb 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/messaging/shouldWorkWithInputOutputBinary.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/messaging/shouldWorkWithInputOutputBinary.groovy @@ -21,11 +21,7 @@ import org.springframework.cloud.contract.spec.Contract Contract.make { label("positive") input { - messageFrom("bytes_input") - messageBody(fileAsBytes("input.pdf")) - messageHeaders { - messagingContentType(applicationOctetStream()) - } + triggeredBy("hashCode()") } outputMessage { sentTo("bytes_output") diff --git a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/ClientInput.java b/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/ClientInput.java deleted file mode 100644 index e99d02bba8..0000000000 --- a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/ClientInput.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.spec.internal; - -class ClientInput extends Input { - - ClientInput(Input request) { - super(request); - } - -} diff --git a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Input.java b/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Input.java index b3dcc3dc05..7324ddcc39 100644 --- a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Input.java +++ b/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Input.java @@ -17,14 +17,8 @@ package org.springframework.cloud.contract.spec.internal; import java.util.Objects; -import java.util.function.Consumer; import java.util.regex.Pattern; -import groovy.lang.Closure; -import groovy.lang.DelegatesTo; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - /** * Represents an input for messaging. The input can be a message or some action inside the * application. @@ -35,49 +29,12 @@ import org.apache.commons.logging.LogFactory; */ public class Input extends Common implements RegexCreatingProperty { - private static final Log log = LogFactory.getLog(Input.class); - private ClientPatternValueDslProperty property = new ClientPatternValueDslProperty(); - private DslProperty messageFrom; - private ExecutionProperty triggeredBy; - private Headers messageHeaders = new Headers(); - - private BodyType messageBody; - private ExecutionProperty assertThat; - private BodyMatchers bodyMatchers; - - public Input() { - } - - public Input(Input input) { - this.messageFrom = input.getMessageFrom(); - this.messageHeaders = input.getMessageHeaders(); - this.messageBody = input.getMessageBody(); - } - - /** - * Name of a destination from which message would come to trigger action in the - * system. - * @param messageFrom message destination - */ - public void messageFrom(String messageFrom) { - this.messageFrom = new DslProperty<>(messageFrom); - } - - /** - * Name of a destination from which message would come to trigger action in the - * system. - * @param messageFrom message destination - */ - public void messageFrom(DslProperty messageFrom) { - this.messageFrom = messageFrom; - } - /** * Function that needs to be executed to trigger action in the system. * @param triggeredBy method name that triggers the message @@ -86,11 +43,6 @@ public class Input extends Common implements RegexCreatingProperty getMessageFrom() { - return messageFrom; - } - - public void setMessageFrom(DslProperty messageFrom) { - this.messageFrom = messageFrom; - } - public ExecutionProperty getTriggeredBy() { return triggeredBy; } @@ -145,22 +89,6 @@ public class Input extends Common implements RegexCreatingProperty consumer) { - this.messageHeaders = new Headers(); - consumer.accept(this.messageHeaders); - } - - /** - * The stub matchers part of the contract. - * @param consumer function to manipulate the message headers - */ - public void bodyMatchers(Consumer consumer) { - this.bodyMatchers = new BodyMatchers(); - consumer.accept(this.bodyMatchers); - } - - /** - * The message headers part of the contract. - * @param consumer function to manipulate the message headers - */ - public void messageHeaders(@DelegatesTo(Headers.class) Closure consumer) { - this.messageHeaders = new Headers(); - consumer.setDelegate(this.messageHeaders); - consumer.call(); - } - - /** - * The stub matchers part of the contract. - * @param consumer function to manipulate the message headers - */ - public void bodyMatchers(@DelegatesTo(BodyMatchers.class) Closure consumer) { - this.bodyMatchers = new BodyMatchers(); - consumer.setDelegate(this.bodyMatchers); - consumer.call(); - } - @Override public boolean equals(Object o) { if (this == o) { @@ -329,34 +211,18 @@ public class Input extends Common implements RegexCreatingProperty { diff --git a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/ServerInput.java b/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/ServerInput.java deleted file mode 100644 index a520cb8253..0000000000 --- a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/ServerInput.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.spec.internal; - -class ServerInput extends Input { - - ServerInput(Input request) { - super(request); - } - -} diff --git a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/InputDsl.kt b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/InputDsl.kt index cbda14a5de..33dcc3a0e9 100644 --- a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/InputDsl.kt +++ b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/InputDsl.kt @@ -31,53 +31,16 @@ class InputDsl : CommonDsl() { private val delegate = Input() - /** - * Name of a destination from which message would come to trigger action in the - * system. - */ - var messageFrom: DslProperty? = null - /** * Function that needs to be executed to trigger action in the system. */ var triggeredBy: String? = null - /** - * The message headers part of the contract. - */ - var headers: Headers? = null - - /** - * The contents of the incoming message. - */ - var messageBody: Input.BodyType? = null - /** * Function that needs to be executed after the message has been received/processed by the system. */ var assertThat: String? = null - /** - * The body matchers part of the contract. - */ - var bodyMatchers: BodyMatchers? = null - - fun messageFrom(messageFrom: String) = messageFrom.toDslProperty() - - fun headers(headers: HeadersDsl.() -> Unit) { - this.headers = HeadersDsl().apply(headers).get() - } - - fun messageBody(vararg pairs: Pair) = Input.BodyType(pairs.toMap()) - - fun messageBody(pair: Pair) = Input.BodyType(mapOf(pair)) - - fun messageBody(value: String) = Input.BodyType(value) - - fun bodyMatchers(configurer: BodyMatchersDsl.() -> Unit) { - this.bodyMatchers = BodyMatchersDsl().apply(configurer).get() - } - /* HELPER VARIABLES */ val anyAlphaUnicode: ClientDslProperty @@ -170,12 +133,8 @@ class InputDsl : CommonDsl() { internal fun get(): Input { val input = Input() - messageFrom?.also { input.messageFrom = messageFrom } triggeredBy?.also { input.triggeredBy(triggeredBy) } - headers?.also { input.messageHeaders = headers } - messageBody?.also { input.messageBody = messageBody } assertThat?.also { input.assertThat(assertThat) } - bodyMatchers?.also { input.bodyMatchers = bodyMatchers } return input } } diff --git a/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt b/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt index 7949174a99..16e7797038 100644 --- a/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt +++ b/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt @@ -142,90 +142,6 @@ class ContractTests { } } - @Test - fun `should work for messaging`() { - val contract = contract { - input { - messageFrom = messageFrom("input") - messageBody = messageBody("foo" to "bar") - headers { - header("foo", "bar") - header("X-Custom-Header", value(consumer(regex("^.*2134.*\$")), producer("121345"))) - } - } - outputMessage { - sentTo = sentTo("output") - body = body("foo" to "bar", "foo2" to "bar2") - headers { - header("foo2", "bar") - header("X-Custom-Header", value(consumer("121345"), producer(regex("^.*2134.*\$")))) - } - } - } - - assertDoesNotThrow { - Contract.assertContract(contract) - }.also { - val input = contract.input - assertThat(input.messageFrom.clientValue).isEqualTo("input") - assertThat(input.messageFrom.serverValue).isEqualTo("input") - assertThat(input.messageBody.clientValue).isEqualTo(mapOf("foo" to "bar")) - assertThat(input.messageBody.serverValue).isEqualTo(mapOf("foo" to "bar")) - val headers = input.messageHeaders.entries - assertThat(headers).hasSize(2) - assertThat(headers.elementAt(0).name).isEqualTo("foo") - assertThat(headers.elementAt(0).clientValue).isEqualTo("bar") - assertThat(headers.elementAt(0).serverValue).isEqualTo("bar") - assertThat(headers.elementAt(1).name).isEqualTo("X-Custom-Header") - assertThat(headers.elementAt(1).clientValue).isInstanceOf(RegexProperty::class.java) - assertThat((headers.elementAt(1).clientValue as RegexProperty).pattern()).isEqualTo("^.*2134.*\$") - assertThat(headers.elementAt(1).serverValue).isEqualTo("121345") - }.also { - val output = contract.outputMessage - assertThat(output.sentTo.clientValue).isEqualTo("output") - assertThat(output.sentTo.serverValue).isEqualTo("output") - assertThat(output.body.clientValue).isEqualTo(mapOf("foo" to "bar", - "foo2" to "bar2" - )) - assertThat(output.body.serverValue).isEqualTo(mapOf("foo" to "bar", - "foo2" to "bar2" - )) - val headers = output.headers.entries - assertThat(headers).hasSize(2) - assertThat(headers.elementAt(0).name).isEqualTo("foo2") - assertThat(headers.elementAt(0).clientValue).isEqualTo("bar") - assertThat(headers.elementAt(0).serverValue).isEqualTo("bar") - assertThat(headers.elementAt(1).name).isEqualTo("X-Custom-Header") - assertThat(headers.elementAt(1).clientValue).isEqualTo("121345") - assertThat(headers.elementAt(1).serverValue).isInstanceOf(RegexProperty::class.java) - assertThat((headers.elementAt(1).serverValue as RegexProperty).pattern()).isEqualTo("^.*2134.*\$") - } - } - - @Test - fun `should work for messaging with pattern properties`() { - val contract = contract { - input { - messageFrom("input") - messageBody("foo" to anyNonBlankString) - headers { - header("foo", anyNumber) - } - } - outputMessage { - sentTo("output") - body("foo2" to anyNonEmptyString) - headers { - header("foo2", anyIpAddress) - } - } - } - - assertDoesNotThrow { - Contract.assertContract(contract) - } - } - @Test fun `should set a description`() { val contract = @@ -888,4 +804,4 @@ then: } } -} \ No newline at end of file +} diff --git a/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy b/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy index 2937b11d71..d261db7b31 100644 --- a/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy +++ b/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy @@ -101,65 +101,6 @@ class ContractSpec extends Specification { ex.message.contains("Status is missing for HTTP contract") } - def 'should work for messaging'() { - when: - Contract.make { - input { - messageFrom('input') - messageBody([ - foo: 'bar' - ]) - messageHeaders { - header([ - foo: 'bar' - ]) - } - } - outputMessage { - sentTo('output') - body([ - foo2: 'bar' - ]) - headers { - header([ - foo2: 'bar' - ]) - } - } - } - then: - noExceptionThrown() - } - - def 'should work for messaging with pattern properties'() { - when: - Contract.make { - input { - messageFrom('input') - messageBody([ - foo: anyNonBlankString() - ]) - messageHeaders { - header([ - foo: anyNumber() - ]) - } - } - outputMessage { - sentTo('output') - body([ - foo2: anyNonEmptyString() - ]) - headers { - header([ - foo2: anyIpAddress() - ]) - } - } - } - then: - noExceptionThrown() - } def 'should set a description'() { given: 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 371a1f48bf..66d129ad39 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 @@ -57,7 +57,7 @@ class StubRunnerExecutor implements StubFinder { private final AvailablePortScanner portScanner; - private final MessageVerifierSender contractVerifierMessaging; + private final MessageVerifierSender messageVerifierSender; private final List serverStubs; @@ -65,10 +65,10 @@ class StubRunnerExecutor implements StubFinder { private final YamlContractConverter yamlContractConverter = new YamlContractConverter(); - StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifierSender contractVerifierMessaging, + StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifierSender messageVerifierSender, List serverStubs) { this.portScanner = portScanner; - this.contractVerifierMessaging = contractVerifierMessaging; + this.messageVerifierSender = messageVerifierSender; this.serverStubs = serverStubs; } @@ -249,7 +249,7 @@ class StubRunnerExecutor implements StubFinder { YamlContract contract = yamlContracts.get(0); setMessageType(contract, ContractVerifierMessageMetadata.MessageType.OUTPUT); // TODO: Json is harcoded here - this.contractVerifierMessaging.send( + this.messageVerifierSender.send( JsonOutput .toJson(BodyExtractor.extractClientValueFromBody(body == null ? null : body.getClientValue())), headers == null ? null : headers.asStubSideMap(), outputMessage.getSentTo().getClientValue(), contract); diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/StubRunnerStreamsIntegrationAutoConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/StubRunnerStreamsIntegrationAutoConfiguration.java deleted file mode 100644 index c0ecff8a32..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/StubRunnerStreamsIntegrationAutoConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging; - -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; -import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; -import org.springframework.context.annotation.Configuration; - -/** - * Supports - * {@link org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner} by - * loading in AutoConfigurations related to Stream and Integration only if the relevant - * jars are in classpath. - * - * @author Biju Kunjummen - */ -@Configuration(proxyBeanMethods = false) -public class StubRunnerStreamsIntegrationAutoConfiguration { - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(TestChannelBinderConfiguration.class) - @ImportAutoConfiguration(classes = TestChannelBinderConfiguration.class) - static class StreamsRelatedAutoConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(IntegrationAutoConfiguration.class) - @ImportAutoConfiguration(classes = { IntegrationAutoConfiguration.class }) - static class IntegrationRelatedAutoConfiguration { - - } - -} 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 deleted file mode 100644 index 1942a6228c..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelConfiguration.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.camel; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.apache.camel.RoutesBuilder; -import org.apache.camel.builder.RouteBuilder; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -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; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -/** - * Camel configuration that iterates over the downloaded Groovy DSLs and registers a route - * for each DSL. - * - * @author Marcin Grzejszczak - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(RoutesBuilder.class) -@ConditionalOnProperty(name = "stubrunner.camel.enabled", havingValue = "true", matchIfMissing = true) -public class StubRunnerCamelConfiguration { - - static final String STUBRUNNER_DESTINATION_URL_HEADER_NAME = "STUBRUNNER_DESTINATION_URL"; - - @Bean - public RoutesBuilder myRouter(final BatchStubRunner batchStubRunner) { - return new RouteBuilder() { - @Override - public void configure() throws Exception { - Map> contracts = batchStubRunner.getContracts(); - for (Map.Entry> entry : contracts.entrySet()) { - Collection value = entry.getValue(); - MultiValueMap map = new LinkedMultiValueMap<>(); - for (Contract dsl : value) { - if (dsl == null) { - continue; - } - if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null - && StringUtils.hasText(dsl.getInput().getMessageFrom().getClientValue())) { - String from = dsl.getInput().getMessageFrom().getClientValue(); - map.add(from, dsl); - } - } - for (Map.Entry> entries : map.entrySet()) { - from(entries.getKey()).filter(new StubRunnerCamelPredicate(entries.getValue())) - .process(new StubRunnerCamelProcessor()).dynamicRouter( - header(StubRunnerCamelConfiguration.STUBRUNNER_DESTINATION_URL_HEADER_NAME)); - } - } - } - }; - } - - @Bean - DummyProcessor dummyStubRunnerProcessor() { - return new DummyProcessor(); - } - - private static class DummyProcessor implements Processor { - - private static final Log log = LogFactory.getLog(DummyProcessor.class); - - @Override - public void process(Exchange exchange) { - if (log.isTraceEnabled()) { - log.trace("Got exchange [" + exchange + "]"); - } - } - - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPayload.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPayload.java deleted file mode 100644 index 6ae0d24835..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPayload.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.camel; - -import org.springframework.cloud.contract.spec.Contract; - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerCamelPayload { - - final Object payload; - - final Contract contract; - - StubRunnerCamelPayload(Contract contract) { - this.contract = contract; - this.payload = null; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java deleted file mode 100644 index e92a4bc192..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.camel; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import com.toomuchcoding.jsonassert.JsonAssertion; -import org.apache.camel.Exchange; -import org.apache.camel.Message; -import org.apache.camel.Predicate; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.BodyMatcher; -import org.springframework.cloud.contract.spec.internal.BodyMatchers; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.spec.internal.RegexProperty; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.util.ContentType; -import org.springframework.cloud.contract.verifier.util.ContentUtils; -import org.springframework.cloud.contract.verifier.util.JsonPaths; -import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter; -import org.springframework.cloud.contract.verifier.util.MapConverter; -import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable; - -/** - * Passes through a message that matches the one defined in the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerCamelPredicate implements Predicate { - - private static final Log log = LogFactory.getLog(StubRunnerCamelPredicate.class); - - private final List groovyDsls; - - private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper(); - - StubRunnerCamelPredicate(List groovyDsls) { - this.groovyDsls = groovyDsls; - } - - @Override - public boolean matches(Exchange exchange) { - Contract contract = getContract(exchange.getMessage()); - if (log.isDebugEnabled()) { - log.debug("For exchange [" + exchange + "] found contract [" + contract + "]"); - } - if (contract == null) { - return false; - } - exchange.getIn().setBody(new StubRunnerCamelPayload(contract)); - return true; - } - - private Contract getContract(Message message) { - for (Contract groovyDsl : this.groovyDsls) { - Contract contract = matchContract(message, groovyDsl); - if (contract != null) { - return contract; - } - } - return null; - } - - private Contract matchContract(Message message, Contract groovyDsl) { - List unmatchedHeaders = headersMatch(message, groovyDsl); - if (!unmatchedHeaders.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders); - } - return null; - } - Object inputMessage = message.getBody(); - Object dslBody = MapConverter.getStubSideValues(groovyDsl.getInput().getMessageBody()); - if (dslBody instanceof FromFileProperty) { - if (log.isDebugEnabled()) { - log.debug("Will compare file content"); - } - FromFileProperty property = (FromFileProperty) dslBody; - if (property.isString()) { - // continue processing as if body was pure string - dslBody = property.asString(); - } - else if (!(inputMessage instanceof byte[])) { - if (log.isDebugEnabled()) { - log.debug("Contract provided byte comparison, but the input message is of type [" - + inputMessage.getClass() + "]. Can't compare the two."); - } - return null; - } - else { - boolean matches = Arrays.equals(property.asBytes(), (byte[]) inputMessage); - if (log.isDebugEnabled() && !matches) { - log.debug("Contract provided byte comparison, but the byte arrays don't match"); - } - return matches ? groovyDsl : null; - } - } - if (matchViaContent(groovyDsl, inputMessage, dslBody)) { - return groovyDsl; - } - return null; - } - - private boolean matchViaContent(Contract groovyDsl, Object inputMessage, Object dslBody) { - boolean matches; - ContentType type = ContentUtils.getClientContentType(inputMessage, groovyDsl.getInput().getMessageHeaders()); - if (type == ContentType.JSON) { - BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers(); - matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody); - } - else if (dslBody instanceof RegexProperty && inputMessage instanceof String) { - Pattern pattern = ((RegexProperty) dslBody).getPattern(); - matches = pattern.matcher((String) inputMessage).matches(); - bodyUnmatchedLog(dslBody, matches, pattern); - } - else { - matches = dslBody.equals(inputMessage); - bodyUnmatchedLog(dslBody, matches, inputMessage); - } - return matches; - } - - private void bodyUnmatchedLog(Object dslBody, boolean matches, Object pattern) { - if (log.isDebugEnabled() && !matches) { - log.debug("Body was supposed to " + unmatchedText(pattern) + " but the value is [" + dslBody.toString() - + "]"); - } - } - - private boolean matchesForJsonPayload(Contract groovyDsl, Object inputMessage, BodyMatchers matchers, - Object dslBody) { - Object matchingInputMessage = JsonToJsonPathsConverter.removeMatchingJsonPaths(dslBody, matchers); - JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(matchingInputMessage); - DocumentContext parsedJson; - try { - parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage)); - } - catch (JsonProcessingException e) { - throw new IllegalStateException("Cannot serialize to JSON", e); - } - List unmatchedJsonPath = new ArrayList<>(); - boolean matches = true; - for (MethodBufferingJsonVerifiable path : jsonPaths) { - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, path.jsonPath()); - } - if (matchers != null && matchers.hasMatchers()) { - for (BodyMatcher matcher : matchers.matchers()) { - String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody); - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath); - } - } - if (!unmatchedJsonPath.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] didn't match the body due to " + unmatchedJsonPath); - } - } - return matches; - } - - private boolean matchesJsonPath(List unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) { - try { - JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath); - return true; - } - catch (Exception e) { - unmatchedJsonPath.add(e.getLocalizedMessage()); - return false; - } - } - - private List headersMatch(Message message, Contract groovyDsl) { - List unmatchedHeaders = new ArrayList<>(); - Map headers = message.getHeaders(); - for (Header it : groovyDsl.getInput().getMessageHeaders().getEntries()) { - String name = it.getName(); - Object value = it.getClientValue(); - Object valueInHeader = headers.get(name); - boolean matches; - if (value instanceof RegexProperty) { - Pattern pattern = ((RegexProperty) value).getPattern(); - matches = pattern.matcher(valueInHeader.toString()).matches(); - } - else { - matches = valueInHeader != null && valueInHeader.toString().equals(value.toString()); - } - if (!matches) { - unmatchedHeaders.add("Header with name [" + name + "] was supposed to " + unmatchedText(value) - + " but the value is [" + (valueInHeader != null ? valueInHeader.toString() : "null") + "]"); - } - } - return unmatchedHeaders; - } - - private String unmatchedText(Object expectedValue) { - return expectedValue instanceof RegexProperty - ? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]" - : "be equal to [" + expectedValue + "]"; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessor.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessor.java deleted file mode 100644 index 52b08473cb..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessor.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.camel; - -import org.apache.camel.Exchange; -import org.apache.camel.Message; -import org.apache.camel.Processor; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.verifier.util.BodyExtractor; - -/** - * Sends forward a message defined in the DSL. Also removes headers from the input message - * and provides the headers from the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerCamelProcessor implements Processor { - - private static final Log log = LogFactory.getLog(StubRunnerCamelProcessor.class); - - private static final String DUMMY_BEAN_URL = "bean:dummyStubRunnerProcessor"; - - @Override - public void process(Exchange exchange) { - Message input = exchange.getIn(); - StubRunnerCamelPayload body = input.getBody(StubRunnerCamelPayload.class); - Contract groovyDsl = body.contract; - setStubRunnerDestinationHeader(exchange, body); - if (groovyDsl.getInput().getMessageHeaders() != null) { - for (Header entry : groovyDsl.getInput().getMessageHeaders().getEntries()) { - input.removeHeader(entry.getName()); - } - } - if (groovyDsl.getOutputMessage() == null) { - if (log.isDebugEnabled()) { - log.debug("No output message provided, will not modify the body"); - } - return; - } - input.setBody(outputBody(groovyDsl)); - if (groovyDsl.getOutputMessage().getHeaders() != null) { - for (Header entry : groovyDsl.getOutputMessage().getHeaders().getEntries()) { - input.setHeader(entry.getName(), entry.getClientValue()); - } - } - } - - private Object outputBody(Contract groovyDsl) { - Object outputBody = BodyExtractor.extractClientValueFromBody(groovyDsl.getOutputMessage().getBody()); - if (outputBody instanceof FromFileProperty) { - FromFileProperty property = (FromFileProperty) outputBody; - return property.asBytes(); - } - return BodyExtractor.extractStubValueFrom(outputBody); - } - - private void setStubRunnerDestinationHeader(Exchange exchange, StubRunnerCamelPayload body) { - boolean outputPart = body.contract.getOutputMessage() != null; - String url = DUMMY_BEAN_URL; - if (outputPart && body.contract.getOutputMessage().getSentTo() != null) { - url = body.contract.getOutputMessage().getSentTo().getClientValue(); - } - exchange.getIn().setHeader(StubRunnerCamelConfiguration.STUBRUNNER_DESTINATION_URL_HEADER_NAME, url); - if (log.isDebugEnabled()) { - log.debug("Set stub runner destination header to [" + url + "]"); - } - } - -} 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 deleted file mode 100644 index 500335d6e8..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.integration; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Consumer; - -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; -import org.springframework.context.Lifecycle; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.integration.dsl.FilterEndpointSpec; -import org.springframework.integration.dsl.IntegrationFlowBuilder; -import org.springframework.integration.dsl.IntegrationFlows; -import org.springframework.messaging.Message; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -/** - * Spring Integration configuration that iterates over the downloaded Groovy DSLs and - * registers a flow for each DSL. - * - * @author Marcin Grzejszczak - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(IntegrationFlowBuilder.class) -@ConditionalOnProperty(name = "stubrunner.integration.enabled", havingValue = "true", matchIfMissing = true) -public class StubRunnerIntegrationConfiguration { - - @Bean - @ConditionalOnMissingBean(name = "stubFlowRegistrar") - public FlowRegistrar stubFlowRegistrar(AutowireCapableBeanFactory beanFactory, BatchStubRunner batchStubRunner) { - Map> contracts = batchStubRunner.getContracts(); - IntegrationFlowBuilder dummyBuilder = IntegrationFlows.from(DummyMessageHandler.CHANNEL_NAME) - .handle(new DummyMessageHandler(), "handle"); - beanFactory.initializeBean(dummyBuilder.get(), DummyMessageHandler.CHANNEL_NAME + ".flow"); - for (Entry> entry : contracts.entrySet()) { - StubConfiguration key = entry.getKey(); - Collection value = entry.getValue(); - String name = key.getGroupId() + "_" + key.getArtifactId(); - MultiValueMap map = new LinkedMultiValueMap<>(); - for (Contract dsl : value) { - if (dsl == null) { - continue; - } - if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null - && StringUtils.hasText(dsl.getInput().getMessageFrom().getClientValue())) { - String from = dsl.getInput().getMessageFrom().getClientValue(); - map.add(from, dsl); - } - } - for (Entry> entries : map.entrySet()) { - final String flowName = name + "_" + entries.getKey() + "_" + entries.getValue().hashCode(); - IntegrationFlowBuilder builder = IntegrationFlows.from(entries.getKey()) - .filter(new StubRunnerIntegrationMessageSelector(entries.getValue()), - new Consumer() { - @Override - public void accept(FilterEndpointSpec e) { - e.id(flowName + ".filter"); - } - }) - .transform(new StubRunnerIntegrationTransformer(entries.getValue())) - .route(new StubRunnerIntegrationRouter(entries.getValue(), beanFactory)); - beanFactory.initializeBean(builder.get(), flowName); - beanFactory.getBean(flowName + ".filter", Lifecycle.class).start(); - } - - } - return new FlowRegistrar(); - } - - static class DummyMessageHandler { - - static String CHANNEL_NAME = "stub_runner_dummy_channel"; - - public void handle(Message message) { - } - - } - - static class FlowRegistrar { - - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java deleted file mode 100644 index 7176576c91..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.integration; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import com.toomuchcoding.jsonassert.JsonAssertion; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.BodyMatcher; -import org.springframework.cloud.contract.spec.internal.BodyMatchers; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.spec.internal.RegexProperty; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.util.ContentType; -import org.springframework.cloud.contract.verifier.util.ContentUtils; -import org.springframework.cloud.contract.verifier.util.JsonPaths; -import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter; -import org.springframework.cloud.contract.verifier.util.MapConverter; -import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable; -import org.springframework.integration.core.MessageSelector; -import org.springframework.messaging.Message; - -/** - * Passes through a message that matches the one defined in the DSL. - * - * @author Marcin Grzejszczak - * @author Tim Ysewyn - */ -class StubRunnerIntegrationMessageSelector implements MessageSelector { - - private static final Map CACHE = Collections.synchronizedMap(new WeakHashMap<>()); - - private static final Log log = LogFactory.getLog(StubRunnerIntegrationMessageSelector.class); - - private final List groovyDsls; - - private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper(); - - StubRunnerIntegrationMessageSelector(Contract groovyDsl) { - this(Collections.singletonList(groovyDsl)); - } - - StubRunnerIntegrationMessageSelector(List groovyDsls) { - this.groovyDsls = groovyDsls; - } - - @Override - public boolean accept(Message message) { - return matchingContract(message) != null; - } - - Contract matchingContract(Message message) { - if (CACHE.containsKey(message)) { - return CACHE.get(message); - } - Contract contract = getContract(message); - if (contract != null) { - CACHE.put(message, contract); - } - return contract; - } - - void updateCache(Message message, Contract contract) { - CACHE.put(message, contract); - } - - private Contract getContract(Message message) { - for (Contract groovyDsl : this.groovyDsls) { - Contract contract = matchContract(message, groovyDsl); - if (contract != null) { - return contract; - } - } - return null; - } - - private Contract matchContract(Message message, Contract groovyDsl) { - List unmatchedHeaders = headersMatch(message, groovyDsl); - if (!unmatchedHeaders.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders); - } - return null; - } - Object inputMessage = message.getPayload(); - Object dslBody = MapConverter.getStubSideValues(groovyDsl.getInput().getMessageBody()); - if (dslBody instanceof FromFileProperty) { - if (log.isDebugEnabled()) { - log.debug("Will compare file content"); - } - FromFileProperty property = (FromFileProperty) dslBody; - if (property.isString()) { - // continue processing as if body was pure string - dslBody = property.asString(); - } - else if (!(inputMessage instanceof byte[])) { - if (log.isDebugEnabled()) { - log.debug("Contract provided byte comparison, but the input message is of type [" - + inputMessage.getClass() + "]. Can't compare the two."); - } - return null; - } - else { - boolean matches = Arrays.equals(property.asBytes(), (byte[]) inputMessage); - if (log.isDebugEnabled() && !matches) { - log.debug("Contract provided byte comparison, but the byte arrays don't match"); - } - return matches ? groovyDsl : null; - } - } - if (matchViaContent(groovyDsl, inputMessage, dslBody)) { - return groovyDsl; - } - return null; - } - - private boolean matchViaContent(Contract groovyDsl, Object inputMessage, Object dslBody) { - boolean matches; - ContentType type = ContentUtils.getClientContentType(inputMessage, groovyDsl.getInput().getMessageHeaders()); - if (type == ContentType.JSON) { - BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers(); - matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody); - } - else if (dslBody instanceof RegexProperty && inputMessage instanceof String) { - Pattern pattern = ((RegexProperty) dslBody).getPattern(); - matches = pattern.matcher((String) inputMessage).matches(); - bodyUnmatchedLog(dslBody, matches, pattern); - } - else { - matches = dslBody.equals(inputMessage); - bodyUnmatchedLog(dslBody, matches, inputMessage); - } - return matches; - } - - private void bodyUnmatchedLog(Object dslBody, boolean matches, Object pattern) { - if (log.isDebugEnabled() && !matches) { - log.debug("Body was supposed to " + unmatchedText(pattern) + " but the value is [" + dslBody.toString() - + "]"); - } - } - - private boolean matchesForJsonPayload(Contract groovyDsl, Object inputMessage, BodyMatchers matchers, - Object dslBody) { - Object matchingInputMessage = JsonToJsonPathsConverter.removeMatchingJsonPaths(dslBody, matchers); - JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(matchingInputMessage); - DocumentContext parsedJson; - try { - parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage)); - } - catch (JsonProcessingException e) { - throw new IllegalStateException("Cannot serialize to JSON", e); - } - List unmatchedJsonPath = new ArrayList<>(); - boolean matches = true; - for (MethodBufferingJsonVerifiable path : jsonPaths) { - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, path.jsonPath()); - } - if (matchers != null && matchers.hasMatchers()) { - for (BodyMatcher matcher : matchers.matchers()) { - String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody); - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath); - } - } - if (!unmatchedJsonPath.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] didn't match the body due to " + unmatchedJsonPath); - } - } - return matches; - } - - private boolean matchesJsonPath(List unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) { - try { - JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath); - return true; - } - catch (Exception e) { - unmatchedJsonPath.add(e.getLocalizedMessage()); - return false; - } - } - - private List headersMatch(Message message, Contract groovyDsl) { - List unmatchedHeaders = new ArrayList<>(); - Map headers = message.getHeaders(); - for (Header it : groovyDsl.getInput().getMessageHeaders().getEntries()) { - String name = it.getName(); - Object value = it.getClientValue(); - Object valueInHeader = headers.get(name); - boolean matches; - if (value instanceof RegexProperty || value instanceof Pattern) { - Pattern pattern = new RegexProperty(value).getPattern(); - matches = pattern.matcher(valueInHeader.toString()).matches(); - } - else { - matches = valueInHeader != null && valueInHeader.toString().equals(value.toString()); - } - if (!matches) { - unmatchedHeaders.add("Header with name [" + name + "] was supposed to " + unmatchedText(value) - + " but the value is [" + (valueInHeader != null ? valueInHeader.toString() : "null") + "]"); - } - } - return unmatchedHeaders; - } - - private String unmatchedText(Object expectedValue) { - return expectedValue instanceof RegexProperty - ? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]" - : "be equal to [" + expectedValue + "]"; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationRouter.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationRouter.java deleted file mode 100644 index d88ce8ec24..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationRouter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.integration; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.integration.router.AbstractMessageRouter; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerIntegrationRouter extends AbstractMessageRouter { - - private final StubRunnerIntegrationMessageSelector selector; - - private final BeanFactory beanFactory; - - StubRunnerIntegrationRouter(List groovyDsls, BeanFactory beanFactory) { - this.selector = new StubRunnerIntegrationMessageSelector(groovyDsls); - this.beanFactory = beanFactory; - } - - @Override - protected Collection determineTargetChannels(Message message) { - Contract dsl = this.selector.matchingContract(message); - if (dsl != null && dsl.getOutputMessage() != null && dsl.getOutputMessage().getSentTo() != null) { - String channelName = dsl.getOutputMessage().getSentTo().getClientValue(); - return Collections.singleton((MessageChannel) this.beanFactory.getBean(channelName)); - } - return Collections.singleton((MessageChannel) this.beanFactory - .getBean(StubRunnerIntegrationConfiguration.DummyMessageHandler.CHANNEL_NAME)); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformer.java deleted file mode 100644 index 120c7fc794..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.integration; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.verifier.util.BodyExtractor; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - -/** - * Sends forward a message defined in the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerIntegrationTransformer { - - private final StubRunnerIntegrationMessageSelector selector; - - StubRunnerIntegrationTransformer(Contract groovyDsl) { - this(Collections.singletonList(groovyDsl)); - } - - StubRunnerIntegrationTransformer(List groovyDsls) { - this.selector = new StubRunnerIntegrationMessageSelector(groovyDsls); - } - - public Message transform(Message source) { - Contract groovyDsl = matchingContract(source); - if (groovyDsl == null || groovyDsl.getOutputMessage() == null) { - return source; - } - Object outputBody = outputBody(groovyDsl); - Map headers = groovyDsl.getOutputMessage().getHeaders().asStubSideMap(); - MessageHeaders messageHeaders = new MessageHeaders(headers); - Message message = MessageBuilder.createMessage(outputBody, messageHeaders); - this.selector.updateCache(message, groovyDsl); - return message; - } - - private Object outputBody(Contract groovyDsl) { - Object outputBody = BodyExtractor.extractClientValueFromBody(groovyDsl.getOutputMessage().getBody()); - if (outputBody instanceof FromFileProperty) { - FromFileProperty property = (FromFileProperty) outputBody; - return property.asBytes(); - } - return BodyExtractor.extractStubValueFrom(outputBody); - } - - Contract matchingContract(Message source) { - return this.selector.matchingContract(source); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsAccessor.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsAccessor.java deleted file mode 100644 index a5fab6b931..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsAccessor.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.jms; - -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -import jakarta.jms.JMSException; -import jakarta.jms.Message; -import jakarta.jms.ObjectMessage; -import jakarta.jms.StreamMessage; -import jakarta.jms.TextMessage; - -final class StubRunnerJmsAccessor { - - private StubRunnerJmsAccessor() { - throw new IllegalStateException("Can't instantiate an utility class"); - } - - static Object getBody(Message message) { - try { - return getPayload(message); - } - catch (JMSException ex) { - throw new IllegalStateException(ex); - } - } - - static Map getHeaders(Message message) { - try { - return headers(message); - } - catch (JMSException ex) { - throw new IllegalStateException(ex); - } - } - - private static Map headers(Message message) throws JMSException { - Map headers = new HashMap<>(); - if (message == null) { - return headers; - } - Enumeration enumeration = message.getPropertyNames(); - while (enumeration.hasMoreElements()) { - Object element = enumeration.nextElement(); - String asString = element.toString(); - Object property = message.getObjectProperty(asString); - headers.put(asString, property); - } - return headers; - } - - private static Object getPayload(Message message) throws JMSException { - if (message == null) { - return null; - } - else if (message instanceof TextMessage) { - return ((TextMessage) message).getText(); - } - else if (message instanceof StreamMessage) { - return ((StreamMessage) message).readObject(); - } - else if (message instanceof ObjectMessage) { - return ((ObjectMessage) message).getObject(); - } - return message.getBody(Object.class); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsConfiguration.java deleted file mode 100644 index 917edafa8f..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsConfiguration.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.jms; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import jakarta.jms.ConnectionFactory; -import jakarta.jms.MessageListener; - -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -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.verifier.util.MapConverter; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.jms.core.JmsTemplate; -import org.springframework.jms.listener.DefaultMessageListenerContainer; -import org.springframework.jms.listener.MessageListenerContainer; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -/** - * Spring Integration configuration that iterates over the downloaded Groovy DSLs and - * registers a flow for each DSL. - * - * @author Marcin Grzejszczak - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(JmsTemplate.class) -@ConditionalOnProperty(name = "stubrunner.jms.enabled", havingValue = "true", matchIfMissing = true) -public class StubRunnerJmsConfiguration { - - @Bean - @ConditionalOnMissingBean(name = "stubFlowRegistrar") - public FlowRegistrar stubFlowRegistrar(ConfigurableListableBeanFactory beanFactory, - BatchStubRunner batchStubRunner) { - Map> contracts = batchStubRunner.getContracts(); - for (Entry> entry : contracts.entrySet()) { - StubConfiguration key = entry.getKey(); - Collection value = entry.getValue(); - String name = key.getGroupId() + "_" + key.getArtifactId(); - MultiValueMap map = new LinkedMultiValueMap<>(); - for (Contract dsl : value) { - if (dsl == null) { - continue; - } - if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null - && StringUtils.hasText(dsl.getInput().getMessageFrom().getClientValue())) { - String from = dsl.getInput().getMessageFrom().getClientValue(); - map.add(from, dsl); - } - } - for (Entry> entries : map.entrySet()) { - List matchingContracts = entries.getValue(); - final String flowName = name + "_" + entries.getKey() + "_" + Math.abs(matchingContracts.hashCode()); - // listener - StubRunnerJmsRouter router = new StubRunnerJmsRouter(matchingContracts, beanFactory); - StubRunnerJmsRouter listener = (StubRunnerJmsRouter) beanFactory.initializeBean(router, flowName); - beanFactory.registerSingleton(flowName, listener); - registerContainers(beanFactory, matchingContracts, flowName, listener); - } - - } - return new FlowRegistrar(); - } - - private void registerContainers(ConfigurableListableBeanFactory beanFactory, List matchingContracts, - String flowName, StubRunnerJmsRouter listener) { - // listener's container - ConnectionFactory connectionFactory = beanFactory.getBean(ConnectionFactory.class); - for (Contract matchingContract : matchingContracts) { - if (matchingContract.getInput() == null) { - continue; - } - String destination = MapConverter.getStubSideValuesForNonBody(matchingContract.getInput().getMessageFrom()) - .toString(); - MessageListenerContainer container = listenerContainer(destination, connectionFactory, listener); - String containerName = flowName + ".container"; - Object initializedContainer = beanFactory.initializeBean(container, containerName); - beanFactory.registerSingleton(containerName, initializedContainer); - } - } - - private MessageListenerContainer listenerContainer(String queueName, ConnectionFactory connectionFactory, - MessageListener listener) { - DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); - container.setConnectionFactory(connectionFactory); - container.setDestinationName(queueName); - container.setMessageListener(listener); - return container; - } - - static class FlowRegistrar { - - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsMessageSelector.java deleted file mode 100644 index acae7b27d3..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsMessageSelector.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.jms; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import com.toomuchcoding.jsonassert.JsonAssertion; -import jakarta.jms.Message; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.BodyMatcher; -import org.springframework.cloud.contract.spec.internal.BodyMatchers; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.spec.internal.RegexProperty; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.util.ContentType; -import org.springframework.cloud.contract.verifier.util.ContentUtils; -import org.springframework.cloud.contract.verifier.util.JsonPaths; -import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter; -import org.springframework.cloud.contract.verifier.util.MapConverter; -import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable; - -/** - * Passes through a message that matches the one defined in the DSL. - * - * @author Marcin Grzejszczak - * @author Tim Ysewyn - */ -class StubRunnerJmsMessageSelector { - - private static final Map CACHE = Collections.synchronizedMap(new WeakHashMap<>()); - - private static final Log log = LogFactory.getLog(StubRunnerJmsMessageSelector.class); - - private final List groovyDsls; - - private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper(); - - StubRunnerJmsMessageSelector(List groovyDsls) { - this.groovyDsls = groovyDsls; - } - - Contract matchingContract(Message message) { - if (CACHE.containsKey(message)) { - return CACHE.get(message); - } - Contract contract = getContract(message); - if (contract != null) { - CACHE.put(message, contract); - } - return contract; - } - - void updateCache(Message message, Contract contract) { - CACHE.put(message, contract); - } - - private Contract getContract(Message message) { - for (Contract groovyDsl : this.groovyDsls) { - Contract contract = matchContract(message, groovyDsl); - if (contract != null) { - return contract; - } - } - return null; - } - - private Contract matchContract(Message message, Contract groovyDsl) { - List unmatchedHeaders = headersMatch(message, groovyDsl); - if (!unmatchedHeaders.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders); - } - return null; - } - Object inputMessage = StubRunnerJmsAccessor.getBody(message); - Object dslBody = MapConverter.getStubSideValues(groovyDsl.getInput().getMessageBody()); - if (dslBody instanceof FromFileProperty) { - if (log.isDebugEnabled()) { - log.debug("Will compare file content"); - } - FromFileProperty property = (FromFileProperty) dslBody; - if (property.isString()) { - // continue processing as if body was pure string - dslBody = property.asString(); - } - else if (!(inputMessage instanceof byte[])) { - if (log.isDebugEnabled()) { - log.debug("Contract provided byte comparison, but the input message is of type [" - + inputMessage.getClass() + "]. Can't compare the two."); - } - return null; - } - else { - boolean matches = Arrays.equals(property.asBytes(), (byte[]) inputMessage); - if (log.isDebugEnabled() && !matches) { - log.debug("Contract provided byte comparison, but the byte arrays don't match"); - } - return matches ? groovyDsl : null; - } - } - if (matchViaContent(groovyDsl, inputMessage, dslBody)) { - return groovyDsl; - } - return null; - } - - private boolean matchViaContent(Contract groovyDsl, Object inputMessage, Object dslBody) { - boolean matches; - ContentType type = ContentUtils.getClientContentType(inputMessage, groovyDsl.getInput().getMessageHeaders()); - if (type == ContentType.JSON) { - BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers(); - matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody); - } - else if (dslBody instanceof RegexProperty && inputMessage instanceof String) { - Pattern pattern = ((RegexProperty) dslBody).getPattern(); - matches = pattern.matcher((String) inputMessage).matches(); - bodyUnmatchedLog(dslBody, matches, pattern); - } - else { - matches = dslBody.equals(inputMessage); - bodyUnmatchedLog(dslBody, matches, inputMessage); - } - return matches; - } - - private void bodyUnmatchedLog(Object dslBody, boolean matches, Object pattern) { - if (log.isDebugEnabled() && !matches) { - log.debug("Body was supposed to " + unmatchedText(pattern) + " but the value is [" + dslBody.toString() - + "]"); - } - } - - private boolean matchesForJsonPayload(Contract groovyDsl, Object inputMessage, BodyMatchers matchers, - Object dslBody) { - Object matchingInputMessage = JsonToJsonPathsConverter.removeMatchingJsonPaths(dslBody, matchers); - JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(matchingInputMessage); - DocumentContext parsedJson; - try { - parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage)); - } - catch (JsonProcessingException e) { - throw new IllegalStateException("Cannot serialize to JSON", e); - } - List unmatchedJsonPath = new ArrayList<>(); - boolean matches = true; - for (MethodBufferingJsonVerifiable path : jsonPaths) { - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, path.jsonPath()); - } - if (matchers != null && matchers.hasMatchers()) { - for (BodyMatcher matcher : matchers.matchers()) { - String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody); - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath); - } - } - if (!unmatchedJsonPath.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] didn't match the body due to " + unmatchedJsonPath); - } - } - return matches; - } - - private boolean matchesJsonPath(List unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) { - try { - JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath); - return true; - } - catch (Exception e) { - unmatchedJsonPath.add(e.getLocalizedMessage()); - return false; - } - } - - private List headersMatch(Message message, Contract groovyDsl) { - List unmatchedHeaders = new ArrayList<>(); - Map headers = StubRunnerJmsAccessor.getHeaders(message); - for (Header it : groovyDsl.getInput().getMessageHeaders().getEntries()) { - String name = it.getName(); - Object value = it.getClientValue(); - Object valueInHeader = headers.get(name); - boolean matches; - if (value instanceof RegexProperty) { - Pattern pattern = ((RegexProperty) value).getPattern(); - matches = pattern.matcher(valueInHeader.toString()).matches(); - } - else { - matches = valueInHeader != null && valueInHeader.toString().equals(value.toString()); - } - if (!matches) { - unmatchedHeaders.add("Header with name [" + name + "] was supposed to " + unmatchedText(value) - + " but the value is [" + (valueInHeader != null ? valueInHeader.toString() : "null") + "]"); - } - } - return unmatchedHeaders; - } - - private String unmatchedText(Object expectedValue) { - return expectedValue instanceof RegexProperty - ? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]" - : "be equal to [" + expectedValue + "]"; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsRouter.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsRouter.java deleted file mode 100644 index 631a09bfbc..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsRouter.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.jms; - -import java.util.List; - -import jakarta.jms.JMSException; -import jakarta.jms.Message; -import jakarta.jms.MessageListener; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.jms.core.JmsTemplate; -import org.springframework.jms.core.MessagePostProcessor; - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerJmsRouter implements MessageListener { - - private final StubRunnerJmsMessageSelector selector; - - private final BeanFactory beanFactory; - - private final List contracts; - - private JmsTemplate jmsTemplate; - - StubRunnerJmsRouter(List groovyDsls, BeanFactory beanFactory) { - this.selector = new StubRunnerJmsMessageSelector(groovyDsls); - this.beanFactory = beanFactory; - this.contracts = groovyDsls; - } - - @Override - public void onMessage(jakarta.jms.Message message) { - Contract dsl = this.selector.matchingContract(message); - if (dsl != null && dsl.getOutputMessage() != null && dsl.getOutputMessage().getSentTo() != null) { - String destination = dsl.getOutputMessage().getSentTo().getClientValue(); - jmsTemplate().send(destination, - session -> new StubRunnerJmsTransformer(this.contracts).transform(session, dsl)); - } - } - - private JmsTemplate jmsTemplate() { - if (this.jmsTemplate == null) { - this.jmsTemplate = this.beanFactory.getBean(JmsTemplate.class); - } - return this.jmsTemplate; - } - -} - -class ReplyToProcessor implements MessagePostProcessor { - - @Override - public jakarta.jms.Message postProcessMessage(Message message) throws JMSException { - message.setStringProperty("requiresReply", "no"); - return message; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsTransformer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsTransformer.java deleted file mode 100644 index 7716fd5bbb..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/jms/StubRunnerJmsTransformer.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.jms; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -import jakarta.jms.BytesMessage; -import jakarta.jms.JMSException; -import jakarta.jms.Message; -import jakarta.jms.Session; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.verifier.util.BodyExtractor; - -/** - * Sends forward a message defined in the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerJmsTransformer { - - private final StubRunnerJmsMessageSelector selector; - - StubRunnerJmsTransformer(List groovyDsls) { - this.selector = new StubRunnerJmsMessageSelector(groovyDsls); - } - - public Message transform(Session session, Contract groovyDsl) { - Object outputBody = outputBody(groovyDsl); - Map headers = groovyDsl.getOutputMessage().getHeaders().asStubSideMap(); - Message newMessage = createMessage(session, outputBody); - setHeaders(newMessage, headers); - this.selector.updateCache(newMessage, groovyDsl); - return newMessage; - } - - private Object outputBody(Contract groovyDsl) { - Object outputBody = BodyExtractor.extractClientValueFromBody(groovyDsl.getOutputMessage().getBody()); - if (outputBody instanceof FromFileProperty) { - FromFileProperty property = (FromFileProperty) outputBody; - return property.asBytes(); - } - return BodyExtractor.extractStubValueFrom(outputBody); - } - - Contract matchingContract(Message source) { - return this.selector.matchingContract(source); - } - - private Message createMessage(Session session, Object payload) { - try { - if (payload instanceof String) { - return session.createTextMessage((String) payload); - } - else if (payload instanceof byte[]) { - BytesMessage bytesMessage = session.createBytesMessage(); - bytesMessage.writeBytes((byte[]) payload); - return bytesMessage; - } - else if (payload instanceof Serializable) { - return session.createObjectMessage((Serializable) payload); - } - return session.createMessage(); - } - catch (Exception ex) { - throw new IllegalStateException(ex); - } - } - - private void setHeaders(Message message, Map headers) { - for (Map.Entry entry : headers.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - try { - if (value instanceof String) { - message.setStringProperty(key, (String) value); - } - else if (value instanceof Boolean) { - message.setBooleanProperty(key, (Boolean) value); - } - else { - message.setObjectProperty(key, value); - } - } - catch (JMSException ex) { - throw new IllegalStateException(ex); - } - } - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaConfiguration.java deleted file mode 100644 index 284408f95e..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaConfiguration.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.kafka; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -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.verifier.util.MapConverter; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.kafka.core.ConsumerFactory; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.kafka.listener.ContainerProperties; -import org.springframework.kafka.listener.GenericMessageListener; -import org.springframework.kafka.listener.KafkaMessageListenerContainer; -import org.springframework.kafka.test.EmbeddedKafkaBroker; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -/** - * Spring Integration configuration that iterates over the downloaded Groovy DSLs and - * registers a flow for each DSL. - * - * @author Marcin Grzejszczak - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ KafkaTemplate.class, EmbeddedKafkaBroker.class }) -@ConditionalOnProperty(name = "stubrunner.kafka.enabled", havingValue = "true", matchIfMissing = true) -@ConditionalOnBean(EmbeddedKafkaBroker.class) -public class StubRunnerKafkaConfiguration { - - private static final Log log = LogFactory.getLog(StubRunnerKafkaConfiguration.class); - - @Bean - @ConditionalOnMissingBean(name = "stubFlowRegistrar") - public FlowRegistrar stubFlowRegistrar(ConfigurableListableBeanFactory beanFactory, - BatchStubRunner batchStubRunner) { - Map> contracts = batchStubRunner.getContracts(); - for (Entry> entry : contracts.entrySet()) { - StubConfiguration key = entry.getKey(); - Collection value = entry.getValue(); - String name = key.getGroupId() + "_" + key.getArtifactId(); - MultiValueMap map = new LinkedMultiValueMap<>(); - for (Contract dsl : value) { - if (dsl == null) { - continue; - } - if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null - && StringUtils.hasText(dsl.getInput().getMessageFrom().getClientValue())) { - String from = dsl.getInput().getMessageFrom().getClientValue(); - map.add(from, dsl); - } - } - for (Entry> entries : map.entrySet()) { - List matchingContracts = entries.getValue(); - final String flowName = name + "_" + entries.getKey() + "_" + Math.abs(matchingContracts.hashCode()); - // listener - StubRunnerKafkaRouter router = new StubRunnerKafkaRouter(matchingContracts, beanFactory); - StubRunnerKafkaRouter listener = (StubRunnerKafkaRouter) beanFactory.initializeBean(router, flowName); - if (log.isDebugEnabled()) { - log.debug("Initialized kafka router with name [" + flowName + "]"); - } - beanFactory.registerSingleton(flowName, listener); - registerContainers(beanFactory, matchingContracts, flowName, listener); - } - - } - return new FlowRegistrar(); - } - - private void registerContainers(ConfigurableListableBeanFactory beanFactory, List matchingContracts, - String flowName, StubRunnerKafkaRouter listener) { - // listener's container - ConsumerFactory consumerFactory = beanFactory.getBean(ConsumerFactory.class); - for (Contract matchingContract : matchingContracts) { - if (matchingContract.getInput() == null) { - continue; - } - String destination = MapConverter.getStubSideValuesForNonBody(matchingContract.getInput().getMessageFrom()) - .toString(); - ContainerProperties containerProperties = new ContainerProperties(destination); - KafkaMessageListenerContainer container = listenerContainer(consumerFactory, containerProperties, listener); - String containerName = flowName + ".container"; - Object initializedContainer = beanFactory.initializeBean(container, containerName); - beanFactory.registerSingleton(containerName, initializedContainer); - if (log.isDebugEnabled()) { - log.debug("Initialized kafka message container with name [" + containerName - + "] listening to destination [" + destination + "]"); - } - } - } - - private KafkaMessageListenerContainer listenerContainer(ConsumerFactory consumerFactory, - ContainerProperties containerProperties, GenericMessageListener listener) { - KafkaMessageListenerContainer container = new KafkaMessageListenerContainer(consumerFactory, - containerProperties); - container.setupMessageListener(listener); - return container; - } - - static class FlowRegistrar { - - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaMessageSelector.java deleted file mode 100644 index ac26117904..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaMessageSelector.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.kafka; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import com.toomuchcoding.jsonassert.JsonAssertion; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.BodyMatcher; -import org.springframework.cloud.contract.spec.internal.BodyMatchers; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.spec.internal.RegexProperty; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.util.ContentType; -import org.springframework.cloud.contract.verifier.util.ContentUtils; -import org.springframework.cloud.contract.verifier.util.JsonPaths; -import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter; -import org.springframework.cloud.contract.verifier.util.MapConverter; -import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable; -import org.springframework.messaging.Message; - -/** - * Passes through a message that matches the one defined in the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerKafkaMessageSelector { - - private static final Map, Contract> CACHE = Collections.synchronizedMap(new WeakHashMap<>()); - - private static final Log log = LogFactory.getLog(StubRunnerKafkaMessageSelector.class); - - private final List groovyDsls; - - private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper(); - - StubRunnerKafkaMessageSelector(List groovyDsls) { - this.groovyDsls = groovyDsls; - } - - Contract matchingContract(Message message) { - if (CACHE.containsKey(message)) { - return CACHE.get(message); - } - Contract contract = getContract(message); - if (contract != null) { - CACHE.put(message, contract); - } - return contract; - } - - void updateCache(Message message, Contract contract) { - CACHE.put(message, contract); - } - - private Contract getContract(Message message) { - for (Contract groovyDsl : this.groovyDsls) { - Contract contract = matchContract(message, groovyDsl); - if (contract != null) { - return contract; - } - } - return null; - } - - private Contract matchContract(Message message, Contract groovyDsl) { - List unmatchedHeaders = headersMatch(message, groovyDsl); - if (!unmatchedHeaders.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders); - } - return null; - } - Object inputMessage = message.getPayload(); - Object dslBody = MapConverter.getStubSideValues(groovyDsl.getInput().getMessageBody()); - if (dslBody instanceof FromFileProperty) { - if (log.isDebugEnabled()) { - log.debug("Will compare file content"); - } - FromFileProperty property = (FromFileProperty) dslBody; - if (property.isString()) { - // continue processing as if body was pure string - dslBody = property.asString(); - } - else if (!(inputMessage instanceof byte[])) { - if (log.isDebugEnabled()) { - log.debug("Contract provided byte comparison, but the input message is of type [" - + inputMessage.getClass() + "]. Can't compare the two."); - } - return null; - } - else { - boolean matches = Arrays.equals(property.asBytes(), (byte[]) inputMessage); - if (log.isDebugEnabled() && !matches) { - log.debug("Contract provided byte comparison, but the byte arrays don't match"); - } - return matches ? groovyDsl : null; - } - } - if (matchViaContent(groovyDsl, inputMessage, dslBody)) { - return groovyDsl; - } - return null; - } - - private boolean matchViaContent(Contract groovyDsl, Object inputMessage, Object dslBody) { - boolean matches; - ContentType type = ContentUtils.getClientContentType(inputMessage, groovyDsl.getInput().getMessageHeaders()); - if (type == ContentType.JSON) { - BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers(); - matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody); - } - else if (dslBody instanceof RegexProperty && inputMessage instanceof String) { - Pattern pattern = ((RegexProperty) dslBody).getPattern(); - matches = pattern.matcher((String) inputMessage).matches(); - bodyUnmatchedLog(dslBody, matches, pattern); - } - else { - matches = dslBody.equals(inputMessage); - bodyUnmatchedLog(dslBody, matches, inputMessage); - } - return matches; - } - - private void bodyUnmatchedLog(Object dslBody, boolean matches, Object pattern) { - if (log.isDebugEnabled() && !matches) { - log.debug("Body was supposed to " + unmatchedText(pattern) + " but the value is [" + dslBody.toString() - + "]"); - } - } - - private boolean matchesForJsonPayload(Contract groovyDsl, Object inputMessage, BodyMatchers matchers, - Object dslBody) { - Object matchingInputMessage = JsonToJsonPathsConverter.removeMatchingJsonPaths(dslBody, matchers); - JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(matchingInputMessage); - DocumentContext parsedJson; - try { - parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage)); - } - catch (JsonProcessingException e) { - throw new IllegalStateException("Cannot serialize to JSON", e); - } - List unmatchedJsonPath = new ArrayList<>(); - boolean matches = true; - for (MethodBufferingJsonVerifiable path : jsonPaths) { - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, path.jsonPath()); - } - if (matchers != null && matchers.hasMatchers()) { - for (BodyMatcher matcher : matchers.matchers()) { - String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody); - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath); - } - } - if (!unmatchedJsonPath.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] didn't match the body due to " + unmatchedJsonPath); - } - } - return matches; - } - - private boolean matchesJsonPath(List unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) { - try { - JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath); - return true; - } - catch (Exception e) { - unmatchedJsonPath.add(e.getLocalizedMessage()); - return false; - } - } - - private List headersMatch(Message message, Contract groovyDsl) { - List unmatchedHeaders = new ArrayList<>(); - Map headers = message.getHeaders(); - for (Header it : groovyDsl.getInput().getMessageHeaders().getEntries()) { - String name = it.getName(); - Object value = it.getClientValue(); - Object valueInHeader = headers.get(name); - valueInHeader = valueInHeader instanceof byte[] ? fromByte((byte[]) valueInHeader) : valueInHeader; - boolean matches; - if (value instanceof RegexProperty) { - Pattern pattern = ((RegexProperty) value).getPattern(); - matches = pattern.matcher(valueInHeader.toString()).matches(); - } - else { - matches = valueInHeader != null && valueInHeader.toString().equals(value.toString()); - } - if (!matches) { - unmatchedHeaders.add("Header with name [" + name + "] was supposed to " + unmatchedText(value) - + " but the value is [" + (valueInHeader != null ? valueInHeader.toString() : "null") + "]"); - } - } - return unmatchedHeaders; - } - - private String fromByte(byte[] valueInHeader) { - String string = new String(valueInHeader); - if (string.startsWith("\"") && string.endsWith("\"")) { - return string.substring(1, string.length() - 1); - } - return string; - } - - private String unmatchedText(Object expectedValue) { - return expectedValue instanceof RegexProperty - ? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]" - : "be equal to [" + expectedValue + "]"; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaRouter.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaRouter.java deleted file mode 100644 index 907085a5fe..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaRouter.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.kafka; - -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.kafka.clients.consumer.Consumer; -import org.apache.kafka.clients.consumer.ConsumerRecord; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.kafka.listener.MessageListener; -import org.springframework.kafka.support.Acknowledgment; -import org.springframework.kafka.support.converter.MessagingMessageConverter; -import org.springframework.messaging.Message; - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerKafkaRouter implements MessageListener { - - private static final Log log = LogFactory.getLog(StubRunnerKafkaRouter.class); - - private final MessagingMessageConverter messageConverter = new MessagingMessageConverter(); - - private final StubRunnerKafkaMessageSelector selector; - - private final BeanFactory beanFactory; - - private final List contracts; - - private KafkaTemplate kafkaTemplate; - - StubRunnerKafkaRouter(List groovyDsls, BeanFactory beanFactory) { - this.selector = new StubRunnerKafkaMessageSelector(groovyDsls); - this.beanFactory = beanFactory; - this.contracts = groovyDsls; - } - - private KafkaTemplate kafkaTemplate() { - if (this.kafkaTemplate == null) { - this.kafkaTemplate = this.beanFactory.getBean(KafkaTemplate.class); - } - return this.kafkaTemplate; - } - - @Override - public void onMessage(ConsumerRecord data) { - if (log.isDebugEnabled()) { - log.debug("Received message [" + data + "]"); - } - Message message = messageConverter.toMessage(data, null, null, null); - Contract dsl = this.selector.matchingContract(message); - if (dsl != null && dsl.getOutputMessage() != null && dsl.getOutputMessage().getSentTo() != null) { - String destination = dsl.getOutputMessage().getSentTo().getClientValue(); - if (log.isDebugEnabled()) { - log.debug("Found a matching contract with an output message. Will send it to the [" + destination - + "] destination"); - } - Message transform = new StubRunnerKafkaTransformer(this.contracts).transform(dsl); - String defaultTopic = kafkaTemplate().getDefaultTopic(); - try { - kafkaTemplate().setDefaultTopic(destination); - kafkaTemplate().send(transform); - } - finally { - kafkaTemplate().setDefaultTopic(defaultTopic); - } - } - } - - @Override - public void onMessage(ConsumerRecord data, Acknowledgment acknowledgment) { - onMessage(data); - } - - @Override - public void onMessage(ConsumerRecord data, Consumer consumer) { - onMessage(data); - } - - @Override - public void onMessage(ConsumerRecord data, Acknowledgment acknowledgment, Consumer consumer) { - onMessage(data); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaTransformer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaTransformer.java deleted file mode 100644 index 232a1d7d7c..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/kafka/StubRunnerKafkaTransformer.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.kafka; - -import java.util.List; -import java.util.Map; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.verifier.util.BodyExtractor; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - -/** - * Sends forward a message defined in the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerKafkaTransformer { - - private final StubRunnerKafkaMessageSelector selector; - - StubRunnerKafkaTransformer(List groovyDsls) { - this.selector = new StubRunnerKafkaMessageSelector(groovyDsls); - } - - public Message transform(Contract groovyDsl) { - Object outputBody = outputBody(groovyDsl); - Map headers = groovyDsl.getOutputMessage().getHeaders().asStubSideMap(); - Message newMessage = MessageBuilder.createMessage(outputBody, new MessageHeaders(headers)); - this.selector.updateCache(newMessage, groovyDsl); - return newMessage; - } - - private Object outputBody(Contract groovyDsl) { - Object outputBody = BodyExtractor.extractClientValueFromBody(groovyDsl.getOutputMessage().getBody()); - if (outputBody instanceof FromFileProperty) { - FromFileProperty property = (FromFileProperty) outputBody; - return property.asBytes(); - } - return BodyExtractor.extractStubValueFrom(outputBody); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerMessageRouter.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerMessageRouter.java deleted file mode 100644 index aa575a3dd1..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerMessageRouter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.stream; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.integration.router.AbstractMessageRouter; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerMessageRouter extends AbstractMessageRouter { - - private final StubRunnerStreamMessageSelector selector; - - private final BeanFactory beanFactory; - - StubRunnerMessageRouter(List groovyDsls, BeanFactory beanFactory) { - this.selector = new StubRunnerStreamMessageSelector(groovyDsls); - this.beanFactory = beanFactory; - } - - @Override - protected Collection determineTargetChannels(Message message) { - Contract dsl = this.selector.matchingContract(message); - if (dsl != null && dsl.getOutputMessage() != null && dsl.getOutputMessage().getSentTo() != null) { - String channelName = StubRunnerStreamConfiguration.resolvedDestination(this.beanFactory, - dsl.getOutputMessage().getSentTo().getClientValue()); - return Collections.singleton((MessageChannel) this.beanFactory.getBean(channelName)); - } - return Collections.singleton((MessageChannel) this.beanFactory.getBean("nullChannel")); - } - -} 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 deleted file mode 100644 index 380132007d..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamConfiguration.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.stream; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.BeanFactory; -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.stream.binder.test.InputDestination; -import org.springframework.cloud.stream.config.BindingProperties; -import org.springframework.cloud.stream.config.BindingServiceProperties; -import org.springframework.context.Lifecycle; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.integration.dsl.IntegrationFlow; -import org.springframework.integration.dsl.IntegrationFlowBuilder; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -/** - * Spring Cloud Stream configuration that iterates over the downloaded Groovy DSLs and - * registers a flow for each DSL. - * - * @author Marcin Grzejszczak - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ IntegrationFlow.class, InputDestination.class }) -@ConditionalOnProperty(name = "stubrunner.stream.enabled", havingValue = "true", matchIfMissing = true) -@AutoConfigureBefore(StubRunnerIntegrationConfiguration.class) -public class StubRunnerStreamConfiguration { - - private static final Log log = LogFactory.getLog(StubRunnerStreamConfiguration.class); - - static String resolvedDestination(BeanFactory context, String destination) { - Map bindings = bindingProperties(context); - for (Map.Entry entry : bindings.entrySet()) { - if (destination.equals(entry.getValue().getDestination())) { - if (log.isDebugEnabled()) { - log.debug("Found a channel named [" + entry.getKey() + "] with destination [" + destination + "]"); - } - return entry.getKey(); - } - } - if (log.isDebugEnabled()) { - log.debug("No destination named [" + destination - + "] was found. Assuming that the destination equals the channel name"); - } - return destination; - } - - private static Map bindingProperties(BeanFactory context) { - return context.getBean(BindingServiceProperties.class).getBindings(); - } - - @Bean - @ConditionalOnMissingBean(name = "stubFlowRegistrar") - @ConditionalOnBean(BindingServiceProperties.class) - public FlowRegistrar stubFlowRegistrar(AutowireCapableBeanFactory beanFactory, BatchStubRunner batchStubRunner) { - Map> contracts = batchStubRunner.getContracts(); - for (Entry> entry : contracts.entrySet()) { - StubConfiguration key = entry.getKey(); - Collection value = entry.getValue(); - String name = key.getGroupId() + "_" + key.getArtifactId(); - MultiValueMap map = new LinkedMultiValueMap<>(); - for (Contract dsl : value) { - if (dsl == null) { - continue; - } - if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null - && StringUtils.hasText(dsl.getInput().getMessageFrom().getClientValue())) { - String from = resolvedDestination(beanFactory, dsl.getInput().getMessageFrom().getClientValue()); - map.add(from, dsl); - } - } - for (Entry> entries : map.entrySet()) { - final String flowName = name + "_" + entries.getKey() + "_" + entries.getValue().hashCode(); - IntegrationFlowBuilder builder = IntegrationFlow.from(entries.getKey()) - .filter(new StubRunnerStreamMessageSelector(entries.getValue()), - e -> e.id(flowName + ".filter")) - .transform(new StubRunnerStreamTransformer(entries.getValue())) - .route(new StubRunnerMessageRouter(entries.getValue(), beanFactory)); - beanFactory.initializeBean(builder.get(), flowName); - beanFactory.getBean(flowName + ".filter", Lifecycle.class).start(); - } - - } - return new FlowRegistrar(); - } - - static class FlowRegistrar { - - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java deleted file mode 100644 index 3c5508ad71..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.stream; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import com.toomuchcoding.jsonassert.JsonAssertion; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.BodyMatcher; -import org.springframework.cloud.contract.spec.internal.BodyMatchers; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.spec.internal.RegexProperty; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.util.ContentType; -import org.springframework.cloud.contract.verifier.util.ContentUtils; -import org.springframework.cloud.contract.verifier.util.JsonPaths; -import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter; -import org.springframework.cloud.contract.verifier.util.MapConverter; -import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable; -import org.springframework.integration.core.MessageSelector; -import org.springframework.messaging.Message; - -/** - * Passes through a message that matches the one defined in the DSL. - * - * @author Marcin Grzejszczak - * @author Tim Ysewyn - */ -class StubRunnerStreamMessageSelector implements MessageSelector { - - private static final Map CACHE = Collections.synchronizedMap(new WeakHashMap<>()); - - private static final Log log = LogFactory.getLog(StubRunnerStreamMessageSelector.class); - - private final List groovyDsls; - - private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper(); - - StubRunnerStreamMessageSelector(Contract groovyDsl) { - this(Collections.singletonList(groovyDsl)); - } - - StubRunnerStreamMessageSelector(List groovyDsls) { - this.groovyDsls = groovyDsls; - } - - @Override - public boolean accept(Message message) { - return matchingContract(message) != null; - } - - Contract matchingContract(Message message) { - if (CACHE.containsKey(message)) { - return CACHE.get(message); - } - Contract contract = getContract(message); - if (contract != null) { - CACHE.put(message, contract); - } - return contract; - } - - void updateCache(Message message, Contract contract) { - CACHE.put(message, contract); - } - - private Contract getContract(Message message) { - for (Contract groovyDsl : this.groovyDsls) { - Contract contract = matchContract(message, groovyDsl); - if (contract != null) { - return contract; - } - } - return null; - } - - private Contract matchContract(Message message, Contract groovyDsl) { - List unmatchedHeaders = headersMatch(message, groovyDsl); - if (!unmatchedHeaders.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders); - } - return null; - } - Object inputMessage = message.getPayload(); - Object dslBody = MapConverter.getStubSideValues(groovyDsl.getInput().getMessageBody()); - if (dslBody instanceof FromFileProperty) { - if (log.isDebugEnabled()) { - log.debug("Will compare file content"); - } - FromFileProperty property = (FromFileProperty) dslBody; - if (property.isString()) { - // continue processing as if body was pure string - dslBody = property.asString(); - } - else if (!(inputMessage instanceof byte[])) { - if (log.isDebugEnabled()) { - log.debug("Contract provided byte comparison, but the input message is of type [" - + inputMessage.getClass() + "]. Can't compare the two."); - } - return null; - } - else { - boolean matches = Arrays.equals(property.asBytes(), (byte[]) inputMessage); - if (log.isDebugEnabled() && !matches) { - log.debug("Contract provided byte comparison, but the byte arrays don't match"); - } - return matches ? groovyDsl : null; - } - } - if (matchViaContent(groovyDsl, inputMessage, dslBody)) { - return groovyDsl; - } - return null; - } - - private boolean matchViaContent(Contract groovyDsl, Object inputMessage, Object dslBody) { - boolean matches; - ContentType type = ContentUtils.getClientContentType(inputMessage, groovyDsl.getInput().getMessageHeaders()); - if (type == ContentType.JSON) { - BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers(); - matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody); - } - else if ((dslBody instanceof RegexProperty || dslBody instanceof Pattern) && inputMessage instanceof String) { - Pattern pattern = new RegexProperty(dslBody).getPattern(); - matches = pattern.matcher((String) inputMessage).matches(); - bodyUnmatchedLog(dslBody, matches, pattern); - } - else { - matches = dslBody.equals(inputMessage); - bodyUnmatchedLog(dslBody, matches, inputMessage); - } - return matches; - } - - private void bodyUnmatchedLog(Object dslBody, boolean matches, Object pattern) { - if (log.isDebugEnabled() && !matches) { - log.debug("Body was supposed to " + unmatchedText(pattern) + " but the value is [" + dslBody.toString() - + "]"); - } - } - - private boolean matchesForJsonPayload(Contract groovyDsl, Object inputMessage, BodyMatchers matchers, - Object dslBody) { - Object matchingInputMessage = JsonToJsonPathsConverter.removeMatchingJsonPaths(dslBody, matchers); - JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(matchingInputMessage); - DocumentContext parsedJson; - try { - parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage)); - } - catch (JsonProcessingException e) { - throw new IllegalStateException("Cannot serialize to JSON", e); - } - List unmatchedJsonPath = new ArrayList<>(); - boolean matches = true; - for (MethodBufferingJsonVerifiable path : jsonPaths) { - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, path.jsonPath()); - } - if (matchers != null && matchers.hasMatchers()) { - for (BodyMatcher matcher : matchers.matchers()) { - String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody); - matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath); - } - } - if (!unmatchedJsonPath.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Contract [" + groovyDsl + "] didn't match the body due to " + unmatchedJsonPath); - } - } - return matches; - } - - private boolean matchesJsonPath(List unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) { - try { - JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath); - return true; - } - catch (Exception e) { - unmatchedJsonPath.add(e.getLocalizedMessage()); - return false; - } - } - - private List headersMatch(Message message, Contract groovyDsl) { - List unmatchedHeaders = new ArrayList<>(); - Map headers = message.getHeaders(); - for (Header it : groovyDsl.getInput().getMessageHeaders().getEntries()) { - String name = it.getName(); - Object value = it.getClientValue(); - Object valueInHeader = headers.get(name); - boolean matches; - if (value instanceof RegexProperty || value instanceof Pattern) { - Pattern pattern = new RegexProperty(value).getPattern(); - matches = pattern.matcher(valueInHeader.toString()).matches(); - } - else { - matches = valueInHeader != null && valueInHeader.toString().equals(value.toString()); - } - if (!matches) { - unmatchedHeaders.add("Header with name [" + name + "] was supposed to " + unmatchedText(value) - + " but the value is [" + (valueInHeader != null ? valueInHeader.toString() : "null") + "]"); - } - } - return unmatchedHeaders; - } - - private String unmatchedText(Object expectedValue) { - return expectedValue instanceof RegexProperty - ? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]" - : "be equal to [" + expectedValue + "]"; - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformer.java deleted file mode 100644 index 4d17eaf86d..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.stream; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.springframework.cloud.contract.spec.Contract; -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.verifier.util.BodyExtractor; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - -/** - * Sends forward a message defined in the DSL. - * - * @author Marcin Grzejszczak - */ -class StubRunnerStreamTransformer { - - private final StubRunnerStreamMessageSelector selector; - - StubRunnerStreamTransformer(Contract groovyDsl) { - this(Collections.singletonList(groovyDsl)); - } - - StubRunnerStreamTransformer(List groovyDsls) { - this.selector = new StubRunnerStreamMessageSelector(groovyDsls); - } - - public Message transform(Message source) { - Contract groovyDsl = matchingContract(source); - if (groovyDsl == null || groovyDsl.getOutputMessage() == null) { - return source; - } - byte[] outputBody = outputBodyAsBytes(groovyDsl); - Map headers = groovyDsl.getOutputMessage().getHeaders().asStubSideMap(); - MessageHeaders messageHeaders = new MessageHeaders(headers); - Message message = MessageBuilder.createMessage(outputBody, messageHeaders); - this.selector.updateCache(message, groovyDsl); - return message; - } - - private byte[] outputBodyAsBytes(Contract groovyDsl) { - Object outputBody = BodyExtractor.extractClientValueFromBody(groovyDsl.getOutputMessage().getBody()); - if (outputBody instanceof FromFileProperty) { - FromFileProperty property = (FromFileProperty) outputBody; - return property.asBytes(); - } - return BodyExtractor.extractStubValueFrom(outputBody).getBytes(); - } - - Contract matchingContract(Message source) { - return this.selector.matchingContract(source); - } - -} diff --git a/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring/org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner.imports b/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring/org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner.imports index 15c6286085..49fbbe54c1 100644 --- a/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring/org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner.imports +++ b/spring-cloud-contract-stub-runner/src/main/resources/META-INF/spring/org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner.imports @@ -1,11 +1,6 @@ org.springframework.cloud.contract.stubrunner.spring.StubRunnerConfiguration org.springframework.cloud.contract.stubrunner.spring.cloud.StubRunnerSpringCloudAutoConfiguration org.springframework.cloud.contract.stubrunner.spring.cloud.loadbalancer.SpringCloudLoadBalancerAutoConfiguration -org.springframework.cloud.contract.stubrunner.messaging.integration.StubRunnerIntegrationConfiguration -org.springframework.cloud.contract.stubrunner.messaging.jms.StubRunnerJmsConfiguration -org.springframework.cloud.contract.stubrunner.messaging.stream.StubRunnerStreamConfiguration org.springframework.cloud.contract.stubrunner.spring.cloud.eureka.StubRunnerSpringCloudEurekaAutoConfiguration org.springframework.cloud.contract.stubrunner.spring.cloud.zookeeper.StubRunnerSpringCloudZookeeperAutoConfiguration org.springframework.cloud.contract.stubrunner.spring.cloud.consul.StubRunnerSpringCloudConsulAutoConfiguration -org.springframework.cloud.contract.stubrunner.messaging.StubRunnerStreamsIntegrationAutoConfiguration -org.springframework.cloud.contract.stubrunner.messaging.camel.StubRunnerCamelConfiguration diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicateSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicateSpec.groovy deleted file mode 100644 index e3fd9a72d7..0000000000 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicateSpec.groovy +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.camel - -import org.apache.camel.Exchange -import org.apache.camel.Message -import org.apache.camel.impl.DefaultCamelContext -import org.apache.camel.support.DefaultExchange -import org.apache.camel.support.DefaultMessage -import spock.lang.Specification - -import org.springframework.cloud.contract.spec.Contract - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerCamelPredicateSpec extends Specification { - Exchange exchange = new DefaultExchange(new DefaultCamelContext()) - Message message = new DefaultMessage(exchange) - - def "should return false if headers don't match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageBody(foo: "bar") - messageHeaders { - header("foo", $(c(regex("[0-9]{3}")), p(123))) - } - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in = message - message.headers = [ - foo: "non matching stuff" - ] - expect: - !predicate.matches(exchange) - } - - def "should return false if headers match and body doesn't"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in = message - message.headers = [ - foo: 123 - ] - message.body = [ - foo: "non matching stuff" - ] - expect: - !predicate.matches(exchange) - } - - def "should return false if headers match and body doesn't when it's using matchers"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: "non matching stuff") - bodyMatchers { - jsonPath('$.foo', byRegex("[0-9]{3}")) - } - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in >> message - message.headers >> [ - foo: 123 - ] - message.body >> [ - foo: "non matching stuff" - ] - expect: - !predicate.matches(exchange) - } - - def "should return true if headers and body match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in = message - message.headers = [ - foo: 123 - ] - message.body = [ - foo: 123 - ] - expect: - predicate.matches(exchange) - } - - def "should return true if headers and body using matchers match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: 123) - bodyMatchers { - jsonPath('$.foo', byRegex("[0-9]{3}")) - } - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in = message - message.headers = [ - foo: 123 - ] - message.body = [ - foo: 123 - ] - expect: - predicate.matches(exchange) - } - - def "should return true if headers and byte body matches"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(applicationOctetStream()) - } - messageBody(fileAsBytes("request.pdf")) - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in = message - message.headers = [ - foo : 123, - contentType: "application/octet-stream" - ] - message.body = StubRunnerCamelPredicate.getResource("/request.pdf").bytes - expect: - predicate.matches(exchange) - } - - def "should return false if byte body types don't match for binary"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(applicationOctetStream()) - } - messageBody(fileAsBytes("request.pdf")) - } - } - and: - StubRunnerCamelPredicate predicate = new StubRunnerCamelPredicate([dsl]) - exchange.in = message - message.headers = [ - foo : 123, - contentType: "application/octet-stream" - ] - message.body = "hello world" - expect: - !predicate.matches(exchange) - } -} diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelectorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelectorSpec.groovy deleted file mode 100644 index ba0a1371dc..0000000000 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelectorSpec.groovy +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.integration - -import spock.lang.Issue -import spock.lang.Specification - -import org.springframework.cloud.contract.spec.Contract -import org.springframework.messaging.Message - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerIntegrationMessageSelectorSpec extends Specification { - Message message = Mock(Message) - - def "should return false if headers don't match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageBody(foo: "bar") - messageHeaders { - header("foo", $(c(regex("[0-9]{3}")), p(123))) - } - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector(dsl) - message.headers >> [ - foo: "non matching stuff" - ] - expect: - !predicate.accept(message) - } - - def "should return false if headers match and body doesn't"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector(dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: "non matching stuff" - ] - expect: - !predicate.accept(message) - } - - def "should return false if headers match and body doesn't when it's using matchers"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: "non matching stuff") - bodyMatchers { - jsonPath('$.foo', byRegex("[0-9]{3}")) - } - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector(dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: "non matching stuff" - ] - expect: - !predicate.accept(message) - } - - def "should return true if headers and byte body matches"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(applicationOctetStream()) - } - messageBody(fileAsBytes("request.pdf")) - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector(dsl) - message.headers >> [ - foo : 123, - contentType: "application/octet-stream" - ] - message.payload >> StubRunnerIntegrationMessageSelector.getResource("/request.pdf").bytes - expect: - predicate.accept(message) - } - - def "should return false if byte body types don't match for binary"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(applicationOctetStream()) - } - messageBody(fileAsBytes("request.pdf")) - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector(dsl) - message.headers >> [ - foo : 123, - contentType: "application/octet-stream" - ] - message.payload >> "hello world" - expect: - !predicate.accept(message) - } - - def "should return true if headers and body match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector(dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: 123 - ] - expect: - predicate.accept(message) - } - - def "should return true if headers and body using matchers match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: 123) - bodyMatchers { - jsonPath('$.foo', byRegex("[0-9]{3}")) - } - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector( - dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: 123 - ] - expect: - predicate.accept(message) - } - - @Issue("1382") - def "should return true if header matches regex"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", $(anyUuid())) - } - messageBody(foo: 123) - } - } - and: - StubRunnerIntegrationMessageSelector predicate = new StubRunnerIntegrationMessageSelector( - dsl) - message.headers >> [ - foo: "fbcc2ed3-dbac-47e7-9a4b-c2d55709792c" - ] - message.payload >> [ - foo: 123 - ] - expect: - predicate.accept(message) - } -} diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy deleted file mode 100644 index 8a969773d1..0000000000 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.stream - -import spock.lang.Issue -import spock.lang.Specification - -import org.springframework.cloud.contract.spec.Contract -import org.springframework.http.MediaType -import org.springframework.messaging.Message - -/** - * @author Marcin Grzejszczak - */ -class StubRunnerStreamMessageSelectorSpec extends Specification { - Message message = Mock(Message) - - def "should return false if headers don't match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageBody(foo: "bar") - messageHeaders { - header("foo", $(c(regex("[0-9]{3}")), p(123))) - } - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo: "non matching stuff" - ] - expect: - !predicate.accept(message) - } - - def "should return false if headers match and body doesn't"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: "non matching stuff" - ] - expect: - !predicate.accept(message) - } - - def "should return false if headers match and body doesn't when it's using matchers"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: "non matching stuff") - bodyMatchers { - jsonPath('$.foo', byRegex("[0-9]{3}")) - } - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: "non matching stuff" - ] - expect: - !predicate.accept(message) - } - - def "should return true if headers and body match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - header("bar", "bar") - messagingContentType(applicationJsonUtf8()) - header("regex", regex("234")) - } - messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo : 123, - bar : "bar", - contentType: MediaType.APPLICATION_JSON_UTF8, - regex : 234 - ] - message.payload >> [ - foo: 123 - ] - expect: - predicate.accept(message) - } - - def "should return true if headers and text body matches"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(textPlain()) - } - messageBody($(c(regex("[0-9]{3}")), p("123"))) - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo : 123, - contentType: "text/plain" - ] - message.payload >> "123" - expect: - predicate.accept(message) - } - - def "should return true if headers and byte body matches"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(applicationOctetStream()) - } - messageBody(fileAsBytes("request.pdf")) - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo : 123, - contentType: "application/octet-stream" - ] - message.payload >> StubRunnerStreamMessageSelectorSpec.getResource("/request.pdf").bytes - expect: - predicate.accept(message) - } - - def "should return false if byte body types don't match for binary"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - messagingContentType(applicationOctetStream()) - } - messageBody(fileAsBytes("request.pdf")) - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) - message.headers >> [ - foo : 123, - contentType: "application/octet-stream" - ] - message.payload >> "hello world" - expect: - !predicate.accept(message) - } - - def "should return true if headers and body using matchers match"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", 123) - } - messageBody(foo: 123) - bodyMatchers { - jsonPath('$.foo', byRegex("[0-9]{3}")) - } - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector( - dsl) - message.headers >> [ - foo: 123 - ] - message.payload >> [ - foo: 123 - ] - expect: - predicate.accept(message) - } - - @Issue("1382") - def "should return true if header matches regex"() { - given: - Contract dsl = Contract.make { - input { - messageFrom "foo" - messageHeaders { - header("foo", $(anyUuid())) - } - messageBody(foo: 123) - } - } - and: - StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector( - dsl) - message.headers >> [ - foo: "fbcc2ed3-dbac-47e7-9a4b-c2d55709792c" - ] - message.payload >> [ - foo: 123 - ] - expect: - predicate.accept(message) - } -} diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy index 44ee52a2b6..201a533d50 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy @@ -85,7 +85,7 @@ class StubRunnerBootSpec { String response = RestAssuredMockMvc.get('/triggers').body.asString() then: def root = new JsonSlurper().parseText(response) - assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["delete_book", "return_book_1", "return_book_2"]) + assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["return_book_1"]) } @Test diff --git a/spring-cloud-contract-stub-runner/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar b/spring-cloud-contract-stub-runner/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar index c57c836db4..7916329c40 100644 Binary files a/spring-cloud-contract-stub-runner/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar and b/spring-cloud-contract-stub-runner/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar differ diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-baseclass-from-mappings/src/test/resources/contracts/com/hello/v1/Messaging.groovy b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-baseclass-from-mappings/src/test/resources/contracts/com/hello/v1/Messaging.groovy index 39ee18a7c5..dbbfb72a06 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-baseclass-from-mappings/src/test/resources/contracts/com/hello/v1/Messaging.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-baseclass-from-mappings/src/test/resources/contracts/com/hello/v1/Messaging.groovy @@ -15,13 +15,7 @@ */ org.springframework.cloud.contract.spec.Contract.make { label 'some_label' input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } + triggeredBy("hashCode()") } outputMessage { sentTo('jms:output') diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-generated-baseclass/src/test/resources/contracts/hello/v1/Messaging.groovy b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-generated-baseclass/src/test/resources/contracts/hello/v1/Messaging.groovy index 39ee18a7c5..dbbfb72a06 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-generated-baseclass/src/test/resources/contracts/hello/v1/Messaging.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-generated-baseclass/src/test/resources/contracts/hello/v1/Messaging.groovy @@ -15,13 +15,7 @@ */ org.springframework.cloud.contract.spec.Contract.make { label 'some_label' input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } + triggeredBy("hashCode()") } outputMessage { sentTo('jms:output') diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/Messaging.groovy b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/Messaging.groovy index 39ee18a7c5..dbbfb72a06 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/Messaging.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/Messaging.groovy @@ -15,13 +15,7 @@ */ org.springframework.cloud.contract.spec.Contract.make { label 'some_label' input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } + triggeredBy("hashCode()") } outputMessage { sentTo('jms:output') diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/common-repo/consumer1/Messaging.groovy b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/common-repo/consumer1/Messaging.groovy index 39ee18a7c5..dbbfb72a06 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/common-repo/consumer1/Messaging.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/common-repo/consumer1/Messaging.groovy @@ -15,13 +15,7 @@ */ org.springframework.cloud.contract.spec.Contract.make { label 'some_label' input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } + triggeredBy("hashCode()") } outputMessage { sentTo('jms:output') diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/java/org/springframework/cloud/contract/verifier/spec/pact/MessagePactCreator.java b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/java/org/springframework/cloud/contract/verifier/spec/pact/MessagePactCreator.java index 3b26007cc4..e27ecb5b95 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/java/org/springframework/cloud/contract/verifier/spec/pact/MessagePactCreator.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/main/java/org/springframework/cloud/contract/verifier/spec/pact/MessagePactCreator.java @@ -80,9 +80,6 @@ class MessagePactCreator { if (input.getTriggeredBy() != null) { return input.getTriggeredBy().getExecutionCommand(); } - else if (input.getMessageFrom() != null) { - return "received message from " + clientValueExtractor.apply(input.getMessageFrom()); - } else { return ""; } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldSendMessageWhenMessageReceived.groovy b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldSendMessageWhenMessageReceived.groovy deleted file mode 100644 index 43669aba9e..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldSendMessageWhenMessageReceived.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package contracts - -import org.springframework.cloud.contract.spec.Contract - -[ - Contract.make { - label 'some_label' - input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('jms:output') - body([ - bookName: 'foo' - ]) - headers { - header('BOOK-NAME', 'foo') - } - } - } -] diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldSendMessageWhenMessageReceived.json b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldSendMessageWhenMessageReceived.json deleted file mode 100644 index 7c8311b17b..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldSendMessageWhenMessageReceived.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "consumer": { - "name": "Consumer" - }, - "provider": { - "name": "Provider" - }, - "messages": [ - { - "description": "message sent to jms:output", - "metaData": { - "BOOK-NAME": "foo" - }, - "contents": { - "bookName": "foo" - }, - "providerStates": [ - { - "name": "received message from jms:input" - } - ], - "matchingRules": { - "body": { - "$.bookName": { - "matchers": [ - { - "match": "type" - } - ], - "combine": "AND" - } - } - } - } - ], - "metadata": { - "pactSpecification": { - "version": "3.0.0" - }, - "pact-jvm": { - "version": "4.3.15" - } - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldVerifyWhenBookWasDeleted.groovy b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldVerifyWhenBookWasDeleted.groovy deleted file mode 100644 index 3abf06df09..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldVerifyWhenBookWasDeleted.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package contracts - -import org.springframework.cloud.contract.spec.Contract - -[ - Contract.make { - label 'some_label' - input { - messageFrom('jms:delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } - } -] diff --git a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldVerifyWhenBookWasDeleted.json b/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldVerifyWhenBookWasDeleted.json deleted file mode 100644 index 920dfa26d6..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-pact/src/test/resources/contracts/shouldVerifyWhenBookWasDeleted.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "consumer": { - "name": "Consumer" - }, - "provider": { - "name": "Provider" - }, - "messages": [ - { - "description": "assert that bookWasDeleted()", - "metaData": { - }, - "providerStates": [ - { - "name": "received message from jms:delete" - } - ] - } - ], - "metadata": { - "pactSpecification": { - "version": "3.0.0" - }, - "pact-jvm": { - "version": "4.3.15" - } - } -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JavaMessagingGiven.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JavaMessagingGiven.java deleted file mode 100644 index 9d98f88d03..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JavaMessagingGiven.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.builder; - -import org.springframework.cloud.contract.verifier.config.TestFramework; -import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; - -class JavaMessagingGiven extends MessagingGiven { - - private final GeneratedClassMetaData generatedClassMetaData; - - JavaMessagingGiven(BlockBuilder blockBuilder, GeneratedClassMetaData generatedClassMetaData) { - super(blockBuilder, generatedClassMetaData, JavaMessagingBodyParser.INSTANCE); - this.generatedClassMetaData = generatedClassMetaData; - } - - @Override - public boolean accept(SingleContractMetadata metadata) { - return super.accept(metadata) - && this.generatedClassMetaData.configProperties.getTestFramework() != TestFramework.SPOCK; - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingBodyGiven.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingBodyGiven.java deleted file mode 100644 index 567929c70c..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingBodyGiven.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.builder; - -import org.springframework.cloud.contract.spec.internal.FromFileProperty; -import org.springframework.cloud.contract.spec.internal.Input; -import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; -import org.springframework.cloud.contract.verifier.util.ContentType; - -class MessagingBodyGiven implements Given, MethodVisitor { - - private final BlockBuilder blockBuilder; - - private final BodyReader bodyReader; - - private final BodyParser bodyParser; - - MessagingBodyGiven(BlockBuilder blockBuilder, BodyReader bodyReader, BodyParser bodyParser) { - this.blockBuilder = blockBuilder; - this.bodyReader = bodyReader; - this.bodyParser = bodyParser; - } - - @Override - public MethodVisitor apply(SingleContractMetadata metadata) { - appendBodyGiven(metadata); - return this; - } - - private void appendBodyGiven(SingleContractMetadata metadata) { - ContentType contentType = metadata.getInputTestContentType(); - Input inputMessage = metadata.getContract().getInput(); - Object bodyValue = this.bodyParser.extractServerValueFromBody(contentType, - inputMessage.getMessageBody().getServerValue()); - if (bodyValue instanceof FromFileProperty) { - FromFileProperty fileProperty = (FromFileProperty) bodyValue; - String byteText = fileProperty.isByte() - ? this.bodyReader.readBytesFromFileString(metadata, fileProperty, CommunicationType.REQUEST) - : this.bodyParser.quotedLongText(this.bodyReader.readStringFromFileString(metadata, fileProperty, - CommunicationType.REQUEST)); - this.blockBuilder.addIndented(byteText); - } - else { - String text = this.bodyParser.convertToJsonString(bodyValue); - this.blockBuilder.addIndented(this.bodyParser.quotedEscapedLongText(text)); - } - } - - @Override - public boolean accept(SingleContractMetadata metadata) { - return metadata.getContract().getInput().getMessageBody() != null; - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingBodyWhen.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingBodyWhen.java deleted file mode 100644 index 603710d94e..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingBodyWhen.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.builder; - -import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; - -class MessagingBodyWhen implements When { - - private final BlockBuilder blockBuilder; - - private final BodyReader bodyReader; - - MessagingBodyWhen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) { - this.blockBuilder = blockBuilder; - this.bodyReader = new BodyReader(metaData); - } - - @Override - public MethodVisitor apply(SingleContractMetadata metadata) { - this.bodyReader.storeContractAsYaml(metadata); - this.blockBuilder - .addIndented("contractVerifierMessaging.send(inputMessage, \"" - + metadata.getContract().getInput().getMessageFrom().getServerValue() + "\",") - .addEmptyLine().indent().addIndented("contract(this, \"" + metadata.methodName() + ".yml\"))") - .addEndingIfNotPresent().unindent(); - return this; - } - - @Override - public boolean accept(SingleContractMetadata metadata) { - return metadata.getContract().getInput().getMessageFrom() != null; - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingGiven.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingGiven.java deleted file mode 100644 index 5e7a565520..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingGiven.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.builder; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; - -class MessagingGiven implements Given, MethodVisitor, BodyMethodVisitor { - - private final BlockBuilder blockBuilder; - - private final GeneratedClassMetaData generatedClassMetaData; - - private final List givens = new LinkedList<>(); - - MessagingGiven(BlockBuilder blockBuilder, GeneratedClassMetaData generatedClassMetaData, BodyParser bodyParser) { - this.blockBuilder = blockBuilder; - this.generatedClassMetaData = generatedClassMetaData; - this.givens.addAll(Arrays.asList( - new MessagingBodyGiven(this.blockBuilder, new BodyReader(this.generatedClassMetaData), bodyParser), - new MessagingHeadersGiven(this.blockBuilder))); - } - - @Override - public MethodVisitor apply(SingleContractMetadata metadata) { - startBodyBlock(this.blockBuilder, "given:"); - this.blockBuilder.addIndented("ContractVerifierMessage inputMessage = contractVerifierMessaging.create(") - .addEmptyLine().indent(); - this.givens.stream().filter(given -> given.accept(metadata)).forEach(given -> { - given.apply(metadata); - this.blockBuilder.addEmptyLine(); - }); - this.blockBuilder.unindent().unindent().startBlock().addIndented(")").addEndingIfNotPresent().addEmptyLine() - .endBlock(); - return this; - } - - @Override - public boolean accept(SingleContractMetadata metadata) { - return metadata.isMessaging() && metadata.getContract().getInput().getTriggeredBy() == null; - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingHeadersGiven.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingHeadersGiven.java deleted file mode 100644 index 9f84d6131d..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingHeadersGiven.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.builder; - -import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.spec.internal.Input; -import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; -import org.springframework.cloud.contract.verifier.util.MapConverter; - -class MessagingHeadersGiven implements Given, MethodVisitor { - - private final BlockBuilder blockBuilder; - - MessagingHeadersGiven(BlockBuilder blockBuilder) { - this.blockBuilder = blockBuilder; - } - - @Override - public MethodVisitor apply(SingleContractMetadata metadata) { - Input inputMessage = metadata.getContract().getInput(); - this.blockBuilder.startBlock().addIndented(", headers()").startBlock(); - inputMessage.getMessageHeaders().executeForEachHeader(header -> { - this.blockBuilder.addEmptyLine().addIndented(getHeaderString(header)); - }); - this.blockBuilder.endBlock(); - return this; - } - - private String getHeaderString(Header header) { - return ".header(" + getTestSideValue(header.getName()) + ", " + getTestSideValue(header.getServerValue()) + ")"; - } - - private String getTestSideValue(Object object) { - return '"' + MapConverter.getTestSideValues(object).toString() + '"'; - } - - @Override - public boolean accept(SingleContractMetadata metadata) { - return metadata.getContract().getInput().getMessageHeaders() != null; - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingWhen.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingWhen.java index 33ac66a97e..fdc2365a6e 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingWhen.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/MessagingWhen.java @@ -28,10 +28,9 @@ class MessagingWhen implements When, BodyMethodVisitor { private final List whens = new LinkedList<>(); - MessagingWhen(BlockBuilder blockBuilder, GeneratedClassMetaData generatedClassMetaData) { + MessagingWhen(BlockBuilder blockBuilder) { this.blockBuilder = blockBuilder; this.whens.addAll(Arrays.asList(new MessagingTriggeredByWhen(this.blockBuilder), - new MessagingBodyWhen(this.blockBuilder, generatedClassMetaData), new MessagingAssertThatWhen(this.blockBuilder))); } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java index 6623fb609d..bd23b0d0a4 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java @@ -117,9 +117,7 @@ class SingleMethodBuilder { SingleMethodBuilder messaging() { // @formatter:off - return given(new JavaMessagingGiven(this.blockBuilder, this.generatedClassMetaData)) - .given(new SpockMessagingGiven(this.blockBuilder, this.generatedClassMetaData)) - .when(new MessagingWhen(this.blockBuilder, this.generatedClassMetaData)) + return when(new MessagingWhen(this.blockBuilder)) .then(new JavaMessagingWithBodyThen(this.blockBuilder, this.generatedClassMetaData)) .then(new SpockMessagingWithBodyThen(this.blockBuilder, diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SpockMessagingGiven.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SpockMessagingGiven.java deleted file mode 100644 index 082aa7e5bf..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/SpockMessagingGiven.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.builder; - -import org.springframework.cloud.contract.verifier.config.TestFramework; -import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; - -class SpockMessagingGiven extends MessagingGiven { - - private final GeneratedClassMetaData generatedClassMetaData; - - SpockMessagingGiven(BlockBuilder blockBuilder, GeneratedClassMetaData generatedClassMetaData) { - super(blockBuilder, generatedClassMetaData, SpockMessagingBodyParser.INSTANCE); - this.generatedClassMetaData = generatedClassMetaData; - } - - @Override - public boolean accept(SingleContractMetadata metadata) { - return super.accept(metadata) - && this.generatedClassMetaData.configProperties.getTestFramework() == TestFramework.SPOCK; - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.java index e61530ee4d..5966383fe4 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ContractsToYaml.java @@ -367,7 +367,6 @@ class ContractsToYaml { protected void input(Contract contract, YamlContract yamlContract) { Input input = contract.getInput(); if (input != null) { - ContentType contentType = evaluateClientSideContentType(input.getMessageHeaders(), input.getMessageBody()); yamlContract.input = new YamlContract.Input(); yamlContract.input.assertThat = Optional.ofNullable(input.getAssertThat()) .map(assertThat -> MapConverter @@ -377,26 +376,6 @@ class ContractsToYaml { .map(triggeredBy -> MapConverter .getTestSideValues(triggeredBy.toString(), MapConverter.JSON_PARSING_FUNCTION).toString()) .orElse(null); - yamlContract.input.messageHeaders = input.getMessageHeaders().asTestSideMap(); - yamlContract.input.messageBody = MapConverter.getTestSideValues(input.getMessageBody(), - MapConverter.JSON_PARSING_FUNCTION); - yamlContract.input.messageFrom = Optional - .ofNullable(input.getMessageFrom()).map(messageFrom -> MapConverter - .getTestSideValues(messageFrom, MapConverter.JSON_PARSING_FUNCTION).toString()) - .orElse(null); - Optional.ofNullable(input.getBodyMatchers()).map(BodyMatchers::matchers) - .ifPresent(bodyMatchers -> bodyMatchers.forEach(bodyMatcher -> { - YamlContract.BodyStubMatcher bodyStubMatcher = new YamlContract.BodyStubMatcher(); - bodyStubMatcher.path = bodyMatcher.path(); - bodyStubMatcher.type = stubMatcherType(bodyMatcher.matchingType()); - bodyStubMatcher.value = Optional.ofNullable(bodyMatcher.value()).map(Object::toString) - .orElse(null); - yamlContract.input.matchers.body.add(bodyStubMatcher); - })); - if (XML != contentType) { - setInputBodyMatchers(input.getMessageBody(), yamlContract.input.matchers.body); - } - setInputHeadersMatchers(input.getMessageHeaders(), yamlContract.input.matchers.headers); } } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlContract.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlContract.java index 40acbdb76b..2887575051 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlContract.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlContract.java @@ -733,22 +733,10 @@ public class YamlContract { public static class Input { - public String messageFrom; - public String triggeredBy; - public Map messageHeaders = new LinkedHashMap(); - - public Object messageBody; - - public String messageBodyFromFile; - - public String messageBodyFromFileAsBytes; - public String assertThat; - public StubMatchers matchers = new StubMatchers(); - @Override public boolean equals(Object o) { if (this == o) { @@ -758,26 +746,17 @@ public class YamlContract { return false; } Input input = (Input) o; - return Objects.equals(messageFrom, input.messageFrom) && Objects.equals(triggeredBy, input.triggeredBy) - && Objects.equals(messageHeaders, input.messageHeaders) - && Objects.equals(messageBody, input.messageBody) - && Objects.equals(messageBodyFromFile, input.messageBodyFromFile) - && Objects.equals(messageBodyFromFileAsBytes, input.messageBodyFromFileAsBytes) - && Objects.equals(assertThat, input.assertThat) && Objects.equals(matchers, input.matchers); + return Objects.equals(triggeredBy, input.triggeredBy) && Objects.equals(assertThat, input.assertThat); } @Override public int hashCode() { - return Objects.hash(messageFrom, triggeredBy, messageHeaders, messageBody, messageBodyFromFile, - messageBodyFromFileAsBytes, assertThat, matchers); + return Objects.hash(triggeredBy, assertThat); } @Override public String toString() { - return "Input{" + "messageFrom='" + messageFrom + '\'' + ", triggeredBy='" + triggeredBy + '\'' - + ", messageHeaders=" + messageHeaders + ", messageBody=" + messageBody + ", messageBodyFromFile='" - + messageBodyFromFile + '\'' + ", messageBodyFromFileAsBytes='" + messageBodyFromFileAsBytes + '\'' - + ", assertThat='" + assertThat + '\'' + ", matchers=" + matchers + '}'; + return "Input{" + "triggeredBy='" + triggeredBy + '\'' + ", assertThat='" + assertThat + '\'' + '}'; } } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlToContracts.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlToContracts.java index bb6c8577f6..a19407f390 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlToContracts.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/YamlToContracts.java @@ -661,23 +661,13 @@ class YamlToContracts { YamlContract.Input yamlContractInput = yamlContract.input; if (yamlContractInput != null) { dslContract.input(dslContractInput -> { - mapInputMessageFrom(yamlContractInput, dslContractInput); mapInputAssertThat(yamlContractInput, dslContractInput); mapInputTriggeredBy(yamlContractInput, dslContractInput); - mapInputMessageHeaders(yamlContractInput, dslContractInput); - mapInputMessageBody(yamlContractInput, dslContractInput); - mapInputBodyMatchers(yamlContractInput, dslContractInput); }); } } - private void mapInputMessageFrom(YamlContract.Input yamlContractInput, Input dslContractInput) { - if (yamlContractInput.messageFrom != null) { - dslContractInput.messageFrom(yamlContractInput.messageFrom); - } - } - private void mapInputAssertThat(YamlContract.Input yamlContractInput, Input dslContractInput) { if (yamlContractInput.assertThat != null) { dslContractInput.assertThat(yamlContractInput.assertThat); @@ -690,80 +680,6 @@ class YamlToContracts { } } - private void mapInputMessageHeaders(YamlContract.Input yamlContractInput, Input dslContractInput) { - dslContractInput - .messageHeaders(dslContractMessageHeaders -> Optional.ofNullable(yamlContractInput.messageHeaders) - .ifPresent(yamlContractMessageHeaders -> yamlContractMessageHeaders.forEach((key, value) -> { - YamlContract.KeyValueMatcher matcher = Optional.ofNullable(yamlContractInput.matchers) - .map(yamlContractInputMatchers -> yamlContractInputMatchers.headers) - .flatMap(yamlContractInputMatchersHeaders -> yamlContractInputMatchersHeaders - .stream() - .filter(yamlContractInputMatchersHeader -> yamlContractInputMatchersHeader.key - .equals(key)) - .findFirst()) - .orElse(null); - dslContractMessageHeaders.header(key, clientValue(value, matcher, key)); - }))); - } - - private void mapInputMessageBody(YamlContract.Input yamlContractInput, Input dslContractInput) { - if (yamlContractInput.messageBody != null) { - dslContractInput.messageBody(yamlContractInput.messageBody); - } - if (yamlContractInput.messageBodyFromFile != null) { - dslContractInput.messageBody(file(yamlContractInput.messageBodyFromFile)); - } - if (yamlContractInput.messageBodyFromFileAsBytes != null) { - dslContractInput.messageBody(dslContractInput.fileAsBytes(yamlContractInput.messageBodyFromFileAsBytes)); - } - } - - private void mapInputBodyMatchers(YamlContract.Input yamlContractInput, Input dslContractInput) { - dslContractInput - .bodyMatchers(dslContractInputBodyMatchers -> Optional.ofNullable(yamlContractInput.matchers.body) - .ifPresent(yamlContractBodyStubMatchers -> yamlContractBodyStubMatchers - .forEach(yamlContractBodyStubMatcher -> { - ContentType contentType = evaluateClientSideContentType( - yamlHeadersToContractHeaders( - Optional.ofNullable(yamlContractInput.messageHeaders) - .orElse(new HashMap<>())), - Optional.ofNullable(yamlContractInput.messageBody).orElse(null)); - MatchingTypeValue value; - switch (yamlContractBodyStubMatcher.type) { - case by_date: - value = dslContractInputBodyMatchers.byDate(); - break; - case by_time: - value = dslContractInputBodyMatchers.byTime(); - break; - case by_timestamp: - value = dslContractInputBodyMatchers.byTimestamp(); - break; - case by_regex: - String regex = yamlContractBodyStubMatcher.value; - if (yamlContractBodyStubMatcher.predefined != null) { - regex = predefinedToPattern(yamlContractBodyStubMatcher.predefined) - .pattern(); - } - value = dslContractInputBodyMatchers.byRegex(regex); - break; - case by_equality: - value = dslContractInputBodyMatchers.byEquality(); - break; - default: - throw new UnsupportedOperationException("The type " + "[" - + yamlContractBodyStubMatcher.type + "] is unsupported. " - + "Hint: If you're using remember to pass < type:by_regex > "); - } - if (XML == contentType) { - dslContractInputBodyMatchers.xPath(yamlContractBodyStubMatcher.path, value); - } - else { - dslContractInputBodyMatchers.jsonPath(yamlContractBodyStubMatcher.path, value); - } - }))); - } - private Headers yamlHeadersToContractHeaders(Map headers) { Set
convertedHeaders = headers.keySet().stream() .map(header -> Header.build(header, headers.get(header))).collect(toSet()); diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/file/SingleContractMetadata.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/file/SingleContractMetadata.java index f96d6a7274..4845c17da6 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/file/SingleContractMetadata.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/file/SingleContractMetadata.java @@ -29,7 +29,6 @@ import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.spec.internal.DslProperty; import org.springframework.cloud.contract.spec.internal.Header; import org.springframework.cloud.contract.spec.internal.Headers; -import org.springframework.cloud.contract.spec.internal.Input; import org.springframework.cloud.contract.spec.internal.OutputMessage; import org.springframework.cloud.contract.spec.internal.Request; import org.springframework.cloud.contract.spec.internal.Response; @@ -183,12 +182,11 @@ public class SingleContractMetadata { private DslProperty inputBody(Contract contract) { return Optional.ofNullable(contract.getRequest()).map(Request::getBody).map(DslProperty.class::cast) - .orElseGet(() -> Optional.ofNullable(contract.getInput()).map(Input::getMessageBody).orElse(null)); + .orElse(null); } private Headers inputHeaders(Contract contract) { - return Optional.ofNullable(contract.getRequest()).map(Request::getHeaders) - .orElseGet(() -> Optional.ofNullable(contract.getInput()).map(Input::getMessageHeaders).orElse(null)); + return Optional.ofNullable(contract.getRequest()).map(Request::getHeaders).orElse(null); } private DslProperty outputBody(Contract contract) { diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java index 041872911d..bdf1612d1e 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java @@ -56,7 +56,8 @@ public class ContractVerifierCamelConfiguration { @Bean @ConditionalOnMissingBean - public ContractVerifierMessaging contractVerifierMessaging(MessageVerifierSender sender, MessageVerifierReceiver receiver) { + public ContractVerifierMessaging contractVerifierMessaging(MessageVerifierSender sender, + MessageVerifierReceiver receiver) { return new ContractVerifierCamelHelper(sender, receiver); } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java index 87c1aca775..5f8cb6403b 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java @@ -49,7 +49,8 @@ public class ContractVerifierIntegrationConfiguration { @Bean @ConditionalOnMissingBean - public ContractVerifierMessaging> contractVerifierMessaging(MessageVerifierSender> sender, MessageVerifierReceiver> receiver) { + public ContractVerifierMessaging> contractVerifierMessaging(MessageVerifierSender> sender, + MessageVerifierReceiver> receiver) { return new ContractVerifierHelper(sender, receiver); } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/jms/ContractVerifierJmsConfiguration.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/jms/ContractVerifierJmsConfiguration.java index 199921df02..e335730fd0 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/jms/ContractVerifierJmsConfiguration.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/jms/ContractVerifierJmsConfiguration.java @@ -62,7 +62,8 @@ public class ContractVerifierJmsConfiguration { @Bean @ConditionalOnMissingBean - ContractVerifierMessaging contractVerifierJmsMessaging(MessageVerifierSender sender, MessageVerifierReceiver receiver) { + ContractVerifierMessaging contractVerifierJmsMessaging(MessageVerifierSender sender, + MessageVerifierReceiver receiver) { return new ContractVerifierJmsHelper(sender, receiver); } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/noop/NoOpContractVerifierAutoConfiguration.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/noop/NoOpContractVerifierAutoConfiguration.java index 04b8d27e4d..2f75fcdf05 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/noop/NoOpContractVerifierAutoConfiguration.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/noop/NoOpContractVerifierAutoConfiguration.java @@ -22,8 +22,6 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; -import org.springframework.cloud.contract.verifier.messaging.MessageVerifierReceiver; -import org.springframework.cloud.contract.verifier.messaging.MessageVerifierSender; import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; import org.springframework.context.annotation.Bean; diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierStreamAutoConfiguration.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierStreamAutoConfiguration.java index 88d6a46fa1..366e20802e 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierStreamAutoConfiguration.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierStreamAutoConfiguration.java @@ -19,7 +19,6 @@ package org.springframework.cloud.contract.verifier.messaging.stream; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.cloud.contract.verifier.messaging.MessageVerifierReceiver; @@ -46,7 +45,8 @@ public class ContractVerifierStreamAutoConfiguration { @Bean @ConditionalOnMissingBean - public ContractVerifierMessaging contractVerifierMessagingConverter(MessageVerifierSender> sender, MessageVerifierReceiver> receiver) { + public ContractVerifierMessaging contractVerifierMessagingConverter(MessageVerifierSender> sender, + MessageVerifierReceiver> receiver) { return new ContractVerifierHelper(sender, receiver); } @@ -63,20 +63,6 @@ public class ContractVerifierStreamAutoConfiguration { } - @Configuration(proxyBeanMethods = false) - @ConditionalOnMissingClass({ "org.springframework.cloud.stream.binder.test.InputDestination" }) - static class NoOpStreamClassConfiguration { - - @Bean - @ConditionalOnMissingBean - MessageVerifier> contractVerifierMessageExchangeWithNoMessageCollector( - ApplicationContext applicationContext) { - return new StreamStubMessages(new StreamStubMessageSender(applicationContext), - new StreamPollableChannelMessageReceiver(applicationContext)); - } - - } - } class ContractVerifierHelper extends ContractVerifierMessaging> { diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamFromBinderMappingMessageSender.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamFromBinderMappingMessageSender.java deleted file mode 100644 index b0d74636fa..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamFromBinderMappingMessageSender.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.messaging.stream; - -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.verifier.converter.YamlContract; -import org.springframework.cloud.contract.verifier.messaging.MessageVerifierSender; -import org.springframework.context.ApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; - -/** - * @author Marcin Grzejszczak - */ -class StreamFromBinderMappingMessageSender implements MessageVerifierSender> { - - private static final Log log = LogFactory.getLog(StreamFromBinderMappingMessageSender.class); - - private final ApplicationContext context; - - private final DestinationResolver resolver; - - private final ContractVerifierStreamMessageBuilder builder = new ContractVerifierStreamMessageBuilder(); - - StreamFromBinderMappingMessageSender(ApplicationContext context, DestinationResolver resolver) { - this.context = context; - this.resolver = resolver; - } - - @Override - public void send(T payload, Map headers, String destination, YamlContract contract) { - send(this.builder.create(payload, headers), destination, contract); - } - - @Override - public void send(Message message, String destination, YamlContract contract) { - try { - MessageChannel messageChannel = this.context.getBean( - this.resolver.resolvedDestination(destination, DefaultChannels.OUTPUT), MessageChannel.class); - messageChannel.send(message); - } - catch (Exception e) { - log.error("Exception occurred while trying to send a message [" + message + "] " - + "to a channel with name [" + destination + "]", e); - throw e; - } - } - -} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamPollableChannelMessageReceiver.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamPollableChannelMessageReceiver.java deleted file mode 100644 index 793ceceb1c..0000000000 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/stream/StreamPollableChannelMessageReceiver.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.messaging.stream; - -import java.util.concurrent.TimeUnit; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.contract.verifier.converter.YamlContract; -import org.springframework.cloud.contract.verifier.messaging.MessageVerifierReceiver; -import org.springframework.context.ApplicationContext; -import org.springframework.integration.channel.QueueChannel; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.PollableChannel; -import org.springframework.messaging.SubscribableChannel; - -class StreamPollableChannelMessageReceiver implements MessageVerifierReceiver> { - - private static final Log log = LogFactory.getLog(StreamPollableChannelMessageReceiver.class); - - private final ApplicationContext context; - - private final DestinationResolver destinationResolver; - - private final PollableChannel messageChannel; - - StreamPollableChannelMessageReceiver(ApplicationContext context) { - this.context = context; - this.destinationResolver = new DestinationResolver(context); - this.messageChannel = new QueueChannel(1); - } - - @Override - public Message receive(String destination, long timeout, TimeUnit timeUnit, YamlContract contract) { - MessageHandler handler = this.messageChannel::send; - MessageChannel channel = null; - try { - channel = this.context.getBean( - this.destinationResolver.resolvedDestination(destination, DefaultChannels.INPUT), - MessageChannel.class); - if (channel instanceof SubscribableChannel) { - ((SubscribableChannel) channel).subscribe(handler); - return this.messageChannel.receive(timeUnit.toMillis(timeout)); - } - else if (channel instanceof PollableChannel) { - return ((PollableChannel) channel).receive(timeUnit.toMillis(timeout)); - } - throw new IllegalStateException("Unsupported channel type"); - } - catch (Exception e) { - log.error("Exception occurred while trying to read a message from " + " a channel with name [" + destination - + "]", e); - throw new IllegalStateException(e); - } - finally { - if (channel instanceof SubscribableChannel) { - ((SubscribableChannel) channel).unsubscribe(handler); - } - } - } - - @Override - public Message receive(String destination, YamlContract contract) { - return receive(destination, 5, TimeUnit.SECONDS, contract); - } - -} diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy index 5927d5ae5d..5f43ff6fb1 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy @@ -234,550 +234,6 @@ public class FooTest { test.trim() == messageWithoutTags(expectedMessage, "trigger_method_junit_test") } - def "should generate tests triggered by a message for Spock"() { - given: - // tag::trigger_message_dsl[] - def contractDsl = Contract.make { - name "foo" - label 'some_label' - input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('jms:output') - body([ - bookName: 'foo' - ]) - headers { - header('BOOK-NAME', 'foo') - } - } - } - // end::trigger_message_dsl[] - properties.testFramework = TestFramework.SPOCK - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - """\ -// tag::trigger_message_spock[] -package com.example - -import com.jayway.jsonpath.DocumentContext -import com.jayway.jsonpath.JsonPath -import spock.lang.Specification -import javax.inject.Inject -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.* -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes - -@SuppressWarnings("rawtypes") -class FooSpec extends Specification { - @Inject ContractVerifierMessaging contractVerifierMessaging - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper - - def validate_foo() throws Exception { - given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - '''{"bookName":"foo"}''' - , headers() - .header("sample", "header") - ) - - when: - contractVerifierMessaging.send(inputMessage, "jms:input", - contract(this, "foo.yml")) - - then: - ContractVerifierMessage response = contractVerifierMessaging.receive("jms:output", - contract(this, "foo.yml")) - response != null - - and: - response.getHeader("BOOK-NAME") != null - response.getHeader("BOOK-NAME").toString() == 'foo' - - and: - DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload())) - assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo") - } - -} -// end::trigger_message_spock[] -""" - test.trim() == messageWithoutTags(expectedMessage, "trigger_message_spock") - } - - def "should generate tests triggered by a message for JUnit"() { - given: - def contractDsl = Contract.make { - name "foo" - label 'some_label' - input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('jms:output') - body([ - bookName: 'foo' - ]) - headers { - header('BOOK-NAME', 'foo') - } - } - } - - properties.testFramework = TestFramework.JUNIT - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - '''\ -// tag::trigger_message_junit[] -package com.example; - -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import org.junit.Test; -import org.junit.Rule; -import javax.inject.Inject; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*; -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes; - -@SuppressWarnings("rawtypes") -public class FooTest { - @Inject ContractVerifierMessaging contractVerifierMessaging; - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper; - - @Test - public void validate_foo() throws Exception { - // given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - "{\\"bookName\\":\\"foo\\"}" - , headers() - .header("sample", "header") - ); - - // when: - contractVerifierMessaging.send(inputMessage, "jms:input", - contract(this, "foo.yml")); - - // then: - ContractVerifierMessage response = contractVerifierMessaging.receive("jms:output", - contract(this, "foo.yml")); - assertThat(response).isNotNull(); - - // and: - assertThat(response.getHeader("BOOK-NAME")).isNotNull(); - assertThat(response.getHeader("BOOK-NAME").toString()).isEqualTo("foo"); - - // and: - DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload())); - assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo"); - } - -} -// end::trigger_message_junit[] -''' - test.trim() == messageWithoutTags(expectedMessage, "trigger_message_junit") - } - - def "should generate tests without destination, triggered by a message"() { - given: - // tag::trigger_no_output_dsl[] - def contractDsl = Contract.make { - name "foo" - label 'some_label' - input { - messageFrom('jms:delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } - } - // end::trigger_no_output_dsl[] - properties.testFramework = TestFramework.SPOCK - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - """\ -// tag::trigger_no_output_spock[] -package com.example - -import com.jayway.jsonpath.DocumentContext -import com.jayway.jsonpath.JsonPath -import spock.lang.Specification -import javax.inject.Inject -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.* -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes - -@SuppressWarnings("rawtypes") -class FooSpec extends Specification { - @Inject ContractVerifierMessaging contractVerifierMessaging - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper - - def validate_foo() throws Exception { - given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - '''{"bookName":"foo"}''' - , headers() - .header("sample", "header") - ) - - when: - contractVerifierMessaging.send(inputMessage, "jms:delete", - contract(this, "foo.yml")) - bookWasDeleted() - - then: - noExceptionThrown() - } - -} -// end::trigger_no_output_spock[] -""" - test.trim() == messageWithoutTags(expectedMessage, "trigger_no_output_spock") - } - - def "should generate tests without destination, triggered by a message for JUnit"() { - given: - def contractDsl = Contract.make { - name "foo" - label 'some_label' - input { - messageFrom('jms:delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } - } - - properties.testFramework = TestFramework.JUNIT - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - """\ -// tag::trigger_no_output_junit[] -package com.example; - -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import org.junit.Test; -import org.junit.Rule; -import javax.inject.Inject; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*; -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes; - -@SuppressWarnings("rawtypes") -public class FooTest { - @Inject ContractVerifierMessaging contractVerifierMessaging; - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper; - - @Test - public void validate_foo() throws Exception { - // given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - "{\\"bookName\\":\\"foo\\"}" - , headers() - .header("sample", "header") - ); - - // when: - contractVerifierMessaging.send(inputMessage, "jms:delete", - contract(this, "foo.yml")); - bookWasDeleted(); - - } - -} -// end::trigger_no_output_junit[] -""" - test.trim() == messageWithoutTags(expectedMessage, "trigger_no_output_junit") - } - - def "should generate tests without headers for JUnit"() { - given: - def contractDsl = Contract.make { - name "foo" - label 'some_label' - input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('jms:output') - body([ - bookName: 'foo' - ]) - } - } - properties.testFramework = TestFramework.JUNIT - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - """\ -package com.example; - -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import org.junit.Test; -import org.junit.Rule; -import javax.inject.Inject; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*; -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes; - -@SuppressWarnings("rawtypes") -public class FooTest { - @Inject ContractVerifierMessaging contractVerifierMessaging; - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper; - - @Test - public void validate_foo() throws Exception { - // given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - "{\\"bookName\\":\\"foo\\"}" - , headers() - .header("sample", "header") - ); - - // when: - contractVerifierMessaging.send(inputMessage, "jms:input", - contract(this, "foo.yml")); - - // then: - ContractVerifierMessage response = contractVerifierMessaging.receive("jms:output", - contract(this, "foo.yml")); - assertThat(response).isNotNull(); - - // and: - DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload())); - assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo"); - } - -} -""" - test.trim() == messageWithoutTags(expectedMessage, "expectedMsg") - } - - def "should generate tests without headers for Spock"() { - given: - def contractDsl = Contract.make { - name "foo" - label 'some_label' - input { - messageFrom('jms:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('jms:output') - body([ - bookName: 'foo' - ]) - } - } - properties.testFramework = TestFramework.SPOCK - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - """\ -package com.example - -import com.jayway.jsonpath.DocumentContext -import com.jayway.jsonpath.JsonPath -import spock.lang.Specification -import javax.inject.Inject -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.* -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes - -@SuppressWarnings("rawtypes") -class FooSpec extends Specification { - @Inject ContractVerifierMessaging contractVerifierMessaging - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper - - def validate_foo() throws Exception { - given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - '''{"bookName":"foo"}''' - , headers() - .header("sample", "header") - ) - - when: - contractVerifierMessaging.send(inputMessage, "jms:input", - contract(this, "foo.yml")) - - then: - ContractVerifierMessage response = contractVerifierMessaging.receive("jms:output", - contract(this, "foo.yml")) - response != null - - and: - DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload())) - assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo") - } - -} - -""" - test.trim() == messageWithoutTags(expectedMessage, "expectedMsg") - } - - def "should generate tests without headers for JUnit with consumer / producer notation"() { - given: - def contractDsl = - // tag::consumer_producer[] - Contract.make { - name "foo" - label 'some_label' - input { - messageFrom value(consumer('jms:output'), producer('jms:input')) - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo $(consumer('jms:input'), producer('jms:output')) - body([ - bookName: 'foo' - ]) - } - } - // end::consumer_producer[] - - properties.testFramework = TestFramework.JUNIT - when: - String test = singleTestGenerator(contractDsl) - then: - String expectedMessage = - ''' -package com.example; - -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import org.junit.Test; -import org.junit.Rule; -import javax.inject.Inject; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*; -import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes; - -@SuppressWarnings("rawtypes") -public class FooTest { - @Inject ContractVerifierMessaging contractVerifierMessaging; - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper; - - @Test - public void validate_foo() throws Exception { - // given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - "{\\"bookName\\":\\"foo\\"}" - , headers() - .header("sample", "header") - ); - - // when: - contractVerifierMessaging.send(inputMessage, "jms:input", - contract(this, "foo.yml")); - - // then: - ContractVerifierMessage response = contractVerifierMessaging.receive("jms:output", - contract(this, "foo.yml")); - assertThat(response).isNotNull(); - - // and: - DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload())); - assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo"); - } - -} -''' - test.trim() == messageWithoutTags(expectedMessage, "expectedMsg") - } - @Issue("336") def "should generate tests with message headers containing regular expression for JUnit"() { given: @@ -1398,132 +854,6 @@ public class FooTest { assertThatJson(parsedJson).field("['field']").isEqualTo("value"); } -} -""" - } - - @Issue('#664') - def "should generate tests for messages having binary payloads [#methodBuilderName]"() { - given: - Contract contractDsl = Contract.make { - name "foo" - label 'shouldPublishMessage' - input { - messageFrom("foo") - messageBody(fileAsBytes("body_builder/request.pdf")) - messageHeaders { - messagingContentType(applicationOctetStream()) - } - } - outputMessage { - sentTo('messageExchange') - body(fileAsBytes("body_builder/response.pdf")) - headers { - messagingContentType(applicationOctetStream()) - } - } - } - methodBuilder() - when: - String test = singleTestGenerator(contractDsl) - then: - !test.contains('cursor') - !test.contains('REGEXP>>') - test.trim() == expectedTest.trim() - where: - methodBuilderName | methodBuilder | expectedTest - "spock" | { properties.testFramework = TestFramework.SPOCK } | """\ -package com.example - -import spock.lang.Specification -import javax.inject.Inject -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.* -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes - -@SuppressWarnings("rawtypes") -class FooSpec extends Specification { - @Inject ContractVerifierMessaging contractVerifierMessaging - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper - - def validate_foo() throws Exception { - given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - fileToBytes(this, "foo_request_request.pdf") - , headers() - .header("contentType", "application/octet-stream") - ) - - when: - contractVerifierMessaging.send(inputMessage, "foo", - contract(this, "foo.yml")) - - then: - ContractVerifierMessage response = contractVerifierMessaging.receive("messageExchange", - contract(this, "foo.yml")) - response != null - - and: - response.getHeader("contentType") != null - response.getHeader("contentType").toString() == 'application/octet-stream' - - and: - response.getPayloadAsByteArray() == fileToBytes(this, "foo_response_response.pdf") - } - -} -""" - "junit" | { properties.testFramework = TestFramework.JUNIT } | """\ -package com.example; - -import org.junit.Test; -import org.junit.Rule; -import javax.inject.Inject; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; - -import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*; -import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers; -import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes; - -@SuppressWarnings("rawtypes") -public class FooTest { - @Inject ContractVerifierMessaging contractVerifierMessaging; - @Inject ContractVerifierObjectMapper contractVerifierObjectMapper; - - @Test - public void validate_foo() throws Exception { - // given: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - fileToBytes(this, "foo_request_request.pdf") - , headers() - .header("contentType", "application/octet-stream") - ); - - // when: - contractVerifierMessaging.send(inputMessage, "foo", - contract(this, "foo.yml")); - - // then: - ContractVerifierMessage response = contractVerifierMessaging.receive("messageExchange", - contract(this, "foo.yml")); - assertThat(response).isNotNull(); - - // and: - assertThat(response.getHeader("contentType")).isNotNull(); - assertThat(response.getHeader("contentType").toString()).isEqualTo("application/octet-stream"); - - // and: - assertThat(response.getPayloadAsByteArray()).isEqualTo(fileToBytes(this, "foo_response_response.pdf")); - } - } """ } 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 a426d1ebe5..663faa2e57 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 @@ -331,13 +331,7 @@ class SingleTestGeneratorSpec extends Specification { ignored() label 'some_label' input { - messageFrom('delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } + triggeredBy("hashCode()") assertThat('hashCode()') } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy index d59b7e8ed1..7909e89faf 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy @@ -64,11 +64,8 @@ class YamlContractConverterSpec extends Specification { @Shared File ymlWithRest3 = new File(ymlUrl3.toURI()) URL ymlMsgUrl = YamlContractConverterSpec.getResource("/yml/contract_message.yml") - File ymlMessaging = new File(ymlMsgUrl.toURI()) URL ymlMsgMethodUrl = YamlContractConverterSpec.getResource("/yml/contract_message_method.yml") File ymlMessagingMethod = new File(ymlMsgMethodUrl.toURI()) - URL ymlMsgMsgUrl = YamlContractConverterSpec.getResource("/yml/contract_message_input_message.yml") - File ymlMessagingMsg = new File(ymlMsgMsgUrl.toURI()) URL ymlBodyFile = YamlContractConverterSpec.getResource("/yml/contract_from_file.yml") File ymlBody = new File(ymlBodyFile.toURI()) URL ymlReferenceFile = YamlContractConverterSpec.getResource("/yml/contract_reference_request.yml") @@ -413,38 +410,7 @@ class YamlContractConverterSpec extends Specification { Collection contracts = converter.convertFrom(ymlMessagingMatchers) then: contracts.size() == 1 - Contract contract = contracts.first() - contract.input.messageHeaders.entries.find { - it.name == "contentType" && - ((Pattern) it.clientValue).pattern() == "application/json.*" && it.serverValue == "application/json" - } - contract.input.bodyMatchers.matchers[0].path() == '$.duck' - contract.input.bodyMatchers.matchers[0].matchingType() == REGEX - contract.input.bodyMatchers.matchers[0].value().pattern() == '[0-9]{3}' - contract.input.bodyMatchers.matchers[1].path() == '$.duck' - contract.input.bodyMatchers.matchers[1].matchingType() == EQUALITY - contract.input.bodyMatchers.matchers[2].path() == '$.alpha' - contract.input.bodyMatchers.matchers[2].matchingType() == REGEX - contract.input.bodyMatchers.matchers[2].value().pattern() == RegexPatterns.onlyAlphaUnicode().pattern() - contract.input.bodyMatchers.matchers[3].path() == '$.alpha' - contract.input.bodyMatchers.matchers[3].matchingType() == EQUALITY - contract.input.bodyMatchers.matchers[4].path() == '$.number' - contract.input.bodyMatchers.matchers[4].matchingType() == REGEX - contract.input.bodyMatchers.matchers[4].value().pattern() == RegexPatterns.number().pattern() - contract.input.bodyMatchers.matchers[5].path() == '$.aBoolean' - contract.input.bodyMatchers.matchers[5].matchingType() == REGEX - contract.input.bodyMatchers.matchers[5].value().pattern() == RegexPatterns.anyBoolean().pattern() - contract.input.bodyMatchers.matchers[6].path() == '$.date' - contract.input.bodyMatchers.matchers[6].matchingType() == DATE - contract.input.bodyMatchers.matchers[6].value().pattern() == RegexPatterns.isoDate().pattern() - contract.input.bodyMatchers.matchers[7].path() == '$.dateTime' - contract.input.bodyMatchers.matchers[7].matchingType() == TIMESTAMP - contract.input.bodyMatchers.matchers[7].value().pattern() == RegexPatterns.isoDateTime().pattern() - contract.input.bodyMatchers.matchers[8].path() == '$.time' - contract.input.bodyMatchers.matchers[8].matchingType() == TIME - contract.input.bodyMatchers.matchers[8].value().pattern() == RegexPatterns.isoTime().pattern() - contract.input.bodyMatchers.matchers[9].path() == "\$.['key'].['complex.key']" - contract.input.bodyMatchers.matchers[9].matchingType() == EQUALITY + Contract contract = contracts[0] and: contract.outputMessage.bodyMatchers.matchers[0].path() == '$.duck' contract.outputMessage.bodyMatchers.matchers[0].matchingType() == REGEX @@ -534,51 +500,6 @@ class YamlContractConverterSpec extends Specification { contract.response.status.serverValue == 200 } - def "should convert YAML with messaging to DSL"() { - given: - assert converter.isAccepted(ymlMessaging) - when: - Collection contracts = converter.convertFrom(ymlMessaging) - then: - contracts.size() == 1 - Contract contract = contracts.first() - contract.description == "Some description" - contract.name == "some name" - contract.label == "some_label" - contract.ignored == true - contract.input.assertThat.toString() == "bar()" - contract.input.messageFrom.serverValue == "foo" - contract.input.triggeredBy.toString() == "foo()" - contract.input.messageHeaders.entries.find { - it.name == "foo" && - ((Pattern) it.clientValue).pattern() == "bar" && it.serverValue == "bar" - } - contract.input.messageBody.clientValue == [foo: "bar"] - contract.input.bodyMatchers.matchers[0].path() == '$.bar' - contract.input.bodyMatchers.matchers[0].matchingType() == REGEX - contract.input.bodyMatchers.matchers[0].value().pattern() == 'bar' - and: - contract.outputMessage.assertThat.toString() == "baz()" - contract.outputMessage.headers.entries.find { - it.name == "foo2" && - ((Pattern) it.serverValue).pattern() == "bar" && it.clientValue == "bar" - } - contract.outputMessage.headers.entries.find { - it.name == "foo3" && - ((ExecutionProperty) it.serverValue).insertValue('foo') == "andMeToo(foo)" - } - contract.outputMessage.headers.entries.find { - it.name == "fooRes" && - it.clientValue == "baz" - } - contract.outputMessage.body.clientValue == [foo2: "bar", foo3: "baz"] - contract.outputMessage.bodyMatchers.matchers[0].path() == '$.foo2' - contract.outputMessage.bodyMatchers.matchers[0].matchingType() == REGEX - contract.outputMessage.bodyMatchers.matchers[0].value().pattern() == 'bar' - contract.outputMessage.bodyMatchers.matchers[1].path() == '$.foo3' - contract.outputMessage.bodyMatchers.matchers[1].matchingType() == COMMAND - contract.outputMessage.bodyMatchers.matchers[1].value() == new ExecutionProperty('executeMe($it)') - } def "should convert YAML with messaging triggered by a method to DSL"() { given: @@ -599,30 +520,6 @@ class YamlContractConverterSpec extends Specification { contract.outputMessage.body.clientValue == [bookName: "foo"] } - def "should convert YAML with messaging triggered by a message to DSL"() { - given: - assert converter.isAccepted(ymlMessagingMsg) - when: - Collection contracts = converter.convertFrom(ymlMessagingMsg) - then: - contracts.size() == 1 - Contract contract = contracts.first() - contract.description == "Some description" - contract.label == "some_label" - contract.input.messageFrom.serverValue == "input" - contract.input.messageHeaders.entries.find { - it.name == "sample" && - it.serverValue == "header" - } - contract.input.messageBody.clientValue == [bookName: "foo"] - and: - contract.outputMessage.sentTo.clientValue == "output" - contract.outputMessage.headers.entries.find { - it.name == "BOOK-NAME" && it.clientValue == "foo" - } - contract.outputMessage.body.clientValue == [bookName: "foo"] - } - def "should convert YAML with HTTP binary body to DSL"() { given: assert converter.isAccepted(ymlBytes) @@ -646,8 +543,6 @@ class YamlContractConverterSpec extends Specification { then: contracts.size() == 1 Contract contract = contracts.first() - contract.input.messageBody.clientValue instanceof FromFileProperty - ((FromFileProperty) contract.input.messageBody.clientValue).type == byte[] and: contract.outputMessage.body.clientValue instanceof FromFileProperty ((FromFileProperty) contract.outputMessage.body.clientValue).type == byte[] @@ -800,8 +695,10 @@ metadata: {} )] } - def "should parse messaging contract for [#file]"() { + def "should parse messaging contract for messaging scenario 1"() { given: + URI uri = YamlContractConverterSpec.getResource("/yml/contract_message_scenario1.yml").toURI() + File file = new File(uri) assert converter.isAccepted(file) when: Collection contracts = converter.convertFrom(file) @@ -809,10 +706,6 @@ metadata: {} contracts.size() == 1 and: contracts.first().input != null || contracts.first().outputMessage != null - where: - file << [1, 2, 3].collect { - new File(YamlContractConverterSpec.getResource("/yml/contract_message_scenario${it}.yml").toURI()) - } } def "should convert HTTP DSL to YAML"() { @@ -949,59 +842,6 @@ metadata: {} yamlContract.outputMessage.headers == ["BOOK-NAME": "foo"] } - def "should convert Messaging DSL with input and output message to YAML"() { - given: - List contracts = [Contract.make { - input { - messageFrom("jms:input") - messageBody([bookName: 'foo']) - messageHeaders { - header("sample", "header") - } - } - outputMessage { - sentTo("output") - body([bookName: "foo"]) - headers { - header("BOOK-NAME", "foo") - } - } - }] - when: - Collection yamlContracts = converter.convertTo(contracts) - then: - yamlContracts.size() == 1 - YamlContract yamlContract = yamlContracts.first() - yamlContract.input.messageFrom == "jms:input" - yamlContract.input.messageBody == [bookName: 'foo'] - yamlContract.input.messageHeaders == ["sample": "header"] - yamlContract.outputMessage.sentTo == "output" - yamlContract.outputMessage.body == [bookName: "foo"] - yamlContract.outputMessage.headers == ["BOOK-NAME": "foo"] - } - - def "should convert Messaging DSL with only input message to YAML"() { - given: - List contracts = [Contract.make { - input { - messageFrom("jms:input") - messageBody([bookName: 'foo']) - messageHeaders { - header("sample", "header") - } - assertThat("bookWasDeleted()") - } - }] - when: - Collection yamlContracts = converter.convertTo(contracts) - then: - yamlContracts.size() == 1 - YamlContract yamlContract = yamlContracts.first() - yamlContract.input.messageFrom == "jms:input" - yamlContract.input.messageBody == [bookName: 'foo'] - yamlContract.input.messageHeaders == ["sample": "header"] - yamlContract.input.assertThat == "bookWasDeleted()" - } def "should convert Messaging with a message DSL to YAML"() { given: @@ -1013,35 +853,7 @@ metadata: {} ignored() inProgress() input { - messageFrom("input") - messageBody([ - duck : 123, - alpha : "abc", - number : 123, - aBoolean : true, - date : "2017-01-01", - dateTime : "2017-01-01T01:23:45", - time : "01:02:34", - valueWithoutAMatcher: "foo", - valueWithTypeMatch : "string", - key : ["complex.key": 'foo'] - ]) - bodyMatchers { - jsonPath('$.duck', byRegex("[0-9]{3}")) - jsonPath('$.duck', byEquality()) - jsonPath('$.alpha', byRegex(onlyAlphaUnicode())) - jsonPath('$.alpha', byEquality()) - jsonPath('$.number', byRegex(number())) - jsonPath('$.aBoolean', byRegex(anyBoolean())) - jsonPath('$.date', byDate()) - jsonPath('$.dateTime', byTimestamp()) - jsonPath('$.time', byTime()) - jsonPath("\$.['key'].['complex.key']", byEquality()) - } - messageHeaders { - header("sample", $(c(regex("foo.*")), p("foo"))) - messagingContentType(applicationJson()) - } + triggeredBy("foo()") } outputMessage { sentTo("channel") @@ -1121,67 +933,8 @@ metadata: {} yamlContract.name == "fooo" yamlContract.ignored == true yamlContract.inProgress == true + yamlContract.input.triggeredBy == "foo()" yamlContract.label == "card_rejected" - yamlContract.input.messageFrom == "input" - yamlContract.input.messageBody == [ - duck : 123, - alpha : "abc", - number : 123, - aBoolean : true, - date : "2017-01-01", - dateTime : "2017-01-01T01:23:45", - time : "01:02:34", - valueWithoutAMatcher: "foo", - valueWithTypeMatch : "string", - key : ["complex.key": 'foo'] - ] - yamlContract.input.messageHeaders == [ - sample : 'foo', - contentType: "application/json" - ] - yamlContract.input.matchers.headers == [ - new YamlContract.KeyValueMatcher( - key: "sample", regex: "foo.*", regexType: YamlContract.RegexType.as_string) - ] - yamlContract.input.matchers.body == [ - new YamlContract.BodyStubMatcher( - path: '$.duck', - type: YamlContract.StubMatcherType.by_regex, - value: "[0-9]{3}"), - new YamlContract.BodyStubMatcher( - path: '$.duck', - type: YamlContract.StubMatcherType.by_equality), - new YamlContract.BodyStubMatcher( - path: '$.alpha', - type: YamlContract.StubMatcherType.by_regex, - value: "[\\p{L}]*"), - new YamlContract.BodyStubMatcher( - path: '$.alpha', - type: YamlContract.StubMatcherType.by_equality), - new YamlContract.BodyStubMatcher( - path: '$.number', - type: YamlContract.StubMatcherType.by_regex, - value: "-?(\\d*\\.\\d+|\\d+)"), - new YamlContract.BodyStubMatcher( - path: '$.aBoolean', - type: YamlContract.StubMatcherType.by_regex, - value: "(true|false)"), - new YamlContract.BodyStubMatcher( - path: '$.date', - type: YamlContract.StubMatcherType.by_date, - value: "(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"), - new YamlContract.BodyStubMatcher( - path: '$.dateTime', - type: YamlContract.StubMatcherType.by_timestamp, - value: "([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"), - new YamlContract.BodyStubMatcher( - path: '$.time', - type: YamlContract.StubMatcherType.by_time, - value: "(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"), - new YamlContract.BodyStubMatcher( - path: "\$.['key'].['complex.key']", - type: YamlContract.StubMatcherType.by_equality), - ] yamlContract.outputMessage.sentTo == "channel" yamlContract.outputMessage.body == [duck : 123, alpha : "abc", diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierHelperForStreamTest.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierHelperForStreamTest.groovy index 18446c0718..a42898ff33 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierHelperForStreamTest.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/messaging/stream/ContractVerifierHelperForStreamTest.groovy @@ -25,7 +25,7 @@ class ContractVerifierHelperForStreamTest extends Specification { def 'should throw exception when a null payload was sent'() { given: - ContractVerifierHelper helper = new ContractVerifierHelper(null) + ContractVerifierHelper helper = new ContractVerifierHelper(null, null) when: helper.convert(null) then: diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml index 5ea2bb9eeb..10687ca89c 100644 --- a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml @@ -3,21 +3,8 @@ label: some_label name: some name ignored: true input: - messageFrom: foo triggeredBy: foo() - messageHeaders: - foo: bar - messageBody: - foo: bar assertThat: bar() - matchers: - body: - - path: $.bar - type: by_regex - value: bar - headers: - - key: foo - regex: bar outputMessage: sentTo: bar headers: diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml deleted file mode 100644 index 0d0e7d5b6c..0000000000 --- a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Human readable description -description: Some description -# Label by means of which the output message can be triggered -label: some_label -# input is a message -input: - messageFrom: input - # has the following body - messageBody: - bookName: 'foo' - # and the following headers - messageHeaders: - sample: 'header' -# output message of the contract -outputMessage: - # destination to which the output message will be sent - sentTo: output - # the body of the output message - body: - bookName: foo - # the headers of the output message - headers: - BOOK-NAME: foo diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml index 64d1c39759..936355a5b4 100644 --- a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml @@ -1,50 +1,6 @@ label: card_rejected input: - messageFrom: input - messageBody: - duck: 123 - alpha: "abc" - number: 123 - aBoolean: true - date: "2017-01-01" - dateTime: "2017-01-01T01:23:45" - time: "01:02:34" - valueWithoutAMatcher: "foo" - valueWithTypeMatch: "string" - key: - "complex.key": 'foo' - messageHeaders: - sample: 'header' - contentType: application/json - matchers: - headers: - - key: contentType - regex: "application/json.*" - body: - - path: $.duck - type: by_regex - value: "[0-9]{3}" - - path: $.duck - type: by_equality - - path: $.alpha - type: by_regex - predefined: only_alpha_unicode - - path: $.alpha - type: by_equality - - path: $.number - type: by_regex - predefined: number - - path: $.aBoolean - type: by_regex - predefined: any_boolean - - path: $.date - type: by_date - - path: $.dateTime - type: by_timestamp - - path: $.time - type: by_time - - path: "$.['key'].['complex.key']" - type: by_equality + triggeredBy: "foo()" outputMessage: sentTo: channel body: diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml deleted file mode 100644 index 6edd133b6f..0000000000 --- a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml +++ /dev/null @@ -1,13 +0,0 @@ -label: some_label -input: - messageFrom: jms:input - messageBody: - bookName: 'foo' - messageHeaders: - sample: header -outputMessage: - sentTo: jms:output - body: - bookName: foo - headers: - BOOK-NAME: foo diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml deleted file mode 100644 index 88037cf2fa..0000000000 --- a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml +++ /dev/null @@ -1,8 +0,0 @@ -label: some_label -input: - messageFrom: jms:delete - messageBody: - bookName: 'foo' - messageHeaders: - sample: header - assertThat: bookWasDeleted() diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_messaging_pdf.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_messaging_pdf.yml index 2ee5fc677a..d402fed175 100644 --- a/spring-cloud-contract-verifier/src/test/resources/yml/contract_messaging_pdf.yml +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_messaging_pdf.yml @@ -1,9 +1,6 @@ label: some_label input: - messageFrom: jms:input - messageBodyFromFileAsBytes: request.pdf - messageHeaders: - contentType: application/octet-stream + triggeredBy: foo() outputMessage: sentTo: jms:output bodyFromFileAsBytes: response.pdf diff --git a/tests/pom.xml b/tests/pom.xml index b18acb0130..824ecd4f5d 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -49,7 +49,6 @@ samples-messaging-integration samples-messaging-amqp samples-messaging-jms - spring-cloud-contract-stub-runner-camel spring-cloud-contract-stub-runner-boot-eureka spring-cloud-contract-stub-runner-boot-zookeeper spring-cloud-contract-stub-runner-context-path @@ -72,7 +71,6 @@ samples-messaging-integration samples-messaging-amqp samples-messaging-jms - spring-cloud-contract-stub-runner-camel spring-cloud-contract-stub-runner-boot-eureka spring-cloud-contract-stub-runner-boot-zookeeper spring-cloud-contract-stub-runner-context-path diff --git a/tests/samples-messaging-amqp/src/test/groovy/com/example/AmqpMessagingApplicationSpec.groovy b/tests/samples-messaging-amqp/src/test/groovy/com/example/AmqpMessagingApplicationSpec.groovy index 44704120a5..30b4a3a653 100644 --- a/tests/samples-messaging-amqp/src/test/groovy/com/example/AmqpMessagingApplicationSpec.groovy +++ b/tests/samples-messaging-amqp/src/test/groovy/com/example/AmqpMessagingApplicationSpec.groovy @@ -87,115 +87,6 @@ class AmqpMessagingApplicationSpec { JsonAssertion.assertThat(parsedJson).field('name').isEqualTo('some') } -// @Issue("332") - @Test - void should_work_for_second_scenario() { - // given: - def dsl = - Contract.make { - description(""" -Represents scenario 2 from documentation: -https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_publisher_side_test_generation - -"The input message triggers an output message." - -``` -// given: - rabbit service is running -// when: - input message is received -// then: - message is send -``` - -""") - label 'some_label2' - input { - messageFrom('input') - messageBody([ - name: 'foo2' - ]) - messageHeaders { - messagingContentType(applicationJson()) - header('amqp_replyTo', 'amq.rabbitmq.reply-to') - header('bill', 'bill') - } - } - - outputMessage { - sentTo('') - body('''{ "name" : "foo2" }''') - headers { - messagingContentType(applicationJson()) - } - } - } - // generated test should look like this: - // and: - ContractVerifierMessage inputMessage = contractVerifierMessaging.create( - "{\"name\":\"foo2\"}" - , headers() - .header("contentType", "application/json") - .header("amqp_replyTo", "amq.rabbitmq.reply-to") - .header("bill", "bill") - ) - // when: - contractVerifierMessaging.send(inputMessage, "input") - // then: - ContractVerifierMessage response = contractVerifierMessaging.receive("") - assertThat(response).isNotNull() - assertThat(response.getHeader("contentType")).isNotNull() - assertThat(response.getHeader("contentType").toString()). - isEqualTo("application/json") - // and: - DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper. - writeValueAsString(response.getPayload())) - assertThatJson(parsedJson).field("['name']").isEqualTo("foo2") - } - -// @Issue("178") - @Test - void should_work_for_input_output_when_bytes_are_used() { - // given: - def inputBody = [ - ratedItemId: "992e46d8-ab05-4a26-a740-6ef7b0daeab3", - eventType : "CREATED" - ] - def dsl = Contract.make { - label 'ratedItem-no-metricid' - input { - messageFrom("rated-item-service.rated-item-event.exchange") - messageHeaders { - header("X-tenant", "1234") - header("contentType", "application/json") - } - messageBody(inputBody) - } - outputMessage { - sentTo('bill-service.rated-item-event.retry-exchange') - body( - ratedItemId: "992e46d8-ab05-4a26-a740-6ef7b0daeab3", - eventType: "CREATED" - ) - } - } - // when: - contractVerifierMessaging.send(contractVerifierMessaging. - create(new JsonOutput().toJson(inputBody), [ - "X-tenant" : "1234", - "contentType": "application/json" - ]), "rated-item-service.rated-item-event.exchange") - // then: - def response = contractVerifierMessaging. - receive('bill-service.rated-item-event.retry-exchange') - // and: - DocumentContext parsedJson = JsonPath. - parse(contractVerifierObjectMapper.writeValueAsString(response.payload)) - JsonAssertion.assertThat(parsedJson).field('ratedItemId'). - isEqualTo('992e46d8-ab05-4a26-a740-6ef7b0daeab3') - JsonAssertion.assertThat(parsedJson).field('eventType').isEqualTo('CREATED') - } - // BASE CLASS WOULD HAVE THIS: @Autowired diff --git a/tests/samples-messaging-camel/src/main/java/com/example/BookDeleted.java b/tests/samples-messaging-camel/src/main/java/com/example/BookDeleted.java deleted file mode 100644 index 28bf966944..0000000000 --- a/tests/samples-messaging-camel/src/main/java/com/example/BookDeleted.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.io.Serializable; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -@SuppressWarnings("serial") -public class BookDeleted implements Serializable { - - public final String bookName; - - @JsonCreator - public BookDeleted(@JsonProperty("bookName") String bookName) { - this.bookName = bookName; - } - -} diff --git a/tests/samples-messaging-camel/src/main/java/com/example/BookDeleter.java b/tests/samples-messaging-camel/src/main/java/com/example/BookDeleter.java deleted file mode 100644 index 43ceb5cfa2..0000000000 --- a/tests/samples-messaging-camel/src/main/java/com/example/BookDeleter.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.camel.Exchange; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.stereotype.Component; - -@Component -public class BookDeleter { - - private static final Logger log = LoggerFactory.getLogger(BookDeleter.class); - - private AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false); - - /** - * Scenario for "should generate tests triggered by a message": client side: if sends - * a message to input.messageFrom then message will be sent to output.messageFrom - * server side: will send a message to input, verify the message contents and await - * upon receiving message on the output messageFrom. - * @param exchange - input exchange. - */ - public void bookDeleted(Exchange exchange) { - BookDeleted bookDeleted = exchange.getIn().getBody(BookDeleted.class); - log.info("Deleting book " + bookDeleted); - this.bookSuccessfulyDeleted.set(true); - log.info("Book successfuly deleted [" + this.bookSuccessfulyDeleted + "]"); - } - -} diff --git a/tests/samples-messaging-camel/src/main/java/com/example/BookRouteConfiguration.java b/tests/samples-messaging-camel/src/main/java/com/example/BookRouteConfiguration.java index 1d66bd510a..d01b279fd9 100644 --- a/tests/samples-messaging-camel/src/main/java/com/example/BookRouteConfiguration.java +++ b/tests/samples-messaging-camel/src/main/java/com/example/BookRouteConfiguration.java @@ -33,7 +33,7 @@ import org.springframework.context.annotation.Configuration; public class BookRouteConfiguration { @Bean - RoutesBuilder myRouter(final BookService bookService, final BookDeleter bookDeleter, CamelContext context, + RoutesBuilder myRouter(final BookService bookService, CamelContext context, @Value("${spring.rabbitmq.port}") int port) { return new RouteBuilder() { @@ -45,13 +45,6 @@ public class BookRouteConfiguration { // scenario 1 - from bean to output from("direct:start").unmarshal().json(JsonLibrary.Jackson, BookReturned.class).bean(bookService) .marshal().json(JsonLibrary.Jackson, BookReturned.class).to("rabbitmq:output?queue=output"); - // scenario 2 - from input to output - from("rabbitmq:input?queue=input").unmarshal().json(JsonLibrary.Jackson, BookReturned.class) - .bean(bookService).marshal().json(JsonLibrary.Jackson, BookReturned.class) - .to("rabbitmq:output"); - // scenario 3 - from input to no output - from("rabbitmq:delete?queue=delete").unmarshal().json(JsonLibrary.Jackson, BookDeleted.class) - .bean(bookDeleter); } }; diff --git a/tests/samples-messaging-camel/src/main/java/com/example/BookService.java b/tests/samples-messaging-camel/src/main/java/com/example/BookService.java index eac6f57739..13fb7423ed 100644 --- a/tests/samples-messaging-camel/src/main/java/com/example/BookService.java +++ b/tests/samples-messaging-camel/src/main/java/com/example/BookService.java @@ -29,9 +29,7 @@ public class BookService { /** * Scenario for "should generate tests triggered by a method": client side: must have - * a possibility to "trigger" sending of a message to the given messageFrom server - * side: will run the method and await upon receiving message on the output - * messageFrom. Method triggers sending a message to a source. + * a possibility to "trigger" sending of a message to the given message * @param exchange - input exchange. */ public void returnBook(Exchange exchange) { diff --git a/tests/samples-messaging-camel/src/test/groovy/com/example/CamelMessagingApplicationSpec.groovy b/tests/samples-messaging-camel/src/test/groovy/com/example/CamelMessagingApplicationSpec.groovy index a5d3a52b78..8a4d5aaead 100644 --- a/tests/samples-messaging-camel/src/test/groovy/com/example/CamelMessagingApplicationSpec.groovy +++ b/tests/samples-messaging-camel/src/test/groovy/com/example/CamelMessagingApplicationSpec.groovy @@ -23,7 +23,6 @@ import com.jayway.jsonpath.JsonPath import com.toomuchcoding.jsonassert.JsonAssertion import org.apache.camel.Message import org.apache.camel.model.ModelCamelContext -import org.awaitility.Awaitility import org.junit.jupiter.api.Test import org.testcontainers.containers.RabbitMQContainer import org.testcontainers.junit.jupiter.Container @@ -49,8 +48,6 @@ class CamelMessagingApplicationSpec { // ALL CASES @Autowired ModelCamelContext camelContext - @Autowired - BookDeleter bookDeleter @Inject MessageVerifier contractVerifierMessaging @@ -67,24 +64,26 @@ class CamelMessagingApplicationSpec { @Test void "should work for triggered based messaging"() { given: + // tag::sample_dsl[] Contract.make { - label 'some_label' + label 'return_book_1' input { triggeredBy('bookReturnedTriggered()') } outputMessage { - sentTo('rabbitmq:output') + sentTo('rabbitmq:output?queue=output') body('''{ "bookName" : "foo" }''') headers { header('BOOK-NAME', 'foo') } } } + // end::sample_dsl[] // generated test should look like this: when: bookReturnedTriggered() then: - def response = contractVerifierMessaging.receive('rabbitmq:output') + def response = contractVerifierMessaging.receive('rabbitmq:output?queue=output') assert response.headers.get('BOOK-NAME') == 'foo' and: DocumentContext parsedJson = JsonPath. @@ -92,78 +91,9 @@ class CamelMessagingApplicationSpec { JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') } - @Test - void "should generate tests triggered by a message"() { - given: - Contract.make { - label 'some_label' - input { - messageFrom('rabbitmq:input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - - } - } - outputMessage { - sentTo('rabbitmq:output') - body([ - bookName: 'foo' - ]) - headers { - header('BOOK-NAME', 'foo') - } - } - } - // generated test should look like this: - when: - contractVerifierMessaging.send( - contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']), - [sample: 'header'], 'rabbitmq:input') - then: - def response = contractVerifierMessaging.receive('rabbitmq:output') - assert response.headers.get('BOOK-NAME') == 'foo' - and: - DocumentContext parsedJson = JsonPath. - parse(contractVerifierObjectMapper.writeValueAsString(response.body)) - JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') - } - - @Test - void "should generate tests without destination, triggered by a message"() { - given: - Contract.make { - label 'some_label' - input { - messageFrom('rabbitmq:delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } - } - // generated test should look like this: - when: - contractVerifierMessaging. - send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']), - [sample: 'header'], 'rabbitmq:delete') - then: - bookWasDeleted() - } - void bookReturnedTriggered() { camelContext.createProducerTemplate(). sendBody('direct:start', '''{"bookName" : "foo" }''') } - void bookWasDeleted() { - Awaitility.await().untilAsserted(() -> { - assert bookDeleter.bookSuccessfulyDeleted.get() - }) - } } diff --git a/tests/samples-messaging-integration/src/main/java/com/example/BookListener.java b/tests/samples-messaging-integration/src/main/java/com/example/BookListener.java index ebf78b0de7..3119db8590 100644 --- a/tests/samples-messaging-integration/src/main/java/com/example/BookListener.java +++ b/tests/samples-messaging-integration/src/main/java/com/example/BookListener.java @@ -16,8 +16,6 @@ package com.example; -import java.util.concurrent.atomic.AtomicBoolean; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +26,6 @@ public class BookListener { private static final Logger log = LoggerFactory.getLogger(BookListener.class); - public AtomicBoolean bookSuccessfullyDeleted = new AtomicBoolean(false); - /** * Scenario for "should generate tests triggered by a message": client side: if sends * a message to input.messageFrom then message will be sent to output.messageFrom @@ -43,16 +39,4 @@ public class BookListener { return MessageBuilder.withPayload(bookReturned).setHeader("BOOK-NAME", bookReturned.bookName).build(); } - /** - * Scenario for "should generate tests triggered by a message": client side: if sends - * a message to input.messageFrom then message will be sent to output.messageFrom - * server side: will send a message to input, verify the message contents and await - * upon receiving message on the output messageFrom. - * @param bookDeleted - payload - */ - public void bookDeleted(BookDeleted bookDeleted) { - log.info("Deleting book [ " + bookDeleted + "]"); - this.bookSuccessfullyDeleted.set(true); - } - } diff --git a/tests/samples-messaging-integration/src/main/resources/integration-context.xml b/tests/samples-messaging-integration/src/main/resources/integration-context.xml index 6bd1c8b78c..87116fb2b8 100644 --- a/tests/samples-messaging-integration/src/main/resources/integration-context.xml +++ b/tests/samples-messaging-integration/src/main/resources/integration-context.xml @@ -23,25 +23,8 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd"> - - - - - - - - - - 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 2b122584c2..ded75ceae3 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 @@ -81,79 +81,6 @@ class IntegrationMessagingApplicationSpec { JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') } - void "should generate tests triggered by a message"() { - given: - // tag::message_trigger[] - def dsl = Contract.make { - description 'Some Description' - label 'some_label' - // input is a message - input { - // the message was received from this destination - messageFrom('input') - // has the following body - messageBody([ - bookName: 'foo' - ]) - // and the following headers - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('output') - body([ - bookName: 'foo' - ]) - headers { - header('BOOK-NAME', 'foo') - } - } - } - // end::message_trigger[] - - // generated test should look like this: - - when: - contractVerifierMessaging.send( - contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']), - [sample: 'header'], 'input') - then: - def response = contractVerifierMessaging.receive('output') - response.headers.get('BOOK-NAME') == 'foo' - and: - DocumentContext parsedJson = JsonPath. - parse(contractVerifierObjectMapper.writeValueAsString(response.payload)) - JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') - } - - @Test - void "should generate tests without destination, triggered by a message"() { - given: - def dsl = Contract.make { - label 'some_label' - input { - messageFrom('delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } - } - - // generated test should look like this: - - when: - contractVerifierMessaging. - send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']), - [sample: 'header'], 'delete') - then: - bookWasDeleted() - } - // BASE CLASS WOULD HAVE THIS: @Autowired @@ -165,8 +92,4 @@ class IntegrationMessagingApplicationSpec { bookService.returnBook(new BookReturned("foo")) } - void bookWasDeleted() { - assert bookListener.bookSuccessfullyDeleted.get() - } - } diff --git a/tests/samples-messaging-jms/src/main/java/com/example/BookDeleter.java b/tests/samples-messaging-jms/src/main/java/com/example/BookDeleter.java deleted file mode 100644 index 875a1664c0..0000000000 --- a/tests/samples-messaging-jms/src/main/java/com/example/BookDeleter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.concurrent.atomic.AtomicBoolean; - -import jakarta.jms.JMSException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.jms.annotation.JmsListener; -import org.springframework.messaging.Message; -import org.springframework.stereotype.Component; - -@Component -public class BookDeleter { - - private static final Logger log = LoggerFactory.getLogger(BookDeleter.class); - - private AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false); - - /** - * Scenario for "should generate tests triggered by a message": client side: if sends - * a message to input.messageFrom then message will be sent to output.messageFrom - * server side: will send a message to input, verify the message contents and await - * upon receiving message on the output messageFrom. - * @param message - input message. - * @throws JMSException - jms exception. - */ - @JmsListener(destination = "delete") - public void bookDeleted(Message message) throws JMSException { - log.info("Deleting book " + message); - this.bookSuccessfulyDeleted.set(true); - log.info("Book successfully deleted [" + this.bookSuccessfulyDeleted + "]"); - } - -} diff --git a/tests/samples-messaging-jms/src/test/groovy/com/example/JmsMessagingApplicationSpec.groovy b/tests/samples-messaging-jms/src/test/groovy/com/example/JmsMessagingApplicationSpec.groovy index 61722dcdde..1cc460b992 100644 --- a/tests/samples-messaging-jms/src/test/groovy/com/example/JmsMessagingApplicationSpec.groovy +++ b/tests/samples-messaging-jms/src/test/groovy/com/example/JmsMessagingApplicationSpec.groovy @@ -50,8 +50,6 @@ class JmsMessagingApplicationSpec { // ALL CASES @Autowired JmsTemplate jmsTemplate - @Autowired - BookDeleter bookDeleter @Inject MessageVerifier messageVerifier @Inject @@ -92,70 +90,6 @@ class JmsMessagingApplicationSpec { JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') } - @DirtiesContext - @Test - void "should generate tests triggered by a message"() { - given: - Contract.make { - label 'some_label' - input { - messageFrom('input2') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('output2') - body([ - bookName: 'foo' - ]) - headers { - header('BOOKNAME', 'foo') - } - } - } - // generated test should look like this: - when: - messageVerifier.send( - contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']), - [sample: 'header'], 'input2') - then: - ContractVerifierMessage response = contractVerifierMessaging.receive('output2') - assert response.getHeader('BOOKNAME') == 'foo' - and: - DocumentContext parsedJson = JsonPath. - parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload())) - JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') - } - - @Test - void "should generate tests without destination, triggered by a message"() { - given: - Contract.make { - label 'some_label' - input { - messageFrom('delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } - } - // generated test should look like this: - when: - messageVerifier. - send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']), - [sample: 'header'], 'delete') - then: - bookWasDeleted() - } - void bookReturnedTriggered() { jmsTemplate.convertAndSend("output", '''{"bookName" : "foo" }''', new MessagePostProcessor() { @Override @@ -166,10 +100,4 @@ class JmsMessagingApplicationSpec { }) } - void bookWasDeleted() { - Awaitility.await().untilAsserted( () -> { - assert bookDeleter.bookSuccessfulyDeleted.get() - }) - } - } diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar index c57c836db4..7916329c40 100644 Binary files a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar and b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar differ diff --git a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar index c57c836db4..7916329c40 100644 Binary files a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar and b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar differ diff --git a/tests/spring-cloud-contract-stub-runner-camel/pom.xml b/tests/spring-cloud-contract-stub-runner-camel/pom.xml deleted file mode 100644 index 7fa4ef2fda..0000000000 --- a/tests/spring-cloud-contract-stub-runner-camel/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - 4.0.0 - - org.springframework.cloud - spring-cloud-contract-tests - 4.0.0-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 - - - org.springframework.cloud - spring-cloud-starter-contract-stub-runner-jetty - test - - - org.apache.camel.springboot - camel-spring-boot-starter - - - org.apache.camel - camel-jackson - - - org.spockframework - spock-core - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.apache.camel - camel-activemq - test - - - org.springframework.boot - spring-boot-starter-web - test - - - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - org.apache.maven.plugins - maven-surefire-plugin - - - - 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 deleted file mode 100644 index 615dcef37f..0000000000 --- a/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/BookReturned.groovy +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.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/StubRunnerCamelProcessorSpec.groovy b/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessorSpec.groovy deleted file mode 100644 index 592cc87937..0000000000 --- a/tests/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelProcessorSpec.groovy +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.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 spock.lang.Specification - -import org.springframework.cloud.contract.spec.Contract - -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() - when: - message.in.body = new StubRunnerCamelPayload(noOutputMessageContract) - 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() - when: - message.in.body = new StubRunnerCamelPayload(dsl) - 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() - when: - message.in.body = new StubRunnerCamelPayload(dslWithRegexInGString) - 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 deleted file mode 100644 index 9bc048000e..0000000000 --- a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/application.yml +++ /dev/null @@ -1,8 +0,0 @@ -stubrunner: - repository-root: classpath:m2repo/repository/ - ids: org.springframework.cloud.contract.verifier.stubs:camelService:0.0.1-SNAPSHOT:stubs - stubs-mode: remote -server: - port: 0 -debug: true -logging.level.org.springframework.cloud.contract: debug 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 deleted file mode 100644 index b27b155a3e..0000000000 Binary files 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 and /dev/null differ 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.pom 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.pom deleted file mode 100644 index 63a6b8546f..0000000000 --- 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.pom +++ /dev/null @@ -1,27 +0,0 @@ - - - - - 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 deleted file mode 100644 index 5d0f8bc292..0000000000 --- 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 +++ /dev/null @@ -1,28 +0,0 @@ - - - - - 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 deleted file mode 100644 index 65af363165..0000000000 --- a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata-local.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - 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 deleted file mode 100644 index 65af363165..0000000000 --- a/tests/spring-cloud-contract-stub-runner-camel/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/camelService/maven-metadata.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - 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/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 index fc62945868..a5efcca2fd 100644 --- 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 @@ -57,24 +57,6 @@ class IntegrationStubRunnerSpec { messaging.receive('outputTest', 100, TimeUnit.MILLISECONDS) } - @Test - void '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[] - assert receivedMessage != null - assert assertJsons(receivedMessage.payload) - assert receivedMessage.headers.get('BOOK-NAME') == 'foo' - // end::client_receive_message[] - } - @Test void 'should trigger a message by label'() { when: @@ -148,26 +130,6 @@ class IntegrationStubRunnerSpec { assert receivedMessage.headers.get('BOOK-NAME') == 'foo' } - @Test - void '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[] - } - - @Test - void '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: - assert receivedMessage == null - } - private boolean assertJsons(Object payload) { String objectAsString = payload instanceof String ? payload : JsonOutput.toJson(payload) @@ -192,48 +154,6 @@ class IntegrationStubRunnerSpec { } // 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[] - @Configuration @ComponentScan @EnableAutoConfiguration 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 deleted file mode 100644 index 04d261b64b..0000000000 --- a/tests/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationTransformerSpec.groovy +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.integration - -import spock.lang.Specification - -import org.springframework.cloud.contract.spec.Contract -import org.springframework.messaging.Message -import org.springframework.messaging.support.MessageBuilder - -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) { - @Override - Contract matchingContract(Message source) { - return 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) { - @Override - Contract matchingContract(Message source) { - return 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) { - @Override - Contract matchingContract(Message source) { - return 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/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 index 9cb1d97128..88086ec066 100644 Binary files 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 and 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 differ diff --git a/tests/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy b/tests/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy index 633fb44ec4..e4356ba3e0 100644 --- a/tests/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy +++ b/tests/spring-cloud-contract-stub-runner-jms/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/jms/JmsStubRunnerSpec.groovy @@ -58,31 +58,6 @@ class JmsStubRunnerSpec { jmsTemplate.receive('input') } - @Test - void 'should download the stub and register a route for it'() { - when: - // tag::client_send[] - jmsTemplate. - convertAndSend('input', new BookReturned('foo'), new MessagePostProcessor() { - @Override - Message postProcessMessage(Message message) throws JMSException { - message.setStringProperty("sample", "header") - return message - } - }) - // end::client_send[] - then: - // tag::client_receive[] - TextMessage receivedMessage = (TextMessage) jmsTemplate.receive('output') - // end::client_receive[] - and: - // tag::client_receive_message[] - assert receivedMessage != null - assert assertThatBodyContainsBookNameFoo(receivedMessage.getText()) - assert receivedMessage.getStringProperty('BOOKNAME') == 'foo' - // end::client_receive_message[] - } - @Test void 'should trigger a message by label'() { when: @@ -102,7 +77,7 @@ class JmsStubRunnerSpec { } @Test - void 'should trigger a label for the existing groupId:artifactId'() { + void 'should trigger a label for the existing groupId and artifactId'() { when: // tag::trigger_group_artifact[] stubFinder. @@ -219,46 +194,4 @@ class JmsStubRunnerSpec { } } // 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('BOOKNAME', '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-jms/src/test/resources/stubs/bookDeleted.groovy b/tests/spring-cloud-contract-stub-runner-jms/src/test/resources/stubs/bookDeleted.groovy deleted file mode 100644 index b013bba223..0000000000 --- a/tests/spring-cloud-contract-stub-runner-jms/src/test/resources/stubs/bookDeleted.groovy +++ /dev/null @@ -1,13 +0,0 @@ -org.springframework.cloud.contract.spec.Contract.make { - label 'delete_book' - input { - messageFrom('delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } -} \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-jms/src/test/resources/stubs/bookReturned2.groovy b/tests/spring-cloud-contract-stub-runner-jms/src/test/resources/stubs/bookReturned2.groovy deleted file mode 100644 index 0f568e0d8d..0000000000 --- a/tests/spring-cloud-contract-stub-runner-jms/src/test/resources/stubs/bookReturned2.groovy +++ /dev/null @@ -1,21 +0,0 @@ -org.springframework.cloud.contract.spec.Contract.make { - label 'return_book_2' - input { - messageFrom('input') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - } - outputMessage { - sentTo('output') - body([ - bookName: 'foo' - ]) - headers { - header('BOOKNAME', 'foo') - } - } -} diff --git a/tests/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy b/tests/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy index 2160bb0d83..14711ee956 100644 --- a/tests/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy +++ b/tests/spring-cloud-contract-stub-runner-kafka/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/kafka/KafkaStubRunnerSpec.groovy @@ -302,45 +302,4 @@ class KafkaStubRunnerSpec { } // 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-kafka/src/test/resources/stubs/bookDeleted.groovy b/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookDeleted.groovy deleted file mode 100644 index b013bba223..0000000000 --- a/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookDeleted.groovy +++ /dev/null @@ -1,13 +0,0 @@ -org.springframework.cloud.contract.spec.Contract.make { - label 'delete_book' - input { - messageFrom('delete') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { - header('sample', 'header') - } - assertThat('bookWasDeleted()') - } -} \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookReturned2.groovy b/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookReturned2.groovy deleted file mode 100644 index e0d53098d5..0000000000 --- a/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookReturned2.groovy +++ /dev/null @@ -1,21 +0,0 @@ -org.springframework.cloud.contract.spec.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') - } - } -} \ No newline at end of file diff --git a/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookReturned3.groovy b/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookReturned3.groovy deleted file mode 100644 index 1b27bd1a34..0000000000 --- a/tests/spring-cloud-contract-stub-runner-kafka/src/test/resources/stubs/bookReturned3.groovy +++ /dev/null @@ -1,22 +0,0 @@ -org.springframework.cloud.contract.spec.Contract.make { - label 'return_book_3' - input { - messageFrom('input2') - messageBody([ - bookName: 'bar' - ]) - messageHeaders { - header('kafka_receivedMessageKey', 'bar5150') - } - } - outputMessage { - sentTo('output') - body([ - bookName: 'bar' - ]) - headers { - header('BOOK-NAME', 'bar') - header('kafka_messageKey', 'bar5150') - } - } -} \ No newline at end of file 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 7129880e5b..71beec1f73 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 @@ -16,17 +16,13 @@ package org.springframework.cloud.contract.stubrunner.messaging.stream -import java.util.concurrent.TimeUnit -import java.util.function.Consumer + import java.util.function.Function -import java.util.function.Supplier import groovy.json.JsonOutput import groovy.json.JsonSlurper import org.assertj.core.api.BDDAssertions import org.awaitility.Awaitility -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -59,26 +55,6 @@ class StreamStubRunnerSpec { @Autowired MessageVerifier> messaging - @Test - void '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: - Awaitility.await().untilAsserted(() -> { - // tag::client_receive[] - Message receivedMessage = messaging.receive('returnBook') - // end::client_receive[] - and: - // tag::client_receive_message[] - assert receivedMessage != null - assertJsons(receivedMessage.payload) - assert receivedMessage.headers.get('BOOK-NAME') == 'foo' - // end::client_receive_message[] - }) - } - @Test void 'should trigger a message by label'() { when: @@ -88,7 +64,7 @@ class StreamStubRunnerSpec { then: Awaitility.await().untilAsserted(() -> { // tag::client_trigger_receive[] - Message receivedMessage = messaging.receive('returnBook') + Message receivedMessage = messaging.receive('outputToAssertBook') // end::client_trigger_receive[] and: // tag::client_trigger_message[] @@ -106,7 +82,7 @@ class StreamStubRunnerSpec { 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('outputToAssertBook') and: assert receivedMessage != null assertJsons(receivedMessage.payload) @@ -120,7 +96,7 @@ class StreamStubRunnerSpec { stubFinder.trigger('streamService', 'return_book_1') // end::trigger_artifact[] then: - Message receivedMessage = messaging.receive('returnBook') + Message receivedMessage = messaging.receive('outputToAssertBook') and: assert receivedMessage != null assertJsons(receivedMessage.payload) @@ -146,31 +122,13 @@ class StreamStubRunnerSpec { stubFinder.trigger() // end::trigger_all[] then: - Message receivedMessage = messaging.receive('returnBook') + Message receivedMessage = messaging.receive('outputToAssertBook') and: assert receivedMessage != null assertJsons(receivedMessage.payload) assert receivedMessage.headers.get('BOOK-NAME') == 'foo' } - @Test - void '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[] - } - - @Test - void '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: - assert receivedMessage == null - } - private boolean assertJsons(Object payload) { String objectAsString = payload instanceof String ? payload : payload instanceof byte[] ? new String(payload) @@ -179,6 +137,7 @@ class StreamStubRunnerSpec { return json.bookName == 'foo' } + // Contract from the other service that is a producer (I'm a consumer) Contract dsl = // tag::sample_dsl[] Contract.make { @@ -192,44 +151,22 @@ class StreamStubRunnerSpec { } // end::sample_dsl[] - Contract dsl2 = - // tag::sample_dsl_2[] + // Contract from my service that is processing the input message and sending out another message (I'm a producer) + Contract myDsl = + // tag::sample_dsl[] Contract.make { label 'return_book_2' - input { - messageFrom('bookStorage') - messageBody([ - bookName: 'foo' - ]) - messageHeaders { header('sample', 'header') } - } + input { triggeredBy('gotAMessageFromFunction()') } outputMessage { - sentTo('returnBook') - body([ - bookName: 'foo' - ]) + sentTo('outputToAssertBook') + 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[] + // tag::setup[] @ImportAutoConfiguration(TestChannelBinderConfiguration.class) - @Configuration + @Configuration(proxyBeanMethods = true) @EnableAutoConfiguration protected static class Config { @@ -241,19 +178,7 @@ class StreamStubRunnerSpec { } } - @Bean - Consumer test2() { - return (input) -> { - println "Test 2 [${input}]" - } - } - - @Bean - Consumer test3() { - return (input) -> { - println "Test 3 [${input}]" - } - } } + // end::setup[] } 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 deleted file mode 100644 index d9ede88342..0000000000 --- a/tests/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamTransformerSpec.groovy +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2013-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.messaging.stream - -import org.junit.jupiter.api.Test - -import org.springframework.cloud.contract.spec.Contract -import org.springframework.messaging.Message -import org.springframework.messaging.support.MessageBuilder - -class StubRunnerStreamTransformerSpec { - - 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') - } - } - } - - @Test - void 'should not transform the message if there is no output message'() { - given: - StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(noOutputMessageContract) - when: - def result = streamTransformer.transform(message) - then: - assert 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') - } - } - } - - @Test - void 'should convert dsl into message'() { - given: - StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dsl) { - @Override - Contract matchingContract(Message source) { - return dsl - } - } - when: - def result = streamTransformer.transform(message) - then: - assert result.payload == '{"responseId":"123"}'.bytes - } - - 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"}""") - } - } - - @Test - void 'should convert dsl into message with regex in GString'() { - given: - StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dslWithRegexInGString) { - @Override - Contract matchingContract(Message source) { - return dslWithRegexInGString - } - } - when: - def result = streamTransformer.transform(message) - then: - assert result.payload == '''{"id":"99","temperature":"123.45"}'''.bytes - } - - @Test - void '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) { - @Override - Contract matchingContract(Message source) { - return contract - } - } - when: - def result = streamTransformer.transform(message) - then: - assert result.payload == '''{"orderId":"40058c70-891c-4176-a033-f70bad0c5f77","description":"This is the order description"}'''.bytes - } - - @Test - void 'should work for binary payloads from file'() { - given: - Contract contract = Contract.make { - label 'send_order' - input { - triggeredBy('orderTrigger()') - } - outputMessage { - sentTo('orders') - headers { - messagingContentType(applicationOctetStream()) - } - body(fileAsBytes("response.pdf")) - } - } - StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(contract) { - @Override - Contract matchingContract(Message source) { - return contract - } - } - when: - def result = streamTransformer.transform(message) - then: - assert result.payload == StubRunnerStreamTransformerSpec.getResource("/response.pdf").bytes - } - -} 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 index 1bed3e6cf0..85438a4ff7 100644 --- 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 @@ -5,16 +5,12 @@ spring: cloud: stream: bindings: - test1-out-0: - destination: returnBook test1-in-0: - destination: bookStorage - test2-in-0: - destination: delete - test3-in-0: destination: returnBook + test1-out-0: + destination: outputToAssertBook function: - definition: test1;test2;test3 + definition: test1 server: port: 0 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 index b3daab26c4..498ec1e112 100644 Binary files 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 and 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 differ