GH-2304 Remove old test support binder

Resolves #2304
This commit is contained in:
Oleg Zhurakousky
2022-03-16 10:30:10 +01:00
parent 840933d164
commit c857339e46
13 changed files with 0 additions and 819 deletions

View File

@@ -17,7 +17,6 @@
<module>spring-cloud-stream</module>
<module>spring-cloud-stream-binder-test</module>
<module>spring-cloud-stream-test-support</module>
<module>spring-cloud-stream-test-support-internal</module>
<module>spring-cloud-stream-integration-tests</module>
<module>docs</module>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-stream-test-support</artifactId>
<description>A set of classes to ease testing of Spring Cloud Stream modules.
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2015-2019 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.stream.test.binder;
import java.util.concurrent.BlockingQueue;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
/**
* Maintains a map between (output) channels and messages received (in FIFO order). To be
* injected in tests that can then run assertions on the enqueued messages.
*
* @author Eric Bottard
*/
public interface MessageCollector {
/**
* Obtain a queue that will receive messages sent to the given channel.
* @param channel message channel
* @return blocking queue for stored message
*/
BlockingQueue<Message<?>> forChannel(MessageChannel channel);
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2017-2019 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.stream.test.binder;
import org.springframework.cloud.stream.binder.BinderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
/**
* Automatically registers the {@link MessageCollector} associated with the test binder as
* a bean.
*
* @author Marius Bogoevici
*/
@Configuration
public class MessageCollectorAutoConfiguration {
@Bean
public MessageCollector messageCollector(BinderFactory binderFactory) {
return ((TestSupportBinder) binderFactory.getBinder("test", MessageChannel.class))
.messageCollector();
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2017-2019 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.stream.test.binder;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
/**
* An {@link EnvironmentPostProcessor} that sets some configuration properties for
* {@link TestSupportBinder}.
*
* @author Ilayaperumal Gopinathan
*/
public class TestBinderEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Map<String, Object> propertiesToAdd = new HashMap<>();
propertiesToAdd.put("spring.cloud.stream.binders.test.defaultCandidate", "false");
environment.getPropertySources()
.addLast(new MapPropertySource("testBinderConfig", propertiesToAdd));
}
}

View File

@@ -1,268 +0,0 @@
/*
* Copyright 2015-2017 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.stream.test.binder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingDeque;
import org.springframework.cloud.stream.binder.Binder;
import org.springframework.cloud.stream.binder.Binding;
import org.springframework.cloud.stream.binder.ConsumerProperties;
import org.springframework.cloud.stream.binder.ProducerProperties;
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
import org.springframework.cloud.stream.converter.MessageConverterUtils;
import org.springframework.cloud.stream.test.matcher.MessageQueueMatcher;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.converter.DefaultContentTypeResolver;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.StringUtils;
/**
* A minimal binder that
* <ul>
* <li>does nothing about binding consumers, leaving the channel as-is, so that a test
* author can interact with it directly,</li>
* <li>registers a queue channel on the producer side, so that it is easy to assert what
* is received.</li>
* </ul>
*
* @author Eric Bottard
* @author Gary Russell
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Soby Chacko
* @see MessageQueueMatcher
*/
public class TestSupportBinder
implements Binder<MessageChannel, ConsumerProperties, ProducerProperties> {
private final MessageCollectorImpl messageCollector = new MessageCollectorImpl();
private final ConcurrentMap<String, MessageChannel> messageChannels = new ConcurrentHashMap<>();
@Override
public Binding<MessageChannel> bindConsumer(String name, String group,
MessageChannel inboundBindTarget, ConsumerProperties properties) {
return new TestBinding(inboundBindTarget, null);
}
/**
* Registers a single subscriber to the channel, that enqueues messages for later
* retrieval and assertion in tests.
*/
@Override
public Binding<MessageChannel> bindProducer(String name,
MessageChannel outboundBindTarget, ProducerProperties properties) {
final BlockingQueue<Message<?>> queue = this.messageCollector
.register(outboundBindTarget, properties.isUseNativeEncoding());
((SubscribableChannel) outboundBindTarget).subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
queue.add(message);
}
});
this.messageChannels.put(name, outboundBindTarget);
return new TestBinding(outboundBindTarget, this.messageCollector);
}
public MessageCollector messageCollector() {
return this.messageCollector;
}
public MessageChannel getChannelForName(String name) {
return this.messageChannels.get(name);
}
/**
* Maintains mappings between channels and queues.
*
* @author Eric Bottard
*/
private static class MessageCollectorImpl implements MessageCollector {
private final Map<MessageChannel, BlockingQueue<Message<?>>> results = new HashMap<>();
private BlockingQueue<Message<?>> register(MessageChannel channel,
boolean useNativeEncoding) {
// we need to add this interceptor to ensure MessageCollector's compatibility
// with
// previous versions of SCSt when native encoding is disabled.
if (!useNativeEncoding) {
((AbstractMessageChannel) channel)
.addInterceptor(new InboundMessageConvertingInterceptor());
}
LinkedBlockingDeque<Message<?>> result = new LinkedBlockingDeque<>();
Assert.isTrue(!this.results.containsKey(channel),
"Channel [" + channel + "] was already bound");
this.results.put(channel, result);
return result;
}
private void unregister(MessageChannel channel) {
Assert.notNull(this.results.remove(channel),
"Trying to unregister a mapping for an unknown channel [" + channel
+ "]");
}
@Override
public BlockingQueue<Message<?>> forChannel(MessageChannel channel) {
BlockingQueue<Message<?>> queue = this.results.get(channel);
Assert.notNull(queue, "Channel [" + channel + "] was not bound by "
+ TestSupportBinder.class);
return queue;
}
}
/**
* @author Marius Bogoevici
*/
private static final class TestBinding implements Binding<MessageChannel> {
private final MessageChannel target;
private final MessageCollectorImpl messageCollector;
private TestBinding(MessageChannel target,
MessageCollectorImpl messageCollector) {
this.target = target;
this.messageCollector = messageCollector;
}
@Override
public void unbind() {
if (this.messageCollector != null) {
this.messageCollector.unregister(this.target);
}
}
}
/**
* This is really an interceptor to maintain MessageCollector's backward compatibility
* with the behavior established in 1.3 - BINDER_ORIGINAL_CONTENT_TYPE - Kryo and Java
* deserialization - byte[] to String conversion - etc.
*/
private final static class InboundMessageConvertingInterceptor
implements ChannelInterceptor {
private final DefaultContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
private final CompositeMessageConverterFactory converterFactory = new CompositeMessageConverterFactory();
/*
* Candidate to go into some utils class
*/
private static boolean equalTypeAndSubType(MimeType m1, MimeType m2) {
return m1 != null && m2 != null && m1.getType().equalsIgnoreCase(m2.getType())
&& m1.getSubtype().equalsIgnoreCase(m2.getSubtype());
}
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
Class<?> targetClass = null;
MessageConverter converter = null;
MimeType contentType = MimeType.valueOf(this.contentTypeResolver
.resolve(message.getHeaders()).toString());
if (contentType != null) {
if (equalTypeAndSubType(MessageConverterUtils.X_JAVA_SERIALIZED_OBJECT,
contentType)
|| equalTypeAndSubType(MessageConverterUtils.X_JAVA_OBJECT,
contentType)) {
// for Java and Kryo de-serialization we need to reset the content
// type
message = MessageBuilder.fromMessage(message)
.setHeader(MessageHeaders.CONTENT_TYPE, contentType).build();
converter = equalTypeAndSubType(
MessageConverterUtils.X_JAVA_SERIALIZED_OBJECT, contentType)
? this.converterFactory
.getMessageConverterForType(contentType)
: this.converterFactory
.getMessageConverterForAllRegistered();
String targetClassName = contentType.getParameter("type");
if (StringUtils.hasText(targetClassName)) {
try {
targetClass = Class.forName(targetClassName, false,
Thread.currentThread().getContextClassLoader());
}
catch (Exception e) {
throw new IllegalStateException(
"Failed to determine class name for contentType: "
+ message.getHeaders(),
e);
}
}
}
}
Object payload;
if (converter != null) {
Assert.isTrue(
!(equalTypeAndSubType(MessageConverterUtils.X_JAVA_OBJECT,
contentType) && targetClass == null),
"Cannot deserialize into message since 'contentType` is not "
+ "encoded with the actual target type."
+ "Consider 'application/x-java-object; type=foo.bar.MyClass'");
payload = converter.fromMessage(message, targetClass);
}
else {
MimeType deserializeContentType = this.contentTypeResolver
.resolve(message.getHeaders());
if (deserializeContentType == null) {
deserializeContentType = contentType;
}
payload = deserializeContentType == null ? message.getPayload() : this
.deserializePayload(message.getPayload(), deserializeContentType);
}
message = MessageBuilder.withPayload(payload)
.copyHeaders(message.getHeaders())
.setHeader(MessageHeaders.CONTENT_TYPE, contentType)
.build();
return message;
}
private Object deserializePayload(Object payload, MimeType contentType) {
if (payload instanceof byte[]
&& ("text".equalsIgnoreCase(contentType.getType())
|| equalTypeAndSubType(MimeTypeUtils.APPLICATION_JSON,
contentType))) {
payload = new String((byte[]) payload, StandardCharsets.UTF_8);
}
return payload;
}
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2015-2017 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.stream.test.binder;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.cloud.stream.binder.Binder;
import org.springframework.cloud.stream.binder.BinderFactory;
import org.springframework.cloud.stream.binder.ConsumerProperties;
import org.springframework.cloud.stream.binder.ProducerProperties;
import org.springframework.cloud.stream.config.BindingServiceConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.messaging.MessageChannel;
/**
* Installs the TestSupportBinder} and exposes
* MessageCollectorImplto be injected in tests.
*
* Note that this auto-configuration has higher priority than regular binder
* configuration, so adding this on the classpath in test scope is sufficient to have
* support kick in and replace all binders with the test binder.
*
* @author Eric Bottard
* @author Marius Bogoevici
*/
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Import(TestSupportBinderConfiguration.class)
@AutoConfigureBefore(BindingServiceConfiguration.class)
public class TestSupportBinderAutoConfiguration {
@Bean
@SuppressWarnings("unchecked")
public BinderFactory binderFactory(final Binder<MessageChannel, ?, ?> binder) {
return new BinderFactory() {
@Override
public <T> Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties> getBinder(
String configurationName, Class<? extends T> bindableType) {
return (Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties>) binder;
}
};
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2017-2019 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.stream.test.binder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.stream.binder.Binder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
/**
* Binder {@link org.springframework.context.annotation.Configuration} for the
* {@link TestSupportBinder}
*
* Either imported by the {@link TestSupportBinderAutoConfiguration} for the test binder
* default usage scenario (superseding all binders on the classpath), or used as a binder
* configuration on the classpath when test binder autoconfiguration is disabled.
*
* @author Marius Bogoevici
*/
@Configuration
@ConditionalOnMissingBean(Binder.class)
public class TestSupportBinderConfiguration {
private Binder<MessageChannel, ?, ?> messageChannelBinder = new TestSupportBinder();
@Bean
public Binder<MessageChannel, ?, ?> binder() {
return this.messageChannelBinder;
}
}

View File

@@ -1,164 +0,0 @@
/*
* Copyright 2015-2017 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.stream.test.matcher;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
import org.springframework.cloud.stream.test.binder.TestSupportBinder;
import org.springframework.messaging.Message;
/**
* A Hamcrest Matcher meant to be used in conjunction with {@link TestSupportBinder}.
*
*
* @param <T> return type
* @author Eric Bottard
* @author Janne Valkealahti
*/
public class MessageQueueMatcher<T> extends BaseMatcher<BlockingQueue<Message<?>>> {
private final Matcher<T> delegate;
private final long timeout;
private final TimeUnit unit;
private Extractor<Message<?>, T> extractor;
private Map<BlockingQueue<Message<?>>, T> actuallyReceived = new HashMap<>();
public MessageQueueMatcher(Matcher<T> delegate, long timeout, TimeUnit unit,
Extractor<Message<?>, T> extractor) {
this.delegate = delegate;
this.timeout = timeout;
this.unit = (unit != null ? unit : TimeUnit.SECONDS);
this.extractor = extractor;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <P> MessageQueueMatcher<P> receivesMessageThat(
Matcher<Message<P>> messageMatcher) {
return new MessageQueueMatcher(messageMatcher, 5, TimeUnit.SECONDS,
new Extractor<Message<P>, Message<P>>("a message that ") {
@Override
public Message<P> apply(Message<P> m) {
return m;
}
});
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <P> MessageQueueMatcher<P> receivesPayloadThat(
Matcher<P> payloadMatcher) {
return new MessageQueueMatcher(payloadMatcher, 5, TimeUnit.SECONDS,
new Extractor<Message<P>, P>("a message whose payload ") {
@Override
public P apply(Message<P> m) {
return m.getPayload();
}
});
}
@Override
public boolean matches(Object item) {
@SuppressWarnings("unchecked")
BlockingQueue<Message<?>> queue = (BlockingQueue<Message<?>>) item;
Message<?> received = null;
try {
if (this.timeout > 0) {
received = queue.poll(this.timeout, this.unit);
}
else if (this.timeout == 0) {
received = queue.poll();
}
else {
received = queue.take();
}
}
catch (InterruptedException e) {
return false;
}
T unwrapped = this.extractor.apply(received);
this.actuallyReceived.put(queue, unwrapped);
return this.delegate.matches(unwrapped);
}
@Override
public void describeMismatch(Object item, Description description) {
@SuppressWarnings("unchecked")
BlockingQueue<Message<?>> queue = (BlockingQueue<Message<?>>) item;
T value = this.actuallyReceived.get(queue);
if (value != null) {
description.appendText("received: ").appendValue(value);
}
else {
description.appendText("timed out after " + this.timeout + " "
+ this.unit.name().toLowerCase());
}
}
public MessageQueueMatcher<T> within(long timeout, TimeUnit unit) {
return new MessageQueueMatcher<>(this.delegate, timeout, unit, this.extractor);
}
public MessageQueueMatcher<T> immediately() {
return new MessageQueueMatcher<>(this.delegate, 0, null, this.extractor);
}
public MessageQueueMatcher<T> indefinitely() {
return new MessageQueueMatcher<>(this.delegate, -1, null, this.extractor);
}
@Override
public void describeTo(Description description) {
description.appendText("Channel to receive ").appendDescriptionOf(this.extractor)
.appendDescriptionOf(this.delegate);
}
/**
* A transformation to be applied to a received message before asserting, <i>e.g.</i>
* to only inspect the payload.
*
* @param <R> input type
* @param <T> return type
*/
public static abstract class Extractor<R, T>
implements Function<R, T>, SelfDescribing {
private final String behaviorDescription;
protected Extractor(String behaviorDescription) {
this.behaviorDescription = behaviorDescription;
}
@Override
public void describeTo(Description description) {
description.appendText(this.behaviorDescription);
}
}
}

View File

@@ -1,2 +0,0 @@
test:\
org.springframework.cloud.stream.test.binder.TestSupportBinderConfiguration

View File

@@ -1,5 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration,\
org.springframework.cloud.stream.test.binder.MessageCollectorAutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.stream.test.binder.TestBinderEnvironmentPostProcessor

View File

@@ -1,113 +0,0 @@
/*
* Copyright 2015-2019 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.stream.test.matcher;
import java.util.Collections;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.hamcrest.StringDescription;
import org.junit.jupiter.api.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.is;
/**
* Tests for MessageQueueMatcher.
*
* @author Eric Bottard
*/
public class MessageQueueMatcherTest {
private final BlockingDeque<Message<?>> queue = new LinkedBlockingDeque<>();
private final StringDescription description = new StringDescription();
@Test
public void testTimeout() {
Message<?> msg = new GenericMessage<>("hello");
MessageQueueMatcher<?> matcher = MessageQueueMatcher.receivesMessageThat(is(msg))
.within(2, TimeUnit.MILLISECONDS);
boolean result = matcher.matches(this.queue);
assertThat(result).isFalse();
matcher.describeMismatch(this.queue, this.description);
assertThat(this.description.toString())
.isEqualTo("timed out after 2 milliseconds");
}
@Test
public void testMatch() {
Message<?> msg = new GenericMessage<>("hello");
MessageQueueMatcher<?> matcher = MessageQueueMatcher.receivesMessageThat(is(msg));
this.queue.offer(msg);
boolean result = matcher.matches(this.queue);
assertThat(result).isTrue();
}
@Test
public void testMismatch() {
Message<?> msg = new GenericMessage<>("hello");
Message<?> other = new GenericMessage<>("world");
MessageQueueMatcher<?> matcher = MessageQueueMatcher.receivesMessageThat(is(msg));
this.queue.offer(other);
boolean result = matcher.matches(this.queue);
assertThat(result).isFalse();
matcher.describeMismatch(this.queue, this.description);
assertThat(this.description.toString()).isEqualTo(("received: <" + other + ">"));
}
@Test
public void testExtractor() {
Message<?> msg = new GenericMessage<>("hello",
Collections.singletonMap("foo", (Object) "bar"));
MessageQueueMatcher.Extractor<Message<?>, String> headerExtractor;
headerExtractor = new MessageQueueMatcher.Extractor<Message<?>, String>(
"whose 'foo' header") {
@Override
public String apply(Message<?> message) {
return message.getHeaders().get("foo", String.class);
}
};
MessageQueueMatcher<?> matcher = new MessageQueueMatcher<>(is("bar"), -1, null,
headerExtractor);
this.queue.offer(msg);
boolean result = matcher.matches(this.queue);
assertThat(result);
matcher = new MessageQueueMatcher<>(is("wizz"), -1, null, headerExtractor);
this.queue.offer(msg);
result = matcher.matches(this.queue);
assertThat(result).isFalse();
matcher.describeMismatch(this.queue, this.description);
assertThat(this.description.toString()).isEqualTo(("received: \"bar\""));
}
@Test
public void testDescription() {
Message<?> msg = new GenericMessage<>("hello");
MessageQueueMatcher<?> matcher = MessageQueueMatcher.receivesMessageThat(is(msg));
this.description.appendDescriptionOf(matcher);
assertThat(this.description.toString())
.isEqualTo(("Channel to receive a message that is <" + msg + ">"));
}
}