1
2
3
4
5
6
7 package bsoncore
8
9 import (
10 "bytes"
11 "encoding/binary"
12 "errors"
13 "fmt"
14 "io"
15 "testing"
16
17 "github.com/google/go-cmp/cmp"
18 "go.mongodb.org/mongo-driver/bson/bsontype"
19 )
20
21 func ExampleDocument_Validate() {
22 doc := make(Document, 500)
23 doc[250], doc[251], doc[252], doc[253], doc[254] = 0x05, 0x00, 0x00, 0x00, 0x00
24 err := doc[250:].Validate()
25 fmt.Println(err)
26
27
28 }
29
30 func BenchmarkDocumentValidate(b *testing.B) {
31 for i := 0; i < b.N; i++ {
32 doc := make(Document, 500)
33 doc[250], doc[251], doc[252], doc[253], doc[254] = 0x05, 0x00, 0x00, 0x00, 0x00
34 _ = doc[250:].Validate()
35 }
36 }
37
38 func TestDocument(t *testing.T) {
39 t.Run("Validate", func(t *testing.T) {
40 t.Run("TooShort", func(t *testing.T) {
41 want := NewInsufficientBytesError(nil, nil)
42 got := Document{'\x00', '\x00'}.Validate()
43 if !compareErrors(got, want) {
44 t.Errorf("Did not get expected error. got %v; want %v", got, want)
45 }
46 })
47 t.Run("InvalidLength", func(t *testing.T) {
48 want := NewDocumentLengthError(200, 5)
49 r := make(Document, 5)
50 binary.LittleEndian.PutUint32(r[0:4], 200)
51 got := r.Validate()
52 if !compareErrors(got, want) {
53 t.Errorf("Did not get expected error. got %v; want %v", got, want)
54 }
55 })
56 t.Run("Invalid Element", func(t *testing.T) {
57 want := NewInsufficientBytesError(nil, nil)
58 r := make(Document, 9)
59 binary.LittleEndian.PutUint32(r[0:4], 9)
60 r[4], r[5], r[6], r[7], r[8] = 0x02, 'f', 'o', 'o', 0x00
61 got := r.Validate()
62 if !compareErrors(got, want) {
63 t.Errorf("Did not get expected error. got %v; want %v", got, want)
64 }
65 })
66 t.Run("Missing Null Terminator", func(t *testing.T) {
67 want := ErrMissingNull
68 r := make(Document, 8)
69 binary.LittleEndian.PutUint32(r[0:4], 8)
70 r[4], r[5], r[6], r[7] = 0x0A, 'f', 'o', 'o'
71 got := r.Validate()
72 if !compareErrors(got, want) {
73 t.Errorf("Did not get expected error. got %v; want %v", got, want)
74 }
75 })
76 testCases := []struct {
77 name string
78 r Document
79 want error
80 }{
81 {"null", Document{'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00'}, nil},
82 {"subdocument",
83 Document{
84 '\x15', '\x00', '\x00', '\x00',
85 '\x03',
86 'f', 'o', 'o', '\x00',
87 '\x0B', '\x00', '\x00', '\x00', '\x0A', 'a', '\x00',
88 '\x0A', 'b', '\x00', '\x00', '\x00',
89 },
90 nil,
91 },
92 {"array",
93 Document{
94 '\x15', '\x00', '\x00', '\x00',
95 '\x04',
96 'f', 'o', 'o', '\x00',
97 '\x0B', '\x00', '\x00', '\x00', '\x0A', '1', '\x00',
98 '\x0A', '2', '\x00', '\x00', '\x00',
99 },
100 nil,
101 },
102 }
103
104 for _, tc := range testCases {
105 t.Run(tc.name, func(t *testing.T) {
106 got := tc.r.Validate()
107 if !compareErrors(got, tc.want) {
108 t.Errorf("Returned error does not match. got %v; want %v", got, tc.want)
109 }
110 })
111 }
112 })
113 t.Run("Lookup", func(t *testing.T) {
114 t.Run("empty-key", func(t *testing.T) {
115 rdr := Document{'\x05', '\x00', '\x00', '\x00', '\x00'}
116 _, err := rdr.LookupErr()
117 if !errors.Is(err, ErrEmptyKey) {
118 t.Errorf("Empty key lookup did not return expected result. got %v; want %v", err, ErrEmptyKey)
119 }
120 })
121 t.Run("corrupted-subdocument", func(t *testing.T) {
122 rdr := Document{
123 '\x0D', '\x00', '\x00', '\x00',
124 '\x03', 'x', '\x00',
125 '\x06', '\x00', '\x00', '\x00',
126 '\x01',
127 '\x00',
128 '\x00',
129 }
130 _, got := rdr.LookupErr("x", "y")
131 want := NewInsufficientBytesError(nil, nil)
132 if !cmp.Equal(got, want) {
133 t.Errorf("Empty key lookup did not return expected result. got %v; want %v", got, want)
134 }
135 })
136 t.Run("corrupted-array", func(t *testing.T) {
137 rdr := Document{
138 '\x0D', '\x00', '\x00', '\x00',
139 '\x04', 'x', '\x00',
140 '\x06', '\x00', '\x00', '\x00',
141 '\x01',
142 '\x00',
143 '\x00',
144 }
145 _, got := rdr.LookupErr("x", "y")
146 want := NewInsufficientBytesError(nil, nil)
147 if !cmp.Equal(got, want) {
148 t.Errorf("Empty key lookup did not return expected result. got %v; want %v", got, want)
149 }
150 })
151 t.Run("invalid-traversal", func(t *testing.T) {
152 rdr := Document{'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00'}
153 _, got := rdr.LookupErr("x", "y")
154 want := InvalidDepthTraversalError{Key: "x", Type: bsontype.Null}
155 if !compareErrors(got, want) {
156 t.Errorf("Empty key lookup did not return expected result. got %v; want %v", got, want)
157 }
158 })
159 testCases := []struct {
160 name string
161 r Document
162 key []string
163 want Value
164 err error
165 }{
166 {"first",
167 Document{
168 '\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00',
169 },
170 []string{"x"},
171 Value{Type: bsontype.Null, Data: []byte{}},
172 nil,
173 },
174 {"first-second",
175 Document{
176 '\x15', '\x00', '\x00', '\x00',
177 '\x03',
178 'f', 'o', 'o', '\x00',
179 '\x0B', '\x00', '\x00', '\x00', '\x0A', 'a', '\x00',
180 '\x0A', 'b', '\x00', '\x00', '\x00',
181 },
182 []string{"foo", "b"},
183 Value{Type: bsontype.Null, Data: []byte{}},
184 nil,
185 },
186 {"first-second-array",
187 Document{
188 '\x15', '\x00', '\x00', '\x00',
189 '\x04',
190 'f', 'o', 'o', '\x00',
191 '\x0B', '\x00', '\x00', '\x00', '\x0A', '1', '\x00',
192 '\x0A', '2', '\x00', '\x00', '\x00',
193 },
194 []string{"foo", "2"},
195 Value{Type: bsontype.Null, Data: []byte{}},
196 nil,
197 },
198 }
199
200 for _, tc := range testCases {
201 t.Run(tc.name, func(t *testing.T) {
202 t.Run("Lookup", func(t *testing.T) {
203 got := tc.r.Lookup(tc.key...)
204 if !cmp.Equal(got, tc.want) {
205 t.Errorf("Returned value does not match expected element. got %v; want %v", got, tc.want)
206 }
207 })
208 t.Run("LookupErr", func(t *testing.T) {
209 got, err := tc.r.LookupErr(tc.key...)
210 if !errors.Is(err, tc.err) {
211 t.Errorf("Returned error does not match. got %v; want %v", err, tc.err)
212 }
213 if !cmp.Equal(got, tc.want) {
214 t.Errorf("Returned value does not match expected element. got %v; want %v", got, tc.want)
215 }
216 })
217 })
218 }
219 })
220 t.Run("Index", func(t *testing.T) {
221 t.Run("Out of bounds", func(t *testing.T) {
222 rdr := Document{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0}
223 _, err := rdr.IndexErr(3)
224 if !errors.Is(err, ErrOutOfBounds) {
225 t.Errorf("Out of bounds should be returned when accessing element beyond end of document. got %v; want %v", err, ErrOutOfBounds)
226 }
227 })
228 t.Run("Validation Error", func(t *testing.T) {
229 rdr := Document{0x07, 0x00, 0x00, 0x00, 0x00}
230 _, got := rdr.IndexErr(1)
231 want := NewInsufficientBytesError(nil, nil)
232 if !compareErrors(got, want) {
233 t.Errorf("Did not receive expected error. got %v; want %v", got, want)
234 }
235 })
236 testCases := []struct {
237 name string
238 rdr Document
239 index uint
240 want Element
241 }{
242 {"first",
243 Document{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},
244 0, Element{0x0a, 0x78, 0x00},
245 },
246 {"second",
247 Document{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},
248 1, Element{0x0a, 0x79, 0x00},
249 },
250 {"third",
251 Document{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},
252 2, Element{0x0a, 0x7a, 0x00},
253 },
254 }
255
256 for _, tc := range testCases {
257 t.Run(tc.name, func(t *testing.T) {
258 t.Run("IndexErr", func(t *testing.T) {
259 got, err := tc.rdr.IndexErr(tc.index)
260 if err != nil {
261 t.Errorf("Unexpected error from IndexErr: %s", err)
262 }
263 if diff := cmp.Diff(got, tc.want); diff != "" {
264 t.Errorf("Documents differ: (-got +want)\n%s", diff)
265 }
266 })
267 t.Run("Index", func(t *testing.T) {
268 defer func() {
269 if err := recover(); err != nil {
270 t.Errorf("Unexpected error: %v", err)
271 }
272 }()
273 got := tc.rdr.Index(tc.index)
274 if diff := cmp.Diff(got, tc.want); diff != "" {
275 t.Errorf("Documents differ: (-got +want)\n%s", diff)
276 }
277 })
278 })
279 }
280 })
281 t.Run("NewDocumentFromReader", func(t *testing.T) {
282 testCases := []struct {
283 name string
284 ioReader io.Reader
285 doc Document
286 err error
287 }{
288 {
289 "nil reader",
290 nil,
291 nil,
292 ErrNilReader,
293 },
294 {
295 "premature end of reader",
296 bytes.NewBuffer([]byte{}),
297 nil,
298 io.EOF,
299 },
300 {
301 "empty document",
302 bytes.NewBuffer([]byte{5, 0, 0, 0, 0}),
303 []byte{5, 0, 0, 0, 0},
304 nil,
305 },
306 {
307 "non-empty document",
308 bytes.NewBuffer([]byte{
309
310 0x17, 0x0, 0x0, 0x0,
311
312
313 0x2,
314
315 0x66, 0x6f, 0x6f, 0x0,
316
317 0x4, 0x0, 0x0, 0x0,
318
319 0x62, 0x61, 0x72, 0x0,
320
321
322 0xa,
323
324 0x62, 0x61, 0x7a, 0x0,
325
326
327 0x0,
328 }),
329 []byte{
330
331 0x17, 0x0, 0x0, 0x0,
332
333
334 0x2,
335
336 0x66, 0x6f, 0x6f, 0x0,
337
338 0x4, 0x0, 0x0, 0x0,
339
340 0x62, 0x61, 0x72, 0x0,
341
342
343 0xa,
344
345 0x62, 0x61, 0x7a, 0x0,
346
347
348 0x0,
349 },
350 nil,
351 },
352 }
353
354 for _, tc := range testCases {
355 t.Run(tc.name, func(t *testing.T) {
356 doc, err := NewDocumentFromReader(tc.ioReader)
357 if !compareErrors(err, tc.err) {
358 t.Errorf("errors do not match. got %v; want %v", err, tc.err)
359 }
360 if !bytes.Equal(tc.doc, doc) {
361 t.Errorf("documents differ. got %v; want %v", tc.doc, doc)
362 }
363 })
364 }
365 })
366 t.Run("Elements", func(t *testing.T) {
367 invalidElem := BuildDocument(nil, AppendHeader(nil, bsontype.Double, "foo"))
368 invalidTwoElem := BuildDocument(nil,
369 AppendHeader(
370 AppendDoubleElement(nil, "pi", 3.14159),
371 bsontype.Double, "foo",
372 ),
373 )
374 oneElem := BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159))
375 twoElems := BuildDocument(nil,
376 AppendStringElement(
377 AppendDoubleElement(nil, "pi", 3.14159),
378 "hello", "world!",
379 ),
380 )
381 testCases := []struct {
382 name string
383 doc Document
384 elems []Element
385 err error
386 }{
387 {"Insufficient Bytes Length", Document{0x03, 0x00, 0x00}, nil, NewInsufficientBytesError(nil, nil)},
388 {"Insufficient Bytes First Element", invalidElem, nil, NewInsufficientBytesError(nil, nil)},
389 {"Insufficient Bytes Second Element", invalidTwoElem, []Element{AppendDoubleElement(nil, "pi", 3.14159)}, NewInsufficientBytesError(nil, nil)},
390 {"Success One Element", oneElem, []Element{AppendDoubleElement(nil, "pi", 3.14159)}, nil},
391 {"Success Two Elements", twoElems, []Element{AppendDoubleElement(nil, "pi", 3.14159), AppendStringElement(nil, "hello", "world!")}, nil},
392 }
393
394 for _, tc := range testCases {
395 t.Run(tc.name, func(t *testing.T) {
396 elems, err := tc.doc.Elements()
397 if !compareErrors(err, tc.err) {
398 t.Errorf("errors do not match. got %v; want %v", err, tc.err)
399 }
400 if len(elems) != len(tc.elems) {
401 t.Fatalf("number of elements returned does not match. got %d; want %d", len(elems), len(tc.elems))
402 }
403
404 for idx := range elems {
405 got, want := elems[idx], tc.elems[idx]
406 if !bytes.Equal(got, want) {
407 t.Errorf("Elements at index %d differ. got %v; want %v", idx, got.DebugString(), want.DebugString())
408 }
409 }
410 })
411 }
412 })
413 }
414
View as plain text