bson 4.1.1 → 5.2.0

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.
Files changed (226) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +106 -7
  3. data/Rakefile +86 -43
  4. data/ext/bson/{native-endian.h → bson-endian.h} +14 -107
  5. data/ext/bson/bson-native.h +135 -0
  6. data/ext/bson/bytebuf.c +133 -0
  7. data/ext/bson/endian.c +117 -0
  8. data/ext/bson/extconf.rb +8 -3
  9. data/ext/bson/init.c +364 -0
  10. data/ext/bson/libbson-utf8.c +230 -0
  11. data/ext/bson/read.c +470 -0
  12. data/ext/bson/util.c +250 -0
  13. data/ext/bson/write.c +675 -0
  14. data/lib/bson/active_support.rb +19 -0
  15. data/lib/bson/array.rb +97 -30
  16. data/lib/bson/big_decimal.rb +77 -0
  17. data/lib/bson/binary.rb +510 -70
  18. data/lib/bson/boolean.rb +15 -4
  19. data/lib/bson/code.rb +25 -12
  20. data/lib/bson/code_with_scope.rb +41 -15
  21. data/lib/bson/config.rb +3 -28
  22. data/lib/bson/date.rb +16 -4
  23. data/lib/bson/date_time.rb +6 -4
  24. data/lib/bson/db_pointer.rb +110 -0
  25. data/lib/bson/dbref.rb +154 -0
  26. data/lib/bson/decimal128/builder.rb +456 -0
  27. data/lib/bson/decimal128.rb +272 -0
  28. data/lib/bson/document.rb +177 -7
  29. data/lib/bson/environment.rb +17 -2
  30. data/lib/bson/error/bson_decode_error.rb +11 -0
  31. data/lib/bson/error/ext_json_parse_error.rb +11 -0
  32. data/lib/bson/error/illegal_key.rb +23 -0
  33. data/lib/bson/error/invalid_binary_type.rb +37 -0
  34. data/lib/bson/error/invalid_dbref_argument.rb +12 -0
  35. data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
  36. data/lib/bson/error/invalid_decimal128_range.rb +27 -0
  37. data/lib/bson/error/invalid_decimal128_string.rb +26 -0
  38. data/lib/bson/error/invalid_key.rb +24 -0
  39. data/lib/bson/error/invalid_object_id.rb +11 -0
  40. data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
  41. data/lib/bson/error/unrepresentable_precision.rb +19 -0
  42. data/lib/bson/error/unserializable_class.rb +13 -0
  43. data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
  44. data/lib/bson/error/unsupported_type.rb +11 -0
  45. data/lib/bson/error.rb +22 -0
  46. data/lib/bson/ext_json.rb +389 -0
  47. data/lib/bson/false_class.rb +6 -4
  48. data/lib/bson/float.rb +43 -7
  49. data/lib/bson/hash.rb +152 -37
  50. data/lib/bson/int32.rb +104 -6
  51. data/lib/bson/int64.rb +111 -8
  52. data/lib/bson/integer.rb +43 -9
  53. data/lib/bson/json.rb +3 -1
  54. data/lib/bson/max_key.rb +21 -10
  55. data/lib/bson/min_key.rb +21 -10
  56. data/lib/bson/nil_class.rb +7 -3
  57. data/lib/bson/object.rb +25 -17
  58. data/lib/bson/object_id.rb +98 -113
  59. data/lib/bson/open_struct.rb +59 -0
  60. data/lib/bson/regexp.rb +129 -56
  61. data/lib/bson/registry.rb +7 -10
  62. data/lib/bson/specialized.rb +8 -4
  63. data/lib/bson/string.rb +12 -32
  64. data/lib/bson/symbol.rb +107 -11
  65. data/lib/bson/time.rb +68 -7
  66. data/lib/bson/time_with_zone.rb +67 -0
  67. data/lib/bson/timestamp.rb +50 -10
  68. data/lib/bson/true_class.rb +6 -4
  69. data/lib/bson/undefined.rb +28 -2
  70. data/lib/bson/vector.rb +44 -0
  71. data/lib/bson/version.rb +6 -14
  72. data/lib/bson.rb +22 -12
  73. data/spec/README.md +14 -0
  74. data/spec/bson/array_spec.rb +38 -62
  75. data/spec/bson/big_decimal_spec.rb +328 -0
  76. data/spec/bson/binary_spec.rb +199 -53
  77. data/spec/bson/binary_uuid_spec.rb +190 -0
  78. data/spec/bson/boolean_spec.rb +2 -1
  79. data/spec/bson/byte_buffer_read_spec.rb +198 -0
  80. data/spec/bson/byte_buffer_spec.rb +122 -381
  81. data/spec/bson/byte_buffer_write_spec.rb +855 -0
  82. data/spec/bson/code_spec.rb +6 -4
  83. data/spec/bson/code_with_scope_spec.rb +6 -4
  84. data/spec/bson/config_spec.rb +1 -35
  85. data/spec/bson/date_spec.rb +2 -1
  86. data/spec/bson/date_time_spec.rb +55 -1
  87. data/spec/bson/dbref_legacy_spec.rb +186 -0
  88. data/spec/bson/dbref_spec.rb +487 -0
  89. data/spec/bson/decimal128_spec.rb +1840 -0
  90. data/spec/bson/document_as_spec.rb +61 -0
  91. data/spec/bson/document_spec.rb +205 -32
  92. data/spec/bson/ext_json_parse_spec.rb +346 -0
  93. data/spec/bson/false_class_spec.rb +9 -1
  94. data/spec/bson/float_spec.rb +42 -1
  95. data/spec/bson/hash_as_spec.rb +58 -0
  96. data/spec/bson/hash_spec.rb +318 -66
  97. data/spec/bson/int32_spec.rb +248 -1
  98. data/spec/bson/int64_spec.rb +308 -1
  99. data/spec/bson/integer_spec.rb +61 -3
  100. data/spec/bson/json_spec.rb +2 -1
  101. data/spec/bson/max_key_spec.rb +6 -4
  102. data/spec/bson/min_key_spec.rb +6 -4
  103. data/spec/bson/nil_class_spec.rb +2 -1
  104. data/spec/bson/object_id_spec.rb +95 -5
  105. data/spec/bson/object_spec.rb +3 -2
  106. data/spec/bson/open_struct_spec.rb +87 -0
  107. data/spec/bson/raw_spec.rb +594 -0
  108. data/spec/bson/regexp_spec.rb +61 -8
  109. data/spec/bson/registry_spec.rb +3 -2
  110. data/spec/bson/string_spec.rb +26 -33
  111. data/spec/bson/symbol_raw_spec.rb +70 -0
  112. data/spec/bson/symbol_spec.rb +77 -20
  113. data/spec/bson/time_spec.rb +206 -2
  114. data/spec/bson/time_with_zone_spec.rb +69 -0
  115. data/spec/bson/timestamp_spec.rb +58 -2
  116. data/spec/bson/true_class_spec.rb +9 -1
  117. data/spec/bson/undefined_spec.rb +28 -1
  118. data/spec/bson/vector_spec.rb +33 -0
  119. data/spec/bson_spec.rb +2 -1
  120. data/spec/runners/binary_vector.rb +78 -0
  121. data/spec/runners/common_driver.rb +348 -0
  122. data/spec/runners/corpus.rb +191 -0
  123. data/spec/runners/corpus_legacy.rb +248 -0
  124. data/spec/shared/LICENSE +20 -0
  125. data/spec/shared/bin/get-mongodb-download-url +17 -0
  126. data/spec/shared/bin/s3-copy +45 -0
  127. data/spec/shared/bin/s3-upload +69 -0
  128. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  129. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  130. data/spec/shared/lib/mrss/constraints.rb +378 -0
  131. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  132. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  133. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  134. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  135. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  136. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  137. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  138. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  139. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  140. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  141. data/spec/shared/lib/mrss/utils.rb +37 -0
  142. data/spec/shared/lib/tasks/candidate.rake +64 -0
  143. data/spec/shared/share/Dockerfile.erb +251 -0
  144. data/spec/shared/share/haproxy-1.conf +16 -0
  145. data/spec/shared/share/haproxy-2.conf +17 -0
  146. data/spec/shared/shlib/config.sh +27 -0
  147. data/spec/shared/shlib/distro.sh +84 -0
  148. data/spec/shared/shlib/server.sh +423 -0
  149. data/spec/shared/shlib/set_env.sh +110 -0
  150. data/spec/spec_helper.rb +61 -1
  151. data/spec/spec_tests/binary_vector_spec.rb +82 -0
  152. data/spec/spec_tests/common_driver_spec.rb +84 -0
  153. data/spec/spec_tests/corpus_legacy_spec.rb +72 -0
  154. data/spec/spec_tests/corpus_spec.rb +134 -0
  155. data/spec/spec_tests/data/binary_vector/README.md +61 -0
  156. data/spec/spec_tests/data/binary_vector/float32.json +65 -0
  157. data/spec/spec_tests/data/binary_vector/int8.json +57 -0
  158. data/spec/spec_tests/data/binary_vector/packed_bit.json +83 -0
  159. data/spec/spec_tests/data/corpus/README.md +15 -0
  160. data/spec/spec_tests/data/corpus/array.json +49 -0
  161. data/spec/spec_tests/data/corpus/binary.json +153 -0
  162. data/spec/spec_tests/data/corpus/boolean.json +27 -0
  163. data/spec/spec_tests/data/corpus/code.json +67 -0
  164. data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
  165. data/spec/spec_tests/data/corpus/datetime.json +42 -0
  166. data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
  167. data/spec/spec_tests/data/corpus/dbref.json +51 -0
  168. data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
  169. data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
  170. data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
  171. data/spec/spec_tests/data/corpus/decimal128-4.json +165 -0
  172. data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
  173. data/spec/spec_tests/data/corpus/decimal128-6.json +131 -0
  174. data/spec/spec_tests/data/corpus/decimal128-7.json +327 -0
  175. data/spec/spec_tests/data/corpus/document.json +60 -0
  176. data/spec/spec_tests/data/corpus/double.json +87 -0
  177. data/spec/spec_tests/data/corpus/int32.json +43 -0
  178. data/spec/spec_tests/data/corpus/int64.json +43 -0
  179. data/spec/spec_tests/data/corpus/maxkey.json +12 -0
  180. data/spec/spec_tests/data/corpus/minkey.json +12 -0
  181. data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
  182. data/spec/spec_tests/data/corpus/multi-type.json +11 -0
  183. data/spec/spec_tests/data/corpus/null.json +12 -0
  184. data/spec/spec_tests/data/corpus/oid.json +28 -0
  185. data/spec/spec_tests/data/corpus/regex.json +65 -0
  186. data/spec/spec_tests/data/corpus/string.json +72 -0
  187. data/spec/spec_tests/data/corpus/symbol.json +80 -0
  188. data/spec/spec_tests/data/corpus/timestamp.json +34 -0
  189. data/spec/spec_tests/data/corpus/top.json +262 -0
  190. data/spec/spec_tests/data/corpus/undefined.json +15 -0
  191. data/spec/spec_tests/data/corpus_legacy/array.json +49 -0
  192. data/spec/spec_tests/data/corpus_legacy/binary.json +69 -0
  193. data/spec/spec_tests/data/corpus_legacy/boolean.json +27 -0
  194. data/spec/spec_tests/data/corpus_legacy/code.json +67 -0
  195. data/spec/spec_tests/data/corpus_legacy/code_w_scope.json +78 -0
  196. data/spec/spec_tests/data/corpus_legacy/document.json +36 -0
  197. data/spec/spec_tests/data/corpus_legacy/double.json +69 -0
  198. data/spec/spec_tests/data/corpus_legacy/failures/datetime.json +31 -0
  199. data/spec/spec_tests/data/corpus_legacy/failures/dbpointer.json +42 -0
  200. data/spec/spec_tests/data/corpus_legacy/failures/int64.json +38 -0
  201. data/spec/spec_tests/data/corpus_legacy/failures/symbol.json +62 -0
  202. data/spec/spec_tests/data/corpus_legacy/int32.json +38 -0
  203. data/spec/spec_tests/data/corpus_legacy/maxkey.json +12 -0
  204. data/spec/spec_tests/data/corpus_legacy/minkey.json +12 -0
  205. data/spec/spec_tests/data/corpus_legacy/null.json +12 -0
  206. data/spec/spec_tests/data/corpus_legacy/oid.json +28 -0
  207. data/spec/spec_tests/data/corpus_legacy/regex.json +37 -0
  208. data/spec/spec_tests/data/corpus_legacy/string.json +67 -0
  209. data/spec/spec_tests/data/corpus_legacy/timestamp.json +18 -0
  210. data/spec/spec_tests/data/corpus_legacy/top.json +62 -0
  211. data/spec/spec_tests/data/corpus_legacy/undefined.json +13 -0
  212. data/spec/spec_tests/data/decimal128/decimal128-1.json +363 -0
  213. data/spec/spec_tests/data/decimal128/decimal128-2.json +793 -0
  214. data/spec/spec_tests/data/decimal128/decimal128-3.json +1771 -0
  215. data/spec/spec_tests/data/decimal128/decimal128-4.json +165 -0
  216. data/spec/spec_tests/data/decimal128/decimal128-5.json +402 -0
  217. data/spec/spec_tests/data/decimal128/decimal128-6.json +131 -0
  218. data/spec/spec_tests/data/decimal128/decimal128-7.json +327 -0
  219. data/spec/support/shared_examples.rb +32 -11
  220. data/spec/support/spec_config.rb +17 -0
  221. data/spec/support/utils.rb +58 -0
  222. metadata +284 -45
  223. checksums.yaml.gz.sig +0 -3
  224. data/ext/bson/native.c +0 -722
  225. data.tar.gz.sig +0 -0
  226. metadata.gz.sig +0 -0
@@ -0,0 +1,61 @@
1
+ # rubocop:todo all
2
+ # Copyright (C) 2021 MongoDB Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "spec_helper"
17
+
18
+ # BSON::Document ActiveSupport extensions
19
+ describe BSON::Document do
20
+ require_active_support
21
+
22
+ describe '#symbolize_keys' do
23
+ context 'string keys' do
24
+ let(:doc) do
25
+ described_class.new('foo' => 'bar')
26
+ end
27
+
28
+ it 'works correctly' do
29
+ doc.symbolize_keys.should == {foo: 'bar'}
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#symbolize_keys!' do
35
+ context 'string keys' do
36
+ let(:doc) do
37
+ described_class.new('foo' => 'bar')
38
+ end
39
+
40
+ it 'raises ArgumentError' do
41
+ lambda do
42
+ doc.symbolize_keys!
43
+ end.should raise_error(ArgumentError, /symbolize_keys! is not supported on BSON::Document instances/)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#deep_symbolize_keys!' do
49
+ context 'string keys' do
50
+ let(:doc) do
51
+ described_class.new('foo' => 'bar')
52
+ end
53
+
54
+ it 'raises ArgumentError' do
55
+ expect do
56
+ doc.deep_symbolize_keys!
57
+ end.to output(/\[DEPRECATION\] `deep_symbolize_keys!` is not supported on BSON::Document instances./).to_stderr
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,6 +1,6 @@
1
- # encoding: utf-8
1
+ # rubocop:todo all
2
2
 
3
- # Copyright (C) 2009-2014 MongoDB Inc.
3
+ # Copyright (C) 2009-2020 MongoDB Inc.
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
@@ -49,6 +49,68 @@ describe BSON::Document do
49
49
  end
50
50
  end
51
51
 
52
+ describe "#fetch" do
53
+
54
+ let(:document) do
55
+ described_class["key", "value", "key2", "value"]
56
+ end
57
+
58
+ context "when provided string keys" do
59
+
60
+ it "returns the value" do
61
+ expect(document.fetch("key")).to eq("value")
62
+ end
63
+ end
64
+
65
+ context "when provided symbol keys" do
66
+
67
+ it "returns the value" do
68
+ expect(document.fetch(:key)).to eq("value")
69
+ end
70
+ end
71
+
72
+ context "when key does not exist" do
73
+
74
+ it "raises KeyError" do
75
+ expect do
76
+ document.fetch(:non_existent_key)
77
+ end.to raise_exception(KeyError)
78
+ end
79
+
80
+ context "and default value is provided" do
81
+
82
+ it "returns default value" do
83
+ expect(document.fetch(:non_existent_key, false)).to eq(false)
84
+ end
85
+ end
86
+
87
+ context "and block is passed" do
88
+
89
+ it "returns result of the block" do
90
+ expect(document.fetch(:non_existent_key, &:to_s))
91
+ .to eq("non_existent_key")
92
+ end
93
+ end
94
+ end
95
+
96
+ context "when key exists" do
97
+
98
+ context "and default value is provided" do
99
+
100
+ it "returns the value" do
101
+ expect(document.fetch(:key, "other")).to eq("value")
102
+ end
103
+ end
104
+
105
+ context "and block is passed" do
106
+
107
+ it "returns the value" do
108
+ expect(document.fetch(:key, &:to_s)).to eq("value")
109
+ end
110
+ end
111
+ end
112
+ end
113
+
52
114
  describe "#[]" do
53
115
 
54
116
  let(:document) do
@@ -68,6 +130,13 @@ describe BSON::Document do
68
130
  expect(document[:key]).to eq("value")
69
131
  end
70
132
  end
133
+
134
+ context "when key does not exist" do
135
+
136
+ it "returns nil" do
137
+ expect(document[:non_existent_key]).to be nil
138
+ end
139
+ end
71
140
  end
72
141
 
73
142
  describe "#[]=" do
@@ -94,8 +163,108 @@ describe BSON::Document do
94
163
  it "sets the value" do
95
164
  expect(doc[key]).to eq(val)
96
165
  end
166
+
167
+ context 'when value is a hash' do
168
+ let(:val) do
169
+ {'foo' => {'bar' => 'baz'}}
170
+ end
171
+
172
+ it 'converts value to indifferent access' do
173
+ expect(doc[key][:foo][:bar]).to eq('baz')
174
+ end
175
+ end
176
+
177
+ context 'when value is an array with hash element' do
178
+ let(:val) do
179
+ [42, {'foo' => {'bar' => 'baz'}}]
180
+ end
181
+
182
+ it 'converts hash element to indifferent access' do
183
+ expect(doc[key][1][:foo][:bar]).to eq('baz')
184
+ end
185
+ end
97
186
  end
98
187
 
188
+ if described_class.instance_methods.include?(:dig)
189
+ describe "#dig" do
190
+ let(:document) do
191
+ described_class.new("key1" => { :key2 => "value" })
192
+ end
193
+
194
+ context "when provided string keys" do
195
+
196
+ it "returns the value" do
197
+ expect(document.dig("key1", "key2")).to eq("value")
198
+ end
199
+ end
200
+
201
+ context "when provided symbol keys" do
202
+
203
+ it "returns the value" do
204
+ expect(document.dig(:key1, :key2)).to eq("value")
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ if described_class.instance_methods.include?(:slice)
211
+ describe "#slice" do
212
+ let(:document) do
213
+ described_class.new("key1" => "value1", key2: "value2")
214
+ end
215
+
216
+ context "when provided string keys" do
217
+
218
+ it "is a BSON Document" do
219
+ expect(document.slice("key1")).to be_a(BSON::Document)
220
+ end
221
+
222
+ it "returns the partial document" do
223
+ expect(document.slice("key1")).to contain_exactly(['key1', 'value1'])
224
+ end
225
+ end
226
+
227
+ context "when provided symbol keys" do
228
+
229
+ it "is a BSON Document" do
230
+ expect(document.slice(:key1)).to be_a(BSON::Document)
231
+ end
232
+
233
+ it "returns the partial document" do
234
+ expect(document.slice(:key1)).to contain_exactly(['key1', 'value1'])
235
+ end
236
+ end
237
+
238
+ context "when provided keys that do not exist in the document" do
239
+
240
+ it "returns only the keys that exist in the document" do
241
+ expect(document.slice(:key1, :key3)).to contain_exactly(['key1', 'value1'])
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ describe "#except" do
248
+ let(:document) do
249
+ described_class.new("key1" => "value1", key2: "value2")
250
+ end
251
+
252
+ context "when provided string keys" do
253
+
254
+ it "returns the partial document" do
255
+ expect(document.except("key1")).to contain_exactly(['key2', 'value2'])
256
+ end
257
+ end
258
+
259
+ context "when provided symbol keys" do
260
+
261
+ it "returns the partial document" do
262
+ expect(document.except(:key1)).to contain_exactly(['key2', 'value2'])
263
+ end
264
+ end
265
+ end
266
+
267
+
99
268
  describe "#delete" do
100
269
 
101
270
  shared_examples_for "a document with deletable pairs" do
@@ -351,7 +520,13 @@ describe BSON::Document do
351
520
  context "when the document has been serialized" do
352
521
 
353
522
  let(:deserialized) do
354
- YAML.load(YAML.dump(doc))
523
+ if YAML.respond_to?(:unsafe_load)
524
+ # In psych >= 4.0.0 `load` is basically an alias to `safe_load`,
525
+ # which will fail here.
526
+ YAML.unsafe_load(YAML.dump(doc))
527
+ else
528
+ YAML.load(YAML.dump(doc))
529
+ end
355
530
  end
356
531
 
357
532
  let!(:enum) do
@@ -756,6 +931,24 @@ describe BSON::Document do
756
931
  end
757
932
  end
758
933
 
934
+ context "when the hash contains an array of hashes" do
935
+ let(:obj) do
936
+ described_class["key",[{"a" => 1}, {"b" => 2}]]
937
+ end
938
+
939
+ let(:bson) do
940
+ "#{45.to_bson}#{Array::BSON_TYPE}key#{BSON::NULL_BYTE}" +
941
+ "#{35.to_bson}"+
942
+ "#{BSON::Document::BSON_TYPE}0#{BSON::NULL_BYTE}#{12.to_bson}#{BSON::Int32::BSON_TYPE}a#{BSON::NULL_BYTE}#{1.to_bson}#{BSON::NULL_BYTE}" +
943
+ "#{BSON::Document::BSON_TYPE}1#{BSON::NULL_BYTE}#{12.to_bson}#{BSON::Int32::BSON_TYPE}b#{BSON::NULL_BYTE}#{2.to_bson}#{BSON::NULL_BYTE}" +
944
+ "#{BSON::NULL_BYTE}" +
945
+ "#{BSON::NULL_BYTE}"
946
+ end
947
+
948
+ it_behaves_like "a serializable bson element"
949
+ it_behaves_like "a deserializable bson element"
950
+ end
951
+
759
952
  context "when the hash is a single level" do
760
953
 
761
954
  let(:obj) do
@@ -867,51 +1060,31 @@ describe BSON::Document do
867
1060
  it_behaves_like "a document able to handle utf-8"
868
1061
  end
869
1062
 
870
- context "when utf-8 values exist in wrong encoding" do
1063
+ context "given a utf-8-encodable string in another encoding" do
871
1064
 
872
1065
  let(:string) { "gültig" }
873
1066
  let(:document) do
874
1067
  described_class["type", string.encode("iso-8859-1")]
875
1068
  end
876
1069
 
877
- it "raises an exception", unless: BSON::Environment.jruby? do
878
- expect {
879
- document.to_bson
880
- }.to raise_error(ArgumentError)
881
- end
882
-
883
- it 'converts the values', if: BSON::Environment.jruby? do
884
- expect(
885
- BSON::Document.from_bson(BSON::ByteBuffer.new(document.to_bson.to_s))
886
- ).to eq({ "type" => string })
887
- end
888
- end
889
-
890
- context "when binary strings with utf-8 values exist", if: BSON::Environment.jruby? && (JRUBY_VERSION !~ /9.0/) do
891
-
892
- let(:string) { "europäisch" }
893
- let(:document) do
894
- described_class["type", string.encode("binary")]
895
- end
896
-
897
- it "encodes and decodes the document properly" do
1070
+ it 'converts the values to utf-8' do
898
1071
  expect(
899
1072
  BSON::Document.from_bson(BSON::ByteBuffer.new(document.to_bson.to_s))
900
1073
  ).to eq({ "type" => string })
901
1074
  end
902
1075
  end
903
1076
 
904
- context "when binary strings with utf-8 values exist", unless: BSON::Environment.jruby? do
1077
+ context "given a binary string with utf-8 values" do
905
1078
 
906
- let(:string) { "europäisch" }
1079
+ let(:string) { "europäisch".force_encoding('binary') }
907
1080
  let(:document) do
908
- described_class["type", string.encode("binary", "binary")]
1081
+ described_class["type", string]
909
1082
  end
910
1083
 
911
- it "encodes and decodes the document properly" do
912
- expect(
913
- BSON::Document.from_bson(BSON::ByteBuffer.new(document.to_bson.to_s))
914
- ).to eq({ "type" => string })
1084
+ it "raises encoding error" do
1085
+ expect do
1086
+ document.to_bson
1087
+ end.to raise_error(Encoding::UndefinedConversionError, /from ASCII-8BIT to UTF-8/)
915
1088
  end
916
1089
  end
917
1090
  end
@@ -0,0 +1,346 @@
1
+ # rubocop:todo all
2
+ # Copyright (C) 2020 MongoDB Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "spec_helper"
17
+
18
+ describe "BSON::ExtJSON.parse" do
19
+
20
+ let(:parsed) { BSON::ExtJSON.parse_obj(input) }
21
+
22
+ context 'when input is true' do
23
+ let(:input) { true }
24
+
25
+ it 'returns true' do
26
+ parsed.should == true
27
+ end
28
+ end
29
+
30
+ context 'when input is false' do
31
+ let(:input) { false }
32
+
33
+ it 'returns false' do
34
+ parsed.should == false
35
+ end
36
+ end
37
+
38
+ context 'when input is nil' do
39
+ let(:input) { nil }
40
+
41
+ it 'returns nil' do
42
+ parsed.should be nil
43
+ end
44
+ end
45
+
46
+ context 'when input is a string' do
47
+ let(:input) { 'hello' }
48
+
49
+ it 'returns the string' do
50
+ parsed.should == 'hello'
51
+ end
52
+ end
53
+
54
+ context 'when input is a BSON timestamp' do
55
+ let(:input) { {'$timestamp' => {'t' => 12345, 'i' => 42}} }
56
+
57
+ it 'returns a BSON::Timestamp instance' do
58
+ parsed.should == BSON::Timestamp.new(12345, 42)
59
+ end
60
+ end
61
+
62
+ context 'when input is an ISO time' do
63
+ let(:input) { {'$date' => '1970-01-01T00:00:04Z'} }
64
+
65
+ it 'returns a Time instance ' do
66
+ parsed.should be_a(Time)
67
+ end
68
+
69
+ it 'returns a Time instance with correct value' do
70
+ parsed.should == Time.at(4)
71
+ end
72
+
73
+ it 'returns a Time instance in UTC' do
74
+ parsed.zone.should == 'UTC'
75
+ end
76
+ end
77
+
78
+ context 'when input is a Unix timestamp' do
79
+ let(:input) { {'$date' => {'$numberLong' => '4000'}} }
80
+
81
+ it 'returns a Time instance ' do
82
+ parsed.should be_a(Time)
83
+ end
84
+
85
+ it 'returns a Time instance with correct value' do
86
+ parsed.should == Time.at(4)
87
+ end
88
+
89
+ it 'returns a Time instance in UTC' do
90
+ parsed.zone.should == 'UTC'
91
+ end
92
+ end
93
+
94
+ context 'when input is an int32' do
95
+ let(:input) do
96
+ {'$numberInt' => '42'}
97
+ end
98
+
99
+ let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
100
+
101
+ context 'when :mode is nil' do
102
+ let(:mode) { nil }
103
+
104
+ it 'returns Integer instance' do
105
+ parsed.should be_a(Integer)
106
+ parsed.should == 42
107
+ end
108
+ end
109
+
110
+ context 'when :mode is :bson' do
111
+ let(:mode) { :bson }
112
+
113
+ it 'returns Integer instance' do
114
+ parsed.should be_a(Integer)
115
+ parsed.should == 42
116
+ end
117
+ end
118
+ end
119
+
120
+ context 'when input is an int64' do
121
+ let(:input) do
122
+ {'$numberLong' => '42'}
123
+ end
124
+
125
+ let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
126
+
127
+ context 'when :mode is nil' do
128
+ let(:mode) { nil }
129
+
130
+ it 'returns Integer instance' do
131
+ parsed.should be_a(Integer)
132
+ parsed.should == 42
133
+ end
134
+ end
135
+
136
+ context 'when :mode is :bson' do
137
+ let(:mode) { :bson }
138
+
139
+ it 'returns Int64 instance' do
140
+ parsed.should be_a(BSON::Int64)
141
+ parsed.value.should == 42
142
+ end
143
+ end
144
+ end
145
+
146
+ context 'when input is a hash' do
147
+ let(:input) do
148
+ {}
149
+ end
150
+
151
+ let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
152
+ let(:mode) { :bson }
153
+
154
+ context 'when mode is invalid' do
155
+ let(:mode) { :foo }
156
+
157
+ it 'raises an exception' do
158
+ lambda do
159
+ parsed
160
+ end.should raise_error(ArgumentError, /Invalid value for :mode option/)
161
+ end
162
+ end
163
+
164
+ context 'when it contains a string key with a null byte' do
165
+ let(:input) do
166
+ { "key\x00" => 1 }
167
+ end
168
+
169
+ it 'raises an exception' do
170
+ lambda do
171
+ parsed
172
+ end.should raise_error(BSON::Error::ExtJSONParseError, /Hash key cannot contain a null byte/)
173
+ end
174
+ end
175
+
176
+ context 'when it contains a symbol key with a null byte' do
177
+ let(:input) do
178
+ { "key\x00".to_sym => 1 }
179
+ end
180
+
181
+ it 'raises an exception' do
182
+ lambda do
183
+ parsed
184
+ end.should raise_error(BSON::Error::ExtJSONParseError, /Hash key cannot contain a null byte/)
185
+ end
186
+ end
187
+
188
+ context 'when it contains an integer key' do
189
+ let(:input) do
190
+ { 0 => 1 }
191
+ end
192
+
193
+ it 'does not raises an exception' do
194
+ lambda do
195
+ parsed
196
+ end.should_not raise_error
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'when input is a binary' do
202
+ let(:data) do
203
+ Base64.decode64("//8=")
204
+ end
205
+
206
+ context 'in current format' do
207
+ let(:input) do
208
+ { "$binary" => { "base64"=>"//8=", "subType"=>"00" } }
209
+ end
210
+
211
+ context 'when :mode is nil' do
212
+ let(:mode) { nil }
213
+
214
+ it 'returns BSON::Binary instance' do
215
+ parsed.should be_a(BSON::Binary)
216
+ parsed.data.should == data
217
+ end
218
+ end
219
+
220
+ context 'when mode is :bson' do
221
+ let(:mode) { :bson }
222
+
223
+ it 'returns BSON::Binary instance' do
224
+ parsed.should be_a(BSON::Binary)
225
+ parsed.data.should == data
226
+ end
227
+ end
228
+ end
229
+
230
+ context 'in legacy format' do
231
+ let(:input) do
232
+ { "$binary"=>"//8=", "$type"=>"00" }
233
+ end
234
+
235
+ context 'when :mode is nil' do
236
+ let(:mode) { nil }
237
+
238
+ it 'returns BSON::Binary instance' do
239
+ parsed.should be_a(BSON::Binary)
240
+ parsed.data.should == data
241
+ end
242
+ end
243
+
244
+ context 'when mode is :bson' do
245
+ let(:mode) { :bson }
246
+
247
+ it 'returns BSON::Binary instance' do
248
+ parsed.should be_a(BSON::Binary)
249
+ parsed.data.should == data
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ context 'when input is a regex' do
256
+ let(:pattern) { 'abc' }
257
+ let(:options) { 'im' }
258
+
259
+ context 'in current format' do
260
+ let(:input) do
261
+ { "$regularExpression" => { "pattern" => pattern, "options" => options } }
262
+ end
263
+
264
+ context 'when :mode is nil' do
265
+ let(:mode) { nil }
266
+
267
+ it 'returns a BSON::Regexp::Raw instance' do
268
+ parsed.should be_a(BSON::Regexp::Raw)
269
+ parsed.pattern.should == pattern
270
+ parsed.options.should == options
271
+ end
272
+ end
273
+
274
+ context 'when :mode is :bson' do
275
+ let(:mode) { :bson }
276
+
277
+ it 'returns a BSON::Regexp::Raw instance' do
278
+ parsed.should be_a(BSON::Regexp::Raw)
279
+ parsed.pattern.should == pattern
280
+ parsed.options.should == options
281
+ end
282
+ end
283
+ end
284
+
285
+ context 'in legacy format' do
286
+ let(:input) do
287
+ { "$regex" => pattern, "$options" => options }
288
+ end
289
+
290
+ context 'when :mode is nil' do
291
+ let(:mode) { nil }
292
+
293
+ it 'returns a BSON::Regexp::Raw instance' do
294
+ parsed.should be_a(BSON::Regexp::Raw)
295
+ parsed.pattern.should == pattern
296
+ parsed.options.should == options
297
+ end
298
+ end
299
+
300
+ context 'when :mode is :bson' do
301
+ let(:mode) { :bson }
302
+
303
+ it 'returns a BSON::Regexp::Raw instance' do
304
+ parsed.should be_a(BSON::Regexp::Raw)
305
+ parsed.pattern.should == pattern
306
+ parsed.options.should == options
307
+ end
308
+ end
309
+ end
310
+
311
+ context 'when $regularExpression is nested in $regex' do
312
+ context 'with options' do
313
+ let(:input) do
314
+ {
315
+ "$regex" => {
316
+ "$regularExpression" => { "pattern" => "foo*", "options" => "" },
317
+ },
318
+ "$options" => "ix",
319
+ }
320
+ end
321
+
322
+ it 'parses' do
323
+ parsed.should == {
324
+ '$regex' => BSON::Regexp::Raw.new('foo*'), '$options' => 'ix'
325
+ }
326
+ end
327
+ end
328
+
329
+ context 'without options' do
330
+ let(:input) do
331
+ {
332
+ "$regex" => {
333
+ "$regularExpression" => { "pattern" => "foo*", "options" => "" },
334
+ },
335
+ }
336
+ end
337
+
338
+ it 'parses' do
339
+ parsed.should == {
340
+ '$regex' => BSON::Regexp::Raw.new('foo*'),
341
+ }
342
+ end
343
+ end
344
+ end
345
+ end
346
+ end