This is an automated email from the ASF dual-hosted git repository. voonhous pushed a commit to branch release-1.2.1 in repository https://gitbox.apache.org/repos/asf/hudi.git
commit 86871e04030cfcc9e0dbf7e178eee53a4e5febd3 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]> (cherry picked from commit 21a931572dfd07d8ba18c4cd64cd721aaf5e3df1) --- .../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)); + } +}
