bson 4.2.2 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +25 -7
- data/Rakefile +16 -9
- data/ext/bson/{native-endian.h → bson-endian.h} +5 -99
- data/ext/bson/bson-native.h +125 -0
- data/ext/bson/bytebuf.c +133 -0
- data/ext/bson/endian.c +117 -0
- data/ext/bson/init.c +355 -0
- data/ext/bson/libbson-utf8.c +230 -0
- data/ext/bson/read.c +411 -0
- data/ext/bson/util.c +95 -0
- data/ext/bson/write.c +680 -0
- data/lib/bson.rb +6 -3
- data/lib/bson/active_support.rb +17 -0
- data/lib/bson/array.rb +57 -17
- data/lib/bson/binary.rb +185 -13
- data/lib/bson/boolean.rb +12 -3
- data/lib/bson/code.rb +16 -2
- data/lib/bson/code_with_scope.rb +32 -5
- data/lib/bson/config.rb +1 -1
- data/lib/bson/date.rb +12 -2
- data/lib/bson/date_time.rb +2 -2
- data/lib/bson/db_pointer.rb +110 -0
- data/lib/bson/decimal128.rb +17 -3
- data/lib/bson/decimal128/builder.rb +1 -1
- data/lib/bson/document.rb +152 -5
- data/lib/bson/environment.rb +2 -1
- data/lib/bson/error.rb +27 -0
- data/lib/bson/ext_json.rb +383 -0
- data/lib/bson/false_class.rb +1 -1
- data/lib/bson/float.rb +48 -2
- data/lib/bson/hash.rb +68 -17
- data/lib/bson/int32.rb +52 -13
- data/lib/bson/int64.rb +59 -15
- data/lib/bson/integer.rb +36 -2
- data/lib/bson/json.rb +1 -1
- data/lib/bson/max_key.rb +13 -1
- data/lib/bson/min_key.rb +13 -1
- data/lib/bson/nil_class.rb +4 -2
- data/lib/bson/object.rb +28 -1
- data/lib/bson/object_id.rb +16 -2
- data/lib/bson/open_struct.rb +1 -1
- data/lib/bson/regexp.rb +27 -4
- data/lib/bson/registry.rb +3 -3
- data/lib/bson/specialized.rb +4 -2
- data/lib/bson/string.rb +5 -3
- data/lib/bson/symbol.rb +99 -7
- data/lib/bson/time.rb +63 -4
- data/lib/bson/time_with_zone.rb +54 -0
- data/lib/bson/timestamp.rb +44 -6
- data/lib/bson/true_class.rb +1 -1
- data/lib/bson/undefined.rb +12 -1
- data/lib/bson/version.rb +2 -2
- data/spec/bson/array_spec.rb +18 -1
- data/spec/bson/binary_spec.rb +100 -3
- data/spec/bson/binary_uuid_spec.rb +189 -0
- data/spec/bson/boolean_spec.rb +1 -1
- data/spec/bson/byte_buffer_read_spec.rb +197 -0
- data/spec/bson/byte_buffer_spec.rb +121 -381
- data/spec/bson/byte_buffer_write_spec.rb +854 -0
- data/spec/bson/code_spec.rb +1 -1
- data/spec/bson/code_with_scope_spec.rb +1 -1
- data/spec/bson/date_spec.rb +1 -1
- data/spec/bson/date_time_spec.rb +54 -1
- data/spec/bson/decimal128_spec.rb +35 -35
- data/spec/bson/document_as_spec.rb +46 -0
- data/spec/bson/document_spec.rb +197 -30
- data/spec/bson/ext_json_parse_spec.rb +308 -0
- data/spec/bson/false_class_spec.rb +1 -1
- data/spec/bson/float_spec.rb +37 -1
- data/spec/bson/hash_as_spec.rb +57 -0
- data/spec/bson/hash_spec.rb +209 -1
- data/spec/bson/int32_spec.rb +180 -6
- data/spec/bson/int64_spec.rb +199 -6
- data/spec/bson/integer_spec.rb +29 -3
- data/spec/bson/json_spec.rb +1 -1
- data/spec/bson/max_key_spec.rb +1 -1
- data/spec/bson/min_key_spec.rb +1 -1
- data/spec/bson/nil_class_spec.rb +1 -1
- data/spec/bson/object_id_spec.rb +1 -1
- data/spec/bson/object_spec.rb +1 -1
- data/spec/bson/open_struct_spec.rb +1 -1
- data/spec/bson/raw_spec.rb +34 -2
- data/spec/bson/regexp_spec.rb +1 -1
- data/spec/bson/registry_spec.rb +1 -1
- data/spec/bson/string_spec.rb +19 -1
- data/spec/bson/symbol_raw_spec.rb +45 -0
- data/spec/bson/symbol_spec.rb +63 -3
- data/spec/bson/time_spec.rb +205 -2
- data/spec/bson/time_with_zone_spec.rb +68 -0
- data/spec/bson/timestamp_spec.rb +56 -1
- data/spec/bson/true_class_spec.rb +1 -1
- data/spec/bson/undefined_spec.rb +1 -1
- data/spec/bson_spec.rb +1 -1
- data/spec/{support → runners}/common_driver.rb +1 -1
- data/spec/runners/corpus.rb +185 -0
- data/spec/{support/corpus.rb → runners/corpus_legacy.rb} +41 -59
- data/spec/spec_helper.rb +40 -3
- data/spec/{bson/driver_bson_spec.rb → spec_tests/common_driver_spec.rb} +1 -0
- data/spec/{bson/corpus_spec.rb → spec_tests/corpus_legacy_spec.rb} +10 -7
- data/spec/spec_tests/corpus_spec.rb +124 -0
- data/spec/spec_tests/data/corpus/README.md +15 -0
- data/spec/spec_tests/data/corpus/array.json +49 -0
- data/spec/spec_tests/data/corpus/binary.json +113 -0
- data/spec/spec_tests/data/corpus/boolean.json +27 -0
- data/spec/spec_tests/data/corpus/code.json +67 -0
- data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
- data/spec/spec_tests/data/corpus/datetime.json +42 -0
- data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
- data/spec/spec_tests/data/corpus/dbref.json +31 -0
- data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
- data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
- data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
- data/spec/spec_tests/data/corpus/decimal128-4.json +117 -0
- data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
- data/spec/spec_tests/data/corpus/decimal128-6.json +119 -0
- data/spec/spec_tests/data/corpus/decimal128-7.json +323 -0
- data/spec/spec_tests/data/corpus/document.json +36 -0
- data/spec/spec_tests/data/corpus/double.json +87 -0
- data/spec/spec_tests/data/corpus/int32.json +43 -0
- data/spec/spec_tests/data/corpus/int64.json +43 -0
- data/spec/spec_tests/data/corpus/maxkey.json +12 -0
- data/spec/spec_tests/data/corpus/minkey.json +12 -0
- data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
- data/spec/spec_tests/data/corpus/multi-type.json +11 -0
- data/spec/spec_tests/data/corpus/null.json +12 -0
- data/spec/spec_tests/data/corpus/oid.json +28 -0
- data/spec/spec_tests/data/corpus/regex.json +65 -0
- data/spec/spec_tests/data/corpus/string.json +72 -0
- data/spec/spec_tests/data/corpus/symbol.json +80 -0
- data/spec/spec_tests/data/corpus/timestamp.json +34 -0
- data/spec/spec_tests/data/corpus/top.json +236 -0
- data/spec/spec_tests/data/corpus/undefined.json +15 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/array.json +8 -2
- data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/binary.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/boolean.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code_w_scope.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/document.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/double.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/datetime.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/dbpointer.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/int64.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/symbol.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/int32.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/maxkey.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/minkey.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/null.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/oid.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/regex.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/string.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/timestamp.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/top.json +0 -0
- data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/undefined.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-1.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-2.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-3.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-4.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-5.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-6.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-7.json +0 -0
- data/spec/support/shared_examples.rb +3 -5
- data/spec/support/spec_config.rb +16 -0
- data/spec/support/utils.rb +10 -0
- metadata +227 -124
- metadata.gz.sig +0 -0
- data/ext/bson/bson_native.c +0 -762
@@ -0,0 +1,308 @@
|
|
1
|
+
# Copyright (C) 2020 MongoDB Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "spec_helper"
|
16
|
+
|
17
|
+
describe "BSON::ExtJSON.parse" do
|
18
|
+
|
19
|
+
let(:parsed) { BSON::ExtJSON.parse_obj(input) }
|
20
|
+
|
21
|
+
context 'when input is true' do
|
22
|
+
let(:input) { true }
|
23
|
+
|
24
|
+
it 'returns true' do
|
25
|
+
parsed.should == true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when input is false' do
|
30
|
+
let(:input) { false }
|
31
|
+
|
32
|
+
it 'returns false' do
|
33
|
+
parsed.should == false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when input is nil' do
|
38
|
+
let(:input) { nil }
|
39
|
+
|
40
|
+
it 'returns nil' do
|
41
|
+
parsed.should be nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when input is a string' do
|
46
|
+
let(:input) { 'hello' }
|
47
|
+
|
48
|
+
it 'returns the string' do
|
49
|
+
parsed.should == 'hello'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when input is a BSON timestamp' do
|
54
|
+
let(:input) { {'$timestamp' => {'t' => 12345, 'i' => 42}} }
|
55
|
+
|
56
|
+
it 'returns a BSON::Timestamp instance' do
|
57
|
+
parsed.should == BSON::Timestamp.new(12345, 42)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when input is an ISO time' do
|
62
|
+
let(:input) { {'$date' => '1970-01-01T00:00:04Z'} }
|
63
|
+
|
64
|
+
it 'returns a Time instance ' do
|
65
|
+
parsed.should be_a(Time)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns a Time instance with correct value' do
|
69
|
+
parsed.should == Time.at(4)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns a Time instance in UTC' do
|
73
|
+
parsed.zone.should == 'UTC'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when input is a Unix timestamp' do
|
78
|
+
let(:input) { {'$date' => {'$numberLong' => '4000'}} }
|
79
|
+
|
80
|
+
it 'returns a Time instance ' do
|
81
|
+
parsed.should be_a(Time)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns a Time instance with correct value' do
|
85
|
+
parsed.should == Time.at(4)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'returns a Time instance in UTC' do
|
89
|
+
parsed.zone.should == 'UTC'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when input is an int32' do
|
94
|
+
let(:input) do
|
95
|
+
{'$numberInt' => '42'}
|
96
|
+
end
|
97
|
+
|
98
|
+
let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
|
99
|
+
|
100
|
+
context 'when :mode is nil' do
|
101
|
+
let(:mode) { nil }
|
102
|
+
|
103
|
+
it 'returns Integer instance' do
|
104
|
+
parsed.should be_a(Integer)
|
105
|
+
parsed.should == 42
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when :mode is :bson' do
|
110
|
+
let(:mode) { :bson }
|
111
|
+
|
112
|
+
it 'returns Integer instance' do
|
113
|
+
parsed.should be_a(Integer)
|
114
|
+
parsed.should == 42
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when input is an int64' do
|
120
|
+
let(:input) do
|
121
|
+
{'$numberLong' => '42'}
|
122
|
+
end
|
123
|
+
|
124
|
+
let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
|
125
|
+
|
126
|
+
context 'when :mode is nil' do
|
127
|
+
let(:mode) { nil }
|
128
|
+
|
129
|
+
it 'returns Integer instance' do
|
130
|
+
parsed.should be_a(Integer)
|
131
|
+
parsed.should == 42
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'when :mode is :bson' do
|
136
|
+
let(:mode) { :bson }
|
137
|
+
|
138
|
+
it 'returns Int64 instance' do
|
139
|
+
parsed.should be_a(BSON::Int64)
|
140
|
+
parsed.value.should == 42
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when input is a hash' do
|
146
|
+
let(:input) do
|
147
|
+
{}
|
148
|
+
end
|
149
|
+
|
150
|
+
let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
|
151
|
+
|
152
|
+
context 'when mode is invalid' do
|
153
|
+
let(:mode) { :foo }
|
154
|
+
|
155
|
+
it 'raises an exception' do
|
156
|
+
lambda do
|
157
|
+
parsed
|
158
|
+
end.should raise_error(ArgumentError, /Invalid value for :mode option/)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'when input is a binary' do
|
164
|
+
let(:data) do
|
165
|
+
Base64.decode64("//8=")
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'in current format' do
|
169
|
+
let(:input) do
|
170
|
+
{ "$binary" => { "base64"=>"//8=", "subType"=>"00" } }
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when :mode is nil' do
|
174
|
+
let(:mode) { nil }
|
175
|
+
|
176
|
+
it 'returns BSON::Binary instance' do
|
177
|
+
parsed.should be_a(BSON::Binary)
|
178
|
+
parsed.data.should == data
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when mode is :bson' do
|
183
|
+
let(:mode) { :bson }
|
184
|
+
|
185
|
+
it 'returns BSON::Binary instance' do
|
186
|
+
parsed.should be_a(BSON::Binary)
|
187
|
+
parsed.data.should == data
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'in legacy format' do
|
193
|
+
let(:input) do
|
194
|
+
{ "$binary"=>"//8=", "$type"=>"00" }
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'when :mode is nil' do
|
198
|
+
let(:mode) { nil }
|
199
|
+
|
200
|
+
it 'returns BSON::Binary instance' do
|
201
|
+
parsed.should be_a(BSON::Binary)
|
202
|
+
parsed.data.should == data
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when mode is :bson' do
|
207
|
+
let(:mode) { :bson }
|
208
|
+
|
209
|
+
it 'returns BSON::Binary instance' do
|
210
|
+
parsed.should be_a(BSON::Binary)
|
211
|
+
parsed.data.should == data
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'when input is a regex' do
|
218
|
+
let(:pattern) { 'abc' }
|
219
|
+
let(:options) { 'im' }
|
220
|
+
|
221
|
+
context 'in current format' do
|
222
|
+
let(:input) do
|
223
|
+
{ "$regularExpression" => { "pattern" => pattern, "options" => options } }
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'when :mode is nil' do
|
227
|
+
let(:mode) { nil }
|
228
|
+
|
229
|
+
it 'returns a BSON::Regexp::Raw instance' do
|
230
|
+
parsed.should be_a(BSON::Regexp::Raw)
|
231
|
+
parsed.pattern.should == pattern
|
232
|
+
parsed.options.should == options
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context 'when :mode is :bson' do
|
237
|
+
let(:mode) { :bson }
|
238
|
+
|
239
|
+
it 'returns a BSON::Regexp::Raw instance' do
|
240
|
+
parsed.should be_a(BSON::Regexp::Raw)
|
241
|
+
parsed.pattern.should == pattern
|
242
|
+
parsed.options.should == options
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'in legacy format' do
|
248
|
+
let(:input) do
|
249
|
+
{ "$regex" => pattern, "$options" => options }
|
250
|
+
end
|
251
|
+
|
252
|
+
context 'when :mode is nil' do
|
253
|
+
let(:mode) { nil }
|
254
|
+
|
255
|
+
it 'returns a BSON::Regexp::Raw instance' do
|
256
|
+
parsed.should be_a(BSON::Regexp::Raw)
|
257
|
+
parsed.pattern.should == pattern
|
258
|
+
parsed.options.should == options
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'when :mode is :bson' do
|
263
|
+
let(:mode) { :bson }
|
264
|
+
|
265
|
+
it 'returns a BSON::Regexp::Raw instance' do
|
266
|
+
parsed.should be_a(BSON::Regexp::Raw)
|
267
|
+
parsed.pattern.should == pattern
|
268
|
+
parsed.options.should == options
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context 'when $regularExpression is nested in $regex' do
|
274
|
+
context 'with options' do
|
275
|
+
let(:input) do
|
276
|
+
{
|
277
|
+
"$regex" => {
|
278
|
+
"$regularExpression" => { "pattern" => "foo*", "options" => "" },
|
279
|
+
},
|
280
|
+
"$options" => "ix",
|
281
|
+
}
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'parses' do
|
285
|
+
parsed.should == {
|
286
|
+
'$regex' => BSON::Regexp::Raw.new('foo*'), '$options' => 'ix'
|
287
|
+
}
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'without options' do
|
292
|
+
let(:input) do
|
293
|
+
{
|
294
|
+
"$regex" => {
|
295
|
+
"$regularExpression" => { "pattern" => "foo*", "options" => "" },
|
296
|
+
},
|
297
|
+
}
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'parses' do
|
301
|
+
parsed.should == {
|
302
|
+
'$regex' => BSON::Regexp::Raw.new('foo*'),
|
303
|
+
}
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
data/spec/bson/float_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2009-
|
1
|
+
# Copyright (C) 2009-2020 MongoDB Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -26,4 +26,40 @@ describe Float do
|
|
26
26
|
it_behaves_like "a serializable bson element"
|
27
27
|
it_behaves_like "a deserializable bson element"
|
28
28
|
end
|
29
|
+
|
30
|
+
describe '#to_json' do
|
31
|
+
it 'returns float' do
|
32
|
+
42.0.to_json.should == '42.0'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#as_extended_json' do
|
37
|
+
context 'canonical mode' do
|
38
|
+
it 'returns $numberDouble' do
|
39
|
+
42.0.as_extended_json.should == {'$numberDouble' => '42.0'}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'relaxed mode' do
|
44
|
+
let(:serialized) do
|
45
|
+
42.0.as_extended_json(mode: :relaxed)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns float' do
|
49
|
+
serialized.should be_a(Float)
|
50
|
+
serialized.should be_within(0.00001).of(42)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'legacy mode' do
|
55
|
+
let(:serialized) do
|
56
|
+
42.0.as_extended_json(mode: :legacy)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns float' do
|
60
|
+
serialized.should be_a(Float)
|
61
|
+
serialized.should be_within(0.00001).of(42)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
29
65
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright (C) 2021 MongoDB Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "spec_helper"
|
16
|
+
|
17
|
+
describe 'Hash ActiveSupport extensions' do
|
18
|
+
require_active_support
|
19
|
+
|
20
|
+
describe '#symbolize_keys' do
|
21
|
+
let(:symbolized) { hash.symbolize_keys }
|
22
|
+
|
23
|
+
shared_examples 'works correctly' do
|
24
|
+
it 'returns a hash' do
|
25
|
+
symbolized.class.should be Hash
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'works correctly' do
|
29
|
+
hash.symbolize_keys.should == {foo: 'bar'}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'string keys' do
|
34
|
+
let(:hash) do
|
35
|
+
{'foo' => 'bar'}
|
36
|
+
end
|
37
|
+
|
38
|
+
include_examples 'works correctly'
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'symbol keys' do
|
42
|
+
let(:hash) do
|
43
|
+
{foo: 'bar'}
|
44
|
+
end
|
45
|
+
|
46
|
+
include_examples 'works correctly'
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'both string and symbol keys' do
|
50
|
+
let(:hash) do
|
51
|
+
{'foo' => 42, foo: 'bar'}
|
52
|
+
end
|
53
|
+
|
54
|
+
include_examples 'works correctly'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/bson/hash_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2009-
|
1
|
+
# Copyright (C) 2009-2020 MongoDB Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -157,5 +157,213 @@ describe Hash do
|
|
157
157
|
it_behaves_like "a serializable bson element"
|
158
158
|
it_behaves_like "a deserializable bson element"
|
159
159
|
end
|
160
|
+
|
161
|
+
context 'with symbol values' do
|
162
|
+
let(:value) { :foo }
|
163
|
+
|
164
|
+
let(:serialized) do
|
165
|
+
{foo: value}.to_bson.to_s
|
166
|
+
end
|
167
|
+
|
168
|
+
def perform_test(bson_type_to_use)
|
169
|
+
Symbol.class_eval do
|
170
|
+
alias_method :bson_type_orig, :bson_type
|
171
|
+
define_method(:bson_type) do
|
172
|
+
bson_type_to_use
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
begin
|
177
|
+
yield
|
178
|
+
ensure
|
179
|
+
Symbol.class_eval do
|
180
|
+
alias_method :bson_type, :bson_type_orig
|
181
|
+
remove_method :bson_type_orig
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
let(:bson_with_symbol) do
|
187
|
+
"\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00bar\x00\x00".force_encoding('binary')
|
188
|
+
end
|
189
|
+
|
190
|
+
let(:deserialized) do
|
191
|
+
Hash.from_bson(BSON::ByteBuffer.new(bson_with_symbol))
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'when Symbol#bson_type is set to symbol' do
|
195
|
+
let(:bson_type_to_use) { BSON::Symbol::BSON_TYPE }
|
196
|
+
|
197
|
+
let(:expected) do
|
198
|
+
"\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00foo\x00\x00".force_encoding('binary')
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'serializes to BSON symbol' do
|
202
|
+
perform_test(bson_type_to_use) do
|
203
|
+
serialized
|
204
|
+
end.should == expected
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'deserializes to Symbol' do
|
208
|
+
deserialized.should == {'foo' => :bar}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'when Symbol#bson_type is set to string' do
|
213
|
+
let(:bson_type_to_use) { BSON::String::BSON_TYPE }
|
214
|
+
|
215
|
+
let(:expected) do
|
216
|
+
"\x12\x00\x00\x00\x02foo\x00\x04\x00\x00\x00foo\x00\x00".force_encoding('binary')
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'serializes to BSON string' do
|
220
|
+
perform_test(bson_type_to_use) do
|
221
|
+
serialized
|
222
|
+
end.should == expected
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'deserializes to Symbol' do
|
226
|
+
deserialized.should == {'foo' => :bar}
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'when hash contains value of an unserializable class' do
|
232
|
+
class HashSpecUnserializableClass
|
233
|
+
end
|
234
|
+
|
235
|
+
let(:obj) do
|
236
|
+
{foo: HashSpecUnserializableClass.new}
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'raises UnserializableClass' do
|
240
|
+
lambda do
|
241
|
+
obj.to_bson
|
242
|
+
end.should raise_error(BSON::Error::UnserializableClass,
|
243
|
+
# C extension does not provide hash key in the exception message.
|
244
|
+
/(Hash value for key 'foo'|Value) does not define its BSON serialized type:.*HashSpecUnserializableClass/)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'when reading from a byte buffer that was previously written to' do
|
249
|
+
let(:buffer) do
|
250
|
+
{foo: 42}.to_bson
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'returns the original hash' do
|
254
|
+
expect(Hash.from_bson(buffer)).to eq('foo' => 42)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe '#to_bson' do
|
260
|
+
context 'when a key is not valid utf-8' do
|
261
|
+
let(:key) { Utils.make_byte_string([254, 253, 255]) }
|
262
|
+
let(:hash) do
|
263
|
+
{key => 'foo'}
|
264
|
+
end
|
265
|
+
|
266
|
+
let(:expected_message) do
|
267
|
+
if BSON::Environment.jruby?
|
268
|
+
# Uses JRE conversion to another encoding
|
269
|
+
/Error serializing key.*Encoding::UndefinedConversionError/
|
270
|
+
else
|
271
|
+
# Uses our validator
|
272
|
+
/Key.*is not valid UTF-8/
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'raises EncodingError' do
|
277
|
+
expect do
|
278
|
+
hash.to_bson
|
279
|
+
end.to raise_error(EncodingError, expected_message)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context 'when a key contains null bytes' do
|
284
|
+
let(:hash) do
|
285
|
+
{"\x00".force_encoding('BINARY') => 'foo'}
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'raises ArgumentError' do
|
289
|
+
expect do
|
290
|
+
hash.to_bson
|
291
|
+
end.to raise_error(ArgumentError, /[Kk]ey.*contains null bytes/)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
context 'when a value is not valid utf-8' do
|
296
|
+
let(:hash) do
|
297
|
+
{'foo' => [254, 253, 255].map(&:chr).join.force_encoding('BINARY')}
|
298
|
+
end
|
299
|
+
|
300
|
+
let(:expected_message) do
|
301
|
+
/from ASCII-8BIT to UTF-8/
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'raises EncodingError' do
|
305
|
+
expect do
|
306
|
+
hash.to_bson
|
307
|
+
end.to raise_error(EncodingError, expected_message)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context 'when a value contains null bytes' do
|
312
|
+
let(:hash) do
|
313
|
+
{'foo' => "\x00".force_encoding('BINARY')}
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'works' do
|
317
|
+
expect do
|
318
|
+
hash.to_bson
|
319
|
+
end.not_to raise_error
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
describe '#from_bson' do
|
325
|
+
context 'when bson document has duplicate keys' do
|
326
|
+
let(:buf) do
|
327
|
+
buf = BSON::ByteBuffer.new
|
328
|
+
buf.put_int32(37)
|
329
|
+
buf.put_byte("\x02")
|
330
|
+
buf.put_cstring('foo')
|
331
|
+
buf.put_string('bar')
|
332
|
+
buf.put_byte("\x02")
|
333
|
+
buf.put_cstring('foo')
|
334
|
+
buf.put_string('overwrite')
|
335
|
+
buf.put_byte("\x00")
|
336
|
+
|
337
|
+
BSON::ByteBuffer.new(buf.to_s)
|
338
|
+
end
|
339
|
+
|
340
|
+
let(:doc) { Hash.from_bson(buf) }
|
341
|
+
|
342
|
+
it 'overwrites first value with second value' do
|
343
|
+
doc.should == {'foo' => 'overwrite'}
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context 'when bson document has string and symbol keys of the same name' do
|
348
|
+
let(:buf) do
|
349
|
+
buf = BSON::ByteBuffer.new
|
350
|
+
buf.put_int32(31)
|
351
|
+
buf.put_byte("\x02")
|
352
|
+
buf.put_cstring('foo')
|
353
|
+
buf.put_string('bar')
|
354
|
+
buf.put_byte("\x0e")
|
355
|
+
buf.put_cstring('foo')
|
356
|
+
buf.put_string('bar')
|
357
|
+
buf.put_byte("\x00")
|
358
|
+
|
359
|
+
BSON::ByteBuffer.new(buf.to_s)
|
360
|
+
end
|
361
|
+
|
362
|
+
let(:doc) { Hash.from_bson(buf) }
|
363
|
+
|
364
|
+
it 'overwrites first value with second value' do
|
365
|
+
doc.should == {'foo' => :bar}
|
366
|
+
end
|
367
|
+
end
|
160
368
|
end
|
161
369
|
end
|