org.springframework.boot
spring-boot-starter-test
@@ -30,14 +35,27 @@
io.awspring.cloud
- spring-cloud-starter-aws-messaging
+ spring-cloud-aws-test
+ test
- org.springframework
- spring-webmvc
+ org.awaitility
+ awaitility
+ test
+
+ org.testcontainers
+ junit-jupiter
+ test
+
+
+
+ org.testcontainers
+ localstack
+ test
+
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/MessageSender.java b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/MessageSender.java
new file mode 100644
index 000000000..ec2f63b0d
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/MessageSender.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.sqs.sample;
+
+import java.time.LocalDate;
+
+import io.awspring.cloud.messaging.core.QueueMessagingTemplate;
+
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.stereotype.Component;
+
+@Component
+class MessageSender {
+
+ private final QueueMessagingTemplate queueMessagingTemplate;
+
+ MessageSender(QueueMessagingTemplate queueMessagingTemplate) {
+ this.queueMessagingTemplate = queueMessagingTemplate;
+ }
+
+ @EventListener(ApplicationReadyEvent.class)
+ public void sendMessage() {
+ this.queueMessagingTemplate.send("InfrastructureStack-spring-aws",
+ MessageBuilder.withPayload("Spring cloud Aws SQS sample!").build());
+ this.queueMessagingTemplate.convertAndSend("InfrastructureStack-aws-pojo",
+ new Person("Joe", "Doe", LocalDate.of(2000, 1, 12)));
+ }
+
+}
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SampleListener.java b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SampleListener.java
new file mode 100644
index 000000000..71b4c6d85
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SampleListener.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.sqs.sample;
+
+import io.awspring.cloud.messaging.listener.annotation.SqsListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.stereotype.Component;
+
+@Component
+class SampleListener {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SampleListener.class);
+
+ @SqsListener("InfrastructureStack-spring-aws")
+ public void listenToMessage(String message) {
+ LOGGER.info("This is message you want to see: {}", message);
+ }
+
+ @SqsListener("InfrastructureStack-aws-pojo")
+ public void listenToPerson(Person person) {
+ LOGGER.info(person.toString());
+ }
+
+}
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SqsSampleApplication.java b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SqsSampleApplication.java
index 602054ecc..7c9c44f35 100644
--- a/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SqsSampleApplication.java
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/main/java/io/awspring/cloud/sqs/sample/SqsSampleApplication.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2021 the original author or authors.
+ * Copyright 2013-2022 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.
@@ -16,50 +16,14 @@
package io.awspring.cloud.sqs.sample;
-import java.time.LocalDate;
-
-import io.awspring.cloud.messaging.core.QueueMessagingTemplate;
-import io.awspring.cloud.messaging.listener.annotation.SqsListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
-import org.springframework.context.event.EventListener;
-import org.springframework.messaging.support.MessageBuilder;
@SpringBootApplication
public class SqsSampleApplication {
- private final QueueMessagingTemplate queueMessagingTemplate;
-
- SqsSampleApplication(QueueMessagingTemplate queueMessagingTemplate) {
- this.queueMessagingTemplate = queueMessagingTemplate;
- }
-
- private static final Logger LOGGER = LoggerFactory.getLogger(SqsSampleApplication.class);
-
public static void main(String[] args) {
SpringApplication.run(SqsSampleApplication.class, args);
}
- @EventListener(ApplicationReadyEvent.class)
- public void sendMessage() {
- this.queueMessagingTemplate.send("InfrastructureStack-spring-aws",
- MessageBuilder.withPayload("Spring cloud Aws SQS sample!").build());
- this.queueMessagingTemplate.convertAndSend("InfrastructureStack-aws-pojo",
- new Person("Joe", "Doe", LocalDate.of(2000, 1, 12)));
- }
-
- @SqsListener("InfrastructureStack-spring-aws")
- private void listenToMessage(String message) {
- LOGGER.info("This is message you want to see: {}", message);
- }
-
- @SqsListener("InfrastructureStack-aws-pojo")
- private void listenToPerson(Person person) {
- LOGGER.info(person.toString());
- }
-
}
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/java/io/awspring/cloud/sqs/sample/SqsSampleApplicationTests.java b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/java/io/awspring/cloud/sqs/sample/SqsSampleApplicationTests.java
new file mode 100644
index 000000000..7dc005b7e
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/java/io/awspring/cloud/sqs/sample/SqsSampleApplicationTests.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.sqs.sample;
+
+import java.io.IOException;
+
+import io.awspring.cloud.messaging.core.QueueMessagingTemplate;
+import io.awspring.cloud.test.sqs.SqsTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.localstack.LocalStackContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+
+import static org.awaitility.Awaitility.await;
+import static org.mockito.Mockito.verify;
+import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS;
+
+@SqsTest(properties = { "cloud.aws.credentials.access-key=noop", "cloud.aws.credentials.secret-key=noop",
+ "cloud.aws.region.static=eu-west-1" })
+@Testcontainers
+class SqsSampleApplicationTests {
+
+ private static final String QUEUE_NAME = "InfrastructureStack-spring-aws";
+
+ // create an SQS locally running equivalent with Localstack and Testcontainers
+ @Container
+ static LocalStackContainer localstack = new LocalStackContainer(
+ DockerImageName.parse("localstack/localstack:0.14.0")).withServices(SQS).withReuse(true);
+
+ @Autowired
+ private QueueMessagingTemplate queueMessagingTemplate;
+
+ @SpyBean
+ private SampleListener sampleListener;
+
+ @BeforeAll
+ static void beforeAll() throws IOException, InterruptedException {
+ // create needed queues in SQS
+ localstack.execInContainer("awslocal", "sqs", "create-queue", "--queue-name", QUEUE_NAME);
+ }
+
+ @Test
+ void receivesMessage() {
+ // send a message
+ queueMessagingTemplate.convertAndSend(QUEUE_NAME, "hello");
+
+ // verify that bean handling the message was invoked
+ await().untilAsserted(() -> verify(sampleListener).listenToMessage("hello"));
+ }
+
+ @DynamicPropertySource
+ static void registerSqsProperties(DynamicPropertyRegistry registry) {
+ // overwrite SQS endpoint with one provided by Localstack
+ registry.add("cloud.aws.sqs.endpoint", () -> localstack.getEndpointOverride(SQS).toString());
+ }
+
+}
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/resources/logback-test.xml b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/resources/logback-test.xml
new file mode 100644
index 000000000..3b961b063
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/resources/logback-test.xml
@@ -0,0 +1,15 @@
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-cloud-aws-test/pom.xml b/spring-cloud-aws-test/pom.xml
new file mode 100644
index 000000000..8f3e577bd
--- /dev/null
+++ b/spring-cloud-aws-test/pom.xml
@@ -0,0 +1,78 @@
+
+
+
+
+ 4.0.0
+
+ io.awspring.cloud
+ spring-cloud-aws
+ 2.4.0-SNAPSHOT
+
+ spring-cloud-aws-test
+ Spring Cloud AWS Test
+ Spring Cloud AWS Test
+ https://projects.spring.io/spring-cloud
+
+
+ ${basedir}/../..
+
+
+
+ io.awspring.cloud
+ spring-cloud-aws-autoconfigure
+
+
+ io.awspring.cloud
+ spring-cloud-aws-messaging
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+
+
+ org.springframework
+ spring-test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.assertj
+ assertj-core
+
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.testcontainers
+ localstack
+ test
+
+
+ org.testcontainers
+ junit-jupiter
+ test
+
+
+
diff --git a/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/AutoConfigureSqs.java b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/AutoConfigureSqs.java
new file mode 100644
index 000000000..13a0e7575
--- /dev/null
+++ b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/AutoConfigureSqs.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+
+/**
+ * {@link ImportAutoConfiguration Auto-configuration imports} for typical SQS tests. Most
+ * tests should consider using {@link SqsTest @SqsTest} rather than using this annotation
+ * directly.
+ *
+ * @author Maciej Walkowiak
+ * @since 2.4.0
+ * @see SqsTest
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@ImportAutoConfiguration
+public @interface AutoConfigureSqs {
+
+}
diff --git a/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTest.java b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTest.java
new file mode 100644
index 000000000..3bed391ff
--- /dev/null
+++ b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
+import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.BootstrapWith;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+/**
+ * Annotation for a SQS test that focuses only on SQS-based components.
+ *
+ * Using this annotation will disable full auto-configuration and instead apply only
+ * configuration relevant to SQS tests.
+ *
+ * When using JUnit 4, this annotation should be used in combination with
+ * {@code @RunWith(SpringRunner.class)}.
+ *
+ * @author Maciej Walkowiak
+ * @since 2.4.0
+ * @see AutoConfigureSqs
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@BootstrapWith(SqsTestContextBootstrapper.class)
+@OverrideAutoConfiguration(enabled = false)
+@TypeExcludeFilters(SqsTypeExcludeFilter.class)
+@ExtendWith(SpringExtension.class)
+@AutoConfigureSqs
+@ImportAutoConfiguration
+public @interface SqsTest {
+
+ /**
+ * Determines if default filtering should be used with
+ * {@link SpringBootApplication @SpringBootApplication}. By default no beans are
+ * included.
+ * @see #includeFilters()
+ * @see #excludeFilters()
+ * @return if default filters should be used
+ */
+ boolean useDefaultFilters() default true;
+
+ /**
+ * Specifies the listeners to test. This is an alias of {@link #listeners()} which can
+ * be used for brevity if no other attributes are defined. See {@link #listeners()}
+ * for details.
+ * @see #listeners()
+ * @return the listeners to test
+ */
+ @AliasFor("listeners")
+ Class>[] value() default {};
+
+ /**
+ * Specifies the listeners to test.
+ * @see #value()
+ * @return the listeners to test
+ */
+ @AliasFor("value")
+ Class>[] listeners() default {};
+
+ /**
+ * Properties in form {@literal key=value} that should be added to the Spring
+ * {@link Environment} before the test runs.
+ * @return the properties to add
+ */
+ String[] properties() default {};
+
+ /**
+ * A set of include filters which can be used to add otherwise filtered beans to the
+ * application context.
+ * @return include filters to apply
+ */
+ ComponentScan.Filter[] includeFilters() default {};
+
+ /**
+ * A set of exclude filters which can be used to filter beans that would otherwise be
+ * added to the application context.
+ * @return exclude filters to apply
+ */
+ ComponentScan.Filter[] excludeFilters() default {};
+
+}
diff --git a/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTestContextBootstrapper.java b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTestContextBootstrapper.java
new file mode 100644
index 000000000..89df86b82
--- /dev/null
+++ b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTestContextBootstrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
+import org.springframework.core.annotation.MergedAnnotations;
+import org.springframework.test.context.TestContextBootstrapper;
+
+/**
+ * {@link TestContextBootstrapper} for {@link SqsTest @SqsTest} support.
+ *
+ * @author Maciej Walkowiak
+ * @since 2.4.0
+ */
+public class SqsTestContextBootstrapper extends SpringBootTestContextBootstrapper {
+
+ @Override
+ protected String[] getProperties(Class> testClass) {
+ return MergedAnnotations.from(testClass, MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS)
+ .get(SqsTest.class).getValue("properties", String[].class).orElse(null);
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTypeExcludeFilter.java b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTypeExcludeFilter.java
new file mode 100644
index 000000000..12553e851
--- /dev/null
+++ b/spring-cloud-aws-test/src/main/java/io/awspring/cloud/test/sqs/SqsTypeExcludeFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.springframework.boot.context.TypeExcludeFilter;
+import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter;
+
+/**
+ * {@link TypeExcludeFilter} for {@link SqsTest @SqsTest}.
+ *
+ * @author Maciej Walkowiak
+ * @since 2.4.0
+ */
+public class SqsTypeExcludeFilter extends StandardAnnotationCustomizableTypeExcludeFilter {
+
+ private static final Class>[] NO_LISTENERS = {};
+
+ private final Class>[] listeners;
+
+ SqsTypeExcludeFilter(Class> testClass) {
+ super(testClass);
+ this.listeners = getAnnotation().getValue("listeners", Class[].class).orElse(NO_LISTENERS);
+ }
+
+ @Override
+ protected Set> getComponentIncludes() {
+ return new LinkedHashSet<>(Arrays.asList(this.listeners));
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/main/resources/META-INF/spring.factories b/spring-cloud-aws-test/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..b5be89d27
--- /dev/null
+++ b/spring-cloud-aws-test/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+io.awspring.cloud.test.sqs.AutoConfigureSqs=\
+org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
+io.awspring.cloud.autoconfigure.messaging.SqsAutoConfiguration,\
+io.awspring.cloud.autoconfigure.context.ContextRegionProviderAutoConfiguration,\
+io.awspring.cloud.autoconfigure.context.ContextCredentialsAutoConfiguration
diff --git a/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/BaseSqsIntegrationTest.java b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/BaseSqsIntegrationTest.java
new file mode 100644
index 000000000..dd32949be
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/BaseSqsIntegrationTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.localstack.LocalStackContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+
+import static io.awspring.cloud.test.sqs.SqsSampleListener.QUEUE_NAME;
+import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS;
+
+@Testcontainers
+abstract class BaseSqsIntegrationTest {
+
+ @Container
+ static LocalStackContainer localstack = new LocalStackContainer(
+ DockerImageName.parse("localstack/localstack:0.14.0")).withServices(SQS).withReuse(true);
+
+ @BeforeAll
+ static void beforeAll() throws IOException, InterruptedException {
+ // create needed queues in SQS
+ localstack.execInContainer("awslocal", "sqs", "create-queue", "--queue-name", QUEUE_NAME);
+ }
+
+ @DynamicPropertySource
+ static void registerSqsProperties(DynamicPropertyRegistry registry) {
+ // overwrite SQS endpoint with one provided by Localstack
+ registry.add("cloud.aws.sqs.endpoint", () -> localstack.getEndpointOverride(SQS).toString());
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SampleComponent.java b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SampleComponent.java
new file mode 100644
index 000000000..6d11df253
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SampleComponent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import org.springframework.stereotype.Component;
+
+@Component
+class SampleComponent {
+
+ void save(String message) {
+
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsSampleListener.java b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsSampleListener.java
new file mode 100644
index 000000000..720cc64f6
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsSampleListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import io.awspring.cloud.messaging.listener.annotation.SqsListener;
+
+import org.springframework.stereotype.Component;
+
+@Component
+class SqsSampleListener {
+
+ static final String QUEUE_NAME = "my-queue";
+
+ private final SampleComponent sampleComponent;
+
+ SqsSampleListener(SampleComponent sampleComponent) {
+ this.sampleComponent = sampleComponent;
+ }
+
+ @SqsListener(QUEUE_NAME)
+ void handle(String message) {
+ sampleComponent.save(message);
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestApplication.java b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestApplication.java
new file mode 100644
index 000000000..f136227fe
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SqsTestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SqsTestApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestListenersDefinedTest.java b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestListenersDefinedTest.java
new file mode 100644
index 000000000..f16cb2c45
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestListenersDefinedTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import io.awspring.cloud.messaging.core.QueueMessagingTemplate;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.ApplicationContext;
+
+import static io.awspring.cloud.test.sqs.SqsSampleListener.QUEUE_NAME;
+import static org.assertj.core.api.Assertions.assertThatNoException;
+import static org.awaitility.Awaitility.await;
+import static org.mockito.Mockito.verify;
+
+@SqsTest(listeners = SqsSampleListener.class, properties = { "cloud.aws.credentials.access-key=noop",
+ "cloud.aws.credentials.secret-key=noop", "cloud.aws.region.static=eu-west-1" })
+class SqsTestListenersDefinedTest extends BaseSqsIntegrationTest {
+
+ @Autowired
+ private ApplicationContext ctx;
+
+ @Autowired
+ private QueueMessagingTemplate queueMessagingTemplate;
+
+ @MockBean
+ private SampleComponent sampleComponent;
+
+ @Test
+ void createsQueueMessagingTemplate() {
+ assertThatNoException().isThrownBy(() -> this.ctx.getBean(QueueMessagingTemplate.class));
+ }
+
+ @Test
+ void createsListener() {
+ assertThatNoException().isThrownBy(() -> this.ctx.getBean(SqsSampleListener.class));
+ }
+
+ @Test
+ void listenerHandlesMessage() {
+ queueMessagingTemplate.convertAndSend(QUEUE_NAME, "message");
+
+ await().untilAsserted(() -> verify(sampleComponent).save("message"));
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestNoListenersDefinedTest.java b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestNoListenersDefinedTest.java
new file mode 100644
index 000000000..c2661f43f
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/java/io/awspring/cloud/test/sqs/SqsTestNoListenersDefinedTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013-2022 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 io.awspring.cloud.test.sqs;
+
+import io.awspring.cloud.autoconfigure.context.ContextCredentialsAutoConfiguration;
+import io.awspring.cloud.autoconfigure.context.ContextRegionProviderAutoConfiguration;
+import io.awspring.cloud.autoconfigure.messaging.SqsAutoConfiguration;
+import io.awspring.cloud.messaging.core.QueueMessagingTemplate;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.context.ApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThatNoException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+@SqsTest
+class SqsTestNoListenersDefinedTest extends BaseSqsIntegrationTest {
+
+ private static final Class>[] EXPECTED_AUTOCONFIGURATION_CLASSES = { SqsAutoConfiguration.class,
+ JacksonAutoConfiguration.class, ContextRegionProviderAutoConfiguration.class,
+ ContextCredentialsAutoConfiguration.class };
+
+ @Autowired
+ private ApplicationContext ctx;
+
+ @Test
+ void usesAutoConfigurations() {
+ for (Class> clazz : EXPECTED_AUTOCONFIGURATION_CLASSES) {
+ assertThatNoException().isThrownBy(() -> this.ctx.getBean(clazz));
+ }
+ }
+
+ @Test
+ void createsQueueMessagingTemplate() {
+ assertThatNoException().isThrownBy(() -> this.ctx.getBean(QueueMessagingTemplate.class));
+ }
+
+ @Test
+ void doesNotCreateListenerBean() {
+ assertThatThrownBy(() -> this.ctx.getBean(SqsSampleListener.class))
+ .isInstanceOf(NoSuchBeanDefinitionException.class);
+ }
+
+ @Test
+ void doesNotCreateOtherBeans() {
+ assertThatThrownBy(() -> this.ctx.getBean(SampleComponent.class))
+ .isInstanceOf(NoSuchBeanDefinitionException.class);
+ }
+
+}
diff --git a/spring-cloud-aws-test/src/test/resources/logback-test.xml b/spring-cloud-aws-test/src/test/resources/logback-test.xml
new file mode 100644
index 000000000..3b961b063
--- /dev/null
+++ b/spring-cloud-aws-test/src/test/resources/logback-test.xml
@@ -0,0 +1,15 @@
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
+
+
+
+
+
+
+
+
+
+
+