blob: 97a0619cd95a200b688e0894f504c50052338fc3 [file] [log] [blame]
Herbie Ongc96a79d2019-03-08 10:49:17 -08001// 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 jsonpb_test
6
7import (
8 "math"
9 "testing"
10
11 protoV1 "github.com/golang/protobuf/proto"
12 "github.com/golang/protobuf/v2/encoding/jsonpb"
13 "github.com/golang/protobuf/v2/encoding/testprotos/pb2"
14 "github.com/golang/protobuf/v2/encoding/testprotos/pb3"
15 "github.com/golang/protobuf/v2/internal/scalar"
16 "github.com/golang/protobuf/v2/proto"
17)
18
19func TestUnmarshal(t *testing.T) {
20 tests := []struct {
21 desc string
22 umo jsonpb.UnmarshalOptions
23 inputMessage proto.Message
24 inputText string
25 wantMessage proto.Message
26 // TODO: verify expected error message substring.
27 wantErr bool
28 }{{
29 desc: "proto2 empty message",
30 inputMessage: &pb2.Scalars{},
31 inputText: "{}",
32 wantMessage: &pb2.Scalars{},
33 }, {
34 desc: "unexpected value instead of EOF",
35 inputMessage: &pb2.Scalars{},
36 inputText: "{} {}",
37 wantErr: true,
38 }, {
39 desc: "proto2 optional scalars set to zero values",
40 inputMessage: &pb2.Scalars{},
41 inputText: `{
42 "optBool": false,
43 "optInt32": 0,
44 "optInt64": 0,
45 "optUint32": 0,
46 "optUint64": 0,
47 "optSint32": 0,
48 "optSint64": 0,
49 "optFixed32": 0,
50 "optFixed64": 0,
51 "optSfixed32": 0,
52 "optSfixed64": 0,
53 "optFloat": 0,
54 "optDouble": 0,
55 "optBytes": "",
56 "optString": ""
57}`,
58 wantMessage: &pb2.Scalars{
59 OptBool: scalar.Bool(false),
60 OptInt32: scalar.Int32(0),
61 OptInt64: scalar.Int64(0),
62 OptUint32: scalar.Uint32(0),
63 OptUint64: scalar.Uint64(0),
64 OptSint32: scalar.Int32(0),
65 OptSint64: scalar.Int64(0),
66 OptFixed32: scalar.Uint32(0),
67 OptFixed64: scalar.Uint64(0),
68 OptSfixed32: scalar.Int32(0),
69 OptSfixed64: scalar.Int64(0),
70 OptFloat: scalar.Float32(0),
71 OptDouble: scalar.Float64(0),
72 OptBytes: []byte{},
73 OptString: scalar.String(""),
74 },
75 }, {
76 desc: "proto3 scalars set to zero values",
77 inputMessage: &pb3.Scalars{},
78 inputText: `{
79 "sBool": false,
80 "sInt32": 0,
81 "sInt64": 0,
82 "sUint32": 0,
83 "sUint64": 0,
84 "sSint32": 0,
85 "sSint64": 0,
86 "sFixed32": 0,
87 "sFixed64": 0,
88 "sSfixed32": 0,
89 "sSfixed64": 0,
90 "sFloat": 0,
91 "sDouble": 0,
92 "sBytes": "",
93 "sString": ""
94}`,
95 wantMessage: &pb3.Scalars{},
96 }, {
97 desc: "proto2 optional scalars set to null",
98 inputMessage: &pb2.Scalars{},
99 inputText: `{
100 "optBool": null,
101 "optInt32": null,
102 "optInt64": null,
103 "optUint32": null,
104 "optUint64": null,
105 "optSint32": null,
106 "optSint64": null,
107 "optFixed32": null,
108 "optFixed64": null,
109 "optSfixed32": null,
110 "optSfixed64": null,
111 "optFloat": null,
112 "optDouble": null,
113 "optBytes": null,
114 "optString": null
115}`,
116 wantMessage: &pb2.Scalars{},
117 }, {
118 desc: "proto3 scalars set to null",
119 inputMessage: &pb3.Scalars{},
120 inputText: `{
121 "sBool": null,
122 "sInt32": null,
123 "sInt64": null,
124 "sUint32": null,
125 "sUint64": null,
126 "sSint32": null,
127 "sSint64": null,
128 "sFixed32": null,
129 "sFixed64": null,
130 "sSfixed32": null,
131 "sSfixed64": null,
132 "sFloat": null,
133 "sDouble": null,
134 "sBytes": null,
135 "sString": null
136}`,
137 wantMessage: &pb3.Scalars{},
138 }, {
139 desc: "boolean",
140 inputMessage: &pb3.Scalars{},
141 inputText: `{"sBool": true}`,
142 wantMessage: &pb3.Scalars{
143 SBool: true,
144 },
145 }, {
146 desc: "not boolean",
147 inputMessage: &pb3.Scalars{},
148 inputText: `{"sBool": "true"}`,
149 wantErr: true,
150 }, {
151 desc: "float and double",
152 inputMessage: &pb3.Scalars{},
153 inputText: `{
154 "sFloat": 1.234,
155 "sDouble": 5.678
156}`,
157 wantMessage: &pb3.Scalars{
158 SFloat: 1.234,
159 SDouble: 5.678,
160 },
161 }, {
162 desc: "float and double in string",
163 inputMessage: &pb3.Scalars{},
164 inputText: `{
165 "sFloat": "1.234",
166 "sDouble": "5.678"
167}`,
168 wantMessage: &pb3.Scalars{
169 SFloat: 1.234,
170 SDouble: 5.678,
171 },
172 }, {
173 desc: "float and double in E notation",
174 inputMessage: &pb3.Scalars{},
175 inputText: `{
176 "sFloat": 12.34E-1,
177 "sDouble": 5.678e4
178}`,
179 wantMessage: &pb3.Scalars{
180 SFloat: 1.234,
181 SDouble: 56780,
182 },
183 }, {
184 desc: "float and double in string E notation",
185 inputMessage: &pb3.Scalars{},
186 inputText: `{
187 "sFloat": "12.34E-1",
188 "sDouble": "5.678e4"
189}`,
190 wantMessage: &pb3.Scalars{
191 SFloat: 1.234,
192 SDouble: 56780,
193 },
194 }, {
195 desc: "float exceeds limit",
196 inputMessage: &pb3.Scalars{},
197 inputText: `{"sFloat": 3.4e39}`,
198 wantErr: true,
199 }, {
200 desc: "float in string exceeds limit",
201 inputMessage: &pb3.Scalars{},
202 inputText: `{"sFloat": "-3.4e39"}`,
203 wantErr: true,
204 }, {
205 desc: "double exceeds limit",
206 inputMessage: &pb3.Scalars{},
207 inputText: `{"sFloat": -1.79e+309}`,
208 wantErr: true,
209 }, {
210 desc: "double in string exceeds limit",
211 inputMessage: &pb3.Scalars{},
212 inputText: `{"sFloat": "1.79e+309"}`,
213 wantErr: true,
214 }, {
215 desc: "infinites",
216 inputMessage: &pb3.Scalars{},
217 inputText: `{"sFloat": "Infinity", "sDouble": "-Infinity"}`,
218 wantMessage: &pb3.Scalars{
219 SFloat: float32(math.Inf(+1)),
220 SDouble: math.Inf(-1),
221 },
222 }, {
223 desc: "not float",
224 inputMessage: &pb3.Scalars{},
225 inputText: `{"sFloat": true}`,
226 wantErr: true,
227 }, {
228 desc: "not double",
229 inputMessage: &pb3.Scalars{},
230 inputText: `{"sDouble": "not a number"}`,
231 wantErr: true,
232 }, {
233 desc: "integers",
234 inputMessage: &pb3.Scalars{},
235 inputText: `{
236 "sInt32": 1234,
237 "sInt64": -1234,
238 "sUint32": 1e2,
239 "sUint64": 100E-2,
240 "sSint32": 1.0,
241 "sSint64": -1.0,
242 "sFixed32": 1.234e+5,
243 "sFixed64": 1200E-2,
244 "sSfixed32": -1.234e05,
245 "sSfixed64": -1200e-02
246}`,
247 wantMessage: &pb3.Scalars{
248 SInt32: 1234,
249 SInt64: -1234,
250 SUint32: 100,
251 SUint64: 1,
252 SSint32: 1,
253 SSint64: -1,
254 SFixed32: 123400,
255 SFixed64: 12,
256 SSfixed32: -123400,
257 SSfixed64: -12,
258 },
259 }, {
260 desc: "integers in string",
261 inputMessage: &pb3.Scalars{},
262 inputText: `{
263 "sInt32": "1234",
264 "sInt64": "-1234",
265 "sUint32": "1e2",
266 "sUint64": "100E-2",
267 "sSint32": "1.0",
268 "sSint64": "-1.0",
269 "sFixed32": "1.234e+5",
270 "sFixed64": "1200E-2",
271 "sSfixed32": "-1.234e05",
272 "sSfixed64": "-1200e-02"
273}`,
274 wantMessage: &pb3.Scalars{
275 SInt32: 1234,
276 SInt64: -1234,
277 SUint32: 100,
278 SUint64: 1,
279 SSint32: 1,
280 SSint64: -1,
281 SFixed32: 123400,
282 SFixed64: 12,
283 SSfixed32: -123400,
284 SSfixed64: -12,
285 },
286 }, {
287 desc: "integers in escaped string",
288 inputMessage: &pb3.Scalars{},
289 inputText: `{"sInt32": "\u0031\u0032"}`,
290 wantMessage: &pb3.Scalars{
291 SInt32: 12,
292 },
293 }, {
294 desc: "number is not an integer",
295 inputMessage: &pb3.Scalars{},
296 inputText: `{"sInt32": 1.001}`,
297 wantErr: true,
298 }, {
299 desc: "32-bit int exceeds limit",
300 inputMessage: &pb3.Scalars{},
301 inputText: `{"sInt32": 2e10}`,
302 wantErr: true,
303 }, {
304 desc: "64-bit int exceeds limit",
305 inputMessage: &pb3.Scalars{},
306 inputText: `{"sSfixed64": -9e19}`,
307 wantErr: true,
308 }, {
309 desc: "not integer",
310 inputMessage: &pb3.Scalars{},
311 inputText: `{"sInt32": "not a number"}`,
312 wantErr: true,
313 }, {
314 desc: "not unsigned integer",
315 inputMessage: &pb3.Scalars{},
316 inputText: `{"sUint32": "not a number"}`,
317 wantErr: true,
318 }, {
319 desc: "number is not an unsigned integer",
320 inputMessage: &pb3.Scalars{},
321 inputText: `{"sUint32": -1}`,
322 wantErr: true,
323 }, {
324 desc: "string",
325 inputMessage: &pb2.Scalars{},
326 inputText: `{"optString": "谷歌"}`,
327 wantMessage: &pb2.Scalars{
328 OptString: scalar.String("谷歌"),
329 },
330 }, {
331 desc: "string with invalid UTF-8",
332 inputMessage: &pb3.Scalars{},
333 inputText: "{\"sString\": \"\xff\"}",
334 wantMessage: &pb3.Scalars{
335 SString: "\xff",
336 },
337 wantErr: true,
338 }, {
339 desc: "not string",
340 inputMessage: &pb2.Scalars{},
341 inputText: `{"optString": 42}`,
342 wantErr: true,
343 }, {
344 desc: "bytes",
345 inputMessage: &pb3.Scalars{},
346 inputText: `{"sBytes": "aGVsbG8gd29ybGQ"}`,
347 wantMessage: &pb3.Scalars{
348 SBytes: []byte("hello world"),
349 },
350 }, {
351 desc: "bytes padded",
352 inputMessage: &pb3.Scalars{},
353 inputText: `{"sBytes": "aGVsbG8gd29ybGQ="}`,
354 wantMessage: &pb3.Scalars{
355 SBytes: []byte("hello world"),
356 },
357 }, {
358 desc: "not bytes",
359 inputMessage: &pb3.Scalars{},
360 inputText: `{"sBytes": true}`,
361 wantErr: true,
362 }, {
363 desc: "proto2 enum",
364 inputMessage: &pb2.Enums{},
365 inputText: `{
366 "optEnum": "ONE",
367 "optNestedEnum": "UNO"
368}`,
369 wantMessage: &pb2.Enums{
370 OptEnum: pb2.Enum_ONE.Enum(),
371 OptNestedEnum: pb2.Enums_UNO.Enum(),
372 },
373 }, {
374 desc: "proto3 enum",
375 inputMessage: &pb3.Enums{},
376 inputText: `{
377 "sEnum": "ONE",
378 "sNestedEnum": "DIEZ"
379}`,
380 wantMessage: &pb3.Enums{
381 SEnum: pb3.Enum_ONE,
382 SNestedEnum: pb3.Enums_DIEZ,
383 },
384 }, {
385 desc: "enum numeric value",
386 inputMessage: &pb3.Enums{},
387 inputText: `{
388 "sEnum": 2,
389 "sNestedEnum": 2
390}`,
391 wantMessage: &pb3.Enums{
392 SEnum: pb3.Enum_TWO,
393 SNestedEnum: pb3.Enums_DOS,
394 },
395 }, {
396 desc: "enum unnamed numeric value",
397 inputMessage: &pb3.Enums{},
398 inputText: `{
399 "sEnum": 101,
400 "sNestedEnum": -101
401}`,
402 wantMessage: &pb3.Enums{
403 SEnum: 101,
404 SNestedEnum: -101,
405 },
406 }, {
407 desc: "enum set to number string",
408 inputMessage: &pb3.Enums{},
409 inputText: `{
410 "sEnum": "1",
411}`,
412 wantErr: true,
413 }, {
414 desc: "enum set to invalid named",
415 inputMessage: &pb3.Enums{},
416 inputText: `{
417 "sEnum": "UNNAMED",
418}`,
419 wantErr: true,
420 }, {
421 desc: "enum set to not enum",
422 inputMessage: &pb3.Enums{},
423 inputText: `{
424 "sEnum": true,
425}`,
426 wantErr: true,
427 }, {
428 desc: "proto name",
429 inputMessage: &pb3.JSONNames{},
430 inputText: `{
431 "s_string": "proto name used"
432}`,
433 wantMessage: &pb3.JSONNames{
434 SString: "proto name used",
435 },
436 }, {
437 desc: "json_name",
438 inputMessage: &pb3.JSONNames{},
439 inputText: `{
440 "foo_bar": "json_name used"
441}`,
442 wantMessage: &pb3.JSONNames{
443 SString: "json_name used",
444 },
445 }, {
446 desc: "camelCase name",
447 inputMessage: &pb3.JSONNames{},
448 inputText: `{
449 "sString": "camelcase used"
450}`,
451 wantErr: true,
452 }, {
453 desc: "proto name and json_name",
454 inputMessage: &pb3.JSONNames{},
455 inputText: `{
456 "foo_bar": "json_name used",
457 "s_string": "proto name used"
458}`,
459 wantErr: true,
460 }, {
461 desc: "duplicate field names",
462 inputMessage: &pb3.JSONNames{},
463 inputText: `{
464 "foo_bar": "one",
465 "foo_bar": "two",
466}`,
467 wantErr: true,
468 }, {
469 desc: "null message",
470 inputMessage: &pb2.Nests{},
471 inputText: "null",
472 wantErr: true,
473 }, {
474 desc: "proto2 nested message not set",
475 inputMessage: &pb2.Nests{},
476 inputText: "{}",
477 wantMessage: &pb2.Nests{},
478 }, {
479 desc: "proto2 nested message set to null",
480 inputMessage: &pb2.Nests{},
481 inputText: `{
482 "optNested": null,
483 "optgroup": null
484}`,
485 wantMessage: &pb2.Nests{},
486 }, {
487 desc: "proto2 nested message set to empty",
488 inputMessage: &pb2.Nests{},
489 inputText: `{
490 "optNested": {},
491 "optgroup": {}
492}`,
493 wantMessage: &pb2.Nests{
494 OptNested: &pb2.Nested{},
495 Optgroup: &pb2.Nests_OptGroup{},
496 },
497 }, {
498 desc: "proto2 nested messages",
499 inputMessage: &pb2.Nests{},
500 inputText: `{
501 "optNested": {
502 "optString": "nested message",
503 "optNested": {
504 "optString": "another nested message"
505 }
506 }
507}`,
508 wantMessage: &pb2.Nests{
509 OptNested: &pb2.Nested{
510 OptString: scalar.String("nested message"),
511 OptNested: &pb2.Nested{
512 OptString: scalar.String("another nested message"),
513 },
514 },
515 },
516 }, {
517 desc: "proto2 groups",
518 inputMessage: &pb2.Nests{},
519 inputText: `{
520 "optgroup": {
521 "optString": "inside a group",
522 "optNested": {
523 "optString": "nested message inside a group"
524 },
525 "optnestedgroup": {
526 "optFixed32": 47
527 }
528 }
529}`,
530 wantMessage: &pb2.Nests{
531 Optgroup: &pb2.Nests_OptGroup{
532 OptString: scalar.String("inside a group"),
533 OptNested: &pb2.Nested{
534 OptString: scalar.String("nested message inside a group"),
535 },
536 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
537 OptFixed32: scalar.Uint32(47),
538 },
539 },
540 },
541 }, {
542 desc: "proto3 nested message not set",
543 inputMessage: &pb3.Nests{},
544 inputText: "{}",
545 wantMessage: &pb3.Nests{},
546 }, {
547 desc: "proto3 nested message set to null",
548 inputMessage: &pb3.Nests{},
549 inputText: `{"sNested": null}`,
550 wantMessage: &pb3.Nests{},
551 }, {
552 desc: "proto3 nested message set to empty",
553 inputMessage: &pb3.Nests{},
554 inputText: `{"sNested": {}}`,
555 wantMessage: &pb3.Nests{
556 SNested: &pb3.Nested{},
557 },
558 }, {
559 desc: "proto3 nested message",
560 inputMessage: &pb3.Nests{},
561 inputText: `{
562 "sNested": {
563 "sString": "nested message",
564 "sNested": {
565 "sString": "another nested message"
566 }
567 }
568}`,
569 wantMessage: &pb3.Nests{
570 SNested: &pb3.Nested{
571 SString: "nested message",
572 SNested: &pb3.Nested{
573 SString: "another nested message",
574 },
575 },
576 },
577 }, {
578 desc: "message set to non-message",
579 inputMessage: &pb3.Nests{},
580 inputText: `"not valid"`,
581 wantErr: true,
582 }, {
583 desc: "nested message set to non-message",
584 inputMessage: &pb3.Nests{},
585 inputText: `{"sNested": true}`,
586 wantErr: true,
587 }, {
588 desc: "oneof not set",
589 inputMessage: &pb3.Oneofs{},
590 inputText: "{}",
591 wantMessage: &pb3.Oneofs{},
592 }, {
593 desc: "oneof set to empty string",
594 inputMessage: &pb3.Oneofs{},
595 inputText: `{"oneofString": ""}`,
596 wantMessage: &pb3.Oneofs{
597 Union: &pb3.Oneofs_OneofString{},
598 },
599 }, {
600 desc: "oneof set to string",
601 inputMessage: &pb3.Oneofs{},
602 inputText: `{"oneofString": "hello"}`,
603 wantMessage: &pb3.Oneofs{
604 Union: &pb3.Oneofs_OneofString{
605 OneofString: "hello",
606 },
607 },
608 }, {
609 desc: "oneof set to enum",
610 inputMessage: &pb3.Oneofs{},
611 inputText: `{"oneofEnum": "ZERO"}`,
612 wantMessage: &pb3.Oneofs{
613 Union: &pb3.Oneofs_OneofEnum{
614 OneofEnum: pb3.Enum_ZERO,
615 },
616 },
617 }, {
618 desc: "oneof set to empty message",
619 inputMessage: &pb3.Oneofs{},
620 inputText: `{"oneofNested": {}}`,
621 wantMessage: &pb3.Oneofs{
622 Union: &pb3.Oneofs_OneofNested{
623 OneofNested: &pb3.Nested{},
624 },
625 },
626 }, {
627 desc: "oneof set to message",
628 inputMessage: &pb3.Oneofs{},
629 inputText: `{
630 "oneofNested": {
631 "sString": "nested message"
632 }
633}`,
634 wantMessage: &pb3.Oneofs{
635 Union: &pb3.Oneofs_OneofNested{
636 OneofNested: &pb3.Nested{
637 SString: "nested message",
638 },
639 },
640 },
641 }, {
642 desc: "repeated null fields",
643 inputMessage: &pb2.Repeats{},
644 inputText: `{
645 "rptString": null,
646 "rptInt32" : null,
647 "rptFloat" : null,
648 "rptBytes" : null
649}`,
650 wantMessage: &pb2.Repeats{},
651 }, {
652 desc: "repeated scalars",
653 inputMessage: &pb2.Repeats{},
654 inputText: `{
655 "rptString": ["hello", "world"],
656 "rptInt32" : [-1, 0, 1],
657 "rptBool" : [false, true]
658}`,
659 wantMessage: &pb2.Repeats{
660 RptString: []string{"hello", "world"},
661 RptInt32: []int32{-1, 0, 1},
662 RptBool: []bool{false, true},
663 },
664 }, {
665 desc: "repeated enums",
666 inputMessage: &pb2.Enums{},
667 inputText: `{
668 "rptEnum" : ["TEN", 1, 42],
669 "rptNestedEnum": ["DOS", 2, -47]
670}`,
671 wantMessage: &pb2.Enums{
672 RptEnum: []pb2.Enum{pb2.Enum_TEN, pb2.Enum_ONE, 42},
673 RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_DOS, pb2.Enums_DOS, -47},
674 },
675 }, {
676 desc: "repeated messages",
677 inputMessage: &pb2.Nests{},
678 inputText: `{
679 "rptNested": [
680 {
681 "optString": "repeat nested one"
682 },
683 {
684 "optString": "repeat nested two",
685 "optNested": {
686 "optString": "inside repeat nested two"
687 }
688 },
689 {}
690 ]
691}`,
692 wantMessage: &pb2.Nests{
693 RptNested: []*pb2.Nested{
694 {
695 OptString: scalar.String("repeat nested one"),
696 },
697 {
698 OptString: scalar.String("repeat nested two"),
699 OptNested: &pb2.Nested{
700 OptString: scalar.String("inside repeat nested two"),
701 },
702 },
703 {},
704 },
705 },
706 }, {
707 desc: "repeated groups",
708 inputMessage: &pb2.Nests{},
709 inputText: `{
710 "rptgroup": [
711 {
712 "rptString": ["hello", "world"]
713 },
714 {}
715 ]
716}
717`,
718 wantMessage: &pb2.Nests{
719 Rptgroup: []*pb2.Nests_RptGroup{
720 {
721 RptString: []string{"hello", "world"},
722 },
723 {},
724 },
725 },
726 }, {
727 desc: "repeated scalars containing invalid type",
728 inputMessage: &pb2.Repeats{},
729 inputText: `{"rptString": ["hello", null, "world"]}`,
730 wantErr: true,
731 }, {
732 desc: "repeated messages containing invalid type",
733 inputMessage: &pb2.Nests{},
734 inputText: `{"rptNested": [{}, null]}`,
735 wantErr: true,
736 }, {
737 desc: "map fields 1",
738 inputMessage: &pb3.Maps{},
739 inputText: `{
740 "int32ToStr": {
741 "-101": "-101",
742 "0" : "zero",
743 "255" : "0xff"
744 },
745 "boolToUint32": {
746 "false": 101,
747 "true" : "42"
748 }
749}`,
750 wantMessage: &pb3.Maps{
751 Int32ToStr: map[int32]string{
752 -101: "-101",
753 0xff: "0xff",
754 0: "zero",
755 },
756 BoolToUint32: map[bool]uint32{
757 true: 42,
758 false: 101,
759 },
760 },
761 }, {
762 desc: "map fields 2",
763 inputMessage: &pb3.Maps{},
764 inputText: `{
765 "uint64ToEnum": {
766 "1" : "ONE",
767 "2" : 2,
768 "10": 101
769 }
770}`,
771 wantMessage: &pb3.Maps{
772 Uint64ToEnum: map[uint64]pb3.Enum{
773 1: pb3.Enum_ONE,
774 2: pb3.Enum_TWO,
775 10: 101,
776 },
777 },
778 }, {
779 desc: "map fields 3",
780 inputMessage: &pb3.Maps{},
781 inputText: `{
782 "strToNested": {
783 "nested_one": {
784 "sString": "nested in a map"
785 },
786 "nested_two": {}
787 }
788}`,
789 wantMessage: &pb3.Maps{
790 StrToNested: map[string]*pb3.Nested{
791 "nested_one": {
792 SString: "nested in a map",
793 },
794 "nested_two": {},
795 },
796 },
797 }, {
798 desc: "map fields 4",
799 inputMessage: &pb3.Maps{},
800 inputText: `{
801 "strToOneofs": {
802 "nested": {
803 "oneofNested": {
804 "sString": "nested oneof in map field value"
805 }
806 },
807 "string": {
808 "oneofString": "hello"
809 }
810 }
811}`,
812 wantMessage: &pb3.Maps{
813 StrToOneofs: map[string]*pb3.Oneofs{
814 "string": {
815 Union: &pb3.Oneofs_OneofString{
816 OneofString: "hello",
817 },
818 },
819 "nested": {
820 Union: &pb3.Oneofs_OneofNested{
821 OneofNested: &pb3.Nested{
822 SString: "nested oneof in map field value",
823 },
824 },
825 },
826 },
827 },
828 }, {
829 desc: "map contains duplicate keys",
830 inputMessage: &pb3.Maps{},
831 inputText: `{
832 "int32ToStr": {
833 "0": "cero",
834 "0": "zero"
835 }
836}
837`,
838 wantErr: true,
839 }, {
840 desc: "map key empty string",
841 inputMessage: &pb3.Maps{},
842 inputText: `{
843 "strToNested": {
844 "": {}
845 }
846}`,
847 wantMessage: &pb3.Maps{
848 StrToNested: map[string]*pb3.Nested{
849 "": {},
850 },
851 },
852 }, {
853 desc: "map contains invalid key 1",
854 inputMessage: &pb3.Maps{},
855 inputText: `{
856 "int32ToStr": {
857 "invalid": "cero"
858}`,
859 wantErr: true,
860 }, {
861 desc: "map contains invalid key 2",
862 inputMessage: &pb3.Maps{},
863 inputText: `{
864 "int32ToStr": {
865 "1.02": "float"
866}`,
867 wantErr: true,
868 }, {
869 desc: "map contains invalid key 3",
870 inputMessage: &pb3.Maps{},
871 inputText: `{
872 "int32ToStr": {
873 "2147483648": "exceeds 32-bit integer max limit"
874}`,
875 wantErr: true,
876 }, {
877 desc: "map contains invalid key 4",
878 inputMessage: &pb3.Maps{},
879 inputText: `{
880 "uint64ToEnum": {
881 "-1": 0
882 }
883}`,
884 wantErr: true,
885 }, {
886 desc: "map contains invalid value",
887 inputMessage: &pb3.Maps{},
888 inputText: `{
889 "int32ToStr": {
890 "101": true
891}`,
892 wantErr: true,
893 }, {
894 desc: "map contains null for scalar value",
895 inputMessage: &pb3.Maps{},
896 inputText: `{
897 "int32ToStr": {
898 "101": null
899}`,
900 wantErr: true,
901 }, {
902 desc: "map contains null for message value",
903 inputMessage: &pb3.Maps{},
904 inputText: `{
905 "strToNested": {
906 "hello": null
907 }
908}`,
909 wantErr: true,
910 }}
911
912 for _, tt := range tests {
913 tt := tt
914 t.Run(tt.desc, func(t *testing.T) {
915 err := tt.umo.Unmarshal(tt.inputMessage, []byte(tt.inputText))
916 if err != nil && !tt.wantErr {
917 t.Errorf("Unmarshal() returned error: %v\n\n", err)
918 }
919 if err == nil && tt.wantErr {
920 t.Error("Unmarshal() got nil error, want error\n\n")
921 }
922 if tt.wantMessage != nil && !protoV1.Equal(tt.inputMessage.(protoV1.Message), tt.wantMessage.(protoV1.Message)) {
923 t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage)
924 }
925 })
926 }
927}