...

Source file src/go.mongodb.org/mongo-driver/bson/primitive_codecs_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  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  	"net/url"
    15  	"reflect"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/google/go-cmp/cmp"
    20  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
    21  	"go.mongodb.org/mongo-driver/bson/bsonrw"
    22  	"go.mongodb.org/mongo-driver/bson/bsonrw/bsonrwtest"
    23  	"go.mongodb.org/mongo-driver/bson/bsontype"
    24  	"go.mongodb.org/mongo-driver/bson/primitive"
    25  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    26  )
    27  
    28  func bytesFromDoc(doc interface{}) []byte {
    29  	b, err := Marshal(doc)
    30  	if err != nil {
    31  		panic(fmt.Errorf("Couldn't marshal BSON document: %w", err))
    32  	}
    33  	return b
    34  }
    35  
    36  func compareDecimal128(d1, d2 primitive.Decimal128) bool {
    37  	d1H, d1L := d1.GetBytes()
    38  	d2H, d2L := d2.GetBytes()
    39  
    40  	if d1H != d2H {
    41  		return false
    42  	}
    43  
    44  	if d1L != d2L {
    45  		return false
    46  	}
    47  
    48  	return true
    49  }
    50  
    51  func compareErrors(err1, err2 error) bool {
    52  	if err1 == nil && err2 == nil {
    53  		return true
    54  	}
    55  
    56  	if err1 == nil || err2 == nil {
    57  		return false
    58  	}
    59  
    60  	if err1.Error() != err2.Error() {
    61  		return false
    62  	}
    63  
    64  	return true
    65  }
    66  
    67  func TestDefaultValueEncoders(t *testing.T) {
    68  	t.Parallel()
    69  
    70  	var pc PrimitiveCodecs
    71  
    72  	var wrong = func(string, string) string { return "wrong" }
    73  
    74  	type subtest struct {
    75  		name   string
    76  		val    interface{}
    77  		ectx   *bsoncodec.EncodeContext
    78  		llvrw  *bsonrwtest.ValueReaderWriter
    79  		invoke bsonrwtest.Invoked
    80  		err    error
    81  	}
    82  
    83  	testCases := []struct {
    84  		name     string
    85  		ve       bsoncodec.ValueEncoder
    86  		subtests []subtest
    87  	}{
    88  		{
    89  			"RawValueEncodeValue",
    90  			bsoncodec.ValueEncoderFunc(pc.RawValueEncodeValue),
    91  			[]subtest{
    92  				{
    93  					"wrong type",
    94  					wrong,
    95  					nil,
    96  					nil,
    97  					bsonrwtest.Nothing,
    98  					bsoncodec.ValueEncoderError{
    99  						Name:     "RawValueEncodeValue",
   100  						Types:    []reflect.Type{tRawValue},
   101  						Received: reflect.ValueOf(wrong),
   102  					},
   103  				},
   104  				{
   105  					"RawValue/success",
   106  					RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, 3.14159)},
   107  					nil,
   108  					nil,
   109  					bsonrwtest.WriteDouble,
   110  					nil,
   111  				},
   112  				{
   113  					"RawValue Type is zero with non-zero value",
   114  					RawValue{
   115  						Type:  0x00,
   116  						Value: bsoncore.AppendDouble(nil, 3.14159),
   117  					},
   118  					nil,
   119  					nil,
   120  					bsonrwtest.Nothing,
   121  					fmt.Errorf("the RawValue Type specifies an invalid BSON type: 0x0"),
   122  				},
   123  				{
   124  					"RawValue Type is invalid",
   125  					RawValue{
   126  						Type:  0x8F,
   127  						Value: bsoncore.AppendDouble(nil, 3.14159),
   128  					},
   129  					nil,
   130  					nil,
   131  					bsonrwtest.Nothing,
   132  					fmt.Errorf("the RawValue Type specifies an invalid BSON type: 0x8f"),
   133  				},
   134  			},
   135  		},
   136  		{
   137  			"RawEncodeValue",
   138  			bsoncodec.ValueEncoderFunc(pc.RawEncodeValue),
   139  			[]subtest{
   140  				{
   141  					"wrong type",
   142  					wrong,
   143  					nil,
   144  					nil,
   145  					bsonrwtest.Nothing,
   146  					bsoncodec.ValueEncoderError{Name: "RawEncodeValue", Types: []reflect.Type{tRaw}, Received: reflect.ValueOf(wrong)},
   147  				},
   148  				{
   149  					"WriteDocument Error",
   150  					Raw{},
   151  					nil,
   152  					&bsonrwtest.ValueReaderWriter{Err: errors.New("wd error"), ErrAfter: bsonrwtest.WriteDocument},
   153  					bsonrwtest.WriteDocument,
   154  					errors.New("wd error"),
   155  				},
   156  				{
   157  					"Raw.Elements Error",
   158  					Raw{0xFF, 0x00, 0x00, 0x00, 0x00},
   159  					nil,
   160  					&bsonrwtest.ValueReaderWriter{},
   161  					bsonrwtest.WriteDocument,
   162  					errors.New("length read exceeds number of bytes available. length=5 bytes=255"),
   163  				},
   164  				{
   165  					"WriteDocumentElement Error",
   166  					Raw(bytesFromDoc(D{{"foo", nil}})),
   167  					nil,
   168  					&bsonrwtest.ValueReaderWriter{Err: errors.New("wde error"), ErrAfter: bsonrwtest.WriteDocumentElement},
   169  					bsonrwtest.WriteDocumentElement,
   170  					errors.New("wde error"),
   171  				},
   172  				{
   173  					"encodeValue error",
   174  					Raw(bytesFromDoc(D{{"foo", nil}})),
   175  					nil,
   176  					&bsonrwtest.ValueReaderWriter{Err: errors.New("ev error"), ErrAfter: bsonrwtest.WriteNull},
   177  					bsonrwtest.WriteNull,
   178  					errors.New("ev error"),
   179  				},
   180  				{
   181  					"iterator error",
   182  					Raw{0x0C, 0x00, 0x00, 0x00, 0x01, 'f', 'o', 'o', 0x00, 0x01, 0x02, 0x03},
   183  					nil,
   184  					&bsonrwtest.ValueReaderWriter{},
   185  					bsonrwtest.WriteDocumentElement,
   186  					errors.New("not enough bytes available to read type. bytes=3 type=double"),
   187  				},
   188  			},
   189  		},
   190  	}
   191  
   192  	for _, tc := range testCases {
   193  		tc := tc // Capture the range variable
   194  
   195  		t.Run(tc.name, func(t *testing.T) {
   196  			t.Parallel()
   197  
   198  			for _, subtest := range tc.subtests {
   199  				subtest := subtest // Capture the range variable
   200  
   201  				t.Run(subtest.name, func(t *testing.T) {
   202  					t.Parallel()
   203  
   204  					var ec bsoncodec.EncodeContext
   205  					if subtest.ectx != nil {
   206  						ec = *subtest.ectx
   207  					}
   208  					llvrw := new(bsonrwtest.ValueReaderWriter)
   209  					if subtest.llvrw != nil {
   210  						llvrw = subtest.llvrw
   211  					}
   212  					llvrw.T = t
   213  					err := tc.ve.EncodeValue(ec, llvrw, reflect.ValueOf(subtest.val))
   214  					if !compareErrors(err, subtest.err) {
   215  						t.Errorf("Errors do not match. got %v; want %v", err, subtest.err)
   216  					}
   217  					invoked := llvrw.Invoked
   218  					if !cmp.Equal(invoked, subtest.invoke) {
   219  						t.Errorf("Incorrect method invoked. got %v; want %v", invoked, subtest.invoke)
   220  					}
   221  				})
   222  			}
   223  		})
   224  	}
   225  
   226  	t.Run("success path", func(t *testing.T) {
   227  		t.Parallel()
   228  
   229  		oid := primitive.NewObjectID()
   230  		oids := []primitive.ObjectID{primitive.NewObjectID(), primitive.NewObjectID(), primitive.NewObjectID()}
   231  		var str = new(string)
   232  		*str = "bar"
   233  		now := time.Now().Truncate(time.Millisecond)
   234  		murl, err := url.Parse("https://mongodb.com/random-url?hello=world")
   235  		if err != nil {
   236  			t.Errorf("Error parsing URL: %v", err)
   237  			t.FailNow()
   238  		}
   239  		decimal128, err := primitive.ParseDecimal128("1.5e10")
   240  		if err != nil {
   241  			t.Errorf("Error parsing decimal128: %v", err)
   242  			t.FailNow()
   243  		}
   244  
   245  		testCases := []struct {
   246  			name  string
   247  			value interface{}
   248  			b     []byte
   249  			err   error
   250  		}{
   251  			{
   252  				"D to JavaScript",
   253  				D{{"a", primitive.JavaScript(`function() { var hello = "world"; }`)}},
   254  				docToBytes(D{{"a", primitive.JavaScript(`function() { var hello = "world"; }`)}}),
   255  				nil,
   256  			},
   257  			{
   258  				"D to Symbol",
   259  				D{{"a", primitive.Symbol("foobarbaz")}},
   260  				docToBytes(D{{"a", primitive.Symbol("foobarbaz")}}),
   261  				nil,
   262  			},
   263  			{
   264  				"omitempty map",
   265  				struct {
   266  					T map[string]string `bson:",omitempty"`
   267  				}{
   268  					T: map[string]string{},
   269  				},
   270  				docToBytes(D{}),
   271  				nil,
   272  			},
   273  			{
   274  				"omitempty slice",
   275  				struct {
   276  					T []struct{} `bson:",omitempty"`
   277  				}{
   278  					T: []struct{}{},
   279  				},
   280  				docToBytes(D{}),
   281  				nil,
   282  			},
   283  			{
   284  				"omitempty string",
   285  				struct {
   286  					T string `bson:",omitempty"`
   287  				}{
   288  					T: "",
   289  				},
   290  				docToBytes(D{}),
   291  				nil,
   292  			},
   293  			{
   294  				"struct{}",
   295  				struct {
   296  					A bool
   297  					B int32
   298  					C int64
   299  					D uint16
   300  					E uint64
   301  					F float64
   302  					G string
   303  					H map[string]string
   304  					I []byte
   305  					K [2]string
   306  					L struct {
   307  						M string
   308  					}
   309  					P  Raw
   310  					Q  primitive.ObjectID
   311  					T  []struct{}
   312  					Y  json.Number
   313  					Z  time.Time
   314  					AA json.Number
   315  					AB *url.URL
   316  					AC primitive.Decimal128
   317  					AD *time.Time
   318  					AE testValueMarshaler
   319  					AF RawValue
   320  					AG *RawValue
   321  					AH D
   322  					AI *D
   323  					AJ *D
   324  				}{
   325  					A: true,
   326  					B: 123,
   327  					C: 456,
   328  					D: 789,
   329  					E: 101112,
   330  					F: 3.14159,
   331  					G: "Hello, world",
   332  					H: map[string]string{"foo": "bar"},
   333  					I: []byte{0x01, 0x02, 0x03},
   334  					K: [2]string{"baz", "qux"},
   335  					L: struct {
   336  						M string
   337  					}{
   338  						M: "foobar",
   339  					},
   340  					P:  Raw{0x05, 0x00, 0x00, 0x00, 0x00},
   341  					Q:  oid,
   342  					T:  nil,
   343  					Y:  json.Number("5"),
   344  					Z:  now,
   345  					AA: json.Number("10.1"),
   346  					AB: murl,
   347  					AC: decimal128,
   348  					AD: &now,
   349  					AE: testValueMarshaler{t: TypeString, buf: bsoncore.AppendString(nil, "hello, world")},
   350  					AF: RawValue{Type: bsontype.String, Value: bsoncore.AppendString(nil, "hello, raw value")},
   351  					AG: &RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, 3.14159)},
   352  					AH: D{{"foo", "bar"}},
   353  					AI: &D{{"pi", 3.14159}},
   354  					AJ: nil,
   355  				},
   356  				docToBytes(D{
   357  					{"a", true},
   358  					{"b", int32(123)},
   359  					{"c", int64(456)},
   360  					{"d", int32(789)},
   361  					{"e", int64(101112)},
   362  					{"f", float64(3.14159)},
   363  					{"g", "Hello, world"},
   364  					{"h", D{{"foo", "bar"}}},
   365  					{"i", primitive.Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}},
   366  					{"k", A{"baz", "qux"}},
   367  					{"l", D{{"m", "foobar"}}},
   368  					{"p", D{}},
   369  					{"q", oid},
   370  					{"t", nil},
   371  					{"y", int64(5)},
   372  					{"z", primitive.DateTime(now.UnixNano() / int64(time.Millisecond))},
   373  					{"aa", float64(10.1)},
   374  					{"ab", murl.String()},
   375  					{"ac", decimal128},
   376  					{"ad", primitive.DateTime(now.UnixNano() / int64(time.Millisecond))},
   377  					{"ae", "hello, world"},
   378  					{"af", "hello, raw value"},
   379  					{"ag", 3.14159},
   380  					{"ah", D{{"foo", "bar"}}},
   381  					{"ai", D{{"pi", float64(3.14159)}}},
   382  					{"aj", nil},
   383  				}),
   384  				nil,
   385  			},
   386  			{
   387  				"struct{[]interface{}}",
   388  				struct {
   389  					A []bool
   390  					B []int32
   391  					C []int64
   392  					D []uint16
   393  					E []uint64
   394  					F []float64
   395  					G []string
   396  					H []map[string]string
   397  					I [][]byte
   398  					K [1][2]string
   399  					L []struct {
   400  						M string
   401  					}
   402  					N  [][]string
   403  					Q  []Raw
   404  					R  []primitive.ObjectID
   405  					T  []struct{}
   406  					W  []map[string]struct{}
   407  					X  []map[string]struct{}
   408  					Y  []map[string]struct{}
   409  					Z  []time.Time
   410  					AA []json.Number
   411  					AB []*url.URL
   412  					AC []primitive.Decimal128
   413  					AD []*time.Time
   414  					AE []testValueMarshaler
   415  					AF []D
   416  					AG []*D
   417  				}{
   418  					A: []bool{true},
   419  					B: []int32{123},
   420  					C: []int64{456},
   421  					D: []uint16{789},
   422  					E: []uint64{101112},
   423  					F: []float64{3.14159},
   424  					G: []string{"Hello, world"},
   425  					H: []map[string]string{{"foo": "bar"}},
   426  					I: [][]byte{{0x01, 0x02, 0x03}},
   427  					K: [1][2]string{{"baz", "qux"}},
   428  					L: []struct {
   429  						M string
   430  					}{
   431  						{
   432  							M: "foobar",
   433  						},
   434  					},
   435  					N:  [][]string{{"foo", "bar"}},
   436  					Q:  []Raw{{0x05, 0x00, 0x00, 0x00, 0x00}},
   437  					R:  oids,
   438  					T:  nil,
   439  					W:  nil,
   440  					X:  []map[string]struct{}{},   // Should be empty BSON Array
   441  					Y:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument
   442  					Z:  []time.Time{now, now},
   443  					AA: []json.Number{json.Number("5"), json.Number("10.1")},
   444  					AB: []*url.URL{murl},
   445  					AC: []primitive.Decimal128{decimal128},
   446  					AD: []*time.Time{&now, &now},
   447  					AE: []testValueMarshaler{
   448  						{t: TypeString, buf: bsoncore.AppendString(nil, "hello")},
   449  						{t: TypeString, buf: bsoncore.AppendString(nil, "world")},
   450  					},
   451  					AF: []D{{{"foo", "bar"}}, {{"hello", "world"}, {"number", 12345}}},
   452  					AG: []*D{{{"pi", 3.14159}}, nil},
   453  				},
   454  				docToBytes(D{
   455  					{"a", A{true}},
   456  					{"b", A{int32(123)}},
   457  					{"c", A{int64(456)}},
   458  					{"d", A{int32(789)}},
   459  					{"e", A{int64(101112)}},
   460  					{"f", A{float64(3.14159)}},
   461  					{"g", A{"Hello, world"}},
   462  					{"h", A{D{{"foo", "bar"}}}},
   463  					{"i", A{primitive.Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}}},
   464  					{"k", A{A{"baz", "qux"}}},
   465  					{"l", A{D{{"m", "foobar"}}}},
   466  					{"n", A{A{"foo", "bar"}}},
   467  					{"q", A{D{}}},
   468  					{"r", A{oids[0], oids[1], oids[2]}},
   469  					{"t", nil},
   470  					{"w", nil},
   471  					{"x", A{}},
   472  					{"y", A{D{}}},
   473  					{"z", A{
   474  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
   475  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
   476  					}},
   477  					{"aa", A{int64(5), float64(10.10)}},
   478  					{"ab", A{murl.String()}},
   479  					{"ac", A{decimal128}},
   480  					{"ad", A{
   481  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
   482  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
   483  					}},
   484  					{"ae", A{"hello", "world"}},
   485  					{"af", A{D{{"foo", "bar"}}, D{{"hello", "world"}, {"number", int32(12345)}}}},
   486  					{"ag", A{D{{"pi", float64(3.14159)}}, nil}},
   487  				}),
   488  				nil,
   489  			},
   490  		}
   491  
   492  		for _, tc := range testCases {
   493  			tc := tc // Capture the range variable
   494  
   495  			t.Run(tc.name, func(t *testing.T) {
   496  				t.Parallel()
   497  
   498  				b := make(bsonrw.SliceWriter, 0, 512)
   499  				vw, err := bsonrw.NewBSONValueWriter(&b)
   500  				noerr(t, err)
   501  				enc, err := NewEncoder(vw)
   502  				noerr(t, err)
   503  				err = enc.Encode(tc.value)
   504  				if !errors.Is(err, tc.err) {
   505  					t.Errorf("Did not receive expected error. got %v; want %v", err, tc.err)
   506  				}
   507  				if diff := cmp.Diff([]byte(b), tc.b); diff != "" {
   508  					t.Errorf("Bytes written differ: (-got +want)\n%s", diff)
   509  					t.Errorf("Bytes\ngot: %v\nwant:%v\n", b, tc.b)
   510  					t.Errorf("Readers\ngot: %v\nwant:%v\n", Raw(b), Raw(tc.b))
   511  				}
   512  			})
   513  		}
   514  	})
   515  }
   516  
   517  func TestDefaultValueDecoders(t *testing.T) {
   518  	var pc PrimitiveCodecs
   519  
   520  	var wrong = func(string, string) string { return "wrong" }
   521  
   522  	const cansetreflectiontest = "cansetreflectiontest"
   523  
   524  	type subtest struct {
   525  		name   string
   526  		val    interface{}
   527  		dctx   *bsoncodec.DecodeContext
   528  		llvrw  *bsonrwtest.ValueReaderWriter
   529  		invoke bsonrwtest.Invoked
   530  		err    error
   531  	}
   532  
   533  	testCases := []struct {
   534  		name     string
   535  		vd       bsoncodec.ValueDecoder
   536  		subtests []subtest
   537  	}{
   538  		{
   539  			"RawValueDecodeValue",
   540  			bsoncodec.ValueDecoderFunc(pc.RawValueDecodeValue),
   541  			[]subtest{
   542  				{
   543  					"wrong type",
   544  					wrong,
   545  					nil,
   546  					&bsonrwtest.ValueReaderWriter{},
   547  					bsonrwtest.Nothing,
   548  					bsoncodec.ValueDecoderError{
   549  						Name:     "RawValueDecodeValue",
   550  						Types:    []reflect.Type{tRawValue},
   551  						Received: reflect.ValueOf(wrong),
   552  					},
   553  				},
   554  				{
   555  					"ReadValue Error",
   556  					RawValue{},
   557  					nil,
   558  					&bsonrwtest.ValueReaderWriter{
   559  						BSONType: bsontype.Binary,
   560  						Err:      errors.New("rb error"),
   561  						ErrAfter: bsonrwtest.ReadBinary,
   562  					},
   563  					bsonrwtest.ReadBinary,
   564  					errors.New("rb error"),
   565  				},
   566  				{
   567  					"RawValue/success",
   568  					RawValue{Type: bsontype.Binary, Value: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03})},
   569  					nil,
   570  					&bsonrwtest.ValueReaderWriter{
   571  						BSONType: bsontype.Binary,
   572  						Return: bsoncore.Value{
   573  							Type: bsontype.Binary,
   574  							Data: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03}),
   575  						},
   576  					},
   577  					bsonrwtest.ReadBinary,
   578  					nil,
   579  				},
   580  			},
   581  		},
   582  		{
   583  			"RawDecodeValue",
   584  			bsoncodec.ValueDecoderFunc(pc.RawDecodeValue),
   585  			[]subtest{
   586  				{
   587  					"wrong type",
   588  					wrong,
   589  					nil,
   590  					&bsonrwtest.ValueReaderWriter{},
   591  					bsonrwtest.Nothing,
   592  					bsoncodec.ValueDecoderError{Name: "RawDecodeValue", Types: []reflect.Type{tRaw}, Received: reflect.ValueOf(wrong)},
   593  				},
   594  				{
   595  					"*Raw is nil",
   596  					(*Raw)(nil),
   597  					nil,
   598  					nil,
   599  					bsonrwtest.Nothing,
   600  					bsoncodec.ValueDecoderError{
   601  						Name:     "RawDecodeValue",
   602  						Types:    []reflect.Type{tRaw},
   603  						Received: reflect.ValueOf((*Raw)(nil)),
   604  					},
   605  				},
   606  				{
   607  					"Copy error",
   608  					Raw{},
   609  					nil,
   610  					&bsonrwtest.ValueReaderWriter{Err: errors.New("copy error"), ErrAfter: bsonrwtest.ReadDocument},
   611  					bsonrwtest.ReadDocument,
   612  					errors.New("copy error"),
   613  				},
   614  			},
   615  		},
   616  	}
   617  
   618  	for _, tc := range testCases {
   619  		t.Run(tc.name, func(t *testing.T) {
   620  			for _, rc := range tc.subtests {
   621  				t.Run(rc.name, func(t *testing.T) {
   622  					var dc bsoncodec.DecodeContext
   623  					if rc.dctx != nil {
   624  						dc = *rc.dctx
   625  					}
   626  					llvrw := new(bsonrwtest.ValueReaderWriter)
   627  					if rc.llvrw != nil {
   628  						llvrw = rc.llvrw
   629  					}
   630  					llvrw.T = t
   631  					if rc.val == cansetreflectiontest { // We're doing a CanSet reflection test
   632  						err := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})
   633  						if !compareErrors(err, rc.err) {
   634  							t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
   635  						}
   636  
   637  						val := reflect.New(reflect.TypeOf(rc.val)).Elem()
   638  						err = tc.vd.DecodeValue(dc, llvrw, val)
   639  						if !compareErrors(err, rc.err) {
   640  							t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
   641  						}
   642  						return
   643  					}
   644  					var val reflect.Value
   645  					if rtype := reflect.TypeOf(rc.val); rtype != nil {
   646  						val = reflect.New(rtype).Elem()
   647  					}
   648  					want := rc.val
   649  					defer func() {
   650  						if err := recover(); err != nil {
   651  							fmt.Println(t.Name())
   652  							panic(err)
   653  						}
   654  					}()
   655  					err := tc.vd.DecodeValue(dc, llvrw, val)
   656  					if !compareErrors(err, rc.err) {
   657  						t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
   658  					}
   659  					invoked := llvrw.Invoked
   660  					if !cmp.Equal(invoked, rc.invoke) {
   661  						t.Errorf("Incorrect method invoked. got %v; want %v", invoked, rc.invoke)
   662  					}
   663  					var got interface{}
   664  					if val.IsValid() && val.CanInterface() {
   665  						got = val.Interface()
   666  					}
   667  					if rc.err == nil && !cmp.Equal(got, want) {
   668  						t.Errorf("Values do not match. got (%T)%v; want (%T)%v", got, got, want, want)
   669  					}
   670  				})
   671  			}
   672  		})
   673  	}
   674  
   675  	t.Run("success path", func(t *testing.T) {
   676  		oid := primitive.NewObjectID()
   677  		oids := []primitive.ObjectID{primitive.NewObjectID(), primitive.NewObjectID(), primitive.NewObjectID()}
   678  		var str = new(string)
   679  		*str = "bar"
   680  		now := time.Now().Truncate(time.Millisecond)
   681  		murl, err := url.Parse("https://mongodb.com/random-url?hello=world")
   682  		if err != nil {
   683  			t.Errorf("Error parsing URL: %v", err)
   684  			t.FailNow()
   685  		}
   686  		decimal128, err := primitive.ParseDecimal128("1.5e10")
   687  		if err != nil {
   688  			t.Errorf("Error parsing decimal128: %v", err)
   689  			t.FailNow()
   690  		}
   691  
   692  		testCases := []struct {
   693  			name  string
   694  			value interface{}
   695  			b     []byte
   696  			err   error
   697  		}{
   698  			{
   699  				"map[string]int",
   700  				map[string]int32{"foo": 1},
   701  				[]byte{
   702  					0x0E, 0x00, 0x00, 0x00,
   703  					0x10, 'f', 'o', 'o', 0x00,
   704  					0x01, 0x00, 0x00, 0x00,
   705  					0x00,
   706  				},
   707  				nil,
   708  			},
   709  			{
   710  				"map[string]primitive.ObjectID",
   711  				map[string]primitive.ObjectID{"foo": oid},
   712  				docToBytes(D{{"foo", oid}}),
   713  				nil,
   714  			},
   715  			{
   716  				"map[string]Reader",
   717  				map[string]Raw{"Z": {0x05, 0x00, 0x00, 0x00, 0x00}},
   718  				docToBytes(D{{"Z", Raw{0x05, 0x00, 0x00, 0x00, 0x00}}}),
   719  				nil,
   720  			},
   721  			{
   722  				"map[string][]int32",
   723  				map[string][]int32{"Z": {1, 2, 3}},
   724  				docToBytes(D{{"Z", A{int32(1), int32(2), int32(3)}}}),
   725  				nil,
   726  			},
   727  			{
   728  				"map[string][]primitive.ObjectID",
   729  				map[string][]primitive.ObjectID{"Z": oids},
   730  				docToBytes(D{{"Z", A{oids[0], oids[1], oids[2]}}}),
   731  				nil,
   732  			},
   733  			{
   734  				"map[string][]json.Number(int64)",
   735  				map[string][]json.Number{"Z": {json.Number("5"), json.Number("10")}},
   736  				docToBytes(D{{"Z", A{int64(5), int64(10)}}}),
   737  				nil,
   738  			},
   739  			{
   740  				"map[string][]json.Number(float64)",
   741  				map[string][]json.Number{"Z": {json.Number("5"), json.Number("10.1")}},
   742  				docToBytes(D{{"Z", A{int64(5), float64(10.1)}}}),
   743  				nil,
   744  			},
   745  			{
   746  				"map[string][]*url.URL",
   747  				map[string][]*url.URL{"Z": {murl}},
   748  				docToBytes(D{{"Z", A{murl.String()}}}),
   749  				nil,
   750  			},
   751  			{
   752  				"map[string][]primitive.Decimal128",
   753  				map[string][]primitive.Decimal128{"Z": {decimal128}},
   754  				docToBytes(D{{"Z", A{decimal128}}}),
   755  				nil,
   756  			},
   757  			{
   758  				"-",
   759  				struct {
   760  					A string `bson:"-"`
   761  				}{
   762  					A: "",
   763  				},
   764  				docToBytes(D{}),
   765  				nil,
   766  			},
   767  			{
   768  				"omitempty",
   769  				struct {
   770  					A string `bson:",omitempty"`
   771  				}{
   772  					A: "",
   773  				},
   774  				docToBytes(D{}),
   775  				nil,
   776  			},
   777  			{
   778  				"omitempty, empty time",
   779  				struct {
   780  					A time.Time `bson:",omitempty"`
   781  				}{
   782  					A: time.Time{},
   783  				},
   784  				docToBytes(D{}),
   785  				nil,
   786  			},
   787  			{
   788  				"no private fields",
   789  				noPrivateFields{a: "should be empty"},
   790  				docToBytes(D{}),
   791  				nil,
   792  			},
   793  			{
   794  				"minsize",
   795  				struct {
   796  					A int64 `bson:",minsize"`
   797  				}{
   798  					A: 12345,
   799  				},
   800  				docToBytes(D{{"a", int32(12345)}}),
   801  				nil,
   802  			},
   803  			{
   804  				"inline",
   805  				struct {
   806  					Foo struct {
   807  						A int64 `bson:",minsize"`
   808  					} `bson:",inline"`
   809  				}{
   810  					Foo: struct {
   811  						A int64 `bson:",minsize"`
   812  					}{
   813  						A: 12345,
   814  					},
   815  				},
   816  				docToBytes(D{{"a", int32(12345)}}),
   817  				nil,
   818  			},
   819  			{
   820  				"inline map",
   821  				struct {
   822  					Foo map[string]string `bson:",inline"`
   823  				}{
   824  					Foo: map[string]string{"foo": "bar"},
   825  				},
   826  				docToBytes(D{{"foo", "bar"}}),
   827  				nil,
   828  			},
   829  			{
   830  				"alternate name bson:name",
   831  				struct {
   832  					A string `bson:"foo"`
   833  				}{
   834  					A: "bar",
   835  				},
   836  				docToBytes(D{{"foo", "bar"}}),
   837  				nil,
   838  			},
   839  			{
   840  				"alternate name",
   841  				struct {
   842  					A string `bson:"foo"`
   843  				}{
   844  					A: "bar",
   845  				},
   846  				docToBytes(D{{"foo", "bar"}}),
   847  				nil,
   848  			},
   849  			{
   850  				"inline, omitempty",
   851  				struct {
   852  					A   string
   853  					Foo zeroTest `bson:"omitempty,inline"`
   854  				}{
   855  					A:   "bar",
   856  					Foo: zeroTest{true},
   857  				},
   858  				docToBytes(D{{"a", "bar"}}),
   859  				nil,
   860  			},
   861  			{
   862  				"JavaScript to D",
   863  				D{{"a", primitive.JavaScript(`function() { var hello = "world"; }`)}},
   864  				docToBytes(D{{"a", primitive.JavaScript(`function() { var hello = "world"; }`)}}),
   865  				nil,
   866  			},
   867  			{
   868  				"Symbol to D",
   869  				D{{"a", primitive.Symbol("foobarbaz")}},
   870  				docToBytes(D{{"a", primitive.Symbol("foobarbaz")}}),
   871  				nil,
   872  			},
   873  			{
   874  				"struct{}",
   875  				struct {
   876  					A bool
   877  					B int32
   878  					C int64
   879  					D uint16
   880  					E uint64
   881  					F float64
   882  					G string
   883  					H map[string]string
   884  					I []byte
   885  					K [2]string
   886  					L struct {
   887  						M string
   888  					}
   889  					P  Raw
   890  					Q  primitive.ObjectID
   891  					T  []struct{}
   892  					Y  json.Number
   893  					Z  time.Time
   894  					AA json.Number
   895  					AB *url.URL
   896  					AC primitive.Decimal128
   897  					AD *time.Time
   898  					AE *testValueUnmarshaler
   899  					AF RawValue
   900  					AG *RawValue
   901  					AH D
   902  					AI *D
   903  					AJ *D
   904  				}{
   905  					A: true,
   906  					B: 123,
   907  					C: 456,
   908  					D: 789,
   909  					E: 101112,
   910  					F: 3.14159,
   911  					G: "Hello, world",
   912  					H: map[string]string{"foo": "bar"},
   913  					I: []byte{0x01, 0x02, 0x03},
   914  					K: [2]string{"baz", "qux"},
   915  					L: struct {
   916  						M string
   917  					}{
   918  						M: "foobar",
   919  					},
   920  					P:  Raw{0x05, 0x00, 0x00, 0x00, 0x00},
   921  					Q:  oid,
   922  					T:  nil,
   923  					Y:  json.Number("5"),
   924  					Z:  now,
   925  					AA: json.Number("10.1"),
   926  					AB: murl,
   927  					AC: decimal128,
   928  					AD: &now,
   929  					AE: &testValueUnmarshaler{t: bsontype.String, val: bsoncore.AppendString(nil, "hello, world!")},
   930  					AF: RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, 3.14159)},
   931  					AG: &RawValue{Type: bsontype.Binary, Value: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03})},
   932  					AH: D{{"foo", "bar"}},
   933  					AI: &D{{"pi", 3.14159}},
   934  					AJ: nil,
   935  				},
   936  				docToBytes(D{
   937  					{"a", true},
   938  					{"b", int32(123)},
   939  					{"c", int64(456)},
   940  					{"d", int32(789)},
   941  					{"e", int64(101112)},
   942  					{"f", float64(3.14159)},
   943  					{"g", "Hello, world"},
   944  					{"h", D{{"foo", "bar"}}},
   945  					{"i", primitive.Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}},
   946  					{"k", A{"baz", "qux"}},
   947  					{"l", D{{"m", "foobar"}}},
   948  					{"p", D{}},
   949  					{"q", oid},
   950  					{"t", nil},
   951  					{"y", int64(5)},
   952  					{"z", primitive.DateTime(now.UnixNano() / int64(time.Millisecond))},
   953  					{"aa", float64(10.1)},
   954  					{"ab", murl.String()},
   955  					{"ac", decimal128},
   956  					{"ad", primitive.DateTime(now.UnixNano() / int64(time.Millisecond))},
   957  					{"ae", "hello, world!"},
   958  					{"af", float64(3.14159)},
   959  					{"ag", primitive.Binary{Subtype: 0xFF, Data: []byte{0x01, 0x02, 0x03}}},
   960  					{"ah", D{{"foo", "bar"}}},
   961  					{"ai", D{{"pi", float64(3.14159)}}},
   962  					{"aj", nil},
   963  				}),
   964  				nil,
   965  			},
   966  			{
   967  				"struct{[]interface{}}",
   968  				struct {
   969  					A []bool
   970  					B []int32
   971  					C []int64
   972  					D []uint16
   973  					E []uint64
   974  					F []float64
   975  					G []string
   976  					H []map[string]string
   977  					I [][]byte
   978  					K [1][2]string
   979  					L []struct {
   980  						M string
   981  					}
   982  					N  [][]string
   983  					Q  []Raw
   984  					R  []primitive.ObjectID
   985  					T  []struct{}
   986  					W  []map[string]struct{}
   987  					X  []map[string]struct{}
   988  					Y  []map[string]struct{}
   989  					Z  []time.Time
   990  					AA []json.Number
   991  					AB []*url.URL
   992  					AC []primitive.Decimal128
   993  					AD []*time.Time
   994  					AE []*testValueUnmarshaler
   995  					AF []D
   996  					AG []*D
   997  				}{
   998  					A: []bool{true},
   999  					B: []int32{123},
  1000  					C: []int64{456},
  1001  					D: []uint16{789},
  1002  					E: []uint64{101112},
  1003  					F: []float64{3.14159},
  1004  					G: []string{"Hello, world"},
  1005  					H: []map[string]string{{"foo": "bar"}},
  1006  					I: [][]byte{{0x01, 0x02, 0x03}},
  1007  					K: [1][2]string{{"baz", "qux"}},
  1008  					L: []struct {
  1009  						M string
  1010  					}{
  1011  						{
  1012  							M: "foobar",
  1013  						},
  1014  					},
  1015  					N:  [][]string{{"foo", "bar"}},
  1016  					Q:  []Raw{{0x05, 0x00, 0x00, 0x00, 0x00}},
  1017  					R:  oids,
  1018  					T:  nil,
  1019  					W:  nil,
  1020  					X:  []map[string]struct{}{},   // Should be empty BSON Array
  1021  					Y:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument
  1022  					Z:  []time.Time{now, now},
  1023  					AA: []json.Number{json.Number("5"), json.Number("10.1")},
  1024  					AB: []*url.URL{murl},
  1025  					AC: []primitive.Decimal128{decimal128},
  1026  					AD: []*time.Time{&now, &now},
  1027  					AE: []*testValueUnmarshaler{
  1028  						{t: bsontype.String, val: bsoncore.AppendString(nil, "hello")},
  1029  						{t: bsontype.String, val: bsoncore.AppendString(nil, "world")},
  1030  					},
  1031  					AF: []D{{{"foo", "bar"}}, {{"hello", "world"}, {"number", int64(12345)}}},
  1032  					AG: []*D{{{"pi", 3.14159}}, nil},
  1033  				},
  1034  				docToBytes(D{
  1035  					{"a", A{true}},
  1036  					{"b", A{int32(123)}},
  1037  					{"c", A{int64(456)}},
  1038  					{"d", A{int32(789)}},
  1039  					{"e", A{int64(101112)}},
  1040  					{"f", A{float64(3.14159)}},
  1041  					{"g", A{"Hello, world"}},
  1042  					{"h", A{D{{"foo", "bar"}}}},
  1043  					{"i", A{primitive.Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}}},
  1044  					{"k", A{A{"baz", "qux"}}},
  1045  					{"l", A{D{{"m", "foobar"}}}},
  1046  					{"n", A{A{"foo", "bar"}}},
  1047  					{"q", A{D{}}},
  1048  					{"r", A{oids[0], oids[1], oids[2]}},
  1049  					{"t", nil},
  1050  					{"w", nil},
  1051  					{"x", A{}},
  1052  					{"y", A{D{}}},
  1053  					{"z", A{
  1054  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
  1055  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
  1056  					}},
  1057  					{"aa", A{int64(5), float64(10.10)}},
  1058  					{"ab", A{murl.String()}},
  1059  					{"ac", A{decimal128}},
  1060  					{"ad", A{
  1061  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
  1062  						primitive.DateTime(now.UnixNano() / int64(time.Millisecond)),
  1063  					}},
  1064  					{"ae", A{"hello", "world"}},
  1065  					{"af", A{
  1066  						D{{"foo", "bar"}},
  1067  						D{{"hello", "world"}, {"number", int64(12345)}},
  1068  					}},
  1069  					{"ag", A{D{{"pi", float64(3.14159)}}, nil}},
  1070  				}),
  1071  				nil,
  1072  			},
  1073  		}
  1074  
  1075  		t.Run("Decode", func(t *testing.T) {
  1076  			for _, tc := range testCases {
  1077  				t.Run(tc.name, func(t *testing.T) {
  1078  					vr := bsonrw.NewBSONDocumentReader(tc.b)
  1079  					dec, err := NewDecoder(vr)
  1080  					noerr(t, err)
  1081  					gotVal := reflect.New(reflect.TypeOf(tc.value))
  1082  					err = dec.Decode(gotVal.Interface())
  1083  					noerr(t, err)
  1084  					got := gotVal.Elem().Interface()
  1085  					want := tc.value
  1086  					if diff := cmp.Diff(
  1087  						got, want,
  1088  						cmp.Comparer(compareDecimal128),
  1089  						cmp.Comparer(compareNoPrivateFields),
  1090  						cmp.Comparer(compareZeroTest),
  1091  					); diff != "" {
  1092  						t.Errorf("difference:\n%s", diff)
  1093  						t.Errorf("Values are not equal.\ngot: %#v\nwant:%#v", got, want)
  1094  					}
  1095  				})
  1096  			}
  1097  		})
  1098  	})
  1099  }
  1100  
  1101  type testValueMarshaler struct {
  1102  	t   bsontype.Type
  1103  	buf []byte
  1104  	err error
  1105  }
  1106  
  1107  func (tvm testValueMarshaler) MarshalBSONValue() (bsontype.Type, []byte, error) {
  1108  	return tvm.t, tvm.buf, tvm.err
  1109  }
  1110  
  1111  type testValueUnmarshaler struct {
  1112  	t   bsontype.Type
  1113  	val []byte
  1114  	err error
  1115  }
  1116  
  1117  func (tvu *testValueUnmarshaler) UnmarshalBSONValue(t bsontype.Type, val []byte) error {
  1118  	tvu.t, tvu.val = t, val
  1119  	return tvu.err
  1120  }
  1121  func (tvu testValueUnmarshaler) Equal(tvu2 testValueUnmarshaler) bool {
  1122  	return tvu.t == tvu2.t && bytes.Equal(tvu.val, tvu2.val)
  1123  }
  1124  
  1125  type noPrivateFields struct {
  1126  	a string
  1127  }
  1128  
  1129  func compareNoPrivateFields(npf1, npf2 noPrivateFields) bool {
  1130  	return npf1.a != npf2.a // We don't want these to be equal
  1131  }
  1132  
  1133  type zeroTest struct {
  1134  	reportZero bool
  1135  }
  1136  
  1137  func (z zeroTest) IsZero() bool { return z.reportZero }
  1138  
  1139  func compareZeroTest(_, _ zeroTest) bool { return true }
  1140  

View as plain text