Skip to content

Commit 2bc6b42

Browse files
authored
Add failing test for #343 (#666)
1 parent 8f16ab2 commit 2bc6b42

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package tools.jackson.dataformat.avro.tofix;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.nio.ByteBuffer;
5+
6+
import org.apache.avro.Schema;
7+
import org.apache.avro.generic.GenericData;
8+
import org.apache.avro.generic.GenericDatumWriter;
9+
import org.apache.avro.generic.GenericRecord;
10+
import org.apache.avro.io.BinaryEncoder;
11+
import org.apache.avro.io.DatumWriter;
12+
import org.apache.avro.io.EncoderFactory;
13+
import org.apache.avro.message.BinaryMessageEncoder;
14+
15+
import org.junit.jupiter.api.Test;
16+
17+
import tools.jackson.databind.JsonNode;
18+
import tools.jackson.dataformat.avro.AvroMapper;
19+
import tools.jackson.dataformat.avro.AvroSchema;
20+
import tools.jackson.dataformat.avro.testutil.failure.JacksonTestFailureExpected;
21+
22+
import static org.junit.jupiter.api.Assertions.*;
23+
24+
/**
25+
* Reproducer for [dataformats-binary#343]: decoding data encoded with Apache Avro's
26+
* Single Object Encoding (magic bytes 0xC3 0x01 + 8-byte schema fingerprint + payload)
27+
* fails with "Invalid length indicator for String: -98".
28+
*/
29+
public class SingleObjectEncoding343Test
30+
{
31+
private static final String SCHEMA_JSON = "{"
32+
+ "\"type\": \"record\","
33+
+ "\"name\": \"SampleRecord\","
34+
+ "\"fields\": ["
35+
+ " {\"name\": \"name\", \"type\": \"string\"}"
36+
+ "]}";
37+
38+
private final AvroMapper MAPPER = new AvroMapper();
39+
40+
/**
41+
* Test showing the failure: data encoded with Apache Avro's BinaryMessageEncoder
42+
* (Single Object Encoding) cannot be decoded by Jackson's AvroMapper.
43+
* The first two bytes are the magic 0xC3 0x01 which decode as zigzag VarInt -98,
44+
* causing "Invalid length indicator for String: -98".
45+
*/
46+
@JacksonTestFailureExpected
47+
@Test
48+
public void testReadSingleObjectEncoding() throws Exception
49+
{
50+
Schema apacheSchema = new Schema.Parser().parse(SCHEMA_JSON);
51+
AvroSchema jacksonSchema = new AvroSchema(apacheSchema);
52+
53+
// Encode using Apache Avro's Single Object Encoding (BinaryMessageEncoder),
54+
// which is what SpecificRecord.toByteBuffer() uses in Apache Avro 1.8+.
55+
// The encoded bytes start with magic: 0xC3 0x01
56+
BinaryMessageEncoder<GenericRecord> encoder =
57+
new BinaryMessageEncoder<>(GenericData.get(), apacheSchema);
58+
GenericRecord record = new GenericData.Record(apacheSchema);
59+
record.put("name", "apm");
60+
ByteBuffer encoded = encoder.encode(record);
61+
byte[] bytes = toByteArray(encoded);
62+
63+
// Verify the magic bytes are present (the root cause of the failure)
64+
assertEquals((byte) 0xC3, bytes[0], "First magic byte should be 0xC3");
65+
assertEquals((byte) 0x01, bytes[1], "Second magic byte should be 0x01");
66+
67+
// This currently fails with:
68+
// "Invalid length indicator for String: -98"
69+
// because Jackson tries to decode 0xC3 0x01 as a zigzag VarInt string length
70+
JsonNode result = MAPPER.reader(jacksonSchema).readTree(bytes);
71+
assertNotNull(result);
72+
assertEquals("apm", result.get("name").asText());
73+
}
74+
75+
/**
76+
* Contrast: regular Avro binary encoding (NOT Single Object Encoding) works fine.
77+
*/
78+
@Test
79+
public void testReadRegularBinaryEncoding() throws Exception
80+
{
81+
Schema apacheSchema = new Schema.Parser().parse(SCHEMA_JSON);
82+
AvroSchema jacksonSchema = new AvroSchema(apacheSchema);
83+
84+
// Encode using regular binary encoder (no SOE header)
85+
ByteArrayOutputStream out = new ByteArrayOutputStream();
86+
BinaryEncoder binaryEncoder = EncoderFactory.get().binaryEncoder(out, null);
87+
DatumWriter<GenericRecord> writer = new GenericDatumWriter<>(apacheSchema);
88+
GenericRecord record = new GenericData.Record(apacheSchema);
89+
record.put("name", "apm");
90+
writer.write(record, binaryEncoder);
91+
binaryEncoder.flush();
92+
byte[] bytes = out.toByteArray();
93+
94+
// First byte should be 0x06 (zigzag VarInt for length 3), not 0xC3
95+
assertEquals((byte) 0x06, bytes[0]);
96+
97+
JsonNode result = MAPPER.reader(jacksonSchema).readTree(bytes);
98+
assertNotNull(result);
99+
assertEquals("apm", result.get("name").asText());
100+
}
101+
102+
private static byte[] toByteArray(ByteBuffer buf) {
103+
byte[] bytes = new byte[buf.remaining()];
104+
buf.get(bytes);
105+
return bytes;
106+
}
107+
}

0 commit comments

Comments
 (0)