Skip to content

Commit ec4eef5

Browse files
LucasEbylhotari
authored andcommitted
[fix][test] Made ProtobufNativeSchemaTest.testSchema order-independent (#24805)
(cherry picked from commit b8d0f14)
1 parent d90da78 commit ec4eef5

File tree

1 file changed

+70
-3
lines changed

1 file changed

+70
-3
lines changed

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

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

2121
import static org.testng.Assert.assertEquals;
2222
import static org.testng.Assert.assertNotNull;
23+
import com.fasterxml.jackson.databind.JsonNode;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import com.fasterxml.jackson.databind.node.ObjectNode;
26+
import com.google.protobuf.DescriptorProtos;
2327
import com.google.protobuf.Descriptors;
28+
import com.google.protobuf.InvalidProtocolBufferException;
29+
import com.google.protobuf.util.JsonFormat;
2430
import io.netty.buffer.ByteBuf;
2531
import io.netty.buffer.ByteBufAllocator;
2632
import java.nio.charset.StandardCharsets;
33+
import java.util.Base64;
2734
import java.util.Collections;
2835
import java.util.HashMap;
2936
import lombok.extern.slf4j.Slf4j;
3037
import org.apache.pulsar.common.schema.SchemaType;
38+
import org.json.JSONException;
39+
import org.json.JSONObject;
40+
import org.skyscreamer.jsonassert.JSONAssert;
41+
import org.skyscreamer.jsonassert.JSONCompareMode;
3142
import org.testng.Assert;
3243
import org.testng.annotations.Test;
3344

@@ -46,6 +57,28 @@ public class ProtobufNativeSchemaTest {
4657
+ "gBQjUKJW9yZy5hcGFjaGUucHVsc2FyLmNsaWVudC5zY2hlbWEucHJvdG9CDEV4dGVybmFsVGVzdGIGcHJvdG8z\",\"rootMessageT"
4758
+ "ypeName\":\"proto.TestMessage\",\"rootFileDescriptorName\":\"Test.proto\"}";
4859

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

6396
@Test
64-
public void testSchema() {
97+
public void testSchema() throws Exception {
6598
ProtobufNativeSchema<org.apache.pulsar.client.schema.proto.Test.TestMessage> protobufSchema =
6699
ProtobufNativeSchema.of(org.apache.pulsar.client.schema.proto.Test.TestMessage.class);
67100

68101
assertEquals(protobufSchema.getSchemaInfo().getType(), SchemaType.PROTOBUF_NATIVE);
69102

70103
assertNotNull(ProtobufNativeSchemaUtils.deserialize(protobufSchema.getSchemaInfo().getSchema()));
71-
assertEquals(new String(protobufSchema.getSchemaInfo().getSchema(),
72-
StandardCharsets.UTF_8), EXPECTED_SCHEMA_JSON);
104+
105+
// Parse the actual/expected JSON into trees
106+
String actualJson = new String(protobufSchema.getSchemaInfo().getSchema(), StandardCharsets.UTF_8);
107+
JsonNode actualRoot = MAPPER.readTree(actualJson);
108+
JsonNode expectedRoot = MAPPER.readTree(EXPECTED_SCHEMA_JSON);
109+
110+
// Extract and validate the FileDescriptorSet field for semantic comparison
111+
// (When decoded, Protobuf descriptors can serialize fields in varying orders
112+
// causing hard coded string comparisons to fail)
113+
String fdSetField = "fileDescriptorSet";
114+
JsonNode actualB64Node = actualRoot.path(fdSetField);
115+
JsonNode expectedB64Node = expectedRoot.path(fdSetField);
116+
Assert.assertFalse(actualB64Node.isMissingNode());
117+
Assert.assertFalse(expectedB64Node.isMissingNode());
118+
Assert.assertTrue(actualB64Node.isValueNode());
119+
Assert.assertTrue(expectedB64Node.isValueNode());
120+
121+
// Decode FileDescriptorSets to JSON and compare semantically (order-insensitive)
122+
JSONObject actualFdsObj = fdsToJson(actualB64Node.asText());
123+
JSONObject expectedFdsObj = fdsToJson(expectedB64Node.asText());
124+
JSONAssert.assertEquals(
125+
"FileDescriptorSet mismatch: decoded Protobuf descriptors have mismatched schema contents",
126+
expectedFdsObj,
127+
actualFdsObj,
128+
JSONCompareMode.NON_EXTENSIBLE
129+
);
130+
131+
// Remove the already verified field and compare remaining schema attributes, order does not matter
132+
((ObjectNode) actualRoot).remove(fdSetField);
133+
((ObjectNode) expectedRoot).remove(fdSetField);
134+
JSONAssert.assertEquals(
135+
"Schema metadata mismatch: remaining JSON fields differ after verifying FileDescriptorSet",
136+
MAPPER.writeValueAsString(expectedRoot),
137+
MAPPER.writeValueAsString(actualRoot),
138+
JSONCompareMode.NON_EXTENSIBLE
139+
);
73140
}
74141

75142
@Test

0 commit comments

Comments
 (0)