1
2
3
4
5
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
898
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