diff --git a/README.adoc b/README.adoc
index 075d26c24f..92bf0a059d 100644
--- a/README.adoc
+++ b/README.adoc
@@ -666,7 +666,7 @@ package com.example.fraud;
import org.junit.Before;
-import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
+import io.restassured.module.mockmvc.RestAssuredMockMvc;
public class FraudBase {
@Before
@@ -1109,15 +1109,15 @@ Example of a `pom.xml` inside the `server` folder.
org.springframework.boot
spring-boot-starter-parent
- 1.5.4.BUILD-SNAPSHOT
+ 1.5.4.RELEASE
UTF-8
1.8
- 1.1.2.BUILD-SNAPSHOT
- Dalston.BUILD-SNAPSHOT
+ 1.2.0.BUILD-SNAPSHOT
+ Edgware.BUILD-SNAPSHOT
true
diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy
index 2c7dec04ba..5681566294 100644
--- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy
+++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy
@@ -171,10 +171,10 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
request = "${request}\n\t\t\t\t\"${StringEscapeUtils.escapeJava(bodyAsString)}\"\n"
}
if (inputMessage.messageHeaders) {
- request = "${request}\t\t\t\t, headers()\n"
+ request = "${request}\t\t\t\t, headers()"
}
inputMessage.messageHeaders?.executeForEachHeader { Header header ->
- request = "${request}\t\t\t\t\t\t${getHeaderString(header)}"
+ request = "${request}\n\t\t\t\t\t\t${getHeaderString(header)}"
}
return "${request}\n\t\t\t)"
}
diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java
index 06e072aa0c..32a0599cee 100644
--- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java
+++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessages.java
@@ -16,13 +16,6 @@
package org.springframework.cloud.contract.verifier.messaging.amqp;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mockingDetails;
-import static org.mockito.Mockito.verify;
-import static org.springframework.amqp.support.converter.DefaultClassMapper.DEFAULT_CLASSID_FIELD_NAME;
-
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -40,6 +33,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
import org.springframework.util.Assert;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.Mockito.verify;
+import static org.springframework.amqp.support.converter.DefaultClassMapper.DEFAULT_CLASSID_FIELD_NAME;
+
/**
* {@link MessageVerifier} implementation to integrate with plain spring-amqp/spring-rabbit.
* It is meant to be used without interacting with a running bus.
@@ -99,7 +100,7 @@ public class SpringAmqpStubMessages implements
@Override
public Message receive(String destination, long timeout, TimeUnit timeUnit) {
ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(this.rabbitTemplate).send(eq(destination), anyString(), messageCaptor.capture(), any(CorrelationData.class));
+ verify(this.rabbitTemplate, atLeastOnce()).send(eq(destination), anyString(), messageCaptor.capture(), any(CorrelationData.class));
if (messageCaptor.getAllValues().isEmpty()) {
log.info("no messages found on destination {}", destination);
diff --git a/tests/samples-messaging-amqp/src/main/java/com/example/RabbitManager.java b/tests/samples-messaging-amqp/src/main/java/com/example/RabbitManager.java
new file mode 100644
index 0000000000..eddd966995
--- /dev/null
+++ b/tests/samples-messaging-amqp/src/main/java/com/example/RabbitManager.java
@@ -0,0 +1,116 @@
+package com.example;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.amqp.rabbit.annotation.Exchange;
+import org.springframework.amqp.rabbit.annotation.Queue;
+import org.springframework.amqp.rabbit.annotation.QueueBinding;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.handler.annotation.Headers;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RabbitManager {
+
+ public static final Logger LOG = LoggerFactory.getLogger(RabbitManager.class);
+
+
+ private BookService service;
+ private RabbitTemplate rabbitTemplate;
+
+ @Autowired
+ public RabbitManager(BookService service, RabbitTemplate rabbitTemplate) {
+ this.service = service;
+ this.rabbitTemplate = rabbitTemplate;
+ }
+
+ @RabbitListener(bindings = @QueueBinding(
+ value = @Queue(),
+ exchange = @Exchange(
+ value = "input",
+ durable="true",
+ autoDelete="false",
+ type="topic"),
+ key = "event"
+ ))
+ public void newBook(Book book, @Headers Map headers) {
+ LOG.info("Received new book with bookname = " + book.getName());
+ LOG.info("Headers = " + headers);
+ service.sendBook(book, headers.get("amqp_replyTo"));
+ }
+
+ @RabbitListener(bindings = @QueueBinding(
+ value = @Queue(),
+ exchange = @Exchange(
+ value = "input",
+ durable="true",
+ autoDelete="false",
+ type="topic"),
+ key = "event2"
+ ))
+ public void newBook2(Book book, @Headers Map headers) {
+ LOG.info("newBook2 Received new book with bookname = " + book.getName());
+ LOG.info("newBook2 Headers = " + headers);
+ service.sendBook(book, headers.get("amqp_replyTo"));
+ }
+}
+
+interface BookService {
+ void sendBook(Book book, String replyTo);
+
+ void newBook(Book book);
+
+ Book getBook(int index);
+
+ int noOfBooks();
+
+ List getBooks();
+
+}
+
+@Component
+class BookServiceImpl implements BookService {
+ public static final Logger LOG = LoggerFactory.getLogger(BookServiceImpl.class);
+
+ private List books;
+ private RabbitTemplate rabbitTemplate;
+
+ @Autowired
+ public BookServiceImpl(RabbitTemplate rabbitTemplate) {
+ books = new LinkedList<>();
+ this.rabbitTemplate = rabbitTemplate;
+ }
+
+ @Override
+ public void sendBook(Book book, String replyTo) {
+ LOG.info("Received new book with bookname = " + book.getName());
+ newBook(book);
+ rabbitTemplate.convertAndSend("", replyTo, book);
+ }
+
+ @Override
+ public void newBook(Book book) {
+ books.add(book);
+ }
+
+ @Override
+ public Book getBook(int index) {
+ return books.get(index);
+ }
+
+ @Override
+ public int noOfBooks() {
+ return books.size();
+ }
+
+ @Override
+ public List getBooks() {
+ return books;
+ }
+}
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 f0bf19e7ee..15b41d427b 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
@@ -25,6 +25,7 @@ import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
+import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
import org.springframework.test.context.ContextConfiguration
@@ -33,6 +34,9 @@ import spock.lang.Specification
import javax.inject.Inject
+import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson
+import static org.assertj.core.api.Assertions.assertThat
+import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers
// Context configuration would end up in base class
@ContextConfiguration(classes = [AmqpMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureMessageVerifier
@@ -79,6 +83,69 @@ class AmqpMessagingApplicationSpec extends Specification {
JsonAssertion.assertThat(parsedJson).field('name').isEqualTo('some')
}
+ @Issue("332")
+ def "should work for second scenario"() {
+ given:
+ def dsl =
+ Contract.make {
+ description("""
+Represents scenario 2 from documentation:
+http://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")
def "should work for input/output when bytes are used"() {
given: