Skip to content

Commit 41ee74d

Browse files
committed
Add Azure Eventhubs Emulator container to Azure module
- Add Azure Eventhubs Emulator container - Implement new test case - Update Azure documentation Signed-off-by: Esta Nagy <[email protected]>
1 parent 2c83a05 commit 41ee74d

File tree

6 files changed

+252
-4
lines changed

6 files changed

+252
-4
lines changed

docs/modules/azure.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ This module is INCUBATING. While it is ready for use and operational in the curr
55

66
Testcontainers module for the Microsoft Azure's [SDK](https://github.com/Azure/azure-sdk-for-java).
77

8-
Currently, the module supports `Azurite` and `CosmosDB` emulators. In order to use them, you should use the following classes:
8+
Currently, the module supports `Azurite`, `Azure Eventhubs` and `CosmosDB` emulators. In order to use them, you should use the following classes:
99

1010
Class | Container Image
1111
-|-
1212
AzuriteContainer | [mcr.microsoft.com/azure-storage/azurite](https://github.com/microsoft/containerregistry)
13+
AzureEventhubsEmulatorContainer | [mcr.microsoft.com/azure-messaging/eventhubs-emulator](https://github.com/microsoft/containerregistry)
1314
CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry)
1415

1516
## Usage example
@@ -49,6 +50,24 @@ Build Azure Table client:
4950
[Build Azure Table Service client](../../modules/azure/src/test/java/org/testcontainers/azure/AzuriteContainerTest.java) inside_block:createTableClient
5051
<!--/codeinclude-->
5152

53+
### Azure Eventhubs Emulator
54+
55+
<!--codeinclude-->
56+
[Configuring the Azure Eventhubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
57+
<!--/codeinclude-->
58+
59+
Start Azure Eventhubs Emulator during a test:
60+
61+
<!--codeinclude-->
62+
[Starting a Azure Eventhubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:emulatorContainer
63+
<!--/codeinclude-->
64+
65+
Configure the consumer and the producer clients:
66+
67+
<!--codeinclude-->
68+
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:createProducerAndConsumer
69+
<!--/codeinclude-->
70+
5271
### CosmosDB
5372

5473
Start Azure CosmosDB Emulator during a test:

modules/azure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ dependencies {
1010
testImplementation 'com.azure:azure-storage-blob:12.29.0'
1111
testImplementation 'com.azure:azure-storage-queue:12.24.0'
1212
testImplementation 'com.azure:azure-data-tables:12.5.0'
13+
testImplementation 'com.azure:azure-messaging-eventhubs:5.19.2'
1314
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package org.testcontainers.azure;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.wait.strategy.Wait;
5+
import org.testcontainers.utility.DockerImageName;
6+
import org.testcontainers.utility.MountableFile;
7+
8+
/**
9+
* Testcontainers implementation for Azure Eventhubs Emulator.
10+
* <p>
11+
* Supported image: {@code "mcr.microsoft.com/azure-messaging/eventhubs-emulator"}
12+
* <p>
13+
* Exposed ports:
14+
* <ul>
15+
* <li>5672 (AMQP port)</li>
16+
* <li>9092 (Kafka port)</li>
17+
* </ul>
18+
*/
19+
public class AzureEventhubsEmulatorContainer extends GenericContainer<AzureEventhubsEmulatorContainer> {
20+
21+
private static final String DEFAULT_HOST = "127.0.0.1";
22+
23+
private static final int DEFAULT_AMQP_PORT = 5672;
24+
25+
private static final int DEFAULT_KAFKA_PORT = 9092;
26+
27+
private static final String CONNECTION_STRING_FORMAT =
28+
"Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
29+
30+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
31+
"mcr.microsoft.com/azure-messaging/eventhubs-emulator"
32+
);
33+
34+
private String host = DEFAULT_HOST;
35+
36+
private AzuriteContainer azuriteContainer;
37+
38+
private MountableFile config;
39+
40+
/**
41+
* @param dockerImageName specified docker image name to run
42+
*/
43+
public AzureEventhubsEmulatorContainer(final DockerImageName dockerImageName) {
44+
super(dockerImageName);
45+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
46+
47+
waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
48+
withExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_KAFKA_PORT);
49+
}
50+
51+
@Override
52+
public void start() {
53+
if (azuriteContainer == null) {
54+
azuriteContainer = new AzuriteContainer(AzuriteContainer.DEFAULT_IMAGE_NAME).withNetwork(getNetwork());
55+
}
56+
azuriteContainer.start();
57+
58+
super.start();
59+
}
60+
61+
@Override
62+
public void stop() {
63+
super.stop();
64+
if (azuriteContainer != null) {
65+
azuriteContainer.stop();
66+
}
67+
}
68+
69+
/**
70+
* Provide the broker configuration to the container.
71+
*
72+
* @param config The file containing the broker configuration
73+
* @return this
74+
*/
75+
public AzureEventhubsEmulatorContainer withConfig(final MountableFile config) {
76+
this.config = config;
77+
return this;
78+
}
79+
80+
/**
81+
* Sets the hostname we want to use to connect to our emulator. (default: {@link #DEFAULT_HOST})
82+
*
83+
* @param host The host name
84+
* @return this
85+
*/
86+
public AzureEventhubsEmulatorContainer withHost(final String host) {
87+
this.host = host;
88+
return this;
89+
}
90+
91+
/**
92+
* Accepts the EULA of the container.
93+
*
94+
* @return this
95+
*/
96+
public AzureEventhubsEmulatorContainer acceptEula() {
97+
return withEnv("ACCEPT_EULA", "Y");
98+
}
99+
100+
@Override
101+
protected void configure() {
102+
dependsOn(azuriteContainer);
103+
final String azuriteHost = azuriteContainer.getNetworkAliases().get(0);
104+
withEnv("BLOB_SERVER", azuriteHost);
105+
withEnv("METADATA_SERVER", azuriteHost);
106+
if (config != null) {
107+
logger().info("Using path for configuration file: '{}'", config);
108+
withCopyFileToContainer(config, "/Eventhubs_Emulator/ConfigFiles/Config.json");
109+
}
110+
}
111+
112+
/**
113+
* Returns the connection string.
114+
*
115+
* @return connection string
116+
*/
117+
public String getConnectionString() {
118+
return String.format(CONNECTION_STRING_FORMAT, host, getMappedPort(DEFAULT_AMQP_PORT));
119+
}
120+
}

modules/azure/src/main/java/org/testcontainers/azure/AzuriteContainer.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ public class AzuriteContainer extends GenericContainer<AzuriteContainer> {
4242
private static final String WELL_KNOWN_ACCOUNT_KEY =
4343
"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
4444

45-
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
46-
"mcr.microsoft.com/azure-storage/azurite"
47-
);
45+
static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/azure-storage/azurite");
4846

4947
private String host = DEFAULT_HOST;
5048

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.testcontainers.azure;
2+
3+
import com.azure.core.util.IterableStream;
4+
import com.azure.messaging.eventhubs.EventData;
5+
import com.azure.messaging.eventhubs.EventHubClientBuilder;
6+
import com.azure.messaging.eventhubs.EventHubConsumerClient;
7+
import com.azure.messaging.eventhubs.EventHubProducerClient;
8+
import com.azure.messaging.eventhubs.models.EventPosition;
9+
import com.azure.messaging.eventhubs.models.PartitionEvent;
10+
import org.junit.AfterClass;
11+
import org.junit.BeforeClass;
12+
import org.junit.Rule;
13+
import org.junit.Test;
14+
import org.testcontainers.containers.Network;
15+
import org.testcontainers.utility.DockerImageName;
16+
import org.testcontainers.utility.MountableFile;
17+
18+
import java.time.Duration;
19+
import java.util.Collections;
20+
import java.util.Optional;
21+
import java.util.Properties;
22+
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
import static org.awaitility.Awaitility.waitAtMost;
25+
26+
public class AzureEventhubsEmulatorContainerTest {
27+
28+
private static Properties originalSystemProperties;
29+
30+
@BeforeClass
31+
public static void captureOriginalSystemProperties() {
32+
originalSystemProperties = (Properties) System.getProperties().clone();
33+
}
34+
35+
@AfterClass
36+
public static void restoreOriginalSystemProperties() {
37+
System.setProperties(originalSystemProperties);
38+
}
39+
40+
@Rule
41+
// emulatorContainer {
42+
public AzureEventhubsEmulatorContainer emulator = new AzureEventhubsEmulatorContainer(
43+
DockerImageName.parse("mcr.microsoft.com/azure-messaging/eventhubs-emulator:2.0.1")
44+
)
45+
.acceptEula()
46+
.withNetwork(Network.newNetwork())
47+
.withConfig(MountableFile.forClasspathResource("/eventhubs_config.json"))
48+
.withHost("127.0.0.1");
49+
50+
// }
51+
52+
@Test
53+
public void testWithEventhubsClient() {
54+
try (
55+
// createProducerAndConsumer {
56+
EventHubProducerClient producer = new EventHubClientBuilder()
57+
.connectionString(emulator.getConnectionString())
58+
.fullyQualifiedNamespace("emulatorNs1")
59+
.eventHubName("eh1")
60+
.buildProducerClient();
61+
EventHubConsumerClient consumer = new EventHubClientBuilder()
62+
.connectionString(emulator.getConnectionString())
63+
.fullyQualifiedNamespace("emulatorNs1")
64+
.eventHubName("eh1")
65+
.consumerGroup("cg1")
66+
.buildConsumerClient();
67+
// }
68+
) {
69+
producer.send(Collections.singletonList(new EventData("test")));
70+
71+
waitAtMost(Duration.ofSeconds(30))
72+
.pollDelay(Duration.ofSeconds(5))
73+
.untilAsserted(() -> {
74+
IterableStream<PartitionEvent> events = consumer.receiveFromPartition(
75+
"0",
76+
1,
77+
EventPosition.earliest(),
78+
Duration.ofSeconds(2)
79+
);
80+
Optional<PartitionEvent> event = events.stream().findFirst();
81+
assertThat(event).isPresent();
82+
assertThat(event.get().getData().getBodyAsString()).isEqualTo("test");
83+
});
84+
}
85+
}
86+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"UserConfig": {
3+
"NamespaceConfig": [
4+
{
5+
"Type": "EventHub",
6+
"Name": "emulatorNs1",
7+
"Entities": [
8+
{
9+
"Name": "eh1",
10+
"PartitionCount": "1",
11+
"ConsumerGroups": [
12+
{
13+
"Name": "cg1"
14+
}
15+
]
16+
}
17+
]
18+
}
19+
],
20+
"LoggingConfig": {
21+
"Type": "File"
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)