...

Source file src/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore_test.go

Documentation: go.mongodb.org/mongo-driver/x/bsonx/bsoncore

     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 bsoncore
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/binary"
    12  	"math"
    13  	"reflect"
    14  	"testing"
    15  
    16  	"github.com/google/go-cmp/cmp"
    17  
    18  	"go.mongodb.org/mongo-driver/bson/bsontype"
    19  	"go.mongodb.org/mongo-driver/bson/primitive"
    20  	"go.mongodb.org/mongo-driver/internal/assert"
    21  )
    22  
    23  func compareErrors(err1, err2 error) bool {
    24  	if err1 == nil && err2 == nil {
    25  		return true
    26  	}
    27  
    28  	if err1 == nil || err2 == nil {
    29  		return false
    30  	}
    31  
    32  	if err1.Error() != err2.Error() {
    33  		return false
    34  	}
    35  
    36  	return true
    37  }
    38  
    39  func TestAppend(t *testing.T) {
    40  	bits := math.Float64bits(3.14159)
    41  	pi := make([]byte, 8)
    42  	binary.LittleEndian.PutUint64(pi, bits)
    43  
    44  	testCases := []struct {
    45  		name     string
    46  		fn       interface{}
    47  		params   []interface{}
    48  		expected []byte
    49  	}{
    50  		{
    51  			"AppendType",
    52  			AppendType,
    53  			[]interface{}{make([]byte, 0), bsontype.Null},
    54  			[]byte{byte(bsontype.Null)},
    55  		},
    56  		{
    57  			"AppendKey",
    58  			AppendKey,
    59  			[]interface{}{make([]byte, 0), "foobar"},
    60  			[]byte{'f', 'o', 'o', 'b', 'a', 'r', 0x00},
    61  		},
    62  		{
    63  			"AppendHeader",
    64  			AppendHeader,
    65  			[]interface{}{make([]byte, 0), bsontype.Null, "foobar"},
    66  			[]byte{byte(bsontype.Null), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
    67  		},
    68  		{
    69  			"AppendValueElement",
    70  			AppendValueElement,
    71  			[]interface{}{make([]byte, 0), "testing", Value{Type: bsontype.Boolean, Data: []byte{0x01}}},
    72  			[]byte{byte(bsontype.Boolean), 't', 'e', 's', 't', 'i', 'n', 'g', 0x00, 0x01},
    73  		},
    74  		{
    75  			"AppendDouble",
    76  			AppendDouble,
    77  			[]interface{}{make([]byte, 0), float64(3.14159)},
    78  			pi,
    79  		},
    80  		{
    81  			"AppendDoubleElement",
    82  			AppendDoubleElement,
    83  			[]interface{}{make([]byte, 0), "foobar", float64(3.14159)},
    84  			append([]byte{byte(bsontype.Double), 'f', 'o', 'o', 'b', 'a', 'r', 0x00}, pi...),
    85  		},
    86  		{
    87  			"AppendString",
    88  			AppendString,
    89  			[]interface{}{make([]byte, 0), "barbaz"},
    90  			[]byte{0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00},
    91  		},
    92  		{
    93  			"AppendStringElement",
    94  			AppendStringElement,
    95  			[]interface{}{make([]byte, 0), "foobar", "barbaz"},
    96  			[]byte{byte(bsontype.String),
    97  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
    98  				0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,
    99  			},
   100  		},
   101  		{
   102  			"AppendDocument",
   103  			AppendDocument,
   104  			[]interface{}{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}, []byte{0x05, 0x00, 0x00, 0x00, 0x00}},
   105  			[]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00},
   106  		},
   107  		{
   108  			"AppendDocumentElement",
   109  			AppendDocumentElement,
   110  			[]interface{}{make([]byte, 0), "foobar", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},
   111  			[]byte{byte(bsontype.EmbeddedDocument),
   112  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   113  				0x05, 0x00, 0x00, 0x00, 0x00,
   114  			},
   115  		},
   116  		{
   117  			"AppendArray",
   118  			AppendArray,
   119  			[]interface{}{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}, []byte{0x05, 0x00, 0x00, 0x00, 0x00}},
   120  			[]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00},
   121  		},
   122  		{
   123  			"AppendArrayElement",
   124  			AppendArrayElement,
   125  			[]interface{}{make([]byte, 0), "foobar", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},
   126  			[]byte{byte(bsontype.Array),
   127  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   128  				0x05, 0x00, 0x00, 0x00, 0x00,
   129  			},
   130  		},
   131  		{
   132  			"BuildArray",
   133  			BuildArray,
   134  			[]interface{}{make([]byte, 0), Value{Type: bsontype.Double, Data: AppendDouble(nil, 3.14159)}},
   135  			[]byte{
   136  				0x10, 0x00, 0x00, 0x00,
   137  				byte(bsontype.Double), '0', 0x00,
   138  				pi[0], pi[1], pi[2], pi[3], pi[4], pi[5], pi[6], pi[7],
   139  				0x00,
   140  			},
   141  		},
   142  		{
   143  			"BuildArrayElement",
   144  			BuildArrayElement,
   145  			[]interface{}{make([]byte, 0), "foobar", Value{Type: bsontype.Double, Data: AppendDouble(nil, 3.14159)}},
   146  			[]byte{byte(bsontype.Array),
   147  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   148  				0x10, 0x00, 0x00, 0x00,
   149  				byte(bsontype.Double), '0', 0x00,
   150  				pi[0], pi[1], pi[2], pi[3], pi[4], pi[5], pi[6], pi[7],
   151  				0x00,
   152  			},
   153  		},
   154  		{
   155  			"AppendBinary Subtype2",
   156  			AppendBinary,
   157  			[]interface{}{make([]byte, 0), byte(0x02), []byte{0x01, 0x02, 0x03}},
   158  			[]byte{0x07, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
   159  		},
   160  		{
   161  			"AppendBinaryElement Subtype 2",
   162  			AppendBinaryElement,
   163  			[]interface{}{make([]byte, 0), "foobar", byte(0x02), []byte{0x01, 0x02, 0x03}},
   164  			[]byte{byte(bsontype.Binary),
   165  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   166  				0x07, 0x00, 0x00, 0x00,
   167  				0x02,
   168  				0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
   169  			},
   170  		},
   171  		{
   172  			"AppendBinary",
   173  			AppendBinary,
   174  			[]interface{}{make([]byte, 0), byte(0xFF), []byte{0x01, 0x02, 0x03}},
   175  			[]byte{0x03, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x02, 0x03},
   176  		},
   177  		{
   178  			"AppendBinaryElement",
   179  			AppendBinaryElement,
   180  			[]interface{}{make([]byte, 0), "foobar", byte(0xFF), []byte{0x01, 0x02, 0x03}},
   181  			[]byte{byte(bsontype.Binary),
   182  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   183  				0x03, 0x00, 0x00, 0x00,
   184  				0xFF,
   185  				0x01, 0x02, 0x03,
   186  			},
   187  		},
   188  		{
   189  			"AppendUndefinedElement",
   190  			AppendUndefinedElement,
   191  			[]interface{}{make([]byte, 0), "foobar"},
   192  			[]byte{byte(bsontype.Undefined), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   193  		},
   194  		{
   195  			"AppendObjectID",
   196  			AppendObjectID,
   197  			[]interface{}{
   198  				make([]byte, 0),
   199  				primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   200  			},
   201  			[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   202  		},
   203  		{
   204  			"AppendObjectIDElement",
   205  			AppendObjectIDElement,
   206  			[]interface{}{
   207  				make([]byte, 0), "foobar",
   208  				primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   209  			},
   210  			[]byte{byte(bsontype.ObjectID),
   211  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   212  				0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
   213  			},
   214  		},
   215  		{
   216  			"AppendBoolean (true)",
   217  			AppendBoolean,
   218  			[]interface{}{make([]byte, 0), true},
   219  			[]byte{0x01},
   220  		},
   221  		{
   222  			"AppendBoolean (false)",
   223  			AppendBoolean,
   224  			[]interface{}{make([]byte, 0), false},
   225  			[]byte{0x00},
   226  		},
   227  		{
   228  			"AppendBooleanElement",
   229  			AppendBooleanElement,
   230  			[]interface{}{make([]byte, 0), "foobar", true},
   231  			[]byte{byte(bsontype.Boolean), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x01},
   232  		},
   233  		{
   234  			"AppendDateTime",
   235  			AppendDateTime,
   236  			[]interface{}{make([]byte, 0), int64(256)},
   237  			[]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
   238  		},
   239  		{
   240  			"AppendDateTimeElement",
   241  			AppendDateTimeElement,
   242  			[]interface{}{make([]byte, 0), "foobar", int64(256)},
   243  			[]byte{byte(bsontype.DateTime), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
   244  		},
   245  		{
   246  			"AppendNullElement",
   247  			AppendNullElement,
   248  			[]interface{}{make([]byte, 0), "foobar"},
   249  			[]byte{byte(bsontype.Null), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   250  		},
   251  		{
   252  			"AppendRegex",
   253  			AppendRegex,
   254  			[]interface{}{make([]byte, 0), "bar", "baz"},
   255  			[]byte{'b', 'a', 'r', 0x00, 'b', 'a', 'z', 0x00},
   256  		},
   257  		{
   258  			"AppendRegexElement",
   259  			AppendRegexElement,
   260  			[]interface{}{make([]byte, 0), "foobar", "bar", "baz"},
   261  			[]byte{byte(bsontype.Regex),
   262  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   263  				'b', 'a', 'r', 0x00, 'b', 'a', 'z', 0x00,
   264  			},
   265  		},
   266  		{
   267  			"AppendDBPointer",
   268  			AppendDBPointer,
   269  			[]interface{}{
   270  				make([]byte, 0),
   271  				"foobar",
   272  				primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   273  			},
   274  			[]byte{
   275  				0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   276  				0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
   277  			},
   278  		},
   279  		{
   280  			"AppendDBPointerElement",
   281  			AppendDBPointerElement,
   282  			[]interface{}{
   283  				make([]byte, 0), "foobar",
   284  				"barbaz",
   285  				primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   286  			},
   287  			[]byte{byte(bsontype.DBPointer),
   288  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   289  				0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,
   290  				0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
   291  			},
   292  		},
   293  		{
   294  			"AppendJavaScript",
   295  			AppendJavaScript,
   296  			[]interface{}{make([]byte, 0), "barbaz"},
   297  			[]byte{0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00},
   298  		},
   299  		{
   300  			"AppendJavaScriptElement",
   301  			AppendJavaScriptElement,
   302  			[]interface{}{make([]byte, 0), "foobar", "barbaz"},
   303  			[]byte{byte(bsontype.JavaScript),
   304  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   305  				0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,
   306  			},
   307  		},
   308  		{
   309  			"AppendSymbol",
   310  			AppendSymbol,
   311  			[]interface{}{make([]byte, 0), "barbaz"},
   312  			[]byte{0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00},
   313  		},
   314  		{
   315  			"AppendSymbolElement",
   316  			AppendSymbolElement,
   317  			[]interface{}{make([]byte, 0), "foobar", "barbaz"},
   318  			[]byte{byte(bsontype.Symbol),
   319  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   320  				0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,
   321  			},
   322  		},
   323  		{
   324  			"AppendCodeWithScope",
   325  			AppendCodeWithScope,
   326  			[]interface{}{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}, "foobar", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},
   327  			[]byte{0x05, 0x00, 0x00, 0x00, 0x00,
   328  				0x14, 0x00, 0x00, 0x00,
   329  				0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   330  				0x05, 0x00, 0x00, 0x00, 0x00,
   331  			},
   332  		},
   333  		{
   334  			"AppendCodeWithScopeElement",
   335  			AppendCodeWithScopeElement,
   336  			[]interface{}{make([]byte, 0), "foobar", "barbaz", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},
   337  			[]byte{byte(bsontype.CodeWithScope),
   338  				'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   339  				0x14, 0x00, 0x00, 0x00,
   340  				0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,
   341  				0x05, 0x00, 0x00, 0x00, 0x00,
   342  			},
   343  		},
   344  		{
   345  			"AppendInt32",
   346  			AppendInt32,
   347  			[]interface{}{make([]byte, 0), int32(256)},
   348  			[]byte{0x00, 0x01, 0x00, 0x00},
   349  		},
   350  		{
   351  			"AppendInt32Element",
   352  			AppendInt32Element,
   353  			[]interface{}{make([]byte, 0), "foobar", int32(256)},
   354  			[]byte{byte(bsontype.Int32), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x01, 0x00, 0x00},
   355  		},
   356  		{
   357  			"AppendTimestamp",
   358  			AppendTimestamp,
   359  			[]interface{}{make([]byte, 0), uint32(65536), uint32(256)},
   360  			[]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
   361  		},
   362  		{
   363  			"AppendTimestampElement",
   364  			AppendTimestampElement,
   365  			[]interface{}{make([]byte, 0), "foobar", uint32(65536), uint32(256)},
   366  			[]byte{byte(bsontype.Timestamp), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
   367  		},
   368  		{
   369  			"AppendInt64",
   370  			AppendInt64,
   371  			[]interface{}{make([]byte, 0), int64(4294967296)},
   372  			[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
   373  		},
   374  		{
   375  			"AppendInt64Element",
   376  			AppendInt64Element,
   377  			[]interface{}{make([]byte, 0), "foobar", int64(4294967296)},
   378  			[]byte{byte(bsontype.Int64), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
   379  		},
   380  		{
   381  			"AppendDecimal128",
   382  			AppendDecimal128,
   383  			[]interface{}{make([]byte, 0), primitive.NewDecimal128(4294967296, 65536)},
   384  			[]byte{
   385  				0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
   386  				0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   387  			},
   388  		},
   389  		{
   390  			"AppendDecimal128Element",
   391  			AppendDecimal128Element,
   392  			[]interface{}{make([]byte, 0), "foobar", primitive.NewDecimal128(4294967296, 65536)},
   393  			[]byte{
   394  				byte(bsontype.Decimal128), 'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   395  				0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
   396  				0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   397  			},
   398  		},
   399  		{
   400  			"AppendMaxKeyElement",
   401  			AppendMaxKeyElement,
   402  			[]interface{}{make([]byte, 0), "foobar"},
   403  			[]byte{byte(bsontype.MaxKey), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   404  		},
   405  		{
   406  			"AppendMinKeyElement",
   407  			AppendMinKeyElement,
   408  			[]interface{}{make([]byte, 0), "foobar"},
   409  			[]byte{byte(bsontype.MinKey), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   410  		},
   411  	}
   412  
   413  	for _, tc := range testCases {
   414  		t.Run(tc.name, func(t *testing.T) {
   415  			fn := reflect.ValueOf(tc.fn)
   416  			if fn.Kind() != reflect.Func {
   417  				t.Fatalf("fn must be of kind Func but is a %v", fn.Kind())
   418  			}
   419  			if fn.Type().NumIn() != len(tc.params) {
   420  				t.Fatalf("tc.params must match the number of params in tc.fn. params %d; fn %d", fn.Type().NumIn(), len(tc.params))
   421  			}
   422  			if fn.Type().NumOut() != 1 || fn.Type().Out(0) != reflect.TypeOf([]byte{}) {
   423  				t.Fatalf("fn must have one return parameter and it must be a []byte.")
   424  			}
   425  			params := make([]reflect.Value, 0, len(tc.params))
   426  			for _, param := range tc.params {
   427  				params = append(params, reflect.ValueOf(param))
   428  			}
   429  			results := fn.Call(params)
   430  			got := results[0].Interface().([]byte)
   431  			want := tc.expected
   432  			if !bytes.Equal(got, want) {
   433  				t.Errorf("Did not receive expected bytes. got %v; want %v", got, want)
   434  			}
   435  		})
   436  	}
   437  }
   438  
   439  func TestRead(t *testing.T) {
   440  	bits := math.Float64bits(3.14159)
   441  	pi := make([]byte, 8)
   442  	binary.LittleEndian.PutUint64(pi, bits)
   443  
   444  	testCases := []struct {
   445  		name     string
   446  		fn       interface{}
   447  		param    []byte
   448  		expected []interface{}
   449  	}{
   450  		{
   451  			"ReadType/not enough bytes",
   452  			ReadType,
   453  			[]byte{},
   454  			[]interface{}{bsontype.Type(0), []byte{}, false},
   455  		},
   456  		{
   457  			"ReadType/success",
   458  			ReadType,
   459  			[]byte{0x0A},
   460  			[]interface{}{bsontype.Null, []byte{}, true},
   461  		},
   462  		{
   463  			"ReadKey/not enough bytes",
   464  			ReadKey,
   465  			[]byte{},
   466  			[]interface{}{"", []byte{}, false},
   467  		},
   468  		{
   469  			"ReadKey/success",
   470  			ReadKey,
   471  			[]byte{'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   472  			[]interface{}{"foobar", []byte{}, true},
   473  		},
   474  		{
   475  			"ReadHeader/not enough bytes (type)",
   476  			ReadHeader,
   477  			[]byte{},
   478  			[]interface{}{bsontype.Type(0), "", []byte{}, false},
   479  		},
   480  		{
   481  			"ReadHeader/not enough bytes (key)",
   482  			ReadHeader,
   483  			[]byte{0x0A, 'f', 'o', 'o'},
   484  			[]interface{}{bsontype.Type(0), "", []byte{0x0A, 'f', 'o', 'o'}, false},
   485  		},
   486  		{
   487  			"ReadHeader/success",
   488  			ReadHeader,
   489  			[]byte{0x0A, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   490  			[]interface{}{bsontype.Null, "foobar", []byte{}, true},
   491  		},
   492  		{
   493  			"ReadDouble/not enough bytes",
   494  			ReadDouble,
   495  			[]byte{0x01, 0x02, 0x03, 0x04},
   496  			[]interface{}{float64(0.00), []byte{0x01, 0x02, 0x03, 0x04}, false},
   497  		},
   498  		{
   499  			"ReadDouble/success",
   500  			ReadDouble,
   501  			pi,
   502  			[]interface{}{float64(3.14159), []byte{}, true},
   503  		},
   504  		{
   505  			"ReadString/not enough bytes (length)",
   506  			ReadString,
   507  			[]byte{},
   508  			[]interface{}{"", []byte{}, false},
   509  		},
   510  		{
   511  			"ReadString/not enough bytes (value)",
   512  			ReadString,
   513  			[]byte{0x0F, 0x00, 0x00, 0x00},
   514  			[]interface{}{"", []byte{0x0F, 0x00, 0x00, 0x00}, false},
   515  		},
   516  		{
   517  			"ReadString/success",
   518  			ReadString,
   519  			[]byte{0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   520  			[]interface{}{"foobar", []byte{}, true},
   521  		},
   522  		{
   523  			"ReadDocument/not enough bytes (length)",
   524  			ReadDocument,
   525  			[]byte{},
   526  			[]interface{}{Document(nil), []byte{}, false},
   527  		},
   528  		{
   529  			"ReadDocument/not enough bytes (value)",
   530  			ReadDocument,
   531  			[]byte{0x0F, 0x00, 0x00, 0x00},
   532  			[]interface{}{Document(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},
   533  		},
   534  		{
   535  			"ReadDocument/success",
   536  			ReadDocument,
   537  			[]byte{0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00},
   538  			[]interface{}{Document{0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00}, []byte{}, true},
   539  		},
   540  		{
   541  			"ReadArray/not enough bytes (length)",
   542  			ReadArray,
   543  			[]byte{},
   544  			[]interface{}{Array(nil), []byte{}, false},
   545  		},
   546  		{
   547  			"ReadArray/not enough bytes (value)",
   548  			ReadArray,
   549  			[]byte{0x0F, 0x00, 0x00, 0x00},
   550  			[]interface{}{Array(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},
   551  		},
   552  		{
   553  			"ReadArray/success",
   554  			ReadArray,
   555  			[]byte{0x08, 0x00, 0x00, 0x00, 0x0A, '0', 0x00, 0x00},
   556  			[]interface{}{Array{0x08, 0x00, 0x00, 0x00, 0x0A, '0', 0x00, 0x00}, []byte{}, true},
   557  		},
   558  		{
   559  			"ReadBinary/not enough bytes (length)",
   560  			ReadBinary,
   561  			[]byte{},
   562  			[]interface{}{byte(0), []byte(nil), []byte{}, false},
   563  		},
   564  		{
   565  			"ReadBinary/not enough bytes (subtype)",
   566  			ReadBinary,
   567  			[]byte{0x0F, 0x00, 0x00, 0x00},
   568  			[]interface{}{byte(0), []byte(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},
   569  		},
   570  		{
   571  			"ReadBinary/not enough bytes (value)",
   572  			ReadBinary,
   573  			[]byte{0x0F, 0x00, 0x00, 0x00, 0x00},
   574  			[]interface{}{byte(0), []byte(nil), []byte{0x0F, 0x00, 0x00, 0x00, 0x00}, false},
   575  		},
   576  		{
   577  			"ReadBinary/not enough bytes (subtype 2 length)",
   578  			ReadBinary,
   579  			[]byte{0x03, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00},
   580  			[]interface{}{byte(0), []byte(nil), []byte{0x03, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00}, false},
   581  		},
   582  		{
   583  			"ReadBinary/not enough bytes (subtype 2 value)",
   584  			ReadBinary,
   585  			[]byte{0x0F, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x02},
   586  			[]interface{}{
   587  				byte(0), []byte(nil),
   588  				[]byte{0x0F, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x02}, false,
   589  			},
   590  		},
   591  		{
   592  			"ReadBinary/success (subtype 2)",
   593  			ReadBinary,
   594  			[]byte{0x06, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02},
   595  			[]interface{}{byte(0x02), []byte{0x01, 0x02}, []byte{}, true},
   596  		},
   597  		{
   598  			"ReadBinary/success",
   599  			ReadBinary,
   600  			[]byte{0x03, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x02, 0x03},
   601  			[]interface{}{byte(0xFF), []byte{0x01, 0x02, 0x03}, []byte{}, true},
   602  		},
   603  		{
   604  			"ReadObjectID/not enough bytes",
   605  			ReadObjectID,
   606  			[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
   607  			[]interface{}{primitive.ObjectID{}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, false},
   608  		},
   609  		{
   610  			"ReadObjectID/success",
   611  			ReadObjectID,
   612  			[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   613  			[]interface{}{
   614  				primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   615  				[]byte{}, true,
   616  			},
   617  		},
   618  		{
   619  			"ReadBoolean/not enough bytes",
   620  			ReadBoolean,
   621  			[]byte{},
   622  			[]interface{}{false, []byte{}, false},
   623  		},
   624  		{
   625  			"ReadBoolean/success",
   626  			ReadBoolean,
   627  			[]byte{0x01},
   628  			[]interface{}{true, []byte{}, true},
   629  		},
   630  		{
   631  			"ReadDateTime/not enough bytes",
   632  			ReadDateTime,
   633  			[]byte{0x01, 0x02, 0x03, 0x04},
   634  			[]interface{}{int64(0), []byte{0x01, 0x02, 0x03, 0x04}, false},
   635  		},
   636  		{
   637  			"ReadDateTime/success",
   638  			ReadDateTime,
   639  			[]byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
   640  			[]interface{}{int64(65536), []byte{}, true},
   641  		},
   642  		{
   643  			"ReadRegex/not enough bytes (pattern)",
   644  			ReadRegex,
   645  			[]byte{},
   646  			[]interface{}{"", "", []byte{}, false},
   647  		},
   648  		{
   649  			"ReadRegex/not enough bytes (options)",
   650  			ReadRegex,
   651  			[]byte{'f', 'o', 'o', 0x00},
   652  			[]interface{}{"", "", []byte{'f', 'o', 'o', 0x00}, false},
   653  		},
   654  		{
   655  			"ReadRegex/success",
   656  			ReadRegex,
   657  			[]byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r', 0x00},
   658  			[]interface{}{"foo", "bar", []byte{}, true},
   659  		},
   660  		{
   661  			"ReadDBPointer/not enough bytes (ns)",
   662  			ReadDBPointer,
   663  			[]byte{},
   664  			[]interface{}{"", primitive.ObjectID{}, []byte{}, false},
   665  		},
   666  		{
   667  			"ReadDBPointer/not enough bytes (objectID)",
   668  			ReadDBPointer,
   669  			[]byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},
   670  			[]interface{}{"", primitive.ObjectID{}, []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}, false},
   671  		},
   672  		{
   673  			"ReadDBPointer/success",
   674  			ReadDBPointer,
   675  			[]byte{
   676  				0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00,
   677  				0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
   678  			},
   679  			[]interface{}{
   680  				"foo", primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
   681  				[]byte{}, true,
   682  			},
   683  		},
   684  		{
   685  			"ReadJavaScript/not enough bytes (length)",
   686  			ReadJavaScript,
   687  			[]byte{},
   688  			[]interface{}{"", []byte{}, false},
   689  		},
   690  		{
   691  			"ReadJavaScript/not enough bytes (value)",
   692  			ReadJavaScript,
   693  			[]byte{0x0F, 0x00, 0x00, 0x00},
   694  			[]interface{}{"", []byte{0x0F, 0x00, 0x00, 0x00}, false},
   695  		},
   696  		{
   697  			"ReadJavaScript/success",
   698  			ReadJavaScript,
   699  			[]byte{0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   700  			[]interface{}{"foobar", []byte{}, true},
   701  		},
   702  		{
   703  			"ReadSymbol/not enough bytes (length)",
   704  			ReadSymbol,
   705  			[]byte{},
   706  			[]interface{}{"", []byte{}, false},
   707  		},
   708  		{
   709  			"ReadSymbol/not enough bytes (value)",
   710  			ReadSymbol,
   711  			[]byte{0x0F, 0x00, 0x00, 0x00},
   712  			[]interface{}{"", []byte{0x0F, 0x00, 0x00, 0x00}, false},
   713  		},
   714  		{
   715  			"ReadSymbol/success",
   716  			ReadSymbol,
   717  			[]byte{0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},
   718  			[]interface{}{"foobar", []byte{}, true},
   719  		},
   720  		{
   721  			"ReadCodeWithScope/ not enough bytes (length)",
   722  			ReadCodeWithScope,
   723  			[]byte{},
   724  			[]interface{}{"", []byte(nil), []byte{}, false},
   725  		},
   726  		{
   727  			"ReadCodeWithScope/ not enough bytes (value)",
   728  			ReadCodeWithScope,
   729  			[]byte{0x0F, 0x00, 0x00, 0x00},
   730  			[]interface{}{"", []byte(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},
   731  		},
   732  		{
   733  			"ReadCodeWithScope/not enough bytes (code value)",
   734  			ReadCodeWithScope,
   735  			[]byte{
   736  				0x0C, 0x00, 0x00, 0x00,
   737  				0x0F, 0x00, 0x00, 0x00,
   738  				'f', 'o', 'o', 0x00,
   739  			},
   740  			[]interface{}{
   741  				"", []byte(nil),
   742  				[]byte{
   743  					0x0C, 0x00, 0x00, 0x00,
   744  					0x0F, 0x00, 0x00, 0x00,
   745  					'f', 'o', 'o', 0x00,
   746  				},
   747  				false,
   748  			},
   749  		},
   750  		{
   751  			"ReadCodeWithScope/success",
   752  			ReadCodeWithScope,
   753  			[]byte{
   754  				0x19, 0x00, 0x00, 0x00,
   755  				0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00,
   756  				0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00,
   757  			},
   758  			[]interface{}{
   759  				"foobar", []byte{0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00},
   760  				[]byte{}, true,
   761  			},
   762  		},
   763  		{
   764  			"ReadInt32/not enough bytes",
   765  			ReadInt32,
   766  			[]byte{0x01},
   767  			[]interface{}{int32(0), []byte{0x01}, false},
   768  		},
   769  		{
   770  			"ReadInt32/success",
   771  			ReadInt32,
   772  			[]byte{0x00, 0x01, 0x00, 0x00},
   773  			[]interface{}{int32(256), []byte{}, true},
   774  		},
   775  		{
   776  			"ReadTimestamp/not enough bytes (increment)",
   777  			ReadTimestamp,
   778  			[]byte{},
   779  			[]interface{}{uint32(0), uint32(0), []byte{}, false},
   780  		},
   781  		{
   782  			"ReadTimestamp/not enough bytes (timestamp)",
   783  			ReadTimestamp,
   784  			[]byte{0x00, 0x01, 0x00, 0x00},
   785  			[]interface{}{uint32(0), uint32(0), []byte{0x00, 0x01, 0x00, 0x00}, false},
   786  		},
   787  		{
   788  			"ReadTimestamp/success",
   789  			ReadTimestamp,
   790  			[]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
   791  			[]interface{}{uint32(65536), uint32(256), []byte{}, true},
   792  		},
   793  		{
   794  			"ReadInt64/not enough bytes",
   795  			ReadInt64,
   796  			[]byte{0x01},
   797  			[]interface{}{int64(0), []byte{0x01}, false},
   798  		},
   799  		{
   800  			"ReadInt64/success",
   801  			ReadInt64,
   802  			[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
   803  			[]interface{}{int64(4294967296), []byte{}, true},
   804  		},
   805  		{
   806  			"ReadDecimal128/not enough bytes (low)",
   807  			ReadDecimal128,
   808  			[]byte{},
   809  			[]interface{}{primitive.Decimal128{}, []byte{}, false},
   810  		},
   811  		{
   812  			"ReadDecimal128/not enough bytes (high)",
   813  			ReadDecimal128,
   814  			[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
   815  			[]interface{}{primitive.Decimal128{}, []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, false},
   816  		},
   817  		{
   818  			"ReadDecimal128/success",
   819  			ReadDecimal128,
   820  			[]byte{
   821  				0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
   822  				0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   823  			},
   824  			[]interface{}{primitive.NewDecimal128(4294967296, 16777216), []byte{}, true},
   825  		},
   826  	}
   827  
   828  	for _, tc := range testCases {
   829  		t.Run(tc.name, func(t *testing.T) {
   830  			fn := reflect.ValueOf(tc.fn)
   831  			if fn.Kind() != reflect.Func {
   832  				t.Fatalf("fn must be of kind Func but it is a %v", fn.Kind())
   833  			}
   834  			if fn.Type().NumIn() != 1 || fn.Type().In(0) != reflect.TypeOf([]byte{}) {
   835  				t.Fatalf("fn must have one parameter and it must be a []byte.")
   836  			}
   837  			results := fn.Call([]reflect.Value{reflect.ValueOf(tc.param)})
   838  			if len(results) != len(tc.expected) {
   839  				t.Fatalf("Length of results does not match. got %d; want %d", len(results), len(tc.expected))
   840  			}
   841  			for idx := range results {
   842  				got := results[idx].Interface()
   843  				want := tc.expected[idx]
   844  				if !cmp.Equal(got, want, cmp.Comparer(compareDecimal128)) {
   845  					t.Errorf("Result %d does not match. got %v; want %v", idx, got, want)
   846  				}
   847  			}
   848  		})
   849  	}
   850  }
   851  
   852  func TestBuild(t *testing.T) {
   853  	testCases := []struct {
   854  		name  string
   855  		elems [][]byte
   856  		want  []byte
   857  	}{
   858  		{
   859  			"one element",
   860  			[][]byte{AppendDoubleElement(nil, "pi", 3.14159)},
   861  			[]byte{0x11, 0x00, 0x00, 0x00, 0x1, 0x70, 0x69, 0x00, 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x9, 0x40, 0x00},
   862  		},
   863  		{
   864  			"two elements",
   865  			[][]byte{AppendDoubleElement(nil, "pi", 3.14159), AppendStringElement(nil, "hello", "world!!")},
   866  			[]byte{
   867  				0x24, 0x00, 0x00, 0x00, 0x01, 0x70, 0x69, 0x00, 0x6e, 0x86, 0x1b, 0xf0,
   868  				0xf9, 0x21, 0x09, 0x40, 0x02, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x08,
   869  				0x00, 0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x21, 0x00, 0x00,
   870  			},
   871  		},
   872  	}
   873  
   874  	for _, tc := range testCases {
   875  		t.Run(tc.name, func(t *testing.T) {
   876  			t.Run("BuildDocument", func(t *testing.T) {
   877  				elems := make([]byte, 0)
   878  				for _, elem := range tc.elems {
   879  					elems = append(elems, elem...)
   880  				}
   881  				got := BuildDocument(nil, elems)
   882  				if !bytes.Equal(got, tc.want) {
   883  					t.Errorf("Documents do not match. got %v; want %v", got, tc.want)
   884  				}
   885  			})
   886  			t.Run("BuildDocumentFromElements", func(t *testing.T) {
   887  				got := BuildDocumentFromElements(nil, tc.elems...)
   888  				if !bytes.Equal(got, tc.want) {
   889  					t.Errorf("Documents do not match. got %v; want %v", got, tc.want)
   890  				}
   891  			})
   892  		})
   893  	}
   894  }
   895  
   896  func TestNullBytes(t *testing.T) {
   897  	// Helper function to execute the provided callback and assert that it panics with the expected message. The
   898  	// createBSONFn callback should create a BSON document/array/value and return the stringified version.
   899  	assertBSONCreationPanics := func(t *testing.T, createBSONFn func(), expected string) {
   900  		t.Helper()
   901  
   902  		defer func() {
   903  			got := recover()
   904  			assert.Equal(t, expected, got, "expected panic with error %v, got error %v", expected, got)
   905  		}()
   906  		createBSONFn()
   907  	}
   908  
   909  	t.Run("element keys", func(t *testing.T) {
   910  		createDocFn := func() {
   911  			NewDocumentBuilder().AppendString("a\x00", "foo")
   912  		}
   913  		assertBSONCreationPanics(t, createDocFn, invalidKeyPanicMsg)
   914  	})
   915  	t.Run("regex values", func(t *testing.T) {
   916  		testCases := []struct {
   917  			name    string
   918  			pattern string
   919  			options string
   920  		}{
   921  			{"null bytes in pattern", "a\x00", "i"},
   922  			{"null bytes in options", "pattern", "i\x00"},
   923  		}
   924  		for _, tc := range testCases {
   925  			t.Run(tc.name+"-AppendRegexElement", func(t *testing.T) {
   926  				createDocFn := func() {
   927  					AppendRegexElement(nil, "foo", tc.pattern, tc.options)
   928  				}
   929  				assertBSONCreationPanics(t, createDocFn, invalidRegexPanicMsg)
   930  			})
   931  			t.Run(tc.name+"-AppendRegex", func(t *testing.T) {
   932  				createValFn := func() {
   933  					AppendRegex(nil, tc.pattern, tc.options)
   934  				}
   935  				assertBSONCreationPanics(t, createValFn, invalidRegexPanicMsg)
   936  			})
   937  		}
   938  	})
   939  	t.Run("sub document field name", func(t *testing.T) {
   940  		createDocFn := func() {
   941  			NewDocumentBuilder().StartDocument("foobar").AppendDocument("a\x00", []byte("foo")).FinishDocument()
   942  		}
   943  		assertBSONCreationPanics(t, createDocFn, invalidKeyPanicMsg)
   944  	})
   945  }
   946  
   947  func TestInvalidBytes(t *testing.T) {
   948  	t.Parallel()
   949  
   950  	t.Run("read length less than 4 int bytes", func(t *testing.T) {
   951  		t.Parallel()
   952  
   953  		_, src, ok := readLengthBytes([]byte{0x01, 0x00, 0x00, 0x00})
   954  		assert.False(t, ok, "expected not ok response for invalid length read")
   955  		assert.Equal(t, 4, len(src), "expected src to contain the size parameter still")
   956  	})
   957  }
   958  
   959  func compareDecimal128(d1, d2 primitive.Decimal128) bool {
   960  	d1H, d1L := d1.GetBytes()
   961  	d2H, d2L := d2.GetBytes()
   962  
   963  	if d1H != d2H {
   964  		return false
   965  	}
   966  
   967  	if d1L != d2L {
   968  		return false
   969  	}
   970  
   971  	return true
   972  }
   973  

View as plain text