github.com/bakjos/protoreflect@v1.9.2/dynamic/json_test.go (about) 1 package dynamic 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/golang/protobuf/jsonpb" 12 "github.com/golang/protobuf/proto" 13 "github.com/golang/protobuf/protoc-gen-go/descriptor" 14 "github.com/golang/protobuf/ptypes" 15 "github.com/golang/protobuf/ptypes/any" 16 "github.com/golang/protobuf/ptypes/duration" 17 structpb "github.com/golang/protobuf/ptypes/struct" 18 "github.com/golang/protobuf/ptypes/timestamp" 19 "github.com/golang/protobuf/ptypes/wrappers" 20 21 "github.com/bakjos/protoreflect/desc" 22 "github.com/bakjos/protoreflect/internal/testprotos" 23 "github.com/bakjos/protoreflect/internal/testutil" 24 ) 25 26 func TestJSONUnaryFields(t *testing.T) { 27 jsonTranslationParty(t, unaryFieldsPosMsg, false) 28 jsonTranslationParty(t, unaryFieldsNegMsg, false) 29 jsonTranslationParty(t, unaryFieldsPosInfMsg, false) 30 jsonTranslationParty(t, unaryFieldsNegInfMsg, false) 31 jsonTranslationParty(t, unaryFieldsNanMsg, true) 32 } 33 34 func TestJSONRepeatedFields(t *testing.T) { 35 jsonTranslationParty(t, repeatedFieldsMsg, false) 36 jsonTranslationParty(t, repeatedFieldsInfNanMsg, true) 37 } 38 39 func TestJSONMapKeyFields(t *testing.T) { 40 jsonTranslationParty(t, mapKeyFieldsMsg, false) 41 } 42 43 func TestJSONMapValueFields(t *testing.T) { 44 jsonTranslationParty(t, mapValueFieldsMsg, false) 45 jsonTranslationParty(t, mapValueFieldsInfNanMsg, true) 46 jsonTranslationParty(t, mapValueFieldsNilMsg, false) 47 jsonTranslationParty(t, mapValueFieldsNilUnknownMsg, false) 48 } 49 50 func TestJSONExtensionFields(t *testing.T) { 51 // TODO 52 } 53 54 func createTestFileDescriptor(t *testing.T, packageName string) *desc.FileDescriptor { 55 // Create a new type that could only be resolved via custom resolver 56 // because it does not exist in compiled form 57 fdp := descriptor.FileDescriptorProto{ 58 Name: proto.String(fmt.Sprintf("%s.proto", packageName)), 59 Dependency: []string{"google/protobuf/any.proto"}, 60 Package: proto.String(packageName), 61 MessageType: []*descriptor.DescriptorProto{ 62 { 63 Name: proto.String("MyMessage"), 64 Field: []*descriptor.FieldDescriptorProto{ 65 { 66 Name: proto.String("abc"), 67 Number: proto.Int(1), 68 Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 69 Type: descriptor.FieldDescriptorProto_TYPE_STRING.Enum(), 70 }, 71 { 72 Name: proto.String("def"), 73 Number: proto.Int(2), 74 Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 75 Type: descriptor.FieldDescriptorProto_TYPE_INT32.Enum(), 76 }, 77 { 78 Name: proto.String("ghi"), 79 Number: proto.Int(3), 80 Label: descriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(), 81 Type: descriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 82 TypeName: proto.String(".google.protobuf.Any"), 83 }, 84 }, 85 }, 86 }, 87 } 88 anyfd, err := desc.LoadFileDescriptor("google/protobuf/any.proto") 89 testutil.Ok(t, err) 90 fd, err := desc.CreateFileDescriptor(&fdp, anyfd) 91 testutil.Ok(t, err) 92 return fd 93 } 94 95 func TestJSONAnyResolver(t *testing.T) { 96 fd1 := createTestFileDescriptor(t, "foobar") 97 fd2 := createTestFileDescriptor(t, "snafu") 98 md := fd1.FindMessage("foobar.MyMessage") 99 dm := NewMessage(md) 100 dm.SetFieldByNumber(1, "fubar") 101 dm.SetFieldByNumber(2, int32(123)) 102 a1, err := ptypes.MarshalAny(dm) 103 testutil.Ok(t, err) 104 md = fd2.FindMessage("snafu.MyMessage") 105 dm = NewMessage(md) 106 dm.SetFieldByNumber(1, "snafu") 107 dm.SetFieldByNumber(2, int32(456)) 108 a2, err := ptypes.MarshalAny(dm) 109 testutil.Ok(t, err) 110 111 msg := &testprotos.TestWellKnownTypes{Extras: []*any.Any{a1, a2}} 112 resolver := AnyResolver(nil, fd1, fd2) 113 114 jsm := jsonpb.Marshaler{AnyResolver: resolver} 115 js, err := jsm.MarshalToString(msg) 116 testutil.Ok(t, err) 117 expected := `{"extras":[{"@type":"type.googleapis.com/foobar.MyMessage","abc":"fubar","def":123},{"@type":"type.googleapis.com/snafu.MyMessage","abc":"snafu","def":456}]}` 118 testutil.Eq(t, expected, js) 119 120 jsu := jsonpb.Unmarshaler{AnyResolver: resolver} 121 msg2 := &testprotos.TestWellKnownTypes{} 122 err = jsu.Unmarshal(strings.NewReader(js), msg2) 123 testutil.Ok(t, err) 124 125 testutil.Ceq(t, msg, msg2, eqpm) 126 } 127 128 func TestJSONAnyResolver_AutomaticForDynamicMessage(t *testing.T) { 129 // marshaling and unmarshaling a dynamic message automatically enables 130 // resolving Any messages for known types (a known type is one that is 131 // in the dynamic message's file descriptor or that file's transitive 132 // dependencies) 133 fd := createTestFileDescriptor(t, "foobar") 134 md := fd.FindMessage("foobar.MyMessage") 135 dm := NewMessageFactoryWithDefaults().NewMessage(md).(*Message) 136 dm.SetFieldByNumber(1, "fubar") 137 dm.SetFieldByNumber(2, int32(123)) 138 a1, err := ptypes.MarshalAny(dm) 139 testutil.Ok(t, err) 140 dm.SetFieldByNumber(1, "snafu") 141 dm.SetFieldByNumber(2, int32(456)) 142 a2, err := ptypes.MarshalAny(dm) 143 testutil.Ok(t, err) 144 dm.SetFieldByNumber(1, "xyz") 145 dm.SetFieldByNumber(2, int32(-987)) 146 dm.SetFieldByNumber(3, []*any.Any{a1, a2}) 147 148 js, err := dm.MarshalJSON() 149 testutil.Ok(t, err) 150 expected := `{"abc":"xyz","def":-987,"ghi":[{"@type":"type.googleapis.com/foobar.MyMessage","abc":"fubar","def":123},{"@type":"type.googleapis.com/foobar.MyMessage","abc":"snafu","def":456}]}` 151 testutil.Eq(t, expected, string(js)) 152 153 dm2 := NewMessageFactoryWithDefaults().NewMessage(md).(*Message) 154 err = dm2.UnmarshalJSON(js) 155 testutil.Ok(t, err) 156 157 testutil.Ceq(t, dm, dm2, eqdm) 158 } 159 160 func TestMarshalJSONEmitDefaults(t *testing.T) { 161 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.ReallySimpleMessage)(nil)) 162 testutil.Ok(t, err) 163 dm := NewMessage(md) 164 js, err := dm.MarshalJSON() 165 testutil.Ok(t, err) 166 testutil.Eq(t, `{}`, string(js)) 167 jsDefaults, err := dm.MarshalJSONPB(&jsonpb.Marshaler{EmitDefaults: true}) 168 testutil.Ok(t, err) 169 testutil.Eq(t, `{"id":"0","name":""}`, string(jsDefaults)) 170 } 171 172 func TestMarshalJSONEmitDefaultsMapKeyFields(t *testing.T) { 173 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.MapKeyFields)(nil)) 174 testutil.Ok(t, err) 175 dm := NewMessage(md) 176 m := &jsonpb.Marshaler{EmitDefaults: true} 177 jsDefaults, err := dm.MarshalJSONPB(m) 178 testutil.Ok(t, err) 179 testutil.Eq(t, `{"i":{},"j":{},"k":{},"l":{},"m":{},"n":{},"o":{},"p":{},"q":{},"r":{},"s":{},"t":{}}`, string(jsDefaults)) 180 181 jsDefaults2, err := m.MarshalToString(&testprotos.MapKeyFields{}) 182 testutil.Ok(t, err) 183 testutil.Eq(t, string(jsDefaults), string(jsDefaults2)) 184 } 185 186 func TestMarshalJSONEmitDefaultsOneOfFields(t *testing.T) { 187 // we don't include default values for fields in a one-of 188 // since it would not round-trip correctly 189 testCases := []struct { 190 msg *testprotos.OneOfMessage 191 expectedJson string 192 }{ 193 { 194 msg: &testprotos.OneOfMessage{}, 195 expectedJson: `{}`, 196 }, 197 { 198 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_IntValue{IntValue: 12345}}, 199 expectedJson: `{"intValue":12345}`, 200 }, 201 { 202 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_StringValue{StringValue: "foobar"}}, 203 expectedJson: `{"stringValue":"foobar"}`, 204 }, 205 { 206 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_MsgValue{MsgValue: &testprotos.OneOfMessage{}}}, 207 expectedJson: `{"msgValue":{}}`, 208 }, 209 { 210 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_MsgValue{MsgValue: nil}}, 211 expectedJson: `{"msgValue":null}`, 212 }, 213 } 214 m := &jsonpb.Marshaler{EmitDefaults: true} 215 for _, testCase := range testCases { 216 dm, err := AsDynamicMessageWithMessageFactory(testCase.msg, NewMessageFactoryWithDefaults()) 217 testutil.Ok(t, err) 218 asJson, err := dm.MarshalJSONPB(m) 219 testutil.Ok(t, err) 220 actualJson := string(asJson) 221 testutil.Eq(t, testCase.expectedJson, actualJson) 222 223 // round-trip 224 err = jsonpb.UnmarshalString(actualJson, dm) 225 testutil.Ok(t, err) 226 var roundtripped testprotos.OneOfMessage 227 err = dm.ConvertTo(&roundtripped) 228 testutil.Ok(t, err) 229 testutil.Ceq(t, testCase.msg, &roundtripped, eqpm) 230 } 231 } 232 233 func TestMarshalJSONEnumsAsInts(t *testing.T) { 234 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 235 testutil.Ok(t, err) 236 dm := NewMessage(md) 237 dm.SetFieldByNumber(1, []int32{1}) 238 dm.SetFieldByNumber(2, "bedazzle") 239 js, err := dm.MarshalJSONPB(&jsonpb.Marshaler{EnumsAsInts: true}) 240 testutil.Ok(t, err) 241 testutil.Eq(t, `{"foo":[1],"bar":"bedazzle"}`, string(js)) 242 } 243 244 func TestMarshalJSONOrigName(t *testing.T) { 245 // TODO 246 } 247 248 func TestMarshalJSONIndent(t *testing.T) { 249 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 250 testutil.Ok(t, err) 251 dm := NewMessage(md) 252 dm.SetFieldByNumber(1, []int32{1}) 253 dm.SetFieldByNumber(2, "bedazzle") 254 js, err := dm.MarshalJSON() 255 testutil.Ok(t, err) 256 testutil.Eq(t, `{"foo":["VALUE1"],"bar":"bedazzle"}`, string(js)) 257 jsIndent, err := dm.MarshalJSONIndent() 258 testutil.Ok(t, err) 259 testutil.Eq(t, `{ 260 "foo": [ 261 "VALUE1" 262 ], 263 "bar": "bedazzle" 264 }`, string(jsIndent)) 265 jsIndent, err = dm.MarshalJSONPB(&jsonpb.Marshaler{Indent: "\t"}) 266 testutil.Ok(t, err) 267 testutil.Eq(t, `{ 268 "foo": [ 269 "VALUE1" 270 ], 271 "bar": "bedazzle" 272 }`, string(jsIndent)) 273 } 274 275 func TestMarshalJSONIndentEmbedWellKnownTypes(t *testing.T) { 276 // testing the formatting of dynamic message that embeds non-dynamic message, 277 // both those w/ special/simple JSON encoding (like timestamp) and those with 278 // more structure (Any). 279 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestWellKnownTypes)(nil)) 280 testutil.Ok(t, err) 281 dm := NewMessage(md) 282 283 ts, err := ptypes.TimestampProto(time.Date(2010, 3, 4, 5, 6, 7, 809000, time.UTC)) 284 testutil.Ok(t, err) 285 dm.SetFieldByNumber(1, ts) 286 287 anys := make([]*any.Any, 3) 288 anys[0], err = ptypes.MarshalAny(&testprotos.TestRequest{Bar: "foo"}) 289 testutil.Ok(t, err) 290 anys[1], err = ptypes.MarshalAny(&testprotos.TestRequest{Bar: "bar"}) 291 testutil.Ok(t, err) 292 anys[2], err = ptypes.MarshalAny(&testprotos.TestRequest{Bar: "baz"}) 293 testutil.Ok(t, err) 294 dm.SetFieldByNumber(13, anys) 295 296 js, err := dm.MarshalJSON() 297 testutil.Ok(t, err) 298 testutil.Eq(t, `{"startTime":"2010-03-04T05:06:07.000809Z","extras":[{"@type":"type.googleapis.com/testprotos.TestRequest","bar":"foo"},{"@type":"type.googleapis.com/testprotos.TestRequest","bar":"bar"},{"@type":"type.googleapis.com/testprotos.TestRequest","bar":"baz"}]}`, string(js)) 299 jsIndent, err := dm.MarshalJSONIndent() 300 testutil.Ok(t, err) 301 testutil.Eq(t, `{ 302 "startTime": "2010-03-04T05:06:07.000809Z", 303 "extras": [ 304 { 305 "@type": "type.googleapis.com/testprotos.TestRequest", 306 "bar": "foo" 307 }, 308 { 309 "@type": "type.googleapis.com/testprotos.TestRequest", 310 "bar": "bar" 311 }, 312 { 313 "@type": "type.googleapis.com/testprotos.TestRequest", 314 "bar": "baz" 315 } 316 ] 317 }`, string(jsIndent)) 318 jsIndent, err = dm.MarshalJSONPB(&jsonpb.Marshaler{Indent: "\t"}) 319 testutil.Ok(t, err) 320 testutil.Eq(t, `{ 321 "startTime": "2010-03-04T05:06:07.000809Z", 322 "extras": [ 323 { 324 "@type": "type.googleapis.com/testprotos.TestRequest", 325 "bar": "foo" 326 }, 327 { 328 "@type": "type.googleapis.com/testprotos.TestRequest", 329 "bar": "bar" 330 }, 331 { 332 "@type": "type.googleapis.com/testprotos.TestRequest", 333 "bar": "baz" 334 } 335 ] 336 }`, string(jsIndent)) 337 } 338 339 func TestUnmarshalJSONAllowUnknownFields(t *testing.T) { 340 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 341 testutil.Ok(t, err) 342 js := []byte(`{"foo":["VALUE1"],"bar":"bedazzle","xxx": 1}`) 343 dm := NewMessage(md) 344 err = dm.UnmarshalJSON(js) 345 testutil.Nok(t, err) 346 unmarshaler := &jsonpb.Unmarshaler{AllowUnknownFields: true} 347 err = dm.UnmarshalJSONPB(unmarshaler, js) 348 testutil.Ok(t, err) 349 foo := dm.GetFieldByNumber(1) 350 bar := dm.GetFieldByNumber(2) 351 testutil.Eq(t, []int32{1}, foo) 352 testutil.Eq(t, "bedazzle", bar) 353 } 354 355 func TestJSONWellKnownType(t *testing.T) { 356 any1, err := ptypes.MarshalAny(&testprotos.TestRequest{ 357 Foo: []testprotos.Proto3Enum{testprotos.Proto3Enum_VALUE1, testprotos.Proto3Enum_VALUE2}, 358 Bar: "bar", 359 Baz: &testprotos.TestMessage{Ne: []testprotos.TestMessage_NestedEnum{testprotos.TestMessage_VALUE1}}, 360 }) 361 testutil.Ok(t, err) 362 any2, err := ptypes.MarshalAny(ptypes.TimestampNow()) 363 testutil.Ok(t, err) 364 365 wkts := &testprotos.TestWellKnownTypes{ 366 StartTime: ×tamp.Timestamp{Seconds: 1010101, Nanos: 20202}, 367 Elapsed: &duration.Duration{Seconds: 30303, Nanos: 40404}, 368 Dbl: &wrappers.DoubleValue{Value: 3.14159}, 369 Flt: &wrappers.FloatValue{Value: -1.0101010}, 370 Bl: &wrappers.BoolValue{Value: true}, 371 I32: &wrappers.Int32Value{Value: -42}, 372 I64: &wrappers.Int64Value{Value: -9090909090}, 373 U32: &wrappers.UInt32Value{Value: 42}, 374 U64: &wrappers.UInt64Value{Value: 9090909090}, 375 Str: &wrappers.StringValue{Value: "foobar"}, 376 Byt: &wrappers.BytesValue{Value: []byte("snafu")}, 377 Json: []*structpb.Value{ 378 {Kind: &structpb.Value_BoolValue{BoolValue: true}}, 379 {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ 380 {Kind: &structpb.Value_NullValue{}}, 381 {Kind: &structpb.Value_StringValue{StringValue: "fubar"}}, 382 {Kind: &structpb.Value_NumberValue{NumberValue: 10101.20202}}, 383 }}}}, 384 {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{ 385 "foo": {Kind: &structpb.Value_NullValue{}}, 386 "bar": {Kind: &structpb.Value_StringValue{StringValue: "snafu"}}, 387 "baz": {Kind: &structpb.Value_NumberValue{NumberValue: 30303.40404}}, 388 }}}}, 389 }, 390 Extras: []*any.Any{any1, any2}, 391 } 392 393 jsm := jsonpb.Marshaler{} 394 js, err := jsm.MarshalToString(wkts) 395 testutil.Ok(t, err) 396 397 md, err := desc.LoadMessageDescriptorForMessage(wkts) 398 testutil.Ok(t, err) 399 dm := NewMessage(md) 400 err = dm.UnmarshalJSON([]byte(js)) 401 testutil.Ok(t, err) 402 403 // check that the unmarshalled fields were constructed correctly with the 404 // right value and type (e.g. generated well-known-type, not dynamic message) 405 ts, ok := dm.GetFieldByNumber(1).(*timestamp.Timestamp) 406 testutil.Require(t, ok) 407 testutil.Ceq(t, wkts.StartTime, ts, eqpm) 408 409 dur, ok := dm.GetFieldByNumber(2).(*duration.Duration) 410 testutil.Require(t, ok) 411 testutil.Ceq(t, wkts.Elapsed, dur, eqpm) 412 413 dbl, ok := dm.GetFieldByNumber(3).(*wrappers.DoubleValue) 414 testutil.Require(t, ok) 415 testutil.Eq(t, wkts.Dbl.Value, dbl.Value) 416 417 flt, ok := dm.GetFieldByNumber(4).(*wrappers.FloatValue) 418 testutil.Require(t, ok) 419 testutil.Eq(t, wkts.Flt.Value, flt.Value) 420 421 bl, ok := dm.GetFieldByNumber(5).(*wrappers.BoolValue) 422 testutil.Require(t, ok) 423 testutil.Eq(t, wkts.Bl.Value, bl.Value) 424 425 i32, ok := dm.GetFieldByNumber(6).(*wrappers.Int32Value) 426 testutil.Require(t, ok) 427 testutil.Eq(t, wkts.I32.Value, i32.Value) 428 429 i64, ok := dm.GetFieldByNumber(7).(*wrappers.Int64Value) 430 testutil.Require(t, ok) 431 testutil.Eq(t, wkts.I64.Value, i64.Value) 432 433 u32, ok := dm.GetFieldByNumber(8).(*wrappers.UInt32Value) 434 testutil.Require(t, ok) 435 testutil.Eq(t, wkts.U32.Value, u32.Value) 436 437 u64, ok := dm.GetFieldByNumber(9).(*wrappers.UInt64Value) 438 testutil.Require(t, ok) 439 testutil.Eq(t, wkts.U64.Value, u64.Value) 440 441 str, ok := dm.GetFieldByNumber(10).(*wrappers.StringValue) 442 testutil.Require(t, ok) 443 testutil.Eq(t, wkts.Str.Value, str.Value) 444 445 byt, ok := dm.GetFieldByNumber(11).(*wrappers.BytesValue) 446 testutil.Require(t, ok) 447 testutil.Eq(t, wkts.Byt.Value, byt.Value) 448 449 vals, ok := dm.GetFieldByNumber(12).([]interface{}) 450 testutil.Require(t, ok) 451 testutil.Eq(t, len(wkts.Json), len(vals)) 452 for i := range vals { 453 v, ok := vals[i].(*structpb.Value) 454 testutil.Require(t, ok) 455 testutil.Ceq(t, wkts.Json[i], v, eqpm) 456 } 457 458 extras, ok := dm.GetFieldByNumber(13).([]interface{}) 459 testutil.Require(t, ok) 460 testutil.Eq(t, len(wkts.Extras), len(extras)) 461 for i := range extras { 462 v, ok := extras[i].(*any.Any) 463 testutil.Require(t, ok) 464 testutil.Eq(t, wkts.Extras[i].TypeUrl, v.TypeUrl) 465 testutil.Eq(t, wkts.Extras[i].Value, v.Value) 466 } 467 } 468 469 func TestJSONWellKnownTypeFromFileDescriptorSet(t *testing.T) { 470 // TODO: generalize this so it tests all well-known types, not just duration 471 472 data, err := ioutil.ReadFile("../internal/testprotos/duration.protoset") 473 testutil.Ok(t, err) 474 fds := &descriptor.FileDescriptorSet{} 475 err = proto.Unmarshal(data, fds) 476 testutil.Ok(t, err) 477 fd, err := desc.CreateFileDescriptorFromSet(fds) 478 testutil.Ok(t, err) 479 md := fd.FindMessage("google.protobuf.Duration") 480 testutil.Neq(t, nil, md) 481 482 dur := &duration.Duration{Seconds: 30303, Nanos: 40404} 483 484 // marshal duration to JSON 485 jsm := jsonpb.Marshaler{} 486 js, err := jsm.MarshalToString(dur) 487 testutil.Ok(t, err) 488 489 // make sure we can unmarshal it 490 dm := NewMessage(md) 491 err = dm.UnmarshalJSON([]byte(js)) 492 testutil.Ok(t, err) 493 494 // and then marshal it again with same output as original 495 dynJs, err := jsm.MarshalToString(dm) 496 testutil.Ok(t, err) 497 testutil.Eq(t, js, dynJs) 498 } 499 500 func jsonTranslationParty(t *testing.T, msg proto.Message, includesNaN bool) { 501 doTranslationParty(t, msg, 502 func(pm proto.Message) ([]byte, error) { 503 m := jsonpb.Marshaler{} 504 var b bytes.Buffer 505 err := m.Marshal(&b, pm) 506 if err != nil { 507 return nil, err 508 } else { 509 return b.Bytes(), nil 510 } 511 }, 512 func(b []byte, pm proto.Message) error { 513 return jsonpb.Unmarshal(bytes.NewReader(b), pm) 514 }, 515 (*Message).MarshalJSON, (*Message).UnmarshalJSON, includesNaN, true, true) 516 }