Skip to content

Commit 5246f6d

Browse files
committed
Add Azure Service Bus Emulator container to Azure module
- Implements new Service Bus Container - Adds new test case for Service Bus - Updates the Azure documentation Signed-off-by: Esta Nagy <[email protected]>
1 parent 2707f31 commit 5246f6d

File tree

5 files changed

+253
-1
lines changed

5 files changed

+253
-1
lines changed

docs/modules/azure.md

Lines changed: 34 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 Service Bus` 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+
AzureServiceBusEmulatorContainer | [mcr.microsoft.com/azure-messaging/servicebus-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,38 @@ Build Azure Table client:
7273
!!! note
7374
We can use custom credentials the same way as defined in the Blob section.
7475

76+
### Azure Service Bus Emulator
77+
78+
<!--codeinclude-->
79+
[Configuring the Azure Service Bus Emulator container](../../modules/azure/src/test/resources/service-bus-config.json)
80+
<!--/codeinclude-->
81+
82+
Start Azure Service Bus Emulator during a test:
83+
84+
<!--codeinclude-->
85+
[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusEmulatorContainerTest.java) inside_block:network
86+
<!--/codeinclude-->
87+
88+
<!--codeinclude-->
89+
[Starting a SQL Server container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusEmulatorContainerTest.java) inside_block:sqlContainer
90+
<!--/codeinclude-->
91+
92+
<!--codeinclude-->
93+
[Starting a Service Bus Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusEmulatorContainerTest.java) inside_block:emulatorContainer
94+
<!--/codeinclude-->
95+
96+
#### Using Azure Service Bus clients
97+
98+
Configure the sender and the processor clients:
99+
100+
<!--codeinclude-->
101+
[Configuring the sender client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusEmulatorContainerTest.java) inside_block:senderClient
102+
<!--/codeinclude-->
103+
104+
<!--codeinclude-->
105+
[Configuring the processor client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusEmulatorContainerTest.java) inside_block:processorClient
106+
<!--/codeinclude-->
107+
75108
### CosmosDB
76109

77110
Start Azure CosmosDB Emulator during a test:

modules/azure/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ description = "Testcontainers :: Azure"
22

33
dependencies {
44
api project(':testcontainers')
5+
api project(':mssqlserver')
56
// TODO use JDK's HTTP client and/or Apache HttpClient5
67
shaded 'com.squareup.okhttp3:okhttp:4.12.0'
78

@@ -10,4 +11,6 @@ dependencies {
1011
testImplementation 'com.azure:azure-storage-blob:12.29.0'
1112
testImplementation 'com.azure:azure-storage-queue:12.24.0'
1213
testImplementation 'com.azure:azure-data-tables:12.5.0'
14+
testImplementation 'com.azure:azure-messaging-servicebus:7.17.8'
15+
testImplementation 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre8'
1316
}
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 org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.MSSQLServerContainer;
5+
import org.testcontainers.containers.wait.strategy.Wait;
6+
import org.testcontainers.images.builder.Transferable;
7+
import org.testcontainers.utility.DockerImageName;
8+
import org.testcontainers.utility.LicenseAcceptance;
9+
10+
/**
11+
* Testcontainers implementation for Azure Service Bus Emulator.
12+
* <p>
13+
* Supported image: {@code mcr.microsoft.com/azure-messaging/servicebus-emulator}
14+
* <p>
15+
* Exposed port: 5672
16+
*/
17+
public class AzureServiceBusEmulatorContainer extends GenericContainer<AzureServiceBusEmulatorContainer> {
18+
19+
private static final String CONNECTION_STRING_FORMAT =
20+
"Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
21+
22+
private static final int DEFAULT_PORT = 5672;
23+
24+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
25+
"mcr.microsoft.com/azure-messaging/servicebus-emulator"
26+
);
27+
28+
private final MSSQLServerContainer<?> mssqlServerContainer;
29+
30+
/**
31+
* @param dockerImageName The specified docker image name to run
32+
* @param mssqlServerContainer The MS SQL Server container used by Service Bus as a dependency
33+
*/
34+
public AzureServiceBusEmulatorContainer(
35+
final DockerImageName dockerImageName,
36+
final MSSQLServerContainer<?> mssqlServerContainer
37+
) {
38+
super(dockerImageName);
39+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
40+
this.mssqlServerContainer = mssqlServerContainer;
41+
dependsOn(mssqlServerContainer);
42+
withExposedPorts(DEFAULT_PORT);
43+
waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
44+
}
45+
46+
/**
47+
* Provide the Service Bus configuration JSON.
48+
*
49+
* @param config The configuration
50+
* @return this
51+
*/
52+
public AzureServiceBusEmulatorContainer withConfig(final Transferable config) {
53+
withCopyToContainer(config, "/ServiceBus_Emulator/ConfigFiles/Config.json");
54+
return this;
55+
}
56+
57+
/**
58+
* Accepts the EULA of the container.
59+
*
60+
* @return this
61+
*/
62+
public AzureServiceBusEmulatorContainer acceptLicense() {
63+
return withEnv("ACCEPT_EULA", "Y");
64+
}
65+
66+
@Override
67+
protected void configure() {
68+
withEnv("SQL_SERVER", mssqlServerContainer.getNetworkAliases().get(0));
69+
withEnv("MSSQL_SA_PASSWORD", mssqlServerContainer.getPassword());
70+
// If license was not accepted programmatically, check if it was accepted via resource file
71+
if (!getEnvMap().containsKey("ACCEPT_EULA")) {
72+
LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName());
73+
acceptLicense();
74+
}
75+
}
76+
77+
/**
78+
* Returns the connection string.
79+
*
80+
* @return connection string
81+
*/
82+
public String getConnectionString() {
83+
return String.format(CONNECTION_STRING_FORMAT, getHost(), getMappedPort(DEFAULT_PORT));
84+
}
85+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package org.testcontainers.azure;
2+
3+
import com.azure.messaging.servicebus.ServiceBusClientBuilder;
4+
import com.azure.messaging.servicebus.ServiceBusErrorContext;
5+
import com.azure.messaging.servicebus.ServiceBusMessage;
6+
import com.azure.messaging.servicebus.ServiceBusProcessorClient;
7+
import com.azure.messaging.servicebus.ServiceBusReceivedMessage;
8+
import com.azure.messaging.servicebus.ServiceBusReceivedMessageContext;
9+
import com.azure.messaging.servicebus.ServiceBusSenderClient;
10+
import com.azure.messaging.servicebus.ServiceBusTransactionContext;
11+
import com.github.dockerjava.api.model.Capability;
12+
import org.assertj.core.api.Assertions;
13+
import org.junit.Rule;
14+
import org.junit.Test;
15+
import org.testcontainers.containers.MSSQLServerContainer;
16+
import org.testcontainers.containers.Network;
17+
import org.testcontainers.utility.DockerImageName;
18+
import org.testcontainers.utility.MountableFile;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.concurrent.TimeUnit;
23+
import java.util.function.Consumer;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
public class AzureServiceBusEmulatorContainerTest {
28+
29+
@Rule
30+
// network {
31+
public Network network = Network.newNetwork();
32+
33+
// }
34+
35+
@Rule
36+
// sqlContainer {
37+
public MSSQLServerContainer<?> mssqlServerContainer = new MSSQLServerContainer<>(
38+
"mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04"
39+
)
40+
.acceptLicense()
41+
.withPassword("yourStrong(!)Password")
42+
.withCreateContainerCmdModifier(cmd -> {
43+
cmd.getHostConfig().withCapAdd(Capability.SYS_PTRACE);
44+
})
45+
.withNetwork(network);
46+
47+
// }
48+
49+
@Rule
50+
// emulatorContainer {
51+
public AzureServiceBusEmulatorContainer emulator = new AzureServiceBusEmulatorContainer(
52+
DockerImageName.parse("mcr.microsoft.com/azure-messaging/servicebus-emulator:1.0.1"),
53+
mssqlServerContainer
54+
)
55+
.acceptLicense()
56+
.withConfig(MountableFile.forClasspathResource("/service-bus-config.json"))
57+
.withNetwork(network);
58+
59+
// }
60+
61+
@Test
62+
public void testWithClient() throws InterruptedException {
63+
assertThat(emulator.getConnectionString()).startsWith("Endpoint=sb://");
64+
65+
// senderClient {
66+
ServiceBusSenderClient senderClient = new ServiceBusClientBuilder()
67+
.connectionString(emulator.getConnectionString())
68+
.sender()
69+
.queueName("queue.1")
70+
.buildClient();
71+
// }
72+
73+
TimeUnit.SECONDS.sleep(5);
74+
ServiceBusTransactionContext transaction = senderClient.createTransaction();
75+
senderClient.sendMessage(new ServiceBusMessage("Hello, Testcontainers!"), transaction);
76+
senderClient.commitTransaction(transaction);
77+
senderClient.close();
78+
79+
TimeUnit.SECONDS.sleep(5);
80+
final List<ServiceBusReceivedMessage> received = new ArrayList<>();
81+
Consumer<ServiceBusReceivedMessageContext> messageConsumer = m -> {
82+
received.add(m.getMessage());
83+
m.complete();
84+
};
85+
Consumer<ServiceBusErrorContext> errorConsumer = e -> Assertions.fail("Unexpected error: " + e);
86+
// processorClient {
87+
ServiceBusProcessorClient processorClient = new ServiceBusClientBuilder()
88+
.connectionString(emulator.getConnectionString())
89+
.processor()
90+
.queueName("queue.1")
91+
.processMessage(messageConsumer)
92+
.processError(errorConsumer)
93+
.buildProcessorClient();
94+
// }
95+
processorClient.start();
96+
97+
TimeUnit.SECONDS.sleep(10);
98+
processorClient.close();
99+
assertThat(received).hasSize(1);
100+
assertThat(received.get(0).getBody().toString()).isEqualTo("Hello, Testcontainers!");
101+
}
102+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"UserConfig": {
3+
"Namespaces": [
4+
{
5+
"Name": "sbemulatorns",
6+
"Queues": [
7+
{
8+
"Name": "queue.1",
9+
"Properties": {
10+
"DeadLetteringOnMessageExpiration": false,
11+
"DefaultMessageTimeToLive": "PT1H",
12+
"DuplicateDetectionHistoryTimeWindow": "PT20S",
13+
"ForwardDeadLetteredMessagesTo": "",
14+
"ForwardTo": "",
15+
"LockDuration": "PT1M",
16+
"MaxDeliveryCount": 3,
17+
"RequiresDuplicateDetection": false,
18+
"RequiresSession": false
19+
}
20+
}
21+
],
22+
"Topics": []
23+
}
24+
],
25+
"Logging": {
26+
"Type": "File"
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)