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
@@ -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.
@@ -25,4 +26,11 @@ describe FalseClass do
25
26
  it_behaves_like "a bson element"
26
27
  it_behaves_like "a serializable bson element"
27
28
  end
29
+
30
+ describe '#as_extended_json' do
31
+ let(:object) { false }
32
+
33
+ it_behaves_like '#as_extended_json returns self'
34
+ it_behaves_like 'an Extended JSON serializable object'
35
+ end
28
36
  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.
@@ -26,4 +27,44 @@ describe Float do
26
27
  it_behaves_like "a serializable bson element"
27
28
  it_behaves_like "a deserializable bson element"
28
29
  end
30
+
31
+ describe '#to_json' do
32
+ it 'returns float' do
33
+ 42.0.to_json.should == '42.0'
34
+ end
35
+ end
36
+
37
+ describe '#as_extended_json' do
38
+ let(:object) { 42.0 }
39
+
40
+ context 'canonical mode' do
41
+ it 'returns $numberDouble' do
42
+ object.as_extended_json.should == {'$numberDouble' => '42.0'}
43
+ end
44
+ end
45
+
46
+ context 'relaxed mode' do
47
+ let(:serialized) do
48
+ object.as_extended_json(mode: :relaxed)
49
+ end
50
+
51
+ it 'returns float' do
52
+ serialized.should be_a(Float)
53
+ serialized.should be_within(0.00001).of(42)
54
+ end
55
+ end
56
+
57
+ context 'legacy mode' do
58
+ let(:serialized) do
59
+ object.as_extended_json(mode: :legacy)
60
+ end
61
+
62
+ it 'returns float' do
63
+ serialized.should be_a(Float)
64
+ serialized.should be_within(0.00001).of(42)
65
+ end
66
+ end
67
+
68
+ it_behaves_like "an Extended JSON serializable object"
69
+ end
29
70
  end
@@ -0,0 +1,58 @@
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
+ describe 'Hash ActiveSupport extensions' do
19
+ require_active_support
20
+
21
+ describe '#symbolize_keys' do
22
+ let(:symbolized) { hash.symbolize_keys }
23
+
24
+ shared_examples 'works correctly' do
25
+ it 'returns a hash' do
26
+ symbolized.class.should be Hash
27
+ end
28
+
29
+ it 'works correctly' do
30
+ hash.symbolize_keys.should == {foo: 'bar'}
31
+ end
32
+ end
33
+
34
+ context 'string keys' do
35
+ let(:hash) do
36
+ {'foo' => 'bar'}
37
+ end
38
+
39
+ include_examples 'works correctly'
40
+ end
41
+
42
+ context 'symbol keys' do
43
+ let(:hash) do
44
+ {foo: 'bar'}
45
+ end
46
+
47
+ include_examples 'works correctly'
48
+ end
49
+
50
+ context 'both string and symbol keys' do
51
+ let(:hash) do
52
+ {'foo' => 42, foo: 'bar'}
53
+ end
54
+
55
+ include_examples 'works correctly'
56
+ end
57
+ end
58
+ 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.
@@ -52,110 +53,361 @@ describe Hash do
52
53
  end
53
54
  end
54
55
 
55
- context "when the hash has invalid keys" do
56
+ context "when the hash has dollar keys" do
56
57
 
57
58
  let(:obj) do
58
59
  { "$testing" => "value" }
59
60
  end
60
61
 
61
- context "when validating keys" do
62
62
 
63
- context "when validating globally" do
64
-
65
- before do
66
- BSON::Config.validating_keys = true
67
- end
63
+ let(:bson) do
64
+ "#{25.to_bson.to_s}#{String::BSON_TYPE}$testing#{BSON::NULL_BYTE}" +
65
+ "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
66
+ end
68
67
 
69
- after do
70
- BSON::Config.validating_keys = false
71
- end
68
+ it "serializes the hash" do
69
+ expect(obj.to_bson.to_s).to eq(bson)
70
+ end
72
71
 
73
- it "raises an error" do
74
- expect {
75
- obj.to_bson
76
- }.to raise_error(BSON::String::IllegalKey)
77
- end
72
+ context "when the hash contains an array of documents containing invalid keys" do
78
73
 
79
- context "when the hash contains an array of documents containing invalid keys" do
74
+ let(:obj) do
75
+ { "array" => [{ "$testing" => "value" }] }
76
+ end
80
77
 
81
- let(:obj) do
82
- { "array" => [{ "$testing" => "value" }] }
83
- end
78
+ let(:bson) do
79
+ "#{45.to_bson.to_s}#{Array::BSON_TYPE}array#{BSON::NULL_BYTE}" +
80
+ "#{[{ "$testing" => "value" }].to_bson.to_s}#{BSON::NULL_BYTE}"
81
+ end
84
82
 
85
- it "raises an error" do
86
- expect {
87
- obj.to_bson
88
- }.to raise_error(BSON::String::IllegalKey)
89
- end
90
- end
83
+ it "serializes the hash" do
84
+ expect(obj.to_bson.to_s).to eq(bson)
91
85
  end
86
+ end
87
+ end
92
88
 
93
- context "when validating locally" do
89
+ context "when the hash is embedded" do
94
90
 
95
- it "raises an error" do
96
- expect {
97
- obj.to_bson(BSON::ByteBuffer.new, true)
98
- }.to raise_error(BSON::String::IllegalKey)
99
- end
91
+ let(:obj) do
92
+ { "field" => { "key" => "value" }}
93
+ end
94
+
95
+ let(:bson) do
96
+ "#{32.to_bson.to_s}#{Hash::BSON_TYPE}field#{BSON::NULL_BYTE}" +
97
+ "#{20.to_bson.to_s}#{String::BSON_TYPE}key#{BSON::NULL_BYTE}" +
98
+ "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
99
+ end
100
+
101
+ it_behaves_like "a serializable bson element"
102
+ it_behaves_like "a deserializable bson element"
103
+ end
100
104
 
101
- context "when the hash contains an array of documents containing invalid keys" do
105
+ context 'with symbol values' do
106
+ let(:value) { :foo }
102
107
 
103
- let(:obj) do
104
- { "array" => [{ "$testing" => "value" }] }
105
- end
108
+ let(:serialized) do
109
+ {foo: value}.to_bson.to_s
110
+ end
106
111
 
107
- it "raises an error" do
108
- expect {
109
- obj.to_bson(BSON::ByteBuffer.new, true)
110
- }.to raise_error(BSON::String::IllegalKey)
111
- end
112
+ def perform_test(bson_type_to_use)
113
+ Symbol.class_eval do
114
+ alias_method :bson_type_orig, :bson_type
115
+ define_method(:bson_type) do
116
+ bson_type_to_use
117
+ end
118
+ end
119
+
120
+ begin
121
+ yield
122
+ ensure
123
+ Symbol.class_eval do
124
+ alias_method :bson_type, :bson_type_orig
125
+ remove_method :bson_type_orig
112
126
  end
113
127
  end
114
128
  end
115
129
 
116
- context "when not validating keys" do
130
+ let(:bson_with_symbol) do
131
+ "\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00bar\x00\x00".force_encoding('binary')
132
+ end
117
133
 
118
- let(:bson) do
119
- "#{25.to_bson.to_s}#{String::BSON_TYPE}$testing#{BSON::NULL_BYTE}" +
120
- "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
134
+ let(:deserialized) do
135
+ Hash.from_bson(BSON::ByteBuffer.new(bson_with_symbol))
136
+ end
137
+
138
+ context 'when Symbol#bson_type is set to symbol' do
139
+ let(:bson_type_to_use) { BSON::Symbol::BSON_TYPE }
140
+
141
+ let(:expected) do
142
+ "\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00foo\x00\x00".force_encoding('binary')
121
143
  end
122
144
 
123
- it "serializes the hash" do
124
- expect(obj.to_bson.to_s).to eq(bson)
145
+ it 'serializes to BSON symbol' do
146
+ perform_test(bson_type_to_use) do
147
+ serialized
148
+ end.should == expected
125
149
  end
126
150
 
127
- context "when the hash contains an array of documents containing invalid keys" do
151
+ it 'deserializes to Symbol' do
152
+ deserialized.should == {'foo' => :bar}
153
+ end
154
+ end
128
155
 
129
- let(:obj) do
130
- { "array" => [{ "$testing" => "value" }] }
131
- end
156
+ context 'when Symbol#bson_type is set to string' do
157
+ let(:bson_type_to_use) { BSON::String::BSON_TYPE }
132
158
 
133
- let(:bson) do
134
- "#{45.to_bson.to_s}#{Array::BSON_TYPE}array#{BSON::NULL_BYTE}" +
135
- "#{[{ "$testing" => "value" }].to_bson.to_s}#{BSON::NULL_BYTE}"
136
- end
159
+ let(:expected) do
160
+ "\x12\x00\x00\x00\x02foo\x00\x04\x00\x00\x00foo\x00\x00".force_encoding('binary')
161
+ end
137
162
 
138
- it "serializes the hash" do
139
- expect(obj.to_bson.to_s).to eq(bson)
140
- end
163
+ it 'serializes to BSON string' do
164
+ perform_test(bson_type_to_use) do
165
+ serialized
166
+ end.should == expected
167
+ end
168
+
169
+ it 'deserializes to Symbol' do
170
+ deserialized.should == {'foo' => :bar}
141
171
  end
142
172
  end
143
173
  end
144
174
 
145
- context "when the hash is embedded" do
175
+ context 'when hash contains value of an unserializable class' do
176
+ class HashSpecUnserializableClass
177
+ end
146
178
 
147
179
  let(:obj) do
148
- { "field" => { "key" => "value" }}
180
+ {foo: HashSpecUnserializableClass.new}
149
181
  end
150
182
 
151
- let(:bson) do
152
- "#{32.to_bson.to_s}#{Hash::BSON_TYPE}field#{BSON::NULL_BYTE}" +
153
- "#{20.to_bson.to_s}#{String::BSON_TYPE}key#{BSON::NULL_BYTE}" +
154
- "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
183
+ it 'raises UnserializableClass' do
184
+ lambda do
185
+ obj.to_bson
186
+ end.should raise_error(BSON::Error::UnserializableClass,
187
+ # C extension does not provide hash key in the exception message.
188
+ /(Hash value for key 'foo'|Value) does not define its BSON serialized type:.*HashSpecUnserializableClass/)
155
189
  end
190
+ end
156
191
 
157
- it_behaves_like "a serializable bson element"
158
- it_behaves_like "a deserializable bson element"
192
+ context 'when reading from a byte buffer that was previously written to' do
193
+ let(:buffer) do
194
+ {foo: 42}.to_bson
195
+ end
196
+
197
+ it 'returns the original hash' do
198
+ expect(Hash.from_bson(buffer)).to eq('foo' => 42)
199
+ end
200
+ end
201
+
202
+ context 'when round-tripping a BigDecimal' do
203
+ let(:to_bson) do
204
+ {"x" => BigDecimal('1')}.to_bson
205
+ end
206
+
207
+ let(:from_bson) do
208
+ Hash.from_bson(to_bson)
209
+ end
210
+
211
+ it 'doesn\'t raise on serialization' do
212
+ expect do
213
+ to_bson
214
+ end.to_not raise_error
215
+ end
216
+
217
+ it 'deserializes as a BigDecimal' do
218
+ expect(from_bson).to eq({"x" => BigDecimal(1)})
219
+ end
220
+ end
221
+
222
+ context 'when deserializing round-tripping a Decimal128' do
223
+ let(:to_bson) do
224
+ {x:BSON::Decimal128.new('1')}.to_bson
225
+ end
226
+
227
+ let(:from_bson) do
228
+ Hash.from_bson(to_bson)
229
+ end
230
+
231
+ it 'doesn\'t raise on serialization' do
232
+ expect do
233
+ to_bson
234
+ end.to_not raise_error
235
+ end
236
+
237
+ it 'deserializes as a BigDecimal' do
238
+ expect(from_bson).to eq({"x" => BigDecimal(1)})
239
+ end
240
+ end
241
+ end
242
+
243
+ describe '#to_bson' do
244
+ context 'when a key is not valid utf-8' do
245
+ let(:key) { Utils.make_byte_string([254, 253, 255]) }
246
+ let(:hash) do
247
+ {key => 'foo'}
248
+ end
249
+
250
+ let(:expected_message) do
251
+ if BSON::Environment.jruby?
252
+ # Uses JRE conversion to another encoding
253
+ /Error serializing key.*Encoding::UndefinedConversionError/
254
+ else
255
+ # Uses our validator
256
+ /Key.*is not valid UTF-8/
257
+ end
258
+ end
259
+
260
+ it 'raises EncodingError' do
261
+ expect do
262
+ hash.to_bson
263
+ end.to raise_error(EncodingError, expected_message)
264
+ end
265
+ end
266
+
267
+ context 'when a key contains null bytes' do
268
+ let(:hash) do
269
+ {"\x00".force_encoding('BINARY') => 'foo'}
270
+ end
271
+
272
+ it 'raises ArgumentError' do
273
+ expect do
274
+ hash.to_bson
275
+ end.to raise_error(ArgumentError, /[Kk]ey.*contains null bytes/)
276
+ end
277
+ end
278
+
279
+ context 'when a value is not valid utf-8' do
280
+ let(:hash) do
281
+ {'foo' => [254, 253, 255].map(&:chr).join.force_encoding('BINARY')}
282
+ end
283
+
284
+ let(:expected_message) do
285
+ /from ASCII-8BIT to UTF-8/
286
+ end
287
+
288
+ it 'raises EncodingError' do
289
+ expect do
290
+ hash.to_bson
291
+ end.to raise_error(EncodingError, expected_message)
292
+ end
293
+ end
294
+
295
+ context 'when a value contains null bytes' do
296
+ let(:hash) do
297
+ {'foo' => "\x00".force_encoding('BINARY')}
298
+ end
299
+
300
+ it 'works' do
301
+ expect do
302
+ hash.to_bson
303
+ end.not_to raise_error
304
+ end
305
+ end
306
+
307
+ context 'when serializing a hash with a BigDecimal' do
308
+ let(:hash) do
309
+ {'foo' => BigDecimal('1')}
310
+ end
311
+
312
+ it 'works' do
313
+ expect do
314
+ hash.to_bson
315
+ end.to_not raise_error
316
+ end
317
+ end
318
+ end
319
+
320
+ describe '#from_bson' do
321
+ context 'when bson document has duplicate keys' do
322
+ let(:buf) do
323
+ buf = BSON::ByteBuffer.new
324
+ buf.put_int32(37)
325
+ buf.put_byte("\x02")
326
+ buf.put_cstring('foo')
327
+ buf.put_string('bar')
328
+ buf.put_byte("\x02")
329
+ buf.put_cstring('foo')
330
+ buf.put_string('overwrite')
331
+ buf.put_byte("\x00")
332
+
333
+ BSON::ByteBuffer.new(buf.to_s)
334
+ end
335
+
336
+ let(:doc) { Hash.from_bson(buf) }
337
+
338
+ it 'overwrites first value with second value' do
339
+ expect(doc).to eq({ 'foo' => 'overwrite' })
340
+ end
159
341
  end
342
+
343
+ context 'when bson document has string and symbol keys of the same name' do
344
+ let(:buf) do
345
+ buf = BSON::ByteBuffer.new
346
+ buf.put_int32(31)
347
+ buf.put_byte("\x02")
348
+ buf.put_cstring('foo')
349
+ buf.put_string('bar')
350
+ buf.put_byte("\x0e")
351
+ buf.put_cstring('foo')
352
+ buf.put_string('bar')
353
+ buf.put_byte("\x00")
354
+
355
+ BSON::ByteBuffer.new(buf.to_s)
356
+ end
357
+
358
+ let(:doc) { Hash.from_bson(buf) }
359
+
360
+ it 'overwrites first value with second value' do
361
+ expect(doc).to eq({ 'foo' => :bar })
362
+ end
363
+ end
364
+
365
+ context 'when deserializing a hash with a BigDecimal' do
366
+ let(:to_bson) do
367
+ {x: BigDecimal('1')}.to_bson
368
+ end
369
+
370
+ context 'when it has mode: :bson' do
371
+
372
+ let(:from_bson) do
373
+ Hash.from_bson(to_bson, mode: :bson)
374
+ end
375
+
376
+ it 'deserializes as a BigDecimal' do
377
+ expect(from_bson).to eq({"x" => BSON::Decimal128.new('1')})
378
+ end
379
+ end
380
+
381
+ context 'when it doesn\'t have mode: :bson' do
382
+
383
+ let(:from_bson) do
384
+ Hash.from_bson(to_bson)
385
+ end
386
+
387
+ it 'deserializes as a BigDecimal' do
388
+ expect(from_bson).to eq({"x" => BigDecimal(1)})
389
+ end
390
+ end
391
+ end
392
+ end
393
+
394
+ describe '#as_extended_json' do
395
+ let(:object) do
396
+ { 'foo' => :bar, 'baz' => ['qux', 1, 2.0, { 'lorem' => 1 }] }
397
+ end
398
+
399
+ let(:expected) do
400
+ { "foo" => { "$symbol" => "bar" },
401
+ "baz" => [ "qux",
402
+ { "$numberInt" => "1" },
403
+ { "$numberDouble" => "2.0" },
404
+ { "lorem" => { "$numberInt" => "1" } } ] }
405
+ end
406
+
407
+ it "returns the binary data plus type" do
408
+ expect(object.as_extended_json).to eq(expected)
409
+ end
410
+
411
+ it_behaves_like 'an Extended JSON serializable object'
160
412
  end
161
413
  end