Skip to content

Commit e1dc19f

Browse files
Add AzureEventHubsContainer (#9665)
Signed-off-by: Esta Nagy <[email protected]> Co-authored-by: Eddú Meléndez <[email protected]>
1 parent 92a475d commit e1dc19f

File tree

5 files changed

+247
-1
lines changed

5 files changed

+247
-1
lines changed

docs/modules/azure.md

Lines changed: 30 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 Event Hubs` 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+
AzureEventHubsContainer | [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,34 @@ Build Azure Table client:
7273
!!! note
7374
We can use custom credentials the same way as defined in the Blob section.
7475

76+
### Azure Event Hubs Emulator
77+
78+
<!--codeinclude-->
79+
[Configuring the Azure Event Hubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
80+
<!--/codeinclude-->
81+
82+
Start Azure Event Hubs Emulator during a test:
83+
84+
<!--codeinclude-->
85+
[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:network
86+
<!--/codeinclude-->
87+
88+
<!--codeinclude-->
89+
[Starting an Azurite container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:azuriteContainer
90+
<!--/codeinclude-->
91+
92+
<!--codeinclude-->
93+
[Starting an Azure Event Hubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:emulatorContainer
94+
<!--/codeinclude-->
95+
96+
#### Using Azure Event Hubs clients
97+
98+
Configure the consumer and the producer clients:
99+
100+
<!--codeinclude-->
101+
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:createProducerAndConsumer
102+
<!--/codeinclude-->
103+
75104
### CosmosDB
76105

77106
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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package org.testcontainers.azure;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.wait.strategy.Wait;
5+
import org.testcontainers.images.builder.Transferable;
6+
import org.testcontainers.utility.DockerImageName;
7+
import org.testcontainers.utility.LicenseAcceptance;
8+
9+
/**
10+
* Testcontainers implementation for Azure Eventhubs Emulator.
11+
* <p>
12+
* Supported image: {@code "mcr.microsoft.com/azure-messaging/eventhubs-emulator"}
13+
* <p>
14+
* Exposed ports:
15+
* <ul>
16+
* <li>AMQP: 5672</li>
17+
* </ul>
18+
*/
19+
public class AzureEventHubsContainer extends GenericContainer<AzureEventHubsContainer> {
20+
21+
private static final int DEFAULT_AMQP_PORT = 5672;
22+
23+
private static final String CONNECTION_STRING_FORMAT =
24+
"Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
25+
26+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
27+
"mcr.microsoft.com/azure-messaging/eventhubs-emulator"
28+
);
29+
30+
private AzuriteContainer azuriteContainer;
31+
32+
/**
33+
* @param dockerImageName specified docker image name to run
34+
*/
35+
public AzureEventHubsContainer(final String dockerImageName) {
36+
this(DockerImageName.parse(dockerImageName));
37+
}
38+
39+
/**
40+
* @param dockerImageName specified docker image name to run
41+
*/
42+
public AzureEventHubsContainer(final DockerImageName dockerImageName) {
43+
super(dockerImageName);
44+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
45+
waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
46+
withExposedPorts(DEFAULT_AMQP_PORT);
47+
}
48+
49+
/**
50+
* * Sets the Azurite dependency needed by the Event Hubs Container,
51+
*
52+
* @param azuriteContainer The Azurite container used by Event HUbs as a dependency
53+
* @return this
54+
*/
55+
public AzureEventHubsContainer withAzuriteContainer(final AzuriteContainer azuriteContainer) {
56+
this.azuriteContainer = azuriteContainer;
57+
dependsOn(this.azuriteContainer);
58+
return this;
59+
}
60+
61+
/**
62+
* Provide the broker configuration to the container.
63+
*
64+
* @param config The file containing the broker configuration
65+
* @return this
66+
*/
67+
public AzureEventHubsContainer withConfig(final Transferable config) {
68+
withCopyToContainer(config, "/Eventhubs_Emulator/ConfigFiles/Config.json");
69+
return this;
70+
}
71+
72+
/**
73+
* Accepts the EULA of the container.
74+
*
75+
* @return this
76+
*/
77+
public AzureEventHubsContainer acceptLicense() {
78+
withEnv("ACCEPT_EULA", "Y");
79+
return this;
80+
}
81+
82+
@Override
83+
protected void configure() {
84+
if (azuriteContainer == null) {
85+
throw new IllegalStateException(
86+
"The image " +
87+
getDockerImageName() +
88+
" requires an Azurite container. Please provide one with the withAzuriteContainer method!"
89+
);
90+
}
91+
final String azuriteHost = azuriteContainer.getNetworkAliases().get(0);
92+
withEnv("BLOB_SERVER", azuriteHost);
93+
withEnv("METADATA_SERVER", azuriteHost);
94+
// If license was not accepted programmatically, check if it was accepted via resource file
95+
if (!getEnvMap().containsKey("ACCEPT_EULA")) {
96+
LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName());
97+
acceptLicense();
98+
}
99+
}
100+
101+
/**
102+
* Returns the connection string.
103+
*
104+
* @return connection string
105+
*/
106+
public String getConnectionString() {
107+
return String.format(CONNECTION_STRING_FORMAT, getHost(), getMappedPort(DEFAULT_AMQP_PORT));
108+
}
109+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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.Rule;
11+
import org.junit.Test;
12+
import org.testcontainers.containers.Network;
13+
import org.testcontainers.utility.MountableFile;
14+
15+
import java.time.Duration;
16+
import java.util.Collections;
17+
import java.util.Optional;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.awaitility.Awaitility.waitAtMost;
21+
22+
public class AzureEventHubsContainerTest {
23+
24+
@Rule
25+
// network {
26+
public Network network = Network.newNetwork();
27+
28+
// }
29+
30+
@Rule
31+
// azuriteContainer {
32+
public AzuriteContainer azuriteContainer = new AzuriteContainer("mcr.microsoft.com/azure-storage/azurite:3.33.0")
33+
.withNetwork(network);
34+
35+
// }
36+
37+
@Rule
38+
// emulatorContainer {
39+
public AzureEventHubsContainer emulator = new AzureEventHubsContainer(
40+
"mcr.microsoft.com/azure-messaging/eventhubs-emulator:2.0.1"
41+
)
42+
.acceptLicense()
43+
.withNetwork(network)
44+
.withConfig(MountableFile.forClasspathResource("/eventhubs_config.json"))
45+
.withAzuriteContainer(azuriteContainer);
46+
47+
// }
48+
49+
@Test
50+
public void testWithEventHubsClient() {
51+
try (
52+
// createProducerAndConsumer {
53+
EventHubProducerClient producer = new EventHubClientBuilder()
54+
.connectionString(emulator.getConnectionString())
55+
.fullyQualifiedNamespace("emulatorNs1")
56+
.eventHubName("eh1")
57+
.buildProducerClient();
58+
EventHubConsumerClient consumer = new EventHubClientBuilder()
59+
.connectionString(emulator.getConnectionString())
60+
.fullyQualifiedNamespace("emulatorNs1")
61+
.eventHubName("eh1")
62+
.consumerGroup("cg1")
63+
.buildConsumerClient()
64+
// }
65+
) {
66+
producer.send(Collections.singletonList(new EventData("test")));
67+
68+
waitAtMost(Duration.ofSeconds(30))
69+
.pollDelay(Duration.ofSeconds(5))
70+
.untilAsserted(() -> {
71+
IterableStream<PartitionEvent> events = consumer.receiveFromPartition(
72+
"0",
73+
1,
74+
EventPosition.earliest(),
75+
Duration.ofSeconds(2)
76+
);
77+
Optional<PartitionEvent> event = events.stream().findFirst();
78+
assertThat(event).isPresent();
79+
assertThat(event.get().getData().getBodyAsString()).isEqualTo("test");
80+
});
81+
}
82+
}
83+
}
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)