This is an automated email from the ASF dual-hosted git repository.

quantranhong1999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit a8e10d099af36d0eeb7af6589662d3ab0fbe05e4
Author: Quan Tran <[email protected]>
AuthorDate: Wed May 6 15:06:58 2026 +0700

    JAMES-4182 Add tests documenting the metadata name format
---
 .../org/apache/james/blob/api/BlobStoreDAO.java    | 14 +++++++
 .../apache/james/blob/api/BlobMetadataTest.java    | 44 ++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git 
a/server/blob/blob-api/src/main/java/org/apache/james/blob/api/BlobStoreDAO.java
 
b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/BlobStoreDAO.java
index ed0679bd51..a527dad825 100644
--- 
a/server/blob/blob-api/src/main/java/org/apache/james/blob/api/BlobStoreDAO.java
+++ 
b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/BlobStoreDAO.java
@@ -38,6 +38,19 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.io.ByteSource;
 import com.google.common.io.FileBackedOutputStream;
 
+/**
+ * James virtual blob store abstraction.
+ *
+ * <p>A {@link BucketName} is a James-specific logical bucket. Each storage 
connector decides how this logical
+ * bucket is represented in its backend. It should not be conflated with an S3 
bucket name and does not have to map one-to-one
+ * to a physical bucket.</p>
+ *
+ * <p>{@link BlobMetadata} is part of the contract so wrapper DAOs and storage 
implementations can keep side information
+ * needed to interpret a payload, such as compression markers. Metadata 
actively used by James should expose typed
+ * helpers, while the underlying metadata map remains an extension point for 
James library users and custom implementations.</p>
+ *
+ * <p>See {@code docs/modules/servers/partials/architecture/blobstore.adoc} 
for more details.</p>
+ */
 public interface BlobStoreDAO {
     record BlobMetadataName(String name) {
         private static final CharMatcher CHAR_MATCHER = 
CharMatcher.inRange('a', 'z')
@@ -46,6 +59,7 @@ public interface BlobStoreDAO {
             .or(CharMatcher.is('-'));
 
         public BlobMetadataName {
+            Preconditions.checkArgument(!name.isEmpty(), "Metadata name cannot 
be empty");
             Preconditions.checkArgument(CHAR_MATCHER.matchesAllOf(name), 
"Invalid char in metadata name. Must be a-z,A-Z,0-9 or - got " + name);
             Preconditions.checkArgument(name.length() < 128, "Metadata name is 
too long. Size exceed 128 chars");
             name = name.toLowerCase(Locale.US);
diff --git 
a/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobMetadataTest.java
 
b/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobMetadataTest.java
index 3cabf6991a..4f36eea2a5 100644
--- 
a/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobMetadataTest.java
+++ 
b/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobMetadataTest.java
@@ -20,10 +20,17 @@
 package org.apache.james.blob.api;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.ValueSource;
 
 class BlobMetadataTest {
+    private static final String ONE_HUNDRED_TWENTY_SEVEN_CHARS_METADATA_NAME = 
"a".repeat(127);
+    private static final String ONE_HUNDRED_TWENTY_EIGHT_CHARS_METADATA_NAME = 
"a".repeat(128);
+
     @Test
     void blobMetadataNameShouldBeCaseInsensitive() {
         assertThat(new BlobStoreDAO.BlobMetadataName("X-Test").name())
@@ -31,4 +38,41 @@ class BlobMetadataTest {
         assertThat(new BlobStoreDAO.BlobMetadataName("X-Test"))
             .isEqualTo(new BlobStoreDAO.BlobMetadataName("x-test"));
     }
+
+    @ParameterizedTest
+    @CsvSource({
+        "metadata, metadata",
+        "CONTENT-ENCODING, content-encoding",
+        "x-test-123, x-test-123",
+        "A1-B2-C3, a1-b2-c3"
+    })
+    void blobMetadataNameShouldAcceptLettersDigitsAndDash(String rawName, 
String expectedName) {
+        assertThat(new BlobStoreDAO.BlobMetadataName(rawName).name())
+            .isEqualTo(expectedName);
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"metadata_name", "metadata.name", "metadata name", 
"metadata/name", "metadata:name", "metadata#name"})
+    void blobMetadataNameShouldRejectUnsupportedCharacters(String rawName) {
+        assertThatThrownBy(() -> new BlobStoreDAO.BlobMetadataName(rawName))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void blobMetadataNameShouldRejectEmptyName() {
+        assertThatThrownBy(() -> new BlobStoreDAO.BlobMetadataName(""))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void 
blobMetadataNameShouldAcceptNameBelowOneHundredTwentyEightCharacters() {
+        assertThat(new 
BlobStoreDAO.BlobMetadataName(ONE_HUNDRED_TWENTY_SEVEN_CHARS_METADATA_NAME).name())
+            .isEqualTo(ONE_HUNDRED_TWENTY_SEVEN_CHARS_METADATA_NAME);
+    }
+
+    @Test
+    void blobMetadataNameShouldRejectNameOfOneHundredTwentyEightCharacters() {
+        assertThatThrownBy(() -> new 
BlobStoreDAO.BlobMetadataName(ONE_HUNDRED_TWENTY_EIGHT_CHARS_METADATA_NAME))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to