From 2f19a33bc62d0d77affeafa320c0f80c4c7a0861 Mon Sep 17 00:00:00 2001 From: Mikhail Gordienko Date: Wed, 1 Sep 2021 16:59:35 +0300 Subject: [PATCH 1/2] fix #1705: AMQP stub runner NullPointerException (#1708) fixes #1705 --- .../amqp/SpringAmqpStubMessages.java | 43 ++++++++------- .../amqp/SpringAmqpStubMessagesTests.java | 55 +++++++++++++++++++ 2 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 spring-cloud-contract-verifier/src/test/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesTests.java 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 35a9ee96a2..96cbdeffd9 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 @@ -41,6 +41,7 @@ import org.springframework.cloud.contract.verifier.converter.YamlContract; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessageMetadata; import org.springframework.cloud.contract.verifier.util.MetadataUtil; +import org.springframework.messaging.MessageHeaders; import org.springframework.util.Assert; import static org.mockito.Matchers.eq; @@ -85,21 +86,38 @@ public class SpringAmqpStubMessages implements MessageVerifier { } @Override - public void send(T payload, Map headers, String destination, YamlContract contract) { + public void send(T payload, Map messageHeaders, String destination, YamlContract contract) { + final MessageHeaders headers = new MessageHeaders(messageHeaders); Message message = org.springframework.amqp.core.MessageBuilder.withBody(((String) payload).getBytes()) - .andProperties(MessagePropertiesBuilder.newInstance().setContentType(header(headers, "contentType")) - .copyHeaders(headers).build()) + .andProperties(MessagePropertiesBuilder.newInstance() + .setContentType(header(headers, MessageHeaders.CONTENT_TYPE)).copyHeaders(headers).build()) .build(); - if (headers != null && headers.containsKey(DEFAULT_CLASSID_FIELD_NAME)) { + if (headers.containsKey(DEFAULT_CLASSID_FIELD_NAME)) { message.getMessageProperties().setHeader(DEFAULT_CLASSID_FIELD_NAME, headers.get(DEFAULT_CLASSID_FIELD_NAME)); } - if (headers != null && headers.containsKey(AmqpHeaders.RECEIVED_ROUTING_KEY)) { + if (headers.containsKey(AmqpHeaders.RECEIVED_ROUTING_KEY)) { message.getMessageProperties().setReceivedRoutingKey(header(headers, AmqpHeaders.RECEIVED_ROUTING_KEY)); } send(message, destination, contract); } + private String header(MessageHeaders headers, String headerName) { + Object value = headers.get(headerName); + + if (value == null) { + return ""; + } + else if (value instanceof String) { + return (String) value; + } + else if (value instanceof Iterable) { + Iterable values = ((Iterable) value); + return values.iterator().hasNext() ? (String) values.iterator().next() : ""; + } + return value.toString(); + } + public void mergeMessagePropertiesFromMetadata(YamlContract contract, Message message) { if (contract != null && contract.metadata.containsKey(AmqpMetadata.METADATA_KEY)) { AmqpMetadata amqpMetadata = AmqpMetadata.fromMetadata(contract.metadata); @@ -116,21 +134,6 @@ public class SpringAmqpStubMessages implements MessageVerifier { return messageMetadata.getMessageType() == ContractVerifierMessageMetadata.MessageType.INPUT; } - private String header(Map headers, String headerName) { - if (headers == null) { - return ""; - } - Object value = headers.get(headerName); - if (value instanceof String) { - return (String) value; - } - else if (value instanceof Iterable) { - Iterable values = ((Iterable) value); - return values.iterator().hasNext() ? (String) values.iterator().next() : ""; - } - return value.toString(); - } - @Override public void send(Message message, String destination, YamlContract contract) { mergeMessagePropertiesFromMetadata(contract, message); diff --git a/spring-cloud-contract-verifier/src/test/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesTests.java b/spring-cloud-contract-verifier/src/test/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesTests.java new file mode 100644 index 0000000000..444f81a2e1 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/java/org/springframework/cloud/contract/verifier/messaging/amqp/SpringAmqpStubMessagesTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021-2021 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.amqp; + +import java.util.Collections; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.amqp.core.MessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.boot.autoconfigure.amqp.RabbitProperties; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SpringAmqpStubMessagesTests { + + @Test + void should_send_message_without_headers_and_contract() { + final RabbitTemplate rabbitTemplate = mock(RabbitTemplate.class); + final MessageListenerAccessor messageListenerAccessor = mock(MessageListenerAccessor.class); + final RabbitProperties rabbitProperties = mock(RabbitProperties.class); + final SimpleMessageListenerContainer messageListenerContainer = mock(SimpleMessageListenerContainer.class); + final MessageListener messageListener = mock(MessageListener.class); + + when(messageListenerContainer.getMessageListener()).thenReturn(messageListener); + when(messageListenerAccessor.getListenerContainersForDestination(any(), any())) + .thenReturn(Collections.singletonList(messageListenerContainer)); + + final SpringAmqpStubMessages springAmqpStubMessages = new SpringAmqpStubMessages(rabbitTemplate, + messageListenerAccessor, rabbitProperties); + + Assertions.assertThatCode(() -> springAmqpStubMessages.send(anyString(), null, anyString(), null)) + .doesNotThrowAnyException(); + } + +} From c24f531a2b25eeb1de1f02783e6c644e09e95908 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 1 Sep 2021 18:19:11 +0200 Subject: [PATCH 2/2] Fixed the regression from #1666 --- .../cloud/contract/spec/internal/Input.java | 4 - .../verifier/converter/YamlToContracts.java | 2 +- .../WireMockRequestStubStrategySpec.groovy | 76 +++++++++++++++++++ .../WireMockResponseStubStrategySpec.groovy | 10 +-- 4 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategySpec.groovy 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 92a2077f0c..b3dcc3dc05 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 @@ -294,8 +294,6 @@ public class Input extends Common implements RegexCreatingProperty consumer) { this.bodyMatchers = new BodyMatchers(); @@ -315,8 +313,6 @@ public class Input extends Common implements RegexCreatingProperty> ContentType.JSON + def subject = new WireMockRequestStubStrategy(contract, metadata) + def content = subject.buildClientRequestContent() + then: + content.getHeaders().get("Content-Type").getValuePattern().getValue() != "multipart/form-data;boundary=AaB03x" + } +} diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy index 1492e62527..3bad520dbf 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy @@ -16,16 +16,16 @@ package org.springframework.cloud.contract.verifier.dsl.wiremock -import java.util.function.Function - import groovy.json.JsonSlurper -import spock.lang.Issue -import spock.lang.Specification - import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.verifier.converter.YamlContractConverter import org.springframework.cloud.contract.verifier.file.SingleContractMetadata import org.springframework.cloud.contract.verifier.util.ContentType import org.springframework.cloud.contract.verifier.util.MapConverter +import spock.lang.Issue +import spock.lang.Specification + +import java.util.function.Function class WireMockResponseStubStrategySpec extends Specification {