goext/mongo/x/bsonx/primitive_codecs_test.go

911 lines
32 KiB
Go
Raw Normal View History

2023-06-18 15:50:55 +02:00
// Copyright (C) MongoDB, Inc. 2022-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package bsonx
import (
"errors"
"fmt"
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/bsonrw/bsonrwtest"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
func TestDefaultValueEncoders(t *testing.T) {
var pcx PrimitiveCodecs
var wrong = func(string, string) string { return "wrong" }
type subtest struct {
name string
val interface{}
ectx *bsoncodec.EncodeContext
llvrw *bsonrwtest.ValueReaderWriter
invoke bsonrwtest.Invoked
err error
}
testCases := []struct {
name string
ve bsoncodec.ValueEncoder
subtests []subtest
}{
{
"ValueEncodeValue",
bsoncodec.ValueEncoderFunc(pcx.ValueEncodeValue),
[]subtest{
{
"wrong type",
wrong,
nil,
nil,
bsonrwtest.Nothing,
bsoncodec.ValueEncoderError{Name: "ValueEncodeValue", Types: []reflect.Type{tValue}, Received: reflect.ValueOf(wrong)},
},
{"empty value", Val{}, nil, nil, bsonrwtest.WriteNull, nil},
{
"success",
Null(),
&bsoncodec.EncodeContext{Registry: DefaultRegistry},
&bsonrwtest.ValueReaderWriter{},
bsonrwtest.WriteNull,
nil,
},
},
},
{
"ElementSliceEncodeValue",
bsoncodec.ValueEncoderFunc(pcx.ElementSliceEncodeValue),
[]subtest{
{
"wrong type",
wrong,
nil,
nil,
bsonrwtest.Nothing,
bsoncodec.ValueEncoderError{
Name: "ElementSliceEncodeValue",
Types: []reflect.Type{tElementSlice},
Received: reflect.ValueOf(wrong),
},
},
},
},
{
"ArrayEncodeValue",
bsoncodec.ValueEncoderFunc(pcx.ArrayEncodeValue),
[]subtest{
{
"wrong type",
wrong,
nil,
nil,
bsonrwtest.Nothing,
bsoncodec.ValueEncoderError{Name: "ArrayEncodeValue", Types: []reflect.Type{tArray}, Received: reflect.ValueOf(wrong)},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, subtest := range tc.subtests {
t.Run(subtest.name, func(t *testing.T) {
var ec bsoncodec.EncodeContext
if subtest.ectx != nil {
ec = *subtest.ectx
}
llvrw := new(bsonrwtest.ValueReaderWriter)
if subtest.llvrw != nil {
llvrw = subtest.llvrw
}
llvrw.T = t
err := tc.ve.EncodeValue(ec, llvrw, reflect.ValueOf(subtest.val))
if !compareErrors(err, subtest.err) {
t.Errorf("Errors do not match. got %v; want %v", err, subtest.err)
}
invoked := llvrw.Invoked
if !cmp.Equal(invoked, subtest.invoke) {
t.Errorf("Incorrect method invoked. got %v; want %v", invoked, subtest.invoke)
}
})
}
})
}
t.Run("DocumentEncodeValue", func(t *testing.T) {
t.Run("ValueEncoderError", func(t *testing.T) {
val := reflect.ValueOf(bool(true))
want := bsoncodec.ValueEncoderError{Name: "DocumentEncodeValue", Types: []reflect.Type{tDocument}, Received: val}
got := (PrimitiveCodecs{}).DocumentEncodeValue(bsoncodec.EncodeContext{}, nil, val)
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("WriteDocument Error", func(t *testing.T) {
want := errors.New("WriteDocument Error")
llvrw := &bsonrwtest.ValueReaderWriter{
T: t,
Err: want,
ErrAfter: bsonrwtest.WriteDocument,
}
got := (PrimitiveCodecs{}).DocumentEncodeValue(bsoncodec.EncodeContext{}, llvrw, reflect.MakeSlice(tDocument, 0, 0))
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("encodeDocument errors", func(t *testing.T) {
ec := bsoncodec.EncodeContext{}
err := errors.New("encodeDocument error")
oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}
testCases := []struct {
name string
ec bsoncodec.EncodeContext
llvrw *bsonrwtest.ValueReaderWriter
doc Doc
err error
}{
{
"WriteDocumentElement",
ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("wde error"), ErrAfter: bsonrwtest.WriteDocumentElement},
Doc{{"foo", Null()}},
errors.New("wde error"),
},
{
"WriteDouble", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDouble},
Doc{{"foo", Double(3.14159)}}, err,
},
{
"WriteString", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteString},
Doc{{"foo", String("bar")}}, err,
},
{
"WriteDocument (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t},
Doc{{"foo", Document(Doc{{"bar", Null()}})}},
bsoncodec.ErrNoEncoder{Type: tDocument},
},
{
"WriteArray (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t},
Doc{{"foo", Array(Arr{Null()})}},
bsoncodec.ErrNoEncoder{Type: tArray},
},
{
"WriteBinary", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBinaryWithSubtype},
Doc{{"foo", Binary(0xFF, []byte{0x01, 0x02, 0x03})}}, err,
},
{
"WriteUndefined", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteUndefined},
Doc{{"foo", Undefined()}}, err,
},
{
"WriteObjectID", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteObjectID},
Doc{{"foo", ObjectID(oid)}}, err,
},
{
"WriteBoolean", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBoolean},
Doc{{"foo", Boolean(true)}}, err,
},
{
"WriteDateTime", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDateTime},
Doc{{"foo", DateTime(1234567890)}}, err,
},
{
"WriteNull", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteNull},
Doc{{"foo", Null()}}, err,
},
{
"WriteRegex", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteRegex},
Doc{{"foo", Regex("bar", "baz")}}, err,
},
{
"WriteDBPointer", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDBPointer},
Doc{{"foo", DBPointer("bar", oid)}}, err,
},
{
"WriteJavascript", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteJavascript},
Doc{{"foo", JavaScript("var hello = 'world';")}}, err,
},
{
"WriteSymbol", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteSymbol},
Doc{{"foo", Symbol("symbolbaz")}}, err,
},
{
"WriteCodeWithScope (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteCodeWithScope},
Doc{{"foo", CodeWithScope("var hello = 'world';", Doc{}.Append("bar", Null()))}},
err,
},
{
"WriteInt32", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt32},
Doc{{"foo", Int32(12345)}}, err,
},
{
"WriteInt64", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt64},
Doc{{"foo", Int64(1234567890)}}, err,
},
{
"WriteTimestamp", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteTimestamp},
Doc{{"foo", Timestamp(10, 20)}}, err,
},
{
"WriteDecimal128", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDecimal128},
Doc{{"foo", Decimal128(primitive.NewDecimal128(10, 20))}}, err,
},
{
"WriteMinKey", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMinKey},
Doc{{"foo", MinKey()}}, err,
},
{
"WriteMaxKey", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMaxKey},
Doc{{"foo", MaxKey()}}, err,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := (PrimitiveCodecs{}).DocumentEncodeValue(tc.ec, tc.llvrw, reflect.ValueOf(tc.doc))
if !compareErrors(err, tc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
}
})
}
})
t.Run("success", func(t *testing.T) {
oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}
d128 := primitive.NewDecimal128(10, 20)
want := Doc{
{"a", Double(3.14159)}, {"b", String("foo")},
{"c", Document(Doc{{"aa", Null()}})}, {"d", Array(Arr{Null()})},
{"e", Binary(0xFF, []byte{0x01, 0x02, 0x03})}, {"f", Undefined()},
{"g", ObjectID(oid)}, {"h", Boolean(true)},
{"i", DateTime(1234567890)}, {"j", Null()},
{"k", Regex("foo", "abr")},
{"l", DBPointer("foobar", oid)}, {"m", JavaScript("var hello = 'world';")},
{"n", Symbol("bazqux")},
{"o", CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}})},
{"p", Int32(12345)},
{"q", Timestamp(10, 20)}, {"r", Int64(1234567890)}, {"s", Decimal128(d128)}, {"t", MinKey()}, {"u", MaxKey()},
}
slc := make(bsonrw.SliceWriter, 0, 128)
vw, err := bsonrw.NewBSONValueWriter(&slc)
noerr(t, err)
ec := bsoncodec.EncodeContext{Registry: DefaultRegistry}
err = (PrimitiveCodecs{}).DocumentEncodeValue(ec, vw, reflect.ValueOf(want))
noerr(t, err)
got, err := ReadDoc(slc)
noerr(t, err)
if !got.Equal(want) {
t.Error("Documents do not match")
t.Errorf("\ngot :%v\nwant:%v", got, want)
}
})
})
t.Run("ArrayEncodeValue", func(t *testing.T) {
t.Run("CodecEncodeError", func(t *testing.T) {
val := reflect.ValueOf(bool(true))
want := bsoncodec.ValueEncoderError{Name: "ArrayEncodeValue", Types: []reflect.Type{tArray}, Received: val}
got := (PrimitiveCodecs{}).ArrayEncodeValue(bsoncodec.EncodeContext{}, nil, val)
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("WriteArray Error", func(t *testing.T) {
want := errors.New("WriteArray Error")
llvrw := &bsonrwtest.ValueReaderWriter{
T: t,
Err: want,
ErrAfter: bsonrwtest.WriteArray,
}
got := (PrimitiveCodecs{}).ArrayEncodeValue(bsoncodec.EncodeContext{}, llvrw, reflect.MakeSlice(tArray, 0, 0))
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("encode array errors", func(t *testing.T) {
ec := bsoncodec.EncodeContext{}
err := errors.New("encode array error")
oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}
testCases := []struct {
name string
ec bsoncodec.EncodeContext
llvrw *bsonrwtest.ValueReaderWriter
arr Arr
err error
}{
{
"WriteDocumentElement",
ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("wde error"), ErrAfter: bsonrwtest.WriteArrayElement},
Arr{Null()},
errors.New("wde error"),
},
{
"WriteDouble", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDouble},
Arr{Double(3.14159)}, err,
},
{
"WriteString", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteString},
Arr{String("bar")}, err,
},
{
"WriteDocument (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t},
Arr{Document(Doc{{"bar", Null()}})},
bsoncodec.ErrNoEncoder{Type: tDocument},
},
{
"WriteArray (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t},
Arr{Array(Arr{Null()})},
bsoncodec.ErrNoEncoder{Type: tArray},
},
{
"WriteBinary", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBinaryWithSubtype},
Arr{Binary(0xFF, []byte{0x01, 0x02, 0x03})}, err,
},
{
"WriteUndefined", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteUndefined},
Arr{Undefined()}, err,
},
{
"WriteObjectID", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteObjectID},
Arr{ObjectID(oid)}, err,
},
{
"WriteBoolean", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBoolean},
Arr{Boolean(true)}, err,
},
{
"WriteDateTime", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDateTime},
Arr{DateTime(1234567890)}, err,
},
{
"WriteNull", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteNull},
Arr{Null()}, err,
},
{
"WriteRegex", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteRegex},
Arr{Regex("bar", "baz")}, err,
},
{
"WriteDBPointer", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDBPointer},
Arr{DBPointer("bar", oid)}, err,
},
{
"WriteJavascript", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteJavascript},
Arr{JavaScript("var hello = 'world';")}, err,
},
{
"WriteSymbol", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteSymbol},
Arr{Symbol("symbolbaz")}, err,
},
{
"WriteCodeWithScope (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteCodeWithScope},
Arr{CodeWithScope("var hello = 'world';", Doc{{"bar", Null()}})},
err,
},
{
"WriteInt32", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt32},
Arr{Int32(12345)}, err,
},
{
"WriteInt64", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt64},
Arr{Int64(1234567890)}, err,
},
{
"WriteTimestamp", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteTimestamp},
Arr{Timestamp(10, 20)}, err,
},
{
"WriteDecimal128", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDecimal128},
Arr{Decimal128(primitive.NewDecimal128(10, 20))}, err,
},
{
"WriteMinKey", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMinKey},
Arr{MinKey()}, err,
},
{
"WriteMaxKey", ec,
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMaxKey},
Arr{MaxKey()}, err,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := (PrimitiveCodecs{}).ArrayEncodeValue(tc.ec, tc.llvrw, reflect.ValueOf(tc.arr))
if !compareErrors(err, tc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
}
})
}
})
t.Run("success", func(t *testing.T) {
oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}
d128 := primitive.NewDecimal128(10, 20)
want := Arr{
Double(3.14159), String("foo"), Document(Doc{{"aa", Null()}}),
Array(Arr{Null()}),
Binary(0xFF, []byte{0x01, 0x02, 0x03}), Undefined(),
ObjectID(oid), Boolean(true), DateTime(1234567890), Null(), Regex("foo", "abr"),
DBPointer("foobar", oid), JavaScript("var hello = 'world';"), Symbol("bazqux"),
CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}}), Int32(12345),
Timestamp(10, 20), Int64(1234567890), Decimal128(d128), MinKey(), MaxKey(),
}
ec := bsoncodec.EncodeContext{Registry: DefaultRegistry}
slc := make(bsonrw.SliceWriter, 0, 128)
vw, err := bsonrw.NewBSONValueWriter(&slc)
noerr(t, err)
dr, err := vw.WriteDocument()
noerr(t, err)
vr, err := dr.WriteDocumentElement("foo")
noerr(t, err)
err = (PrimitiveCodecs{}).ArrayEncodeValue(ec, vr, reflect.ValueOf(want))
noerr(t, err)
err = dr.WriteDocumentEnd()
noerr(t, err)
val, err := bsoncore.Document(slc).LookupErr("foo")
noerr(t, err)
rgot := val.Array()
doc, err := ReadDoc(rgot)
noerr(t, err)
got := make(Arr, 0)
for _, elem := range doc {
got = append(got, elem.Value)
}
if !got.Equal(want) {
t.Error("Documents do not match")
t.Errorf("\ngot :%v\nwant:%v", got, want)
}
})
})
}
func TestDefaultValueDecoders(t *testing.T) {
var pcx PrimitiveCodecs
var wrong = func(string, string) string { return "wrong" }
const cansetreflectiontest = "cansetreflectiontest"
type subtest struct {
name string
val interface{}
dctx *bsoncodec.DecodeContext
llvrw *bsonrwtest.ValueReaderWriter
invoke bsonrwtest.Invoked
err error
}
testCases := []struct {
name string
vd bsoncodec.ValueDecoder
subtests []subtest
}{
{
"ValueDecodeValue",
bsoncodec.ValueDecoderFunc(pcx.ValueDecodeValue),
[]subtest{
{
"wrong type",
wrong,
nil,
nil,
bsonrwtest.Nothing,
bsoncodec.ValueDecoderError{
Name: "ValueDecodeValue",
Types: []reflect.Type{tValue},
Received: reflect.ValueOf(wrong),
},
},
{
"invalid value",
(*Val)(nil),
nil,
nil,
bsonrwtest.Nothing,
bsoncodec.ValueDecoderError{
Name: "ValueDecodeValue",
Types: []reflect.Type{tValue},
Received: reflect.ValueOf((*Val)(nil)),
},
},
{
"success",
Double(3.14159),
&bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14159)},
bsonrwtest.ReadDouble,
nil,
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, rc := range tc.subtests {
t.Run(rc.name, func(t *testing.T) {
var dc bsoncodec.DecodeContext
if rc.dctx != nil {
dc = *rc.dctx
}
llvrw := new(bsonrwtest.ValueReaderWriter)
if rc.llvrw != nil {
llvrw = rc.llvrw
}
llvrw.T = t
// var got interface{}
if rc.val == cansetreflectiontest { // We're doing a CanSet reflection test
err := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})
if !compareErrors(err, rc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
}
val := reflect.New(reflect.TypeOf(rc.val)).Elem()
err = tc.vd.DecodeValue(dc, llvrw, val)
if !compareErrors(err, rc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
}
return
}
var val reflect.Value
if rtype := reflect.TypeOf(rc.val); rtype != nil {
val = reflect.New(rtype).Elem()
}
want := rc.val
defer func() {
if err := recover(); err != nil {
fmt.Println(t.Name())
panic(err)
}
}()
err := tc.vd.DecodeValue(dc, llvrw, val)
if !compareErrors(err, rc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
}
invoked := llvrw.Invoked
if !cmp.Equal(invoked, rc.invoke) {
t.Errorf("Incorrect method invoked. got %v; want %v", invoked, rc.invoke)
}
var got interface{}
if val.IsValid() && val.CanInterface() {
got = val.Interface()
}
if rc.err == nil && !cmp.Equal(got, want, cmp.Comparer(compareValues)) {
t.Errorf("Values do not match. got (%T)%v; want (%T)%v", got, got, want, want)
}
})
}
})
}
t.Run("DocumentDecodeValue", func(t *testing.T) {
t.Run("CodecDecodeError", func(t *testing.T) {
val := reflect.New(reflect.TypeOf(false)).Elem()
want := bsoncodec.ValueDecoderError{Name: "DocumentDecodeValue", Types: []reflect.Type{tDocument}, Received: val}
got := pcx.DocumentDecodeValue(bsoncodec.DecodeContext{}, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.EmbeddedDocument}, val)
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("ReadDocument Error", func(t *testing.T) {
want := errors.New("ReadDocument Error")
llvrw := &bsonrwtest.ValueReaderWriter{
T: t,
Err: want,
ErrAfter: bsonrwtest.ReadDocument,
BSONType: bsontype.EmbeddedDocument,
}
got := pcx.DocumentDecodeValue(bsoncodec.DecodeContext{}, llvrw, reflect.New(reflect.TypeOf(Doc{})).Elem())
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("decodeDocument errors", func(t *testing.T) {
dc := bsoncodec.DecodeContext{}
err := errors.New("decodeDocument error")
testCases := []struct {
name string
dc bsoncodec.DecodeContext
llvrw *bsonrwtest.ValueReaderWriter
err error
}{
{
"ReadElement",
dc,
&bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("re error"), ErrAfter: bsonrwtest.ReadElement},
errors.New("re error"),
},
{"ReadDouble", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDouble, BSONType: bsontype.Double}, err},
{"ReadString", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadString, BSONType: bsontype.String}, err},
{"ReadBinary", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBinary, BSONType: bsontype.Binary}, err},
{"ReadUndefined", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadUndefined, BSONType: bsontype.Undefined}, err},
{"ReadObjectID", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadObjectID, BSONType: bsontype.ObjectID}, err},
{"ReadBoolean", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBoolean, BSONType: bsontype.Boolean}, err},
{"ReadDateTime", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDateTime, BSONType: bsontype.DateTime}, err},
{"ReadNull", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadNull, BSONType: bsontype.Null}, err},
{"ReadRegex", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadRegex, BSONType: bsontype.Regex}, err},
{"ReadDBPointer", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDBPointer, BSONType: bsontype.DBPointer}, err},
{"ReadJavascript", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadJavascript, BSONType: bsontype.JavaScript}, err},
{"ReadSymbol", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadSymbol, BSONType: bsontype.Symbol}, err},
{
"ReadCodeWithScope (Lookup)", bsoncodec.DecodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadCodeWithScope, BSONType: bsontype.CodeWithScope},
err,
},
{"ReadInt32", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt32, BSONType: bsontype.Int32}, err},
{"ReadInt64", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt64, BSONType: bsontype.Int64}, err},
{"ReadTimestamp", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadTimestamp, BSONType: bsontype.Timestamp}, err},
{"ReadDecimal128", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDecimal128, BSONType: bsontype.Decimal128}, err},
{"ReadMinKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMinKey, BSONType: bsontype.MinKey}, err},
{"ReadMaxKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMaxKey, BSONType: bsontype.MaxKey}, err},
{"Invalid Type", dc, &bsonrwtest.ValueReaderWriter{T: t, BSONType: bsontype.Type(0)}, fmt.Errorf("Cannot read unknown BSON type %s", bsontype.Type(0))},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := pcx.DecodeDocument(tc.dc, tc.llvrw, new(Doc))
if !compareErrors(err, tc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
}
})
}
})
t.Run("success", func(t *testing.T) {
oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}
d128 := primitive.NewDecimal128(10, 20)
want := Doc{
{"a", Double(3.14159)}, {"b", String("foo")},
{"c", Document(Doc{{"aa", Null()}})},
{"d", Array(Arr{Null()})},
{"e", Binary(0xFF, []byte{0x01, 0x02, 0x03})}, {"f", Undefined()},
{"g", ObjectID(oid)}, {"h", Boolean(true)},
{"i", DateTime(1234567890)}, {"j", Null()}, {"k", Regex("foo", "bar")},
{"l", DBPointer("foobar", oid)}, {"m", JavaScript("var hello = 'world';")},
{"n", Symbol("bazqux")},
{"o", CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}})},
{"p", Int32(12345)},
{"q", Timestamp(10, 20)}, {"r", Int64(1234567890)},
{"s", Decimal128(d128)}, {"t", MinKey()}, {"u", MaxKey()},
}
got := reflect.New(reflect.TypeOf(Doc{})).Elem()
dc := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()}
b, err := want.MarshalBSON()
noerr(t, err)
err = pcx.DocumentDecodeValue(dc, bsonrw.NewBSONDocumentReader(b), got)
noerr(t, err)
if !got.Interface().(Doc).Equal(want) {
t.Error("Documents do not match")
t.Errorf("\ngot :%v\nwant:%v", got, want)
}
})
})
t.Run("ArrayDecodeValue", func(t *testing.T) {
t.Run("CodecDecodeError", func(t *testing.T) {
val := reflect.New(reflect.TypeOf(false)).Elem()
want := bsoncodec.ValueDecoderError{Name: "ArrayDecodeValue", Types: []reflect.Type{tArray}, Received: val}
got := pcx.ArrayDecodeValue(bsoncodec.DecodeContext{}, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array}, val)
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("ReadArray Error", func(t *testing.T) {
want := errors.New("ReadArray Error")
llvrw := &bsonrwtest.ValueReaderWriter{
T: t,
Err: want,
ErrAfter: bsonrwtest.ReadArray,
BSONType: bsontype.Array,
}
got := pcx.ArrayDecodeValue(bsoncodec.DecodeContext{}, llvrw, reflect.New(tArray).Elem())
if !compareErrors(got, want) {
t.Errorf("Errors do not match. got %v; want %v", got, want)
}
})
t.Run("decode array errors", func(t *testing.T) {
dc := bsoncodec.DecodeContext{}
err := errors.New("decode array error")
testCases := []struct {
name string
dc bsoncodec.DecodeContext
llvrw *bsonrwtest.ValueReaderWriter
err error
}{
{
"ReadValue",
dc,
&bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("re error"), ErrAfter: bsonrwtest.ReadValue},
errors.New("re error"),
},
{"ReadDouble", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDouble, BSONType: bsontype.Double}, err},
{"ReadString", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadString, BSONType: bsontype.String}, err},
{"ReadBinary", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBinary, BSONType: bsontype.Binary}, err},
{"ReadUndefined", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadUndefined, BSONType: bsontype.Undefined}, err},
{"ReadObjectID", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadObjectID, BSONType: bsontype.ObjectID}, err},
{"ReadBoolean", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBoolean, BSONType: bsontype.Boolean}, err},
{"ReadDateTime", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDateTime, BSONType: bsontype.DateTime}, err},
{"ReadNull", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadNull, BSONType: bsontype.Null}, err},
{"ReadRegex", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadRegex, BSONType: bsontype.Regex}, err},
{"ReadDBPointer", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDBPointer, BSONType: bsontype.DBPointer}, err},
{"ReadJavascript", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadJavascript, BSONType: bsontype.JavaScript}, err},
{"ReadSymbol", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadSymbol, BSONType: bsontype.Symbol}, err},
{
"ReadCodeWithScope (Lookup)", bsoncodec.DecodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()},
&bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadCodeWithScope, BSONType: bsontype.CodeWithScope},
err,
},
{"ReadInt32", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt32, BSONType: bsontype.Int32}, err},
{"ReadInt64", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt64, BSONType: bsontype.Int64}, err},
{"ReadTimestamp", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadTimestamp, BSONType: bsontype.Timestamp}, err},
{"ReadDecimal128", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDecimal128, BSONType: bsontype.Decimal128}, err},
{"ReadMinKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMinKey, BSONType: bsontype.MinKey}, err},
{"ReadMaxKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMaxKey, BSONType: bsontype.MaxKey}, err},
{"Invalid Type", dc, &bsonrwtest.ValueReaderWriter{T: t, BSONType: bsontype.Type(0)}, fmt.Errorf("Cannot read unknown BSON type %s", bsontype.Type(0))},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := pcx.ArrayDecodeValue(tc.dc, tc.llvrw, reflect.New(tArray).Elem())
if !compareErrors(err, tc.err) {
t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
}
})
}
})
t.Run("success", func(t *testing.T) {
oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}
d128 := primitive.NewDecimal128(10, 20)
want := Arr{
Double(3.14159), String("foo"), Document(Doc{{"aa", Null()}}),
Array(Arr{Null()}),
Binary(0xFF, []byte{0x01, 0x02, 0x03}), Undefined(),
ObjectID(oid), Boolean(true), DateTime(1234567890), Null(), Regex("foo", "bar"),
DBPointer("foobar", oid), JavaScript("var hello = 'world';"), Symbol("bazqux"),
CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}}), Int32(12345),
Timestamp(10, 20), Int64(1234567890), Decimal128(d128), MinKey(), MaxKey(),
}
dc := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()}
b, err := Doc{{"", Array(want)}}.MarshalBSON()
noerr(t, err)
dvr := bsonrw.NewBSONDocumentReader(b)
dr, err := dvr.ReadDocument()
noerr(t, err)
_, vr, err := dr.ReadElement()
noerr(t, err)
val := reflect.New(tArray).Elem()
err = pcx.ArrayDecodeValue(dc, vr, val)
noerr(t, err)
got := val.Interface().(Arr)
if !got.Equal(want) {
t.Error("Documents do not match")
t.Errorf("\ngot :%v\nwant:%v", got, want)
}
})
})
t.Run("success path", func(t *testing.T) {
testCases := []struct {
name string
value interface{}
b []byte
err error
}{
{
"map[string][]Element",
map[string][]Elem{"Z": {{"A", Int32(1)}, {"B", Int32(2)}, {"EC", Int32(3)}}},
docToBytes(Doc{{"Z", Document(Doc{{"A", Int32(1)}, {"B", Int32(2)}, {"EC", Int32(3)}})}}),
nil,
},
{
"map[string][]Value",
map[string][]Val{"Z": {Int32(1), Int32(2), Int32(3)}},
docToBytes(Doc{{"Z", Array(Arr{Int32(1), Int32(2), Int32(3)})}}),
nil,
},
{
"map[string]*Document",
map[string]Doc{"Z": {{"foo", Null()}}},
docToBytes(Doc{{"Z", Document(Doc{{"foo", Null()}})}}),
nil,
},
}
t.Run("Decode", func(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
vr := bsonrw.NewBSONDocumentReader(tc.b)
dec, err := bson.NewDecoderWithContext(bsoncodec.DecodeContext{Registry: DefaultRegistry}, vr)
noerr(t, err)
gotVal := reflect.New(reflect.TypeOf(tc.value))
err = dec.Decode(gotVal.Interface())
noerr(t, err)
got := gotVal.Elem().Interface()
want := tc.value
if diff := cmp.Diff(
got, want,
); diff != "" {
t.Errorf("difference:\n%s", diff)
t.Errorf("Values are not equal.\ngot: %#v\nwant:%#v", got, want)
}
})
}
})
})
}
func compareValues(v1, v2 Val) bool { return v1.Equal(v2) }
func docToBytes(d Doc) []byte {
b, err := d.MarshalBSON()
if err != nil {
panic(err)
}
return b
}