1
2
3
4
5
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
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
298 0x17, 0x0, 0x0, 0x0,
299
300
301 0x2,
302
303 0x66, 0x6f, 0x6f, 0x0,
304
305 0x4, 0x0, 0x0, 0x0,
306
307 0x62, 0x61, 0x72, 0x0,
308
309
310 0xa,
311
312 0x62, 0x61, 0x7a, 0x0,
313
314
315 0x0,
316 }),
317 []byte{
318
319 0x17, 0x0, 0x0, 0x0,
320
321
322 0x2,
323
324 0x66, 0x6f, 0x6f, 0x0,
325
326 0x4, 0x0, 0x0, 0x0,
327
328 0x62, 0x61, 0x72, 0x0,
329
330
331 0xa,
332
333 0x62, 0x61, 0x7a, 0x0,
334
335
336 0x0,
337 },
338 nil,
339 },
340 }
341
342 for _, tc := range testCases {
343 tc := tc
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
357
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