...

Source file src/go.mongodb.org/mongo-driver/bson/decoder_test.go

Documentation: go.mongodb.org/mongo-driver/bson

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     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  			// Avoid unused code lint error.
    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  		// Test that AllowTruncatingDoubles causes the Decoder to unmarshal BSON doubles with
   339  		// fractional parts into Go integer types by truncating the fractional part.
   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  		// Test that BinaryAsSlice causes the Decoder to unmarshal BSON binary fields into Go byte
   372  		// slices when there is no type information (e.g when unmarshaling into a bson.D).
   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  		// Test that DefaultDocumentD overrides the default "ancestor" logic and always decodes BSON
   385  		// documents into bson.D values, independent of the top-level Go value type.
   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  		// Test that DefaultDocumentM overrides the default "ancestor" logic and always decodes BSON
   402  		// documents into bson.M values, independent of the top-level Go value type.
   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  		// Test that UseJSONStructTags causes the Decoder to fall back to "json" struct tags if
   419  		// "bson" struct tags are not available.
   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  		// Test that UseLocalTimeZone causes the Decoder to use the local time zone for decoded
   432  		// time.Time values instead of UTC.
   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  		// Test that ZeroMaps causes the Decoder to empty any Go map values before decoding BSON
   445  		// documents into them.
   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  		// Test that ZeroStructs causes the Decoder to empty any Go struct values before decoding
   462  		// BSON documents into them.
   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 // Capture range variable.
   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