This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new 485eb57144 Fix Azure Storage Blob tests failing when blob versioning
is enabled (#9117)
485eb57144 is described below
commit 485eb57144e95576b29f91e9bc93b5b4288f9e99
Author: JinyuChen97 <[email protected]>
AuthorDate: Mon Sep 7 15:43:55 2026 +0100
Fix Azure Storage Blob tests failing when blob versioning is enabled (#9117)
Use separate blob names for different blob types (block, append, page) to
avoid InvalidBlobType conflicts when versioning retains type history. Guard
blobVersions test with BlobVersioningEnabled condition and sort versions by
versionId to handle accumulated version history.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
integration-test-groups/azure/README.adoc | 7 ++++
.../storage/blob/it/AzureStorageBlobResource.java | 23 ++++++++++---
.../storage/blob/it/AzureStorageBlobRoutes.java | 29 +++++++++++-----
.../azure/storage/blob/it/AzureStorageHelper.java | 8 +++++
.../storage/blob/it/AzureStorageBlobTest.java | 40 +++++++++++++++-------
5 files changed, 81 insertions(+), 26 deletions(-)
diff --git a/integration-test-groups/azure/README.adoc
b/integration-test-groups/azure/README.adoc
index 79a55fed8d..12bf682335 100644
--- a/integration-test-groups/azure/README.adoc
+++ b/integration-test-groups/azure/README.adoc
@@ -68,6 +68,13 @@ To clean up, run
$ ./azure-resources.sh delete
----
+To run blob versioning tests,
https://learn.microsoft.com/en-us/azure/storage/blobs/versioning-enable?tabs=portal[enable
blob versioning] on your storage account and set the following (requires
`CAMEL_QUARKUS_START_MOCK_BACKEND=false`):
+
+[source,shell]
+----
+export AZURE_BLOB_VERSIONING_ENABLED=true
+----
+
You may want to `export CAMEL_QUARKUS_START_MOCK_BACKEND=false` to avoid
starting the local Azurite container and make sure that you test against the
real remote Azure API.
You may want to disable identity tests except key-vault (which is mandatory)
`export CAMEL_QUARKUS_DISABLE_IDENTITY_EXCEPT_KEY_VAULT=true` to avoid starting
test using identity with other extension then key-vault. This option might be
helpful in case of testing with client without permissions for eventhubs.
diff --git
a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java
b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java
index db2741593e..a26ff983f7 100644
---
a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java
+++
b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java
@@ -122,7 +122,8 @@ public class AzureStorageBlobResource {
@Produces(MediaType.TEXT_PLAIN)
public String readBlob(
@QueryParam("containerName") String containerName,
- @QueryParam("uri") String uri) {
+ @QueryParam("uri") String uri,
+ @QueryParam("blobName") String blobName) {
if (containerName == null) {
containerName = azureBlobContainerName;
}
@@ -134,16 +135,24 @@ public class AzureStorageBlobResource {
Map<String, Object> headers = new HashMap<>();
headers.put(Exchange.CHARSET_NAME, StandardCharsets.UTF_8.name());
headers.put(BlobConstants.BLOB_CONTAINER_NAME, containerName);
+ if (blobName != null) {
+ headers.put(BlobConstants.BLOB_NAME, blobName);
+ }
return producerTemplate.requestBodyAndHeaders(uri, null, headers,
String.class);
}
@Path("/blob/read/bytes")
@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
- public byte[] readBlobBytes() {
- return producerTemplate.requestBodyAndHeader(
+ public byte[] readBlobBytes(@QueryParam("blobName") String blobName) {
+ Map<String, Object> headers = new HashMap<>();
+ headers.put(Exchange.CHARSET_NAME, StandardCharsets.UTF_8.name());
+ if (blobName != null) {
+ headers.put(BlobConstants.BLOB_NAME, blobName);
+ }
+ return producerTemplate.requestBodyAndHeaders(
"direct:read",
- null, Exchange.CHARSET_NAME, StandardCharsets.UTF_8.name(),
byte[].class);
+ null, headers, byte[].class);
}
@Path("/blob/list")
@@ -174,12 +183,16 @@ public class AzureStorageBlobResource {
@Path("/blob/delete")
@DELETE
- public Response deleteBlob(@QueryParam("deleteSnapshots") String
deleteSnapshots) {
+ public Response deleteBlob(@QueryParam("deleteSnapshots") String
deleteSnapshots,
+ @QueryParam("blobName") String blobName) {
try {
Map<String, Object> headers = new HashMap<>();
if (deleteSnapshots != null) {
headers.put(BlobConstants.DELETE_SNAPSHOT_OPTION_TYPE,
DeleteSnapshotsOptionType.fromString(deleteSnapshots));
}
+ if (blobName != null) {
+ headers.put(BlobConstants.BLOB_NAME, blobName);
+ }
producerTemplate.sendBodyAndHeaders("direct:delete", null,
headers);
} catch (CamelExecutionException e) {
Throwable cause = e.getCause();
diff --git
a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java
b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java
index 5d96bd6358..71e120de77 100644
---
a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java
+++
b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java
@@ -27,6 +27,8 @@ import org.eclipse.microprofile.config.inject.ConfigProperty;
public class AzureStorageBlobRoutes extends RouteBuilder {
public static final String BLOB_NAME = "test";
+ public static final String APPEND_BLOB_NAME = "test-append";
+ public static final String PAGE_BLOB_NAME = "test-page";
@ConfigProperty(name = "azure.storage.account-name")
public String azureStorageAccountName;
@@ -102,25 +104,25 @@ public class AzureStorageBlobRoutes extends RouteBuilder {
.to(componentUri(BlobOperationsDefinition.getBlobBlockList));
from("direct:createAppendBlob")
- .to(componentUri(BlobOperationsDefinition.createAppendBlob));
+ .to(componentUri(BlobOperationsDefinition.createAppendBlob,
APPEND_BLOB_NAME));
from("direct:commitAppendBlob")
- .to(componentUri(BlobOperationsDefinition.commitAppendBlob));
+ .to(componentUri(BlobOperationsDefinition.commitAppendBlob,
APPEND_BLOB_NAME));
from("direct:createPageBlob")
- .to(componentUri(BlobOperationsDefinition.createPageBlob));
+ .to(componentUri(BlobOperationsDefinition.createPageBlob,
PAGE_BLOB_NAME));
from("direct:uploadPageBlob")
- .to(componentUri(BlobOperationsDefinition.uploadPageBlob));
+ .to(componentUri(BlobOperationsDefinition.uploadPageBlob,
PAGE_BLOB_NAME));
from("direct:resizePageBlob")
- .to(componentUri(BlobOperationsDefinition.resizePageBlob));
+ .to(componentUri(BlobOperationsDefinition.resizePageBlob,
PAGE_BLOB_NAME));
from("direct:clearPageBlob")
- .to(componentUri(BlobOperationsDefinition.clearPageBlob));
+ .to(componentUri(BlobOperationsDefinition.clearPageBlob,
PAGE_BLOB_NAME));
from("direct:getPageBlobRanges")
- .to(componentUri(BlobOperationsDefinition.getPageBlobRanges));
+ .to(componentUri(BlobOperationsDefinition.getPageBlobRanges,
PAGE_BLOB_NAME));
from("direct:getChangeFeed")
.toF(componentUri(BlobOperationsDefinition.getChangeFeed));
@@ -162,14 +164,23 @@ public class AzureStorageBlobRoutes extends RouteBuilder {
}
private String componentUri(final BlobOperationsDefinition operation) {
- return componentUri("azure-storage-blob", operation);
+ return componentUri("azure-storage-blob", operation, BLOB_NAME);
+ }
+
+ private String componentUri(final BlobOperationsDefinition operation,
final String blobName) {
+ return componentUri("azure-storage-blob", operation, blobName);
}
private String componentUri(final String componentName, final
BlobOperationsDefinition operation) {
+ return componentUri(componentName, operation, BLOB_NAME);
+ }
+
+ private String componentUri(final String componentName, final
BlobOperationsDefinition operation,
+ final String blobName) {
return String.format("%s://%s/%s?operation=%s&blobName=%s",
componentName,
azureStorageAccountName,
azureBlobContainerName,
- operation.name(), BLOB_NAME);
+ operation.name(), blobName);
}
}
diff --git
a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageHelper.java
b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageHelper.java
index 8254297b7f..93acb1b9ff 100644
---
a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageHelper.java
+++
b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageHelper.java
@@ -47,6 +47,14 @@ public class AzureStorageHelper {
}
}
+ public static class BlobVersioningEnabled implements BooleanSupplier {
+ @Override
+ public boolean getAsBoolean() {
+ return !MockBackendUtils.startMockBackend() &&
+ isAzureConfigValueEquals("AZURE_BLOB_VERSIONING_ENABLED",
String.class, "true");
+ }
+ }
+
private AzureStorageHelper() {
// Utility class
}
diff --git
a/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
b/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
index c958361dc8..725507cb35 100644
---
a/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
+++
b/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
@@ -23,6 +23,7 @@ import java.nio.file.Paths;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,6 +37,7 @@ import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
+import
org.apache.camel.quarkus.component.azure.storage.blob.it.AzureStorageHelper.BlobVersioningEnabled;
import
org.apache.camel.quarkus.component.azure.storage.blob.it.AzureStorageHelper.ClientCertificateAuthEnabled;
import
org.apache.camel.quarkus.component.azure.storage.blob.it.AzureStorageHelper.ClientSecretAuthEnabled;
import org.apache.camel.quarkus.test.EnabledIf;
@@ -253,13 +255,17 @@ class AzureStorageBlobTest {
.body(is("true"));
// Read
- RestAssured.get("/azure-storage-blob/blob/read")
+ RestAssured.given()
+ .queryParam("blobName",
AzureStorageBlobRoutes.APPEND_BLOB_NAME)
+ .get("/azure-storage-blob/blob/read")
.then()
.statusCode(200)
.body(is(appendedContent));
} finally {
// Delete
- RestAssured.delete("/azure-storage-blob/blob/delete")
+ RestAssured.given()
+ .queryParam("blobName",
AzureStorageBlobRoutes.APPEND_BLOB_NAME)
+ .delete("/azure-storage-blob/blob/delete")
.then()
.statusCode(anyOf(is(204), is(404)));
}
@@ -283,7 +289,9 @@ class AzureStorageBlobTest {
.statusCode(200)
.body(is("true"));
- byte[] pageData =
RestAssured.get("/azure-storage-blob/blob/read/bytes")
+ byte[] pageData = RestAssured.given()
+ .queryParam("blobName",
AzureStorageBlobRoutes.PAGE_BLOB_NAME)
+ .get("/azure-storage-blob/blob/read/bytes")
.then()
.statusCode(200)
.extract()
@@ -312,7 +320,9 @@ class AzureStorageBlobTest {
.body(is("true"));
// Read after resize
- pageData = RestAssured.get("/azure-storage-blob/blob/read/bytes")
+ pageData = RestAssured.given()
+ .queryParam("blobName",
AzureStorageBlobRoutes.PAGE_BLOB_NAME)
+ .get("/azure-storage-blob/blob/read/bytes")
.then()
.statusCode(200)
.extract()
@@ -338,7 +348,9 @@ class AzureStorageBlobTest {
.body(is("true"));
// Read after clear
- pageData = RestAssured.get("/azure-storage-blob/blob/read/bytes")
+ pageData = RestAssured.given()
+ .queryParam("blobName",
AzureStorageBlobRoutes.PAGE_BLOB_NAME)
+ .get("/azure-storage-blob/blob/read/bytes")
.then()
.statusCode(200)
.extract()
@@ -353,7 +365,9 @@ class AzureStorageBlobTest {
}
} finally {
// Delete
- RestAssured.delete("/azure-storage-blob/blob/delete")
+ RestAssured.given()
+ .queryParam("blobName",
AzureStorageBlobRoutes.PAGE_BLOB_NAME)
+ .delete("/azure-storage-blob/blob/delete")
.then()
.statusCode(anyOf(is(204), is(404)));
}
@@ -756,8 +770,8 @@ class AzureStorageBlobTest {
}
}
- // Blob versioning is not fully supported in Azurite
- @EnabledIf({ MockBackendDisabled.class })
+ // Blob versioning requires the Azure Storage account to have versioning
enabled
+ @EnabledIf({ BlobVersioningEnabled.class })
@Test
public void blobVersions() {
try {
@@ -788,17 +802,19 @@ class AzureStorageBlobTest {
.jsonPath()
.getList("versions");
- // Verify we have at least 2 versions
- assertEquals(2, versions.size(), "Should have 2 versions (original
and updated)");
-
- // Find the current version and an older version
+ // Find the current version and an older version, scoped to the
blob under test
+ String blobName = AzureStorageBlobRoutes.BLOB_NAME;
Map<String, Object> currentVersion = versions.stream()
+ .filter(v -> blobName.equals(v.get("name")))
.filter(v -> (Boolean) v.get("isCurrentVersion"))
.findFirst()
.orElse(null);
Map<String, Object> oldVersion = versions.stream()
+ .filter(v -> blobName.equals(v.get("name")))
.filter(v -> !(Boolean) v.get("isCurrentVersion"))
+ .filter(v -> v.get("versionId") != null)
+ .sorted(Comparator.comparing((Map<String, Object> v) ->
(String) v.get("versionId")).reversed())
.findFirst()
.orElse(null);