1
2
3
4
5
6
7 package bson
8
9 import (
10 "bytes"
11 "errors"
12 "reflect"
13 "testing"
14 "time"
15
16 "github.com/google/go-cmp/cmp"
17 "go.mongodb.org/mongo-driver/bson/bsoncodec"
18 "go.mongodb.org/mongo-driver/bson/bsonrw"
19 "go.mongodb.org/mongo-driver/bson/bsonrw/bsonrwtest"
20 "go.mongodb.org/mongo-driver/bson/bsontype"
21 "go.mongodb.org/mongo-driver/internal/assert"
22 "go.mongodb.org/mongo-driver/internal/require"
23 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
24 )
25
26 func TestBasicDecode(t *testing.T) {
27 t.Parallel()
28
29 for _, tc := range unmarshalingTestCases() {
30 tc := tc
31
32 t.Run(tc.name, func(t *testing.T) {
33 t.Parallel()
34
35 got := reflect.New(tc.sType).Elem()
36 vr := bsonrw.NewBSONDocumentReader(tc.data)
37 reg := DefaultRegistry
38 decoder, err := reg.LookupDecoder(reflect.TypeOf(got))
39 noerr(t, err)
40 err = decoder.DecodeValue(bsoncodec.DecodeContext{Registry: reg}, vr, got)
41 noerr(t, err)
42 assert.Equal(t, tc.want, got.Addr().Interface(), "Results do not match.")
43 })
44 }
45 }
46
47 func TestDecoderv2(t *testing.T) {
48 t.Parallel()
49
50 t.Run("Decode", func(t *testing.T) {
51 t.Parallel()
52
53 for _, tc := range unmarshalingTestCases() {
54 tc := tc
55
56 t.Run(tc.name, func(t *testing.T) {
57 t.Parallel()
58
59 got := reflect.New(tc.sType).Interface()
60 vr := bsonrw.NewBSONDocumentReader(tc.data)
61 dec, err := NewDecoderWithContext(bsoncodec.DecodeContext{Registry: DefaultRegistry}, vr)
62 noerr(t, err)
63 err = dec.Decode(got)
64 noerr(t, err)
65 assert.Equal(t, tc.want, got, "Results do not match.")
66 })
67 }
68 t.Run("lookup error", func(t *testing.T) {
69 t.Parallel()
70
71 type certainlydoesntexistelsewhereihope func(string, string) string
72
73 _ = certainlydoesntexistelsewhereihope(func(string, string) string { return "" })
74
75 cdeih := func(string, string) string { return "certainlydoesntexistelsewhereihope" }
76 dec, err := NewDecoder(bsonrw.NewBSONDocumentReader([]byte{}))
77 noerr(t, err)
78 want := bsoncodec.ErrNoDecoder{Type: reflect.TypeOf(cdeih)}
79 got := dec.Decode(&cdeih)
80 assert.Equal(t, want, got, "Received unexpected error.")
81 })
82 t.Run("Unmarshaler", func(t *testing.T) {
83 t.Parallel()
84
85 testCases := []struct {
86 name string
87 err error
88 vr bsonrw.ValueReader
89 invoked bool
90 }{
91 {
92 "error",
93 errors.New("Unmarshaler error"),
94 &bsonrwtest.ValueReaderWriter{BSONType: bsontype.EmbeddedDocument, Err: bsonrw.ErrEOD, ErrAfter: bsonrwtest.ReadElement},
95 true,
96 },
97 {
98 "copy error",
99 errors.New("copy error"),
100 &bsonrwtest.ValueReaderWriter{Err: errors.New("copy error"), ErrAfter: bsonrwtest.ReadDocument},
101 false,
102 },
103 {
104 "success",
105 nil,
106 &bsonrwtest.ValueReaderWriter{BSONType: bsontype.EmbeddedDocument, Err: bsonrw.ErrEOD, ErrAfter: bsonrwtest.ReadElement},
107 true,
108 },
109 }
110
111 for _, tc := range testCases {
112 tc := tc
113
114 t.Run(tc.name, func(t *testing.T) {
115 t.Parallel()
116
117 unmarshaler := &testUnmarshaler{err: tc.err}
118 dec, err := NewDecoder(tc.vr)
119 noerr(t, err)
120 got := dec.Decode(unmarshaler)
121 want := tc.err
122 if !compareErrors(got, want) {
123 t.Errorf("Did not receive expected error. got %v; want %v", got, want)
124 }
125 if unmarshaler.invoked != tc.invoked {
126 if tc.invoked {
127 t.Error("Expected to have UnmarshalBSON invoked, but it wasn't.")
128 } else {
129 t.Error("Expected UnmarshalBSON to not be invoked, but it was.")
130 }
131 }
132 })
133 }
134
135 t.Run("Unmarshaler/success bsonrw.ValueReader", func(t *testing.T) {
136 t.Parallel()
137
138 want := bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159))
139 unmarshaler := &testUnmarshaler{}
140 vr := bsonrw.NewBSONDocumentReader(want)
141 dec, err := NewDecoder(vr)
142 noerr(t, err)
143 err = dec.Decode(unmarshaler)
144 noerr(t, err)
145 got := unmarshaler.data
146 if !bytes.Equal(got, want) {
147 t.Errorf("Did not unmarshal properly. got %v; want %v", got, want)
148 }
149 })
150 })
151 })
152 t.Run("NewDecoder", func(t *testing.T) {
153 t.Parallel()
154
155 t.Run("error", func(t *testing.T) {
156 t.Parallel()
157
158 _, got := NewDecoder(nil)
159 want := errors.New("cannot create a new Decoder with a nil ValueReader")
160 if !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {
161 t.Errorf("Was expecting error but got different error. got %v; want %v", got, want)
162 }
163 })
164 t.Run("success", func(t *testing.T) {
165 t.Parallel()
166
167 got, err := NewDecoder(bsonrw.NewBSONDocumentReader([]byte{}))
168 noerr(t, err)
169 if got == nil {
170 t.Errorf("Was expecting a non-nil Decoder, but got <nil>")
171 }
172 })
173 })
174 t.Run("NewDecoderWithContext", func(t *testing.T) {
175 t.Parallel()
176
177 t.Run("errors", func(t *testing.T) {
178 t.Parallel()
179
180 dc := bsoncodec.DecodeContext{Registry: DefaultRegistry}
181 _, got := NewDecoderWithContext(dc, nil)
182 want := errors.New("cannot create a new Decoder with a nil ValueReader")
183 if !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {
184 t.Errorf("Was expecting error but got different error. got %v; want %v", got, want)
185 }
186 })
187 t.Run("success", func(t *testing.T) {
188 t.Parallel()
189
190 got, err := NewDecoderWithContext(bsoncodec.DecodeContext{}, bsonrw.NewBSONDocumentReader([]byte{}))
191 noerr(t, err)
192 if got == nil {
193 t.Errorf("Was expecting a non-nil Decoder, but got <nil>")
194 }
195 dc := bsoncodec.DecodeContext{Registry: DefaultRegistry}
196 got, err = NewDecoderWithContext(dc, bsonrw.NewBSONDocumentReader([]byte{}))
197 noerr(t, err)
198 if got == nil {
199 t.Errorf("Was expecting a non-nil Decoder, but got <nil>")
200 }
201 })
202 })
203 t.Run("Decode doesn't zero struct", func(t *testing.T) {
204 t.Parallel()
205
206 type foo struct {
207 Item string
208 Qty int
209 Bonus int
210 }
211 var got foo
212 got.Item = "apple"
213 got.Bonus = 2
214 data := docToBytes(D{{"item", "canvas"}, {"qty", 4}})
215 vr := bsonrw.NewBSONDocumentReader(data)
216 dec, err := NewDecoder(vr)
217 noerr(t, err)
218 err = dec.Decode(&got)
219 noerr(t, err)
220 want := foo{Item: "canvas", Qty: 4, Bonus: 2}
221 assert.Equal(t, want, got, "Results do not match.")
222 })
223 t.Run("Reset", func(t *testing.T) {
224 t.Parallel()
225
226 vr1, vr2 := bsonrw.NewBSONDocumentReader([]byte{}), bsonrw.NewBSONDocumentReader([]byte{})
227 dc := bsoncodec.DecodeContext{Registry: DefaultRegistry}
228 dec, err := NewDecoderWithContext(dc, vr1)
229 noerr(t, err)
230 if dec.vr != vr1 {
231 t.Errorf("Decoder should use the value reader provided. got %v; want %v", dec.vr, vr1)
232 }
233 err = dec.Reset(vr2)
234 noerr(t, err)
235 if dec.vr != vr2 {
236 t.Errorf("Decoder should use the value reader provided. got %v; want %v", dec.vr, vr2)
237 }
238 })
239 t.Run("SetContext", func(t *testing.T) {
240 t.Parallel()
241
242 dc1 := bsoncodec.DecodeContext{Registry: DefaultRegistry}
243 dc2 := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()}
244 dec, err := NewDecoderWithContext(dc1, bsonrw.NewBSONDocumentReader([]byte{}))
245 noerr(t, err)
246 if !reflect.DeepEqual(dec.dc, dc1) {
247 t.Errorf("Decoder should use the Registry provided. got %v; want %v", dec.dc, dc1)
248 }
249 err = dec.SetContext(dc2)
250 noerr(t, err)
251 if !reflect.DeepEqual(dec.dc, dc2) {
252 t.Errorf("Decoder should use the Registry provided. got %v; want %v", dec.dc, dc2)
253 }
254 })
255 t.Run("SetRegistry", func(t *testing.T) {
256 t.Parallel()
257
258 r1, r2 := DefaultRegistry, NewRegistryBuilder().Build()
259 dc1 := bsoncodec.DecodeContext{Registry: r1}
260 dc2 := bsoncodec.DecodeContext{Registry: r2}
261 dec, err := NewDecoder(bsonrw.NewBSONDocumentReader([]byte{}))
262 noerr(t, err)
263 if !reflect.DeepEqual(dec.dc, dc1) {
264 t.Errorf("Decoder should use the Registry provided. got %v; want %v", dec.dc, dc1)
265 }
266 err = dec.SetRegistry(r2)
267 noerr(t, err)
268 if !reflect.DeepEqual(dec.dc, dc2) {
269 t.Errorf("Decoder should use the Registry provided. got %v; want %v", dec.dc, dc2)
270 }
271 })
272 t.Run("DecodeToNil", func(t *testing.T) {
273 t.Parallel()
274
275 data := docToBytes(D{{"item", "canvas"}, {"qty", 4}})
276 vr := bsonrw.NewBSONDocumentReader(data)
277 dec, err := NewDecoder(vr)
278 noerr(t, err)
279
280 var got *D
281 err = dec.Decode(got)
282 if !errors.Is(err, ErrDecodeToNil) {
283 t.Fatalf("Decode error mismatch; expected %v, got %v", ErrDecodeToNil, err)
284 }
285 })
286 }
287
288 type testUnmarshaler struct {
289 invoked bool
290 err error
291 data []byte
292 }
293
294 func (tu *testUnmarshaler) UnmarshalBSON(d []byte) error {
295 tu.invoked = true
296 tu.data = d
297 return tu.err
298 }
299
300 func TestDecoderConfiguration(t *testing.T) {
301 type truncateDoublesTest struct {
302 MyInt int
303 MyInt8 int8
304 MyInt16 int16
305 MyInt32 int32
306 MyInt64 int64
307 MyUint uint
308 MyUint8 uint8
309 MyUint16 uint16
310 MyUint32 uint32
311 MyUint64 uint64
312 }
313
314 type jsonStructTest struct {
315 StructFieldName string `json:"jsonFieldName"`
316 }
317
318 type localTimeZoneTest struct {
319 MyTime time.Time
320 }
321
322 type zeroMapsTest struct {
323 MyMap map[string]string
324 }
325
326 type zeroStructsTest struct {
327 MyString string
328 MyInt int
329 }
330
331 testCases := []struct {
332 description string
333 configure func(*Decoder)
334 input []byte
335 decodeInto func() interface{}
336 want interface{}
337 }{
338
339
340 {
341 description: "AllowTruncatingDoubles",
342 configure: func(dec *Decoder) {
343 dec.AllowTruncatingDoubles()
344 },
345 input: bsoncore.NewDocumentBuilder().
346 AppendDouble("myInt", 1.999).
347 AppendDouble("myInt8", 1.999).
348 AppendDouble("myInt16", 1.999).
349 AppendDouble("myInt32", 1.999).
350 AppendDouble("myInt64", 1.999).
351 AppendDouble("myUint", 1.999).
352 AppendDouble("myUint8", 1.999).
353 AppendDouble("myUint16", 1.999).
354 AppendDouble("myUint32", 1.999).
355 AppendDouble("myUint64", 1.999).
356 Build(),
357 decodeInto: func() interface{} { return &truncateDoublesTest{} },
358 want: &truncateDoublesTest{
359 MyInt: 1,
360 MyInt8: 1,
361 MyInt16: 1,
362 MyInt32: 1,
363 MyInt64: 1,
364 MyUint: 1,
365 MyUint8: 1,
366 MyUint16: 1,
367 MyUint32: 1,
368 MyUint64: 1,
369 },
370 },
371
372
373 {
374 description: "BinaryAsSlice",
375 configure: func(dec *Decoder) {
376 dec.BinaryAsSlice()
377 },
378 input: bsoncore.NewDocumentBuilder().
379 AppendBinary("myBinary", bsontype.BinaryGeneric, []byte{}).
380 Build(),
381 decodeInto: func() interface{} { return &D{} },
382 want: &D{{Key: "myBinary", Value: []byte{}}},
383 },
384
385
386 {
387 description: "DefaultDocumentD nested",
388 configure: func(dec *Decoder) {
389 dec.DefaultDocumentD()
390 },
391 input: bsoncore.NewDocumentBuilder().
392 AppendDocument("myDocument", bsoncore.NewDocumentBuilder().
393 AppendString("myString", "test value").
394 Build()).
395 Build(),
396 decodeInto: func() interface{} { return M{} },
397 want: M{
398 "myDocument": D{{Key: "myString", Value: "test value"}},
399 },
400 },
401
402
403 {
404 description: "DefaultDocumentM nested",
405 configure: func(dec *Decoder) {
406 dec.DefaultDocumentM()
407 },
408 input: bsoncore.NewDocumentBuilder().
409 AppendDocument("myDocument", bsoncore.NewDocumentBuilder().
410 AppendString("myString", "test value").
411 Build()).
412 Build(),
413 decodeInto: func() interface{} { return &D{} },
414 want: &D{
415 {Key: "myDocument", Value: M{"myString": "test value"}},
416 },
417 },
418
419
420 {
421 description: "UseJSONStructTags",
422 configure: func(dec *Decoder) {
423 dec.UseJSONStructTags()
424 },
425 input: bsoncore.NewDocumentBuilder().
426 AppendString("jsonFieldName", "test value").
427 Build(),
428 decodeInto: func() interface{} { return &jsonStructTest{} },
429 want: &jsonStructTest{StructFieldName: "test value"},
430 },
431
432
433 {
434 description: "UseLocalTimeZone",
435 configure: func(dec *Decoder) {
436 dec.UseLocalTimeZone()
437 },
438 input: bsoncore.NewDocumentBuilder().
439 AppendDateTime("myTime", 1684349179939).
440 Build(),
441 decodeInto: func() interface{} { return &localTimeZoneTest{} },
442 want: &localTimeZoneTest{MyTime: time.UnixMilli(1684349179939)},
443 },
444
445
446 {
447 description: "ZeroMaps",
448 configure: func(dec *Decoder) {
449 dec.ZeroMaps()
450 },
451 input: bsoncore.NewDocumentBuilder().
452 AppendDocument("myMap", bsoncore.NewDocumentBuilder().
453 AppendString("myString", "test value").
454 Build()).
455 Build(),
456 decodeInto: func() interface{} {
457 return &zeroMapsTest{MyMap: map[string]string{"myExtraValue": "extra value"}}
458 },
459 want: &zeroMapsTest{MyMap: map[string]string{"myString": "test value"}},
460 },
461
462
463 {
464 description: "ZeroStructs",
465 configure: func(dec *Decoder) {
466 dec.ZeroStructs()
467 },
468 input: bsoncore.NewDocumentBuilder().
469 AppendString("myString", "test value").
470 Build(),
471 decodeInto: func() interface{} {
472 return &zeroStructsTest{MyInt: 1}
473 },
474 want: &zeroStructsTest{MyString: "test value"},
475 },
476 }
477
478 for _, tc := range testCases {
479 tc := tc
480
481 t.Run(tc.description, func(t *testing.T) {
482 t.Parallel()
483
484 dec, err := NewDecoder(bsonrw.NewBSONDocumentReader(tc.input))
485 require.NoError(t, err, "NewDecoder error")
486
487 tc.configure(dec)
488
489 got := tc.decodeInto()
490 err = dec.Decode(got)
491 require.NoError(t, err, "Decode error")
492
493 assert.Equal(t, tc.want, got, "expected and actual decode results do not match")
494 })
495 }
496
497 t.Run("DefaultDocumentM top-level", func(t *testing.T) {
498 t.Parallel()
499
500 input := bsoncore.NewDocumentBuilder().
501 AppendDocument("myDocument", bsoncore.NewDocumentBuilder().
502 AppendString("myString", "test value").
503 Build()).
504 Build()
505
506 dec, err := NewDecoder(bsonrw.NewBSONDocumentReader(input))
507 require.NoError(t, err, "NewDecoder error")
508
509 dec.DefaultDocumentM()
510
511 var got interface{}
512 err = dec.Decode(&got)
513 require.NoError(t, err, "Decode error")
514
515 want := M{
516 "myDocument": M{
517 "myString": "test value",
518 },
519 }
520 assert.Equal(t, want, got, "expected and actual decode results do not match")
521 })
522 t.Run("DefaultDocumentD top-level", func(t *testing.T) {
523 t.Parallel()
524
525 input := bsoncore.NewDocumentBuilder().
526 AppendDocument("myDocument", bsoncore.NewDocumentBuilder().
527 AppendString("myString", "test value").
528 Build()).
529 Build()
530
531 dec, err := NewDecoder(bsonrw.NewBSONDocumentReader(input))
532 require.NoError(t, err, "NewDecoder error")
533
534 dec.DefaultDocumentD()
535
536 var got interface{}
537 err = dec.Decode(&got)
538 require.NoError(t, err, "Decode error")
539
540 want := D{
541 {Key: "myDocument", Value: D{
542 {Key: "myString", Value: "test value"},
543 }},
544 }
545 assert.Equal(t, want, got, "expected and actual decode results do not match")
546 })
547 }
548
View as plain text