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,328 @@
1
+ # rubocop:todo all
2
+ # Copyright (C) 2016-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
+ describe BigDecimal do
19
+
20
+ describe '#from_bson' do
21
+ shared_examples_for 'a BSON::BigDecimal deserializer' do
22
+
23
+ let(:decimal128) do
24
+ BSON::Decimal128.new(argument)
25
+ end
26
+
27
+ let(:deserialized_big_decimal) do
28
+ BigDecimal.from_bson(decimal128.to_bson)
29
+ end
30
+
31
+ let(:deserialized_decimal128) do
32
+ BSON::Decimal128.from_bson(decimal128.to_bson)
33
+ end
34
+
35
+ it 'deserializes Decimal128 encoded bson correctly' do
36
+ if deserialized_decimal128.to_s == "NaN"
37
+ expect(deserialized_big_decimal.nan?).to be true
38
+ else
39
+ expect(deserialized_big_decimal).to eq(deserialized_decimal128.to_d)
40
+ end
41
+ end
42
+ end
43
+
44
+ context 'when Infinity is passed' do
45
+
46
+ let(:argument) { "Infinity" }
47
+
48
+ it_behaves_like 'a BSON::BigDecimal deserializer'
49
+ end
50
+
51
+ context 'when -Infinity is passed' do
52
+
53
+ let(:argument) { "-Infinity" }
54
+
55
+ it_behaves_like 'a BSON::BigDecimal deserializer'
56
+ end
57
+
58
+ context 'when NaN is passed' do
59
+
60
+ let(:argument) { "NaN" }
61
+
62
+ it_behaves_like 'a BSON::BigDecimal deserializer'
63
+ end
64
+
65
+ context 'when -NaN is passed' do
66
+ let(:argument) { "-NaN" }
67
+
68
+ it_behaves_like 'a BSON::BigDecimal deserializer'
69
+ end
70
+
71
+ context 'when SNaN is passed' do
72
+ let(:argument) { "SNaN" }
73
+
74
+ it_behaves_like 'a BSON::BigDecimal deserializer'
75
+ end
76
+
77
+ context 'when -SNaN is passed' do
78
+ let(:argument) { "SNaN" }
79
+
80
+ it_behaves_like 'a BSON::BigDecimal deserializer'
81
+ end
82
+
83
+ context 'when -0 is passed' do
84
+ let(:argument) { "-0" }
85
+
86
+ it_behaves_like 'a BSON::BigDecimal deserializer'
87
+ end
88
+
89
+ context 'when a positive integer is passed' do
90
+ let(:argument) { "12" }
91
+
92
+ it_behaves_like 'a BSON::BigDecimal deserializer'
93
+ end
94
+
95
+ context 'when a negative integer is passed' do
96
+ let(:argument) { "-12" }
97
+
98
+ it_behaves_like 'a BSON::BigDecimal deserializer'
99
+ end
100
+
101
+ context 'when a positive float is passed' do
102
+ let(:argument) { "0.12345" }
103
+
104
+ it_behaves_like 'a BSON::BigDecimal deserializer'
105
+ end
106
+
107
+ context 'when a negative float is passed' do
108
+ let(:argument) { "-0.12345" }
109
+
110
+ it_behaves_like 'a BSON::BigDecimal deserializer'
111
+ end
112
+
113
+ context 'when a large positive integer is passed' do
114
+ let(:argument) { "1234567890123456789012345678901234" }
115
+
116
+ it_behaves_like 'a BSON::BigDecimal deserializer'
117
+ end
118
+
119
+ context 'when a large negative integer is passed' do
120
+ let(:argument) { "-1234567890123456789012345678901234" }
121
+
122
+ it_behaves_like 'a BSON::BigDecimal deserializer'
123
+ end
124
+ end
125
+
126
+ describe "#to_bson" do
127
+ shared_examples_for 'a BSON::BigDecimal serializer' do
128
+
129
+ let(:decimal128) do
130
+ BSON::Decimal128.new(BigDecimal(argument).to_s)
131
+ end
132
+
133
+ let(:decimal_128_bson) do
134
+ decimal128.to_bson
135
+ end
136
+
137
+ let(:big_decimal_bson) do
138
+ BigDecimal(argument).to_bson
139
+ end
140
+
141
+ it 'serializes BigDecimals correctly' do
142
+ expect(decimal_128_bson.to_s).to eq(big_decimal_bson.to_s)
143
+ end
144
+ end
145
+
146
+ context 'when Infinity is passed' do
147
+
148
+ let(:argument) { "Infinity" }
149
+
150
+ it_behaves_like 'a BSON::BigDecimal serializer'
151
+ end
152
+
153
+ context 'when -Infinity is passed' do
154
+
155
+ let(:argument) { "-Infinity" }
156
+
157
+ it_behaves_like 'a BSON::BigDecimal serializer'
158
+ end
159
+
160
+ context 'when NaN is passed' do
161
+
162
+ let(:argument) { "NaN" }
163
+
164
+ it_behaves_like 'a BSON::BigDecimal serializer'
165
+ end
166
+
167
+ context 'when -0 is passed' do
168
+ let(:argument) { "-0" }
169
+
170
+ it_behaves_like 'a BSON::BigDecimal serializer'
171
+ end
172
+
173
+ context 'when a positive integer is passed' do
174
+ let(:argument) { "12" }
175
+
176
+ it_behaves_like 'a BSON::BigDecimal serializer'
177
+ end
178
+
179
+ context 'when a negative integer is passed' do
180
+ let(:argument) { "-12" }
181
+
182
+ it_behaves_like 'a BSON::BigDecimal serializer'
183
+ end
184
+
185
+ context 'when a positive float is passed' do
186
+ let(:argument) { "0.12345" }
187
+
188
+ it_behaves_like 'a BSON::BigDecimal serializer'
189
+ end
190
+
191
+ context 'when a negative float is passed' do
192
+ let(:argument) { "-0.12345" }
193
+
194
+ it_behaves_like 'a BSON::BigDecimal serializer'
195
+ end
196
+
197
+ context 'when a large positive integer is passed' do
198
+ let(:argument) { "1234567890123456789012345678901234" }
199
+
200
+ it_behaves_like 'a BSON::BigDecimal serializer'
201
+ end
202
+
203
+ context 'when a large negative integer is passed' do
204
+ let(:argument) { "-1234567890123456789012345678901234" }
205
+
206
+ it_behaves_like 'a BSON::BigDecimal serializer'
207
+ end
208
+
209
+ context "when passing an out of range Decimal128" do
210
+ let(:argument) { "1E1000000" }
211
+
212
+ it "raises an error" do
213
+ expect do
214
+ BigDecimal(argument).to_bson
215
+ end.to raise_error(BSON::Error::InvalidDecimal128Range)
216
+ end
217
+ end
218
+
219
+ context "when passing a number with too much precision for Decimal128" do
220
+ let(:argument) { "1.000000000000000000000000000000000000000000000000001" }
221
+
222
+ it "raises an error" do
223
+ expect do
224
+ BigDecimal(argument).to_bson
225
+ end.to raise_error(BSON::Error::UnrepresentablePrecision)
226
+ end
227
+ end
228
+ end
229
+
230
+ describe "#from_bson/#to_bson" do
231
+ shared_examples_for 'a BSON::BigDecimal round trip' do
232
+
233
+ let(:big_decimal) do
234
+ BigDecimal(argument)
235
+ end
236
+
237
+ let(:big_decimal_bson) do
238
+ big_decimal.to_bson
239
+ end
240
+
241
+ let(:deserialized_big_decimal) do
242
+ BigDecimal.from_bson(big_decimal_bson)
243
+ end
244
+
245
+ it 'serializes BigDecimals correctly' do
246
+ if big_decimal.nan?
247
+ expect(deserialized_big_decimal.nan?).to be true
248
+ else
249
+ expect(deserialized_big_decimal).to eq(big_decimal)
250
+ end
251
+ end
252
+ end
253
+
254
+ context 'when Infinity is passed' do
255
+
256
+ let(:argument) { "Infinity" }
257
+
258
+ it_behaves_like 'a BSON::BigDecimal round trip'
259
+ end
260
+
261
+ context 'when -Infinity is passed' do
262
+
263
+ let(:argument) { "-Infinity" }
264
+
265
+ it_behaves_like 'a BSON::BigDecimal round trip'
266
+ end
267
+
268
+ context 'when NaN is passed' do
269
+
270
+ let(:argument) { "NaN" }
271
+
272
+ it_behaves_like 'a BSON::BigDecimal round trip'
273
+ end
274
+
275
+ context 'when -0 is passed' do
276
+ let(:argument) { "-0" }
277
+
278
+ it_behaves_like 'a BSON::BigDecimal round trip'
279
+ end
280
+
281
+ context 'when a positive integer is passed' do
282
+ let(:argument) { "12" }
283
+
284
+ it_behaves_like 'a BSON::BigDecimal round trip'
285
+ end
286
+
287
+ context 'when a negative integer is passed' do
288
+ let(:argument) { "-12" }
289
+
290
+ it_behaves_like 'a BSON::BigDecimal round trip'
291
+ end
292
+
293
+ context 'when a positive float is passed' do
294
+ let(:argument) { "0.12345" }
295
+
296
+ it_behaves_like 'a BSON::BigDecimal round trip'
297
+ end
298
+
299
+ context 'when a negative float is passed' do
300
+ let(:argument) { "-0.12345" }
301
+
302
+ it_behaves_like 'a BSON::BigDecimal round trip'
303
+ end
304
+
305
+ context 'when a large positive integer is passed' do
306
+ let(:argument) { "1234567890123456789012345678901234" }
307
+
308
+ it_behaves_like 'a BSON::BigDecimal round trip'
309
+ end
310
+
311
+ context 'when a large negative integer is passed' do
312
+ let(:argument) { "-1234567890123456789012345678901234" }
313
+
314
+ it_behaves_like 'a BSON::BigDecimal round trip'
315
+ end
316
+ end
317
+
318
+ context "when the class is loaded" do
319
+
320
+ let(:registered) do
321
+ BSON::Registry.get(described_class::BSON_TYPE, 'field')
322
+ end
323
+
324
+ it "registers the type" do
325
+ expect(registered).to eq(described_class)
326
+ end
327
+ end
328
+ end
@@ -1,4 +1,5 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # rubocop:todo all
2
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
4
5
  # you may not use this file except in compliance with the License.
@@ -13,22 +14,62 @@
13
14
  # limitations under the License.
14
15
 
15
16
  require "spec_helper"
17
+ require "base64"
16
18
 
17
19
  describe BSON::Binary do
18
20
  let(:testing1) { described_class.new("testing") }
19
21
  let(:testing2) { described_class.new("testing") }
20
22
  let(:not_testing) { described_class.new("not testing") }
23
+ let(:testing3) { described_class.new("testing", :user) }
24
+
25
+ describe "Comparable" do
26
+ describe "#eql?" do
27
+ context "for two equal objects" do
28
+ it "returns true" do
29
+ expect(testing1).to eql(testing2)
30
+ end
31
+ end
21
32
 
22
- describe "#eql?" do
23
- context "for two equal objects" do
24
- it "returns true" do
25
- expect(testing1).to eql(testing2)
33
+ context "for two different objects" do
34
+ it "returns false" do
35
+ expect(testing1).not_to eql(not_testing)
36
+ end
37
+ end
38
+
39
+ context 'for objects with identical data but different types' do
40
+ it 'returns false' do
41
+ expect(testing1).not_to eql(testing3)
42
+ end
26
43
  end
27
44
  end
28
45
 
29
- context "for two different objects" do
30
- it "returns false" do
31
- expect(testing1).not_to eql(not_testing)
46
+ describe '#<=>' do
47
+ context 'with a non-Binary object' do
48
+ it 'returns nil' do
49
+ expect(testing1 <=> 'bogus').to be_nil
50
+ end
51
+ end
52
+
53
+ context 'with identical type and data' do
54
+ it 'returns 0' do
55
+ expect(testing1 <=> testing2).to be == 0
56
+ end
57
+ end
58
+
59
+ context 'with mismatched type' do
60
+ it 'returns nil' do
61
+ expect(testing1 <=> testing3).to be_nil
62
+ end
63
+ end
64
+
65
+ context 'with identical type but mismatched data' do
66
+ it 'returns -1 when a < b' do
67
+ expect(not_testing <=> testing1).to be == -1
68
+ end
69
+
70
+ it 'returns 1 when a > b' do
71
+ expect(testing1 <=> not_testing).to be == 1
72
+ end
32
73
  end
33
74
  end
34
75
  end
@@ -54,34 +95,52 @@ describe BSON::Binary do
54
95
  expect(hash[not_testing]).to be_nil
55
96
  end
56
97
 
57
- describe "#as_json" do
98
+ describe "#as_extended_json" do
58
99
 
59
100
  let(:object) do
60
101
  described_class.new("testing", :user)
61
102
  end
62
103
 
63
104
  it "returns the binary data plus type" do
64
- expect(object.as_json).to eq(
65
- { "$binary" => "testing", "$type" => :user }
105
+ expect(object.as_extended_json).to eq(
106
+ { "$binary" => {'base64' => Base64.encode64("testing").strip, "subType" => '80' }}
66
107
  )
67
108
  end
68
109
 
69
- it_behaves_like "a JSON serializable object"
110
+ it_behaves_like 'an Extended JSON serializable object'
111
+ it_behaves_like '#as_json calls #as_extended_json'
70
112
  end
71
113
 
72
114
  describe "#initialize" do
73
115
 
74
- context "when he type is invalid" do
116
+ context 'when type is not given' do
117
+ let(:obj) { described_class.new('foo') }
118
+
119
+ it 'defaults to generic type' do
120
+ expect(obj.type).to eq(:generic)
121
+ end
122
+ end
123
+
124
+ context "when the type is invalid" do
75
125
 
76
126
  it "raises an error" do
77
127
  expect {
78
128
  described_class.new("testing", :error)
79
129
  }.to raise_error { |error|
80
- expect(error).to be_a(BSON::Binary::InvalidType)
130
+ expect(error).to be_a(BSON::Error::InvalidBinaryType)
81
131
  expect(error.message).to match /is not a valid binary type/
82
132
  }
83
133
  end
84
134
  end
135
+
136
+ context 'when initialized via legacy YAML' do
137
+ let(:yaml) { "--- !ruby/object:BSON::Binary\ndata: hello\ntype: :generic\n" }
138
+ let(:deserialized) { YAML.safe_load(yaml, permitted_classes: [ Symbol, BSON::Binary ]) }
139
+
140
+ it 'correctly sets the raw_type' do
141
+ expect(deserialized.raw_type).to be == BSON::Binary::SUBTYPES[:generic]
142
+ end
143
+ end
85
144
  end
86
145
 
87
146
  describe '#inspect' do
@@ -104,7 +163,11 @@ describe BSON::Binary do
104
163
  expect(object.inspect).to eq("<BSON::Binary:0x#{object.object_id} type=user data=0x1f8b08000c787055...>")
105
164
  end
106
165
 
107
- it 'is not different from default encoding' do
166
+ it 'is not binary' do
167
+ # As long as the default Ruby encoding is not binary, the inspected
168
+ # string should also not be in the binary encoding (it should be
169
+ # in one of the text encodings, but which one could depend on
170
+ # the Ruby runtime environment).
108
171
  expect(object.inspect.encoding).not_to eq(Encoding::BINARY)
109
172
  end
110
173
 
@@ -112,73 +175,156 @@ describe BSON::Binary do
112
175
 
113
176
  end
114
177
 
115
- describe "#to_bson/#from_bson" do
116
-
117
- let(:type) { 5.chr }
178
+ describe '#from_bson' do
179
+ let(:buffer) { BSON::ByteBuffer.new(bson) }
180
+ let(:obj) { described_class.from_bson(buffer) }
118
181
 
119
- it_behaves_like "a bson element"
182
+ let(:bson) { "#{5.to_bson}#{0.chr}hello".force_encoding('BINARY') }
120
183
 
121
- context "when the type is :generic" do
184
+ it 'sets data encoding to binary' do
185
+ expect(obj.data.encoding).to eq(Encoding.find('BINARY'))
186
+ end
122
187
 
123
- let(:obj) { described_class.new("testing") }
124
- let(:bson) { "#{7.to_bson}#{0.chr}testing" }
188
+ context 'when binary subtype is supported' do
189
+ let(:bson) { [3, 0, 0, 0, 1].map(&:chr).join.force_encoding('BINARY') + 'foo' }
125
190
 
126
- it_behaves_like "a serializable bson element"
127
- it_behaves_like "a deserializable bson element"
191
+ it 'works' do
192
+ obj.should be_a(described_class)
193
+ obj.type.should be :function
194
+ end
128
195
  end
129
196
 
130
- context "when the type is :function" do
131
-
132
- let(:obj) { described_class.new("testing", :function) }
133
- let(:bson) { "#{7.to_bson}#{1.chr}testing" }
197
+ context 'when binary subtype is not supported' do
198
+ let(:bson) { [3, 0, 0, 0, 16].map(&:chr).join.force_encoding('BINARY') + 'foo' }
134
199
 
135
- it_behaves_like "a serializable bson element"
136
- it_behaves_like "a deserializable bson element"
200
+ it 'raises an exception' do
201
+ lambda do
202
+ obj
203
+ end.should raise_error(BSON::Error::UnsupportedBinarySubtype,
204
+ /BSON data contains unsupported binary subtype 0x10/)
205
+ end
137
206
  end
207
+ end
138
208
 
139
- context "when the type is :old" do
209
+ describe "#to_bson/#from_bson" do
140
210
 
141
- let(:obj) { described_class.new("testing", :old) }
142
- let(:bson) { "#{11.to_bson}#{2.chr}#{7.to_bson}testing" }
211
+ let(:type) { 5.chr }
143
212
 
144
- it_behaves_like "a serializable bson element"
145
- it_behaves_like "a deserializable bson element"
146
- end
213
+ it_behaves_like "a bson element"
147
214
 
148
- context "when the type is :uuid_old" do
215
+ [
216
+ {
217
+ types: [ nil, 0, 0.chr, :generic, 'generic' ],
218
+ bson: "#{7.to_bson}#{0.chr}testing",
219
+ type: :generic,
220
+ },
221
+ {
222
+ types: [ 1, 1.chr, :function, 'function' ],
223
+ bson: "#{7.to_bson}#{1.chr}testing",
224
+ type: :function,
225
+ },
226
+ {
227
+ types: [ 2, 2.chr, :old, 'old' ],
228
+ bson: "#{11.to_bson}#{2.chr}#{7.to_bson}testing",
229
+ type: :old,
230
+ },
231
+ {
232
+ types: [ 3, 3.chr, :uuid_old, 'uuid_old' ],
233
+ bson: "#{7.to_bson}#{3.chr}testing",
234
+ type: :uuid_old,
235
+ },
236
+ {
237
+ types: [ 4, 4.chr, :uuid, 'uuid' ],
238
+ bson: "#{7.to_bson}#{4.chr}testing",
239
+ type: :uuid,
240
+ },
241
+ {
242
+ types: [ 5, 5.chr, :md5, 'md5' ],
243
+ bson: "#{7.to_bson}#{5.chr}testing",
244
+ type: :md5,
245
+ },
246
+ {
247
+ types: [ 6, 6.chr, :ciphertext, 'ciphertext' ],
248
+ bson: "#{7.to_bson}#{6.chr}testing",
249
+ type: :ciphertext,
250
+ },
251
+ {
252
+ types: [ 0x80, 0x80.chr, :user, 'user' ],
253
+ bson: "#{7.to_bson}#{128.chr}testing",
254
+ type: :user,
255
+ },
256
+ {
257
+ types: [ 0xFF, 0xFF.chr ],
258
+ bson: "#{7.to_bson}#{0xFF.chr}testing",
259
+ type: :user,
260
+ },
261
+ ].each do |defn|
262
+ defn[:types].each do |type|
263
+ context "when the type is #{type ? type.inspect : 'not provided'}" do
264
+ let(:obj) do
265
+ if type
266
+ described_class.new("testing", type)
267
+ else
268
+ described_class.new("testing")
269
+ end
270
+ end
271
+
272
+ let(:bson) { defn[:bson] }
273
+
274
+ it_behaves_like "a serializable bson element"
275
+ it_behaves_like "a deserializable bson element"
276
+
277
+ it "reports its type as #{defn[:type].inspect}" do
278
+ expect(obj.type).to be == defn[:type]
279
+ end
280
+ end
281
+ end
282
+ end
149
283
 
150
- let(:obj) { described_class.new("testing", :uuid_old) }
151
- let(:bson) { "#{7.to_bson}#{3.chr}testing" }
284
+ context 'when given binary string' do
285
+ let(:obj) { described_class.new("\x00\xfe\xff".force_encoding('BINARY')) }
286
+ let(:bson) { "#{3.to_bson}#{0.chr}\x00\xfe\xff".force_encoding('BINARY') }
152
287
 
153
288
  it_behaves_like "a serializable bson element"
154
289
  it_behaves_like "a deserializable bson element"
155
290
  end
156
291
 
157
- context "when the type is :uuid" do
158
-
159
- let(:obj) { described_class.new("testing", :uuid) }
160
- let(:bson) { "#{7.to_bson}#{4.chr}testing" }
292
+ context 'when given a frozen string' do
293
+ let(:str) { "\x00\xfe\xff".force_encoding('BINARY').freeze }
294
+ let(:obj) { described_class.new(str) }
295
+ let(:bson) { "#{3.to_bson}#{0.chr}\x00\xfe\xff".force_encoding('BINARY') }
161
296
 
162
297
  it_behaves_like "a serializable bson element"
163
298
  it_behaves_like "a deserializable bson element"
164
299
  end
300
+ end
165
301
 
166
- context "when the type is :md5" do
302
+ describe '#to_uuid' do
303
+ let(:obj) { described_class.new("\x00" * 16, :uuid) }
167
304
 
168
- let(:obj) { described_class.new("testing", :md5) }
169
- let(:bson) { "#{7.to_bson}#{5.chr}testing" }
305
+ it 'accepts symbol representation' do
306
+ expect(obj.to_uuid(:standard)).to eq('00000000-0000-0000-0000-000000000000')
307
+ end
170
308
 
171
- it_behaves_like "a serializable bson element"
172
- it_behaves_like "a deserializable bson element"
309
+ it 'rejects string representation' do
310
+ expect do
311
+ obj.to_uuid('standard')
312
+ end.to raise_error(ArgumentError, /Representation must be given as a symbol/)
173
313
  end
314
+ end
174
315
 
175
- context "when the type is :user" do
316
+ describe '#from_uuid' do
317
+ let(:uuid) { '00000000-0000-0000-0000000000000000' }
176
318
 
177
- let(:obj) { described_class.new("testing", :user) }
178
- let(:bson) { "#{7.to_bson}#{128.chr}testing" }
319
+ it 'accepts symbol representation' do
320
+ obj = described_class.from_uuid(uuid, :standard)
321
+ expect(obj.data).to eq("\x00" * 16)
322
+ end
179
323
 
180
- it_behaves_like "a serializable bson element"
181
- it_behaves_like "a deserializable bson element"
324
+ it 'rejects string representation' do
325
+ expect do
326
+ described_class.from_uuid(uuid, 'standard')
327
+ end.to raise_error(ArgumentError, /Representation must be given as a symbol/)
182
328
  end
183
329
  end
184
330
  end