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

voonhous pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new 21a931572dfd test(client): cover low-coverage small classes across 
hudi-client (#19224)
21a931572dfd is described below

commit 21a931572dfd07d8ba18c4cd64cd721aaf5e3df1
Author: Y Ethan Guo <[email protected]>
AuthorDate: Wed Jul 8 05:30:15 2026 -0700

    test(client): cover low-coverage small classes across hudi-client (#19224)
    
    * test(client): cover low-coverage small classes across hudi-client
    
    * test(client): address review nits on tail-sweep coverage tests
    
    - Assert content equality instead of reference identity for the 
candidate-key
      and data-write-stat accessors, so the tests no longer pin the 
no-defensive-copy
      implementation detail.
    - Document that isKeyInRange fails fast with an NPE when bounds are unset.
    - Use assertNotEquals for the BloomIndexFileInfo inequality check, matching 
the
      sibling equality tests.
    
    ---------
    
    Co-authored-by: voon <[email protected]>
---
 .../apache/hudi/client/TestClientStatsPojos.java   | 94 ++++++++++++++++++++++
 .../TestBootstrapPartitionPathTranslators.java     | 46 +++++++++++
 .../transaction/lock/TestLockResultEnums.java      | 62 ++++++++++++++
 .../lock/audit/TestAuditOperationState.java        | 54 +++++++++++++
 .../hudi/exception/TestClientExceptions.java       | 71 ++++++++++++++++
 .../bulkinsert/TestBulkInsertSortMode.java         | 52 ++++++++++++
 .../hudi/index/bloom/TestBloomIndexFileInfo.java   | 79 ++++++++++++++++++
 .../table/action/commit/TestBucketTypeAndInfo.java | 72 +++++++++++++++++
 .../apache/hudi/util/TestOperationConverter.java   | 46 +++++++++++
 ...stJavaBulkInsertInternalPartitionerFactory.java | 53 ++++++++++++
 .../bloom/TestHoodieBloomFilterProbingResult.java  | 64 +++++++++++++++
 .../action/commit/TestSparkBucketInfoGetter.java   | 68 ++++++++++++++++
 12 files changed, 761 insertions(+)

diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestClientStatsPojos.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestClientStatsPojos.java
new file mode 100644
index 000000000000..f971c0369568
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestClientStatsPojos.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.client;
+
+import org.apache.hudi.common.model.HoodieWriteStat;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the plain client-side stat holders {@link SecondaryIndexStats} and 
{@link TableWriteStats}.
+ */
+public class TestClientStatsPojos {
+
+  @Test
+  void secondaryIndexStatsExposesConstructorValues() {
+    SecondaryIndexStats deleted = new SecondaryIndexStats("rk1", "sk1", true);
+    assertEquals("rk1", deleted.getRecordKey());
+    assertEquals("sk1", deleted.getSecondaryKeyValue());
+    assertTrue(deleted.isDeleted());
+
+    SecondaryIndexStats live = new SecondaryIndexStats("rk2", "sk2", false);
+    assertEquals("rk2", live.getRecordKey());
+    assertEquals("sk2", live.getSecondaryKeyValue());
+    assertFalse(live.isDeleted());
+  }
+
+  @Test
+  void secondaryIndexStatsSetterMutatesRecordKey() {
+    SecondaryIndexStats stats = new SecondaryIndexStats("rk1", "sk1", false);
+    stats.setRecordKey("rk2");
+    stats.setSecondaryKeyValue("sk2");
+    assertEquals("rk2", stats.getRecordKey());
+    assertEquals("sk2", stats.getSecondaryKeyValue());
+  }
+
+  @Test
+  void secondaryIndexStatsEqualityUsesAllFields() {
+    SecondaryIndexStats a = new SecondaryIndexStats("rk", "sk", false);
+    SecondaryIndexStats same = new SecondaryIndexStats("rk", "sk", false);
+    SecondaryIndexStats deleteDiffers = new SecondaryIndexStats("rk", "sk", 
true);
+    assertEquals(a, same);
+    assertEquals(a.hashCode(), same.hashCode());
+    assertNotEquals(a, deleteDiffers);
+  }
+
+  @Test
+  void tableWriteStatsSingleArgDefaultsMetadataToEmpty() {
+    List<HoodieWriteStat> dataStats = Collections.singletonList(new 
HoodieWriteStat());
+    TableWriteStats stats = new TableWriteStats(dataStats);
+    assertEquals(dataStats, stats.getDataTableWriteStats());
+    assertTrue(stats.getMetadataTableWriteStats().isEmpty());
+    assertFalse(stats.isEmptyDataTableWriteStats());
+  }
+
+  @Test
+  void tableWriteStatsReportsEmptyDataStats() {
+    TableWriteStats empty = new TableWriteStats(Collections.emptyList());
+    assertTrue(empty.isEmptyDataTableWriteStats());
+  }
+
+  @Test
+  void tableWriteStatsRetainsBothLists() {
+    List<HoodieWriteStat> dataStats = Arrays.asList(new HoodieWriteStat(), new 
HoodieWriteStat());
+    List<HoodieWriteStat> metadataStats = Collections.singletonList(new 
HoodieWriteStat());
+    TableWriteStats stats = new TableWriteStats(dataStats, metadataStats);
+    assertEquals(2, stats.getDataTableWriteStats().size());
+    assertEquals(1, stats.getMetadataTableWriteStats().size());
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/bootstrap/translator/TestBootstrapPartitionPathTranslators.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/bootstrap/translator/TestBootstrapPartitionPathTranslators.java
new file mode 100644
index 000000000000..9598b581aa68
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/bootstrap/translator/TestBootstrapPartitionPathTranslators.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.client.bootstrap.translator;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests the two built-in bootstrap partition-path translators.
+ */
+public class TestBootstrapPartitionPathTranslators {
+
+  @Test
+  void identityTranslatorReturnsInputUnchanged() {
+    IdentityBootstrapPartitionPathTranslator translator = new 
IdentityBootstrapPartitionPathTranslator();
+    assertEquals("2024/01/01", 
translator.getBootstrapTranslatedPath("2024/01/01"));
+    // Even already-encoded input is passed through verbatim.
+    assertEquals("2024%2F01", 
translator.getBootstrapTranslatedPath("2024%2F01"));
+  }
+
+  @Test
+  void decodedTranslatorUriDecodesEscapedPath() {
+    DecodedBootstrapPartitionPathTranslator translator = new 
DecodedBootstrapPartitionPathTranslator();
+    // %2F decodes to a slash.
+    assertEquals("2024/01", 
translator.getBootstrapTranslatedPath("2024%2F01"));
+    // Paths without escape sequences are unaffected.
+    assertEquals("region=us", 
translator.getBootstrapTranslatedPath("region=us"));
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/TestLockResultEnums.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/TestLockResultEnums.java
new file mode 100644
index 000000000000..51e944c214a9
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/TestLockResultEnums.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.client.transaction.lock;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+/**
+ * Tests the small result enums used by the storage-based lock provider.
+ */
+public class TestLockResultEnums {
+
+  @Test
+  void lockGetResultCodesAreStable() {
+    assertEquals(0, LockGetResult.NOT_EXISTS.getCode());
+    assertEquals(1, LockGetResult.SUCCESS.getCode());
+    assertEquals(2, LockGetResult.UNKNOWN_ERROR.getCode());
+    // Codes must be unique so callers can map them one-to-one.
+    assertEquals(3, LockGetResult.values().length);
+  }
+
+  @Test
+  void lockGetResultValueOfRoundTrips() {
+    for (LockGetResult result : LockGetResult.values()) {
+      assertSame(result, LockGetResult.valueOf(result.name()));
+    }
+  }
+
+  @Test
+  void lockUpsertResultCodesAreStable() {
+    assertEquals(0, LockUpsertResult.SUCCESS.getCode());
+    assertEquals(1, LockUpsertResult.ACQUIRED_BY_OTHERS.getCode());
+    assertEquals(2, LockUpsertResult.UNKNOWN_ERROR.getCode());
+    assertEquals(3, LockUpsertResult.THROTTLED.getCode());
+    assertEquals(4, LockUpsertResult.values().length);
+  }
+
+  @Test
+  void lockUpsertResultValueOfRoundTrips() {
+    for (LockUpsertResult result : LockUpsertResult.values()) {
+      assertSame(result, LockUpsertResult.valueOf(result.name()));
+    }
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/audit/TestAuditOperationState.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/audit/TestAuditOperationState.java
new file mode 100644
index 000000000000..fd9cdf7f44e1
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/audit/TestAuditOperationState.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.client.transaction.lock.audit;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests the {@link AuditOperationState} lifecycle enum.
+ */
+public class TestAuditOperationState {
+
+  @Test
+  void declaresExpectedStatesInOrder() {
+    assertArrayEquals(
+        new AuditOperationState[] {
+            AuditOperationState.START,
+            AuditOperationState.RENEW,
+            AuditOperationState.END
+        },
+        AuditOperationState.values());
+  }
+
+  @Test
+  void valueOfResolvesEachState() {
+    for (AuditOperationState state : AuditOperationState.values()) {
+      assertSame(state, AuditOperationState.valueOf(state.name()));
+    }
+  }
+
+  @Test
+  void valueOfRejectsUnknownState() {
+    assertThrows(IllegalArgumentException.class, () -> 
AuditOperationState.valueOf("PAUSE"));
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/exception/TestClientExceptions.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/exception/TestClientExceptions.java
new file mode 100644
index 000000000000..8501aa3774b6
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/exception/TestClientExceptions.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.exception;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the thin client-side exception types that wrap {@link 
HoodieException}.
+ */
+public class TestClientExceptions {
+
+  @Test
+  void keyGeneratorExceptionPreservesMessageAndCause() {
+    Throwable cause = new IllegalStateException("boom");
+    HoodieKeyGeneratorException withCause = new 
HoodieKeyGeneratorException("bad key", cause);
+    assertEquals("bad key", withCause.getMessage());
+    assertSame(cause, withCause.getCause());
+    assertTrue(withCause instanceof HoodieException);
+
+    HoodieKeyGeneratorException messageOnly = new 
HoodieKeyGeneratorException("no cause");
+    assertEquals("no cause", messageOnly.getMessage());
+    assertNull(messageOnly.getCause());
+  }
+
+  @Test
+  void compactionExceptionPreservesMessageAndCause() {
+    Throwable cause = new RuntimeException("io");
+    HoodieCompactionException withCause = new 
HoodieCompactionException("compaction failed", cause);
+    assertEquals("compaction failed", withCause.getMessage());
+    assertSame(cause, withCause.getCause());
+    assertTrue(withCause instanceof HoodieException);
+
+    HoodieCompactionException messageOnly = new 
HoodieCompactionException("compaction failed");
+    assertEquals("compaction failed", messageOnly.getMessage());
+    assertNull(messageOnly.getCause());
+  }
+
+  @Test
+  void rollbackExceptionPreservesMessageAndCause() {
+    Throwable cause = new RuntimeException("io");
+    HoodieRollbackException withCause = new HoodieRollbackException("rollback 
failed", cause);
+    assertEquals("rollback failed", withCause.getMessage());
+    assertSame(cause, withCause.getCause());
+    assertTrue(withCause instanceof HoodieException);
+
+    HoodieRollbackException messageOnly = new 
HoodieRollbackException("rollback failed");
+    assertEquals("rollback failed", messageOnly.getMessage());
+    assertNull(messageOnly.getCause());
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/execution/bulkinsert/TestBulkInsertSortMode.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/execution/bulkinsert/TestBulkInsertSortMode.java
new file mode 100644
index 000000000000..8ff7ef0679d5
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/execution/bulkinsert/TestBulkInsertSortMode.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.execution.bulkinsert;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests {@link BulkInsertSortMode}. The constant names double as config 
values, so the
+ * full set is pinned here to catch accidental renames or removals.
+ */
+public class TestBulkInsertSortMode {
+
+  @Test
+  void exposesExpectedModes() {
+    assertEquals(
+        
"NONE,GLOBAL_SORT,PARTITION_SORT,PARTITION_PATH_REPARTITION,PARTITION_PATH_REPARTITION_AND_SORT",
+        Arrays.stream(BulkInsertSortMode.values())
+            .map(Enum::name)
+            .collect(Collectors.joining(",")));
+  }
+
+  @Test
+  void valueOfResolvesEachModeCaseSensitively() {
+    for (BulkInsertSortMode mode : BulkInsertSortMode.values()) {
+      assertSame(mode, BulkInsertSortMode.valueOf(mode.name()));
+    }
+    assertThrows(IllegalArgumentException.class, () -> 
BulkInsertSortMode.valueOf("global_sort"));
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/index/bloom/TestBloomIndexFileInfo.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/index/bloom/TestBloomIndexFileInfo.java
new file mode 100644
index 000000000000..9f235d1d2337
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/index/bloom/TestBloomIndexFileInfo.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.index.bloom;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests {@link BloomIndexFileInfo} accessors and key-range checks.
+ */
+public class TestBloomIndexFileInfo {
+
+  @Test
+  void fileIdOnlyConstructorLeavesRangeUnset() {
+    BloomIndexFileInfo info = new BloomIndexFileInfo("f1");
+    assertEquals("f1", info.getFileId());
+    assertNull(info.getMinRecordKey());
+    assertNull(info.getMaxRecordKey());
+    assertFalse(info.hasKeyRanges());
+  }
+
+  @Test
+  void fullConstructorPopulatesRange() {
+    BloomIndexFileInfo info = new BloomIndexFileInfo("f1", "key05", "key20");
+    assertEquals("key05", info.getMinRecordKey());
+    assertEquals("key20", info.getMaxRecordKey());
+    assertTrue(info.hasKeyRanges());
+  }
+
+  @Test
+  void isKeyInRangeIsInclusiveOfBounds() {
+    BloomIndexFileInfo info = new BloomIndexFileInfo("f1", "key05", "key20");
+    assertTrue(info.isKeyInRange("key05"));
+    assertTrue(info.isKeyInRange("key10"));
+    assertTrue(info.isKeyInRange("key20"));
+    assertFalse(info.isKeyInRange("key04"));
+    assertFalse(info.isKeyInRange("key21"));
+  }
+
+  @Test
+  void isKeyInRangeRequiresBounds() {
+    BloomIndexFileInfo noRange = new BloomIndexFileInfo("f1");
+    // isKeyInRange does not guard on hasKeyRanges(); with no bounds set it 
currently
+    // fails fast with an NPE from Objects.requireNonNull rather than 
returning false.
+    assertThrows(NullPointerException.class, () -> 
noRange.isKeyInRange("key10"));
+  }
+
+  @Test
+  void valueSemanticsForEqualsAndHashCode() {
+    BloomIndexFileInfo a = new BloomIndexFileInfo("f1", "min", "max");
+    BloomIndexFileInfo same = new BloomIndexFileInfo("f1", "min", "max");
+    BloomIndexFileInfo different = new BloomIndexFileInfo("f2", "min", "max");
+    assertEquals(a, same);
+    assertEquals(a.hashCode(), same.hashCode());
+    assertNotEquals(a, different);
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/table/action/commit/TestBucketTypeAndInfo.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/table/action/commit/TestBucketTypeAndInfo.java
new file mode 100644
index 000000000000..537250c00e61
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/table/action/commit/TestBucketTypeAndInfo.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.table.action.commit;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests {@link BucketType} and the {@link BucketInfo} value holder that 
carries it.
+ */
+public class TestBucketTypeAndInfo {
+
+  @Test
+  void bucketTypeHasUpdateAndInsert() {
+    assertEquals(2, BucketType.values().length);
+    assertSame(BucketType.UPDATE, BucketType.valueOf("UPDATE"));
+    assertSame(BucketType.INSERT, BucketType.valueOf("INSERT"));
+  }
+
+  @Test
+  void bucketInfoGettersReturnConstructorArgs() {
+    BucketInfo info = new BucketInfo(BucketType.INSERT, "fileId-1", 
"2024/01/01");
+    assertSame(BucketType.INSERT, info.getBucketType());
+    assertEquals("fileId-1", info.getFileIdPrefix());
+    assertEquals("2024/01/01", info.getPartitionPath());
+  }
+
+  @Test
+  void bucketInfoEqualsAndHashCodeUseAllFields() {
+    BucketInfo a = new BucketInfo(BucketType.UPDATE, "f1", "p1");
+    BucketInfo same = new BucketInfo(BucketType.UPDATE, "f1", "p1");
+    BucketInfo differentType = new BucketInfo(BucketType.INSERT, "f1", "p1");
+    BucketInfo differentFile = new BucketInfo(BucketType.UPDATE, "f2", "p1");
+    BucketInfo differentPartition = new BucketInfo(BucketType.UPDATE, "f1", 
"p2");
+
+    assertEquals(a, same);
+    assertEquals(a.hashCode(), same.hashCode());
+    assertNotEquals(a, differentType);
+    assertNotEquals(a, differentFile);
+    assertNotEquals(a, differentPartition);
+    assertNotEquals(a, null);
+    assertNotEquals(a, "not a bucket info");
+  }
+
+  @Test
+  void bucketInfoToStringContainsFields() {
+    String rendered = new BucketInfo(BucketType.INSERT, "fileId-9", 
"part-9").toString();
+    assertTrue(rendered.contains("INSERT"));
+    assertTrue(rendered.contains("fileId-9"));
+    assertTrue(rendered.contains("part-9"));
+  }
+}
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/util/TestOperationConverter.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/util/TestOperationConverter.java
new file mode 100644
index 000000000000..e8cee32e7f4d
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/util/TestOperationConverter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.util;
+
+import org.apache.hudi.common.model.WriteOperationType;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests {@link OperationConverter}, the jcommander string-to-enum converter.
+ */
+public class TestOperationConverter {
+
+  private final OperationConverter converter = new OperationConverter();
+
+  @Test
+  void convertsEnumConstantNames() {
+    assertSame(WriteOperationType.INSERT, converter.convert("INSERT"));
+    assertSame(WriteOperationType.UPSERT, converter.convert("UPSERT"));
+    assertSame(WriteOperationType.BULK_INSERT, 
converter.convert("BULK_INSERT"));
+  }
+
+  @Test
+  void rejectsUnknownOperation() {
+    assertThrows(IllegalArgumentException.class, () -> 
converter.convert("not_an_operation"));
+  }
+}
diff --git 
a/hudi-client/hudi-java-client/src/test/java/org/apache/hudi/execution/bulkinsert/TestJavaBulkInsertInternalPartitionerFactory.java
 
b/hudi-client/hudi-java-client/src/test/java/org/apache/hudi/execution/bulkinsert/TestJavaBulkInsertInternalPartitionerFactory.java
new file mode 100644
index 000000000000..708e25d29cba
--- /dev/null
+++ 
b/hudi-client/hudi-java-client/src/test/java/org/apache/hudi/execution/bulkinsert/TestJavaBulkInsertInternalPartitionerFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.execution.bulkinsert;
+
+import org.apache.hudi.exception.HoodieException;
+import org.apache.hudi.table.BulkInsertPartitioner;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests the sort-mode based selection in {@link 
JavaBulkInsertInternalPartitionerFactory}.
+ */
+public class TestJavaBulkInsertInternalPartitionerFactory {
+
+  @Test
+  void noneModeReturnsNonSortPartitioner() {
+    BulkInsertPartitioner partitioner = 
JavaBulkInsertInternalPartitionerFactory.get(BulkInsertSortMode.NONE);
+    assertInstanceOf(JavaNonSortPartitioner.class, partitioner);
+  }
+
+  @Test
+  void globalSortModeReturnsGlobalSortPartitioner() {
+    BulkInsertPartitioner partitioner = 
JavaBulkInsertInternalPartitionerFactory.get(BulkInsertSortMode.GLOBAL_SORT);
+    assertInstanceOf(JavaGlobalSortPartitioner.class, partitioner);
+  }
+
+  @Test
+  void unsupportedModeThrows() {
+    assertThrows(HoodieException.class,
+        () -> 
JavaBulkInsertInternalPartitionerFactory.get(BulkInsertSortMode.PARTITION_SORT));
+    assertThrows(HoodieException.class,
+        () -> 
JavaBulkInsertInternalPartitionerFactory.get(BulkInsertSortMode.PARTITION_PATH_REPARTITION));
+  }
+}
diff --git 
a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/bloom/TestHoodieBloomFilterProbingResult.java
 
b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/bloom/TestHoodieBloomFilterProbingResult.java
new file mode 100644
index 000000000000..ea4f663d2d49
--- /dev/null
+++ 
b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/bloom/TestHoodieBloomFilterProbingResult.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.index.bloom;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the {@link HoodieBloomFilterProbingResult} value holder.
+ */
+public class TestHoodieBloomFilterProbingResult {
+
+  @Test
+  void exposesCandidateKeys() {
+    Set<String> keys = new HashSet<>();
+    keys.add("k1");
+    keys.add("k2");
+    HoodieBloomFilterProbingResult result = new 
HoodieBloomFilterProbingResult(keys);
+    assertEquals(keys, result.getCandidateKeys());
+  }
+
+  @Test
+  void supportsEmptyCandidateSet() {
+    HoodieBloomFilterProbingResult result =
+        new HoodieBloomFilterProbingResult(Collections.emptySet());
+    assertTrue(result.getCandidateKeys().isEmpty());
+  }
+
+  @Test
+  void valueSemanticsForEqualsAndHashCode() {
+    HoodieBloomFilterProbingResult a =
+        new HoodieBloomFilterProbingResult(new 
HashSet<>(Collections.singletonList("k")));
+    HoodieBloomFilterProbingResult same =
+        new HoodieBloomFilterProbingResult(new 
HashSet<>(Collections.singletonList("k")));
+    HoodieBloomFilterProbingResult different =
+        new HoodieBloomFilterProbingResult(new 
HashSet<>(Collections.singletonList("other")));
+    assertEquals(a, same);
+    assertEquals(a.hashCode(), same.hashCode());
+    assertNotEquals(a, different);
+  }
+}
diff --git 
a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/table/action/commit/TestSparkBucketInfoGetter.java
 
b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/table/action/commit/TestSparkBucketInfoGetter.java
new file mode 100644
index 000000000000..670db2c77c65
--- /dev/null
+++ 
b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/table/action/commit/TestSparkBucketInfoGetter.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.table.action.commit;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests the list- and map-backed {@link SparkBucketInfoGetter} 
implementations.
+ */
+public class TestSparkBucketInfoGetter {
+
+  private static BucketInfo bucket(String fileIdPrefix) {
+    return new BucketInfo(BucketType.INSERT, fileIdPrefix, "partition");
+  }
+
+  @Test
+  void listBasedGetterIndexesByPosition() {
+    BucketInfo first = bucket("f0");
+    BucketInfo second = bucket("f1");
+    List<BucketInfo> bucketInfoList = Arrays.asList(first, second);
+    ListBasedSparkBucketInfoGetter getter = new 
ListBasedSparkBucketInfoGetter(bucketInfoList);
+    assertSame(first, getter.getBucketInfo(0));
+    assertSame(second, getter.getBucketInfo(1));
+  }
+
+  @Test
+  void listBasedGetterRejectsOutOfRangeIndex() {
+    ListBasedSparkBucketInfoGetter getter =
+        new ListBasedSparkBucketInfoGetter(Arrays.asList(bucket("f0")));
+    assertThrows(IndexOutOfBoundsException.class, () -> 
getter.getBucketInfo(5));
+  }
+
+  @Test
+  void mapBasedGetterLooksUpByBucketNumber() {
+    Map<Integer, BucketInfo> bucketInfoMap = new HashMap<>();
+    BucketInfo target = bucket("f7");
+    bucketInfoMap.put(7, target);
+    MapBasedSparkBucketInfoGetter getter = new 
MapBasedSparkBucketInfoGetter(bucketInfoMap);
+    assertSame(target, getter.getBucketInfo(7));
+    // Missing keys resolve to null rather than throwing.
+    assertNull(getter.getBucketInfo(0));
+  }
+}


Reply via email to