eskabetxe commented on code in PR #29422:
URL: https://github.com/apache/flink/pull/29422#discussion_r4422618799


##########
flink-filesystems/flink-s3-fs-native/src/test/java/org/apache/flink/fs/s3native/writer/NativeS3RecoverableSerializerTest.java:
##########
@@ -148,4 +152,320 @@ void testVersion1BytesWithTailKeyAtBucketRoot() throws 
IOException {
         assertThat(deserialized.incompleteObjectLength()).isEqualTo(1024L);
         assertThat(serializer.serialize(deserialized)).isEqualTo(version1);
     }
+
+    @Test
+    void testUnsupportedVersionThrows() throws IOException {
+        NativeS3RecoverableSerializer serializer = 
NativeS3RecoverableSerializer.INSTANCE;
+        NativeS3Recoverable original =
+                new NativeS3Recoverable("key", "upload-id", new ArrayList<>(), 
0L);
+        byte[] serialized = serializer.serialize(original);
+
+        assertThatThrownBy(() -> serializer.deserialize(2, serialized))
+                .isInstanceOf(IOException.class)
+                .hasMessageContaining("Unsupported version");
+    }
+
+    @Test
+    void testDeserializeLegacyFormatWithIncompletePart() throws IOException {
+        NativeS3Recoverable deserialized =
+                NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                        NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                        serializeLegacy(
+                                "test-object-key",
+                                "test-upload-id",
+                                new int[] {1, 2},
+                                new String[] {"etag1", "etag2"},
+                                12345L,
+                                "incomplete-object-key",
+                                1024L));
+
+        assertThat(deserialized.getObjectName()).isEqualTo("test-object-key");
+        assertThat(deserialized.uploadId()).isEqualTo("test-upload-id");
+        assertThat(deserialized.numBytesInParts()).isEqualTo(12345L);
+        assertThat(deserialized.parts()).hasSize(2);
+        assertThat(deserialized.parts().get(0).getPartNumber()).isEqualTo(1);
+        assertThat(deserialized.parts().get(0).getETag()).isEqualTo("etag1");
+        assertThat(deserialized.parts().get(1).getPartNumber()).isEqualTo(2);
+        assertThat(deserialized.parts().get(1).getETag()).isEqualTo("etag2");
+        
assertThat(deserialized.incompleteObjectName()).isEqualTo("incomplete-object-key");
+        assertThat(deserialized.incompleteObjectLength()).isEqualTo(1024L);
+    }
+
+    @Test
+    void testDeserializeLegacyFormatWithoutIncompletePart() throws IOException 
{
+        NativeS3Recoverable deserialized =
+                NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                        NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                        serializeLegacy(
+                                "test-object-key",
+                                "test-upload-id",
+                                new int[] {1},
+                                new String[] {"etag1"},
+                                54321L,
+                                null,
+                                -1L));
+
+        assertThat(deserialized.getObjectName()).isEqualTo("test-object-key");
+        assertThat(deserialized.uploadId()).isEqualTo("test-upload-id");
+        assertThat(deserialized.numBytesInParts()).isEqualTo(54321L);
+        assertThat(deserialized.parts()).hasSize(1);
+        assertThat(deserialized.incompleteObjectName()).isNull();
+        assertThat(deserialized.incompleteObjectLength()).isEqualTo(-1L);
+    }
+
+    @Test
+    void testLegacyFormatWithoutParts() throws IOException {
+        NativeS3Recoverable deserialized =
+                NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                        NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                        serializeLegacy(
+                                "key", "upload-id", new int[0], new String[0], 
0L, null, -1L));
+
+        assertThat(deserialized.getObjectName()).isEqualTo("key");
+        assertThat(deserialized.parts()).isEmpty();
+    }
+
+    @Test
+    void testCurrentFormatNotMistakenForLegacyFormat() throws IOException {
+        NativeS3RecoverableSerializer serializer = 
NativeS3RecoverableSerializer.INSTANCE;
+
+        List<NativeS3Recoverable.PartETag> parts = new ArrayList<>();
+        parts.add(new NativeS3Recoverable.PartETag(1, "etag1"));
+        NativeS3Recoverable original =
+                new NativeS3Recoverable("test-object-key", "test-upload-id", 
parts, 12345L);
+
+        byte[] serialized = serializer.serialize(original);
+        NativeS3Recoverable deserialized =
+                serializer.deserialize(serializer.getVersion(), serialized);
+
+        
assertThat(deserialized.getObjectName()).isEqualTo(original.getObjectName());
+        assertThat(deserialized.uploadId()).isEqualTo(original.uploadId());
+        
assertThat(deserialized.numBytesInParts()).isEqualTo(original.numBytesInParts());
+        assertThat(deserialized.parts()).hasSize(1);
+    }
+
+    /**
+     * Golden test against bytes captured once from the real {@code
+     * S3RecoverableSerializer#serialize(S3Recoverable)} in flink-s3-fs-base 
(object name {@code
+     * dir/test-key-ä日🔑}, upload id {@code upload-ä-id}, 2 parts, incomplete 
part with a multi-byte
+     * name), pinning the legacy wire format independently of the test-side 
re-encoder below.
+     */
+    @Test
+    void testDeserializeGoldenLegacyBytes() throws IOException {
+        NativeS3Recoverable deserialized =
+                NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                        NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                        hexToBytes(
+                                "3214769816000000"
+                                        + "6469722f74657374"
+                                        + "2d6b65792dc3a4e6"
+                                        + "97a5f09f94910c00"
+                                        + "000075706c6f6164"
+                                        + "2dc3a42d69640200"
+                                        + "0000010000000800"
+                                        + "0000657461672d6f"
+                                        + "6e65020000000800"
+                                        + "0000657461672d74"
+                                        + "776f393000000000"
+                                        + "000011000000696e"
+                                        + "636f6d706c657465"
+                                        + "2d6b65792dc3a400"
+                                        + "04000000000000"));
+
+        
assertThat(deserialized.getObjectName()).isEqualTo("dir/test-key-\u00e4\u65e5\uD83D\uDD11");
+        assertThat(deserialized.uploadId()).isEqualTo("upload-\u00e4-id");
+        assertThat(deserialized.numBytesInParts()).isEqualTo(12345L);
+        assertThat(deserialized.parts()).hasSize(2);
+        assertThat(deserialized.parts().get(0).getPartNumber()).isEqualTo(1);
+        
assertThat(deserialized.parts().get(0).getETag()).isEqualTo("etag-one");
+        assertThat(deserialized.parts().get(1).getPartNumber()).isEqualTo(2);
+        
assertThat(deserialized.parts().get(1).getETag()).isEqualTo("etag-two");
+        
assertThat(deserialized.incompleteObjectName()).isEqualTo("incomplete-key-\u00e4");
+        assertThat(deserialized.incompleteObjectLength()).isEqualTo(1024L);
+    }
+
+    @Test
+    void testDeserializeLegacyWithMultiByteNames() throws IOException {
+        NativeS3Recoverable deserialized =
+                NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                        NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                        serializeLegacy(
+                                "dir/ключ-\u00e4日\uD83D\uDD11",
+                                "upload-id-\u00e9",
+                                new int[] {7},
+                                new String[] {"etag-ä"},
+                                99L,
+                                "trailing-\u00fc",
+                                7L));
+
+        
assertThat(deserialized.getObjectName()).isEqualTo("dir/ключ-\u00e4日\uD83D\uDD11");
+        assertThat(deserialized.uploadId()).isEqualTo("upload-id-\u00e9");
+        assertThat(deserialized.parts().get(0).getETag()).isEqualTo("etag-ä");
+        
assertThat(deserialized.incompleteObjectName()).isEqualTo("trailing-\u00fc");
+    }
+
+    @Test
+    void testDeserializeLegacyTruncatedThrowsIOException() {
+        byte[] legacy =
+                hexToBytes(
+                        "3214769816000000"
+                                + "6469722f74657374"
+                                + "2d6b65792dc3a4e6"
+                                + "97a5f09f94910c00"
+                                + "000075706c6f6164"
+                                + "2dc3a42d69640200");
+        byte[] truncated = new byte[legacy.length - 5];
+        System.arraycopy(legacy, 0, truncated, 0, truncated.length);
+
+        assertThatThrownBy(
+                        () ->
+                                
NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                                        
NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                                        truncated))
+                .isInstanceOf(IOException.class)
+                .hasMessageContaining("Corrupt legacy");
+    }
+
+    @Test
+    void testDeserializeLegacyWithTrailingBytesThrowsIOException() {
+        byte[] legacy = serializeLegacyQuietly("key", "upload-id", 0L);
+        byte[] withTrailing = new byte[legacy.length + 1];
+        System.arraycopy(legacy, 0, withTrailing, 0, legacy.length);
+
+        assertThatThrownBy(
+                        () ->
+                                
NativeS3RecoverableSerializer.INSTANCE.deserialize(
+                                        
NativeS3RecoverableSerializer.INSTANCE.getVersion(),
+                                        withTrailing))
+                .isInstanceOf(IOException.class)
+                .hasMessageContaining("trailing bytes");
+    }
+
+    @Test
+    void testDeserializeLegacyWithNegativeLengthThrowsIOException() {

Review Comment:
   done



-- 
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]

Reply via email to