Skip to content

Commit f9dc2f7

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 e1dc19f commit f9dc2f7

File tree

5 files changed

+274
-1
lines changed

5 files changed

+274
-1
lines changed

docs/modules/azure.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ 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`, `Azure Event Hubs` and `CosmosDB` emulators. In order to use them, you should use the following classes:
8+
Currently, the module supports `Azurite`, `Azure Event Hubs`, `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)
1313
AzureEventHubsContainer | [mcr.microsoft.com/azure-messaging/eventhubs-emulator](https://github.com/microsoft/containerregistry)
14+
AzureServiceBusEmulatorContainer | [mcr.microsoft.com/azure-messaging/servicebus-emulator](https://github.com/microsoft/containerregistry)
15+
AzureServiceBusContainer | [mcr.microsoft.com/azure-messaging/servicebus-emulator](https://github.com/microsoft/containerregistry)
1416
CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry)
1517

1618
## Usage example
@@ -101,6 +103,38 @@ Configure the consumer and the producer clients:
101103
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:createProducerAndConsumer
102104
<!--/codeinclude-->
103105

106+
### Azure Service Bus Emulator
107+
108+
<!--codeinclude-->
109+
[Configuring the Azure Service Bus Emulator container](../../modules/azure/src/test/resources/service-bus-config.json)
110+
<!--/codeinclude-->
111+
112+
Start Azure Service Bus Emulator during a test:
113+
114+
<!--codeinclude-->
115+
[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:network
116+
<!--/codeinclude-->
117+
118+
<!--codeinclude-->
119+
[Starting a SQL Server container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:sqlContainer
120+
<!--/codeinclude-->
121+
122+
<!--codeinclude-->
123+
[Starting a Service Bus Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:emulatorContainer
124+
<!--/codeinclude-->
125+
126+
#### Using Azure Service Bus clients
127+
128+
Configure the sender and the processor clients:
129+
130+
<!--codeinclude-->
131+
[Configuring the sender client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:senderClient
132+
<!--/codeinclude-->
133+
134+
<!--codeinclude-->
135+
[Configuring the processor client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:processorClient
136+
<!--/codeinclude-->
137+
104138
### CosmosDB
105139

106140
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

@@ -11,4 +12,6 @@ dependencies {
1112
testImplementation 'com.azure:azure-storage-queue:12.24.0'
1213
testImplementation 'com.azure:azure-data-tables:12.5.0'
1314
testImplementation 'com.azure:azure-messaging-eventhubs:5.19.2'
15+
testImplementation 'com.azure:azure-messaging-servicebus:7.17.8'
16+
testImplementation 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre8'
1417
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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 AzureServiceBusContainer extends GenericContainer<AzureServiceBusContainer> {
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 MSSQLServerContainer<?> msSqlServerContainer;
29+
30+
/**
31+
* @param dockerImageName The specified docker image name to run
32+
*/
33+
public AzureServiceBusContainer(final String dockerImageName) {
34+
this(DockerImageName.parse(dockerImageName));
35+
}
36+
37+
/**
38+
* @param dockerImageName The specified docker image name to run
39+
*/
40+
public AzureServiceBusContainer(final DockerImageName dockerImageName) {
41+
super(dockerImageName);
42+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
43+
withExposedPorts(DEFAULT_PORT);
44+
waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
45+
}
46+
47+
/**
48+
* Sets the MS SQL Server dependency needed by the Service Bus Container,
49+
*
50+
* @param msSqlServerContainer The MS SQL Server container used by Service Bus as a dependency
51+
* @return this
52+
*/
53+
public AzureServiceBusContainer withMsSqlServerContainer(final MSSQLServerContainer<?> msSqlServerContainer) {
54+
dependsOn(msSqlServerContainer);
55+
this.msSqlServerContainer = msSqlServerContainer;
56+
return this;
57+
}
58+
59+
/**
60+
* Provide the Service Bus configuration JSON.
61+
*
62+
* @param config The configuration
63+
* @return this
64+
*/
65+
public AzureServiceBusContainer withConfig(final Transferable config) {
66+
withCopyToContainer(config, "/ServiceBus_Emulator/ConfigFiles/Config.json");
67+
return this;
68+
}
69+
70+
/**
71+
* Accepts the EULA of the container.
72+
*
73+
* @return this
74+
*/
75+
public AzureServiceBusContainer acceptLicense() {
76+
return withEnv("ACCEPT_EULA", "Y");
77+
}
78+
79+
@Override
80+
protected void configure() {
81+
if (msSqlServerContainer == null) {
82+
throw new IllegalStateException(
83+
"The image " +
84+
getDockerImageName() +
85+
" requires a Microsoft SQL Server container. Please provide one with the withMsSqlServerContainer method!"
86+
);
87+
}
88+
withEnv("SQL_SERVER", msSqlServerContainer.getNetworkAliases().get(0));
89+
withEnv("MSSQL_SA_PASSWORD", msSqlServerContainer.getPassword());
90+
// If license was not accepted programmatically, check if it was accepted via resource file
91+
if (!getEnvMap().containsKey("ACCEPT_EULA")) {
92+
LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName());
93+
acceptLicense();
94+
}
95+
}
96+
97+
/**
98+
* Returns the connection string.
99+
*
100+
* @return connection string
101+
*/
102+
public String getConnectionString() {
103+
return String.format(CONNECTION_STRING_FORMAT, getHost(), getMappedPort(DEFAULT_PORT));
104+
}
105+
}
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.ServiceBusException;
6+
import com.azure.messaging.servicebus.ServiceBusMessage;
7+
import com.azure.messaging.servicebus.ServiceBusProcessorClient;
8+
import com.azure.messaging.servicebus.ServiceBusReceivedMessageContext;
9+
import com.azure.messaging.servicebus.ServiceBusSenderClient;
10+
import com.github.dockerjava.api.model.Capability;
11+
import org.assertj.core.api.Assertions;
12+
import org.junit.Rule;
13+
import org.junit.Test;
14+
import org.testcontainers.containers.MSSQLServerContainer;
15+
import org.testcontainers.containers.Network;
16+
import org.testcontainers.utility.MountableFile;
17+
18+
import java.util.List;
19+
import java.util.concurrent.CopyOnWriteArrayList;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.function.Consumer;
22+
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
import static org.awaitility.Awaitility.await;
25+
26+
public class AzureServiceBusContainerTest {
27+
28+
@Rule
29+
// network {
30+
public Network network = Network.newNetwork();
31+
// }
32+
33+
@Rule
34+
// sqlContainer {
35+
public MSSQLServerContainer<?> mssqlServerContainer = new MSSQLServerContainer<>(
36+
"mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04"
37+
)
38+
.acceptLicense()
39+
.withPassword("yourStrong(!)Password")
40+
.withCreateContainerCmdModifier(cmd -> {
41+
cmd.getHostConfig().withCapAdd(Capability.SYS_PTRACE);
42+
})
43+
.withNetwork(network);
44+
// }
45+
46+
@Rule
47+
// emulatorContainer {
48+
public AzureServiceBusContainer emulator = new AzureServiceBusContainer(
49+
"mcr.microsoft.com/azure-messaging/servicebus-emulator:1.0.1"
50+
)
51+
.acceptLicense()
52+
.withConfig(MountableFile.forClasspathResource("/service-bus-config.json"))
53+
.withNetwork(network)
54+
.withMsSqlServerContainer(mssqlServerContainer);
55+
// }
56+
57+
@Test
58+
public void testWithClient() {
59+
assertThat(emulator.getConnectionString()).startsWith("Endpoint=sb://");
60+
61+
// senderClient {
62+
ServiceBusSenderClient senderClient = new ServiceBusClientBuilder()
63+
.connectionString(emulator.getConnectionString())
64+
.sender()
65+
.queueName("queue.1")
66+
.buildClient();
67+
// }
68+
69+
await()
70+
.atMost(20, TimeUnit.SECONDS)
71+
.ignoreException(ServiceBusException.class)
72+
.until(() -> {
73+
senderClient.sendMessage(new ServiceBusMessage("Hello, Testcontainers!"));
74+
return true;
75+
});
76+
senderClient.close();
77+
78+
final List<String> received = new CopyOnWriteArrayList<>();
79+
Consumer<ServiceBusReceivedMessageContext> messageConsumer = m -> {
80+
received.add(m.getMessage().getBody().toString());
81+
m.complete();
82+
};
83+
Consumer<ServiceBusErrorContext> errorConsumer = e -> Assertions.fail("Unexpected error: " + e);
84+
// processorClient {
85+
ServiceBusProcessorClient processorClient = new ServiceBusClientBuilder()
86+
.connectionString(emulator.getConnectionString())
87+
.processor()
88+
.queueName("queue.1")
89+
.processMessage(messageConsumer)
90+
.processError(errorConsumer)
91+
.buildProcessorClient();
92+
// }
93+
processorClient.start();
94+
95+
await()
96+
.atMost(20, TimeUnit.SECONDS)
97+
.untilAsserted(() -> {
98+
assertThat(received).hasSize(1).containsExactlyInAnyOrder("Hello, Testcontainers!");
99+
});
100+
processorClient.close();
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)