Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
deae913
Use AzuriteContainer from testcontainers-azure module
May 6, 2026
442d698
Migrate embedded-keycloak to use dasniko/testcontainers-keycloak library
May 6, 2026
bfc315c
Update to use Testcontainers native Kafka modules and remove embedded…
May 6, 2026
da57753
Migrate embedded-minio to use MinIOContainer API and remove custom wa…
May 7, 2026
fac4ff1
Use VaultContainer's withInitCommand for secret initialization and re…
May 7, 2026
0904899
Migrate embedded-google-pubsub to use PubSubEmulatorContainer from te…
May 7, 2026
80977d8
Migrate embedded-google-storage to use FakeGcsServerContainer from te…
May 7, 2026
84babf6
Migrate embedded-memsql to use custom MemSqlContainer extending JdbcD…
May 7, 2026
deb75e7
Migrate embedded-grafana to use Grafana LGTM stack container with Lok…
May 7, 2026
8f9f84f
Migrate embedded-wiremock to use WireMockContainer from wiremock-test…
May 7, 2026
d07eab6
Migrate embedded-nats to use NatsContainer from nats-testcontainers l…
May 7, 2026
26dd157
Update embedded-wiremock README to reflect WireMockContainer migratio…
May 7, 2026
07fe840
Migrate embedded-selenium to use Selenium module from testcontainers-…
May 7, 2026
fd8c7ee
Simplify lambda expressions and use modern Java APIs
May 7, 2026
3dae065
Refactor code using modern Java APIs and simplify expressions
May 7, 2026
b461fb6
Update ContainerUtilsTest to use copyToTransferableContainerPathMap i…
May 7, 2026
b5ca267
Migrate embedded-kafka to use ConfluentKafkaContainer and override co…
May 11, 2026
ad9f84f
Migrate embedded-native-kafka to use KafkaContainer instead of Conflu…
May 11, 2026
0ba5346
Refactor embedded-selenium tests to use RemoteWebDriver instead of de…
May 14, 2026
c12c9ed
Fix MemSQL container startup by creating database before JDBC connect…
May 14, 2026
9adeec1
Replace LoggerFactory with Lombok @Slf4j annotation in EmbeddedNatsBo…
May 14, 2026
d5068b9
Replace LoggerFactory with Lombok @Slf4j annotation in EmbeddedNatsBo…
May 14, 2026
f136cfe
Add HTTPS and OAuth support to embedded Azurite with embedded self-si…
May 14, 2026
359a639
Add anonymous authentication configuration support to embedded Grafana
May 14, 2026
021b29f
Enable Toxiproxy for MemSQL and add latency emulation test
May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private List<ScanJob> getScanJobs(String stdout) {
if (!StringUtils.hasText(stdout)) {
return Collections.emptyList();
}
return Arrays.stream(stdout.replaceAll("\n", "").split(";"))
return Arrays.stream(stdout.replace("\n", "").split(";"))
.map(this::parseToObScanJobObject)
.collect(Collectors.toList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ private int getMappedPort(NetworkSettings networkSettings, int originalPort) {
Ports ports = networkSettings.getPorts();
Map<ExposedPort, Ports.Binding[]> bindings = ports.getBindings();
Ports.Binding[] binding = bindings.get(exposedPort);
return Integer.valueOf(binding[0].getHostPortSpec());
return Integer.parseInt(binding[0].getHostPortSpec());
}
}
52 changes: 49 additions & 3 deletions embedded-azurite/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
* `embedded.azurite.blobStoragePort` `(default is 10000)`
* `embedded.azurite.queueStoragePort` `(default is 10001)`
* `embedded.azurite.tableStoragePort` `(default is 10002)`
* `embedded.azurite.https-enabled` `(true|false, default is false)` — enables HTTPS. When `true` and no cert paths are provided, an embedded self-signed certificate for `localhost` is used automatically.
* `embedded.azurite.oauth-enabled` `(true|false, default is false)` — enables OAuth basic emulation (`--oauth basic`), required for `DefaultAzureCredential`. Implies `https-enabled=true`.
* `embedded.azurite.pem-cert-path` — path to a custom PEM certificate (prefix with `classpath:` for classpath resources, or provide an absolute file path). Used together with `pem-key-path`.
* `embedded.azurite.pem-key-path` — path to a custom PEM private key. Used together with `pem-cert-path`.
* `embedded.azurite.pfx-cert-path` — path to a custom PFX/PKCS12 certificate. Used together with `pfx-password`.
* `embedded.azurite.pfx-password` — password for the PFX certificate.
* `embedded.toxiproxy.proxies.azurite.enabled` Enables both creation of the container with ToxiProxy TCP proxy and a proxy to the `embedded-azurite` container.


Expand All @@ -30,9 +36,9 @@ Account name and account key are hardcoded as of https://github.com/Azure/Azurit
* `embedded.azurite.host`
* `embedded.azurite.account-name`
* `embedded.azurite.account-key`
* `embedded.azurite.blob-endpoint` (computed property `http://${host}:${port}/${accountName}` for convient configuration with `spring-cloud-azure-starter-storage-blob`)
* `embedded.azurite.queue-endpoint` (computed property `http://${host}:${port}/${accountName}` for convient configuration with `spring-cloud-azure-starter-storage-queue`)
* `embedded.azurite.table-endpoint` (computed property `http://${host}:${port}/${accountName}` for convient configuration with `spring-cloud-azure-starter-storage-table`)
* `embedded.azurite.blob-endpoint` (computed property `{http|https}://${host}:${port}/${accountName}` for convenient configuration with `spring-cloud-azure-starter-storage-blob`)
* `embedded.azurite.queue-endpoint` (computed property `{http|https}://${host}:${port}/${accountName}` for convenient configuration with `spring-cloud-azure-starter-storage-queue`)
* `embedded.azurite.table-endpoint` (computed property `{http|https}://${host}:${port}/${accountName}` for convenient configuration with `spring-cloud-azure-starter-storage-table`)
* `embedded.azurite.toxiproxy.host`
* `embedded.azurite.toxiproxy.blobStoragePort`
* `embedded.azurite.toxiproxy.queueStoragePort`
Expand Down Expand Up @@ -64,3 +70,43 @@ spring:
----

You can then access all beans from `spring-cloud-azure-starter-storage-blob`, i.e. `BlobServiceClientBuilder`.

==== HTTPS and OAuth (DefaultAzureCredential) Example

To use `DefaultAzureCredential` or any token-based credential, enable HTTPS and OAuth:

./src/test/resources/bootstrap.properties
[source,properties]
----
embedded.azurite.https-enabled=true
embedded.azurite.oauth-enabled=true
----

The embedded self-signed certificate is used automatically. Since it is self-signed you must configure the Azure SDK HTTP client to trust it in tests. The certificate is available as a classpath resource at `azurite/cert.pem`:

[source,java]
----
@Bean
BlobServiceClient blobServiceClient(AzuriteContainer azuriteContainer) throws SSLException {
SslContext insecureSslContext = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient reactor = HttpClient.create().secure(spec -> spec.sslContext(insecureSslContext));
com.azure.core.http.HttpClient azureHttpClient = new NettyAsyncHttpClientBuilder(reactor).build();

return new BlobServiceClientBuilder()
.connectionString(azuriteContainer.getConnectionString())
.httpClient(azureHttpClient)
.buildClient();
}
----

To provide your own certificate instead of the embedded one:

./src/test/resources/bootstrap.properties
[source,properties]
----
embedded.azurite.https-enabled=true
embedded.azurite.pem-cert-path=classpath:my-cert.pem
embedded.azurite.pem-key-path=classpath:my-key.pem
----
4 changes: 4 additions & 0 deletions embedded-azurite/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<artifactId>embedded-azurite</artifactId>

<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-azure</artifactId>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-storage-blob</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,46 @@ public class AzuriteProperties extends CommonContainerProperties {
*/
static final String ACCOUNT_KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";

static final String DEFAULT_CERT_CLASSPATH = "azurite/cert.pem";
static final String DEFAULT_KEY_CLASSPATH = "azurite/key.pem";

int blobStoragePort = 10000;
int queueStoragePort = 10001;
int tableStoragePort = 10002;

/**
* Enables HTTPS for Azurite. Required for OAuth (DefaultAzureCredential) support.
* When enabled without providing cert/key paths, uses an embedded self-signed certificate.
*/
boolean httpsEnabled = false;

/**
* Enables OAuth basic emulation (--oauth basic). Requires httpsEnabled=true.
* Allows using DefaultAzureCredential with Azurite.
*/
boolean oauthEnabled = false;

/**
* Classpath or file path to a PEM certificate for HTTPS. Used together with pemKeyPath.
* If not set when httpsEnabled=true, the embedded self-signed certificate is used.
*/
String pemCertPath;

/**
* Classpath or file path to a PEM private key for HTTPS. Used together with pemCertPath.
*/
String pemKeyPath;

/**
* Classpath or file path to a PFX certificate for HTTPS. Used together with pfxPassword.
*/
String pfxCertPath;

/**
* Password for the PFX certificate.
*/
String pfxPassword;

@Override
public String getDefaultDockerImage() {
// Please don`t remove this comment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.azure.AzuriteContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.toxiproxy.ToxiproxyContainer;
import org.testcontainers.utility.MountableFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;

import static com.playtika.testcontainer.azurite.AzuriteProperties.AZURITE_BEAN_NAME;
import static com.playtika.testcontainer.azurite.AzuriteProperties.DEFAULT_CERT_CLASSPATH;
import static com.playtika.testcontainer.azurite.AzuriteProperties.DEFAULT_KEY_CLASSPATH;
import static com.playtika.testcontainer.common.utils.ContainerUtils.configureCommonsAndStart;

@Slf4j
Expand All @@ -40,7 +46,7 @@ public class EmbeddedAzuriteBootstrapConfiguration {
@ConditionalOnToxiProxyEnabled(module = "azurite")
ToxiproxyClientProxy azuriteBlobContainerProxy(ToxiproxyClient toxiproxyClient,
ToxiproxyContainer toxiproxyContainer,
@Qualifier(AZURITE_BEAN_NAME) GenericContainer<?> azurite,
@Qualifier(AZURITE_BEAN_NAME) AzuriteContainer azurite,
AzuriteProperties properties,
ConfigurableEnvironment environment) {
ToxiproxyClientProxy proxy = ToxiproxyHelper.createProxy(
Expand All @@ -59,7 +65,7 @@ ToxiproxyClientProxy azuriteBlobContainerProxy(ToxiproxyClient toxiproxyClient,
@ConditionalOnToxiProxyEnabled(module = "azurite")
ToxiproxyClientProxy azuriteQueueContainerProxy(ToxiproxyClient toxiproxyClient,
ToxiproxyContainer toxiproxyContainer,
@Qualifier(AZURITE_BEAN_NAME) GenericContainer<?> azurite,
@Qualifier(AZURITE_BEAN_NAME) AzuriteContainer azurite,
AzuriteProperties properties,
ConfigurableEnvironment environment) {
ToxiproxyClientProxy proxy = ToxiproxyHelper.createProxy(
Expand All @@ -78,7 +84,7 @@ ToxiproxyClientProxy azuriteQueueContainerProxy(ToxiproxyClient toxiproxyClient,
@ConditionalOnToxiProxyEnabled(module = "azurite")
ToxiproxyClientProxy azuriteTableContainerProxy(ToxiproxyClient toxiproxyClient,
ToxiproxyContainer toxiproxyContainer,
@Qualifier(AZURITE_BEAN_NAME) GenericContainer<?> azurite,
@Qualifier(AZURITE_BEAN_NAME) AzuriteContainer azurite,
AzuriteProperties properties,
ConfigurableEnvironment environment) {
ToxiproxyClientProxy proxy = ToxiproxyHelper.createProxy(
Expand All @@ -94,37 +100,67 @@ ToxiproxyClientProxy azuriteTableContainerProxy(ToxiproxyClient toxiproxyClient,
}

@Bean(name = AZURITE_BEAN_NAME, destroyMethod = "stop")
public GenericContainer<?> azurite(ConfigurableEnvironment environment,
AzuriteProperties properties,
Optional<Network> network) {
GenericContainer<?> azuriteContainer = new GenericContainer<>(ContainerUtils.getDockerImageName(properties))
.withExposedPorts(properties.getBlobStoragePort(), properties.getQueueStoragePort(), properties.getTableStoragePort())
public AzuriteContainer azurite(ConfigurableEnvironment environment,
AzuriteProperties properties,
Optional<Network> network) {
AzuriteContainer azuriteContainer = new AzuriteContainer(ContainerUtils.getDockerImageName(properties))
.withNetworkAliases(AZURITE_BLOB_NETWORK_ALIAS)
.withCommand("azurite",
"-l", "/data",
"--blobHost", "0.0.0.0",
"--blobPort", String.valueOf(properties.getBlobStoragePort()),
"--queueHost", "0.0.0.0",
"--queuePort", String.valueOf(properties.getQueueStoragePort()),
"--tableHost", "0.0.0.0",
"--tablePort", String.valueOf(properties.getTableStoragePort()),
"--skipApiVersionCheck");
.withCreateContainerCmdModifier(cmd -> {
List<String> args = new ArrayList<>(Arrays.asList(cmd.getCmd()));
args.add("--skipApiVersionCheck");
if (properties.isOauthEnabled()) {
args.add("--oauth");
args.add("basic");
}
cmd.withCmd(args);
});
Comment on lines +108 to +116
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether AzuriteContainer in testcontainers-java calls withCommand() internally,
# which would determine if cmd.getCmd() can safely return non-null.
fd -i "AzuriteContainer.java" --exec cat {}

Repository: PlaytikaOSS/testcontainers-spring-boot

Length of output: 64


🏁 Script executed:

fd "EmbeddedAzuriteBootstrapConfiguration.java" --exec cat {}

Repository: PlaytikaOSS/testcontainers-spring-boot

Length of output: 7654


🏁 Script executed:

cd embedded-azurite && find . -name "pom.xml" -o -name "build.gradle" | head -1 | xargs cat | grep -A 5 -i testcontainers

Repository: PlaytikaOSS/testcontainers-spring-boot

Length of output: 1089


🌐 Web query:

testcontainers CreateContainerCmd getCmd() null Docker Java SDK

💡 Result:

No definitive evidence found of CreateContainerCmd.getCmd returning null in Testcontainers or Docker Java SDK under normal usage. The getCmd method in docker-java's CreateContainerCmd interface returns String[] annotated with @CheckForNull, meaning it can be null if no command (CMD) has been set via withCmd [1][2][3][4]. Testcontainers' GenericContainer creates a CreateContainerCmd via dockerClient.createContainerCmd(image) and applies modifiers, including command if withCommand is used [5][6]. If no explicit command is set, getCmd may legitimately return null, as the container uses the image's default CMD [7]. No GitHub issues or docs report getCmd unexpectedly null in Testcontainers contexts, including with Testcontainers Desktop [1-33]. Potential related issues include Docker connection problems [8][9][10] or modifier deadlocks [11], but not getCmd null specifically. Ensure withCommand is called if a custom CMD is needed, or check Docker client initialization.

Citations:


🌐 Web query:

AzuriteContainer testcontainers withCommand entrypoint

💡 Result:

AzuriteContainer is available in multiple Testcontainers libraries for different languages, providing a convenient way to spin up Azurite (Azure Storage emulator) in tests. Official modules handle the default command/entrypoint automatically, matching the Azurite Dockerfile CMD: ["azurite", "-l", "/data", "--blobHost", "0.0.0.0","--queueHost", "0.0.0.0", "--tableHost", "0.0.0.0"][1][2]. To customize using withCommand (overrides CMD) or entrypoint (overrides ENTRYPOINT), use language-specific methods on the AzuriteContainer or underlying GenericContainer: Java (org.testcontainers:azure module): AzuriteContainer extends SingleOffsetMapContainer and configures ports/envs but inherits.withCommand(String... cmd) and.withEntrypoint(String... entrypoint) from GenericContainer[3][4][5]. Example: AzuriteContainer azurite = new AzuriteContainer("mcr.microsoft.com/azure-storage/azurite:3.33.0") .withCommand("azurite", "--loose", "--blobHost", "0.0.0.0") .withExposedPorts(10000, 10001, 10002); azurite.start(); Default usage without custom command: new AzuriteContainer(image).start;[6][5]..NET (Testcontainers.Azurite): Use AzuriteBuilder.WithCommand(params string[] command)[7]. Example for HTTPS: var container = new AzuriteBuilder("mcr.microsoft.com/azure-storage/azurite:latest") .WithCommand("--cert", "/workspace/127.0.0.1.pem", "--key", "/workspace/127.0.0.1-key.pem", "--oauth", "basic") .WithResourceMapping(...) .Build(); [6]. Rust (testcontainers-modules): Azurite implements ImageArgsExt.with_cmd(vec![String])[8]. Example: let azurite = Azurite::default().with_cmd(vec!["azurite".to_owned(), "--loose".to_owned()]).start(); [8]. Python/Node.js: Specialized methods like withSsl internally handle commands; fall back to GenericContainer equivalents[9][10]. For blob-only:.withCommand("azurite-blob", "--blobHost", "0.0.0.0")[2][11]. Expose only needed ports. Official Testcontainers docs prioritize simple new AzuriteContainer.start as it uses correct defaults[12][6][5]. Custom commands needed for options like --loose, custom ports, SSL[9][7].

Citations:


cmd.getCmd() returns null when no command is explicitly set, causing an NPE via Arrays.asList.

The CreateContainerCmd.getCmd() method is annotated with @CheckForNull in the Docker Java SDK, confirming it can return null when no command has been set via withCmd. Since AzuriteContainer does not call withCommand() internally and relies on the image's default CMD, getCmd() returns null when new AzuriteContainer(image) is created without prior customization. Calling Arrays.asList(cmd.getCmd()) on a null input throws a NullPointerException.

Add a null guard to safely handle this case:

Proposed fix
.withCreateContainerCmdModifier(cmd -> {
-    List<String> args = new ArrayList<>(Arrays.asList(cmd.getCmd()));
+    String[] existing = cmd.getCmd();
+    List<String> args = existing != null ? new ArrayList<>(Arrays.asList(existing)) : new ArrayList<>();
     args.add("--skipApiVersionCheck");
     cmd.withCmd(args);
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.withCreateContainerCmdModifier(cmd -> {
List<String> args = new ArrayList<>(Arrays.asList(cmd.getCmd()));
args.add("--skipApiVersionCheck");
cmd.withCmd(args);
});
.withCreateContainerCmdModifier(cmd -> {
String[] existing = cmd.getCmd();
List<String> args = existing != null ? new ArrayList<>(Arrays.asList(existing)) : new ArrayList<>();
args.add("--skipApiVersionCheck");
cmd.withCmd(args);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@embedded-azurite/src/main/java/com/playtika/testcontainer/azurite/EmbeddedAzuriteBootstrapConfiguration.java`
around lines 105 - 109, The lambda passed to withCreateContainerCmdModifier uses
Arrays.asList(cmd.getCmd()) which throws NPE because CreateContainerCmd.getCmd()
can be null; update the modifier in EmbeddedAzuriteBootstrapConfiguration so it
first checks cmd.getCmd() for null and builds the args list accordingly (e.g.,
start from an empty List if null or new ArrayList<>(Arrays.asList(cmd.getCmd()))
if non-null), then add "--skipApiVersionCheck" and call cmd.withCmd(args) as
before; reference the CreateContainerCmd instance named cmd and the existing
cmd.withCmd(...) call when making the change.


configureSsl(azuriteContainer, properties);

network.ifPresent(azuriteContainer::withNetwork);

configureCommonsAndStart(azuriteContainer, properties, log);
azuriteContainer = (AzuriteContainer) configureCommonsAndStart(azuriteContainer, properties, log);
registerEnvironment(azuriteContainer, environment, properties);
return azuriteContainer;
}

private void registerEnvironment(GenericContainer<?> azurite,
private void configureSsl(AzuriteContainer azuriteContainer, AzuriteProperties properties) {
if (!properties.isHttpsEnabled()) {
return;
}
if (properties.isOauthEnabled()) {
log.info("Azurite OAuth enabled — HTTPS is required and will be configured.");
}
if (properties.getPfxCertPath() != null) {
azuriteContainer.withSsl(resolveMountableFile(properties.getPfxCertPath()), properties.getPfxPassword());
} else if (properties.getPemCertPath() != null && properties.getPemKeyPath() != null) {
azuriteContainer.withSsl(
resolveMountableFile(properties.getPemCertPath()),
resolveMountableFile(properties.getPemKeyPath()));
} else {
log.info("Azurite HTTPS enabled with embedded self-signed certificate.");
azuriteContainer.withSsl(
MountableFile.forClasspathResource(DEFAULT_CERT_CLASSPATH),
MountableFile.forClasspathResource(DEFAULT_KEY_CLASSPATH));
}
}

private MountableFile resolveMountableFile(String path) {
if (path.startsWith("classpath:")) {
return MountableFile.forClasspathResource(path.substring("classpath:".length()));
}
return MountableFile.forHostPath(path);
}

private void registerEnvironment(AzuriteContainer azurite,
ConfigurableEnvironment environment,
AzuriteProperties properties) {

Integer mappedBlobStoragePort = azurite.getMappedPort(properties.getBlobStoragePort());
Integer mappedQueueStoragePort = azurite.getMappedPort(properties.getQueueStoragePort());
Integer mappedTableStoragePort = azurite.getMappedPort(properties.getTableStoragePort());
String host = azurite.getHost();
String protocol = properties.isHttpsEnabled() ? "https" : "http";

LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("embedded.azurite.host", host);
Expand All @@ -133,9 +169,9 @@ private void registerEnvironment(GenericContainer<?> azurite,
map.put("embedded.azurite.tableStoragePort", mappedTableStoragePort);
map.put("embedded.azurite.account-name", AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.account-key", AzuriteProperties.ACCOUNT_KEY);
map.put("embedded.azurite.blob-endpoint", "http://" + host + ":" + mappedBlobStoragePort + "/" + AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.queue-endpoint", "http://" + host + ":" + mappedQueueStoragePort + "/" + AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.table-endpoint", "http://" + host + ":" + mappedTableStoragePort + "/" + AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.blob-endpoint", protocol + "://" + host + ":" + mappedBlobStoragePort + "/" + AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.queue-endpoint", protocol + "://" + host + ":" + mappedQueueStoragePort + "/" + AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.table-endpoint", protocol + "://" + host + ":" + mappedTableStoragePort + "/" + AzuriteProperties.ACCOUNT_NAME);
map.put("embedded.azurite.networkAlias", AZURITE_BLOB_NETWORK_ALIAS);

log.info("Started Azurite. Connection details: {}", map);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ void createAndDeleteContainerQueue() {
SendMessageResult sendMessageResult = queueClient.sendMessage("test");
QueueMessageItem queueMessageItem = queueClient.receiveMessage();
assertThat(queueMessageItem.getBody().toString()).isEqualTo("test");
assertThat(queueMessageItem.getMessageId().toString()).isEqualTo(sendMessageResult.getMessageId());
assertThat(queueMessageItem.getMessageId()).isEqualTo(sendMessageResult.getMessageId());
queueClient.delete();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.playtika.testcontainer.azurite;

import com.azure.core.http.netty.NettyAsyncHttpClientBuilder;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.testcontainers.azure.AzuriteContainer;
import reactor.netty.http.client.HttpClient;

import javax.net.ssl.SSLException;

import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(
classes = EmbeddedAzuriteHttpsTest.AzuriteHttpsTestConfiguration.class,
properties = {"embedded.azurite.https-enabled=true"})
@Disabled("Create private and public key to check https")
class EmbeddedAzuriteHttpsTest {

@Autowired
AzuriteContainer azuriteContainer;

@Autowired
BlobServiceClient blobServiceClient;

@Value("${embedded.azurite.blob-endpoint}")
String blobEndpoint;

@Test
void blobEndpointUsesHttps() {
assertThat(blobEndpoint).startsWith("https://");
}

@Test
void connectionStringUsesHttps() {
assertThat(azuriteContainer.getConnectionString()).contains("DefaultEndpointsProtocol=https");
}

@Test
@DisplayName("basic blob operations work over HTTPS with the embedded self-signed certificate")
void createAndDeleteContainerBlobOverHttps() {
long containersBefore = blobServiceClient.listBlobContainers().stream().count();
BlobContainerClient container = blobServiceClient.createBlobContainer(UUID.randomUUID().toString());
assertThat(container.listBlobs().stream()).isEmpty();
assertThat(blobServiceClient.listBlobContainers().stream().count()).isEqualTo(containersBefore + 1);
container.delete();
assertThat(blobServiceClient.listBlobContainers().stream().count()).isEqualTo(containersBefore);
}

@EnableAutoConfiguration
@Configuration
static class AzuriteHttpsTestConfiguration {

@Bean
BlobServiceClient blobServiceClient(AzuriteContainer azuriteContainer) throws SSLException {
// Trust all certs so the embedded self-signed certificate is accepted in tests.
io.netty.handler.ssl.SslContext insecureSslContext = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient reactor = HttpClient.create().secure(spec -> spec.sslContext(insecureSslContext));
com.azure.core.http.HttpClient azureHttpClient = new NettyAsyncHttpClientBuilder(reactor).build();

return new BlobServiceClientBuilder()
.connectionString(azuriteContainer.getConnectionString())
.httpClient(azureHttpClient)
.buildClient();
}
}
}
Loading