1
2
3
4
5
6
7 package bson
8
9 import (
10 "bytes"
11 "fmt"
12 "reflect"
13 "strconv"
14 "strings"
15 "testing"
16 "time"
17
18 "github.com/google/go-cmp/cmp"
19 "go.mongodb.org/mongo-driver/bson/bsoncodec"
20 "go.mongodb.org/mongo-driver/bson/bsonoptions"
21 "go.mongodb.org/mongo-driver/bson/bsontype"
22 "go.mongodb.org/mongo-driver/internal/assert"
23 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
24 )
25
26 func noerr(t *testing.T, err error) {
27 if err != nil {
28 t.Helper()
29 t.Errorf("Unexpected error: (%T)%v", err, err)
30 t.FailNow()
31 }
32 }
33
34 func TestTimeRoundTrip(t *testing.T) {
35 val := struct {
36 Value time.Time
37 ID string
38 }{
39 ID: "time-rt-test",
40 }
41
42 if !val.Value.IsZero() {
43 t.Errorf("Did not get zero time as expected.")
44 }
45
46 bsonOut, err := Marshal(val)
47 noerr(t, err)
48 rtval := struct {
49 Value time.Time
50 ID string
51 }{}
52
53 err = Unmarshal(bsonOut, &rtval)
54 noerr(t, err)
55 if !cmp.Equal(val, rtval) {
56 t.Errorf("Did not round trip properly. got %v; want %v", val, rtval)
57 }
58 if !rtval.Value.IsZero() {
59 t.Errorf("Did not get zero time as expected.")
60 }
61 }
62
63 func TestNonNullTimeRoundTrip(t *testing.T) {
64 now := time.Now()
65 now = time.Unix(now.Unix(), 0)
66 val := struct {
67 Value time.Time
68 ID string
69 }{
70 ID: "time-rt-test",
71 Value: now,
72 }
73
74 bsonOut, err := Marshal(val)
75 noerr(t, err)
76 rtval := struct {
77 Value time.Time
78 ID string
79 }{}
80
81 err = Unmarshal(bsonOut, &rtval)
82 noerr(t, err)
83 if !cmp.Equal(val, rtval) {
84 t.Errorf("Did not round trip properly. got %v; want %v", val, rtval)
85 }
86 }
87
88 func TestD(t *testing.T) {
89 t.Run("can marshal", func(t *testing.T) {
90 d := D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
91 idx, want := bsoncore.AppendDocumentStart(nil)
92 want = bsoncore.AppendStringElement(want, "foo", "bar")
93 want = bsoncore.AppendStringElement(want, "hello", "world")
94 want = bsoncore.AppendDoubleElement(want, "pi", 3.14159)
95 want, err := bsoncore.AppendDocumentEnd(want, idx)
96 noerr(t, err)
97 got, err := Marshal(d)
98 noerr(t, err)
99 if !bytes.Equal(got, want) {
100 t.Errorf("Marshaled documents do not match. got %v; want %v", Raw(got), Raw(want))
101 }
102 })
103 t.Run("can unmarshal", func(t *testing.T) {
104 want := D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
105 idx, doc := bsoncore.AppendDocumentStart(nil)
106 doc = bsoncore.AppendStringElement(doc, "foo", "bar")
107 doc = bsoncore.AppendStringElement(doc, "hello", "world")
108 doc = bsoncore.AppendDoubleElement(doc, "pi", 3.14159)
109 doc, err := bsoncore.AppendDocumentEnd(doc, idx)
110 noerr(t, err)
111 var got D
112 err = Unmarshal(doc, &got)
113 noerr(t, err)
114 if !cmp.Equal(got, want) {
115 t.Errorf("Unmarshaled documents do not match. got %v; want %v", got, want)
116 }
117 })
118 }
119
120 type stringerString string
121
122 func (ss stringerString) String() string {
123 return "bar"
124 }
125
126 type keyBool bool
127
128 func (kb keyBool) MarshalKey() (string, error) {
129 return fmt.Sprintf("%v", kb), nil
130 }
131
132 func (kb *keyBool) UnmarshalKey(key string) error {
133 switch key {
134 case "true":
135 *kb = true
136 case "false":
137 *kb = false
138 default:
139 return fmt.Errorf("invalid bool value %v", key)
140 }
141 return nil
142 }
143
144 type keyStruct struct {
145 val int64
146 }
147
148 func (k keyStruct) MarshalText() (text []byte, err error) {
149 str := strconv.FormatInt(k.val, 10)
150
151 return []byte(str), nil
152 }
153
154 func (k *keyStruct) UnmarshalText(text []byte) error {
155 val, err := strconv.ParseInt(string(text), 10, 64)
156 if err != nil {
157 return err
158 }
159
160 *k = keyStruct{
161 val: val,
162 }
163
164 return nil
165 }
166
167 func TestMapCodec(t *testing.T) {
168 t.Run("EncodeKeysWithStringer", func(t *testing.T) {
169 strstr := stringerString("foo")
170 mapObj := map[stringerString]int{strstr: 1}
171 testCases := []struct {
172 name string
173 opts *bsonoptions.MapCodecOptions
174 key string
175 }{
176 {"default", bsonoptions.MapCodec(), "foo"},
177 {"true", bsonoptions.MapCodec().SetEncodeKeysWithStringer(true), "bar"},
178 {"false", bsonoptions.MapCodec().SetEncodeKeysWithStringer(false), "foo"},
179 }
180 for _, tc := range testCases {
181 t.Run(tc.name, func(t *testing.T) {
182 mapCodec := bsoncodec.NewMapCodec(tc.opts)
183 mapRegistry := NewRegistryBuilder().RegisterDefaultEncoder(reflect.Map, mapCodec).Build()
184 val, err := MarshalWithRegistry(mapRegistry, mapObj)
185 assert.Nil(t, err, "Marshal error: %v", err)
186 assert.True(t, strings.Contains(string(val), tc.key), "expected result to contain %v, got: %v", tc.key, string(val))
187 })
188 }
189 })
190
191 t.Run("keys implements keyMarshaler and keyUnmarshaler", func(t *testing.T) {
192 mapObj := map[keyBool]int{keyBool(true): 1}
193
194 doc, err := Marshal(mapObj)
195 assert.Nil(t, err, "Marshal error: %v", err)
196 idx, want := bsoncore.AppendDocumentStart(nil)
197 want = bsoncore.AppendInt32Element(want, "true", 1)
198 want, _ = bsoncore.AppendDocumentEnd(want, idx)
199 assert.Equal(t, want, doc, "expected result %v, got %v", string(want), string(doc))
200
201 var got map[keyBool]int
202 err = Unmarshal(doc, &got)
203 assert.Nil(t, err, "Unmarshal error: %v", err)
204 assert.Equal(t, mapObj, got, "expected result %v, got %v", mapObj, got)
205
206 })
207
208 t.Run("keys implements encoding.TextMarshaler and encoding.TextUnmarshaler", func(t *testing.T) {
209 mapObj := map[keyStruct]int{
210 {val: 10}: 100,
211 }
212
213 doc, err := Marshal(mapObj)
214 assert.Nil(t, err, "Marshal error: %v", err)
215 idx, want := bsoncore.AppendDocumentStart(nil)
216 want = bsoncore.AppendInt32Element(want, "10", 100)
217 want, _ = bsoncore.AppendDocumentEnd(want, idx)
218 assert.Equal(t, want, doc, "expected result %v, got %v", string(want), string(doc))
219
220 var got map[keyStruct]int
221 err = Unmarshal(doc, &got)
222 assert.Nil(t, err, "Unmarshal error: %v", err)
223 assert.Equal(t, mapObj, got, "expected result %v, got %v", mapObj, got)
224
225 })
226 }
227
228 func TestExtJSONEscapeKey(t *testing.T) {
229 doc := D{{Key: "\\usb#", Value: int32(1)}}
230 b, err := MarshalExtJSON(&doc, false, false)
231 noerr(t, err)
232
233 want := "{\"\\\\usb#\":1}"
234 if diff := cmp.Diff(want, string(b)); diff != "" {
235 t.Errorf("Marshaled documents do not match. got %v, want %v", string(b), want)
236 }
237
238 var got D
239 err = UnmarshalExtJSON(b, false, &got)
240 noerr(t, err)
241 if !cmp.Equal(got, doc) {
242 t.Errorf("Unmarshaled documents do not match. got %v; want %v", got, doc)
243 }
244 }
245
246 func TestBsoncoreArray(t *testing.T) {
247 type BSONDocumentArray struct {
248 Array []D `bson:"array"`
249 }
250
251 type BSONArray struct {
252 Array bsoncore.Array `bson:"array"`
253 }
254
255 bda := BSONDocumentArray{
256 Array: []D{
257 {{"x", 1}},
258 {{"x", 2}},
259 {{"x", 3}},
260 },
261 }
262
263 expectedBSON, err := Marshal(bda)
264 assert.Nil(t, err, "Marshal bsoncore.Document array error: %v", err)
265
266 var ba BSONArray
267 err = Unmarshal(expectedBSON, &ba)
268 assert.Nil(t, err, "Unmarshal error: %v", err)
269
270 actualBSON, err := Marshal(ba)
271 assert.Nil(t, err, "Marshal bsoncore.Array error: %v", err)
272
273 assert.Equal(t, expectedBSON, actualBSON,
274 "expected BSON to be %v after Marshalling again; got %v", expectedBSON, actualBSON)
275
276 doc := bsoncore.Document(actualBSON)
277 v := doc.Lookup("array")
278 assert.Equal(t, bsontype.Array, v.Type, "expected type array, got %v", v.Type)
279 }
280
View as plain text