github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/runtime/marshal_jsonpb_test.go (about) 1 package runtime_test 2 3 import ( 4 "bytes" 5 "reflect" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" 12 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime/internal/examplepb" 13 "google.golang.org/protobuf/encoding/protojson" 14 "google.golang.org/protobuf/proto" 15 "google.golang.org/protobuf/testing/protocmp" 16 "google.golang.org/protobuf/types/known/durationpb" 17 "google.golang.org/protobuf/types/known/emptypb" 18 "google.golang.org/protobuf/types/known/structpb" 19 "google.golang.org/protobuf/types/known/timestamppb" 20 "google.golang.org/protobuf/types/known/wrapperspb" 21 ) 22 23 func TestJSONPbMarshal(t *testing.T) { 24 msg := examplepb.ABitOfEverything{ 25 SingleNested: &examplepb.ABitOfEverything_Nested{}, 26 RepeatedStringValue: []string{}, 27 MappedStringValue: map[string]string{}, 28 MappedNestedValue: map[string]*examplepb.ABitOfEverything_Nested{}, 29 RepeatedEnumValue: []examplepb.NumericEnum{}, 30 TimestampValue: ×tamppb.Timestamp{}, 31 Uuid: "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 32 Nested: []*examplepb.ABitOfEverything_Nested{ 33 { 34 Name: "foo", 35 Amount: 12345, 36 }, 37 }, 38 Uint64Value: 0xFFFFFFFFFFFFFFFF, 39 EnumValue: examplepb.NumericEnum_ONE, 40 OneofValue: &examplepb.ABitOfEverything_OneofString{ 41 OneofString: "bar", 42 }, 43 MapValue: map[string]examplepb.NumericEnum{ 44 "a": examplepb.NumericEnum_ONE, 45 "b": examplepb.NumericEnum_ZERO, 46 }, 47 RepeatedEnumAnnotation: []examplepb.NumericEnum{}, 48 EnumValueAnnotation: examplepb.NumericEnum_ONE, 49 RepeatedStringAnnotation: []string{}, 50 RepeatedNestedAnnotation: []*examplepb.ABitOfEverything_Nested{}, 51 NestedAnnotation: &examplepb.ABitOfEverything_Nested{}, 52 } 53 54 for i, spec := range []struct { 55 useEnumNumbers, emitUnpopulated bool 56 indent string 57 useProtoNames bool 58 verifier func(json string) 59 }{ 60 { 61 verifier: func(json string) { 62 if !strings.Contains(json, "ONE") { 63 t.Errorf(`strings.Contains(%q, "ONE") = false; want true`, json) 64 } 65 if want := "uint64Value"; !strings.Contains(json, want) { 66 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 67 } 68 }, 69 }, 70 { 71 useEnumNumbers: true, 72 verifier: func(json string) { 73 if strings.Contains(json, "ONE") { 74 t.Errorf(`strings.Contains(%q, "ONE") = true; want false`, json) 75 } 76 }, 77 }, 78 { 79 emitUnpopulated: true, 80 verifier: func(json string) { 81 if want := `"sfixed32Value"`; !strings.Contains(json, want) { 82 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 83 } 84 }, 85 }, 86 { 87 indent: "\t\t", 88 verifier: func(json string) { 89 if want := "\t\t\"amount\":"; !strings.Contains(json, want) { 90 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 91 } 92 }, 93 }, 94 { 95 useProtoNames: true, 96 verifier: func(json string) { 97 if want := "uint64_value"; !strings.Contains(json, want) { 98 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 99 } 100 }, 101 }, 102 } { 103 t.Run(strconv.Itoa(i), func(t *testing.T) { 104 m := runtime.JSONPb{ 105 MarshalOptions: protojson.MarshalOptions{ 106 EmitUnpopulated: spec.emitUnpopulated, 107 Indent: spec.indent, 108 UseProtoNames: spec.useProtoNames, 109 UseEnumNumbers: spec.useEnumNumbers, 110 }, 111 } 112 buf, err := m.Marshal(&msg) 113 if err != nil { 114 t.Errorf("m.Marshal(%v) failed with %v; want success; spec=%v", &msg, err, spec) 115 } 116 117 var got examplepb.ABitOfEverything 118 unmarshaler := &protojson.UnmarshalOptions{} 119 if err = unmarshaler.Unmarshal(buf, &got); err != nil { 120 t.Errorf("jsonpb.UnmarshalString(%q, &got) failed with %v; want success; spec=%v", string(buf), err, spec) 121 } 122 if diff := cmp.Diff(&got, &msg, protocmp.Transform()); diff != "" { 123 t.Errorf("case %d: spec=%v; %s", i, spec, diff) 124 } 125 if spec.verifier != nil { 126 spec.verifier(string(buf)) 127 } 128 }) 129 } 130 } 131 132 func TestJSONPbMarshalFields(t *testing.T) { 133 var m runtime.JSONPb 134 m.UseEnumNumbers = true // builtin fixtures include an enum, expected to be marshaled as int 135 for _, spec := range builtinFieldFixtures { 136 buf, err := m.Marshal(spec.data) 137 if err != nil { 138 t.Errorf("m.Marshal(%#v) failed with %v; want success", spec.data, err) 139 } 140 if got, want := string(buf), spec.json; got != want { 141 t.Errorf("m.Marshal(%#v) = %q; want %q", spec.data, got, want) 142 } 143 } 144 145 nums := []examplepb.NumericEnum{examplepb.NumericEnum_ZERO, examplepb.NumericEnum_ONE} 146 147 buf, err := m.Marshal(nums) 148 if err != nil { 149 t.Errorf("m.Marshal(%#v) failed with %v; want success", nums, err) 150 } 151 if got, want := string(buf), `[0,1]`; got != want { 152 t.Errorf("m.Marshal(%#v) = %q; want %q", nums, got, want) 153 } 154 155 m.UseEnumNumbers = false 156 buf, err = m.Marshal(examplepb.NumericEnum_ONE) 157 if err != nil { 158 t.Errorf("m.Marshal(%#v) failed with %v; want success", examplepb.NumericEnum_ONE, err) 159 } 160 if got, want := string(buf), `"ONE"`; got != want { 161 t.Errorf("m.Marshal(%#v) = %q; want %q", examplepb.NumericEnum_ONE, got, want) 162 } 163 164 buf, err = m.Marshal(nums) 165 if err != nil { 166 t.Errorf("m.Marshal(%#v) failed with %v; want success", nums, err) 167 } 168 if got, want := string(buf), `["ZERO","ONE"]`; got != want { 169 t.Errorf("m.Marshal(%#v) = %q; want %q", nums, got, want) 170 } 171 } 172 173 func TestJSONPbUnmarshal(t *testing.T) { 174 var ( 175 m runtime.JSONPb 176 got examplepb.ABitOfEverything 177 ) 178 for i, data := range []string{ 179 `{ 180 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 181 "nested": [ 182 {"name": "foo", "amount": 12345} 183 ], 184 "uint64Value": 18446744073709551615, 185 "enumValue": "ONE", 186 "oneofString": "bar", 187 "mapValue": { 188 "a": 1, 189 "b": 0 190 } 191 }`, 192 `{ 193 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 194 "nested": [ 195 {"name": "foo", "amount": 12345} 196 ], 197 "uint64Value": "18446744073709551615", 198 "enumValue": "ONE", 199 "oneofString": "bar", 200 "mapValue": { 201 "a": 1, 202 "b": 0 203 } 204 }`, 205 `{ 206 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 207 "nested": [ 208 {"name": "foo", "amount": 12345} 209 ], 210 "uint64Value": 18446744073709551615, 211 "enumValue": 1, 212 "oneofString": "bar", 213 "mapValue": { 214 "a": 1, 215 "b": 0 216 } 217 }`, 218 } { 219 if err := m.Unmarshal([]byte(data), &got); err != nil { 220 t.Errorf("case %d: m.Unmarshal(%q, &got) failed with %v; want success", i, data, err) 221 } 222 223 want := examplepb.ABitOfEverything{ 224 Uuid: "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 225 Nested: []*examplepb.ABitOfEverything_Nested{ 226 { 227 Name: "foo", 228 Amount: 12345, 229 }, 230 }, 231 Uint64Value: 0xFFFFFFFFFFFFFFFF, 232 EnumValue: examplepb.NumericEnum_ONE, 233 OneofValue: &examplepb.ABitOfEverything_OneofString{ 234 OneofString: "bar", 235 }, 236 MapValue: map[string]examplepb.NumericEnum{ 237 "a": examplepb.NumericEnum_ONE, 238 "b": examplepb.NumericEnum_ZERO, 239 }, 240 } 241 242 if diff := cmp.Diff(&got, &want, protocmp.Transform()); diff != "" { 243 t.Errorf("case %d: %s", i, diff) 244 } 245 } 246 } 247 248 func TestJSONPbUnmarshalFields(t *testing.T) { 249 var m runtime.JSONPb 250 for _, fixt := range fieldFixtures { 251 if fixt.skipUnmarshal { 252 continue 253 } 254 255 dest := reflect.New(reflect.TypeOf(fixt.data)) 256 if err := m.Unmarshal([]byte(fixt.json), dest.Interface()); err != nil { 257 t.Errorf("m.Unmarshal(%q, %T) failed with %v; want success", fixt.json, dest.Interface(), err) 258 } 259 if diff := cmp.Diff(dest.Elem().Interface(), fixt.data, protocmp.Transform()); diff != "" { 260 t.Errorf("dest = %#v; want %#v; input = %v", dest.Elem().Interface(), fixt.data, fixt.json) 261 } 262 } 263 } 264 265 func TestJSONPbEncoder(t *testing.T) { 266 msg := examplepb.ABitOfEverything{ 267 SingleNested: &examplepb.ABitOfEverything_Nested{}, 268 RepeatedStringValue: []string{}, 269 MappedStringValue: map[string]string{}, 270 MappedNestedValue: map[string]*examplepb.ABitOfEverything_Nested{}, 271 RepeatedEnumValue: []examplepb.NumericEnum{}, 272 TimestampValue: ×tamppb.Timestamp{}, 273 Uuid: "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 274 Nested: []*examplepb.ABitOfEverything_Nested{ 275 { 276 Name: "foo", 277 Amount: 12345, 278 }, 279 }, 280 Uint64Value: 0xFFFFFFFFFFFFFFFF, 281 OneofValue: &examplepb.ABitOfEverything_OneofString{ 282 OneofString: "bar", 283 }, 284 MapValue: map[string]examplepb.NumericEnum{ 285 "a": examplepb.NumericEnum_ONE, 286 "b": examplepb.NumericEnum_ZERO, 287 }, 288 RepeatedEnumAnnotation: []examplepb.NumericEnum{}, 289 EnumValueAnnotation: examplepb.NumericEnum_ONE, 290 RepeatedStringAnnotation: []string{}, 291 RepeatedNestedAnnotation: []*examplepb.ABitOfEverything_Nested{}, 292 NestedAnnotation: &examplepb.ABitOfEverything_Nested{}, 293 } 294 295 for i, spec := range []struct { 296 useEnumNumbers, emitUnpopulated bool 297 indent string 298 useProtoNames bool 299 verifier func(json string) 300 }{ 301 { 302 verifier: func(json string) { 303 if !strings.Contains(json, "ONE") { 304 t.Errorf(`strings.Contains(%q, "ONE") = false; want true`, json) 305 } 306 if want := "uint64Value"; !strings.Contains(json, want) { 307 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 308 } 309 }, 310 }, 311 { 312 useEnumNumbers: true, 313 verifier: func(json string) { 314 if strings.Contains(json, "ONE") { 315 t.Errorf(`strings.Contains(%q, "ONE") = true; want false`, json) 316 } 317 }, 318 }, 319 { 320 emitUnpopulated: true, 321 verifier: func(json string) { 322 if want := `"sfixed32Value"`; !strings.Contains(json, want) { 323 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 324 } 325 }, 326 }, 327 { 328 indent: "\t\t", 329 verifier: func(json string) { 330 if want := "\t\t\"amount\":"; !strings.Contains(json, want) { 331 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 332 } 333 }, 334 }, 335 { 336 useProtoNames: true, 337 verifier: func(json string) { 338 if want := "uint64_value"; !strings.Contains(json, want) { 339 t.Errorf(`strings.Contains(%q, %q) = false; want true`, json, want) 340 } 341 }, 342 }, 343 } { 344 m := runtime.JSONPb{ 345 MarshalOptions: protojson.MarshalOptions{ 346 EmitUnpopulated: spec.emitUnpopulated, 347 Indent: spec.indent, 348 UseProtoNames: spec.useProtoNames, 349 UseEnumNumbers: spec.useEnumNumbers, 350 }, 351 } 352 353 var buf bytes.Buffer 354 enc := m.NewEncoder(&buf) 355 if err := enc.Encode(&msg); err != nil { 356 t.Errorf("enc.Encode(%v) failed with %v; want success; spec=%v", &msg, err, spec) 357 } 358 359 var got examplepb.ABitOfEverything 360 unmarshaler := &protojson.UnmarshalOptions{} 361 if err := unmarshaler.Unmarshal(buf.Bytes(), &got); err != nil { 362 t.Errorf("jsonpb.UnmarshalString(%q, &got) failed with %v; want success; spec=%v", buf.String(), err, spec) 363 } 364 if diff := cmp.Diff(&got, &msg, protocmp.Transform()); diff != "" { 365 t.Errorf("case %d: %s", i, diff) 366 } 367 if spec.verifier != nil { 368 spec.verifier(buf.String()) 369 } 370 } 371 } 372 373 func TestJSONPbEncoderFields(t *testing.T) { 374 var m runtime.JSONPb 375 for _, fixt := range fieldFixtures { 376 var buf bytes.Buffer 377 enc := m.NewEncoder(&buf) 378 if err := enc.Encode(fixt.data); err != nil { 379 t.Errorf("enc.Encode(%#v) failed with %v; want success", fixt.data, err) 380 } 381 if got, want := buf.String(), fixt.json+string(m.Delimiter()); got != want { 382 t.Errorf("enc.Encode(%#v) = %q; want %q", fixt.data, got, want) 383 } 384 } 385 386 m.UseEnumNumbers = true 387 buf, err := m.Marshal(examplepb.NumericEnum_ONE) 388 if err != nil { 389 t.Errorf("m.Marshal(%#v) failed with %v; want success", examplepb.NumericEnum_ONE, err) 390 } 391 if got, want := string(buf), "1"; got != want { 392 t.Errorf("m.Marshal(%#v) = %q; want %q", examplepb.NumericEnum_ONE, got, want) 393 } 394 } 395 396 func TestJSONPbDecoder(t *testing.T) { 397 var ( 398 m runtime.JSONPb 399 got examplepb.ABitOfEverything 400 ) 401 for _, data := range []string{ 402 `{ 403 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 404 "nested": [ 405 {"name": "foo", "amount": 12345} 406 ], 407 "uint64Value": 18446744073709551615, 408 "enumValue": "ONE", 409 "oneofString": "bar", 410 "mapValue": { 411 "a": 1, 412 "b": 0 413 } 414 }`, 415 `{ 416 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 417 "nested": [ 418 {"name": "foo", "amount": 12345} 419 ], 420 "uint64Value": "18446744073709551615", 421 "enumValue": "ONE", 422 "oneofString": "bar", 423 "mapValue": { 424 "a": 1, 425 "b": 0 426 } 427 }`, 428 `{ 429 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 430 "nested": [ 431 {"name": "foo", "amount": 12345} 432 ], 433 "uint64Value": 18446744073709551615, 434 "enumValue": 1, 435 "oneofString": "bar", 436 "mapValue": { 437 "a": 1, 438 "b": 0 439 } 440 }`, 441 } { 442 r := strings.NewReader(data) 443 dec := m.NewDecoder(r) 444 if err := dec.Decode(&got); err != nil { 445 t.Errorf("m.Unmarshal(&got) failed with %v; want success; data=%q", err, data) 446 } 447 448 want := examplepb.ABitOfEverything{ 449 Uuid: "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 450 Nested: []*examplepb.ABitOfEverything_Nested{ 451 { 452 Name: "foo", 453 Amount: 12345, 454 }, 455 }, 456 Uint64Value: 0xFFFFFFFFFFFFFFFF, 457 EnumValue: examplepb.NumericEnum_ONE, 458 OneofValue: &examplepb.ABitOfEverything_OneofString{ 459 OneofString: "bar", 460 }, 461 MapValue: map[string]examplepb.NumericEnum{ 462 "a": examplepb.NumericEnum_ONE, 463 "b": examplepb.NumericEnum_ZERO, 464 }, 465 } 466 if diff := cmp.Diff(&got, &want, protocmp.Transform()); diff != "" { 467 t.Errorf("data %q: %s", data, diff) 468 } 469 } 470 } 471 472 func TestJSONPbDecoderFields(t *testing.T) { 473 var m runtime.JSONPb 474 for _, fixt := range fieldFixtures { 475 if fixt.skipUnmarshal { 476 continue 477 } 478 479 dest := reflect.New(reflect.TypeOf(fixt.data)) 480 dec := m.NewDecoder(strings.NewReader(fixt.json)) 481 if err := dec.Decode(dest.Interface()); err != nil { 482 t.Errorf("dec.Decode(%T) failed with %v; want success; input = %q", dest.Interface(), err, fixt.json) 483 } 484 if got, want := dest.Elem().Interface(), fixt.data; !reflect.DeepEqual(got, want) { 485 t.Errorf("dest = %#v; want %#v; input = %v", got, want, fixt.json) 486 } 487 } 488 } 489 490 func TestJSONPbDecoderUnknownField(t *testing.T) { 491 var ( 492 m = runtime.JSONPb{ 493 UnmarshalOptions: protojson.UnmarshalOptions{ 494 DiscardUnknown: false, 495 }, 496 } 497 got examplepb.ABitOfEverything 498 ) 499 data := `{ 500 "uuid": "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 501 "unknownField": "111" 502 }` 503 504 r := strings.NewReader(data) 505 dec := m.NewDecoder(r) 506 if err := dec.Decode(&got); err == nil { 507 t.Errorf("m.Unmarshal(&got) not failed; want `unknown field` error; data=%q", data) 508 } 509 } 510 511 var ( 512 fieldFixtures = []struct { 513 data interface{} 514 json string 515 skipUnmarshal bool 516 }{ 517 {data: int32(1), json: "1"}, 518 {data: proto.Int32(1), json: "1"}, 519 {data: int64(1), json: "1"}, 520 {data: proto.Int64(1), json: "1"}, 521 {data: uint32(1), json: "1"}, 522 {data: proto.Uint32(1), json: "1"}, 523 {data: uint64(1), json: "1"}, 524 {data: proto.Uint64(1), json: "1"}, 525 {data: "abc", json: `"abc"`}, 526 {data: []byte("abc"), json: `"YWJj"`}, 527 {data: []byte{}, json: `""`}, 528 {data: proto.String("abc"), json: `"abc"`}, 529 {data: float32(1.5), json: "1.5"}, 530 {data: proto.Float32(1.5), json: "1.5"}, 531 {data: float64(1.5), json: "1.5"}, 532 {data: proto.Float64(1.5), json: "1.5"}, 533 {data: true, json: "true"}, 534 {data: false, json: "false"}, 535 {data: (*string)(nil), json: "null"}, 536 { 537 data: examplepb.NumericEnum_ONE, 538 json: `"ONE"`, 539 // TODO(yugui) support unmarshaling of symbolic enum 540 skipUnmarshal: true, 541 }, 542 { 543 data: (*examplepb.NumericEnum)(proto.Int32(int32(examplepb.NumericEnum_ONE))), 544 json: `"ONE"`, 545 // TODO(yugui) support unmarshaling of symbolic enum 546 skipUnmarshal: true, 547 }, 548 549 { 550 data: map[string]int32{ 551 "foo": 1, 552 }, 553 json: `{"foo":1}`, 554 }, 555 { 556 data: map[string]*examplepb.SimpleMessage{ 557 "foo": {Id: "bar"}, 558 }, 559 json: `{"foo":{"id":"bar"}}`, 560 }, 561 { 562 data: map[int32]*examplepb.SimpleMessage{ 563 1: {Id: "foo"}, 564 }, 565 json: `{"1":{"id":"foo"}}`, 566 }, 567 { 568 data: map[bool]*examplepb.SimpleMessage{ 569 true: {Id: "foo"}, 570 }, 571 json: `{"true":{"id":"foo"}}`, 572 }, 573 { 574 data: &durationpb.Duration{ 575 Seconds: 123, 576 Nanos: 456000000, 577 }, 578 json: `"123.456s"`, 579 }, 580 { 581 data: ×tamppb.Timestamp{ 582 Seconds: 1462875553, 583 Nanos: 123000000, 584 }, 585 json: `"2016-05-10T10:19:13.123Z"`, 586 }, 587 { 588 data: new(emptypb.Empty), 589 json: "{}", 590 }, 591 { 592 data: &structpb.Value{ 593 Kind: new(structpb.Value_NullValue), 594 }, 595 json: "null", 596 skipUnmarshal: true, 597 }, 598 { 599 data: &structpb.Value{ 600 Kind: &structpb.Value_NumberValue{ 601 NumberValue: 123.4, 602 }, 603 }, 604 json: "123.4", 605 skipUnmarshal: true, 606 }, 607 { 608 data: &structpb.Value{ 609 Kind: &structpb.Value_StringValue{ 610 StringValue: "abc", 611 }, 612 }, 613 json: `"abc"`, 614 skipUnmarshal: true, 615 }, 616 { 617 data: &structpb.Value{ 618 Kind: &structpb.Value_BoolValue{ 619 BoolValue: true, 620 }, 621 }, 622 json: "true", 623 skipUnmarshal: true, 624 }, 625 { 626 data: &structpb.Struct{ 627 Fields: map[string]*structpb.Value{ 628 "foo_bar": { 629 Kind: &structpb.Value_BoolValue{ 630 BoolValue: true, 631 }, 632 }, 633 }, 634 }, 635 json: `{"foo_bar":true}`, 636 skipUnmarshal: true, 637 }, 638 639 { 640 data: wrapperspb.Bool(true), 641 json: "true", 642 }, 643 { 644 data: wrapperspb.Double(123.456), 645 json: "123.456", 646 }, 647 { 648 data: wrapperspb.Float(123.456), 649 json: "123.456", 650 }, 651 { 652 data: wrapperspb.Int32(-123), 653 json: "-123", 654 }, 655 { 656 data: wrapperspb.Int64(-123), 657 json: `"-123"`, 658 }, 659 { 660 data: wrapperspb.UInt32(123), 661 json: "123", 662 }, 663 { 664 data: wrapperspb.UInt64(123), 665 json: `"123"`, 666 }, 667 // TODO(yugui) Add other well-known types once jsonpb supports them 668 } 669 ) 670 671 func TestJSONPbUnmarshalNullField(t *testing.T) { 672 var out map[string]interface{} 673 674 const json = `{"foo": null}` 675 marshaler := &runtime.JSONPb{} 676 if err := marshaler.Unmarshal([]byte(json), &out); err != nil { 677 t.Fatalf("unexpected error: %v", err) 678 } 679 680 value, hasKey := out["foo"] 681 if !hasKey { 682 t.Fatalf("unmarshaled map did not have key 'foo'") 683 } 684 if value != nil { 685 t.Fatalf("unexpected value: %v", value) 686 } 687 } 688 689 func TestJSONPbMarshalResponseBodies(t *testing.T) { 690 marshaler := &runtime.JSONPb{} 691 for i, spec := range []struct { 692 input interface{} 693 emitUnpopulated bool 694 verifier func(*testing.T, interface{}, []byte) 695 }{ 696 { 697 input: &examplepb.ResponseBodyOut{ 698 Response: &examplepb.ResponseBodyOut_Response{Data: "abcdef"}, 699 }, 700 verifier: func(t *testing.T, input interface{}, json []byte) { 701 var out examplepb.ResponseBodyOut 702 err := marshaler.Unmarshal(json, &out) 703 if err != nil { 704 t.Fatalf("unexpected error: %v", err) 705 } 706 diff := cmp.Diff(input, &out, protocmp.Transform()) 707 if diff != "" { 708 t.Errorf("json not equal:\n%s", diff) 709 } 710 }, 711 }, 712 { 713 emitUnpopulated: true, 714 input: &examplepb.ResponseBodyOut{}, 715 verifier: func(t *testing.T, input interface{}, json []byte) { 716 var out examplepb.ResponseBodyOut 717 err := marshaler.Unmarshal(json, &out) 718 if err != nil { 719 t.Fatalf("unexpected error: %v", err) 720 } 721 diff := cmp.Diff(input, &out, protocmp.Transform()) 722 if diff != "" { 723 t.Errorf("json not equal:\n%s", diff) 724 } 725 }, 726 }, 727 { 728 input: &examplepb.RepeatedResponseBodyOut_Response{}, 729 verifier: func(t *testing.T, input interface{}, json []byte) { 730 var out examplepb.RepeatedResponseBodyOut_Response 731 err := marshaler.Unmarshal(json, &out) 732 if err != nil { 733 t.Fatalf("unexpected error: %v", err) 734 } 735 diff := cmp.Diff(input, &out, protocmp.Transform()) 736 if diff != "" { 737 t.Errorf("json not equal:\n%s", diff) 738 } 739 }, 740 }, 741 { 742 emitUnpopulated: true, 743 input: &examplepb.RepeatedResponseBodyOut_Response{}, 744 verifier: func(t *testing.T, input interface{}, json []byte) { 745 var out examplepb.RepeatedResponseBodyOut_Response 746 err := marshaler.Unmarshal(json, &out) 747 if err != nil { 748 t.Fatalf("unexpected error: %v", err) 749 } 750 diff := cmp.Diff(input, &out, protocmp.Transform()) 751 if diff != "" { 752 t.Errorf("json not equal:\n%s", diff) 753 } 754 }, 755 }, 756 { 757 input: ([]*examplepb.RepeatedResponseBodyOut_Response)(nil), 758 verifier: func(t *testing.T, input interface{}, json []byte) { 759 var out []*examplepb.RepeatedResponseBodyOut_Response 760 err := marshaler.Unmarshal(json, &out) 761 if err != nil { 762 t.Fatalf("unexpected error: %v", err) 763 } 764 diff := cmp.Diff(input, out, protocmp.Transform()) 765 if diff != "" { 766 t.Errorf("json not equal:\n%s", diff) 767 } 768 }, 769 }, 770 { 771 emitUnpopulated: true, 772 input: ([]*examplepb.RepeatedResponseBodyOut_Response)(nil), 773 verifier: func(t *testing.T, _ interface{}, json []byte) { 774 var out []*examplepb.RepeatedResponseBodyOut_Response 775 err := marshaler.Unmarshal(json, &out) 776 if err != nil { 777 t.Fatalf("unexpected error: %v", err) 778 } 779 diff := cmp.Diff([]*examplepb.RepeatedResponseBodyOut_Response{}, out, protocmp.Transform()) 780 if diff != "" { 781 t.Errorf("json not equal:\n%s", diff) 782 } 783 }, 784 }, 785 { 786 input: []*examplepb.RepeatedResponseBodyOut_Response{}, 787 verifier: func(t *testing.T, input interface{}, json []byte) { 788 var out []*examplepb.RepeatedResponseBodyOut_Response 789 err := marshaler.Unmarshal(json, &out) 790 if err != nil { 791 t.Fatalf("unexpected error: %v", err) 792 } 793 diff := cmp.Diff(input, out, protocmp.Transform()) 794 if diff != "" { 795 t.Errorf("json not equal:\n%s", diff) 796 } 797 }, 798 }, 799 { 800 input: []string{"something"}, 801 verifier: func(t *testing.T, input interface{}, json []byte) { 802 var out []string 803 err := marshaler.Unmarshal(json, &out) 804 if err != nil { 805 t.Fatalf("unexpected error: %v", err) 806 } 807 diff := cmp.Diff(input, out, protocmp.Transform()) 808 if diff != "" { 809 t.Errorf("json not equal:\n%s", diff) 810 } 811 }, 812 }, 813 { 814 input: []string{}, 815 verifier: func(t *testing.T, input interface{}, json []byte) { 816 var out []string 817 err := marshaler.Unmarshal(json, &out) 818 if err != nil { 819 t.Fatalf("unexpected error: %v", err) 820 } 821 diff := cmp.Diff(input, out, protocmp.Transform()) 822 if diff != "" { 823 t.Errorf("json not equal:\n%s", diff) 824 } 825 }, 826 }, 827 { 828 input: ([]string)(nil), 829 verifier: func(t *testing.T, input interface{}, json []byte) { 830 var out []string 831 err := marshaler.Unmarshal(json, &out) 832 if err != nil { 833 t.Fatalf("unexpected error: %v", err) 834 } 835 diff := cmp.Diff(input, out, protocmp.Transform()) 836 if diff != "" { 837 t.Errorf("json not equal:\n%s", diff) 838 } 839 }, 840 }, 841 { 842 emitUnpopulated: true, 843 input: ([]string)(nil), 844 verifier: func(t *testing.T, _ interface{}, json []byte) { 845 var out []string 846 err := marshaler.Unmarshal(json, &out) 847 if err != nil { 848 t.Fatalf("unexpected error: %v", err) 849 } 850 diff := cmp.Diff([]string{}, out, protocmp.Transform()) 851 if diff != "" { 852 t.Errorf("json not equal:\n%s", diff) 853 } 854 }, 855 }, 856 { 857 input: []*examplepb.RepeatedResponseBodyOut_Response{ 858 {}, 859 { 860 Data: "abc", 861 Type: examplepb.RepeatedResponseBodyOut_Response_A, 862 }, 863 }, 864 verifier: func(t *testing.T, input interface{}, json []byte) { 865 var out []*examplepb.RepeatedResponseBodyOut_Response 866 err := marshaler.Unmarshal(json, &out) 867 if err != nil { 868 t.Fatalf("unexpected error: %v", err) 869 } 870 diff := cmp.Diff(input, out, protocmp.Transform()) 871 if diff != "" { 872 t.Errorf("json not equal:\n%s", diff) 873 } 874 }, 875 }, 876 { 877 emitUnpopulated: true, 878 input: []*examplepb.RepeatedResponseBodyOut_Response{ 879 {}, 880 { 881 Data: "abc", 882 Type: examplepb.RepeatedResponseBodyOut_Response_B, 883 }, 884 }, 885 verifier: func(t *testing.T, input interface{}, json []byte) { 886 var out []*examplepb.RepeatedResponseBodyOut_Response 887 err := marshaler.Unmarshal(json, &out) 888 if err != nil { 889 t.Fatalf("unexpected error: %v", err) 890 } 891 diff := cmp.Diff(input, out, protocmp.Transform()) 892 if diff != "" { 893 t.Errorf("json not equal:\n%s", diff) 894 } 895 }, 896 }, 897 } { 898 899 t.Run(strconv.Itoa(i), func(t *testing.T) { 900 m := runtime.JSONPb{ 901 MarshalOptions: protojson.MarshalOptions{ 902 EmitUnpopulated: spec.emitUnpopulated, 903 }, 904 } 905 val := spec.input 906 buf, err := m.Marshal(val) 907 if err != nil { 908 t.Errorf("m.Marshal(%v) failed with %v; want success; spec=%v", val, err, spec) 909 } 910 if spec.verifier != nil { 911 spec.verifier(t, spec.input, buf) 912 } 913 }) 914 } 915 }