1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package protojson_test 6 7import ( 8 "math" 9 "strings" 10 "testing" 11 12 "google.golang.org/protobuf/encoding/protojson" 13 "google.golang.org/protobuf/internal/errors" 14 "google.golang.org/protobuf/internal/flags" 15 "google.golang.org/protobuf/proto" 16 "google.golang.org/protobuf/reflect/protoregistry" 17 18 testpb "google.golang.org/protobuf/internal/testprotos/test" 19 weakpb "google.golang.org/protobuf/internal/testprotos/test/weak1" 20 pb2 "google.golang.org/protobuf/internal/testprotos/textpb2" 21 pb3 "google.golang.org/protobuf/internal/testprotos/textpb3" 22 "google.golang.org/protobuf/types/known/anypb" 23 "google.golang.org/protobuf/types/known/durationpb" 24 "google.golang.org/protobuf/types/known/emptypb" 25 "google.golang.org/protobuf/types/known/fieldmaskpb" 26 "google.golang.org/protobuf/types/known/structpb" 27 "google.golang.org/protobuf/types/known/timestamppb" 28 "google.golang.org/protobuf/types/known/wrapperspb" 29) 30 31func TestUnmarshal(t *testing.T) { 32 tests := []struct { 33 desc string 34 umo protojson.UnmarshalOptions 35 inputMessage proto.Message 36 inputText string 37 wantMessage proto.Message 38 wantErr string // Expected error substring. 39 skip bool 40 }{{ 41 desc: "proto2 empty message", 42 inputMessage: &pb2.Scalars{}, 43 inputText: "{}", 44 wantMessage: &pb2.Scalars{}, 45 }, { 46 desc: "unexpected value instead of EOF", 47 inputMessage: &pb2.Scalars{}, 48 inputText: "{} {}", 49 wantErr: `(line 1:4): unexpected token {`, 50 }, { 51 desc: "proto2 optional scalars set to zero values", 52 inputMessage: &pb2.Scalars{}, 53 inputText: `{ 54 "optBool": false, 55 "optInt32": 0, 56 "optInt64": 0, 57 "optUint32": 0, 58 "optUint64": 0, 59 "optSint32": 0, 60 "optSint64": 0, 61 "optFixed32": 0, 62 "optFixed64": 0, 63 "optSfixed32": 0, 64 "optSfixed64": 0, 65 "optFloat": 0, 66 "optDouble": 0, 67 "optBytes": "", 68 "optString": "" 69}`, 70 wantMessage: &pb2.Scalars{ 71 OptBool: proto.Bool(false), 72 OptInt32: proto.Int32(0), 73 OptInt64: proto.Int64(0), 74 OptUint32: proto.Uint32(0), 75 OptUint64: proto.Uint64(0), 76 OptSint32: proto.Int32(0), 77 OptSint64: proto.Int64(0), 78 OptFixed32: proto.Uint32(0), 79 OptFixed64: proto.Uint64(0), 80 OptSfixed32: proto.Int32(0), 81 OptSfixed64: proto.Int64(0), 82 OptFloat: proto.Float32(0), 83 OptDouble: proto.Float64(0), 84 OptBytes: []byte{}, 85 OptString: proto.String(""), 86 }, 87 }, { 88 desc: "proto3 scalars set to zero values", 89 inputMessage: &pb3.Scalars{}, 90 inputText: `{ 91 "sBool": false, 92 "sInt32": 0, 93 "sInt64": 0, 94 "sUint32": 0, 95 "sUint64": 0, 96 "sSint32": 0, 97 "sSint64": 0, 98 "sFixed32": 0, 99 "sFixed64": 0, 100 "sSfixed32": 0, 101 "sSfixed64": 0, 102 "sFloat": 0, 103 "sDouble": 0, 104 "sBytes": "", 105 "sString": "" 106}`, 107 wantMessage: &pb3.Scalars{}, 108 }, { 109 desc: "proto3 optional set to zero values", 110 inputMessage: &pb3.Proto3Optional{}, 111 inputText: `{ 112 "optBool": false, 113 "optInt32": 0, 114 "optInt64": 0, 115 "optUint32": 0, 116 "optUint64": 0, 117 "optFloat": 0, 118 "optDouble": 0, 119 "optString": "", 120 "optBytes": "", 121 "optEnum": "ZERO", 122 "optMessage": {} 123}`, 124 wantMessage: &pb3.Proto3Optional{ 125 OptBool: proto.Bool(false), 126 OptInt32: proto.Int32(0), 127 OptInt64: proto.Int64(0), 128 OptUint32: proto.Uint32(0), 129 OptUint64: proto.Uint64(0), 130 OptFloat: proto.Float32(0), 131 OptDouble: proto.Float64(0), 132 OptString: proto.String(""), 133 OptBytes: []byte{}, 134 OptEnum: pb3.Enum_ZERO.Enum(), 135 OptMessage: &pb3.Nested{}, 136 }, 137 }, { 138 desc: "proto2 optional scalars set to null", 139 inputMessage: &pb2.Scalars{}, 140 inputText: `{ 141 "optBool": null, 142 "optInt32": null, 143 "optInt64": null, 144 "optUint32": null, 145 "optUint64": null, 146 "optSint32": null, 147 "optSint64": null, 148 "optFixed32": null, 149 "optFixed64": null, 150 "optSfixed32": null, 151 "optSfixed64": null, 152 "optFloat": null, 153 "optDouble": null, 154 "optBytes": null, 155 "optString": null 156}`, 157 wantMessage: &pb2.Scalars{}, 158 }, { 159 desc: "proto3 scalars set to null", 160 inputMessage: &pb3.Scalars{}, 161 inputText: `{ 162 "sBool": null, 163 "sInt32": null, 164 "sInt64": null, 165 "sUint32": null, 166 "sUint64": null, 167 "sSint32": null, 168 "sSint64": null, 169 "sFixed32": null, 170 "sFixed64": null, 171 "sSfixed32": null, 172 "sSfixed64": null, 173 "sFloat": null, 174 "sDouble": null, 175 "sBytes": null, 176 "sString": null 177}`, 178 wantMessage: &pb3.Scalars{}, 179 }, { 180 desc: "boolean", 181 inputMessage: &pb3.Scalars{}, 182 inputText: `{"sBool": true}`, 183 wantMessage: &pb3.Scalars{ 184 SBool: true, 185 }, 186 }, { 187 desc: "not boolean", 188 inputMessage: &pb3.Scalars{}, 189 inputText: `{"sBool": "true"}`, 190 wantErr: `invalid value for bool type: "true"`, 191 }, { 192 desc: "float and double", 193 inputMessage: &pb3.Scalars{}, 194 inputText: `{ 195 "sFloat": 1.234, 196 "sDouble": 5.678 197}`, 198 wantMessage: &pb3.Scalars{ 199 SFloat: 1.234, 200 SDouble: 5.678, 201 }, 202 }, { 203 desc: "float and double in string", 204 inputMessage: &pb3.Scalars{}, 205 inputText: `{ 206 "sFloat": "1.234", 207 "sDouble": "5.678" 208}`, 209 wantMessage: &pb3.Scalars{ 210 SFloat: 1.234, 211 SDouble: 5.678, 212 }, 213 }, { 214 desc: "float and double in E notation", 215 inputMessage: &pb3.Scalars{}, 216 inputText: `{ 217 "sFloat": 12.34E-1, 218 "sDouble": 5.678e4 219}`, 220 wantMessage: &pb3.Scalars{ 221 SFloat: 1.234, 222 SDouble: 56780, 223 }, 224 }, { 225 desc: "float and double in string E notation", 226 inputMessage: &pb3.Scalars{}, 227 inputText: `{ 228 "sFloat": "12.34E-1", 229 "sDouble": "5.678e4" 230}`, 231 wantMessage: &pb3.Scalars{ 232 SFloat: 1.234, 233 SDouble: 56780, 234 }, 235 }, { 236 desc: "float exceeds limit", 237 inputMessage: &pb3.Scalars{}, 238 inputText: `{"sFloat": 3.4e39}`, 239 wantErr: `invalid value for float type: 3.4e39`, 240 }, { 241 desc: "float in string exceeds limit", 242 inputMessage: &pb3.Scalars{}, 243 inputText: `{"sFloat": "-3.4e39"}`, 244 wantErr: `invalid value for float type: "-3.4e39"`, 245 }, { 246 desc: "double exceeds limit", 247 inputMessage: &pb3.Scalars{}, 248 inputText: `{"sDouble": -1.79e+309}`, 249 wantErr: `invalid value for double type: -1.79e+309`, 250 }, { 251 desc: "double in string exceeds limit", 252 inputMessage: &pb3.Scalars{}, 253 inputText: `{"sDouble": "1.79e+309"}`, 254 wantErr: `invalid value for double type: "1.79e+309"`, 255 }, { 256 desc: "infinites", 257 inputMessage: &pb3.Scalars{}, 258 inputText: `{"sFloat": "Infinity", "sDouble": "-Infinity"}`, 259 wantMessage: &pb3.Scalars{ 260 SFloat: float32(math.Inf(+1)), 261 SDouble: math.Inf(-1), 262 }, 263 }, { 264 desc: "float string with leading space", 265 inputMessage: &pb3.Scalars{}, 266 inputText: `{"sFloat": " 1.234"}`, 267 wantErr: `invalid value for float type: " 1.234"`, 268 }, { 269 desc: "double string with trailing space", 270 inputMessage: &pb3.Scalars{}, 271 inputText: `{"sDouble": "5.678 "}`, 272 wantErr: `invalid value for double type: "5.678 "`, 273 }, { 274 desc: "not float", 275 inputMessage: &pb3.Scalars{}, 276 inputText: `{"sFloat": true}`, 277 wantErr: `invalid value for float type: true`, 278 }, { 279 desc: "not double", 280 inputMessage: &pb3.Scalars{}, 281 inputText: `{"sDouble": "not a number"}`, 282 wantErr: `invalid value for double type: "not a number"`, 283 }, { 284 desc: "integers", 285 inputMessage: &pb3.Scalars{}, 286 inputText: `{ 287 "sInt32": 1234, 288 "sInt64": -1234, 289 "sUint32": 1e2, 290 "sUint64": 100E-2, 291 "sSint32": 1.0, 292 "sSint64": -1.0, 293 "sFixed32": 1.234e+5, 294 "sFixed64": 1200E-2, 295 "sSfixed32": -1.234e05, 296 "sSfixed64": -1200e-02 297}`, 298 wantMessage: &pb3.Scalars{ 299 SInt32: 1234, 300 SInt64: -1234, 301 SUint32: 100, 302 SUint64: 1, 303 SSint32: 1, 304 SSint64: -1, 305 SFixed32: 123400, 306 SFixed64: 12, 307 SSfixed32: -123400, 308 SSfixed64: -12, 309 }, 310 }, { 311 desc: "integers in string", 312 inputMessage: &pb3.Scalars{}, 313 inputText: `{ 314 "sInt32": "1234", 315 "sInt64": "-1234", 316 "sUint32": "1e2", 317 "sUint64": "100E-2", 318 "sSint32": "1.0", 319 "sSint64": "-1.0", 320 "sFixed32": "1.234e+5", 321 "sFixed64": "1200E-2", 322 "sSfixed32": "-1.234e05", 323 "sSfixed64": "-1200e-02" 324}`, 325 wantMessage: &pb3.Scalars{ 326 SInt32: 1234, 327 SInt64: -1234, 328 SUint32: 100, 329 SUint64: 1, 330 SSint32: 1, 331 SSint64: -1, 332 SFixed32: 123400, 333 SFixed64: 12, 334 SSfixed32: -123400, 335 SSfixed64: -12, 336 }, 337 }, { 338 desc: "integers in escaped string", 339 inputMessage: &pb3.Scalars{}, 340 inputText: `{"sInt32": "\u0031\u0032"}`, 341 wantMessage: &pb3.Scalars{ 342 SInt32: 12, 343 }, 344 }, { 345 desc: "integer string with leading space", 346 inputMessage: &pb3.Scalars{}, 347 inputText: `{"sInt32": " 1234"}`, 348 wantErr: `invalid value for int32 type: " 1234"`, 349 }, { 350 desc: "integer string with trailing space", 351 inputMessage: &pb3.Scalars{}, 352 inputText: `{"sUint32": "1e2 "}`, 353 wantErr: `invalid value for uint32 type: "1e2 "`, 354 }, { 355 desc: "number is not an integer", 356 inputMessage: &pb3.Scalars{}, 357 inputText: `{"sInt32": 1.001}`, 358 wantErr: `invalid value for int32 type: 1.001`, 359 }, { 360 desc: "32-bit int exceeds limit", 361 inputMessage: &pb3.Scalars{}, 362 inputText: `{"sInt32": 2e10}`, 363 wantErr: `invalid value for int32 type: 2e10`, 364 }, { 365 desc: "64-bit int exceeds limit", 366 inputMessage: &pb3.Scalars{}, 367 inputText: `{"sSfixed64": -9e19}`, 368 wantErr: `invalid value for sfixed64 type: -9e19`, 369 }, { 370 desc: "not integer", 371 inputMessage: &pb3.Scalars{}, 372 inputText: `{"sInt32": "not a number"}`, 373 wantErr: `invalid value for int32 type: "not a number"`, 374 }, { 375 desc: "not unsigned integer", 376 inputMessage: &pb3.Scalars{}, 377 inputText: `{"sUint32": "not a number"}`, 378 wantErr: `invalid value for uint32 type: "not a number"`, 379 }, { 380 desc: "number is not an unsigned integer", 381 inputMessage: &pb3.Scalars{}, 382 inputText: `{"sUint32": -1}`, 383 wantErr: `invalid value for uint32 type: -1`, 384 }, { 385 desc: "string", 386 inputMessage: &pb2.Scalars{}, 387 inputText: `{"optString": "谷歌"}`, 388 wantMessage: &pb2.Scalars{ 389 OptString: proto.String("谷歌"), 390 }, 391 }, { 392 desc: "string with invalid UTF-8", 393 inputMessage: &pb3.Scalars{}, 394 inputText: "{\"sString\": \"\xff\"}", 395 wantErr: `(line 1:13): invalid UTF-8 in string`, 396 }, { 397 desc: "not string", 398 inputMessage: &pb2.Scalars{}, 399 inputText: `{"optString": 42}`, 400 wantErr: `invalid value for string type: 42`, 401 }, { 402 desc: "bytes", 403 inputMessage: &pb3.Scalars{}, 404 inputText: `{"sBytes": "aGVsbG8gd29ybGQ"}`, 405 wantMessage: &pb3.Scalars{ 406 SBytes: []byte("hello world"), 407 }, 408 }, { 409 desc: "bytes padded", 410 inputMessage: &pb3.Scalars{}, 411 inputText: `{"sBytes": "aGVsbG8gd29ybGQ="}`, 412 wantMessage: &pb3.Scalars{ 413 SBytes: []byte("hello world"), 414 }, 415 }, { 416 desc: "not bytes", 417 inputMessage: &pb3.Scalars{}, 418 inputText: `{"sBytes": true}`, 419 wantErr: `invalid value for bytes type: true`, 420 }, { 421 desc: "proto2 enum", 422 inputMessage: &pb2.Enums{}, 423 inputText: `{ 424 "optEnum": "ONE", 425 "optNestedEnum": "UNO" 426}`, 427 wantMessage: &pb2.Enums{ 428 OptEnum: pb2.Enum_ONE.Enum(), 429 OptNestedEnum: pb2.Enums_UNO.Enum(), 430 }, 431 }, { 432 desc: "proto3 enum", 433 inputMessage: &pb3.Enums{}, 434 inputText: `{ 435 "sEnum": "ONE", 436 "sNestedEnum": "DIEZ" 437}`, 438 wantMessage: &pb3.Enums{ 439 SEnum: pb3.Enum_ONE, 440 SNestedEnum: pb3.Enums_DIEZ, 441 }, 442 }, { 443 desc: "enum numeric value", 444 inputMessage: &pb3.Enums{}, 445 inputText: `{ 446 "sEnum": 2, 447 "sNestedEnum": 2 448}`, 449 wantMessage: &pb3.Enums{ 450 SEnum: pb3.Enum_TWO, 451 SNestedEnum: pb3.Enums_DOS, 452 }, 453 }, { 454 desc: "enum unnamed numeric value", 455 inputMessage: &pb3.Enums{}, 456 inputText: `{ 457 "sEnum": 101, 458 "sNestedEnum": -101 459}`, 460 wantMessage: &pb3.Enums{ 461 SEnum: 101, 462 SNestedEnum: -101, 463 }, 464 }, { 465 desc: "enum set to number string", 466 inputMessage: &pb3.Enums{}, 467 inputText: `{ 468 "sEnum": "1" 469}`, 470 wantErr: `invalid value for enum type: "1"`, 471 }, { 472 desc: "enum set to invalid named", 473 inputMessage: &pb3.Enums{}, 474 inputText: `{ 475 "sEnum": "UNNAMED" 476}`, 477 wantErr: `invalid value for enum type: "UNNAMED"`, 478 }, { 479 desc: "enum set to not enum", 480 inputMessage: &pb3.Enums{}, 481 inputText: `{ 482 "sEnum": true 483}`, 484 wantErr: `invalid value for enum type: true`, 485 }, { 486 desc: "enum set to JSON null", 487 inputMessage: &pb3.Enums{}, 488 inputText: `{ 489 "sEnum": null 490}`, 491 wantMessage: &pb3.Enums{}, 492 }, { 493 desc: "proto name", 494 inputMessage: &pb3.JSONNames{}, 495 inputText: `{ 496 "s_string": "proto name used" 497}`, 498 wantMessage: &pb3.JSONNames{ 499 SString: "proto name used", 500 }, 501 }, { 502 desc: "proto group name", 503 inputMessage: &pb2.Nests{}, 504 inputText: `{ 505 "OptGroup": {"optString": "hello"}, 506 "RptGroup": [{"rptString": ["goodbye"]}] 507 }`, 508 wantMessage: &pb2.Nests{ 509 Optgroup: &pb2.Nests_OptGroup{OptString: proto.String("hello")}, 510 Rptgroup: []*pb2.Nests_RptGroup{{RptString: []string{"goodbye"}}}, 511 }, 512 }, { 513 desc: "json_name", 514 inputMessage: &pb3.JSONNames{}, 515 inputText: `{ 516 "foo_bar": "json_name used" 517}`, 518 wantMessage: &pb3.JSONNames{ 519 SString: "json_name used", 520 }, 521 }, { 522 desc: "camelCase name", 523 inputMessage: &pb3.JSONNames{}, 524 inputText: `{ 525 "sString": "camelcase used" 526}`, 527 wantErr: `unknown field "sString"`, 528 }, { 529 desc: "proto name and json_name", 530 inputMessage: &pb3.JSONNames{}, 531 inputText: `{ 532 "foo_bar": "json_name used", 533 "s_string": "proto name used" 534}`, 535 wantErr: `(line 3:3): duplicate field "s_string"`, 536 }, { 537 desc: "duplicate field names", 538 inputMessage: &pb3.JSONNames{}, 539 inputText: `{ 540 "foo_bar": "one", 541 "foo_bar": "two", 542}`, 543 wantErr: `(line 3:3): duplicate field "foo_bar"`, 544 }, { 545 desc: "null message", 546 inputMessage: &pb2.Nests{}, 547 inputText: "null", 548 wantErr: `unexpected token null`, 549 }, { 550 desc: "proto2 nested message not set", 551 inputMessage: &pb2.Nests{}, 552 inputText: "{}", 553 wantMessage: &pb2.Nests{}, 554 }, { 555 desc: "proto2 nested message set to null", 556 inputMessage: &pb2.Nests{}, 557 inputText: `{ 558 "optNested": null, 559 "optgroup": null 560}`, 561 wantMessage: &pb2.Nests{}, 562 }, { 563 desc: "proto2 nested message set to empty", 564 inputMessage: &pb2.Nests{}, 565 inputText: `{ 566 "optNested": {}, 567 "optgroup": {} 568}`, 569 wantMessage: &pb2.Nests{ 570 OptNested: &pb2.Nested{}, 571 Optgroup: &pb2.Nests_OptGroup{}, 572 }, 573 }, { 574 desc: "proto2 nested messages", 575 inputMessage: &pb2.Nests{}, 576 inputText: `{ 577 "optNested": { 578 "optString": "nested message", 579 "optNested": { 580 "optString": "another nested message" 581 } 582 } 583}`, 584 wantMessage: &pb2.Nests{ 585 OptNested: &pb2.Nested{ 586 OptString: proto.String("nested message"), 587 OptNested: &pb2.Nested{ 588 OptString: proto.String("another nested message"), 589 }, 590 }, 591 }, 592 }, { 593 desc: "proto2 groups", 594 inputMessage: &pb2.Nests{}, 595 inputText: `{ 596 "optgroup": { 597 "optString": "inside a group", 598 "optNested": { 599 "optString": "nested message inside a group" 600 }, 601 "optnestedgroup": { 602 "optFixed32": 47 603 } 604 } 605}`, 606 wantMessage: &pb2.Nests{ 607 Optgroup: &pb2.Nests_OptGroup{ 608 OptString: proto.String("inside a group"), 609 OptNested: &pb2.Nested{ 610 OptString: proto.String("nested message inside a group"), 611 }, 612 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{ 613 OptFixed32: proto.Uint32(47), 614 }, 615 }, 616 }, 617 }, { 618 desc: "proto3 nested message not set", 619 inputMessage: &pb3.Nests{}, 620 inputText: "{}", 621 wantMessage: &pb3.Nests{}, 622 }, { 623 desc: "proto3 nested message set to null", 624 inputMessage: &pb3.Nests{}, 625 inputText: `{"sNested": null}`, 626 wantMessage: &pb3.Nests{}, 627 }, { 628 desc: "proto3 nested message set to empty", 629 inputMessage: &pb3.Nests{}, 630 inputText: `{"sNested": {}}`, 631 wantMessage: &pb3.Nests{ 632 SNested: &pb3.Nested{}, 633 }, 634 }, { 635 desc: "proto3 nested message", 636 inputMessage: &pb3.Nests{}, 637 inputText: `{ 638 "sNested": { 639 "sString": "nested message", 640 "sNested": { 641 "sString": "another nested message" 642 } 643 } 644}`, 645 wantMessage: &pb3.Nests{ 646 SNested: &pb3.Nested{ 647 SString: "nested message", 648 SNested: &pb3.Nested{ 649 SString: "another nested message", 650 }, 651 }, 652 }, 653 }, { 654 desc: "message set to non-message", 655 inputMessage: &pb3.Nests{}, 656 inputText: `"not valid"`, 657 wantErr: `unexpected token "not valid"`, 658 }, { 659 desc: "nested message set to non-message", 660 inputMessage: &pb3.Nests{}, 661 inputText: `{"sNested": true}`, 662 wantErr: `(line 1:13): unexpected token true`, 663 }, { 664 desc: "oneof not set", 665 inputMessage: &pb3.Oneofs{}, 666 inputText: "{}", 667 wantMessage: &pb3.Oneofs{}, 668 }, { 669 desc: "oneof set to empty string", 670 inputMessage: &pb3.Oneofs{}, 671 inputText: `{"oneofString": ""}`, 672 wantMessage: &pb3.Oneofs{ 673 Union: &pb3.Oneofs_OneofString{}, 674 }, 675 }, { 676 desc: "oneof set to string", 677 inputMessage: &pb3.Oneofs{}, 678 inputText: `{"oneofString": "hello"}`, 679 wantMessage: &pb3.Oneofs{ 680 Union: &pb3.Oneofs_OneofString{ 681 OneofString: "hello", 682 }, 683 }, 684 }, { 685 desc: "oneof set to enum", 686 inputMessage: &pb3.Oneofs{}, 687 inputText: `{"oneofEnum": "ZERO"}`, 688 wantMessage: &pb3.Oneofs{ 689 Union: &pb3.Oneofs_OneofEnum{ 690 OneofEnum: pb3.Enum_ZERO, 691 }, 692 }, 693 }, { 694 desc: "oneof set to empty message", 695 inputMessage: &pb3.Oneofs{}, 696 inputText: `{"oneofNested": {}}`, 697 wantMessage: &pb3.Oneofs{ 698 Union: &pb3.Oneofs_OneofNested{ 699 OneofNested: &pb3.Nested{}, 700 }, 701 }, 702 }, { 703 desc: "oneof set to message", 704 inputMessage: &pb3.Oneofs{}, 705 inputText: `{ 706 "oneofNested": { 707 "sString": "nested message" 708 } 709}`, 710 wantMessage: &pb3.Oneofs{ 711 Union: &pb3.Oneofs_OneofNested{ 712 OneofNested: &pb3.Nested{ 713 SString: "nested message", 714 }, 715 }, 716 }, 717 }, { 718 desc: "oneof set to more than one field", 719 inputMessage: &pb3.Oneofs{}, 720 inputText: `{ 721 "oneofEnum": "ZERO", 722 "oneofString": "hello" 723}`, 724 wantErr: `(line 3:3): error parsing "oneofString", oneof pb3.Oneofs.union is already set`, 725 }, { 726 desc: "oneof set to null and value", 727 inputMessage: &pb3.Oneofs{}, 728 inputText: `{ 729 "oneofEnum": "ZERO", 730 "oneofString": null 731}`, 732 wantMessage: &pb3.Oneofs{ 733 Union: &pb3.Oneofs_OneofEnum{ 734 OneofEnum: pb3.Enum_ZERO, 735 }, 736 }, 737 }, { 738 desc: "repeated null fields", 739 inputMessage: &pb2.Repeats{}, 740 inputText: `{ 741 "rptString": null, 742 "rptInt32" : null, 743 "rptFloat" : null, 744 "rptBytes" : null 745}`, 746 wantMessage: &pb2.Repeats{}, 747 }, { 748 desc: "repeated scalars", 749 inputMessage: &pb2.Repeats{}, 750 inputText: `{ 751 "rptString": ["hello", "world"], 752 "rptInt32" : [-1, 0, 1], 753 "rptBool" : [false, true] 754}`, 755 wantMessage: &pb2.Repeats{ 756 RptString: []string{"hello", "world"}, 757 RptInt32: []int32{-1, 0, 1}, 758 RptBool: []bool{false, true}, 759 }, 760 }, { 761 desc: "repeated enums", 762 inputMessage: &pb2.Enums{}, 763 inputText: `{ 764 "rptEnum" : ["TEN", 1, 42], 765 "rptNestedEnum": ["DOS", 2, -47] 766}`, 767 wantMessage: &pb2.Enums{ 768 RptEnum: []pb2.Enum{pb2.Enum_TEN, pb2.Enum_ONE, 42}, 769 RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_DOS, pb2.Enums_DOS, -47}, 770 }, 771 }, { 772 desc: "repeated messages", 773 inputMessage: &pb2.Nests{}, 774 inputText: `{ 775 "rptNested": [ 776 { 777 "optString": "repeat nested one" 778 }, 779 { 780 "optString": "repeat nested two", 781 "optNested": { 782 "optString": "inside repeat nested two" 783 } 784 }, 785 {} 786 ] 787}`, 788 wantMessage: &pb2.Nests{ 789 RptNested: []*pb2.Nested{ 790 { 791 OptString: proto.String("repeat nested one"), 792 }, 793 { 794 OptString: proto.String("repeat nested two"), 795 OptNested: &pb2.Nested{ 796 OptString: proto.String("inside repeat nested two"), 797 }, 798 }, 799 {}, 800 }, 801 }, 802 }, { 803 desc: "repeated groups", 804 inputMessage: &pb2.Nests{}, 805 inputText: `{ 806 "rptgroup": [ 807 { 808 "rptString": ["hello", "world"] 809 }, 810 {} 811 ] 812} 813`, 814 wantMessage: &pb2.Nests{ 815 Rptgroup: []*pb2.Nests_RptGroup{ 816 { 817 RptString: []string{"hello", "world"}, 818 }, 819 {}, 820 }, 821 }, 822 }, { 823 desc: "repeated string contains invalid UTF8", 824 inputMessage: &pb2.Repeats{}, 825 inputText: `{"rptString": ["` + "abc\xff" + `"]}`, 826 wantErr: `invalid UTF-8`, 827 }, { 828 desc: "repeated messages contain invalid UTF8", 829 inputMessage: &pb2.Nests{}, 830 inputText: `{"rptNested": [{"optString": "` + "abc\xff" + `"}]}`, 831 wantErr: `invalid UTF-8`, 832 }, { 833 desc: "repeated scalars contain invalid type", 834 inputMessage: &pb2.Repeats{}, 835 inputText: `{"rptString": ["hello", null, "world"]}`, 836 wantErr: `invalid value for string type: null`, 837 }, { 838 desc: "repeated messages contain invalid type", 839 inputMessage: &pb2.Nests{}, 840 inputText: `{"rptNested": [{}, null]}`, 841 wantErr: `unexpected token null`, 842 }, { 843 desc: "map fields 1", 844 inputMessage: &pb3.Maps{}, 845 inputText: `{ 846 "int32ToStr": { 847 "-101": "-101", 848 "0" : "zero", 849 "255" : "0xff" 850 }, 851 "boolToUint32": { 852 "false": 101, 853 "true" : "42" 854 } 855}`, 856 wantMessage: &pb3.Maps{ 857 Int32ToStr: map[int32]string{ 858 -101: "-101", 859 0xff: "0xff", 860 0: "zero", 861 }, 862 BoolToUint32: map[bool]uint32{ 863 true: 42, 864 false: 101, 865 }, 866 }, 867 }, { 868 desc: "map fields 2", 869 inputMessage: &pb3.Maps{}, 870 inputText: `{ 871 "uint64ToEnum": { 872 "1" : "ONE", 873 "2" : 2, 874 "10": 101 875 } 876}`, 877 wantMessage: &pb3.Maps{ 878 Uint64ToEnum: map[uint64]pb3.Enum{ 879 1: pb3.Enum_ONE, 880 2: pb3.Enum_TWO, 881 10: 101, 882 }, 883 }, 884 }, { 885 desc: "map fields 3", 886 inputMessage: &pb3.Maps{}, 887 inputText: `{ 888 "strToNested": { 889 "nested_one": { 890 "sString": "nested in a map" 891 }, 892 "nested_two": {} 893 } 894}`, 895 wantMessage: &pb3.Maps{ 896 StrToNested: map[string]*pb3.Nested{ 897 "nested_one": { 898 SString: "nested in a map", 899 }, 900 "nested_two": {}, 901 }, 902 }, 903 }, { 904 desc: "map fields 4", 905 inputMessage: &pb3.Maps{}, 906 inputText: `{ 907 "strToOneofs": { 908 "nested": { 909 "oneofNested": { 910 "sString": "nested oneof in map field value" 911 } 912 }, 913 "string": { 914 "oneofString": "hello" 915 } 916 } 917}`, 918 wantMessage: &pb3.Maps{ 919 StrToOneofs: map[string]*pb3.Oneofs{ 920 "string": { 921 Union: &pb3.Oneofs_OneofString{ 922 OneofString: "hello", 923 }, 924 }, 925 "nested": { 926 Union: &pb3.Oneofs_OneofNested{ 927 OneofNested: &pb3.Nested{ 928 SString: "nested oneof in map field value", 929 }, 930 }, 931 }, 932 }, 933 }, 934 }, { 935 desc: "map contains duplicate keys", 936 inputMessage: &pb3.Maps{}, 937 inputText: `{ 938 "int32ToStr": { 939 "0": "cero", 940 "0": "zero" 941 } 942} 943`, 944 wantErr: `(line 4:5): duplicate map key "0"`, 945 }, { 946 desc: "map key empty string", 947 inputMessage: &pb3.Maps{}, 948 inputText: `{ 949 "strToNested": { 950 "": {} 951 } 952}`, 953 wantMessage: &pb3.Maps{ 954 StrToNested: map[string]*pb3.Nested{ 955 "": {}, 956 }, 957 }, 958 }, { 959 desc: "map contains invalid key 1", 960 inputMessage: &pb3.Maps{}, 961 inputText: `{ 962 "int32ToStr": { 963 "invalid": "cero" 964 } 965}`, 966 wantErr: `invalid value for int32 key: "invalid"`, 967 }, { 968 desc: "map contains invalid key 2", 969 inputMessage: &pb3.Maps{}, 970 inputText: `{ 971 "int32ToStr": { 972 "1.02": "float" 973 } 974}`, 975 wantErr: `invalid value for int32 key: "1.02"`, 976 }, { 977 desc: "map contains invalid key 3", 978 inputMessage: &pb3.Maps{}, 979 inputText: `{ 980 "int32ToStr": { 981 "2147483648": "exceeds 32-bit integer max limit" 982 } 983}`, 984 wantErr: `invalid value for int32 key: "2147483648"`, 985 }, { 986 desc: "map contains invalid key 4", 987 inputMessage: &pb3.Maps{}, 988 inputText: `{ 989 "uint64ToEnum": { 990 "-1": 0 991 } 992}`, 993 wantErr: `invalid value for uint64 key: "-1"`, 994 }, { 995 desc: "map contains invalid value", 996 inputMessage: &pb3.Maps{}, 997 inputText: `{ 998 "int32ToStr": { 999 "101": true 1000}`, 1001 wantErr: `invalid value for string type: true`, 1002 }, { 1003 desc: "map contains null for scalar value", 1004 inputMessage: &pb3.Maps{}, 1005 inputText: `{ 1006 "int32ToStr": { 1007 "101": null 1008}`, 1009 wantErr: `invalid value for string type: null`, 1010 }, { 1011 desc: "map contains null for message value", 1012 inputMessage: &pb3.Maps{}, 1013 inputText: `{ 1014 "strToNested": { 1015 "hello": null 1016 } 1017}`, 1018 wantErr: `unexpected token null`, 1019 }, { 1020 desc: "map contains contains message value with invalid UTF8", 1021 inputMessage: &pb3.Maps{}, 1022 inputText: `{ 1023 "strToNested": { 1024 "hello": { 1025 "sString": "` + "abc\xff" + `" 1026 } 1027 } 1028}`, 1029 wantErr: `invalid UTF-8`, 1030 }, { 1031 desc: "map key contains invalid UTF8", 1032 inputMessage: &pb3.Maps{}, 1033 inputText: `{ 1034 "strToNested": { 1035 "` + "abc\xff" + `": {} 1036 } 1037}`, 1038 wantErr: `invalid UTF-8`, 1039 }, { 1040 desc: "required fields not set", 1041 inputMessage: &pb2.Requireds{}, 1042 inputText: `{}`, 1043 wantErr: errors.RequiredNotSet("pb2.Requireds.req_bool").Error(), 1044 }, { 1045 desc: "required field set", 1046 inputMessage: &pb2.PartialRequired{}, 1047 inputText: `{ 1048 "reqString": "this is required" 1049}`, 1050 wantMessage: &pb2.PartialRequired{ 1051 ReqString: proto.String("this is required"), 1052 }, 1053 }, { 1054 desc: "required fields partially set", 1055 inputMessage: &pb2.Requireds{}, 1056 inputText: `{ 1057 "reqBool": false, 1058 "reqSfixed64": 42, 1059 "reqString": "hello", 1060 "reqEnum": "ONE" 1061}`, 1062 wantMessage: &pb2.Requireds{ 1063 ReqBool: proto.Bool(false), 1064 ReqSfixed64: proto.Int64(42), 1065 ReqString: proto.String("hello"), 1066 ReqEnum: pb2.Enum_ONE.Enum(), 1067 }, 1068 wantErr: errors.RequiredNotSet("pb2.Requireds.req_double").Error(), 1069 }, { 1070 desc: "required fields partially set with AllowPartial", 1071 umo: protojson.UnmarshalOptions{AllowPartial: true}, 1072 inputMessage: &pb2.Requireds{}, 1073 inputText: `{ 1074 "reqBool": false, 1075 "reqSfixed64": 42, 1076 "reqString": "hello", 1077 "reqEnum": "ONE" 1078}`, 1079 wantMessage: &pb2.Requireds{ 1080 ReqBool: proto.Bool(false), 1081 ReqSfixed64: proto.Int64(42), 1082 ReqString: proto.String("hello"), 1083 ReqEnum: pb2.Enum_ONE.Enum(), 1084 }, 1085 }, { 1086 desc: "required fields all set", 1087 inputMessage: &pb2.Requireds{}, 1088 inputText: `{ 1089 "reqBool": false, 1090 "reqSfixed64": 42, 1091 "reqDouble": 1.23, 1092 "reqString": "hello", 1093 "reqEnum": "ONE", 1094 "reqNested": {} 1095}`, 1096 wantMessage: &pb2.Requireds{ 1097 ReqBool: proto.Bool(false), 1098 ReqSfixed64: proto.Int64(42), 1099 ReqDouble: proto.Float64(1.23), 1100 ReqString: proto.String("hello"), 1101 ReqEnum: pb2.Enum_ONE.Enum(), 1102 ReqNested: &pb2.Nested{}, 1103 }, 1104 }, { 1105 desc: "indirect required field", 1106 inputMessage: &pb2.IndirectRequired{}, 1107 inputText: `{ 1108 "optNested": {} 1109}`, 1110 wantMessage: &pb2.IndirectRequired{ 1111 OptNested: &pb2.NestedWithRequired{}, 1112 }, 1113 wantErr: errors.RequiredNotSet("pb2.NestedWithRequired.req_string").Error(), 1114 }, { 1115 desc: "indirect required field with AllowPartial", 1116 umo: protojson.UnmarshalOptions{AllowPartial: true}, 1117 inputMessage: &pb2.IndirectRequired{}, 1118 inputText: `{ 1119 "optNested": {} 1120}`, 1121 wantMessage: &pb2.IndirectRequired{ 1122 OptNested: &pb2.NestedWithRequired{}, 1123 }, 1124 }, { 1125 desc: "indirect required field in repeated", 1126 inputMessage: &pb2.IndirectRequired{}, 1127 inputText: `{ 1128 "rptNested": [ 1129 {"reqString": "one"}, 1130 {} 1131 ] 1132}`, 1133 wantMessage: &pb2.IndirectRequired{ 1134 RptNested: []*pb2.NestedWithRequired{ 1135 { 1136 ReqString: proto.String("one"), 1137 }, 1138 {}, 1139 }, 1140 }, 1141 wantErr: errors.RequiredNotSet("pb2.NestedWithRequired.req_string").Error(), 1142 }, { 1143 desc: "indirect required field in repeated with AllowPartial", 1144 umo: protojson.UnmarshalOptions{AllowPartial: true}, 1145 inputMessage: &pb2.IndirectRequired{}, 1146 inputText: `{ 1147 "rptNested": [ 1148 {"reqString": "one"}, 1149 {} 1150 ] 1151}`, 1152 wantMessage: &pb2.IndirectRequired{ 1153 RptNested: []*pb2.NestedWithRequired{ 1154 { 1155 ReqString: proto.String("one"), 1156 }, 1157 {}, 1158 }, 1159 }, 1160 }, { 1161 desc: "indirect required field in map", 1162 inputMessage: &pb2.IndirectRequired{}, 1163 inputText: `{ 1164 "strToNested": { 1165 "missing": {}, 1166 "contains": { 1167 "reqString": "here" 1168 } 1169 } 1170}`, 1171 wantMessage: &pb2.IndirectRequired{ 1172 StrToNested: map[string]*pb2.NestedWithRequired{ 1173 "missing": &pb2.NestedWithRequired{}, 1174 "contains": &pb2.NestedWithRequired{ 1175 ReqString: proto.String("here"), 1176 }, 1177 }, 1178 }, 1179 wantErr: errors.RequiredNotSet("pb2.NestedWithRequired.req_string").Error(), 1180 }, { 1181 desc: "indirect required field in map with AllowPartial", 1182 umo: protojson.UnmarshalOptions{AllowPartial: true}, 1183 inputMessage: &pb2.IndirectRequired{}, 1184 inputText: `{ 1185 "strToNested": { 1186 "missing": {}, 1187 "contains": { 1188 "reqString": "here" 1189 } 1190 } 1191}`, 1192 wantMessage: &pb2.IndirectRequired{ 1193 StrToNested: map[string]*pb2.NestedWithRequired{ 1194 "missing": &pb2.NestedWithRequired{}, 1195 "contains": &pb2.NestedWithRequired{ 1196 ReqString: proto.String("here"), 1197 }, 1198 }, 1199 }, 1200 }, { 1201 desc: "indirect required field in oneof", 1202 inputMessage: &pb2.IndirectRequired{}, 1203 inputText: `{ 1204 "oneofNested": {} 1205}`, 1206 wantMessage: &pb2.IndirectRequired{ 1207 Union: &pb2.IndirectRequired_OneofNested{ 1208 OneofNested: &pb2.NestedWithRequired{}, 1209 }, 1210 }, 1211 wantErr: errors.RequiredNotSet("pb2.NestedWithRequired.req_string").Error(), 1212 }, { 1213 desc: "indirect required field in oneof with AllowPartial", 1214 umo: protojson.UnmarshalOptions{AllowPartial: true}, 1215 inputMessage: &pb2.IndirectRequired{}, 1216 inputText: `{ 1217 "oneofNested": {} 1218}`, 1219 wantMessage: &pb2.IndirectRequired{ 1220 Union: &pb2.IndirectRequired_OneofNested{ 1221 OneofNested: &pb2.NestedWithRequired{}, 1222 }, 1223 }, 1224 }, { 1225 desc: "extensions of non-repeated fields", 1226 inputMessage: &pb2.Extensions{}, 1227 inputText: `{ 1228 "optString": "non-extension field", 1229 "optBool": true, 1230 "optInt32": 42, 1231 "[pb2.opt_ext_bool]": true, 1232 "[pb2.opt_ext_nested]": { 1233 "optString": "nested in an extension", 1234 "optNested": { 1235 "optString": "another nested in an extension" 1236 } 1237 }, 1238 "[pb2.opt_ext_string]": "extension field", 1239 "[pb2.opt_ext_enum]": "TEN" 1240}`, 1241 wantMessage: func() proto.Message { 1242 m := &pb2.Extensions{ 1243 OptString: proto.String("non-extension field"), 1244 OptBool: proto.Bool(true), 1245 OptInt32: proto.Int32(42), 1246 } 1247 proto.SetExtension(m, pb2.E_OptExtBool, true) 1248 proto.SetExtension(m, pb2.E_OptExtString, "extension field") 1249 proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN) 1250 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{ 1251 OptString: proto.String("nested in an extension"), 1252 OptNested: &pb2.Nested{ 1253 OptString: proto.String("another nested in an extension"), 1254 }, 1255 }) 1256 return m 1257 }(), 1258 }, { 1259 desc: "extensions of repeated fields", 1260 inputMessage: &pb2.Extensions{}, 1261 inputText: `{ 1262 "[pb2.rpt_ext_enum]": ["TEN", 101, "ONE"], 1263 "[pb2.rpt_ext_fixed32]": [42, 47], 1264 "[pb2.rpt_ext_nested]": [ 1265 {"optString": "one"}, 1266 {"optString": "two"}, 1267 {"optString": "three"} 1268 ] 1269}`, 1270 wantMessage: func() proto.Message { 1271 m := &pb2.Extensions{} 1272 proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE}) 1273 proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47}) 1274 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{ 1275 &pb2.Nested{OptString: proto.String("one")}, 1276 &pb2.Nested{OptString: proto.String("two")}, 1277 &pb2.Nested{OptString: proto.String("three")}, 1278 }) 1279 return m 1280 }(), 1281 }, { 1282 desc: "extensions of non-repeated fields in another message", 1283 inputMessage: &pb2.Extensions{}, 1284 inputText: `{ 1285 "[pb2.ExtensionsContainer.opt_ext_bool]": true, 1286 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN", 1287 "[pb2.ExtensionsContainer.opt_ext_nested]": { 1288 "optString": "nested in an extension", 1289 "optNested": { 1290 "optString": "another nested in an extension" 1291 } 1292 }, 1293 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field" 1294}`, 1295 wantMessage: func() proto.Message { 1296 m := &pb2.Extensions{} 1297 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true) 1298 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field") 1299 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN) 1300 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{ 1301 OptString: proto.String("nested in an extension"), 1302 OptNested: &pb2.Nested{ 1303 OptString: proto.String("another nested in an extension"), 1304 }, 1305 }) 1306 return m 1307 }(), 1308 }, { 1309 desc: "extensions of repeated fields in another message", 1310 inputMessage: &pb2.Extensions{}, 1311 inputText: `{ 1312 "optString": "non-extension field", 1313 "optBool": true, 1314 "optInt32": 42, 1315 "[pb2.ExtensionsContainer.rpt_ext_nested]": [ 1316 {"optString": "one"}, 1317 {"optString": "two"}, 1318 {"optString": "three"} 1319 ], 1320 "[pb2.ExtensionsContainer.rpt_ext_enum]": ["TEN", 101, "ONE"], 1321 "[pb2.ExtensionsContainer.rpt_ext_string]": ["hello", "world"] 1322}`, 1323 wantMessage: func() proto.Message { 1324 m := &pb2.Extensions{ 1325 OptString: proto.String("non-extension field"), 1326 OptBool: proto.Bool(true), 1327 OptInt32: proto.Int32(42), 1328 } 1329 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE}) 1330 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"}) 1331 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{ 1332 &pb2.Nested{OptString: proto.String("one")}, 1333 &pb2.Nested{OptString: proto.String("two")}, 1334 &pb2.Nested{OptString: proto.String("three")}, 1335 }) 1336 return m 1337 }(), 1338 }, { 1339 desc: "invalid extension field name", 1340 inputMessage: &pb2.Extensions{}, 1341 inputText: `{ "[pb2.invalid_message_field]": true }`, 1342 wantErr: `(line 1:3): unknown field "[pb2.invalid_message_field]"`, 1343 }, { 1344 desc: "extensions of repeated field contains null", 1345 inputMessage: &pb2.Extensions{}, 1346 inputText: `{ 1347 "[pb2.ExtensionsContainer.rpt_ext_nested]": [ 1348 {"optString": "one"}, 1349 null, 1350 {"optString": "three"} 1351 ], 1352}`, 1353 wantErr: `(line 4:5): unexpected token null`, 1354 }, { 1355 desc: "MessageSet", 1356 inputMessage: &pb2.MessageSet{}, 1357 inputText: `{ 1358 "[pb2.MessageSetExtension]": { 1359 "optString": "a messageset extension" 1360 }, 1361 "[pb2.MessageSetExtension.ext_nested]": { 1362 "optString": "just a regular extension" 1363 }, 1364 "[pb2.MessageSetExtension.not_message_set_extension]": { 1365 "optString": "not a messageset extension" 1366 } 1367}`, 1368 wantMessage: func() proto.Message { 1369 m := &pb2.MessageSet{} 1370 proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{ 1371 OptString: proto.String("a messageset extension"), 1372 }) 1373 proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{ 1374 OptString: proto.String("not a messageset extension"), 1375 }) 1376 proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{ 1377 OptString: proto.String("just a regular extension"), 1378 }) 1379 return m 1380 }(), 1381 skip: !flags.ProtoLegacy, 1382 }, { 1383 desc: "not real MessageSet 1", 1384 inputMessage: &pb2.FakeMessageSet{}, 1385 inputText: `{ 1386 "[pb2.FakeMessageSetExtension.message_set_extension]": { 1387 "optString": "not a messageset extension" 1388 } 1389}`, 1390 wantMessage: func() proto.Message { 1391 m := &pb2.FakeMessageSet{} 1392 proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{ 1393 OptString: proto.String("not a messageset extension"), 1394 }) 1395 return m 1396 }(), 1397 skip: !flags.ProtoLegacy, 1398 }, { 1399 desc: "not real MessageSet 2", 1400 inputMessage: &pb2.FakeMessageSet{}, 1401 inputText: `{ 1402 "[pb2.FakeMessageSetExtension]": { 1403 "optString": "not a messageset extension" 1404 } 1405}`, 1406 wantErr: `unable to resolve "[pb2.FakeMessageSetExtension]": found wrong type`, 1407 skip: !flags.ProtoLegacy, 1408 }, { 1409 desc: "not real MessageSet 3", 1410 inputMessage: &pb2.MessageSet{}, 1411 inputText: `{ 1412 "[pb2.message_set_extension]": { 1413 "optString": "another not a messageset extension" 1414 } 1415}`, 1416 wantMessage: func() proto.Message { 1417 m := &pb2.MessageSet{} 1418 proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{ 1419 OptString: proto.String("another not a messageset extension"), 1420 }) 1421 return m 1422 }(), 1423 skip: !flags.ProtoLegacy, 1424 }, { 1425 desc: "Empty", 1426 inputMessage: &emptypb.Empty{}, 1427 inputText: `{}`, 1428 wantMessage: &emptypb.Empty{}, 1429 }, { 1430 desc: "Empty contains unknown", 1431 inputMessage: &emptypb.Empty{}, 1432 inputText: `{"unknown": null}`, 1433 wantErr: `unknown field "unknown"`, 1434 }, { 1435 desc: "BoolValue false", 1436 inputMessage: &wrapperspb.BoolValue{}, 1437 inputText: `false`, 1438 wantMessage: &wrapperspb.BoolValue{}, 1439 }, { 1440 desc: "BoolValue true", 1441 inputMessage: &wrapperspb.BoolValue{}, 1442 inputText: `true`, 1443 wantMessage: &wrapperspb.BoolValue{Value: true}, 1444 }, { 1445 desc: "BoolValue invalid value", 1446 inputMessage: &wrapperspb.BoolValue{}, 1447 inputText: `{}`, 1448 wantErr: `invalid value for bool type: {`, 1449 }, { 1450 desc: "Int32Value", 1451 inputMessage: &wrapperspb.Int32Value{}, 1452 inputText: `42`, 1453 wantMessage: &wrapperspb.Int32Value{Value: 42}, 1454 }, { 1455 desc: "Int32Value in JSON string", 1456 inputMessage: &wrapperspb.Int32Value{}, 1457 inputText: `"1.23e3"`, 1458 wantMessage: &wrapperspb.Int32Value{Value: 1230}, 1459 }, { 1460 desc: "Int64Value", 1461 inputMessage: &wrapperspb.Int64Value{}, 1462 inputText: `"42"`, 1463 wantMessage: &wrapperspb.Int64Value{Value: 42}, 1464 }, { 1465 desc: "UInt32Value", 1466 inputMessage: &wrapperspb.UInt32Value{}, 1467 inputText: `42`, 1468 wantMessage: &wrapperspb.UInt32Value{Value: 42}, 1469 }, { 1470 desc: "UInt64Value", 1471 inputMessage: &wrapperspb.UInt64Value{}, 1472 inputText: `"42"`, 1473 wantMessage: &wrapperspb.UInt64Value{Value: 42}, 1474 }, { 1475 desc: "FloatValue", 1476 inputMessage: &wrapperspb.FloatValue{}, 1477 inputText: `1.02`, 1478 wantMessage: &wrapperspb.FloatValue{Value: 1.02}, 1479 }, { 1480 desc: "FloatValue exceeds max limit", 1481 inputMessage: &wrapperspb.FloatValue{}, 1482 inputText: `1.23e+40`, 1483 wantErr: `invalid value for float type: 1.23e+40`, 1484 }, { 1485 desc: "FloatValue Infinity", 1486 inputMessage: &wrapperspb.FloatValue{}, 1487 inputText: `"-Infinity"`, 1488 wantMessage: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))}, 1489 }, { 1490 desc: "DoubleValue", 1491 inputMessage: &wrapperspb.DoubleValue{}, 1492 inputText: `1.02`, 1493 wantMessage: &wrapperspb.DoubleValue{Value: 1.02}, 1494 }, { 1495 desc: "DoubleValue Infinity", 1496 inputMessage: &wrapperspb.DoubleValue{}, 1497 inputText: `"Infinity"`, 1498 wantMessage: &wrapperspb.DoubleValue{Value: math.Inf(+1)}, 1499 }, { 1500 desc: "StringValue empty", 1501 inputMessage: &wrapperspb.StringValue{}, 1502 inputText: `""`, 1503 wantMessage: &wrapperspb.StringValue{}, 1504 }, { 1505 desc: "StringValue", 1506 inputMessage: &wrapperspb.StringValue{}, 1507 inputText: `"谷歌"`, 1508 wantMessage: &wrapperspb.StringValue{Value: "谷歌"}, 1509 }, { 1510 desc: "StringValue with invalid UTF8 error", 1511 inputMessage: &wrapperspb.StringValue{}, 1512 inputText: "\"abc\xff\"", 1513 wantErr: `invalid UTF-8`, 1514 }, { 1515 desc: "StringValue field with invalid UTF8 error", 1516 inputMessage: &pb2.KnownTypes{}, 1517 inputText: "{\n \"optString\": \"abc\xff\"\n}", 1518 wantErr: `invalid UTF-8`, 1519 }, { 1520 desc: "NullValue field with JSON null", 1521 inputMessage: &pb2.KnownTypes{}, 1522 inputText: `{ 1523 "optNull": null 1524}`, 1525 wantMessage: &pb2.KnownTypes{OptNull: new(structpb.NullValue)}, 1526 }, { 1527 desc: "NullValue field with string", 1528 inputMessage: &pb2.KnownTypes{}, 1529 inputText: `{ 1530 "optNull": "NULL_VALUE" 1531}`, 1532 wantMessage: &pb2.KnownTypes{OptNull: new(structpb.NullValue)}, 1533 }, { 1534 desc: "BytesValue", 1535 inputMessage: &wrapperspb.BytesValue{}, 1536 inputText: `"aGVsbG8="`, 1537 wantMessage: &wrapperspb.BytesValue{Value: []byte("hello")}, 1538 }, { 1539 desc: "Value null", 1540 inputMessage: &structpb.Value{}, 1541 inputText: `null`, 1542 wantMessage: &structpb.Value{Kind: &structpb.Value_NullValue{}}, 1543 }, { 1544 desc: "Value field null", 1545 inputMessage: &pb2.KnownTypes{}, 1546 inputText: `{ 1547 "optValue": null 1548}`, 1549 wantMessage: &pb2.KnownTypes{ 1550 OptValue: &structpb.Value{Kind: &structpb.Value_NullValue{}}, 1551 }, 1552 }, { 1553 desc: "Value bool", 1554 inputMessage: &structpb.Value{}, 1555 inputText: `false`, 1556 wantMessage: &structpb.Value{Kind: &structpb.Value_BoolValue{}}, 1557 }, { 1558 desc: "Value field bool", 1559 inputMessage: &pb2.KnownTypes{}, 1560 inputText: `{ 1561 "optValue": true 1562}`, 1563 wantMessage: &pb2.KnownTypes{ 1564 OptValue: &structpb.Value{Kind: &structpb.Value_BoolValue{true}}, 1565 }, 1566 }, { 1567 desc: "Value number", 1568 inputMessage: &structpb.Value{}, 1569 inputText: `1.02`, 1570 wantMessage: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}}, 1571 }, { 1572 desc: "Value field number", 1573 inputMessage: &pb2.KnownTypes{}, 1574 inputText: `{ 1575 "optValue": 1.02 1576}`, 1577 wantMessage: &pb2.KnownTypes{ 1578 OptValue: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}}, 1579 }, 1580 }, { 1581 desc: "Value string", 1582 inputMessage: &structpb.Value{}, 1583 inputText: `"hello"`, 1584 wantMessage: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}}, 1585 }, { 1586 desc: "Value string with invalid UTF8", 1587 inputMessage: &structpb.Value{}, 1588 inputText: "\"\xff\"", 1589 wantErr: `invalid UTF-8`, 1590 }, { 1591 desc: "Value field string", 1592 inputMessage: &pb2.KnownTypes{}, 1593 inputText: `{ 1594 "optValue": "NaN" 1595}`, 1596 wantMessage: &pb2.KnownTypes{ 1597 OptValue: &structpb.Value{Kind: &structpb.Value_StringValue{"NaN"}}, 1598 }, 1599 }, { 1600 desc: "Value field string with invalid UTF8", 1601 inputMessage: &pb2.KnownTypes{}, 1602 inputText: `{ 1603 "optValue": "` + "\xff" + `" 1604}`, 1605 wantErr: `invalid UTF-8`, 1606 }, { 1607 desc: "Value empty struct", 1608 inputMessage: &structpb.Value{}, 1609 inputText: `{}`, 1610 wantMessage: &structpb.Value{ 1611 Kind: &structpb.Value_StructValue{ 1612 &structpb.Struct{Fields: map[string]*structpb.Value{}}, 1613 }, 1614 }, 1615 }, { 1616 desc: "Value struct", 1617 inputMessage: &structpb.Value{}, 1618 inputText: `{ 1619 "string": "hello", 1620 "number": 123, 1621 "null": null, 1622 "bool": false, 1623 "struct": { 1624 "string": "world" 1625 }, 1626 "list": [] 1627}`, 1628 wantMessage: &structpb.Value{ 1629 Kind: &structpb.Value_StructValue{ 1630 &structpb.Struct{ 1631 Fields: map[string]*structpb.Value{ 1632 "string": {Kind: &structpb.Value_StringValue{"hello"}}, 1633 "number": {Kind: &structpb.Value_NumberValue{123}}, 1634 "null": {Kind: &structpb.Value_NullValue{}}, 1635 "bool": {Kind: &structpb.Value_BoolValue{false}}, 1636 "struct": { 1637 Kind: &structpb.Value_StructValue{ 1638 &structpb.Struct{ 1639 Fields: map[string]*structpb.Value{ 1640 "string": {Kind: &structpb.Value_StringValue{"world"}}, 1641 }, 1642 }, 1643 }, 1644 }, 1645 "list": { 1646 Kind: &structpb.Value_ListValue{&structpb.ListValue{}}, 1647 }, 1648 }, 1649 }, 1650 }, 1651 }, 1652 }, { 1653 desc: "Value struct with invalid UTF8 string", 1654 inputMessage: &structpb.Value{}, 1655 inputText: "{\"string\": \"abc\xff\"}", 1656 wantErr: `invalid UTF-8`, 1657 }, { 1658 desc: "Value field struct", 1659 inputMessage: &pb2.KnownTypes{}, 1660 inputText: `{ 1661 "optValue": { 1662 "string": "hello" 1663 } 1664}`, 1665 wantMessage: &pb2.KnownTypes{ 1666 OptValue: &structpb.Value{ 1667 Kind: &structpb.Value_StructValue{ 1668 &structpb.Struct{ 1669 Fields: map[string]*structpb.Value{ 1670 "string": {Kind: &structpb.Value_StringValue{"hello"}}, 1671 }, 1672 }, 1673 }, 1674 }, 1675 }, 1676 }, { 1677 desc: "Value empty list", 1678 inputMessage: &structpb.Value{}, 1679 inputText: `[]`, 1680 wantMessage: &structpb.Value{ 1681 Kind: &structpb.Value_ListValue{ 1682 &structpb.ListValue{Values: []*structpb.Value{}}, 1683 }, 1684 }, 1685 }, { 1686 desc: "Value list", 1687 inputMessage: &structpb.Value{}, 1688 inputText: `[ 1689 "string", 1690 123, 1691 null, 1692 true, 1693 {}, 1694 [ 1695 "string", 1696 1.23, 1697 null, 1698 false 1699 ] 1700]`, 1701 wantMessage: &structpb.Value{ 1702 Kind: &structpb.Value_ListValue{ 1703 &structpb.ListValue{ 1704 Values: []*structpb.Value{ 1705 {Kind: &structpb.Value_StringValue{"string"}}, 1706 {Kind: &structpb.Value_NumberValue{123}}, 1707 {Kind: &structpb.Value_NullValue{}}, 1708 {Kind: &structpb.Value_BoolValue{true}}, 1709 {Kind: &structpb.Value_StructValue{&structpb.Struct{}}}, 1710 { 1711 Kind: &structpb.Value_ListValue{ 1712 &structpb.ListValue{ 1713 Values: []*structpb.Value{ 1714 {Kind: &structpb.Value_StringValue{"string"}}, 1715 {Kind: &structpb.Value_NumberValue{1.23}}, 1716 {Kind: &structpb.Value_NullValue{}}, 1717 {Kind: &structpb.Value_BoolValue{false}}, 1718 }, 1719 }, 1720 }, 1721 }, 1722 }, 1723 }, 1724 }, 1725 }, 1726 }, { 1727 desc: "Value list with invalid UTF8 string", 1728 inputMessage: &structpb.Value{}, 1729 inputText: "[\"abc\xff\"]", 1730 wantErr: `invalid UTF-8`, 1731 }, { 1732 desc: "Value field list with invalid UTF8 string", 1733 inputMessage: &pb2.KnownTypes{}, 1734 inputText: `{ 1735 "optValue": [ "` + "abc\xff" + `"] 1736}`, 1737 wantErr: `(line 2:17): invalid UTF-8`, 1738 }, { 1739 desc: "Duration empty string", 1740 inputMessage: &durationpb.Duration{}, 1741 inputText: `""`, 1742 wantErr: `invalid google.protobuf.Duration value ""`, 1743 }, { 1744 desc: "Duration with secs", 1745 inputMessage: &durationpb.Duration{}, 1746 inputText: `"3s"`, 1747 wantMessage: &durationpb.Duration{Seconds: 3}, 1748 }, { 1749 desc: "Duration with escaped unicode", 1750 inputMessage: &durationpb.Duration{}, 1751 inputText: `"\u0033s"`, 1752 wantMessage: &durationpb.Duration{Seconds: 3}, 1753 }, { 1754 desc: "Duration with -secs", 1755 inputMessage: &durationpb.Duration{}, 1756 inputText: `"-3s"`, 1757 wantMessage: &durationpb.Duration{Seconds: -3}, 1758 }, { 1759 desc: "Duration with plus sign", 1760 inputMessage: &durationpb.Duration{}, 1761 inputText: `"+3s"`, 1762 wantMessage: &durationpb.Duration{Seconds: 3}, 1763 }, { 1764 desc: "Duration with nanos", 1765 inputMessage: &durationpb.Duration{}, 1766 inputText: `"0.001s"`, 1767 wantMessage: &durationpb.Duration{Nanos: 1e6}, 1768 }, { 1769 desc: "Duration with -nanos", 1770 inputMessage: &durationpb.Duration{}, 1771 inputText: `"-0.001s"`, 1772 wantMessage: &durationpb.Duration{Nanos: -1e6}, 1773 }, { 1774 desc: "Duration with -nanos", 1775 inputMessage: &durationpb.Duration{}, 1776 inputText: `"-.001s"`, 1777 wantMessage: &durationpb.Duration{Nanos: -1e6}, 1778 }, { 1779 desc: "Duration with +nanos", 1780 inputMessage: &durationpb.Duration{}, 1781 inputText: `"+.001s"`, 1782 wantMessage: &durationpb.Duration{Nanos: 1e6}, 1783 }, { 1784 desc: "Duration with -secs -nanos", 1785 inputMessage: &durationpb.Duration{}, 1786 inputText: `"-123.000000450s"`, 1787 wantMessage: &durationpb.Duration{Seconds: -123, Nanos: -450}, 1788 }, { 1789 desc: "Duration with large secs", 1790 inputMessage: &durationpb.Duration{}, 1791 inputText: `"10000000000.000000001s"`, 1792 wantMessage: &durationpb.Duration{Seconds: 1e10, Nanos: 1}, 1793 }, { 1794 desc: "Duration with decimal without fractional", 1795 inputMessage: &durationpb.Duration{}, 1796 inputText: `"3.s"`, 1797 wantMessage: &durationpb.Duration{Seconds: 3}, 1798 }, { 1799 desc: "Duration with decimal without integer", 1800 inputMessage: &durationpb.Duration{}, 1801 inputText: `"0.5s"`, 1802 wantMessage: &durationpb.Duration{Nanos: 5e8}, 1803 }, { 1804 desc: "Duration max value", 1805 inputMessage: &durationpb.Duration{}, 1806 inputText: `"315576000000.999999999s"`, 1807 wantMessage: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999}, 1808 }, { 1809 desc: "Duration min value", 1810 inputMessage: &durationpb.Duration{}, 1811 inputText: `"-315576000000.999999999s"`, 1812 wantMessage: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999}, 1813 }, { 1814 desc: "Duration with +secs out of range", 1815 inputMessage: &durationpb.Duration{}, 1816 inputText: `"315576000001s"`, 1817 wantErr: `google.protobuf.Duration value out of range: "315576000001s"`, 1818 }, { 1819 desc: "Duration with -secs out of range", 1820 inputMessage: &durationpb.Duration{}, 1821 inputText: `"-315576000001s"`, 1822 wantErr: `google.protobuf.Duration value out of range: "-315576000001s"`, 1823 }, { 1824 desc: "Duration with nanos beyond 9 digits", 1825 inputMessage: &durationpb.Duration{}, 1826 inputText: `"0.1000000000s"`, 1827 wantErr: `invalid google.protobuf.Duration value "0.1000000000s"`, 1828 }, { 1829 desc: "Duration without suffix s", 1830 inputMessage: &durationpb.Duration{}, 1831 inputText: `"123"`, 1832 wantErr: `invalid google.protobuf.Duration value "123"`, 1833 }, { 1834 desc: "Duration invalid signed fraction", 1835 inputMessage: &durationpb.Duration{}, 1836 inputText: `"123.+123s"`, 1837 wantErr: `invalid google.protobuf.Duration value "123.+123s"`, 1838 }, { 1839 desc: "Duration invalid multiple .", 1840 inputMessage: &durationpb.Duration{}, 1841 inputText: `"123.123.s"`, 1842 wantErr: `invalid google.protobuf.Duration value "123.123.s"`, 1843 }, { 1844 desc: "Duration invalid integer", 1845 inputMessage: &durationpb.Duration{}, 1846 inputText: `"01s"`, 1847 wantErr: `invalid google.protobuf.Duration value "01s"`, 1848 }, { 1849 desc: "Timestamp zero", 1850 inputMessage: ×tamppb.Timestamp{}, 1851 inputText: `"1970-01-01T00:00:00Z"`, 1852 wantMessage: ×tamppb.Timestamp{}, 1853 }, { 1854 desc: "Timestamp with tz adjustment", 1855 inputMessage: ×tamppb.Timestamp{}, 1856 inputText: `"1970-01-01T00:00:00+01:00"`, 1857 wantMessage: ×tamppb.Timestamp{Seconds: -3600}, 1858 }, { 1859 desc: "Timestamp UTC", 1860 inputMessage: ×tamppb.Timestamp{}, 1861 inputText: `"2019-03-19T23:03:21Z"`, 1862 wantMessage: ×tamppb.Timestamp{Seconds: 1553036601}, 1863 }, { 1864 desc: "Timestamp with escaped unicode", 1865 inputMessage: ×tamppb.Timestamp{}, 1866 inputText: `"2019-0\u0033-19T23:03:21Z"`, 1867 wantMessage: ×tamppb.Timestamp{Seconds: 1553036601}, 1868 }, { 1869 desc: "Timestamp with nanos", 1870 inputMessage: ×tamppb.Timestamp{}, 1871 inputText: `"2019-03-19T23:03:21.000000001Z"`, 1872 wantMessage: ×tamppb.Timestamp{Seconds: 1553036601, Nanos: 1}, 1873 }, { 1874 desc: "Timestamp max value", 1875 inputMessage: ×tamppb.Timestamp{}, 1876 inputText: `"9999-12-31T23:59:59.999999999Z"`, 1877 wantMessage: ×tamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999}, 1878 }, { 1879 desc: "Timestamp above max value", 1880 inputMessage: ×tamppb.Timestamp{}, 1881 inputText: `"9999-12-31T23:59:59-01:00"`, 1882 wantErr: `google.protobuf.Timestamp value out of range: "9999-12-31T23:59:59-01:00"`, 1883 }, { 1884 desc: "Timestamp min value", 1885 inputMessage: ×tamppb.Timestamp{}, 1886 inputText: `"0001-01-01T00:00:00Z"`, 1887 wantMessage: ×tamppb.Timestamp{Seconds: -62135596800}, 1888 }, { 1889 desc: "Timestamp below min value", 1890 inputMessage: ×tamppb.Timestamp{}, 1891 inputText: `"0001-01-01T00:00:00+01:00"`, 1892 wantErr: `google.protobuf.Timestamp value out of range: "0001-01-01T00:00:00+01:00"`, 1893 }, { 1894 desc: "Timestamp with nanos beyond 9 digits", 1895 inputMessage: ×tamppb.Timestamp{}, 1896 inputText: `"1970-01-01T00:00:00.0000000001Z"`, 1897 wantErr: `invalid google.protobuf.Timestamp value`, 1898 }, { 1899 desc: "FieldMask empty", 1900 inputMessage: &fieldmaskpb.FieldMask{}, 1901 inputText: `""`, 1902 wantMessage: &fieldmaskpb.FieldMask{Paths: []string{}}, 1903 }, { 1904 desc: "FieldMask", 1905 inputMessage: &fieldmaskpb.FieldMask{}, 1906 inputText: `"foo,fooBar,foo.barQux,Foo"`, 1907 wantMessage: &fieldmaskpb.FieldMask{ 1908 Paths: []string{ 1909 "foo", 1910 "foo_bar", 1911 "foo.bar_qux", 1912 "_foo", 1913 }, 1914 }, 1915 }, { 1916 desc: "FieldMask empty path 1", 1917 inputMessage: &fieldmaskpb.FieldMask{}, 1918 inputText: `"foo,"`, 1919 wantErr: `google.protobuf.FieldMask.paths contains invalid path: ""`, 1920 }, { 1921 desc: "FieldMask empty path 2", 1922 inputMessage: &fieldmaskpb.FieldMask{}, 1923 inputText: `"foo, ,bar"`, 1924 wantErr: `google.protobuf.FieldMask.paths contains invalid path: " "`, 1925 }, { 1926 desc: "FieldMask invalid char 1", 1927 inputMessage: &fieldmaskpb.FieldMask{}, 1928 inputText: `"foo_bar"`, 1929 wantErr: `google.protobuf.FieldMask.paths contains invalid path: "foo_bar"`, 1930 }, { 1931 desc: "FieldMask invalid char 2", 1932 inputMessage: &fieldmaskpb.FieldMask{}, 1933 inputText: `"foo@bar"`, 1934 wantErr: `google.protobuf.FieldMask.paths contains invalid path: "foo@bar"`, 1935 }, { 1936 desc: "FieldMask field", 1937 inputMessage: &pb2.KnownTypes{}, 1938 inputText: `{ 1939 "optFieldmask": "foo,qux.fooBar" 1940}`, 1941 wantMessage: &pb2.KnownTypes{ 1942 OptFieldmask: &fieldmaskpb.FieldMask{ 1943 Paths: []string{ 1944 "foo", 1945 "qux.foo_bar", 1946 }, 1947 }, 1948 }, 1949 }, { 1950 desc: "Any empty", 1951 inputMessage: &anypb.Any{}, 1952 inputText: `{}`, 1953 wantMessage: &anypb.Any{}, 1954 }, { 1955 desc: "Any with non-custom message", 1956 inputMessage: &anypb.Any{}, 1957 inputText: `{ 1958 "@type": "foo/pb2.Nested", 1959 "optString": "embedded inside Any", 1960 "optNested": { 1961 "optString": "inception" 1962 } 1963}`, 1964 wantMessage: func() proto.Message { 1965 m := &pb2.Nested{ 1966 OptString: proto.String("embedded inside Any"), 1967 OptNested: &pb2.Nested{ 1968 OptString: proto.String("inception"), 1969 }, 1970 } 1971 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 1972 if err != nil { 1973 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 1974 } 1975 return &anypb.Any{ 1976 TypeUrl: "foo/pb2.Nested", 1977 Value: b, 1978 } 1979 }(), 1980 }, { 1981 desc: "Any with empty embedded message", 1982 inputMessage: &anypb.Any{}, 1983 inputText: `{"@type": "foo/pb2.Nested"}`, 1984 wantMessage: &anypb.Any{TypeUrl: "foo/pb2.Nested"}, 1985 }, { 1986 desc: "Any without registered type", 1987 umo: protojson.UnmarshalOptions{Resolver: new(protoregistry.Types)}, 1988 inputMessage: &anypb.Any{}, 1989 inputText: `{"@type": "foo/pb2.Nested"}`, 1990 wantErr: `(line 1:11): unable to resolve "foo/pb2.Nested":`, 1991 }, { 1992 desc: "Any with missing required", 1993 inputMessage: &anypb.Any{}, 1994 inputText: `{ 1995 "@type": "pb2.PartialRequired", 1996 "optString": "embedded inside Any" 1997}`, 1998 wantMessage: func() proto.Message { 1999 m := &pb2.PartialRequired{ 2000 OptString: proto.String("embedded inside Any"), 2001 } 2002 b, err := proto.MarshalOptions{ 2003 Deterministic: true, 2004 AllowPartial: true, 2005 }.Marshal(m) 2006 if err != nil { 2007 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2008 } 2009 return &anypb.Any{ 2010 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()), 2011 Value: b, 2012 } 2013 }(), 2014 }, { 2015 desc: "Any with partial required and AllowPartial", 2016 umo: protojson.UnmarshalOptions{ 2017 AllowPartial: true, 2018 }, 2019 inputMessage: &anypb.Any{}, 2020 inputText: `{ 2021 "@type": "pb2.PartialRequired", 2022 "optString": "embedded inside Any" 2023}`, 2024 wantMessage: func() proto.Message { 2025 m := &pb2.PartialRequired{ 2026 OptString: proto.String("embedded inside Any"), 2027 } 2028 b, err := proto.MarshalOptions{ 2029 Deterministic: true, 2030 AllowPartial: true, 2031 }.Marshal(m) 2032 if err != nil { 2033 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2034 } 2035 return &anypb.Any{ 2036 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()), 2037 Value: b, 2038 } 2039 }(), 2040 }, { 2041 desc: "Any with invalid UTF8", 2042 inputMessage: &anypb.Any{}, 2043 inputText: `{ 2044 "optString": "` + "abc\xff" + `", 2045 "@type": "foo/pb2.Nested" 2046}`, 2047 wantErr: `(line 2:16): invalid UTF-8`, 2048 }, { 2049 desc: "Any with BoolValue", 2050 inputMessage: &anypb.Any{}, 2051 inputText: `{ 2052 "@type": "type.googleapis.com/google.protobuf.BoolValue", 2053 "value": true 2054}`, 2055 wantMessage: func() proto.Message { 2056 m := &wrapperspb.BoolValue{Value: true} 2057 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 2058 if err != nil { 2059 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2060 } 2061 return &anypb.Any{ 2062 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue", 2063 Value: b, 2064 } 2065 }(), 2066 }, { 2067 desc: "Any with Empty", 2068 inputMessage: &anypb.Any{}, 2069 inputText: `{ 2070 "value": {}, 2071 "@type": "type.googleapis.com/google.protobuf.Empty" 2072}`, 2073 wantMessage: &anypb.Any{ 2074 TypeUrl: "type.googleapis.com/google.protobuf.Empty", 2075 }, 2076 }, { 2077 desc: "Any with missing Empty", 2078 inputMessage: &anypb.Any{}, 2079 inputText: `{ 2080 "@type": "type.googleapis.com/google.protobuf.Empty" 2081}`, 2082 wantErr: `(line 3:1): missing "value" field`, 2083 }, { 2084 desc: "Any with StringValue containing invalid UTF8", 2085 inputMessage: &anypb.Any{}, 2086 inputText: `{ 2087 "@type": "google.protobuf.StringValue", 2088 "value": "` + "abc\xff" + `" 2089}`, 2090 wantErr: `(line 3:12): invalid UTF-8`, 2091 }, { 2092 desc: "Any with Int64Value", 2093 inputMessage: &anypb.Any{}, 2094 inputText: `{ 2095 "@type": "google.protobuf.Int64Value", 2096 "value": "42" 2097}`, 2098 wantMessage: func() proto.Message { 2099 m := &wrapperspb.Int64Value{Value: 42} 2100 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 2101 if err != nil { 2102 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2103 } 2104 return &anypb.Any{ 2105 TypeUrl: "google.protobuf.Int64Value", 2106 Value: b, 2107 } 2108 }(), 2109 }, { 2110 desc: "Any with invalid Int64Value", 2111 inputMessage: &anypb.Any{}, 2112 inputText: `{ 2113 "@type": "google.protobuf.Int64Value", 2114 "value": "forty-two" 2115}`, 2116 wantErr: `(line 3:12): invalid value for int64 type: "forty-two"`, 2117 }, { 2118 desc: "Any with invalid UInt64Value", 2119 inputMessage: &anypb.Any{}, 2120 inputText: `{ 2121 "@type": "google.protobuf.UInt64Value", 2122 "value": -42 2123}`, 2124 wantErr: `(line 3:12): invalid value for uint64 type: -42`, 2125 }, { 2126 desc: "Any with Duration", 2127 inputMessage: &anypb.Any{}, 2128 inputText: `{ 2129 "@type": "type.googleapis.com/google.protobuf.Duration", 2130 "value": "0s" 2131}`, 2132 wantMessage: func() proto.Message { 2133 m := &durationpb.Duration{} 2134 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 2135 if err != nil { 2136 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2137 } 2138 return &anypb.Any{ 2139 TypeUrl: "type.googleapis.com/google.protobuf.Duration", 2140 Value: b, 2141 } 2142 }(), 2143 }, { 2144 desc: "Any with Value of StringValue", 2145 inputMessage: &anypb.Any{}, 2146 inputText: `{ 2147 "@type": "google.protobuf.Value", 2148 "value": "` + "abc\xff" + `" 2149}`, 2150 wantErr: `(line 3:12): invalid UTF-8`, 2151 }, { 2152 desc: "Any with Value of NullValue", 2153 inputMessage: &anypb.Any{}, 2154 inputText: `{ 2155 "@type": "google.protobuf.Value", 2156 "value": null 2157}`, 2158 wantMessage: func() proto.Message { 2159 m := &structpb.Value{Kind: &structpb.Value_NullValue{}} 2160 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 2161 if err != nil { 2162 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2163 } 2164 return &anypb.Any{ 2165 TypeUrl: "google.protobuf.Value", 2166 Value: b, 2167 } 2168 }(), 2169 }, { 2170 desc: "Any with Struct", 2171 inputMessage: &anypb.Any{}, 2172 inputText: `{ 2173 "@type": "google.protobuf.Struct", 2174 "value": { 2175 "bool": true, 2176 "null": null, 2177 "string": "hello", 2178 "struct": { 2179 "string": "world" 2180 } 2181 } 2182}`, 2183 wantMessage: func() proto.Message { 2184 m := &structpb.Struct{ 2185 Fields: map[string]*structpb.Value{ 2186 "bool": {Kind: &structpb.Value_BoolValue{true}}, 2187 "null": {Kind: &structpb.Value_NullValue{}}, 2188 "string": {Kind: &structpb.Value_StringValue{"hello"}}, 2189 "struct": { 2190 Kind: &structpb.Value_StructValue{ 2191 &structpb.Struct{ 2192 Fields: map[string]*structpb.Value{ 2193 "string": {Kind: &structpb.Value_StringValue{"world"}}, 2194 }, 2195 }, 2196 }, 2197 }, 2198 }, 2199 } 2200 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 2201 if err != nil { 2202 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 2203 } 2204 return &anypb.Any{ 2205 TypeUrl: "google.protobuf.Struct", 2206 Value: b, 2207 } 2208 }(), 2209 }, { 2210 desc: "Any with missing @type", 2211 umo: protojson.UnmarshalOptions{}, 2212 inputMessage: &anypb.Any{}, 2213 inputText: `{ 2214 "value": {} 2215}`, 2216 wantErr: `(line 1:1): missing "@type" field`, 2217 }, { 2218 desc: "Any with empty @type", 2219 inputMessage: &anypb.Any{}, 2220 inputText: `{ 2221 "@type": "" 2222}`, 2223 wantErr: `(line 2:12): @type field contains empty value`, 2224 }, { 2225 desc: "Any with duplicate @type", 2226 inputMessage: &anypb.Any{}, 2227 inputText: `{ 2228 "@type": "google.protobuf.StringValue", 2229 "value": "hello", 2230 "@type": "pb2.Nested" 2231}`, 2232 wantErr: `(line 4:3): duplicate "@type" field`, 2233 }, { 2234 desc: "Any with duplicate value", 2235 inputMessage: &anypb.Any{}, 2236 inputText: `{ 2237 "@type": "google.protobuf.StringValue", 2238 "value": "hello", 2239 "value": "world" 2240}`, 2241 wantErr: `(line 4:3): duplicate "value" field`, 2242 }, { 2243 desc: "Any with unknown field", 2244 inputMessage: &anypb.Any{}, 2245 inputText: `{ 2246 "@type": "pb2.Nested", 2247 "optString": "hello", 2248 "unknown": "world" 2249}`, 2250 wantErr: `(line 4:3): unknown field "unknown"`, 2251 }, { 2252 desc: "Any with embedded type containing Any", 2253 inputMessage: &anypb.Any{}, 2254 inputText: `{ 2255 "@type": "pb2.KnownTypes", 2256 "optAny": { 2257 "@type": "google.protobuf.StringValue", 2258 "value": "` + "abc\xff" + `" 2259 } 2260}`, 2261 wantErr: `(line 5:14): invalid UTF-8`, 2262 }, { 2263 desc: "well known types as field values", 2264 inputMessage: &pb2.KnownTypes{}, 2265 inputText: `{ 2266 "optBool": false, 2267 "optInt32": 42, 2268 "optInt64": "42", 2269 "optUint32": 42, 2270 "optUint64": "42", 2271 "optFloat": 1.23, 2272 "optDouble": 3.1415, 2273 "optString": "hello", 2274 "optBytes": "aGVsbG8=", 2275 "optDuration": "123s", 2276 "optTimestamp": "2019-03-19T23:03:21Z", 2277 "optStruct": { 2278 "string": "hello" 2279 }, 2280 "optList": [ 2281 null, 2282 "", 2283 {}, 2284 [] 2285 ], 2286 "optValue": "world", 2287 "optEmpty": {}, 2288 "optAny": { 2289 "@type": "google.protobuf.Empty", 2290 "value": {} 2291 }, 2292 "optFieldmask": "fooBar,barFoo" 2293}`, 2294 wantMessage: &pb2.KnownTypes{ 2295 OptBool: &wrapperspb.BoolValue{Value: false}, 2296 OptInt32: &wrapperspb.Int32Value{Value: 42}, 2297 OptInt64: &wrapperspb.Int64Value{Value: 42}, 2298 OptUint32: &wrapperspb.UInt32Value{Value: 42}, 2299 OptUint64: &wrapperspb.UInt64Value{Value: 42}, 2300 OptFloat: &wrapperspb.FloatValue{Value: 1.23}, 2301 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415}, 2302 OptString: &wrapperspb.StringValue{Value: "hello"}, 2303 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")}, 2304 OptDuration: &durationpb.Duration{Seconds: 123}, 2305 OptTimestamp: ×tamppb.Timestamp{Seconds: 1553036601}, 2306 OptStruct: &structpb.Struct{ 2307 Fields: map[string]*structpb.Value{ 2308 "string": {Kind: &structpb.Value_StringValue{"hello"}}, 2309 }, 2310 }, 2311 OptList: &structpb.ListValue{ 2312 Values: []*structpb.Value{ 2313 {Kind: &structpb.Value_NullValue{}}, 2314 {Kind: &structpb.Value_StringValue{}}, 2315 { 2316 Kind: &structpb.Value_StructValue{ 2317 &structpb.Struct{Fields: map[string]*structpb.Value{}}, 2318 }, 2319 }, 2320 { 2321 Kind: &structpb.Value_ListValue{ 2322 &structpb.ListValue{Values: []*structpb.Value{}}, 2323 }, 2324 }, 2325 }, 2326 }, 2327 OptValue: &structpb.Value{ 2328 Kind: &structpb.Value_StringValue{"world"}, 2329 }, 2330 OptEmpty: &emptypb.Empty{}, 2331 OptAny: &anypb.Any{ 2332 TypeUrl: "google.protobuf.Empty", 2333 }, 2334 OptFieldmask: &fieldmaskpb.FieldMask{ 2335 Paths: []string{"foo_bar", "bar_foo"}, 2336 }, 2337 }, 2338 }, { 2339 desc: "DiscardUnknown: regular messages", 2340 umo: protojson.UnmarshalOptions{DiscardUnknown: true}, 2341 inputMessage: &pb3.Nests{}, 2342 inputText: `{ 2343 "sNested": { 2344 "unknown": { 2345 "foo": 1, 2346 "bar": [1, 2, 3] 2347 } 2348 }, 2349 "unknown": "not known" 2350}`, 2351 wantMessage: &pb3.Nests{SNested: &pb3.Nested{}}, 2352 }, { 2353 desc: "DiscardUnknown: repeated", 2354 umo: protojson.UnmarshalOptions{DiscardUnknown: true}, 2355 inputMessage: &pb2.Nests{}, 2356 inputText: `{ 2357 "rptNested": [ 2358 {"unknown": "blah"}, 2359 {"optString": "hello"} 2360 ] 2361}`, 2362 wantMessage: &pb2.Nests{ 2363 RptNested: []*pb2.Nested{ 2364 {}, 2365 {OptString: proto.String("hello")}, 2366 }, 2367 }, 2368 }, { 2369 desc: "DiscardUnknown: map", 2370 umo: protojson.UnmarshalOptions{DiscardUnknown: true}, 2371 inputMessage: &pb3.Maps{}, 2372 inputText: `{ 2373 "strToNested": { 2374 "nested_one": { 2375 "unknown": "what you see is not" 2376 } 2377 } 2378}`, 2379 wantMessage: &pb3.Maps{ 2380 StrToNested: map[string]*pb3.Nested{ 2381 "nested_one": {}, 2382 }, 2383 }, 2384 }, { 2385 desc: "DiscardUnknown: extension", 2386 umo: protojson.UnmarshalOptions{DiscardUnknown: true}, 2387 inputMessage: &pb2.Extensions{}, 2388 inputText: `{ 2389 "[pb2.opt_ext_nested]": { 2390 "unknown": [] 2391 } 2392}`, 2393 wantMessage: func() proto.Message { 2394 m := &pb2.Extensions{} 2395 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{}) 2396 return m 2397 }(), 2398 }, { 2399 desc: "DiscardUnknown: Empty", 2400 umo: protojson.UnmarshalOptions{DiscardUnknown: true}, 2401 inputMessage: &emptypb.Empty{}, 2402 inputText: `{"unknown": "something"}`, 2403 wantMessage: &emptypb.Empty{}, 2404 }, { 2405 desc: "DiscardUnknown: Any without type", 2406 umo: protojson.UnmarshalOptions{DiscardUnknown: true}, 2407 inputMessage: &anypb.Any{}, 2408 inputText: `{ 2409 "value": {"foo": "bar"}, 2410 "unknown": true 2411}`, 2412 wantMessage: &anypb.Any{}, 2413 }, { 2414 desc: "DiscardUnknown: Any", 2415 umo: protojson.UnmarshalOptions{ 2416 DiscardUnknown: true, 2417 }, 2418 inputMessage: &anypb.Any{}, 2419 inputText: `{ 2420 "@type": "foo/pb2.Nested", 2421 "unknown": "none" 2422}`, 2423 wantMessage: &anypb.Any{ 2424 TypeUrl: "foo/pb2.Nested", 2425 }, 2426 }, { 2427 desc: "DiscardUnknown: Any with Empty", 2428 umo: protojson.UnmarshalOptions{ 2429 DiscardUnknown: true, 2430 }, 2431 inputMessage: &anypb.Any{}, 2432 inputText: `{ 2433 "@type": "type.googleapis.com/google.protobuf.Empty", 2434 "value": {"unknown": 47} 2435}`, 2436 wantMessage: &anypb.Any{ 2437 TypeUrl: "type.googleapis.com/google.protobuf.Empty", 2438 }, 2439 }, { 2440 desc: "weak fields", 2441 inputMessage: &testpb.TestWeak{}, 2442 inputText: `{"weak_message1":{"a":1}}`, 2443 wantMessage: func() *testpb.TestWeak { 2444 m := new(testpb.TestWeak) 2445 m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)}) 2446 return m 2447 }(), 2448 skip: !flags.ProtoLegacy, 2449 }, { 2450 desc: "weak fields; unknown field", 2451 inputMessage: &testpb.TestWeak{}, 2452 inputText: `{"weak_message1":{"a":1}, "weak_message2":{"a":1}}`, 2453 wantErr: `unknown field "weak_message2"`, // weak_message2 is unknown since the package containing it is not imported 2454 skip: !flags.ProtoLegacy, 2455 }} 2456 2457 for _, tt := range tests { 2458 tt := tt 2459 if tt.skip { 2460 continue 2461 } 2462 t.Run(tt.desc, func(t *testing.T) { 2463 err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage) 2464 if err != nil { 2465 if tt.wantErr == "" { 2466 t.Errorf("Unmarshal() got unexpected error: %v", err) 2467 } else if !strings.Contains(err.Error(), tt.wantErr) { 2468 t.Errorf("Unmarshal() error got %q, want %q", err, tt.wantErr) 2469 } 2470 return 2471 } 2472 if tt.wantErr != "" { 2473 t.Errorf("Unmarshal() got nil error, want error %q", tt.wantErr) 2474 } 2475 if tt.wantMessage != nil && !proto.Equal(tt.inputMessage, tt.wantMessage) { 2476 t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage) 2477 } 2478 }) 2479 } 2480} 2481