Merge branch 'master' into 2.0.x
This commit is contained in:
@@ -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)"
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user