1
2
3
4
5
6
7 package bsonrw
8
9 import (
10 "bytes"
11 "errors"
12 "fmt"
13 "io"
14 "math"
15 "testing"
16
17 "github.com/google/go-cmp/cmp"
18 "go.mongodb.org/mongo-driver/bson/bsontype"
19 "go.mongodb.org/mongo-driver/bson/primitive"
20 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
21 )
22
23 func TestValueReader(t *testing.T) {
24 t.Run("ReadBinary", func(t *testing.T) {
25 testCases := []struct {
26 name string
27 data []byte
28 offset int64
29 btype byte
30 b []byte
31 err error
32 vType bsontype.Type
33 }{
34 {
35 "incorrect type",
36 []byte{},
37 0,
38 0,
39 nil,
40 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Binary),
41 bsontype.EmbeddedDocument,
42 },
43 {
44 "length too short",
45 []byte{},
46 0,
47 0,
48 nil,
49 io.EOF,
50 bsontype.Binary,
51 },
52 {
53 "no byte available",
54 []byte{0x00, 0x00, 0x00, 0x00},
55 0,
56 0,
57 nil,
58 io.EOF,
59 bsontype.Binary,
60 },
61 {
62 "not enough bytes for binary",
63 []byte{0x05, 0x00, 0x00, 0x00, 0x00},
64 0,
65 0,
66 nil,
67 io.EOF,
68 bsontype.Binary,
69 },
70 {
71 "success",
72 []byte{0x03, 0x00, 0x00, 0x00, 0xEA, 0x01, 0x02, 0x03},
73 0,
74 0xEA,
75 []byte{0x01, 0x02, 0x03},
76 nil,
77 bsontype.Binary,
78 },
79 }
80
81 for _, tc := range testCases {
82 t.Run(tc.name, func(t *testing.T) {
83 vr := &valueReader{
84 offset: tc.offset,
85 d: tc.data,
86 stack: []vrState{
87 {mode: mTopLevel},
88 {
89 mode: mElement,
90 vType: tc.vType,
91 },
92 },
93 frame: 1,
94 }
95
96 b, btype, err := vr.ReadBinary()
97 if !errequal(t, err, tc.err) {
98 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
99 }
100 if btype != tc.btype {
101 t.Errorf("Incorrect binary type returned. got %v; want %v", btype, tc.btype)
102 }
103 if !bytes.Equal(b, tc.b) {
104 t.Errorf("Binary data does not match. got %v; want %v", b, tc.b)
105 }
106 })
107 }
108 })
109 t.Run("ReadBoolean", func(t *testing.T) {
110 testCases := []struct {
111 name string
112 data []byte
113 offset int64
114 boolean bool
115 err error
116 vType bsontype.Type
117 }{
118 {
119 "incorrect type",
120 []byte{},
121 0,
122 false,
123 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Boolean),
124 bsontype.EmbeddedDocument,
125 },
126 {
127 "no byte available",
128 []byte{},
129 0,
130 false,
131 io.EOF,
132 bsontype.Boolean,
133 },
134 {
135 "invalid byte for boolean",
136 []byte{0x03},
137 0,
138 false,
139 fmt.Errorf("invalid byte for boolean, %b", 0x03),
140 bsontype.Boolean,
141 },
142 {
143 "success",
144 []byte{0x01},
145 0,
146 true,
147 nil,
148 bsontype.Boolean,
149 },
150 }
151
152 for _, tc := range testCases {
153 t.Run(tc.name, func(t *testing.T) {
154 vr := &valueReader{
155 offset: tc.offset,
156 d: tc.data,
157 stack: []vrState{
158 {mode: mTopLevel},
159 {
160 mode: mElement,
161 vType: tc.vType,
162 },
163 },
164 frame: 1,
165 }
166
167 boolean, err := vr.ReadBoolean()
168 if !errequal(t, err, tc.err) {
169 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
170 }
171 if boolean != tc.boolean {
172 t.Errorf("Incorrect boolean returned. got %v; want %v", boolean, tc.boolean)
173 }
174 })
175 }
176 })
177 t.Run("ReadDocument", func(t *testing.T) {
178 t.Run("TopLevel", func(t *testing.T) {
179 doc := []byte{0x05, 0x00, 0x00, 0x00, 0x00}
180 vr := &valueReader{
181 offset: 0,
182 stack: []vrState{{mode: mTopLevel}},
183 frame: 0,
184 }
185
186
187 vr.d = []byte{0x00, 0x00}
188 _, err := vr.ReadDocument()
189 if !errors.Is(err, io.EOF) {
190 t.Errorf("Expected io.EOF with document length too small. got %v; want %v", err, io.EOF)
191 }
192
193 vr.d = doc
194 _, err = vr.ReadDocument()
195 noerr(t, err)
196 if vr.stack[vr.frame].end != 5 {
197 t.Errorf("Incorrect end for document. got %d; want %d", vr.stack[vr.frame].end, 5)
198 }
199 })
200 t.Run("EmbeddedDocument", func(t *testing.T) {
201 vr := &valueReader{
202 offset: 0,
203 stack: []vrState{
204 {mode: mTopLevel},
205 {mode: mElement, vType: bsontype.Boolean},
206 },
207 frame: 1,
208 }
209
210 var wanterr = (&valueReader{stack: []vrState{{mode: mElement, vType: bsontype.Boolean}}}).typeError(bsontype.EmbeddedDocument)
211 _, err := vr.ReadDocument()
212 if err == nil || err.Error() != wanterr.Error() {
213 t.Errorf("Incorrect returned error. got %v; want %v", err, wanterr)
214 }
215
216 vr.stack[1].mode = mArray
217 wanterr = vr.invalidTransitionErr(mDocument, "ReadDocument", []mode{mTopLevel, mElement, mValue})
218 _, err = vr.ReadDocument()
219 if err == nil || err.Error() != wanterr.Error() {
220 t.Errorf("Incorrect returned error. got %v; want %v", err, wanterr)
221 }
222
223 vr.stack[1].mode, vr.stack[1].vType = mElement, bsontype.EmbeddedDocument
224 vr.d = []byte{0x0A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}
225 vr.offset = 4
226 _, err = vr.ReadDocument()
227 noerr(t, err)
228 if len(vr.stack) != 3 {
229 t.Errorf("Incorrect number of stack frames. got %d; want %d", len(vr.stack), 3)
230 }
231 if vr.stack[2].mode != mDocument {
232 t.Errorf("Incorrect mode set. got %v; want %v", vr.stack[2].mode, mDocument)
233 }
234 if vr.stack[2].end != 9 {
235 t.Errorf("End of embedded document is not correct. got %d; want %d", vr.stack[2].end, 9)
236 }
237 if vr.offset != 8 {
238 t.Errorf("Offset not incremented correctly. got %d; want %d", vr.offset, 8)
239 }
240
241 vr.frame--
242 _, err = vr.ReadDocument()
243 if !errors.Is(err, io.EOF) {
244 t.Errorf("Should return error when attempting to read length with not enough bytes. got %v; want %v", err, io.EOF)
245 }
246 })
247 })
248 t.Run("ReadBinary", func(t *testing.T) {
249 codeWithScope := []byte{
250 0x11, 0x00, 0x00, 0x00,
251 0x4, 0x00, 0x00, 0x00,
252 'f', 'o', 'o', 0x00,
253 0x05, 0x00, 0x00, 0x00, 0x00,
254 }
255 mismatchCodeWithScope := []byte{
256 0x11, 0x00, 0x00, 0x00,
257 0x4, 0x00, 0x00, 0x00,
258 'f', 'o', 'o', 0x00,
259 0x07, 0x00, 0x00, 0x00,
260 0x0A, 0x00,
261 0x00,
262 }
263 invalidCodeWithScope := []byte{
264 0x7, 0x00, 0x00, 0x00,
265 0x0, 0x00, 0x00, 0x00,
266 0x05, 0x00, 0x00, 0x00, 0x00,
267 }
268 testCases := []struct {
269 name string
270 data []byte
271 offset int64
272 err error
273 vType bsontype.Type
274 }{
275 {
276 "incorrect type",
277 []byte{},
278 0,
279 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.CodeWithScope),
280 bsontype.EmbeddedDocument,
281 },
282 {
283 "total length not enough bytes",
284 []byte{},
285 0,
286 io.EOF,
287 bsontype.CodeWithScope,
288 },
289 {
290 "string length not enough bytes",
291 codeWithScope[:4],
292 0,
293 io.EOF,
294 bsontype.CodeWithScope,
295 },
296 {
297 "not enough string bytes",
298 codeWithScope[:8],
299 0,
300 io.EOF,
301 bsontype.CodeWithScope,
302 },
303 {
304 "document length not enough bytes",
305 codeWithScope[:12],
306 0,
307 io.EOF,
308 bsontype.CodeWithScope,
309 },
310 {
311 "length mismatch",
312 mismatchCodeWithScope,
313 0,
314 fmt.Errorf("length of CodeWithScope does not match lengths of components; total: %d; components: %d", 17, 19),
315 bsontype.CodeWithScope,
316 },
317 {
318 "invalid strLength",
319 invalidCodeWithScope,
320 0,
321 fmt.Errorf("invalid string length: %d", 0),
322 bsontype.CodeWithScope,
323 },
324 }
325
326 for _, tc := range testCases {
327 t.Run(tc.name, func(t *testing.T) {
328 vr := &valueReader{
329 offset: tc.offset,
330 d: tc.data,
331 stack: []vrState{
332 {mode: mTopLevel},
333 {
334 mode: mElement,
335 vType: tc.vType,
336 },
337 },
338 frame: 1,
339 }
340
341 _, _, err := vr.ReadCodeWithScope()
342 if !errequal(t, err, tc.err) {
343 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
344 }
345 })
346 }
347
348 t.Run("success", func(t *testing.T) {
349 doc := []byte{0x00, 0x00, 0x00, 0x00}
350 doc = append(doc, codeWithScope...)
351 doc = append(doc, 0x00)
352 vr := &valueReader{
353 offset: 4,
354 d: doc,
355 stack: []vrState{
356 {mode: mTopLevel},
357 {mode: mElement, vType: bsontype.CodeWithScope},
358 },
359 frame: 1,
360 }
361
362 code, _, err := vr.ReadCodeWithScope()
363 noerr(t, err)
364 if code != "foo" {
365 t.Errorf("Code does not match. got %s; want %s", code, "foo")
366 }
367 if len(vr.stack) != 3 {
368 t.Errorf("Incorrect number of stack frames. got %d; want %d", len(vr.stack), 3)
369 }
370 if vr.stack[2].mode != mCodeWithScope {
371 t.Errorf("Incorrect mode set. got %v; want %v", vr.stack[2].mode, mDocument)
372 }
373 if vr.stack[2].end != 21 {
374 t.Errorf("End of scope is not correct. got %d; want %d", vr.stack[2].end, 21)
375 }
376 if vr.offset != 20 {
377 t.Errorf("Offset not incremented correctly. got %d; want %d", vr.offset, 20)
378 }
379 })
380 })
381 t.Run("ReadDBPointer", func(t *testing.T) {
382 testCases := []struct {
383 name string
384 data []byte
385 offset int64
386 ns string
387 oid primitive.ObjectID
388 err error
389 vType bsontype.Type
390 }{
391 {
392 "incorrect type",
393 []byte{},
394 0,
395 "",
396 primitive.ObjectID{},
397 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.DBPointer),
398 bsontype.EmbeddedDocument,
399 },
400 {
401 "length too short",
402 []byte{},
403 0,
404 "",
405 primitive.ObjectID{},
406 io.EOF,
407 bsontype.DBPointer,
408 },
409 {
410 "not enough bytes for namespace",
411 []byte{0x04, 0x00, 0x00, 0x00},
412 0,
413 "",
414 primitive.ObjectID{},
415 io.EOF,
416 bsontype.DBPointer,
417 },
418 {
419 "not enough bytes for objectID",
420 []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},
421 0,
422 "",
423 primitive.ObjectID{},
424 io.EOF,
425 bsontype.DBPointer,
426 },
427 {
428 "success",
429 []byte{
430 0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00,
431 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
432 },
433 0,
434 "foo",
435 primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
436 nil,
437 bsontype.DBPointer,
438 },
439 }
440
441 for _, tc := range testCases {
442 t.Run(tc.name, func(t *testing.T) {
443 vr := &valueReader{
444 offset: tc.offset,
445 d: tc.data,
446 stack: []vrState{
447 {mode: mTopLevel},
448 {
449 mode: mElement,
450 vType: tc.vType,
451 },
452 },
453 frame: 1,
454 }
455
456 ns, oid, err := vr.ReadDBPointer()
457 if !errequal(t, err, tc.err) {
458 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
459 }
460 if ns != tc.ns {
461 t.Errorf("Incorrect namespace returned. got %v; want %v", ns, tc.ns)
462 }
463 if oid != tc.oid {
464 t.Errorf("ObjectIDs did not match. got %v; want %v", oid, tc.oid)
465 }
466 })
467 }
468 })
469 t.Run("ReadDateTime", func(t *testing.T) {
470 testCases := []struct {
471 name string
472 data []byte
473 offset int64
474 dt int64
475 err error
476 vType bsontype.Type
477 }{
478 {
479 "incorrect type",
480 []byte{},
481 0,
482 0,
483 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.DateTime),
484 bsontype.EmbeddedDocument,
485 },
486 {
487 "length too short",
488 []byte{},
489 0,
490 0,
491 io.EOF,
492 bsontype.DateTime,
493 },
494 {
495 "success",
496 []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
497 0,
498 255,
499 nil,
500 bsontype.DateTime,
501 },
502 }
503
504 for _, tc := range testCases {
505 t.Run(tc.name, func(t *testing.T) {
506 vr := &valueReader{
507 offset: tc.offset,
508 d: tc.data,
509 stack: []vrState{
510 {mode: mTopLevel},
511 {
512 mode: mElement,
513 vType: tc.vType,
514 },
515 },
516 frame: 1,
517 }
518
519 dt, err := vr.ReadDateTime()
520 if !errequal(t, err, tc.err) {
521 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
522 }
523 if dt != tc.dt {
524 t.Errorf("Incorrect datetime returned. got %d; want %d", dt, tc.dt)
525 }
526 })
527 }
528 })
529 t.Run("ReadDecimal128", func(t *testing.T) {
530 testCases := []struct {
531 name string
532 data []byte
533 offset int64
534 dc128 primitive.Decimal128
535 err error
536 vType bsontype.Type
537 }{
538 {
539 "incorrect type",
540 []byte{},
541 0,
542 primitive.Decimal128{},
543 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Decimal128),
544 bsontype.EmbeddedDocument,
545 },
546 {
547 "length too short",
548 []byte{},
549 0,
550 primitive.Decimal128{},
551 io.EOF,
552 bsontype.Decimal128,
553 },
554 {
555 "success",
556 []byte{
557 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
558 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559 },
560 0,
561 primitive.NewDecimal128(65280, 255),
562 nil,
563 bsontype.Decimal128,
564 },
565 }
566
567 for _, tc := range testCases {
568 t.Run(tc.name, func(t *testing.T) {
569 vr := &valueReader{
570 offset: tc.offset,
571 d: tc.data,
572 stack: []vrState{
573 {mode: mTopLevel},
574 {
575 mode: mElement,
576 vType: tc.vType,
577 },
578 },
579 frame: 1,
580 }
581
582 dc128, err := vr.ReadDecimal128()
583 if !errequal(t, err, tc.err) {
584 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
585 }
586 gotHigh, gotLow := dc128.GetBytes()
587 wantHigh, wantLow := tc.dc128.GetBytes()
588 if gotHigh != wantHigh {
589 t.Errorf("Retuired high byte does not match. got %d; want %d", gotHigh, wantHigh)
590 }
591 if gotLow != wantLow {
592 t.Errorf("Returned low byte does not match. got %d; want %d", gotLow, wantLow)
593 }
594 })
595 }
596 })
597 t.Run("ReadDouble", func(t *testing.T) {
598 testCases := []struct {
599 name string
600 data []byte
601 offset int64
602 double float64
603 err error
604 vType bsontype.Type
605 }{
606 {
607 "incorrect type",
608 []byte{},
609 0,
610 0,
611 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Double),
612 bsontype.EmbeddedDocument,
613 },
614 {
615 "length too short",
616 []byte{},
617 0,
618 0,
619 io.EOF,
620 bsontype.Double,
621 },
622 {
623 "success",
624 []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
625 0,
626 math.Float64frombits(255),
627 nil,
628 bsontype.Double,
629 },
630 }
631
632 for _, tc := range testCases {
633 t.Run(tc.name, func(t *testing.T) {
634 vr := &valueReader{
635 offset: tc.offset,
636 d: tc.data,
637 stack: []vrState{
638 {mode: mTopLevel},
639 {
640 mode: mElement,
641 vType: tc.vType,
642 },
643 },
644 frame: 1,
645 }
646
647 double, err := vr.ReadDouble()
648 if !errequal(t, err, tc.err) {
649 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
650 }
651 if double != tc.double {
652 t.Errorf("Incorrect double returned. got %f; want %f", double, tc.double)
653 }
654 })
655 }
656 })
657 t.Run("ReadInt32", func(t *testing.T) {
658 testCases := []struct {
659 name string
660 data []byte
661 offset int64
662 i32 int32
663 err error
664 vType bsontype.Type
665 }{
666 {
667 "incorrect type",
668 []byte{},
669 0,
670 0,
671 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Int32),
672 bsontype.EmbeddedDocument,
673 },
674 {
675 "length too short",
676 []byte{},
677 0,
678 0,
679 io.EOF,
680 bsontype.Int32,
681 },
682 {
683 "success",
684 []byte{0xFF, 0x00, 0x00, 0x00},
685 0,
686 255,
687 nil,
688 bsontype.Int32,
689 },
690 }
691
692 for _, tc := range testCases {
693 t.Run(tc.name, func(t *testing.T) {
694 vr := &valueReader{
695 offset: tc.offset,
696 d: tc.data,
697 stack: []vrState{
698 {mode: mTopLevel},
699 {
700 mode: mElement,
701 vType: tc.vType,
702 },
703 },
704 frame: 1,
705 }
706
707 i32, err := vr.ReadInt32()
708 if !errequal(t, err, tc.err) {
709 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
710 }
711 if i32 != tc.i32 {
712 t.Errorf("Incorrect int32 returned. got %d; want %d", i32, tc.i32)
713 }
714 })
715 }
716 })
717 t.Run("ReadInt32", func(t *testing.T) {
718 testCases := []struct {
719 name string
720 data []byte
721 offset int64
722 i64 int64
723 err error
724 vType bsontype.Type
725 }{
726 {
727 "incorrect type",
728 []byte{},
729 0,
730 0,
731 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Int64),
732 bsontype.EmbeddedDocument,
733 },
734 {
735 "length too short",
736 []byte{},
737 0,
738 0,
739 io.EOF,
740 bsontype.Int64,
741 },
742 {
743 "success",
744 []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
745 0,
746 255,
747 nil,
748 bsontype.Int64,
749 },
750 }
751
752 for _, tc := range testCases {
753 t.Run(tc.name, func(t *testing.T) {
754 vr := &valueReader{
755 offset: tc.offset,
756 d: tc.data,
757 stack: []vrState{
758 {mode: mTopLevel},
759 {
760 mode: mElement,
761 vType: tc.vType,
762 },
763 },
764 frame: 1,
765 }
766
767 i64, err := vr.ReadInt64()
768 if !errequal(t, err, tc.err) {
769 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
770 }
771 if i64 != tc.i64 {
772 t.Errorf("Incorrect int64 returned. got %d; want %d", i64, tc.i64)
773 }
774 })
775 }
776 })
777 t.Run("ReadJavascript/ReadString/ReadSymbol", func(t *testing.T) {
778 testCases := []struct {
779 name string
780 data []byte
781 offset int64
782 fn func(*valueReader) (string, error)
783 css string
784 err error
785 vType bsontype.Type
786 }{
787 {
788 "ReadJavascript/incorrect type",
789 []byte{},
790 0,
791 (*valueReader).ReadJavascript,
792 "",
793 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.JavaScript),
794 bsontype.EmbeddedDocument,
795 },
796 {
797 "ReadString/incorrect type",
798 []byte{},
799 0,
800 (*valueReader).ReadString,
801 "",
802 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.String),
803 bsontype.EmbeddedDocument,
804 },
805 {
806 "ReadSymbol/incorrect type",
807 []byte{},
808 0,
809 (*valueReader).ReadSymbol,
810 "",
811 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Symbol),
812 bsontype.EmbeddedDocument,
813 },
814 {
815 "ReadJavascript/length too short",
816 []byte{},
817 0,
818 (*valueReader).ReadJavascript,
819 "",
820 io.EOF,
821 bsontype.JavaScript,
822 },
823 {
824 "ReadString/length too short",
825 []byte{},
826 0,
827 (*valueReader).ReadString,
828 "",
829 io.EOF,
830 bsontype.String,
831 },
832 {
833 "ReadSymbol/length too short",
834 []byte{},
835 0,
836 (*valueReader).ReadSymbol,
837 "",
838 io.EOF,
839 bsontype.Symbol,
840 },
841 {
842 "ReadJavascript/incorrect end byte",
843 []byte{0x01, 0x00, 0x00, 0x00, 0x05},
844 0,
845 (*valueReader).ReadJavascript,
846 "",
847 fmt.Errorf("string does not end with null byte, but with %v", 0x05),
848 bsontype.JavaScript,
849 },
850 {
851 "ReadString/incorrect end byte",
852 []byte{0x01, 0x00, 0x00, 0x00, 0x05},
853 0,
854 (*valueReader).ReadString,
855 "",
856 fmt.Errorf("string does not end with null byte, but with %v", 0x05),
857 bsontype.String,
858 },
859 {
860 "ReadSymbol/incorrect end byte",
861 []byte{0x01, 0x00, 0x00, 0x00, 0x05},
862 0,
863 (*valueReader).ReadSymbol,
864 "",
865 fmt.Errorf("string does not end with null byte, but with %v", 0x05),
866 bsontype.Symbol,
867 },
868 {
869 "ReadJavascript/success",
870 []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},
871 0,
872 (*valueReader).ReadJavascript,
873 "foo",
874 nil,
875 bsontype.JavaScript,
876 },
877 {
878 "ReadString/success",
879 []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},
880 0,
881 (*valueReader).ReadString,
882 "foo",
883 nil,
884 bsontype.String,
885 },
886 {
887 "ReadSymbol/success",
888 []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},
889 0,
890 (*valueReader).ReadSymbol,
891 "foo",
892 nil,
893 bsontype.Symbol,
894 },
895 }
896
897 for _, tc := range testCases {
898 t.Run(tc.name, func(t *testing.T) {
899 vr := &valueReader{
900 offset: tc.offset,
901 d: tc.data,
902 stack: []vrState{
903 {mode: mTopLevel},
904 {
905 mode: mElement,
906 vType: tc.vType,
907 },
908 },
909 frame: 1,
910 }
911
912 css, err := tc.fn(vr)
913 if !errequal(t, err, tc.err) {
914 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
915 }
916 if css != tc.css {
917 t.Errorf("Incorrect (JavaScript,String,Symbol) returned. got %s; want %s", css, tc.css)
918 }
919 })
920 }
921 })
922 t.Run("ReadMaxKey/ReadMinKey/ReadNull/ReadUndefined", func(t *testing.T) {
923 testCases := []struct {
924 name string
925 fn func(*valueReader) error
926 err error
927 vType bsontype.Type
928 }{
929 {
930 "ReadMaxKey/incorrect type",
931 (*valueReader).ReadMaxKey,
932 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.MaxKey),
933 bsontype.EmbeddedDocument,
934 },
935 {
936 "ReadMaxKey/success",
937 (*valueReader).ReadMaxKey,
938 nil,
939 bsontype.MaxKey,
940 },
941 {
942 "ReadMinKey/incorrect type",
943 (*valueReader).ReadMinKey,
944 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.MinKey),
945 bsontype.EmbeddedDocument,
946 },
947 {
948 "ReadMinKey/success",
949 (*valueReader).ReadMinKey,
950 nil,
951 bsontype.MinKey,
952 },
953 {
954 "ReadNull/incorrect type",
955 (*valueReader).ReadNull,
956 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Null),
957 bsontype.EmbeddedDocument,
958 },
959 {
960 "ReadNull/success",
961 (*valueReader).ReadNull,
962 nil,
963 bsontype.Null,
964 },
965 {
966 "ReadUndefined/incorrect type",
967 (*valueReader).ReadUndefined,
968 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Undefined),
969 bsontype.EmbeddedDocument,
970 },
971 {
972 "ReadUndefined/success",
973 (*valueReader).ReadUndefined,
974 nil,
975 bsontype.Undefined,
976 },
977 }
978
979 for _, tc := range testCases {
980 t.Run(tc.name, func(t *testing.T) {
981 vr := &valueReader{
982 stack: []vrState{
983 {mode: mTopLevel},
984 {
985 mode: mElement,
986 vType: tc.vType,
987 },
988 },
989 frame: 1,
990 }
991
992 err := tc.fn(vr)
993 if !errequal(t, err, tc.err) {
994 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
995 }
996 })
997 }
998 })
999 t.Run("ReadObjectID", func(t *testing.T) {
1000 testCases := []struct {
1001 name string
1002 data []byte
1003 offset int64
1004 oid primitive.ObjectID
1005 err error
1006 vType bsontype.Type
1007 }{
1008 {
1009 "incorrect type",
1010 []byte{},
1011 0,
1012 primitive.ObjectID{},
1013 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.ObjectID),
1014 bsontype.EmbeddedDocument,
1015 },
1016 {
1017 "not enough bytes for objectID",
1018 []byte{},
1019 0,
1020 primitive.ObjectID{},
1021 io.EOF,
1022 bsontype.ObjectID,
1023 },
1024 {
1025 "success",
1026 []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
1027 0,
1028 primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
1029 nil,
1030 bsontype.ObjectID,
1031 },
1032 }
1033
1034 for _, tc := range testCases {
1035 t.Run(tc.name, func(t *testing.T) {
1036 vr := &valueReader{
1037 offset: tc.offset,
1038 d: tc.data,
1039 stack: []vrState{
1040 {mode: mTopLevel},
1041 {
1042 mode: mElement,
1043 vType: tc.vType,
1044 },
1045 },
1046 frame: 1,
1047 }
1048
1049 oid, err := vr.ReadObjectID()
1050 if !errequal(t, err, tc.err) {
1051 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
1052 }
1053 if oid != tc.oid {
1054 t.Errorf("ObjectIDs did not match. got %v; want %v", oid, tc.oid)
1055 }
1056 })
1057 }
1058 })
1059 t.Run("ReadRegex", func(t *testing.T) {
1060 testCases := []struct {
1061 name string
1062 data []byte
1063 offset int64
1064 pattern string
1065 options string
1066 err error
1067 vType bsontype.Type
1068 }{
1069 {
1070 "incorrect type",
1071 []byte{},
1072 0,
1073 "",
1074 "",
1075 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Regex),
1076 bsontype.EmbeddedDocument,
1077 },
1078 {
1079 "length too short",
1080 []byte{},
1081 0,
1082 "",
1083 "",
1084 io.EOF,
1085 bsontype.Regex,
1086 },
1087 {
1088 "not enough bytes for options",
1089 []byte{'f', 'o', 'o', 0x00},
1090 0,
1091 "",
1092 "",
1093 io.EOF,
1094 bsontype.Regex,
1095 },
1096 {
1097 "success",
1098 []byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r', 0x00},
1099 0,
1100 "foo",
1101 "bar",
1102 nil,
1103 bsontype.Regex,
1104 },
1105 }
1106
1107 for _, tc := range testCases {
1108 t.Run(tc.name, func(t *testing.T) {
1109 vr := &valueReader{
1110 offset: tc.offset,
1111 d: tc.data,
1112 stack: []vrState{
1113 {mode: mTopLevel},
1114 {
1115 mode: mElement,
1116 vType: tc.vType,
1117 },
1118 },
1119 frame: 1,
1120 }
1121
1122 pattern, options, err := vr.ReadRegex()
1123 if !errequal(t, err, tc.err) {
1124 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
1125 }
1126 if pattern != tc.pattern {
1127 t.Errorf("Incorrect pattern returned. got %s; want %s", pattern, tc.pattern)
1128 }
1129 if options != tc.options {
1130 t.Errorf("Incorrect options returned. got %s; want %s", options, tc.options)
1131 }
1132 })
1133 }
1134 })
1135 t.Run("ReadTimestamp", func(t *testing.T) {
1136 testCases := []struct {
1137 name string
1138 data []byte
1139 offset int64
1140 ts uint32
1141 incr uint32
1142 err error
1143 vType bsontype.Type
1144 }{
1145 {
1146 "incorrect type",
1147 []byte{},
1148 0,
1149 0,
1150 0,
1151 (&valueReader{stack: []vrState{{vType: bsontype.EmbeddedDocument}}, frame: 0}).typeError(bsontype.Timestamp),
1152 bsontype.EmbeddedDocument,
1153 },
1154 {
1155 "not enough bytes for increment",
1156 []byte{},
1157 0,
1158 0,
1159 0,
1160 io.EOF,
1161 bsontype.Timestamp,
1162 },
1163 {
1164 "not enough bytes for timestamp",
1165 []byte{0x01, 0x02, 0x03, 0x04},
1166 0,
1167 0,
1168 0,
1169 io.EOF,
1170 bsontype.Timestamp,
1171 },
1172 {
1173 "success",
1174 []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00},
1175 0,
1176 256,
1177 255,
1178 nil,
1179 bsontype.Timestamp,
1180 },
1181 }
1182
1183 for _, tc := range testCases {
1184 t.Run(tc.name, func(t *testing.T) {
1185 vr := &valueReader{
1186 offset: tc.offset,
1187 d: tc.data,
1188 stack: []vrState{
1189 {mode: mTopLevel},
1190 {
1191 mode: mElement,
1192 vType: tc.vType,
1193 },
1194 },
1195 frame: 1,
1196 }
1197
1198 ts, incr, err := vr.ReadTimestamp()
1199 if !errequal(t, err, tc.err) {
1200 t.Errorf("Returned errors do not match. got %v; want %v", err, tc.err)
1201 }
1202 if ts != tc.ts {
1203 t.Errorf("Incorrect timestamp returned. got %d; want %d", ts, tc.ts)
1204 }
1205 if incr != tc.incr {
1206 t.Errorf("Incorrect increment returned. got %d; want %d", incr, tc.incr)
1207 }
1208 })
1209 }
1210 })
1211
1212 t.Run("ReadBytes & Skip", func(t *testing.T) {
1213 index, docb := bsoncore.ReserveLength(nil)
1214 docb = bsoncore.AppendNullElement(docb, "foobar")
1215 docb = append(docb, 0x00)
1216 docb = bsoncore.UpdateLength(docb, index, int32(len(docb)))
1217 cwsbytes := bsoncore.AppendCodeWithScope(nil, "var hellow = world;", docb)
1218 strbytes := []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}
1219 testCases := []struct {
1220 name string
1221 t bsontype.Type
1222 data []byte
1223 err error
1224 offset int64
1225 startingOffset int64
1226 }{
1227 {
1228 "Array/invalid length",
1229 bsontype.Array,
1230 []byte{0x01, 0x02, 0x03},
1231 io.EOF, 0, 0,
1232 },
1233 {
1234 "Array/not enough bytes",
1235 bsontype.Array,
1236 []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
1237 io.EOF, 0, 0,
1238 },
1239 {
1240 "Array/success",
1241 bsontype.Array,
1242 []byte{0x08, 0x00, 0x00, 0x00, 0x0A, '1', 0x00, 0x00},
1243 nil, 8, 0,
1244 },
1245 {
1246 "EmbeddedDocument/invalid length",
1247 bsontype.EmbeddedDocument,
1248 []byte{0x01, 0x02, 0x03},
1249 io.EOF, 0, 0,
1250 },
1251 {
1252 "EmbeddedDocument/not enough bytes",
1253 bsontype.EmbeddedDocument,
1254 []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
1255 io.EOF, 0, 0,
1256 },
1257 {
1258 "EmbeddedDocument/success",
1259 bsontype.EmbeddedDocument,
1260 []byte{0x08, 0x00, 0x00, 0x00, 0x0A, 'A', 0x00, 0x00},
1261 nil, 8, 0,
1262 },
1263 {
1264 "CodeWithScope/invalid length",
1265 bsontype.CodeWithScope,
1266 []byte{0x01, 0x02, 0x03},
1267 io.EOF, 0, 0,
1268 },
1269 {
1270 "CodeWithScope/not enough bytes",
1271 bsontype.CodeWithScope,
1272 []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
1273 io.EOF, 0, 0,
1274 },
1275 {
1276 "CodeWithScope/success",
1277 bsontype.CodeWithScope,
1278 cwsbytes,
1279 nil, 41, 0,
1280 },
1281 {
1282 "Binary/invalid length",
1283 bsontype.Binary,
1284 []byte{0x01, 0x02, 0x03},
1285 io.EOF, 0, 0,
1286 },
1287 {
1288 "Binary/not enough bytes",
1289 bsontype.Binary,
1290 []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
1291 io.EOF, 0, 0,
1292 },
1293 {
1294 "Binary/success",
1295 bsontype.Binary,
1296 []byte{0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
1297 nil, 8, 0,
1298 },
1299 {
1300 "Boolean/invalid length",
1301 bsontype.Boolean,
1302 []byte{},
1303 io.EOF, 0, 0,
1304 },
1305 {
1306 "Boolean/success",
1307 bsontype.Boolean,
1308 []byte{0x01},
1309 nil, 1, 0,
1310 },
1311 {
1312 "DBPointer/invalid length",
1313 bsontype.DBPointer,
1314 []byte{0x01, 0x02, 0x03},
1315 io.EOF, 0, 0,
1316 },
1317 {
1318 "DBPointer/not enough bytes",
1319 bsontype.DBPointer,
1320 []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
1321 io.EOF, 0, 0,
1322 },
1323 {
1324 "DBPointer/success",
1325 bsontype.DBPointer,
1326 []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
1327 nil, 17, 0,
1328 },
1329 {"DBPointer/not enough bytes", bsontype.DateTime, []byte{0x01, 0x02, 0x03, 0x04}, io.EOF, 0, 0},
1330 {"DBPointer/success", bsontype.DateTime, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, nil, 8, 0},
1331 {"Double/not enough bytes", bsontype.Double, []byte{0x01, 0x02, 0x03, 0x04}, io.EOF, 0, 0},
1332 {"Double/success", bsontype.Double, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, nil, 8, 0},
1333 {"Int64/not enough bytes", bsontype.Int64, []byte{0x01, 0x02, 0x03, 0x04}, io.EOF, 0, 0},
1334 {"Int64/success", bsontype.Int64, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, nil, 8, 0},
1335 {"Timestamp/not enough bytes", bsontype.Timestamp, []byte{0x01, 0x02, 0x03, 0x04}, io.EOF, 0, 0},
1336 {"Timestamp/success", bsontype.Timestamp, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, nil, 8, 0},
1337 {
1338 "Decimal128/not enough bytes",
1339 bsontype.Decimal128,
1340 []byte{0x01, 0x02, 0x03, 0x04},
1341 io.EOF, 0, 0,
1342 },
1343 {
1344 "Decimal128/success",
1345 bsontype.Decimal128,
1346 []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10},
1347 nil, 16, 0,
1348 },
1349 {"Int32/not enough bytes", bsontype.Int32, []byte{0x01, 0x02}, io.EOF, 0, 0},
1350 {"Int32/success", bsontype.Int32, []byte{0x01, 0x02, 0x03, 0x04}, nil, 4, 0},
1351 {"Javascript/invalid length", bsontype.JavaScript, strbytes[:2], io.EOF, 0, 0},
1352 {"Javascript/not enough bytes", bsontype.JavaScript, strbytes[:5], io.EOF, 0, 0},
1353 {"Javascript/success", bsontype.JavaScript, strbytes, nil, 8, 0},
1354 {"String/invalid length", bsontype.String, strbytes[:2], io.EOF, 0, 0},
1355 {"String/not enough bytes", bsontype.String, strbytes[:5], io.EOF, 0, 0},
1356 {"String/success", bsontype.String, strbytes, nil, 8, 0},
1357 {"Symbol/invalid length", bsontype.Symbol, strbytes[:2], io.EOF, 0, 0},
1358 {"Symbol/not enough bytes", bsontype.Symbol, strbytes[:5], io.EOF, 0, 0},
1359 {"Symbol/success", bsontype.Symbol, strbytes, nil, 8, 0},
1360 {"MaxKey/success", bsontype.MaxKey, []byte{}, nil, 0, 0},
1361 {"MinKey/success", bsontype.MinKey, []byte{}, nil, 0, 0},
1362 {"Null/success", bsontype.Null, []byte{}, nil, 0, 0},
1363 {"Undefined/success", bsontype.Undefined, []byte{}, nil, 0, 0},
1364 {
1365 "ObjectID/not enough bytes",
1366 bsontype.ObjectID,
1367 []byte{0x01, 0x02, 0x03, 0x04},
1368 io.EOF, 0, 0,
1369 },
1370 {
1371 "ObjectID/success",
1372 bsontype.ObjectID,
1373 []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
1374 nil, 12, 0,
1375 },
1376 {
1377 "Regex/not enough bytes (first string)",
1378 bsontype.Regex,
1379 []byte{'f', 'o', 'o'},
1380 io.EOF, 0, 0,
1381 },
1382 {
1383 "Regex/not enough bytes (second string)",
1384 bsontype.Regex,
1385 []byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r'},
1386 io.EOF, 0, 0,
1387 },
1388 {
1389 "Regex/success",
1390 bsontype.Regex,
1391 []byte{0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00, 'i', 0x00},
1392 nil, 9, 3,
1393 },
1394 {
1395 "Unknown Type",
1396 bsontype.Type(0),
1397 nil,
1398 fmt.Errorf("attempted to read bytes of unknown BSON type %v", bsontype.Type(0)), 0, 0,
1399 },
1400 }
1401
1402 for _, tc := range testCases {
1403 t.Run(tc.name, func(t *testing.T) {
1404 t.Run("Skip", func(t *testing.T) {
1405 vr := &valueReader{
1406 d: tc.data,
1407 stack: []vrState{
1408 {mode: mTopLevel},
1409 {mode: mElement, vType: tc.t},
1410 },
1411 frame: 1,
1412 offset: tc.startingOffset,
1413 }
1414
1415 err := vr.Skip()
1416 if !errequal(t, err, tc.err) {
1417 t.Errorf("Did not receive expected error; got %v; want %v", err, tc.err)
1418 }
1419 if tc.err == nil && vr.offset != tc.offset {
1420 t.Errorf("Offset not set at correct position; got %d; want %d", vr.offset, tc.offset)
1421 }
1422 })
1423 t.Run("ReadBytes", func(t *testing.T) {
1424 vr := &valueReader{
1425 d: tc.data,
1426 stack: []vrState{
1427 {mode: mTopLevel},
1428 {mode: mElement, vType: tc.t},
1429 },
1430 frame: 1,
1431 offset: tc.startingOffset,
1432 }
1433
1434 _, got, err := vr.ReadValueBytes(nil)
1435 if !errequal(t, err, tc.err) {
1436 t.Errorf("Did not receive expected error; got %v; want %v", err, tc.err)
1437 }
1438 if tc.err == nil && vr.offset != tc.offset {
1439 t.Errorf("Offset not set at correct position; got %d; want %d", vr.offset, tc.offset)
1440 }
1441 if tc.err == nil && !bytes.Equal(got, tc.data[tc.startingOffset:]) {
1442 t.Errorf("Did not receive expected bytes. got %v; want %v", got, tc.data[tc.startingOffset:])
1443 }
1444 })
1445 })
1446 }
1447 t.Run("ReadValueBytes/Top Level Doc", func(t *testing.T) {
1448 testCases := []struct {
1449 name string
1450 want []byte
1451 wantType bsontype.Type
1452 wantErr error
1453 }{
1454 {
1455 "success",
1456 bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)),
1457 bsontype.Type(0),
1458 nil,
1459 },
1460 {
1461 "wrong length",
1462 []byte{0x01, 0x02, 0x03},
1463 bsontype.Type(0),
1464 io.EOF,
1465 },
1466 {
1467 "append bytes",
1468 []byte{0x01, 0x02, 0x03, 0x04},
1469 bsontype.Type(0),
1470 io.EOF,
1471 },
1472 }
1473
1474 for _, tc := range testCases {
1475 tc := tc
1476 t.Run(tc.name, func(t *testing.T) {
1477 t.Parallel()
1478 vr := &valueReader{
1479 d: tc.want,
1480 stack: []vrState{
1481 {mode: mTopLevel},
1482 },
1483 frame: 0,
1484 }
1485 gotType, got, gotErr := vr.ReadValueBytes(nil)
1486 if !errors.Is(gotErr, tc.wantErr) {
1487 t.Errorf("Did not receive expected error. got %v; want %v", gotErr, tc.wantErr)
1488 }
1489 if tc.wantErr == nil && gotType != tc.wantType {
1490 t.Errorf("Did not receive expected type. got %v; want %v", gotType, tc.wantType)
1491 }
1492 if tc.wantErr == nil && !bytes.Equal(got, tc.want) {
1493 t.Errorf("Did not receive expected bytes. got %v; want %v", got, tc.want)
1494 }
1495 })
1496 }
1497 })
1498 })
1499
1500 t.Run("invalid transition", func(t *testing.T) {
1501 t.Run("Skip", func(t *testing.T) {
1502 vr := &valueReader{stack: []vrState{{mode: mTopLevel}}}
1503 wanterr := (&valueReader{stack: []vrState{{mode: mTopLevel}}}).invalidTransitionErr(0, "Skip", []mode{mElement, mValue})
1504 goterr := vr.Skip()
1505 if !cmp.Equal(goterr, wanterr, cmp.Comparer(compareErrors)) {
1506 t.Errorf("Expected correct invalid transition error. got %v; want %v", goterr, wanterr)
1507 }
1508 })
1509 })
1510 t.Run("ReadBytes", func(t *testing.T) {
1511 vr := &valueReader{stack: []vrState{{mode: mTopLevel}, {mode: mDocument}}, frame: 1}
1512 wanterr := (&valueReader{stack: []vrState{{mode: mTopLevel}, {mode: mDocument}}, frame: 1}).
1513 invalidTransitionErr(0, "ReadValueBytes", []mode{mElement, mValue})
1514 _, _, goterr := vr.ReadValueBytes(nil)
1515 if !cmp.Equal(goterr, wanterr, cmp.Comparer(compareErrors)) {
1516 t.Errorf("Expected correct invalid transition error. got %v; want %v", goterr, wanterr)
1517 }
1518 })
1519 }
1520
1521 func errequal(t *testing.T, err1, err2 error) bool {
1522 t.Helper()
1523 if err1 == nil && err2 == nil {
1524 return true
1525 }
1526 if err1 == nil || err2 == nil {
1527 return false
1528 }
1529
1530 if errors.Is(err1, err2) {
1531 return true
1532 }
1533
1534 if err1.Error() == err2.Error() {
1535 return true
1536 }
1537
1538 return false
1539 }
1540
View as plain text