laskoviymishka commented on code in PR #18050: URL: https://github.com/apache/iceberg/pull/18050#discussion_r4197926932
########## core/src/test/java/org/apache/iceberg/util/TestSerializationUtil.java: ########## @@ -0,0 +1,163 @@ +/* + * 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.iceberg.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.function.Function; +import org.apache.hadoop.conf.Configuration; +import org.apache.iceberg.hadoop.HadoopConfigurable; +import org.apache.iceberg.hadoop.SerializableConfiguration; +import org.apache.iceberg.relocated.com.google.common.collect.Maps; +import org.junit.jupiter.api.Test; + +class TestSerializationUtil { + + @Test + void bytesRoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + byte[] bytes = SerializationUtil.serializeToBytes(original); + String roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void bytesRoundTripPreservesMapContents() { + Map<String, Integer> original = Maps.newHashMap(); + original.put("added-records", 42); + original.put("total-files", 7); + + byte[] bytes = SerializationUtil.serializeToBytes(original); + Map<String, Integer> roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBytesReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBytes(null)).isNull(); + } + + @Test + void base64RoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + String encoded = SerializationUtil.serializeToBase64(original); + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBase64ReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBase64(null)).isNull(); + } + + @Test + void base64RoundTripHandlesMimeLineWrapping() { + // A payload whose base64 exceeds 76 characters forces the MIME encoder to insert line breaks; + // the round trip verifies the MIME decoder tolerates that wrapping. + String original = "a".repeat(1000); + String encoded = SerializationUtil.serializeToBase64(original); + assertThat(encoded).contains("\n"); + + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void serializeToBytesAppliesCustomConfSerializerToHadoopConfigurable() { + Configuration conf = new Configuration(false); + conf.set("test.key", "test.value"); + TestHadoopConfigurable configurable = new TestHadoopConfigurable(conf); + + boolean[] confSerializerInvoked = {false}; + Function<Configuration, SerializableSupplier<Configuration>> confSerializer = + c -> { + confSerializerInvoked[0] = true; + return new SerializableConfiguration(c); + }; + + SerializationUtil.serializeToBytes(configurable, confSerializer); + + assertThat(configurable.serializeConfWithInvoked) + .as("serializeConfWith should be called for a HadoopConfigurable object") + .isTrue(); + assertThat(confSerializerInvoked[0]) + .as("the provided confSerializer should be applied") + .isTrue(); + } + + @Test + void serializeToBytesWrapsIOException() { + // A non-Serializable object makes ObjectOutputStream throw NotSerializableException. + Object notSerializable = new Object(); + assertThatThrownBy(() -> SerializationUtil.serializeToBytes(notSerializable)) + .isInstanceOf(UncheckedIOException.class) + .hasMessage("Failed to serialize object"); + } + + @Test + void deserializeFromBytesWrapsIOException() { + // Bytes that are not a valid object stream make ObjectInputStream throw an IOException. + byte[] corrupted = {0, 1, 2, 3}; + assertThatThrownBy(() -> SerializationUtil.deserializeFromBytes(corrupted)) + .isInstanceOf(UncheckedIOException.class) + .hasMessage("Failed to deserialize object"); + } + + @Test + void hadoopConfigurableRoundTripPreservesConfiguration() { + Configuration conf = new Configuration(false); + conf.set("test.key", "test.value"); + TestHadoopConfigurable configurable = new TestHadoopConfigurable(conf); + + byte[] bytes = SerializationUtil.serializeToBytes(configurable); + TestHadoopConfigurable roundTripped = SerializationUtil.deserializeFromBytes(bytes); + + assertThat(roundTripped.getConf().get("test.key")).isEqualTo("test.value"); Review Comment: The fixture constructor already wraps the conf in `SerializableConfiguration`, so this round-trip succeeds whether or not `SerializationUtil` ever enters the `instanceof HadoopConfigurable` branch — delete that branch and the test is still green. Since this is the only test of the default `HadoopConfigurable` path, I'd start the fixture from a non-serializable conf holder (a transient `Configuration`, or a supplier that throws `NotSerializableException` until `serializeConfWith` runs), or assert `serializeConfWithInvoked` after the single-arg call, so it fails when that branch regresses. ########## core/src/test/java/org/apache/iceberg/util/TestSerializationUtil.java: ########## @@ -0,0 +1,163 @@ +/* + * 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.iceberg.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.function.Function; +import org.apache.hadoop.conf.Configuration; +import org.apache.iceberg.hadoop.HadoopConfigurable; +import org.apache.iceberg.hadoop.SerializableConfiguration; +import org.apache.iceberg.relocated.com.google.common.collect.Maps; +import org.junit.jupiter.api.Test; + +class TestSerializationUtil { + + @Test + void bytesRoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + byte[] bytes = SerializationUtil.serializeToBytes(original); + String roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void bytesRoundTripPreservesMapContents() { + Map<String, Integer> original = Maps.newHashMap(); + original.put("added-records", 42); + original.put("total-files", 7); + + byte[] bytes = SerializationUtil.serializeToBytes(original); + Map<String, Integer> roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBytesReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBytes(null)).isNull(); + } + + @Test + void base64RoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + String encoded = SerializationUtil.serializeToBase64(original); + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBase64ReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBase64(null)).isNull(); + } + + @Test + void base64RoundTripHandlesMimeLineWrapping() { + // A payload whose base64 exceeds 76 characters forces the MIME encoder to insert line breaks; + // the round trip verifies the MIME decoder tolerates that wrapping. + String original = "a".repeat(1000); + String encoded = SerializationUtil.serializeToBase64(original); + assertThat(encoded).contains("\n"); + + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void serializeToBytesAppliesCustomConfSerializerToHadoopConfigurable() { + Configuration conf = new Configuration(false); + conf.set("test.key", "test.value"); + TestHadoopConfigurable configurable = new TestHadoopConfigurable(conf); + + boolean[] confSerializerInvoked = {false}; + Function<Configuration, SerializableSupplier<Configuration>> confSerializer = + c -> { + confSerializerInvoked[0] = true; + return new SerializableConfiguration(c); Review Comment: The serializer here returns a plain `SerializableConfiguration`, which is exactly what the fixture already uses — so this passes even if `serializeToBytes` dropped our function's result and built its own. The two `isTrue()` flags only prove the lambda ran, not that its output was the thing serialized. I'd have the serializer return a distinctive supplier (one that yields a Configuration carrying a marker key), round-trip the bytes, and assert `getConf()` comes back with the marker. That's the actual `serializeConfWith` contract that Spark and Flink substitute their own serializers into. ########## core/src/test/java/org/apache/iceberg/util/TestSerializationUtil.java: ########## @@ -0,0 +1,163 @@ +/* + * 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.iceberg.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.function.Function; +import org.apache.hadoop.conf.Configuration; +import org.apache.iceberg.hadoop.HadoopConfigurable; +import org.apache.iceberg.hadoop.SerializableConfiguration; +import org.apache.iceberg.relocated.com.google.common.collect.Maps; +import org.junit.jupiter.api.Test; + +class TestSerializationUtil { + + @Test + void bytesRoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + byte[] bytes = SerializationUtil.serializeToBytes(original); + String roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void bytesRoundTripPreservesMapContents() { + Map<String, Integer> original = Maps.newHashMap(); + original.put("added-records", 42); + original.put("total-files", 7); + + byte[] bytes = SerializationUtil.serializeToBytes(original); + Map<String, Integer> roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBytesReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBytes(null)).isNull(); + } + + @Test + void base64RoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + String encoded = SerializationUtil.serializeToBase64(original); + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBase64ReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBase64(null)).isNull(); + } + + @Test + void base64RoundTripHandlesMimeLineWrapping() { + // A payload whose base64 exceeds 76 characters forces the MIME encoder to insert line breaks; + // the round trip verifies the MIME decoder tolerates that wrapping. + String original = "a".repeat(1000); + String encoded = SerializationUtil.serializeToBase64(original); + assertThat(encoded).contains("\n"); + + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void serializeToBytesAppliesCustomConfSerializerToHadoopConfigurable() { + Configuration conf = new Configuration(false); + conf.set("test.key", "test.value"); + TestHadoopConfigurable configurable = new TestHadoopConfigurable(conf); + + boolean[] confSerializerInvoked = {false}; + Function<Configuration, SerializableSupplier<Configuration>> confSerializer = + c -> { + confSerializerInvoked[0] = true; + return new SerializableConfiguration(c); + }; + + SerializationUtil.serializeToBytes(configurable, confSerializer); + + assertThat(configurable.serializeConfWithInvoked) + .as("serializeConfWith should be called for a HadoopConfigurable object") + .isTrue(); + assertThat(confSerializerInvoked[0]) + .as("the provided confSerializer should be applied") + .isTrue(); + } + + @Test + void serializeToBytesWrapsIOException() { + // A non-Serializable object makes ObjectOutputStream throw NotSerializableException. + Object notSerializable = new Object(); + assertThatThrownBy(() -> SerializationUtil.serializeToBytes(notSerializable)) + .isInstanceOf(UncheckedIOException.class) + .hasMessage("Failed to serialize object"); + } + + @Test + void deserializeFromBytesWrapsIOException() { + // Bytes that are not a valid object stream make ObjectInputStream throw an IOException. + byte[] corrupted = {0, 1, 2, 3}; + assertThatThrownBy(() -> SerializationUtil.deserializeFromBytes(corrupted)) + .isInstanceOf(UncheckedIOException.class) + .hasMessage("Failed to deserialize object"); + } + + @Test + void hadoopConfigurableRoundTripPreservesConfiguration() { + Configuration conf = new Configuration(false); + conf.set("test.key", "test.value"); + TestHadoopConfigurable configurable = new TestHadoopConfigurable(conf); + + byte[] bytes = SerializationUtil.serializeToBytes(configurable); + TestHadoopConfigurable roundTripped = SerializationUtil.deserializeFromBytes(bytes); + + assertThat(roundTripped.getConf().get("test.key")).isEqualTo("test.value"); + } + + private static class TestHadoopConfigurable implements HadoopConfigurable, Serializable { Review Comment: The `Test` prefix on this nested helper can read as a test class to discovery tooling and reviewers. Iceberg usually names these fixtures without the leading `Test` — I'd call it something like `HadoopConfigurableFixture`. ########## core/src/test/java/org/apache/iceberg/util/TestSerializationUtil.java: ########## @@ -0,0 +1,163 @@ +/* + * 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.iceberg.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.function.Function; +import org.apache.hadoop.conf.Configuration; +import org.apache.iceberg.hadoop.HadoopConfigurable; +import org.apache.iceberg.hadoop.SerializableConfiguration; +import org.apache.iceberg.relocated.com.google.common.collect.Maps; +import org.junit.jupiter.api.Test; + +class TestSerializationUtil { + + @Test + void bytesRoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + byte[] bytes = SerializationUtil.serializeToBytes(original); + String roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void bytesRoundTripPreservesMapContents() { + Map<String, Integer> original = Maps.newHashMap(); + original.put("added-records", 42); + original.put("total-files", 7); + + byte[] bytes = SerializationUtil.serializeToBytes(original); + Map<String, Integer> roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBytesReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBytes(null)).isNull(); + } + + @Test + void base64RoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + String encoded = SerializationUtil.serializeToBase64(original); + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBase64ReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBase64(null)).isNull(); + } + + @Test + void base64RoundTripHandlesMimeLineWrapping() { + // A payload whose base64 exceeds 76 characters forces the MIME encoder to insert line breaks; + // the round trip verifies the MIME decoder tolerates that wrapping. + String original = "a".repeat(1000); + String encoded = SerializationUtil.serializeToBase64(original); + assertThat(encoded).contains("\n"); Review Comment: `contains("\n")` works since the MIME separator is CRLF, but it's vague about what we're actually pinning. I'd tighten it to the real wrapping behavior: ```suggestion assertThat(encoded).contains("\r\n"); ``` ########## core/src/test/java/org/apache/iceberg/util/TestSerializationUtil.java: ########## @@ -0,0 +1,163 @@ +/* + * 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.iceberg.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.function.Function; +import org.apache.hadoop.conf.Configuration; +import org.apache.iceberg.hadoop.HadoopConfigurable; +import org.apache.iceberg.hadoop.SerializableConfiguration; +import org.apache.iceberg.relocated.com.google.common.collect.Maps; +import org.junit.jupiter.api.Test; + +class TestSerializationUtil { + + @Test + void bytesRoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + byte[] bytes = SerializationUtil.serializeToBytes(original); + String roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void bytesRoundTripPreservesMapContents() { + Map<String, Integer> original = Maps.newHashMap(); + original.put("added-records", 42); + original.put("total-files", 7); + + byte[] bytes = SerializationUtil.serializeToBytes(original); + Map<String, Integer> roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBytesReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBytes(null)).isNull(); + } + + @Test + void base64RoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + String encoded = SerializationUtil.serializeToBase64(original); + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBase64ReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBase64(null)).isNull(); + } + + @Test + void base64RoundTripHandlesMimeLineWrapping() { + // A payload whose base64 exceeds 76 characters forces the MIME encoder to insert line breaks; + // the round trip verifies the MIME decoder tolerates that wrapping. + String original = "a".repeat(1000); + String encoded = SerializationUtil.serializeToBase64(original); + assertThat(encoded).contains("\n"); + + String roundTripped = SerializationUtil.deserializeFromBase64(encoded); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void serializeToBytesAppliesCustomConfSerializerToHadoopConfigurable() { + Configuration conf = new Configuration(false); + conf.set("test.key", "test.value"); + TestHadoopConfigurable configurable = new TestHadoopConfigurable(conf); + + boolean[] confSerializerInvoked = {false}; + Function<Configuration, SerializableSupplier<Configuration>> confSerializer = + c -> { + confSerializerInvoked[0] = true; + return new SerializableConfiguration(c); + }; + + SerializationUtil.serializeToBytes(configurable, confSerializer); + + assertThat(configurable.serializeConfWithInvoked) + .as("serializeConfWith should be called for a HadoopConfigurable object") + .isTrue(); + assertThat(confSerializerInvoked[0]) + .as("the provided confSerializer should be applied") + .isTrue(); + } + + @Test + void serializeToBytesWrapsIOException() { + // A non-Serializable object makes ObjectOutputStream throw NotSerializableException. + Object notSerializable = new Object(); + assertThatThrownBy(() -> SerializationUtil.serializeToBytes(notSerializable)) + .isInstanceOf(UncheckedIOException.class) + .hasMessage("Failed to serialize object"); Review Comment: `hasMessage` pins the human-readable string but never checks the cause, so a reword breaks the test while an actual regression in what gets wrapped slips through. I'd add `.hasCauseInstanceOf(NotSerializableException.class)` here (and `StreamCorruptedException` in `deserializeFromBytesWrapsIOException`) — strictly more informative; keep the message assertion too if you like. ########## core/src/test/java/org/apache/iceberg/util/TestSerializationUtil.java: ########## @@ -0,0 +1,163 @@ +/* + * 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.iceberg.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.function.Function; +import org.apache.hadoop.conf.Configuration; +import org.apache.iceberg.hadoop.HadoopConfigurable; +import org.apache.iceberg.hadoop.SerializableConfiguration; +import org.apache.iceberg.relocated.com.google.common.collect.Maps; +import org.junit.jupiter.api.Test; + +class TestSerializationUtil { + + @Test + void bytesRoundTripPreservesValue() { + String original = "s3://bucket/table/metadata/v1.metadata.json"; + byte[] bytes = SerializationUtil.serializeToBytes(original); + String roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void bytesRoundTripPreservesMapContents() { + Map<String, Integer> original = Maps.newHashMap(); + original.put("added-records", 42); + original.put("total-files", 7); + + byte[] bytes = SerializationUtil.serializeToBytes(original); + Map<String, Integer> roundTripped = SerializationUtil.deserializeFromBytes(bytes); + assertThat(roundTripped).isEqualTo(original); + } + + @Test + void deserializeFromBytesReturnsNullForNullInput() { + assertThat((Object) SerializationUtil.deserializeFromBytes(null)).isNull(); Review Comment: Small thing — the `(Object)` cast is only here to settle generic inference. Pulling the result into a local reads cleaner (same for the base64 null test): ```suggestion Object result = SerializationUtil.deserializeFromBytes(null); assertThat(result).isNull(); ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
