1
2
3
4
5
6
7 package bsonrw
8
9 import (
10 "errors"
11 "io"
12 "strings"
13 "testing"
14
15 "github.com/google/go-cmp/cmp"
16 "go.mongodb.org/mongo-driver/bson/bsontype"
17 )
18
19 var (
20 keyDiff = specificDiff("key")
21 typDiff = specificDiff("type")
22 valDiff = specificDiff("value")
23
24 expectErrEOF = expectSpecificError(io.EOF)
25 expectErrEOD = expectSpecificError(ErrEOD)
26 expectErrEOA = expectSpecificError(ErrEOA)
27 )
28
29 type expectedErrorFunc func(t *testing.T, err error, desc string)
30
31 type peekTypeTestCase struct {
32 desc string
33 input string
34 typs []bsontype.Type
35 errFs []expectedErrorFunc
36 }
37
38 type readKeyValueTestCase struct {
39 desc string
40 input string
41 keys []string
42 typs []bsontype.Type
43 vals []*extJSONValue
44
45 keyEFs []expectedErrorFunc
46 valEFs []expectedErrorFunc
47 }
48
49 func expectSpecificError(expected error) expectedErrorFunc {
50 return func(t *testing.T, err error, desc string) {
51 if !errors.Is(err, expected) {
52 t.Helper()
53 t.Errorf("%s: Expected %v but got: %v", desc, expected, err)
54 t.FailNow()
55 }
56 }
57 }
58
59 func specificDiff(name string) func(t *testing.T, expected, actual interface{}, desc string) {
60 return func(t *testing.T, expected, actual interface{}, desc string) {
61 if diff := cmp.Diff(expected, actual); diff != "" {
62 t.Helper()
63 t.Errorf("%s: Incorrect JSON %s (-want, +got): %s\n", desc, name, diff)
64 t.FailNow()
65 }
66 }
67 }
68
69 func expectErrorNOOP(_ *testing.T, _ error, _ string) {
70 }
71
72 func readKeyDiff(t *testing.T, eKey, aKey string, eTyp, aTyp bsontype.Type, err error, errF expectedErrorFunc, desc string) {
73 keyDiff(t, eKey, aKey, desc)
74 typDiff(t, eTyp, aTyp, desc)
75 errF(t, err, desc)
76 }
77
78 func readValueDiff(t *testing.T, eVal, aVal *extJSONValue, err error, errF expectedErrorFunc, desc string) {
79 if aVal != nil {
80 typDiff(t, eVal.t, aVal.t, desc)
81 valDiff(t, eVal.v, aVal.v, desc)
82 } else {
83 valDiff(t, eVal, aVal, desc)
84 }
85
86 errF(t, err, desc)
87 }
88
89 func TestExtJSONParserPeekType(t *testing.T) {
90 makeValidPeekTypeTestCase := func(input string, typ bsontype.Type, desc string) peekTypeTestCase {
91 return peekTypeTestCase{
92 desc: desc, input: input,
93 typs: []bsontype.Type{typ},
94 errFs: []expectedErrorFunc{expectNoError},
95 }
96 }
97 makeInvalidTestCase := func(desc, input string, lastEF expectedErrorFunc) peekTypeTestCase {
98 return peekTypeTestCase{
99 desc: desc, input: input,
100 typs: []bsontype.Type{bsontype.Type(0)},
101 errFs: []expectedErrorFunc{lastEF},
102 }
103 }
104
105 makeInvalidPeekTypeTestCase := func(desc, input string, lastEF expectedErrorFunc) peekTypeTestCase {
106 return peekTypeTestCase{
107 desc: desc, input: input,
108 typs: []bsontype.Type{bsontype.Array, bsontype.String, bsontype.Type(0)},
109 errFs: []expectedErrorFunc{expectNoError, expectNoError, lastEF},
110 }
111 }
112
113 cases := []peekTypeTestCase{
114 makeValidPeekTypeTestCase(`null`, bsontype.Null, "Null"),
115 makeValidPeekTypeTestCase(`"string"`, bsontype.String, "String"),
116 makeValidPeekTypeTestCase(`true`, bsontype.Boolean, "Boolean--true"),
117 makeValidPeekTypeTestCase(`false`, bsontype.Boolean, "Boolean--false"),
118 makeValidPeekTypeTestCase(`{"$minKey": 1}`, bsontype.MinKey, "MinKey"),
119 makeValidPeekTypeTestCase(`{"$maxKey": 1}`, bsontype.MaxKey, "MaxKey"),
120 makeValidPeekTypeTestCase(`{"$numberInt": "42"}`, bsontype.Int32, "Int32"),
121 makeValidPeekTypeTestCase(`{"$numberLong": "42"}`, bsontype.Int64, "Int64"),
122 makeValidPeekTypeTestCase(`{"$symbol": "symbol"}`, bsontype.Symbol, "Symbol"),
123 makeValidPeekTypeTestCase(`{"$numberDouble": "42.42"}`, bsontype.Double, "Double"),
124 makeValidPeekTypeTestCase(`{"$undefined": true}`, bsontype.Undefined, "Undefined"),
125 makeValidPeekTypeTestCase(`{"$numberDouble": "NaN"}`, bsontype.Double, "Double--NaN"),
126 makeValidPeekTypeTestCase(`{"$numberDecimal": "1234"}`, bsontype.Decimal128, "Decimal"),
127 makeValidPeekTypeTestCase(`{"foo": "bar"}`, bsontype.EmbeddedDocument, "Toplevel document"),
128 makeValidPeekTypeTestCase(`{"$date": {"$numberLong": "0"}}`, bsontype.DateTime, "Datetime"),
129 makeValidPeekTypeTestCase(`{"$code": "function() {}"}`, bsontype.JavaScript, "Code no scope"),
130 makeValidPeekTypeTestCase(`[{"$numberInt": "1"},{"$numberInt": "2"}]`, bsontype.Array, "Array"),
131 makeValidPeekTypeTestCase(`{"$timestamp": {"t": 42, "i": 1}}`, bsontype.Timestamp, "Timestamp"),
132 makeValidPeekTypeTestCase(`{"$oid": "57e193d7a9cc81b4027498b5"}`, bsontype.ObjectID, "Object ID"),
133 makeValidPeekTypeTestCase(`{"$binary": {"base64": "AQIDBAU=", "subType": "80"}}`, bsontype.Binary, "Binary"),
134 makeValidPeekTypeTestCase(`{"$code": "function() {}", "$scope": {}}`, bsontype.CodeWithScope, "Code With Scope"),
135 makeValidPeekTypeTestCase(`{"$binary": {"base64": "o0w498Or7cijeBSpkquNtg==", "subType": "03"}}`, bsontype.Binary, "Binary"),
136 makeValidPeekTypeTestCase(`{"$binary": "o0w498Or7cijeBSpkquNtg==", "$type": "03"}`, bsontype.Binary, "Binary"),
137 makeValidPeekTypeTestCase(`{"$regularExpression": {"pattern": "foo*", "options": "ix"}}`, bsontype.Regex, "Regular expression"),
138 makeValidPeekTypeTestCase(`{"$dbPointer": {"$ref": "db.collection", "$id": {"$oid": "57e193d7a9cc81b4027498b1"}}}`, bsontype.DBPointer, "DBPointer"),
139 makeValidPeekTypeTestCase(`{"$ref": "collection", "$id": {"$oid": "57fd71e96e32ab4225b723fb"}, "$db": "database"}`, bsontype.EmbeddedDocument, "DBRef"),
140 makeInvalidPeekTypeTestCase("invalid array--missing ]", `["a"`, expectError),
141 makeInvalidPeekTypeTestCase("invalid array--colon in array", `["a":`, expectError),
142 makeInvalidPeekTypeTestCase("invalid array--extra comma", `["a",,`, expectError),
143 makeInvalidPeekTypeTestCase("invalid array--trailing comma", `["a",]`, expectError),
144 makeInvalidPeekTypeTestCase("peekType after end of array", `["a"]`, expectErrEOA),
145 {
146 desc: "invalid array--leading comma",
147 input: `[,`,
148 typs: []bsontype.Type{bsontype.Array, bsontype.Type(0)},
149 errFs: []expectedErrorFunc{expectNoError, expectError},
150 },
151 makeInvalidTestCase("lone $scope", `{"$scope": {}}`, expectError),
152 makeInvalidTestCase("empty code with unknown extra key", `{"$code":"", "0":""}`, expectError),
153 makeInvalidTestCase("non-empty code with unknown extra key", `{"$code":"foobar", "0":""}`, expectError),
154 }
155
156 for _, tc := range cases {
157 t.Run(tc.desc, func(t *testing.T) {
158 ejp := newExtJSONParser(strings.NewReader(tc.input), true)
159
160
161
162 ejp.s = jpsSawColon
163
164 for i, eTyp := range tc.typs {
165 errF := tc.errFs[i]
166
167 typ, err := ejp.peekType()
168 errF(t, err, tc.desc)
169 if err != nil {
170
171 return
172 }
173
174 typDiff(t, eTyp, typ, tc.desc)
175 }
176 })
177 }
178 }
179
180 func TestExtJSONParserReadKeyReadValue(t *testing.T) {
181
182
183 keys := []string{"_id", "Symbol", "String", "Int32", "Int64", "Int", "MinKey"}
184 types := []bsontype.Type{bsontype.ObjectID, bsontype.Symbol, bsontype.String, bsontype.Int32, bsontype.Int64, bsontype.Int32, bsontype.MinKey}
185 values := []*extJSONValue{
186 {t: bsontype.String, v: "57e193d7a9cc81b4027498b5"},
187 {t: bsontype.String, v: "symbol"},
188 {t: bsontype.String, v: "string"},
189 {t: bsontype.String, v: "42"},
190 {t: bsontype.String, v: "42"},
191 {t: bsontype.Int32, v: int32(42)},
192 {t: bsontype.Int32, v: int32(1)},
193 }
194
195 errFuncs := make([]expectedErrorFunc, 7)
196 for i := 0; i < 7; i++ {
197 errFuncs[i] = expectNoError
198 }
199
200 firstKeyError := func(desc, input string) readKeyValueTestCase {
201 return readKeyValueTestCase{
202 desc: desc,
203 input: input,
204 keys: []string{""},
205 typs: []bsontype.Type{bsontype.Type(0)},
206 vals: []*extJSONValue{nil},
207 keyEFs: []expectedErrorFunc{expectError},
208 valEFs: []expectedErrorFunc{expectErrorNOOP},
209 }
210 }
211
212 secondKeyError := func(desc, input, firstKey string, firstType bsontype.Type, firstValue *extJSONValue) readKeyValueTestCase {
213 return readKeyValueTestCase{
214 desc: desc,
215 input: input,
216 keys: []string{firstKey, ""},
217 typs: []bsontype.Type{firstType, bsontype.Type(0)},
218 vals: []*extJSONValue{firstValue, nil},
219 keyEFs: []expectedErrorFunc{expectNoError, expectError},
220 valEFs: []expectedErrorFunc{expectNoError, expectErrorNOOP},
221 }
222 }
223
224 cases := []readKeyValueTestCase{
225 {
226 desc: "normal spacing",
227 input: `{
228 "_id": { "$oid": "57e193d7a9cc81b4027498b5" },
229 "Symbol": { "$symbol": "symbol" },
230 "String": "string",
231 "Int32": { "$numberInt": "42" },
232 "Int64": { "$numberLong": "42" },
233 "Int": 42,
234 "MinKey": { "$minKey": 1 }
235 }`,
236 keys: keys, typs: types, vals: values,
237 keyEFs: errFuncs, valEFs: errFuncs,
238 },
239 {
240 desc: "new line before comma",
241 input: `{ "_id": { "$oid": "57e193d7a9cc81b4027498b5" }
242 , "Symbol": { "$symbol": "symbol" }
243 , "String": "string"
244 , "Int32": { "$numberInt": "42" }
245 , "Int64": { "$numberLong": "42" }
246 , "Int": 42
247 , "MinKey": { "$minKey": 1 }
248 }`,
249 keys: keys, typs: types, vals: values,
250 keyEFs: errFuncs, valEFs: errFuncs,
251 },
252 {
253 desc: "tabs around colons",
254 input: `{
255 "_id": { "$oid" : "57e193d7a9cc81b4027498b5" },
256 "Symbol": { "$symbol" : "symbol" },
257 "String": "string",
258 "Int32": { "$numberInt" : "42" },
259 "Int64": { "$numberLong": "42" },
260 "Int": 42,
261 "MinKey": { "$minKey": 1 }
262 }`,
263 keys: keys, typs: types, vals: values,
264 keyEFs: errFuncs, valEFs: errFuncs,
265 },
266 {
267 desc: "no whitespace",
268 input: `{"_id":{"$oid":"57e193d7a9cc81b4027498b5"},"Symbol":{"$symbol":"symbol"},"String":"string","Int32":{"$numberInt":"42"},"Int64":{"$numberLong":"42"},"Int":42,"MinKey":{"$minKey":1}}`,
269 keys: keys, typs: types, vals: values,
270 keyEFs: errFuncs, valEFs: errFuncs,
271 },
272 {
273 desc: "mixed whitespace",
274 input: ` {
275 "_id" : { "$oid": "57e193d7a9cc81b4027498b5" },
276 "Symbol" : { "$symbol": "symbol" } ,
277 "String" : "string",
278 "Int32" : { "$numberInt": "42" } ,
279 "Int64" : {"$numberLong" : "42"},
280 "Int" : 42,
281 "MinKey" : { "$minKey": 1 } } `,
282 keys: keys, typs: types, vals: values,
283 keyEFs: errFuncs, valEFs: errFuncs,
284 },
285 {
286 desc: "nested object",
287 input: `{"k1": 1, "k2": { "k3": { "k4": 4 } }, "k5": 5}`,
288 keys: []string{"k1", "k2", "k3", "k4", "", "", "k5", ""},
289 typs: []bsontype.Type{bsontype.Int32, bsontype.EmbeddedDocument, bsontype.EmbeddedDocument, bsontype.Int32, bsontype.Type(0), bsontype.Type(0), bsontype.Int32, bsontype.Type(0)},
290 vals: []*extJSONValue{
291 {t: bsontype.Int32, v: int32(1)}, nil, nil, {t: bsontype.Int32, v: int32(4)}, nil, nil, {t: bsontype.Int32, v: int32(5)}, nil,
292 },
293 keyEFs: []expectedErrorFunc{
294 expectNoError, expectNoError, expectNoError, expectNoError, expectErrEOD,
295 expectErrEOD, expectNoError, expectErrEOD,
296 },
297 valEFs: []expectedErrorFunc{
298 expectNoError, expectError, expectError, expectNoError, expectErrorNOOP,
299 expectErrorNOOP, expectNoError, expectErrorNOOP,
300 },
301 },
302 {
303 desc: "invalid input: invalid values for extended type",
304 input: `{"a": {"$numberInt": "1", "x"`,
305 keys: []string{"a"},
306 typs: []bsontype.Type{bsontype.Int32},
307 vals: []*extJSONValue{nil},
308 keyEFs: []expectedErrorFunc{expectNoError},
309 valEFs: []expectedErrorFunc{expectError},
310 },
311 firstKeyError("invalid input: missing key--EOF", "{"),
312 firstKeyError("invalid input: missing key--colon first", "{:"),
313 firstKeyError("invalid input: missing value", `{"a":`),
314 firstKeyError("invalid input: missing colon", `{"a" 1`),
315 firstKeyError("invalid input: extra colon", `{"a"::`),
316 secondKeyError("invalid input: missing }", `{"a": 1`, "a", bsontype.Int32, &extJSONValue{t: bsontype.Int32, v: int32(1)}),
317 secondKeyError("invalid input: missing comma", `{"a": 1 "b"`, "a", bsontype.Int32, &extJSONValue{t: bsontype.Int32, v: int32(1)}),
318 secondKeyError("invalid input: extra comma", `{"a": 1,, "b"`, "a", bsontype.Int32, &extJSONValue{t: bsontype.Int32, v: int32(1)}),
319 secondKeyError("invalid input: trailing comma in object", `{"a": 1,}`, "a", bsontype.Int32, &extJSONValue{t: bsontype.Int32, v: int32(1)}),
320 {
321 desc: "invalid input: lone scope after a complete value",
322 input: `{"a": "", "b": {"$scope: ""}}`,
323 keys: []string{"a"},
324 typs: []bsontype.Type{bsontype.String},
325 vals: []*extJSONValue{{bsontype.String, ""}},
326 keyEFs: []expectedErrorFunc{expectNoError, expectNoError},
327 valEFs: []expectedErrorFunc{expectNoError, expectError},
328 },
329 {
330 desc: "invalid input: lone scope nested",
331 input: `{"a":{"b":{"$scope":{`,
332 keys: []string{},
333 typs: []bsontype.Type{},
334 vals: []*extJSONValue{nil},
335 keyEFs: []expectedErrorFunc{expectNoError},
336 valEFs: []expectedErrorFunc{expectError},
337 },
338 }
339
340 for _, tc := range cases {
341 t.Run(tc.desc, func(t *testing.T) {
342 ejp := newExtJSONParser(strings.NewReader(tc.input), true)
343
344 for i, eKey := range tc.keys {
345 eTyp := tc.typs[i]
346 eVal := tc.vals[i]
347
348 keyErrF := tc.keyEFs[i]
349 valErrF := tc.valEFs[i]
350
351 k, typ, err := ejp.readKey()
352 readKeyDiff(t, eKey, k, eTyp, typ, err, keyErrF, tc.desc)
353
354 v, err := ejp.readValue(typ)
355 readValueDiff(t, eVal, v, err, valErrF, tc.desc)
356 }
357 })
358 }
359 }
360
361 type ejpExpectationTest func(t *testing.T, p *extJSONParser, expectedKey string, expectedType bsontype.Type, expectedValue interface{})
362
363 type ejpTestCase struct {
364 f ejpExpectationTest
365 p *extJSONParser
366 k string
367 t bsontype.Type
368 v interface{}
369 }
370
371
372
373 func expectSingleValue(t *testing.T, p *extJSONParser, expectedKey string, expectedType bsontype.Type, expectedValue interface{}) {
374 eVal := expectedValue.(*extJSONValue)
375
376 k, typ, err := p.readKey()
377 readKeyDiff(t, expectedKey, k, expectedType, typ, err, expectNoError, expectedKey)
378
379 v, err := p.readValue(typ)
380 readValueDiff(t, eVal, v, err, expectNoError, expectedKey)
381 }
382
383
384
385 func expectMultipleValues(t *testing.T, p *extJSONParser, expectedKey string, expectedType bsontype.Type, expectedValue interface{}) {
386 k, typ, err := p.readKey()
387 readKeyDiff(t, expectedKey, k, expectedType, typ, err, expectNoError, expectedKey)
388
389 v, err := p.readValue(typ)
390 expectNoError(t, err, "")
391 typDiff(t, bsontype.EmbeddedDocument, v.t, expectedKey)
392
393 actObj := v.v.(*extJSONObject)
394 expObj := expectedValue.(*extJSONObject)
395
396 for i, actKey := range actObj.keys {
397 expKey := expObj.keys[i]
398 actVal := actObj.values[i]
399 expVal := expObj.values[i]
400
401 keyDiff(t, expKey, actKey, expectedKey)
402 typDiff(t, expVal.t, actVal.t, expectedKey)
403 valDiff(t, expVal.v, actVal.v, expectedKey)
404 }
405 }
406
407 type ejpKeyTypValTriple struct {
408 key string
409 typ bsontype.Type
410 val *extJSONValue
411 }
412
413 type ejpSubDocumentTestValue struct {
414 code string
415 ktvs []ejpKeyTypValTriple
416 }
417
418
419
420
421 func expectSubDocument(t *testing.T, p *extJSONParser, expectedKey string, expectedType bsontype.Type, expectedValue interface{}) {
422 subdoc := expectedValue.(ejpSubDocumentTestValue)
423
424 k, typ, err := p.readKey()
425 readKeyDiff(t, expectedKey, k, expectedType, typ, err, expectNoError, expectedKey)
426
427 if expectedType == bsontype.CodeWithScope {
428 v, err := p.readValue(typ)
429 readValueDiff(t, &extJSONValue{t: bsontype.String, v: subdoc.code}, v, err, expectNoError, expectedKey)
430 }
431
432 for _, ktv := range subdoc.ktvs {
433 eKey := ktv.key
434 eTyp := ktv.typ
435 eVal := ktv.val
436
437 k, typ, err = p.readKey()
438 readKeyDiff(t, eKey, k, eTyp, typ, err, expectNoError, expectedKey)
439
440 v, err := p.readValue(typ)
441 readValueDiff(t, eVal, v, err, expectNoError, expectedKey)
442 }
443
444 if expectedType == bsontype.CodeWithScope {
445
446 k, typ, err = p.readKey()
447 readKeyDiff(t, "", k, bsontype.Type(0), typ, err, expectErrEOD, expectedKey)
448 }
449
450
451 k, typ, err = p.readKey()
452 readKeyDiff(t, "", k, bsontype.Type(0), typ, err, expectErrEOD, expectedKey)
453 }
454
455
456
457 func expectArray(t *testing.T, p *extJSONParser, expectedKey string, _ bsontype.Type, expectedValue interface{}) {
458 ktvs := expectedValue.([]ejpKeyTypValTriple)
459
460 k, typ, err := p.readKey()
461 readKeyDiff(t, expectedKey, k, bsontype.Array, typ, err, expectNoError, expectedKey)
462
463 for _, ktv := range ktvs {
464 eTyp := ktv.typ
465 eVal := ktv.val
466
467 typ, err = p.peekType()
468 typDiff(t, eTyp, typ, expectedKey)
469 expectNoError(t, err, expectedKey)
470
471 v, err := p.readValue(typ)
472 readValueDiff(t, eVal, v, err, expectNoError, expectedKey)
473 }
474
475
476 typ, err = p.peekType()
477 typDiff(t, bsontype.Type(0), typ, expectedKey)
478 expectErrEOA(t, err, expectedKey)
479 }
480
481 func TestExtJSONParserAllTypes(t *testing.T) {
482 in := ` { "_id" : { "$oid": "57e193d7a9cc81b4027498b5"}
483 , "Symbol" : { "$symbol": "symbol"}
484 , "String" : "string"
485 , "Int32" : { "$numberInt": "42"}
486 , "Int64" : { "$numberLong": "42"}
487 , "Double" : { "$numberDouble": "42.42"}
488 , "SpecialFloat" : { "$numberDouble": "NaN" }
489 , "Decimal" : { "$numberDecimal": "1234" }
490 , "Binary" : { "$binary": { "base64": "o0w498Or7cijeBSpkquNtg==", "subType": "03" } }
491 , "BinaryLegacy" : { "$binary": "o0w498Or7cijeBSpkquNtg==", "$type": "03" }
492 , "BinaryUserDefined" : { "$binary": { "base64": "AQIDBAU=", "subType": "80" } }
493 , "Code" : { "$code": "function() {}" }
494 , "CodeWithEmptyScope" : { "$code": "function() {}", "$scope": {} }
495 , "CodeWithScope" : { "$code": "function() {}", "$scope": { "x": 1 } }
496 , "EmptySubdocument" : {}
497 , "Subdocument" : { "foo": "bar", "baz": { "$numberInt": "42" } }
498 , "Array" : [{"$numberInt": "1"}, {"$numberLong": "2"}, {"$numberDouble": "3"}, 4, "string", 5.0]
499 , "Timestamp" : { "$timestamp": { "t": 42, "i": 1 } }
500 , "RegularExpression" : { "$regularExpression": { "pattern": "foo*", "options": "ix" } }
501 , "DatetimeEpoch" : { "$date": { "$numberLong": "0" } }
502 , "DatetimePositive" : { "$date": { "$numberLong": "9223372036854775807" } }
503 , "DatetimeNegative" : { "$date": { "$numberLong": "-9223372036854775808" } }
504 , "True" : true
505 , "False" : false
506 , "DBPointer" : { "$dbPointer": { "$ref": "db.collection", "$id": { "$oid": "57e193d7a9cc81b4027498b1" } } }
507 , "DBRef" : { "$ref": "collection", "$id": { "$oid": "57fd71e96e32ab4225b723fb" }, "$db": "database" }
508 , "DBRefNoDB" : { "$ref": "collection", "$id": { "$oid": "57fd71e96e32ab4225b723fb" } }
509 , "MinKey" : { "$minKey": 1 }
510 , "MaxKey" : { "$maxKey": 1 }
511 , "Null" : null
512 , "Undefined" : { "$undefined": true }
513 }`
514
515 ejp := newExtJSONParser(strings.NewReader(in), true)
516
517 cases := []ejpTestCase{
518 {
519 f: expectSingleValue, p: ejp,
520 k: "_id", t: bsontype.ObjectID, v: &extJSONValue{t: bsontype.String, v: "57e193d7a9cc81b4027498b5"},
521 },
522 {
523 f: expectSingleValue, p: ejp,
524 k: "Symbol", t: bsontype.Symbol, v: &extJSONValue{t: bsontype.String, v: "symbol"},
525 },
526 {
527 f: expectSingleValue, p: ejp,
528 k: "String", t: bsontype.String, v: &extJSONValue{t: bsontype.String, v: "string"},
529 },
530 {
531 f: expectSingleValue, p: ejp,
532 k: "Int32", t: bsontype.Int32, v: &extJSONValue{t: bsontype.String, v: "42"},
533 },
534 {
535 f: expectSingleValue, p: ejp,
536 k: "Int64", t: bsontype.Int64, v: &extJSONValue{t: bsontype.String, v: "42"},
537 },
538 {
539 f: expectSingleValue, p: ejp,
540 k: "Double", t: bsontype.Double, v: &extJSONValue{t: bsontype.String, v: "42.42"},
541 },
542 {
543 f: expectSingleValue, p: ejp,
544 k: "SpecialFloat", t: bsontype.Double, v: &extJSONValue{t: bsontype.String, v: "NaN"},
545 },
546 {
547 f: expectSingleValue, p: ejp,
548 k: "Decimal", t: bsontype.Decimal128, v: &extJSONValue{t: bsontype.String, v: "1234"},
549 },
550 {
551 f: expectMultipleValues, p: ejp,
552 k: "Binary", t: bsontype.Binary,
553 v: &extJSONObject{
554 keys: []string{"base64", "subType"},
555 values: []*extJSONValue{
556 {t: bsontype.String, v: "o0w498Or7cijeBSpkquNtg=="},
557 {t: bsontype.String, v: "03"},
558 },
559 },
560 },
561 {
562 f: expectMultipleValues, p: ejp,
563 k: "BinaryLegacy", t: bsontype.Binary,
564 v: &extJSONObject{
565 keys: []string{"base64", "subType"},
566 values: []*extJSONValue{
567 {t: bsontype.String, v: "o0w498Or7cijeBSpkquNtg=="},
568 {t: bsontype.String, v: "03"},
569 },
570 },
571 },
572 {
573 f: expectMultipleValues, p: ejp,
574 k: "BinaryUserDefined", t: bsontype.Binary,
575 v: &extJSONObject{
576 keys: []string{"base64", "subType"},
577 values: []*extJSONValue{
578 {t: bsontype.String, v: "AQIDBAU="},
579 {t: bsontype.String, v: "80"},
580 },
581 },
582 },
583 {
584 f: expectSingleValue, p: ejp,
585 k: "Code", t: bsontype.JavaScript, v: &extJSONValue{t: bsontype.String, v: "function() {}"},
586 },
587 {
588 f: expectSubDocument, p: ejp,
589 k: "CodeWithEmptyScope", t: bsontype.CodeWithScope,
590 v: ejpSubDocumentTestValue{
591 code: "function() {}",
592 ktvs: []ejpKeyTypValTriple{},
593 },
594 },
595 {
596 f: expectSubDocument, p: ejp,
597 k: "CodeWithScope", t: bsontype.CodeWithScope,
598 v: ejpSubDocumentTestValue{
599 code: "function() {}",
600 ktvs: []ejpKeyTypValTriple{
601 {"x", bsontype.Int32, &extJSONValue{t: bsontype.Int32, v: int32(1)}},
602 },
603 },
604 },
605 {
606 f: expectSubDocument, p: ejp,
607 k: "EmptySubdocument", t: bsontype.EmbeddedDocument,
608 v: ejpSubDocumentTestValue{
609 ktvs: []ejpKeyTypValTriple{},
610 },
611 },
612 {
613 f: expectSubDocument, p: ejp,
614 k: "Subdocument", t: bsontype.EmbeddedDocument,
615 v: ejpSubDocumentTestValue{
616 ktvs: []ejpKeyTypValTriple{
617 {"foo", bsontype.String, &extJSONValue{t: bsontype.String, v: "bar"}},
618 {"baz", bsontype.Int32, &extJSONValue{t: bsontype.String, v: "42"}},
619 },
620 },
621 },
622 {
623 f: expectArray, p: ejp,
624 k: "Array", t: bsontype.Array,
625 v: []ejpKeyTypValTriple{
626 {typ: bsontype.Int32, val: &extJSONValue{t: bsontype.String, v: "1"}},
627 {typ: bsontype.Int64, val: &extJSONValue{t: bsontype.String, v: "2"}},
628 {typ: bsontype.Double, val: &extJSONValue{t: bsontype.String, v: "3"}},
629 {typ: bsontype.Int32, val: &extJSONValue{t: bsontype.Int32, v: int32(4)}},
630 {typ: bsontype.String, val: &extJSONValue{t: bsontype.String, v: "string"}},
631 {typ: bsontype.Double, val: &extJSONValue{t: bsontype.Double, v: 5.0}},
632 },
633 },
634 {
635 f: expectMultipleValues, p: ejp,
636 k: "Timestamp", t: bsontype.Timestamp,
637 v: &extJSONObject{
638 keys: []string{"t", "i"},
639 values: []*extJSONValue{
640 {t: bsontype.Int32, v: int32(42)},
641 {t: bsontype.Int32, v: int32(1)},
642 },
643 },
644 },
645 {
646 f: expectMultipleValues, p: ejp,
647 k: "RegularExpression", t: bsontype.Regex,
648 v: &extJSONObject{
649 keys: []string{"pattern", "options"},
650 values: []*extJSONValue{
651 {t: bsontype.String, v: "foo*"},
652 {t: bsontype.String, v: "ix"},
653 },
654 },
655 },
656 {
657 f: expectMultipleValues, p: ejp,
658 k: "DatetimeEpoch", t: bsontype.DateTime,
659 v: &extJSONObject{
660 keys: []string{"$numberLong"},
661 values: []*extJSONValue{
662 {t: bsontype.String, v: "0"},
663 },
664 },
665 },
666 {
667 f: expectMultipleValues, p: ejp,
668 k: "DatetimePositive", t: bsontype.DateTime,
669 v: &extJSONObject{
670 keys: []string{"$numberLong"},
671 values: []*extJSONValue{
672 {t: bsontype.String, v: "9223372036854775807"},
673 },
674 },
675 },
676 {
677 f: expectMultipleValues, p: ejp,
678 k: "DatetimeNegative", t: bsontype.DateTime,
679 v: &extJSONObject{
680 keys: []string{"$numberLong"},
681 values: []*extJSONValue{
682 {t: bsontype.String, v: "-9223372036854775808"},
683 },
684 },
685 },
686 {
687 f: expectSingleValue, p: ejp,
688 k: "True", t: bsontype.Boolean, v: &extJSONValue{t: bsontype.Boolean, v: true},
689 },
690 {
691 f: expectSingleValue, p: ejp,
692 k: "False", t: bsontype.Boolean, v: &extJSONValue{t: bsontype.Boolean, v: false},
693 },
694 {
695 f: expectMultipleValues, p: ejp,
696 k: "DBPointer", t: bsontype.DBPointer,
697 v: &extJSONObject{
698 keys: []string{"$ref", "$id"},
699 values: []*extJSONValue{
700 {t: bsontype.String, v: "db.collection"},
701 {t: bsontype.String, v: "57e193d7a9cc81b4027498b1"},
702 },
703 },
704 },
705 {
706 f: expectSubDocument, p: ejp,
707 k: "DBRef", t: bsontype.EmbeddedDocument,
708 v: ejpSubDocumentTestValue{
709 ktvs: []ejpKeyTypValTriple{
710 {"$ref", bsontype.String, &extJSONValue{t: bsontype.String, v: "collection"}},
711 {"$id", bsontype.ObjectID, &extJSONValue{t: bsontype.String, v: "57fd71e96e32ab4225b723fb"}},
712 {"$db", bsontype.String, &extJSONValue{t: bsontype.String, v: "database"}},
713 },
714 },
715 },
716 {
717 f: expectSubDocument, p: ejp,
718 k: "DBRefNoDB", t: bsontype.EmbeddedDocument,
719 v: ejpSubDocumentTestValue{
720 ktvs: []ejpKeyTypValTriple{
721 {"$ref", bsontype.String, &extJSONValue{t: bsontype.String, v: "collection"}},
722 {"$id", bsontype.ObjectID, &extJSONValue{t: bsontype.String, v: "57fd71e96e32ab4225b723fb"}},
723 },
724 },
725 },
726 {
727 f: expectSingleValue, p: ejp,
728 k: "MinKey", t: bsontype.MinKey, v: &extJSONValue{t: bsontype.Int32, v: int32(1)},
729 },
730 {
731 f: expectSingleValue, p: ejp,
732 k: "MaxKey", t: bsontype.MaxKey, v: &extJSONValue{t: bsontype.Int32, v: int32(1)},
733 },
734 {
735 f: expectSingleValue, p: ejp,
736 k: "Null", t: bsontype.Null, v: &extJSONValue{t: bsontype.Null, v: nil},
737 },
738 {
739 f: expectSingleValue, p: ejp,
740 k: "Undefined", t: bsontype.Undefined, v: &extJSONValue{t: bsontype.Boolean, v: true},
741 },
742 }
743
744
745 for _, tc := range cases {
746 tc.f(t, tc.p, tc.k, tc.t, tc.v)
747 }
748
749
750 k, typ, err := ejp.readKey()
751 readKeyDiff(t, "", k, bsontype.Type(0), typ, err, expectErrEOD, "")
752
753
754 k, typ, err = ejp.readKey()
755 readKeyDiff(t, "", k, bsontype.Type(0), typ, err, expectErrEOF, "")
756 if diff := cmp.Diff(jpsDoneState, ejp.s); diff != "" {
757 t.Errorf("expected parser to be in done state but instead is in %v\n", ejp.s)
758 t.FailNow()
759 }
760 }
761
762 func TestExtJSONValue(t *testing.T) {
763 t.Run("Large Date", func(t *testing.T) {
764 val := &extJSONValue{
765 t: bsontype.String,
766 v: "3001-01-01T00:00:00Z",
767 }
768
769 intVal, err := val.parseDateTime()
770 if err != nil {
771 t.Fatalf("error parsing date time: %v", err)
772 }
773
774 if intVal <= 0 {
775 t.Fatalf("expected value above 0, got %v", intVal)
776 }
777 })
778 t.Run("fallback time format", func(t *testing.T) {
779 val := &extJSONValue{
780 t: bsontype.String,
781 v: "2019-06-04T14:54:31.416+0000",
782 }
783
784 _, err := val.parseDateTime()
785 if err != nil {
786 t.Fatalf("error parsing date time: %v", err)
787 }
788 })
789 }
790
View as plain text