...

Source file src/go.mongodb.org/mongo-driver/bson/raw_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/binary"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/google/go-cmp/cmp"
    19  	"go.mongodb.org/mongo-driver/bson/bsontype"
    20  	"go.mongodb.org/mongo-driver/internal/require"
    21  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    22  )
    23  
    24  func ExampleRaw_Validate() {
    25  	rdr := make(Raw, 500)
    26  	rdr[250], rdr[251], rdr[252], rdr[253], rdr[254] = '\x05', '\x00', '\x00', '\x00', '\x00'
    27  	err := rdr[250:].Validate()
    28  	fmt.Println(err)
    29  
    30  	// Output: <nil>
    31  }
    32  
    33  func BenchmarkRawValidate(b *testing.B) {
    34  	for i := 0; i < b.N; i++ {
    35  		rdr := make(Raw, 500)
    36  		rdr[250], rdr[251], rdr[252], rdr[253], rdr[254] = '\x05', '\x00', '\x00', '\x00', '\x00'
    37  		_ = rdr[250:].Validate()
    38  	}
    39  
    40  }
    41  
    42  func TestRaw(t *testing.T) {
    43  	t.Run("Validate", func(t *testing.T) {
    44  		t.Run("TooShort", func(t *testing.T) {
    45  			want := bsoncore.NewInsufficientBytesError(nil, nil)
    46  			got := Raw{'\x00', '\x00'}.Validate()
    47  			if !compareErrors(got, want) {
    48  				t.Errorf("Did not get expected error. got %v; want %v", got, want)
    49  			}
    50  		})
    51  		t.Run("InvalidLength", func(t *testing.T) {
    52  			want := bsoncore.ValidationError("document length exceeds available bytes. length=200 remainingBytes=5")
    53  			r := make(Raw, 5)
    54  			binary.LittleEndian.PutUint32(r[0:4], 200)
    55  			got := r.Validate()
    56  			if !errors.Is(got, want) {
    57  				t.Errorf("Did not get expected error. got %v; want %v", got, want)
    58  			}
    59  		})
    60  		t.Run("keyLength-error", func(t *testing.T) {
    61  			want := bsoncore.ErrMissingNull
    62  			r := make(Raw, 8)
    63  			binary.LittleEndian.PutUint32(r[0:4], 8)
    64  			r[4], r[5], r[6], r[7] = '\x02', 'f', 'o', 'o'
    65  			got := r.Validate()
    66  			if !errors.Is(got, want) {
    67  				t.Errorf("Did not get expected error. got %v; want %v", got, want)
    68  			}
    69  		})
    70  		t.Run("Missing-Null-Terminator", func(t *testing.T) {
    71  			want := bsoncore.ErrMissingNull
    72  			r := make(Raw, 9)
    73  			binary.LittleEndian.PutUint32(r[0:4], 9)
    74  			r[4], r[5], r[6], r[7], r[8] = '\x0A', 'f', 'o', 'o', '\x00'
    75  			got := r.Validate()
    76  			if !errors.Is(got, want) {
    77  				t.Errorf("Did not get expected error. got %v; want %v", got, want)
    78  			}
    79  		})
    80  		t.Run("validateValue-error", func(t *testing.T) {
    81  			want := bsoncore.ErrMissingNull
    82  			r := make(Raw, 11)
    83  			binary.LittleEndian.PutUint32(r[0:4], 11)
    84  			r[4], r[5], r[6], r[7], r[8], r[9], r[10] = '\x01', 'f', 'o', 'o', '\x00', '\x01', '\x02'
    85  			got := r.Validate()
    86  			if !compareErrors(got, want) {
    87  				t.Errorf("Did not get expected error. got %v; want %v", got, want)
    88  			}
    89  		})
    90  		testCases := []struct {
    91  			name string
    92  			r    Raw
    93  			err  error
    94  		}{
    95  			{"null", Raw{'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00'}, nil},
    96  			{"subdocument",
    97  				Raw{
    98  					'\x15', '\x00', '\x00', '\x00',
    99  					'\x03',
   100  					'f', 'o', 'o', '\x00',
   101  					'\x0B', '\x00', '\x00', '\x00', '\x0A', 'a', '\x00',
   102  					'\x0A', 'b', '\x00', '\x00', '\x00',
   103  				},
   104  				nil,
   105  			},
   106  			{"array",
   107  				Raw{
   108  					'\x15', '\x00', '\x00', '\x00',
   109  					'\x04',
   110  					'f', 'o', 'o', '\x00',
   111  					'\x0B', '\x00', '\x00', '\x00', '\x0A', '1', '\x00',
   112  					'\x0A', '2', '\x00', '\x00', '\x00',
   113  				},
   114  				nil,
   115  			},
   116  		}
   117  
   118  		for _, tc := range testCases {
   119  			t.Run(tc.name, func(t *testing.T) {
   120  				err := tc.r.Validate()
   121  				if !errors.Is(err, tc.err) {
   122  					t.Errorf("Returned error does not match. got %v; want %v", err, tc.err)
   123  				}
   124  			})
   125  		}
   126  	})
   127  	t.Run("Lookup", func(t *testing.T) {
   128  		t.Run("empty-key", func(t *testing.T) {
   129  			rdr := Raw{'\x05', '\x00', '\x00', '\x00', '\x00'}
   130  			_, err := rdr.LookupErr()
   131  			if !errors.Is(err, bsoncore.ErrEmptyKey) {
   132  				t.Errorf("Empty key lookup did not return expected result. got %v; want %v", err, bsoncore.ErrEmptyKey)
   133  			}
   134  		})
   135  		t.Run("corrupted-subdocument", func(t *testing.T) {
   136  			rdr := Raw{
   137  				'\x0D', '\x00', '\x00', '\x00',
   138  				'\x03', 'x', '\x00',
   139  				'\x06', '\x00', '\x00', '\x00',
   140  				'\x01',
   141  				'\x00',
   142  				'\x00',
   143  			}
   144  			_, err := rdr.LookupErr("x", "y")
   145  			want := bsoncore.NewInsufficientBytesError(nil, nil)
   146  			if !compareErrors(err, want) {
   147  				t.Errorf("Empty key lookup did not return expected result. got %v; want %v", err, want)
   148  			}
   149  		})
   150  		t.Run("corrupted-array", func(t *testing.T) {
   151  			rdr := Raw{
   152  				'\x0D', '\x00', '\x00', '\x00',
   153  				'\x04', 'x', '\x00',
   154  				'\x06', '\x00', '\x00', '\x00',
   155  				'\x01',
   156  				'\x00',
   157  				'\x00',
   158  			}
   159  			_, err := rdr.LookupErr("x", "y")
   160  			want := bsoncore.NewInsufficientBytesError(nil, nil)
   161  			if !compareErrors(err, want) {
   162  				t.Errorf("Empty key lookup did not return expected result. got %v; want %v", err, want)
   163  			}
   164  		})
   165  		t.Run("invalid-traversal", func(t *testing.T) {
   166  			rdr := Raw{'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00'}
   167  			_, err := rdr.LookupErr("x", "y")
   168  			want := bsoncore.InvalidDepthTraversalError{Key: "x", Type: bsontype.Null}
   169  			if !compareErrors(err, want) {
   170  				t.Errorf("Empty key lookup did not return expected result. got %v; want %v", err, want)
   171  			}
   172  		})
   173  		testCases := []struct {
   174  			name string
   175  			r    Raw
   176  			key  []string
   177  			want RawValue
   178  			err  error
   179  		}{
   180  			{"first",
   181  				Raw{
   182  					'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00',
   183  				},
   184  				[]string{"x"},
   185  				RawValue{Type: bsontype.Null}, nil,
   186  			},
   187  			{"first-second",
   188  				Raw{
   189  					'\x15', '\x00', '\x00', '\x00',
   190  					'\x03',
   191  					'f', 'o', 'o', '\x00',
   192  					'\x0B', '\x00', '\x00', '\x00', '\x0A', 'a', '\x00',
   193  					'\x0A', 'b', '\x00', '\x00', '\x00',
   194  				},
   195  				[]string{"foo", "b"},
   196  				RawValue{Type: bsontype.Null}, nil,
   197  			},
   198  			{"first-second-array",
   199  				Raw{
   200  					'\x15', '\x00', '\x00', '\x00',
   201  					'\x04',
   202  					'f', 'o', 'o', '\x00',
   203  					'\x0B', '\x00', '\x00', '\x00', '\x0A', '1', '\x00',
   204  					'\x0A', '2', '\x00', '\x00', '\x00',
   205  				},
   206  				[]string{"foo", "2"},
   207  				RawValue{Type: bsontype.Null}, nil,
   208  			},
   209  		}
   210  
   211  		for _, tc := range testCases {
   212  			t.Run(tc.name, func(t *testing.T) {
   213  				got, err := tc.r.LookupErr(tc.key...)
   214  				if !errors.Is(err, tc.err) {
   215  					t.Errorf("Returned error does not match. got %v; want %v", err, tc.err)
   216  				}
   217  				if !cmp.Equal(got, tc.want) {
   218  					t.Errorf("Returned element does not match expected element. got %v; want %v", got, tc.want)
   219  				}
   220  			})
   221  		}
   222  	})
   223  	t.Run("ElementAt", func(t *testing.T) {
   224  		t.Run("Out of bounds", func(t *testing.T) {
   225  			rdr := Raw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0}
   226  			_, err := rdr.IndexErr(3)
   227  			if !errors.Is(err, bsoncore.ErrOutOfBounds) {
   228  				t.Errorf("Out of bounds should be returned when accessing element beyond end of document. got %v; want %v", err, bsoncore.ErrOutOfBounds)
   229  			}
   230  		})
   231  		t.Run("Validation Error", func(t *testing.T) {
   232  			rdr := Raw{0x07, 0x00, 0x00, 0x00, 0x00}
   233  			_, err := rdr.IndexErr(1)
   234  			want := bsoncore.NewInsufficientBytesError(nil, nil)
   235  			if !compareErrors(err, want) {
   236  				t.Errorf("Did not receive expected error. got %v; want %v", err, want)
   237  			}
   238  		})
   239  		testCases := []struct {
   240  			name  string
   241  			rdr   Raw
   242  			index uint
   243  			want  RawElement
   244  		}{
   245  			{"first",
   246  				Raw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},
   247  				0, bsoncore.AppendNullElement(nil, "x")},
   248  			{"second",
   249  				Raw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},
   250  				1, bsoncore.AppendNullElement(nil, "y")},
   251  			{"third",
   252  				Raw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},
   253  				2, bsoncore.AppendNullElement(nil, "z")},
   254  		}
   255  
   256  		for _, tc := range testCases {
   257  			t.Run(tc.name, func(t *testing.T) {
   258  				got, err := tc.rdr.IndexErr(tc.index)
   259  				if err != nil {
   260  					t.Errorf("Unexpected error from ElementAt: %s", err)
   261  				}
   262  				if diff := cmp.Diff(got, tc.want); diff != "" {
   263  					t.Errorf("Documents differ: (-got +want)\n%s", diff)
   264  				}
   265  			})
   266  		}
   267  	})
   268  	t.Run("ReadDocument", func(t *testing.T) {
   269  		t.Parallel()
   270  		testCases := []struct {
   271  			name       string
   272  			ioReader   io.Reader
   273  			bsonReader Raw
   274  			err        error
   275  		}{
   276  			{
   277  				"nil reader",
   278  				nil,
   279  				nil,
   280  				ErrNilReader,
   281  			},
   282  			{
   283  				"premature end of reader",
   284  				bytes.NewBuffer([]byte{}),
   285  				nil,
   286  				io.EOF,
   287  			},
   288  			{
   289  				"empty document",
   290  				bytes.NewBuffer([]byte{5, 0, 0, 0, 0}),
   291  				[]byte{5, 0, 0, 0, 0},
   292  				nil,
   293  			},
   294  			{
   295  				"non-empty document",
   296  				bytes.NewBuffer([]byte{
   297  					// length
   298  					0x17, 0x0, 0x0, 0x0,
   299  
   300  					// type - string
   301  					0x2,
   302  					// key - "foo"
   303  					0x66, 0x6f, 0x6f, 0x0,
   304  					// value - string length
   305  					0x4, 0x0, 0x0, 0x0,
   306  					// value - string "bar"
   307  					0x62, 0x61, 0x72, 0x0,
   308  
   309  					// type - null
   310  					0xa,
   311  					// key - "baz"
   312  					0x62, 0x61, 0x7a, 0x0,
   313  
   314  					// null terminator
   315  					0x0,
   316  				}),
   317  				[]byte{
   318  					// length
   319  					0x17, 0x0, 0x0, 0x0,
   320  
   321  					// type - string
   322  					0x2,
   323  					// key - "foo"
   324  					0x66, 0x6f, 0x6f, 0x0,
   325  					// value - string length
   326  					0x4, 0x0, 0x0, 0x0,
   327  					// value - string "bar"
   328  					0x62, 0x61, 0x72, 0x0,
   329  
   330  					// type - null
   331  					0xa,
   332  					// key - "baz"
   333  					0x62, 0x61, 0x7a, 0x0,
   334  
   335  					// null terminator
   336  					0x0,
   337  				},
   338  				nil,
   339  			},
   340  		}
   341  
   342  		for _, tc := range testCases {
   343  			tc := tc // Capture range variable.
   344  			t.Run(tc.name, func(t *testing.T) {
   345  				t.Parallel()
   346  
   347  				reader, err := ReadDocument(tc.ioReader)
   348  				require.Equal(t, err, tc.err)
   349  				require.True(t, bytes.Equal(tc.bsonReader, reader))
   350  			})
   351  		}
   352  	})
   353  }
   354  
   355  func BenchmarkRawString(b *testing.B) {
   356  	// Create 1KiB and 128B strings to exercise the string-heavy call paths in
   357  	// the "Raw.String" method.
   358  	var buf strings.Builder
   359  	for i := 0; i < 128; i++ {
   360  		buf.WriteString("abcdefgh")
   361  	}
   362  	str1k := buf.String()
   363  	str128 := str1k[:128]
   364  
   365  	cases := []struct {
   366  		description string
   367  		value       interface{}
   368  	}{
   369  		{
   370  			description: "string",
   371  			value:       D{{Key: "key", Value: str128}},
   372  		},
   373  		{
   374  			description: "integer",
   375  			value:       D{{Key: "key", Value: int64(1234567890)}},
   376  		},
   377  		{
   378  			description: "float",
   379  			value:       D{{Key: "key", Value: float64(1234567890.123456789)}},
   380  		},
   381  		{
   382  			description: "nested document",
   383  			value: D{{
   384  				Key: "key",
   385  				Value: D{{
   386  					Key: "key",
   387  					Value: D{{
   388  						Key:   "key",
   389  						Value: str128,
   390  					}},
   391  				}},
   392  			}},
   393  		},
   394  		{
   395  			description: "array of strings",
   396  			value: D{{
   397  				Key:   "key",
   398  				Value: []string{str128, str128, str128, str128},
   399  			}},
   400  		},
   401  		{
   402  			description: "mixed struct",
   403  			value: struct {
   404  				Key1 struct {
   405  					Nested string
   406  				}
   407  				Key2 string
   408  				Key3 int64
   409  				Key4 float64
   410  			}{
   411  				Key1: struct{ Nested string }{Nested: str1k},
   412  				Key2: str1k,
   413  				Key3: 1234567890,
   414  				Key4: 1234567890.123456789,
   415  			},
   416  		},
   417  	}
   418  
   419  	for _, tc := range cases {
   420  		b.Run(tc.description, func(b *testing.B) {
   421  			bs, err := Marshal(tc.value)
   422  			require.NoError(b, err)
   423  
   424  			b.ResetTimer()
   425  			for i := 0; i < b.N; i++ {
   426  				_ = Raw(bs).String()
   427  			}
   428  		})
   429  	}
   430  }
   431  

View as plain text