1
2
3
4
5 package protojson_test
6
7 import (
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
31 func 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
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: "DiscardUnknown: unknown enum name",
2441 inputMessage: &pb3.Enums{},
2442 inputText: `{
2443 "sEnum": "UNNAMED"
2444 }`,
2445 umo: protojson.UnmarshalOptions{DiscardUnknown: true},
2446 wantMessage: &pb3.Enums{},
2447 }, {
2448 desc: "DiscardUnknown: repeated enum unknown name",
2449 inputMessage: &pb2.Enums{},
2450 inputText: `{
2451 "rptEnum" : ["TEN", 1, 42, "UNNAMED"]
2452 }`,
2453 umo: protojson.UnmarshalOptions{DiscardUnknown: true},
2454 wantMessage: &pb2.Enums{
2455 RptEnum: []pb2.Enum{pb2.Enum_TEN, pb2.Enum_ONE, 42},
2456 },
2457 }, {
2458 desc: "DiscardUnknown: enum map value unknown name",
2459 inputMessage: &pb3.Maps{},
2460 inputText: `{
2461 "uint64ToEnum": {
2462 "1" : "ONE",
2463 "2" : 2,
2464 "10": 101,
2465 "3": "UNNAMED"
2466 }
2467 }`,
2468 umo: protojson.UnmarshalOptions{DiscardUnknown: true},
2469 wantMessage: &pb3.Maps{
2470 Uint64ToEnum: map[uint64]pb3.Enum{
2471 1: pb3.Enum_ONE,
2472 2: pb3.Enum_TWO,
2473 10: 101,
2474 },
2475 },
2476 }, {
2477 desc: "weak fields",
2478 inputMessage: &testpb.TestWeak{},
2479 inputText: `{"weak_message1":{"a":1}}`,
2480 wantMessage: func() *testpb.TestWeak {
2481 m := new(testpb.TestWeak)
2482 m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)})
2483 return m
2484 }(),
2485 skip: !flags.ProtoLegacy,
2486 }, {
2487 desc: "weak fields; unknown field",
2488 inputMessage: &testpb.TestWeak{},
2489 inputText: `{"weak_message1":{"a":1}, "weak_message2":{"a":1}}`,
2490 wantErr: `unknown field "weak_message2"`,
2491 skip: !flags.ProtoLegacy,
2492 }, {
2493 desc: "just at recursion limit: nested messages",
2494 inputMessage: &testpb.TestAllTypes{},
2495 inputText: `{"optionalNestedMessage":{"corecursive":{"optionalNestedMessage":{"corecursive":{}}}}}`,
2496 umo: protojson.UnmarshalOptions{RecursionLimit: 5},
2497 }, {
2498 desc: "exceed recursion limit: nested messages",
2499 inputMessage: &testpb.TestAllTypes{},
2500 inputText: `{"optionalNestedMessage":{"corecursive":{"optionalNestedMessage":{"corecursive":{"optionalNestedMessage":{}}}}}}`,
2501 umo: protojson.UnmarshalOptions{RecursionLimit: 5},
2502 wantErr: "exceeded max recursion depth",
2503 }, {
2504
2505 desc: "just at recursion limit: maps",
2506 inputMessage: &testpb.TestAllTypes{},
2507 inputText: `{"mapStringNestedMessage":{"key1":{"corecursive":{"mapStringNestedMessage":{}}}}}`,
2508 umo: protojson.UnmarshalOptions{RecursionLimit: 3},
2509 }, {
2510 desc: "exceed recursion limit: maps",
2511 inputMessage: &testpb.TestAllTypes{},
2512 inputText: `{"mapStringNestedMessage":{"key1":{"corecursive":{"mapStringNestedMessage":{}}}}}`,
2513 umo: protojson.UnmarshalOptions{RecursionLimit: 2},
2514 wantErr: "exceeded max recursion depth",
2515 }, {
2516 desc: "just at recursion limit: arrays",
2517 inputMessage: &testpb.TestAllTypes{},
2518 inputText: `{"repeatedNestedMessage":[{"corecursive":{"repeatedInt32":[1,2,3]}}]}`,
2519 umo: protojson.UnmarshalOptions{RecursionLimit: 3},
2520 }, {
2521 desc: "exceed recursion limit: arrays",
2522 inputMessage: &testpb.TestAllTypes{},
2523 inputText: `{"repeatedNestedMessage":[{"corecursive":{"repeatedNestedMessage":[{}]}}]}`,
2524 umo: protojson.UnmarshalOptions{RecursionLimit: 3},
2525 wantErr: "exceeded max recursion depth",
2526 }, {
2527 desc: "just at recursion limit: value",
2528 inputMessage: &structpb.Value{},
2529 inputText: `{"a":{"b":{"c":{"d":{}}}}}`,
2530 umo: protojson.UnmarshalOptions{RecursionLimit: 5},
2531 }, {
2532 desc: "exceed recursion limit: value",
2533 inputMessage: &structpb.Value{},
2534 inputText: `{"a":{"b":{"c":{"d":{"e":[]}}}}}`,
2535 umo: protojson.UnmarshalOptions{RecursionLimit: 5},
2536 wantErr: "exceeded max recursion depth",
2537 }, {
2538 desc: "just at recursion limit: list value",
2539 inputMessage: &structpb.ListValue{},
2540 inputText: `[[[[[1, 2, 3, 4]]]]]`,
2541
2542
2543
2544 umo: protojson.UnmarshalOptions{RecursionLimit: 6},
2545 }, {
2546 desc: "exceed recursion limit: list value",
2547 inputMessage: &structpb.ListValue{},
2548 inputText: `[[[[[1, 2, 3, 4, ["a", "b"]]]]]]`,
2549 umo: protojson.UnmarshalOptions{RecursionLimit: 6},
2550 wantErr: "exceeded max recursion depth",
2551 }, {
2552 desc: "just at recursion limit: struct value",
2553 inputMessage: &structpb.Struct{},
2554 inputText: `{"a":{"b":{"c":{"d":{}}}}}`,
2555 umo: protojson.UnmarshalOptions{RecursionLimit: 5},
2556 }, {
2557 desc: "exceed recursion limit: struct value",
2558 inputMessage: &structpb.Struct{},
2559 inputText: `{"a":{"b":{"c":{"d":{"e":{}]}}}}}`,
2560 umo: protojson.UnmarshalOptions{RecursionLimit: 5},
2561 wantErr: "exceeded max recursion depth",
2562 }, {
2563 desc: "just at recursion limit: skip unknown",
2564 inputMessage: &testpb.TestAllTypes{},
2565 inputText: `{"foo":{"bar":[{"baz":{}}]}}`,
2566 umo: protojson.UnmarshalOptions{RecursionLimit: 5, DiscardUnknown: true},
2567 }, {
2568 desc: "exceed recursion limit: skip unknown",
2569 inputMessage: &testpb.TestAllTypes{},
2570 inputText: `{"foo":{"bar":[{"baz":[{}]]}}`,
2571 umo: protojson.UnmarshalOptions{RecursionLimit: 5, DiscardUnknown: true},
2572 wantErr: "exceeded max recursion depth",
2573 }}
2574
2575 for _, tt := range tests {
2576 tt := tt
2577 if tt.skip {
2578 continue
2579 }
2580 t.Run(tt.desc, func(t *testing.T) {
2581 err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage)
2582 if err != nil {
2583 if tt.wantErr == "" {
2584 t.Errorf("Unmarshal() got unexpected error: %v", err)
2585 } else if !strings.Contains(err.Error(), tt.wantErr) {
2586 t.Errorf("Unmarshal() error got %q, want %q", err, tt.wantErr)
2587 }
2588 return
2589 }
2590 if tt.wantErr != "" {
2591 t.Errorf("Unmarshal() got nil error, want error %q", tt.wantErr)
2592 }
2593 if tt.wantMessage != nil && !proto.Equal(tt.inputMessage, tt.wantMessage) {
2594 t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage)
2595 }
2596 })
2597 }
2598 }
2599
View as plain text