bson 4.13.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+ require 'json'
6
+
7
+ # These tests are copied from driver when the driver implemented Mongo:DBRef
8
+ # class, and are intended to verify that the current DBRef implementation is
9
+ # compatible with the legacy driver DBRef interface.
10
+
11
+ describe BSON::DBRef do
12
+
13
+ let(:object_id) do
14
+ BSON::ObjectId.new
15
+ end
16
+
17
+ describe '#as_json' do
18
+
19
+ context 'when the database is not provided' do
20
+
21
+ let(:dbref) do
22
+ described_class.new('users', object_id)
23
+ end
24
+
25
+ it 'returns the json document without database' do
26
+ expect(dbref.as_json).to eq({ '$ref' => 'users', '$id' => object_id })
27
+ end
28
+ end
29
+
30
+ context 'when the database is provided' do
31
+
32
+ let(:dbref) do
33
+ described_class.new('users', object_id, 'database')
34
+ end
35
+
36
+ it 'returns the json document with database' do
37
+ expect(dbref.as_json).to eq({
38
+ '$ref' => 'users',
39
+ '$id' => object_id,
40
+ '$db' => 'database'
41
+ })
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#initialize' do
47
+
48
+ let(:dbref) do
49
+ described_class.new('users', object_id)
50
+ end
51
+
52
+ it 'sets the collection' do
53
+ expect(dbref.collection).to eq('users')
54
+ end
55
+
56
+ it 'sets the id' do
57
+ expect(dbref.id).to eq(object_id)
58
+ end
59
+
60
+ context 'when a database is provided' do
61
+
62
+ let(:dbref) do
63
+ described_class.new('users', object_id, 'db')
64
+ end
65
+
66
+ it 'sets the database' do
67
+ expect(dbref.database).to eq('db')
68
+ end
69
+
70
+ context 'when id is not provided' do
71
+
72
+ let(:dbref) do
73
+ described_class.new('users', nil, 'db')
74
+ end
75
+
76
+ it 'raises ArgumentError' do
77
+ lambda do
78
+ dbref
79
+ end.should raise_error(ArgumentError)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '#to_bson' do
86
+
87
+ let(:dbref) do
88
+ described_class.new('users', object_id, 'database')
89
+ end
90
+
91
+ it 'converts the underlying document to bson' do
92
+ expect(dbref.to_bson.to_s).to eq(dbref.as_json.to_bson.to_s)
93
+ end
94
+ end
95
+
96
+ describe '#to_json' do
97
+
98
+ context 'when the database is not provided' do
99
+
100
+ let(:dbref) do
101
+ described_class.new('users', object_id)
102
+ end
103
+
104
+ it 'returns the json document without database' do
105
+ expect(dbref.to_json).to eq("{\"$ref\":\"users\",\"$id\":#{object_id.to_json}}")
106
+ end
107
+ end
108
+
109
+ context 'when the database is provided' do
110
+
111
+ let(:dbref) do
112
+ described_class.new('users', object_id, 'database')
113
+ end
114
+
115
+ it 'returns the json document with database' do
116
+ expect(dbref.to_json).to eq("{\"$ref\":\"users\",\"$id\":#{object_id.to_json},\"$db\":\"database\"}")
117
+ end
118
+ end
119
+ end
120
+
121
+ describe '#from_bson' do
122
+
123
+ let(:buffer) do
124
+ dbref.to_bson
125
+ end
126
+
127
+ let(:decoded) do
128
+ BSON::Document.from_bson(BSON::ByteBuffer.new(buffer.to_s))
129
+ end
130
+
131
+ context 'when a database exists' do
132
+
133
+ let(:dbref) do
134
+ described_class.new('users', object_id, 'database')
135
+ end
136
+
137
+ it 'decodes the ref' do
138
+ expect(decoded.collection).to eq('users')
139
+ end
140
+
141
+ it 'decodes the id' do
142
+ expect(decoded.id).to eq(object_id)
143
+ end
144
+
145
+ it 'decodes the database' do
146
+ expect(decoded.database).to eq('database')
147
+ end
148
+ end
149
+
150
+ context 'when no database exists' do
151
+
152
+ let(:dbref) do
153
+ described_class.new('users', object_id)
154
+ end
155
+
156
+ it 'decodes the ref' do
157
+ expect(decoded.collection).to eq('users')
158
+ end
159
+
160
+ it 'decodes the id' do
161
+ expect(decoded.id).to eq(object_id)
162
+ end
163
+
164
+ it 'sets the database to nil' do
165
+ expect(decoded.database).to be_nil
166
+ end
167
+ end
168
+ end
169
+ end
@@ -73,6 +73,32 @@ describe BSON::DBRef do
73
73
  expect(dbref.id).to eq(object_id)
74
74
  end
75
75
 
76
+ context 'when first argument is a hash and two arguments are provided' do
77
+
78
+ let(:dbref) do
79
+ described_class.new({:$ref => 'users', :$id => object_id}, object_id)
80
+ end
81
+
82
+ it 'raises ArgumentError' do
83
+ lambda do
84
+ dbref
85
+ end.should raise_error(ArgumentError)
86
+ end
87
+ end
88
+
89
+ context 'when first argument is a hash and three arguments are provided' do
90
+
91
+ let(:dbref) do
92
+ described_class.new({:$ref => 'users', :$id => object_id}, object_id, 'db')
93
+ end
94
+
95
+ it 'raises ArgumentError' do
96
+ lambda do
97
+ dbref
98
+ end.should raise_error(ArgumentError)
99
+ end
100
+ end
101
+
76
102
  context 'when a database is provided' do
77
103
 
78
104
  let(:hash) do
@@ -309,6 +309,22 @@ describe BSON::Decimal128 do
309
309
  it_behaves_like 'an initialized BSON::Decimal128'
310
310
  end
311
311
  end
312
+
313
+ context 'when range is exceeded' do
314
+ it 'raises InvalidRange' do
315
+ lambda do
316
+ described_class.new('1e10000')
317
+ end.should raise_error(BSON::Decimal128::InvalidRange, /Value out of range/)
318
+ end
319
+ end
320
+
321
+ context 'when precision is exceeded' do
322
+ it 'raises UnrepresentablePrecision' do
323
+ lambda do
324
+ described_class.new('1.000000000000000000000000000000000000000000000000001')
325
+ end.should raise_error(BSON::Decimal128::UnrepresentablePrecision, /The value contains too much precision/)
326
+ end
327
+ end
312
328
  end
313
329
 
314
330
  context 'when deserializing' do
@@ -1586,4 +1602,219 @@ describe BSON::Decimal128 do
1586
1602
  expect(registered).to eq(described_class)
1587
1603
  end
1588
1604
  end
1605
+
1606
+ %w(== ===).each do |eq_op|
1607
+ let(:lhs) { described_class.new('1.2e12') }
1608
+
1609
+ describe "##{eq_op}" do
1610
+ context 'when rhs is equal to lhs' do
1611
+ context 'when both are Decimal128 instances' do
1612
+ let(:rhs) { described_class.new('1.2e12') }
1613
+
1614
+ it 'is true' do
1615
+ (lhs == rhs).should be true
1616
+ end
1617
+ end
1618
+
1619
+ context 'when rhs is of a different type' do
1620
+ [
1621
+ 1200000000000,
1622
+ 1200000000000.0,
1623
+ BigDecimal('1.2e12'),
1624
+ ].each do |rhs|
1625
+ context "when rhs is #{rhs.class}" do
1626
+ it 'is true' do
1627
+ pending 'RUBY-2952'
1628
+
1629
+ (lhs == rhs).should be true
1630
+ end
1631
+ end
1632
+ end
1633
+ end
1634
+ end
1635
+
1636
+ context 'when rhs is not equal to lhs' do
1637
+ context 'when both are Decimal128 instances' do
1638
+ let(:rhs) { described_class.new('1.21e12') }
1639
+
1640
+ it 'is false' do
1641
+ (lhs == rhs).should be false
1642
+ end
1643
+ end
1644
+
1645
+ context 'when rhs is of a different type' do
1646
+
1647
+ [
1648
+ 1200000000001,
1649
+ 1200000000001.0,
1650
+ BigDecimal('1.21e12'),
1651
+ ].each do |rhs|
1652
+ context "when rhs is #{rhs.class}" do
1653
+ it 'is false' do
1654
+ (lhs == rhs).should be false
1655
+ end
1656
+ end
1657
+ end
1658
+ end
1659
+ end
1660
+ end
1661
+ end
1662
+
1663
+ describe "#<=>" do
1664
+
1665
+ let(:lhs) { described_class.new('1.2e12') }
1666
+
1667
+ context 'when lhs is less than rhs' do
1668
+ context 'when both are Decimal128 instances' do
1669
+ let(:rhs) { described_class.new('1.21e12') }
1670
+
1671
+ it 'is -1' do
1672
+ (lhs <=> rhs).should be -1
1673
+ end
1674
+ end
1675
+
1676
+ context 'when rhs is of a different type' do
1677
+ [
1678
+ 1200000000001,
1679
+ 1200000000001.0,
1680
+ BigDecimal('1.21e12'),
1681
+ ].each do |rhs|
1682
+ context "when rhs is #{rhs.class}" do
1683
+ it 'is -1' do
1684
+ (lhs <=> rhs).should be -1
1685
+ end
1686
+ end
1687
+ end
1688
+ end
1689
+ end
1690
+
1691
+ context 'when rhs is equal to lhs' do
1692
+ context 'when both are Decimal128 instances' do
1693
+ let(:rhs) { described_class.new('1.2e12') }
1694
+
1695
+ it 'is 0' do
1696
+ (lhs <=> rhs).should be 0
1697
+ end
1698
+ end
1699
+
1700
+ context 'when rhs is of a different type' do
1701
+
1702
+ [
1703
+ 1200000000000,
1704
+ 1200000000000.0,
1705
+ BigDecimal('1.2e12'),
1706
+ ].each do |rhs|
1707
+ context "when rhs is #{rhs.class}" do
1708
+ it 'is 0' do
1709
+ (lhs <=> rhs).should be 0
1710
+ end
1711
+ end
1712
+ end
1713
+ end
1714
+ end
1715
+
1716
+ context 'when rhs is greater than lhs' do
1717
+ context 'when both are Decimal128 instances' do
1718
+ let(:rhs) { described_class.new('1.1e12') }
1719
+
1720
+ it 'is 1' do
1721
+ (lhs <=> rhs).should be 1
1722
+ end
1723
+ end
1724
+
1725
+ context 'when rhs is of a different type' do
1726
+
1727
+ [
1728
+ 1100000000000,
1729
+ 1100000000000.0,
1730
+ BigDecimal('1.1e12'),
1731
+ ].each do |rhs|
1732
+ context "when rhs is #{rhs.class}" do
1733
+ it 'is 1' do
1734
+ (lhs <=> rhs).should be 1
1735
+ end
1736
+ end
1737
+ end
1738
+ end
1739
+ end
1740
+ end
1741
+
1742
+ describe "#<" do
1743
+
1744
+ let(:lhs) { described_class.new('1.2e12') }
1745
+
1746
+ context 'when lhs is less than rhs' do
1747
+ context 'when both are Decimal128 instances' do
1748
+ let(:rhs) { described_class.new('1.21e12') }
1749
+
1750
+ it 'is true' do
1751
+ (lhs < rhs).should be true
1752
+ end
1753
+ end
1754
+
1755
+ context 'when rhs is of a different type' do
1756
+ [
1757
+ 1200000000001,
1758
+ 1200000000001.0,
1759
+ BigDecimal('1.21e12'),
1760
+ ].each do |rhs|
1761
+ context "when rhs is #{rhs.class}" do
1762
+ it 'is true' do
1763
+ (lhs < rhs).should be true
1764
+ end
1765
+ end
1766
+ end
1767
+ end
1768
+ end
1769
+
1770
+ context 'when rhs is equal to lhs' do
1771
+ context 'when both are Decimal128 instances' do
1772
+ let(:rhs) { described_class.new('1.2e12') }
1773
+
1774
+ it 'is false' do
1775
+ (lhs < rhs).should be false
1776
+ end
1777
+ end
1778
+
1779
+ context 'when rhs is of a different type' do
1780
+
1781
+ [
1782
+ 1200000000000,
1783
+ 1200000000000.0,
1784
+ BigDecimal('1.2e12'),
1785
+ ].each do |rhs|
1786
+ context "when rhs is #{rhs.class}" do
1787
+ it 'is false' do
1788
+ (lhs < rhs).should be false
1789
+ end
1790
+ end
1791
+ end
1792
+ end
1793
+ end
1794
+
1795
+ context 'when rhs is greater than lhs' do
1796
+ context 'when both are Decimal128 instances' do
1797
+ let(:rhs) { described_class.new('1.1e12') }
1798
+
1799
+ it 'is false' do
1800
+ (lhs < rhs).should be false
1801
+ end
1802
+ end
1803
+
1804
+ context 'when rhs is of a different type' do
1805
+
1806
+ [
1807
+ 1100000000000,
1808
+ 1100000000000.0,
1809
+ BigDecimal('1.1e12'),
1810
+ ].each do |rhs|
1811
+ context "when rhs is #{rhs.class}" do
1812
+ it 'is false' do
1813
+ (lhs < rhs).should be false
1814
+ end
1815
+ end
1816
+ end
1817
+ end
1818
+ end
1819
+ end
1589
1820
  end
@@ -254,6 +254,26 @@ describe Hash do
254
254
  expect(Hash.from_bson(buffer)).to eq('foo' => 42)
255
255
  end
256
256
  end
257
+
258
+ context 'when round-tripping a BigDecimal' do
259
+ let(:to_bson) do
260
+ {"x" => BigDecimal('1')}.to_bson
261
+ end
262
+
263
+ let(:from_bson) do
264
+ Hash.from_bson(to_bson)
265
+ end
266
+
267
+ it 'doesn\'t raise on serialization' do
268
+ expect do
269
+ to_bson
270
+ end.to_not raise_error
271
+ end
272
+
273
+ it 'deserializes as a BSON::Decimal128' do
274
+ expect(from_bson).to eq({"x" => BSON::Decimal128.new('1')})
275
+ end
276
+ end
257
277
  end
258
278
 
259
279
  describe '#to_bson' do
@@ -319,6 +339,18 @@ describe Hash do
319
339
  end.not_to raise_error
320
340
  end
321
341
  end
342
+
343
+ context 'when serializing a hash with a BigDecimal' do
344
+ let(:hash) do
345
+ {'foo' => BigDecimal('1')}
346
+ end
347
+
348
+ it 'works' do
349
+ expect do
350
+ hash.to_bson
351
+ end.not_to raise_error
352
+ end
353
+ end
322
354
  end
323
355
 
324
356
  describe '#from_bson' do
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-copy options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for new files") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--from=BUCKET:PATH", "Bucket name and key (or path) to copy from") do |v|
21
+ options[:from] = v
22
+ end
23
+
24
+ opts.on("-t", "--to=BUCKET:PATH", "Bucket name and key (or path) to write to (may be specified more than once)") do |v|
25
+ options[:to] ||= []
26
+ options[:to] << v
27
+ end
28
+ end.parse!
29
+
30
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
31
+
32
+ bucket, key = options.fetch(:from).split(':', 2)
33
+
34
+ s3 = Aws::S3::Client.new
35
+
36
+ options.fetch(:to).each do |dest|
37
+ STDERR.puts "Copying to #{dest}"
38
+ dbucket, dkey = dest.split(':', 2)
39
+ s3.copy_object(
40
+ bucket: dbucket,
41
+ key: dkey,
42
+ copy_source: "/#{bucket}/#{key}",
43
+ **options[:params] || {},
44
+ )
45
+ end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-upload options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for S3 upload") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--file=PATH", "Path to the file to upload, - to upload standard input") do |v|
21
+ options[:file] = v
22
+ end
23
+
24
+ opts.on("-w", "--write=BUCKET:PATH", "Bucket name and key (or path) to upload to") do |v|
25
+ options[:write] = v
26
+ end
27
+
28
+ opts.on("-c", "--copy=BUCKET:PATH", "Bucket name and key (or path) to copy to (may be specified more than once)") do |v|
29
+ options[:copy] ||= []
30
+ options[:copy] << v
31
+ end
32
+ end.parse!
33
+
34
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
35
+
36
+ def upload(f, options)
37
+ s3 = Aws::S3::Client.new
38
+ write = options.fetch(:write)
39
+ STDERR.puts "Writing #{write}"
40
+ bucket, key = write.split(':', 2)
41
+ s3.put_object(
42
+ body: f.read,
43
+ bucket: bucket,
44
+ key: key,
45
+ **options[:params] || {},
46
+ )
47
+ if copy = options[:copy]
48
+ copy.each do |dest|
49
+ STDERR.puts "Copying to #{dest}"
50
+ dbucket, dkey = dest.split(':', 2)
51
+ s3.copy_object(
52
+ bucket: dbucket,
53
+ key: dkey,
54
+ copy_source: "/#{bucket}/#{key}",
55
+ **options[:params] || {},
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ if options[:file] == '-'
62
+ upload(STDIN, options)
63
+ elsif options[:file]
64
+ File.open(options[:file]) do |f|
65
+ upload(f, options)
66
+ end
67
+ else
68
+ upload(STDIN, options)
69
+ end
@@ -15,6 +15,11 @@ module Mrss
15
15
  @single_server
16
16
  end
17
17
 
18
+ def sharded_ish?
19
+ determine_cluster_config
20
+ @topology == :sharded || @topology == :load_balanced
21
+ end
22
+
18
23
  def replica_set_name
19
24
  determine_cluster_config
20
25
  @replica_set_name
@@ -48,7 +53,7 @@ module Mrss
48
53
  raise "Deployment server version not known - check that connection to deployment succeeded"
49
54
  end
50
55
 
51
- if server_version >= '3.4' && topology != :sharded
56
+ if server_version >= '3.4' && !sharded_ish?
52
57
  fcv
53
58
  else
54
59
  if short_server_version == '4.1'
@@ -115,7 +120,7 @@ module Mrss
115
120
  :mmapv1
116
121
  else
117
122
  client = ClientRegistry.instance.global_client('root_authorized')
118
- if topology == :sharded
123
+ if sharded_ish?
119
124
  shards = client.use(:admin).command(listShards: 1).first
120
125
  if shards['shards'].empty?
121
126
  raise 'Shards are empty'
@@ -204,9 +209,14 @@ module Mrss
204
209
  @server_version = build_info['version']
205
210
  @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
206
211
 
207
- @server_parameters = client.use(:admin).command(getParameter: '*').first
212
+ @server_parameters = begin
213
+ client.use(:admin).command(getParameter: '*').first
214
+ rescue => e
215
+ STDERR.puts("WARNING: Failed to obtain server parameters: #{e.class}: #{e.message}")
216
+ {}
217
+ end
208
218
 
209
- if @topology != :sharded && short_server_version >= '3.4'
219
+ if !sharded_ish? && short_server_version >= '3.4'
210
220
  rv = @server_parameters['featureCompatibilityVersion']
211
221
  @fcv = rv['version'] || rv
212
222
  end