Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2013-2025 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.sns;

import org.junit.jupiter.api.BeforeAll;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.localstack.LocalStackContainer;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sqs.SqsClient;

/**
* The base contract for JUnit tests based on the container for Localstack. The Testcontainers 'reuse' option must be
* disabled, so Ryuk container is started and will clean all the containers up from this test suite after JVM exit.
* Since the Localstack container instance is shared via static property, it is going to be started only once per JVM;
* therefore, the target Docker container is reused automatically.
*
* @author haroya01
*
* @since 4.0
*/
@Testcontainers(disabledWithoutDocker = true)
public interface LocalstackContainerTest {

LocalStackContainer LOCAL_STACK_CONTAINER = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:4.4.0"))
.withEnv("DEBUG", "1");

@BeforeAll
static void startContainer() {
LOCAL_STACK_CONTAINER.start();
}

static SnsClient snsClient() {
return applyAwsClientOptions(SnsClient.builder());
}

static SqsClient sqsClient() {
return applyAwsClientOptions(SqsClient.builder());
}

static AwsCredentialsProvider credentialsProvider() {
return StaticCredentialsProvider.create(
AwsBasicCredentials.create(LOCAL_STACK_CONTAINER.getAccessKey(), LOCAL_STACK_CONTAINER.getSecretKey()));
}

private static <B extends AwsClientBuilder<B, T>, T> T applyAwsClientOptions(B clientBuilder) {
return clientBuilder.region(Region.of(LOCAL_STACK_CONTAINER.getRegion()))
.credentialsProvider(credentialsProvider())
.endpointOverride(LOCAL_STACK_CONTAINER.getEndpoint())
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package io.awspring.cloud.sns.core.batch;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

import io.awspring.cloud.sns.LocalstackContainerTest;
import io.awspring.cloud.sns.Person;
import io.awspring.cloud.sns.core.CachingTopicArnResolver;
import io.awspring.cloud.sns.core.DefaultTopicArnResolver;
Expand All @@ -23,20 +27,19 @@
import io.awspring.cloud.sns.core.TopicArnResolver;
import io.awspring.cloud.sns.core.batch.converter.DefaultSnsMessageConverter;
import io.awspring.cloud.sns.core.batch.executor.SequentialBatchExecutionStrategy;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.JacksonJsonMessageConverter;
import org.springframework.messaging.support.MessageBuilder;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.localstack.LocalStackContainer;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.CreateTopicRequest;
import software.amazon.awssdk.services.sqs.SqsClient;
Expand All @@ -47,32 +50,19 @@
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.json.JsonMapper;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

/**
* Integration tests for {@link SnsBatchTemplate}
*
* @author Matej Nedic
* @author haroya01
*/
@Testcontainers
class SnsBatchTemplateIntegrationTest {
class SnsBatchTemplateIntegrationTest implements LocalstackContainerTest {

public static final String BATCH_TEST_TOPIC = "batch-test-topic";
public static final String BATCH_TEST_QUEUE = "batch-test-queue";
public static final String BATCH_TEST_TOPIC_FIFO = "batch-test-topic.fifo";
public static final String BATCH_TEST_QUEUE_FIFO = "batch-test-queue.fifo";
private static final JsonMapper jsonMapper = JsonMapper.builder().build();
@Container
static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:4.4.0"));
private static SqsClient sqsClient;
private static SnsBatchTemplate snsBatchTemplate;
private static String standardTopicArn;
Expand All @@ -82,14 +72,8 @@ class SnsBatchTemplateIntegrationTest {

@BeforeAll
static void setUp() {
StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider
.create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()));

SnsClient snsClient = SnsClient.builder().endpointOverride(localstack.getEndpoint())
.credentialsProvider(credentialsProvider).region(Region.of(localstack.getRegion())).build();

sqsClient = SqsClient.builder().endpointOverride(localstack.getEndpoint())
.credentialsProvider(credentialsProvider).region(Region.of(localstack.getRegion())).build();
SnsClient snsClient = LocalstackContainerTest.snsClient();
sqsClient = LocalstackContainerTest.sqsClient();

// Standard queue and Topic
standardTopicArn = snsClient.createTopic(r -> r.name(BATCH_TEST_TOPIC)).topicArn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.assertj.core.api.Assertions.*;
import static org.awaitility.Awaitility.await;

import io.awspring.cloud.sns.LocalstackContainerTest;
import io.awspring.cloud.sns.Person;
import io.awspring.cloud.sns.core.SnsTemplate;
import io.awspring.cloud.sns.core.TopicNotFoundException;
Expand All @@ -32,15 +33,8 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.localstack.LocalStackContainer;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonNode;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.CreateTopicRequest;
import software.amazon.awssdk.services.sqs.SqsClient;
Expand All @@ -53,29 +47,20 @@
*
* @author Matej Nedic
* @author Hardik Singh Behl
* @author haroya01
*/
@Testcontainers
class SnsTemplateIntegrationTest {
class SnsTemplateIntegrationTest implements LocalstackContainerTest {

private static final String TOPIC_NAME = "my_topic_name";
private static SnsTemplate snsTemplate;
private static SnsClient snsClient;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static SqsClient sqsClient;

@Container
static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:4.4.0"));

@BeforeAll
public static void createSnsTemplate() {
snsClient = SnsClient.builder().endpointOverride(localstack.getEndpoint())
.region(Region.of(localstack.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("noop", "noop")))
.build();
sqsClient = SqsClient.builder().endpointOverride(localstack.getEndpoint())
.region(Region.of(localstack.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("noop", "noop")))
.build();
snsClient = LocalstackContainerTest.snsClient();
sqsClient = LocalstackContainerTest.sqsClient();
MappingJackson2MessageConverter mappingJackson2MessageConverter = new MappingJackson2MessageConverter();
mappingJackson2MessageConverter.setSerializedPayloadClass(String.class);
snsTemplate = new SnsTemplate(snsClient, mappingJackson2MessageConverter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,26 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

import io.awspring.cloud.sns.LocalstackContainerTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.localstack.LocalStackContainer;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;

/**
* Integration tests for {@link SnsSmsTemplate}.
*
* @author Matej Nedic
* @author haroya01
*/
@Testcontainers
class SnsSmsTemplateIntegrationTest {
private static SnsSmsTemplate snsSmsTemplate;
class SnsSmsTemplateIntegrationTest implements LocalstackContainerTest {

@Container
static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:4.4.0")).withEnv("DEBUG", "1");
private static SnsSmsTemplate snsSmsTemplate;

@BeforeAll
public static void createSnsTemplate() {
SnsClient snsClient = SnsClient.builder().endpointOverride(localstack.getEndpoint())
.region(Region.of(localstack.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("noop", "noop")))
.build();
SnsClient snsClient = LocalstackContainerTest.snsClient();
snsSmsTemplate = new SnsSmsTemplate(snsClient);
}

Expand All @@ -58,7 +46,7 @@ void sendValidMessage_ToPhoneNumber() {
Assertions.assertDoesNotThrow(() -> snsSmsTemplate.send("+385000000000", "Spring Cloud AWS got you covered!"));

await().untilAsserted(() -> {
String logs = localstack.getLogs(OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR);
String logs = LOCAL_STACK_CONTAINER.getLogs(OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR);
assertThat(logs).contains("Delivering SMS message to +385000000000: Spring Cloud AWS got you covered!");
});
}
Expand All @@ -70,7 +58,7 @@ void sendValidMessage_ToPhoneNumber_WithAttributes() {
.builder().smsType(SmsType.PROMOTIONAL).senderID("AWSPRING").maxPrice("1.00").build()));

await().untilAsserted(() -> {
String logs = localstack.getLogs(OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR);
String logs = LOCAL_STACK_CONTAINER.getLogs(OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR);
assertThat(logs).contains("Delivering SMS message to +385000000000: Spring Cloud AWS got you covered!");
});
}
Expand Down