Skip to content

Commit fa3e337

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 2707f31 commit fa3e337

File tree

6 files changed

+237
-4
lines changed

6 files changed

+237
-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
@@ -72,6 +73,24 @@ Build Azure Table client:
7273
!!! note
7374
We can use custom credentials the same way as defined in the Blob section.
7475

76+
### Azure Eventhubs Emulator
77+
78+
<!--codeinclude-->
79+
[Configuring the Azure Eventhubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
80+
<!--/codeinclude-->
81+
82+
Start Azure Eventhubs Emulator during a test:
83+
84+
<!--codeinclude-->
85+
[Starting a Azure Eventhubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:emulatorContainer
86+
<!--/codeinclude-->
87+
88+
Configure the consumer and the producer clients:
89+
90+
<!--codeinclude-->
91+
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventhubsEmulatorContainerTest.java) inside_block:createProducerAndConsumer
92+
<!--/codeinclude-->
93+
7594
### CosmosDB
7695

7796
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: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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>AMQP: 5672</li>
16+
* <li>Kafka: 9092</li>
17+
* </ul>
18+
*/
19+
public class AzureEventhubsEmulatorContainer extends GenericContainer<AzureEventhubsEmulatorContainer> {
20+
21+
private static final int DEFAULT_AMQP_PORT = 5672;
22+
23+
private static final int DEFAULT_KAFKA_PORT = 9092;
24+
25+
private static final String CONNECTION_STRING_FORMAT =
26+
"Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
27+
28+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
29+
"mcr.microsoft.com/azure-messaging/eventhubs-emulator"
30+
);
31+
32+
private AzuriteContainer azuriteContainer;
33+
34+
private MountableFile config;
35+
36+
/**
37+
* @param dockerImageName specified docker image name to run
38+
*/
39+
public AzureEventhubsEmulatorContainer(final DockerImageName dockerImageName) {
40+
super(dockerImageName);
41+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
42+
43+
waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
44+
withExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_KAFKA_PORT);
45+
}
46+
47+
@Override
48+
public void start() {
49+
if (this.azuriteContainer == null) {
50+
this.azuriteContainer =
51+
new AzuriteContainer(AzuriteContainer.DEFAULT_IMAGE_NAME.withTag("3.33.0")).withNetwork(getNetwork());
52+
}
53+
this.azuriteContainer.start();
54+
55+
super.start();
56+
}
57+
58+
@Override
59+
public void stop() {
60+
super.stop();
61+
if (this.azuriteContainer != null) {
62+
this.azuriteContainer.stop();
63+
}
64+
}
65+
66+
/**
67+
* Provide the broker configuration to the container.
68+
*
69+
* @param config The file containing the broker configuration
70+
* @return this
71+
*/
72+
public AzureEventhubsEmulatorContainer withConfig(final MountableFile config) {
73+
this.config = config;
74+
return this;
75+
}
76+
77+
/**
78+
* Accepts the EULA of the container.
79+
*
80+
* @return this
81+
*/
82+
public AzureEventhubsEmulatorContainer acceptEula() {
83+
return withEnv("ACCEPT_EULA", "Y");
84+
}
85+
86+
@Override
87+
protected void configure() {
88+
dependsOn(azuriteContainer);
89+
final String azuriteHost = azuriteContainer.getNetworkAliases().get(0);
90+
withEnv("BLOB_SERVER", azuriteHost);
91+
withEnv("METADATA_SERVER", azuriteHost);
92+
if (this.config != null) {
93+
logger().info("Using path for configuration file: '{}'", this.config);
94+
withCopyFileToContainer(this.config, "/Eventhubs_Emulator/ConfigFiles/Config.json");
95+
}
96+
}
97+
98+
/**
99+
* Returns the connection string.
100+
*
101+
* @return connection string
102+
*/
103+
public String getConnectionString() {
104+
return String.format(CONNECTION_STRING_FORMAT, getHost(), getMappedPort(DEFAULT_AMQP_PORT));
105+
}
106+
}

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

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

43-
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
44-
"mcr.microsoft.com/azure-storage/azurite"
45-
);
43+
static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/azure-storage/azurite");
4644

4745
private MountableFile cert = null;
4846

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
49+
// }
50+
51+
@Test
52+
public void testWithEventhubsClient() {
53+
try (
54+
// createProducerAndConsumer {
55+
EventHubProducerClient producer = new EventHubClientBuilder()
56+
.connectionString(emulator.getConnectionString())
57+
.fullyQualifiedNamespace("emulatorNs1")
58+
.eventHubName("eh1")
59+
.buildProducerClient();
60+
EventHubConsumerClient consumer = new EventHubClientBuilder()
61+
.connectionString(emulator.getConnectionString())
62+
.fullyQualifiedNamespace("emulatorNs1")
63+
.eventHubName("eh1")
64+
.consumerGroup("cg1")
65+
.buildConsumerClient()
66+
// }
67+
) {
68+
producer.send(Collections.singletonList(new EventData("test")));
69+
70+
waitAtMost(Duration.ofSeconds(30))
71+
.pollDelay(Duration.ofSeconds(5))
72+
.untilAsserted(() -> {
73+
IterableStream<PartitionEvent> events = consumer.receiveFromPartition(
74+
"0",
75+
1,
76+
EventPosition.earliest(),
77+
Duration.ofSeconds(2)
78+
);
79+
Optional<PartitionEvent> event = events.stream().findFirst();
80+
assertThat(event).isPresent();
81+
assertThat(event.get().getData().getBodyAsString()).isEqualTo("test");
82+
});
83+
}
84+
}
85+
}
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)