Skip to content

Commit 4c40dfd

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 4c40dfd

File tree

6 files changed

+253
-4
lines changed

6 files changed

+253
-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: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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 =
55+
new AzuriteContainer(AzuriteContainer.DEFAULT_IMAGE_NAME.withTag("3.33.0")).withNetwork(getNetwork());
56+
}
57+
azuriteContainer.start();
58+
59+
super.start();
60+
}
61+
62+
@Override
63+
public void stop() {
64+
super.stop();
65+
if (azuriteContainer != null) {
66+
azuriteContainer.stop();
67+
}
68+
}
69+
70+
/**
71+
* Provide the broker configuration to the container.
72+
*
73+
* @param config The file containing the broker configuration
74+
* @return this
75+
*/
76+
public AzureEventhubsEmulatorContainer withConfig(final MountableFile config) {
77+
this.config = config;
78+
return this;
79+
}
80+
81+
/**
82+
* Sets the hostname we want to use to connect to our emulator. (default: {@link #DEFAULT_HOST})
83+
*
84+
* @param host The host name
85+
* @return this
86+
*/
87+
public AzureEventhubsEmulatorContainer withHost(final String host) {
88+
this.host = host;
89+
return this;
90+
}
91+
92+
/**
93+
* Accepts the EULA of the container.
94+
*
95+
* @return this
96+
*/
97+
public AzureEventhubsEmulatorContainer acceptEula() {
98+
return withEnv("ACCEPT_EULA", "Y");
99+
}
100+
101+
@Override
102+
protected void configure() {
103+
dependsOn(azuriteContainer);
104+
final String azuriteHost = azuriteContainer.getNetworkAliases().get(0);
105+
withEnv("BLOB_SERVER", azuriteHost);
106+
withEnv("METADATA_SERVER", azuriteHost);
107+
if (config != null) {
108+
logger().info("Using path for configuration file: '{}'", config);
109+
withCopyFileToContainer(config, "/Eventhubs_Emulator/ConfigFiles/Config.json");
110+
}
111+
}
112+
113+
/**
114+
* Returns the connection string.
115+
*
116+
* @return connection string
117+
*/
118+
public String getConnectionString() {
119+
return String.format(CONNECTION_STRING_FORMAT, host, getMappedPort(DEFAULT_AMQP_PORT));
120+
}
121+
}

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)