Skip to content

Commit c84ac39

Browse files
committed
[fix][test] Made ProtobufNativeSchemaTest.testSchema order-independent
1 parent cc0eef9 commit c84ac39

File tree

1 file changed

+54
-3
lines changed

1 file changed

+54
-3
lines changed

pulsar-client/src/test/java/org/apache/pulsar/client/impl/schema/ProtobufNativeSchemaTest.java

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,24 @@
2020

2121
import static org.testng.Assert.assertEquals;
2222
import static org.testng.Assert.assertNotNull;
23+
import com.fasterxml.jackson.core.JsonProcessingException;
24+
import com.fasterxml.jackson.databind.JsonNode;
25+
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import com.fasterxml.jackson.databind.node.ObjectNode;
27+
import com.google.protobuf.DescriptorProtos;
2328
import com.google.protobuf.Descriptors;
29+
import com.google.protobuf.InvalidProtocolBufferException;
30+
import com.google.protobuf.util.JsonFormat;
2431
import io.netty.buffer.ByteBuf;
2532
import io.netty.buffer.ByteBufAllocator;
2633
import java.nio.charset.StandardCharsets;
34+
import java.util.Base64;
2735
import java.util.Collections;
2836
import java.util.HashMap;
2937
import lombok.extern.slf4j.Slf4j;
3038
import org.apache.pulsar.common.schema.SchemaType;
39+
import org.skyscreamer.jsonassert.JSONAssert;
40+
import org.skyscreamer.jsonassert.JSONCompareMode;
3141
import org.testng.Assert;
3242
import org.testng.annotations.Test;
3343

@@ -46,6 +56,28 @@ public class ProtobufNativeSchemaTest {
4656
+ "gBQjUKJW9yZy5hcGFjaGUucHVsc2FyLmNsaWVudC5zY2hlbWEucHJvdG9CDEV4dGVybmFsVGVzdGIGcHJvdG8z\",\"rootMessageT"
4757
+ "ypeName\":\"proto.TestMessage\",\"rootFileDescriptorName\":\"Test.proto\"}";
4858

59+
private static final ObjectMapper MAPPER = new ObjectMapper();
60+
61+
/**
62+
* Decode a base64-encoded Protobuf FileDescriptorSet into a canonical JSON tree.
63+
* @param b64 a base64 string
64+
* @return a normalized JSON tree that can be compared with JSONAssert, ensuring deterministic
65+
* equality checks regardless of field ordering.
66+
* @throws IllegalArgumentException if b64 isn't valid Base64
67+
* @throws InvalidProtocolBufferException if the decoded bytes are not a valid FileDescriptorSet
68+
* @throws JsonProcessingException if the JSON string is invalid
69+
*/
70+
private static JsonNode fdsToJson(String b64)
71+
throws IllegalArgumentException, InvalidProtocolBufferException, JsonProcessingException {
72+
DescriptorProtos.FileDescriptorSet fds =
73+
DescriptorProtos.FileDescriptorSet.parseFrom(Base64.getDecoder().decode(b64));
74+
String json = JsonFormat.printer()
75+
.includingDefaultValueFields()
76+
.omittingInsignificantWhitespace()
77+
.print(fds);
78+
return MAPPER.readTree(json);
79+
}
80+
4981
@Test
5082
public void testEncodeAndDecode() {
5183
final String stringFieldValue = "StringFieldValue";
@@ -61,15 +93,34 @@ public void testEncodeAndDecode() {
6193
}
6294

6395
@Test
64-
public void testSchema() {
96+
public void testSchema() throws Exception {
6597
ProtobufNativeSchema<org.apache.pulsar.client.schema.proto.Test.TestMessage> protobufSchema =
6698
ProtobufNativeSchema.of(org.apache.pulsar.client.schema.proto.Test.TestMessage.class);
6799

68100
assertEquals(protobufSchema.getSchemaInfo().getType(), SchemaType.PROTOBUF_NATIVE);
69101

70102
assertNotNull(ProtobufNativeSchemaUtils.deserialize(protobufSchema.getSchemaInfo().getSchema()));
71-
assertEquals(new String(protobufSchema.getSchemaInfo().getSchema(),
72-
StandardCharsets.UTF_8), EXPECTED_SCHEMA_JSON);
103+
104+
// Parse the produced/expected JSON into trees
105+
String actualJson = new String(protobufSchema.getSchemaInfo().getSchema(), StandardCharsets.UTF_8);
106+
JsonNode actualRoot = MAPPER.readTree(actualJson);
107+
JsonNode expectedRoot = MAPPER.readTree(EXPECTED_SCHEMA_JSON);
108+
109+
// Normalize the base64-encoded "fileDescriptorSet" field to remove order-dependent differences.
110+
// When decoded, Protobuf descriptors can serialize fields in varying orders, which causes
111+
// string-based comparisons to fail. By parsing into JsonNodes, we allow for re-ordering.
112+
String fdSetField = "fileDescriptorSet";
113+
JsonNode actualFdsNode = fdsToJson(actualRoot.path(fdSetField).asText());
114+
JsonNode expectedFdsNode = fdsToJson(expectedRoot.path(fdSetField).asText());
115+
((ObjectNode) actualRoot).set(fdSetField, actualFdsNode);
116+
((ObjectNode) expectedRoot).set(fdSetField, expectedFdsNode);
117+
118+
// Ensure the fields and values are the same, order does not matter
119+
JSONAssert.assertEquals(
120+
MAPPER.writeValueAsString(expectedRoot),
121+
MAPPER.writeValueAsString(actualRoot),
122+
JSONCompareMode.NON_EXTENSIBLE
123+
);
73124
}
74125

75126
@Test

0 commit comments

Comments
 (0)