Skip to content

Commit

Permalink
Add Azure Eventhubs Emulator container to Azure module
Browse files Browse the repository at this point in the history
- Add Azure Eventhubs Emulator container
- Implement new test case
- Update Azure documentation

Signed-off-by: Esta Nagy <[email protected]>
  • Loading branch information
nagyesta committed Dec 24, 2024
1 parent 2c83a05 commit 41ee74d
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 4 deletions.
21 changes: 20 additions & 1 deletion docs/modules/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
<!--/codeinclude-->

### Azure Eventhubs Emulator

<!--codeinclude-->
[Configuring the Azure Eventhubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
<!--/codeinclude-->

Start Azure Eventhubs Emulator during a test:

<!--codeinclude-->
[Starting a Azure Eventhubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:emulatorContainer
<!--/codeinclude-->

Configure the consumer and the producer clients:

<!--codeinclude-->
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:createProducerAndConsumer
<!--/codeinclude-->

### CosmosDB

Start Azure CosmosDB Emulator during a test:
Expand Down
1 change: 1 addition & 0 deletions modules/azure/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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.
* <p>
* Supported image: {@code "mcr.microsoft.com/azure-messaging/eventhubs-emulator"}
* <p>
* Exposed ports:
* <ul>
* <li>5672 (AMQP port)</li>
* <li>9092 (Kafka port)</li>
* </ul>
*/
public class AzureEventhubsEmulatorContainer extends GenericContainer<AzureEventhubsEmulatorContainer> {

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).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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ public class AzuriteContainer extends GenericContainer<AzuriteContainer> {
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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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<PartitionEvent> events = consumer.receiveFromPartition(
"0",
1,
EventPosition.earliest(),
Duration.ofSeconds(2)
);
Optional<PartitionEvent> event = events.stream().findFirst();
assertThat(event).isPresent();
assertThat(event.get().getData().getBodyAsString()).isEqualTo("test");
});
}
}
}
24 changes: 24 additions & 0 deletions modules/azure/src/test/resources/eventhubs_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"UserConfig": {
"NamespaceConfig": [
{
"Type": "EventHub",
"Name": "emulatorNs1",
"Entities": [
{
"Name": "eh1",
"PartitionCount": "1",
"ConsumerGroups": [
{
"Name": "cg1"
}
]
}
]
}
],
"LoggingConfig": {
"Type": "File"
}
}
}

0 comments on commit 41ee74d

Please sign in to comment.