diff --git a/docs/modules/azure.md b/docs/modules/azure.md
index e09634b09c4..507a7e3699b 100644
--- a/docs/modules/azure.md
+++ b/docs/modules/azure.md
@@ -5,12 +5,14 @@ This module is INCUBATING. While it is ready for use and operational in the curr
Testcontainers module for the Microsoft Azure's [SDK](https://github.com/Azure/azure-sdk-for-java).
-Currently, the module supports `Azurite`, `Azure Event Hubs` and `CosmosDB` emulators. In order to use them, you should use the following classes:
+Currently, the module supports `Azurite`, `Azure Event Hubs`, `Azure Service Bus` and `CosmosDB` emulators. In order to use them, you should use the following classes:
Class | Container Image
-|-
AzuriteContainer | [mcr.microsoft.com/azure-storage/azurite](https://github.com/microsoft/containerregistry)
AzureEventHubsContainer | [mcr.microsoft.com/azure-messaging/eventhubs-emulator](https://github.com/microsoft/containerregistry)
+AzureServiceBusEmulatorContainer | [mcr.microsoft.com/azure-messaging/servicebus-emulator](https://github.com/microsoft/containerregistry)
+AzureServiceBusContainer | [mcr.microsoft.com/azure-messaging/servicebus-emulator](https://github.com/microsoft/containerregistry)
CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry)
## Usage example
@@ -101,6 +103,38 @@ Configure the consumer and the producer clients:
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:createProducerAndConsumer
+### Azure Service Bus Emulator
+
+
+[Configuring the Azure Service Bus Emulator container](../../modules/azure/src/test/resources/service-bus-config.json)
+
+
+Start Azure Service Bus Emulator during a test:
+
+
+[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:network
+
+
+
+[Starting a SQL Server container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:sqlContainer
+
+
+
+[Starting a Service Bus Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:emulatorContainer
+
+
+#### Using Azure Service Bus clients
+
+Configure the sender and the processor clients:
+
+
+[Configuring the sender client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:senderClient
+
+
+
+[Configuring the processor client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:processorClient
+
+
### CosmosDB
Start Azure CosmosDB Emulator during a test:
diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle
index 3dc97d03fce..1b4242b4eb6 100644
--- a/modules/azure/build.gradle
+++ b/modules/azure/build.gradle
@@ -2,6 +2,7 @@ description = "Testcontainers :: Azure"
dependencies {
api project(':testcontainers')
+ api project(':mssqlserver')
// TODO use JDK's HTTP client and/or Apache HttpClient5
shaded 'com.squareup.okhttp3:okhttp:4.12.0'
@@ -11,4 +12,6 @@ dependencies {
testImplementation 'com.azure:azure-storage-queue:12.24.0'
testImplementation 'com.azure:azure-data-tables:12.5.0'
testImplementation 'com.azure:azure-messaging-eventhubs:5.19.2'
+ testImplementation 'com.azure:azure-messaging-servicebus:7.17.8'
+ testImplementation 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre8'
}
diff --git a/modules/azure/src/main/java/org/testcontainers/azure/AzureServiceBusContainer.java b/modules/azure/src/main/java/org/testcontainers/azure/AzureServiceBusContainer.java
new file mode 100644
index 00000000000..a2322915048
--- /dev/null
+++ b/modules/azure/src/main/java/org/testcontainers/azure/AzureServiceBusContainer.java
@@ -0,0 +1,105 @@
+package org.testcontainers.azure;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.MSSQLServerContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.Transferable;
+import org.testcontainers.utility.DockerImageName;
+import org.testcontainers.utility.LicenseAcceptance;
+
+/**
+ * Testcontainers implementation for Azure Service Bus Emulator.
+ *
+ * Supported image: {@code mcr.microsoft.com/azure-messaging/servicebus-emulator}
+ *
+ * Exposed port: 5672
+ */
+public class AzureServiceBusContainer extends GenericContainer {
+
+ private static final String CONNECTION_STRING_FORMAT =
+ "Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
+
+ private static final int DEFAULT_PORT = 5672;
+
+ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
+ "mcr.microsoft.com/azure-messaging/servicebus-emulator"
+ );
+
+ private MSSQLServerContainer> msSqlServerContainer;
+
+ /**
+ * @param dockerImageName The specified docker image name to run
+ */
+ public AzureServiceBusContainer(final String dockerImageName) {
+ this(DockerImageName.parse(dockerImageName));
+ }
+
+ /**
+ * @param dockerImageName The specified docker image name to run
+ */
+ public AzureServiceBusContainer(final DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
+ withExposedPorts(DEFAULT_PORT);
+ waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
+ }
+
+ /**
+ * Sets the MS SQL Server dependency needed by the Service Bus Container,
+ *
+ * @param msSqlServerContainer The MS SQL Server container used by Service Bus as a dependency
+ * @return this
+ */
+ public AzureServiceBusContainer withMsSqlServerContainer(final MSSQLServerContainer> msSqlServerContainer) {
+ dependsOn(msSqlServerContainer);
+ this.msSqlServerContainer = msSqlServerContainer;
+ return this;
+ }
+
+ /**
+ * Provide the Service Bus configuration JSON.
+ *
+ * @param config The configuration
+ * @return this
+ */
+ public AzureServiceBusContainer withConfig(final Transferable config) {
+ withCopyToContainer(config, "/ServiceBus_Emulator/ConfigFiles/Config.json");
+ return this;
+ }
+
+ /**
+ * Accepts the EULA of the container.
+ *
+ * @return this
+ */
+ public AzureServiceBusContainer acceptLicense() {
+ return withEnv("ACCEPT_EULA", "Y");
+ }
+
+ @Override
+ protected void configure() {
+ if (msSqlServerContainer == null) {
+ throw new IllegalStateException(
+ "The image " +
+ getDockerImageName() +
+ " requires a Microsoft SQL Server container. Please provide one with the withMsSqlServerContainer method!"
+ );
+ }
+ withEnv("SQL_SERVER", msSqlServerContainer.getNetworkAliases().get(0));
+ withEnv("MSSQL_SA_PASSWORD", msSqlServerContainer.getPassword());
+ // If license was not accepted programmatically, check if it was accepted via resource file
+ if (!getEnvMap().containsKey("ACCEPT_EULA")) {
+ LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName());
+ acceptLicense();
+ }
+ }
+
+ /**
+ * Returns the connection string.
+ *
+ * @return connection string
+ */
+ public String getConnectionString() {
+ return String.format(CONNECTION_STRING_FORMAT, getHost(), getMappedPort(DEFAULT_PORT));
+ }
+}
diff --git a/modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java b/modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java
new file mode 100644
index 00000000000..f581de19ab8
--- /dev/null
+++ b/modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java
@@ -0,0 +1,105 @@
+package org.testcontainers.azure;
+
+import com.azure.messaging.servicebus.ServiceBusClientBuilder;
+import com.azure.messaging.servicebus.ServiceBusErrorContext;
+import com.azure.messaging.servicebus.ServiceBusException;
+import com.azure.messaging.servicebus.ServiceBusMessage;
+import com.azure.messaging.servicebus.ServiceBusProcessorClient;
+import com.azure.messaging.servicebus.ServiceBusReceivedMessageContext;
+import com.azure.messaging.servicebus.ServiceBusSenderClient;
+import com.github.dockerjava.api.model.Capability;
+import org.assertj.core.api.Assertions;
+import org.junit.Rule;
+import org.junit.Test;
+import org.testcontainers.containers.MSSQLServerContainer;
+import org.testcontainers.containers.Network;
+import org.testcontainers.utility.MountableFile;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+public class AzureServiceBusContainerTest {
+
+ @Rule
+ // network {
+ public Network network = Network.newNetwork();
+
+ // }
+
+ @Rule
+ // sqlContainer {
+ public MSSQLServerContainer> mssqlServerContainer = new MSSQLServerContainer<>(
+ "mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04"
+ )
+ .acceptLicense()
+ .withPassword("yourStrong(!)Password")
+ .withCreateContainerCmdModifier(cmd -> {
+ cmd.getHostConfig().withCapAdd(Capability.SYS_PTRACE);
+ })
+ .withNetwork(network);
+
+ // }
+
+ @Rule
+ // emulatorContainer {
+ public AzureServiceBusContainer emulator = new AzureServiceBusContainer(
+ "mcr.microsoft.com/azure-messaging/servicebus-emulator:1.0.1"
+ )
+ .acceptLicense()
+ .withConfig(MountableFile.forClasspathResource("/service-bus-config.json"))
+ .withNetwork(network)
+ .withMsSqlServerContainer(mssqlServerContainer);
+
+ // }
+
+ @Test
+ public void testWithClient() {
+ assertThat(emulator.getConnectionString()).startsWith("Endpoint=sb://");
+
+ // senderClient {
+ ServiceBusSenderClient senderClient = new ServiceBusClientBuilder()
+ .connectionString(emulator.getConnectionString())
+ .sender()
+ .queueName("queue.1")
+ .buildClient();
+ // }
+
+ await()
+ .atMost(20, TimeUnit.SECONDS)
+ .ignoreException(ServiceBusException.class)
+ .until(() -> {
+ senderClient.sendMessage(new ServiceBusMessage("Hello, Testcontainers!"));
+ return true;
+ });
+ senderClient.close();
+
+ final List received = new CopyOnWriteArrayList<>();
+ Consumer messageConsumer = m -> {
+ received.add(m.getMessage().getBody().toString());
+ m.complete();
+ };
+ Consumer errorConsumer = e -> Assertions.fail("Unexpected error: " + e);
+ // processorClient {
+ ServiceBusProcessorClient processorClient = new ServiceBusClientBuilder()
+ .connectionString(emulator.getConnectionString())
+ .processor()
+ .queueName("queue.1")
+ .processMessage(messageConsumer)
+ .processError(errorConsumer)
+ .buildProcessorClient();
+ // }
+ processorClient.start();
+
+ await()
+ .atMost(20, TimeUnit.SECONDS)
+ .untilAsserted(() -> {
+ assertThat(received).hasSize(1).containsExactlyInAnyOrder("Hello, Testcontainers!");
+ });
+ processorClient.close();
+ }
+}
diff --git a/modules/azure/src/test/resources/service-bus-config.json b/modules/azure/src/test/resources/service-bus-config.json
new file mode 100644
index 00000000000..18ac2e69c7b
--- /dev/null
+++ b/modules/azure/src/test/resources/service-bus-config.json
@@ -0,0 +1,29 @@
+{
+ "UserConfig": {
+ "Namespaces": [
+ {
+ "Name": "sbemulatorns",
+ "Queues": [
+ {
+ "Name": "queue.1",
+ "Properties": {
+ "DeadLetteringOnMessageExpiration": false,
+ "DefaultMessageTimeToLive": "PT1H",
+ "DuplicateDetectionHistoryTimeWindow": "PT20S",
+ "ForwardDeadLetteredMessagesTo": "",
+ "ForwardTo": "",
+ "LockDuration": "PT1M",
+ "MaxDeliveryCount": 3,
+ "RequiresDuplicateDetection": false,
+ "RequiresSession": false
+ }
+ }
+ ],
+ "Topics": []
+ }
+ ],
+ "Logging": {
+ "Type": "File"
+ }
+ }
+}