Merge branch 'master' into 2.0.x

This commit is contained in:
Marcin Grzejszczak
2017-06-28 11:47:15 +02:00
4 changed files with 194 additions and 10 deletions

View File

@@ -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)"
}

View File

@@ -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.
@@ -100,7 +101,7 @@ public class SpringAmqpStubMessages implements
@Override
public Message receive(String destination, long timeout, TimeUnit timeUnit) {
ArgumentCaptor<Message> 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);

View File

@@ -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<String, String> 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<String, String> 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<Book> getBooks();
}
@Component
class BookServiceImpl implements BookService {
public static final Logger LOG = LoggerFactory.getLogger(BookServiceImpl.class);
private List<Book> 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<Book> getBooks() {
return books;
}
}

View File

@@ -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: