diff --git a/docs/modules/azure.md b/docs/modules/azure.md
index 5d3da034f94..4ff726b620b 100644
--- a/docs/modules/azure.md
+++ b/docs/modules/azure.md
@@ -5,11 +5,12 @@ 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` and `CosmosDB` emulators. In order to use them, you should use the following classes:
+Currently, the module supports `Azurite`, `Azure Eventhubs` 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)
+AzureEventhubsEmulatorContainer | [mcr.microsoft.com/azure-messaging/eventhubs-emulator](https://github.com/microsoft/containerregistry)
CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry)
## Usage example
@@ -49,6 +50,24 @@ Build Azure Table client:
[Build Azure Table Service client](../../modules/azure/src/test/java/org/testcontainers/azure/AzuriteContainerTest.java) inside_block:createTableClient
+### Azure Eventhubs Emulator
+
+
+[Configuring the Azure Eventhubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
+
+
+Start Azure Eventhubs Emulator during a test:
+
+
+[Starting a Azure Eventhubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:emulatorContainer
+
+
+Configure the consumer and the producer clients:
+
+
+[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:createProducerAndConsumer
+
+
### CosmosDB
Start Azure CosmosDB Emulator during a test:
diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle
index c6cfb6738d0..3dc97d03fce 100644
--- a/modules/azure/build.gradle
+++ b/modules/azure/build.gradle
@@ -10,4 +10,5 @@ dependencies {
testImplementation 'com.azure:azure-storage-blob:12.29.0'
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'
}
diff --git a/modules/azure/src/main/java/org/testcontainers/azure/AzureEventhubsEmulatorContainer.java b/modules/azure/src/main/java/org/testcontainers/azure/AzureEventhubsEmulatorContainer.java
new file mode 100644
index 00000000000..e27ab121c18
--- /dev/null
+++ b/modules/azure/src/main/java/org/testcontainers/azure/AzureEventhubsEmulatorContainer.java
@@ -0,0 +1,121 @@
+package org.testcontainers.azure;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+import org.testcontainers.utility.MountableFile;
+
+/**
+ * Testcontainers implementation for Azure Eventhubs Emulator.
+ *
+ * Supported image: {@code "mcr.microsoft.com/azure-messaging/eventhubs-emulator"}
+ *
+ * Exposed ports:
+ *
+ * - 5672 (AMQP port)
+ * - 9092 (Kafka port)
+ *
+ */
+public class AzureEventhubsEmulatorContainer extends GenericContainer {
+
+ private static final String DEFAULT_HOST = "127.0.0.1";
+
+ private static final int DEFAULT_AMQP_PORT = 5672;
+
+ private static final int DEFAULT_KAFKA_PORT = 9092;
+
+ private static final String CONNECTION_STRING_FORMAT =
+ "Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
+
+ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
+ "mcr.microsoft.com/azure-messaging/eventhubs-emulator"
+ );
+
+ private String host = DEFAULT_HOST;
+
+ private AzuriteContainer azuriteContainer;
+
+ private MountableFile config;
+
+ /**
+ * @param dockerImageName specified docker image name to run
+ */
+ public AzureEventhubsEmulatorContainer(final DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
+
+ waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
+ withExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_KAFKA_PORT);
+ }
+
+ @Override
+ public void start() {
+ if (azuriteContainer == null) {
+ azuriteContainer =
+ new AzuriteContainer(AzuriteContainer.DEFAULT_IMAGE_NAME.withTag("3.33.0")).withNetwork(getNetwork());
+ }
+ azuriteContainer.start();
+
+ super.start();
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ if (azuriteContainer != null) {
+ azuriteContainer.stop();
+ }
+ }
+
+ /**
+ * Provide the broker configuration to the container.
+ *
+ * @param config The file containing the broker configuration
+ * @return this
+ */
+ public AzureEventhubsEmulatorContainer withConfig(final MountableFile config) {
+ this.config = config;
+ return this;
+ }
+
+ /**
+ * Sets the hostname we want to use to connect to our emulator. (default: {@link #DEFAULT_HOST})
+ *
+ * @param host The host name
+ * @return this
+ */
+ public AzureEventhubsEmulatorContainer withHost(final String host) {
+ this.host = host;
+ return this;
+ }
+
+ /**
+ * Accepts the EULA of the container.
+ *
+ * @return this
+ */
+ public AzureEventhubsEmulatorContainer acceptEula() {
+ return withEnv("ACCEPT_EULA", "Y");
+ }
+
+ @Override
+ protected void configure() {
+ dependsOn(azuriteContainer);
+ final String azuriteHost = azuriteContainer.getNetworkAliases().get(0);
+ withEnv("BLOB_SERVER", azuriteHost);
+ withEnv("METADATA_SERVER", azuriteHost);
+ if (config != null) {
+ logger().info("Using path for configuration file: '{}'", config);
+ withCopyFileToContainer(config, "/Eventhubs_Emulator/ConfigFiles/Config.json");
+ }
+ }
+
+ /**
+ * Returns the connection string.
+ *
+ * @return connection string
+ */
+ public String getConnectionString() {
+ return String.format(CONNECTION_STRING_FORMAT, host, getMappedPort(DEFAULT_AMQP_PORT));
+ }
+}
diff --git a/modules/azure/src/main/java/org/testcontainers/azure/AzuriteContainer.java b/modules/azure/src/main/java/org/testcontainers/azure/AzuriteContainer.java
index b2338a29608..aa8f327f5cd 100644
--- a/modules/azure/src/main/java/org/testcontainers/azure/AzuriteContainer.java
+++ b/modules/azure/src/main/java/org/testcontainers/azure/AzuriteContainer.java
@@ -42,9 +42,7 @@ public class AzuriteContainer extends GenericContainer {
private static final String WELL_KNOWN_ACCOUNT_KEY =
"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
- private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
- "mcr.microsoft.com/azure-storage/azurite"
- );
+ static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/azure-storage/azurite");
private String host = DEFAULT_HOST;
diff --git a/modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java b/modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java
new file mode 100644
index 00000000000..964969b8faf
--- /dev/null
+++ b/modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java
@@ -0,0 +1,86 @@
+package org.testcontainers.azure;
+
+import com.azure.core.util.IterableStream;
+import com.azure.messaging.eventhubs.EventData;
+import com.azure.messaging.eventhubs.EventHubClientBuilder;
+import com.azure.messaging.eventhubs.EventHubConsumerClient;
+import com.azure.messaging.eventhubs.EventHubProducerClient;
+import com.azure.messaging.eventhubs.models.EventPosition;
+import com.azure.messaging.eventhubs.models.PartitionEvent;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.testcontainers.containers.Network;
+import org.testcontainers.utility.DockerImageName;
+import org.testcontainers.utility.MountableFile;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Properties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.waitAtMost;
+
+public class AzureEventhubsEmulatorContainerTest {
+
+ private static Properties originalSystemProperties;
+
+ @BeforeClass
+ public static void captureOriginalSystemProperties() {
+ originalSystemProperties = (Properties) System.getProperties().clone();
+ }
+
+ @AfterClass
+ public static void restoreOriginalSystemProperties() {
+ System.setProperties(originalSystemProperties);
+ }
+
+ @Rule
+ // emulatorContainer {
+ public AzureEventhubsEmulatorContainer emulator = new AzureEventhubsEmulatorContainer(
+ DockerImageName.parse("mcr.microsoft.com/azure-messaging/eventhubs-emulator:2.0.1")
+ )
+ .acceptEula()
+ .withNetwork(Network.newNetwork())
+ .withConfig(MountableFile.forClasspathResource("/eventhubs_config.json"))
+ .withHost("127.0.0.1");
+
+ // }
+
+ @Test
+ public void testWithEventhubsClient() {
+ try (
+ // createProducerAndConsumer {
+ EventHubProducerClient producer = new EventHubClientBuilder()
+ .connectionString(emulator.getConnectionString())
+ .fullyQualifiedNamespace("emulatorNs1")
+ .eventHubName("eh1")
+ .buildProducerClient();
+ EventHubConsumerClient consumer = new EventHubClientBuilder()
+ .connectionString(emulator.getConnectionString())
+ .fullyQualifiedNamespace("emulatorNs1")
+ .eventHubName("eh1")
+ .consumerGroup("cg1")
+ .buildConsumerClient();
+ // }
+ ) {
+ producer.send(Collections.singletonList(new EventData("test")));
+
+ waitAtMost(Duration.ofSeconds(30))
+ .pollDelay(Duration.ofSeconds(5))
+ .untilAsserted(() -> {
+ IterableStream events = consumer.receiveFromPartition(
+ "0",
+ 1,
+ EventPosition.earliest(),
+ Duration.ofSeconds(2)
+ );
+ Optional event = events.stream().findFirst();
+ assertThat(event).isPresent();
+ assertThat(event.get().getData().getBodyAsString()).isEqualTo("test");
+ });
+ }
+ }
+}
diff --git a/modules/azure/src/test/resources/eventhubs_config.json b/modules/azure/src/test/resources/eventhubs_config.json
new file mode 100644
index 00000000000..554be9d7cbf
--- /dev/null
+++ b/modules/azure/src/test/resources/eventhubs_config.json
@@ -0,0 +1,24 @@
+{
+ "UserConfig": {
+ "NamespaceConfig": [
+ {
+ "Type": "EventHub",
+ "Name": "emulatorNs1",
+ "Entities": [
+ {
+ "Name": "eh1",
+ "PartitionCount": "1",
+ "ConsumerGroups": [
+ {
+ "Name": "cg1"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "LoggingConfig": {
+ "Type": "File"
+ }
+ }
+}