mongo 1.11.1 → 1.12.0.rc0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/VERSION +1 -1
  5. data/lib/mongo/collection.rb +8 -3
  6. data/lib/mongo/collection_writer.rb +1 -1
  7. data/lib/mongo/connection/socket/unix_socket.rb +1 -1
  8. data/lib/mongo/cursor.rb +8 -1
  9. data/lib/mongo/db.rb +61 -33
  10. data/lib/mongo/exception.rb +57 -0
  11. data/lib/mongo/functional.rb +1 -0
  12. data/lib/mongo/functional/authentication.rb +138 -11
  13. data/lib/mongo/functional/read_preference.rb +31 -22
  14. data/lib/mongo/functional/scram.rb +555 -0
  15. data/lib/mongo/functional/uri_parser.rb +107 -79
  16. data/lib/mongo/mongo_client.rb +19 -24
  17. data/lib/mongo/mongo_replica_set_client.rb +2 -1
  18. data/test/functional/authentication_test.rb +3 -0
  19. data/test/functional/client_test.rb +33 -0
  20. data/test/functional/collection_test.rb +29 -19
  21. data/test/functional/db_api_test.rb +16 -1
  22. data/test/functional/pool_test.rb +7 -6
  23. data/test/functional/uri_test.rb +111 -7
  24. data/test/helpers/test_unit.rb +17 -3
  25. data/test/replica_set/client_test.rb +31 -0
  26. data/test/replica_set/insert_test.rb +49 -32
  27. data/test/replica_set/pinning_test.rb +50 -0
  28. data/test/replica_set/query_test.rb +1 -1
  29. data/test/replica_set/replication_ack_test.rb +3 -3
  30. data/test/shared/authentication/basic_auth_shared.rb +14 -1
  31. data/test/shared/authentication/gssapi_shared.rb +13 -8
  32. data/test/shared/authentication/scram_shared.rb +92 -0
  33. data/test/tools/mongo_config.rb +18 -6
  34. data/test/unit/client_test.rb +40 -6
  35. data/test/unit/connection_test.rb +15 -5
  36. data/test/unit/db_test.rb +1 -1
  37. data/test/unit/read_pref_test.rb +291 -0
  38. metadata +9 -6
  39. metadata.gz.sig +0 -0
@@ -22,7 +22,8 @@ module Mongo
22
22
 
23
23
  HOST_REGEX = /([-.\w]+)|(\[[^\]]+\])/
24
24
  PORT_REGEX = /(?::(\w+))?/
25
- NODE_REGEX = /((#{HOST_REGEX}#{PORT_REGEX},?)+)/
25
+ UNIX_SOCK_REGEX = /([\S]+.sock)/
26
+ NODE_REGEX = /((#{HOST_REGEX}#{PORT_REGEX},?)+|#{UNIX_SOCK_REGEX}{1})/
26
27
 
27
28
  PATH_REGEX = /(?:\/([-\w]+))?/
28
29
 
@@ -41,15 +42,15 @@ module Mongo
41
42
 
42
43
  OPT_ATTRS = [
43
44
  :authmechanism,
45
+ :authmechanismproperties,
44
46
  :authsource,
45
- :canonicalizehostname,
46
47
  :connect,
47
48
  :connecttimeoutms,
48
49
  :fsync,
49
- :gssapiservicename,
50
50
  :journal,
51
51
  :pool_size,
52
52
  :readpreference,
53
+ :readpreferencetags,
53
54
  :replicaset,
54
55
  :safe,
55
56
  :slaveok,
@@ -61,89 +62,96 @@ module Mongo
61
62
  ]
62
63
 
63
64
  OPT_VALID = {
64
- :authmechanism => lambda { |arg| Mongo::Authentication.validate_mechanism(arg) },
65
- :authsource => lambda { |arg| arg.length > 0 },
66
- :canonicalizehostname => lambda { |arg| ['true', 'false'].include?(arg) },
67
- :connect => lambda { |arg| [ 'direct', 'replicaset', 'true', 'false', true, false ].include?(arg) },
68
- :connecttimeoutms => lambda { |arg| arg =~ /^\d+$/ },
69
- :fsync => lambda { |arg| ['true', 'false'].include?(arg) },
70
- :gssapiservicename => lambda { |arg| arg.length > 0 },
71
- :journal => lambda { |arg| ['true', 'false'].include?(arg) },
72
- :pool_size => lambda { |arg| arg.to_i > 0 },
73
- :readpreference => lambda { |arg| READ_PREFERENCES.keys.include?(arg) },
74
- :replicaset => lambda { |arg| arg.length > 0 },
75
- :safe => lambda { |arg| ['true', 'false'].include?(arg) },
76
- :slaveok => lambda { |arg| ['true', 'false'].include?(arg) },
77
- :sockettimeoutms => lambda { |arg| arg =~ /^\d+$/ },
78
- :ssl => lambda { |arg| ['true', 'false'].include?(arg) },
79
- :w => lambda { |arg| arg =~ /^\w+$/ },
80
- :wtimeout => lambda { |arg| arg =~ /^\d+$/ },
81
- :wtimeoutms => lambda { |arg| arg =~ /^\d+$/ }
65
+ :authmechanism => lambda { |arg| Mongo::Authentication.validate_mechanism(arg) },
66
+ :authmechanismproperties => lambda { |arg| arg.length > 0 },
67
+ :authsource => lambda { |arg| arg.length > 0 },
68
+ :connect => lambda { |arg| [ 'direct', 'replicaset', 'true', 'false', true, false ].include?(arg) },
69
+ :connecttimeoutms => lambda { |arg| arg =~ /^\d+$/ },
70
+ :fsync => lambda { |arg| ['true', 'false'].include?(arg) },
71
+ :journal => lambda { |arg| ['true', 'false'].include?(arg) },
72
+ :pool_size => lambda { |arg| arg.to_i > 0 },
73
+ :readpreference => lambda { |arg| READ_PREFERENCES.keys.include?(arg) },
74
+ :readpreferencetags => lambda { |arg| arg.none? { |tags| tags.scan(/(\w+:\w+),?/).empty? } },
75
+ :replicaset => lambda { |arg| arg.length > 0 },
76
+ :safe => lambda { |arg| ['true', 'false'].include?(arg) },
77
+ :slaveok => lambda { |arg| ['true', 'false'].include?(arg) },
78
+ :sockettimeoutms => lambda { |arg| arg =~ /^\d+$/ },
79
+ :ssl => lambda { |arg| ['true', 'false'].include?(arg) },
80
+ :w => lambda { |arg| arg =~ /^\w+$/ },
81
+ :wtimeout => lambda { |arg| arg =~ /^\d+$/ },
82
+ :wtimeoutms => lambda { |arg| arg =~ /^\d+$/ }
82
83
  }
83
84
 
84
85
  OPT_ERR = {
85
- :authmechanism => "must be one of #{Mongo::Authentication::MECHANISMS.join(', ')}",
86
- :authsource => "must be a string containing the name of the database being used for authentication",
87
- :canonicalizehostname => "must be 'true' or 'false'",
88
- :connect => "must be 'direct', 'replicaset', 'true', or 'false'",
89
- :connecttimeoutms => "must be an integer specifying milliseconds",
90
- :fsync => "must be 'true' or 'false'",
91
- :gssapiservicename => "must be a string containing the name of the GSSAPI service",
92
- :journal => "must be 'true' or 'false'",
93
- :pool_size => "must be an integer greater than zero",
94
- :readpreference => "must be on of #{READ_PREFERENCES.keys.map(&:inspect).join(",")}",
95
- :replicaset => "must be a string containing the name of the replica set to connect to",
96
- :safe => "must be 'true' or 'false'",
97
- :slaveok => "must be 'true' or 'false'",
98
- :settimeoutms => "must be an integer specifying milliseconds",
99
- :ssl => "must be 'true' or 'false'",
100
- :w => "must be an integer indicating number of nodes to replicate to or a string " +
101
- "specifying that replication is required to the majority or nodes with a " +
102
- "particilar getLastErrorMode.",
103
- :wtimeout => "must be an integer specifying milliseconds",
104
- :wtimeoutms => "must be an integer specifying milliseconds"
86
+ :authmechanism => Mongo::Authentication::MECHANISM_ERROR,
87
+ :authmechanismproperties => "must meet the format requirements of the authentication mechanism's properties",
88
+ :authsource => "must be a string containing the name of the database being used for authentication",
89
+ :connect => "must be 'direct', 'replicaset', 'true', or 'false'",
90
+ :connecttimeoutms => "must be an integer specifying milliseconds",
91
+ :fsync => "must be 'true' or 'false'",
92
+ :journal => "must be 'true' or 'false'",
93
+ :pool_size => "must be an integer greater than zero",
94
+ :readpreference => "must be one of #{READ_PREFERENCES.keys.map(&:inspect).join(",")}",
95
+ :readpreferencetags => "must be a comma-separated list of one or more key:value pairs",
96
+ :replicaset => "must be a string containing the name of the replica set to connect to",
97
+ :safe => "must be 'true' or 'false'",
98
+ :slaveok => "must be 'true' or 'false'",
99
+ :settimeoutms => "must be an integer specifying milliseconds",
100
+ :ssl => "must be 'true' or 'false'",
101
+ :w => "must be an integer indicating number of nodes to replicate to or a string " +
102
+ "specifying that replication is required to the majority or nodes with a " +
103
+ "particilar getLastErrorMode.",
104
+ :wtimeout => "must be an integer specifying milliseconds",
105
+ :wtimeoutms => "must be an integer specifying milliseconds"
105
106
  }
106
107
 
107
108
  OPT_CONV = {
108
- :authmechanism => lambda { |arg| arg.upcase },
109
- :authsource => lambda { |arg| arg },
110
- :canonicalizehostname => lambda { |arg| arg == 'true' ? true : false },
111
- :connect => lambda { |arg| arg == 'false' ? false : arg }, # convert 'false' to FalseClass
112
- :connecttimeoutms => lambda { |arg| arg.to_f / 1000 }, # stored as seconds
113
- :fsync => lambda { |arg| arg == 'true' ? true : false },
114
- :gssapiservicename => lambda { |arg| arg },
115
- :journal => lambda { |arg| arg == 'true' ? true : false },
116
- :pool_size => lambda { |arg| arg.to_i },
117
- :readpreference => lambda { |arg| READ_PREFERENCES[arg] },
118
- :replicaset => lambda { |arg| arg },
119
- :safe => lambda { |arg| arg == 'true' ? true : false },
120
- :slaveok => lambda { |arg| arg == 'true' ? true : false },
121
- :sockettimeoutms => lambda { |arg| arg.to_f / 1000 }, # stored as seconds
122
- :ssl => lambda { |arg| arg == 'true' ? true : false },
123
- :w => lambda { |arg| Mongo::Support.is_i?(arg) ? arg.to_i : arg.to_sym },
124
- :wtimeout => lambda { |arg| arg.to_i },
125
- :wtimeoutms => lambda { |arg| arg.to_i }
109
+ :authmechanism => lambda { |arg| arg.upcase },
110
+ :authmechanismproperties => lambda { |arg| arg },
111
+ :authsource => lambda { |arg| arg },
112
+ :connect => lambda { |arg| arg == 'false' ? false : arg }, # convert 'false' to FalseClass
113
+ :connecttimeoutms => lambda { |arg| arg.to_f / 1000 }, # stored as seconds
114
+ :fsync => lambda { |arg| arg == 'true' ? true : false },
115
+ :journal => lambda { |arg| arg == 'true' ? true : false },
116
+ :pool_size => lambda { |arg| arg.to_i },
117
+ :readpreference => lambda { |arg| READ_PREFERENCES[arg] },
118
+ :readpreferencetags => lambda { |arg| arg.map do |tags|
119
+ tags.scan(/(\w+:\w+),?/).reduce({}) do |tags, pair|
120
+ key, value = pair.first.split(":")
121
+ tags.merge!(key => value)
122
+ end
123
+ end },
124
+ :replicaset => lambda { |arg| arg },
125
+ :safe => lambda { |arg| arg == 'true' ? true : false },
126
+ :slaveok => lambda { |arg| arg == 'true' ? true : false },
127
+ :sockettimeoutms => lambda { |arg| arg.to_f / 1000 }, # stored as seconds
128
+ :ssl => lambda { |arg| arg == 'true' ? true : false },
129
+ :w => lambda { |arg| Mongo::Support.is_i?(arg) ? arg.to_i : arg.to_sym },
130
+ :wtimeout => lambda { |arg| arg.to_i },
131
+ :wtimeoutms => lambda { |arg| arg.to_i }
126
132
  }
127
133
 
128
134
  OPT_CASE_SENSITIVE = [ :authsource,
129
- :gssapiservicename,
135
+ :authmechanismproperties,
130
136
  :replicaset,
131
137
  :w
132
138
  ]
133
139
 
140
+ OPT_NOT_STRING = [ :readpreferencetags ]
141
+
134
142
  attr_reader :auths,
135
143
  :authmechanism,
144
+ :authmechanismproperties,
136
145
  :authsource,
137
- :canonicalizehostname,
138
146
  :connect,
139
147
  :connecttimeoutms,
140
148
  :db_name,
141
149
  :fsync,
142
- :gssapiservicename,
143
150
  :journal,
144
151
  :nodes,
145
152
  :pool_size,
146
153
  :readpreference,
154
+ :readpreferencetags,
147
155
  :replicaset,
148
156
  :safe,
149
157
  :slaveok,
@@ -249,6 +257,7 @@ module Mongo
249
257
  opts[:op_timeout] = @sockettimeoutms if @sockettimeoutms
250
258
  opts[:pool_size] = @pool_size if @pool_size
251
259
  opts[:read] = @readpreference if @readpreference
260
+ opts[:tag_sets] = @readpreferencetags if @readpreferencetags
252
261
 
253
262
  if @slaveok && !@readpreference
254
263
  unless replicaset?
@@ -287,20 +296,24 @@ module Mongo
287
296
 
288
297
  user_info = matches[2].split(':') if matches[2]
289
298
  host_info = matches[3].split(',')
290
- @db_name = matches[8]
299
+ @db_name = matches[9]
291
300
 
292
- host_info.each do |host|
293
- if host[0,1] == '['
294
- host, port = host.split(']:') << MongoClient::DEFAULT_PORT
295
- host = host.end_with?(']') ? host[1...-1] : host[1..-1]
296
- else
297
- host, port = host.split(':') << MongoClient::DEFAULT_PORT
298
- end
299
- unless port.to_s =~ /^\d+$/
300
- raise MongoArgumentError,
301
- "Invalid port #{port}; port must be specified as digits."
301
+ if host_info.first.end_with?('.sock')
302
+ @nodes << [ host_info.first ]
303
+ else
304
+ host_info.each do |host|
305
+ if host[0,1] == '['
306
+ host, port = host.split(']:') << MongoClient::DEFAULT_PORT
307
+ host = host.end_with?(']') ? host[1...-1] : host[1..-1]
308
+ else
309
+ host, port = host.split(':') << MongoClient::DEFAULT_PORT
310
+ end
311
+ unless port.to_s =~ /^\d+$/
312
+ raise MongoArgumentError,
313
+ "Invalid port #{port}; port must be specified as digits."
314
+ end
315
+ @nodes << [host, port.to_i]
302
316
  end
303
- @nodes << [host, port.to_i]
304
317
  end
305
318
 
306
319
  if @nodes.empty?
@@ -330,10 +343,10 @@ module Mongo
330
343
  :username => URI.unescape(username),
331
344
  :password => password ? URI.unescape(password) : nil,
332
345
  :source => @authsource,
333
- :mechanism => @authmechanism
346
+ :mechanism => @authmechanism,
347
+ :extra => @authmechanismproperties || {}
334
348
  })
335
- auth[:extra] = @canonicalizehostname ? { :canonicalize_host_name => @canonicalizehostname } : {}
336
- auth[:extra].merge!(:gssapi_service_name => @gssapiservicename) if @gssapiservicename
349
+
337
350
  @auths << auth
338
351
  end
339
352
  end
@@ -353,9 +366,13 @@ module Mongo
353
366
  end
354
367
 
355
368
  opts = CGI.parse(string_opts).inject({}) do |memo, (key, value)|
356
- value = value.first
357
369
  key_sym = key.downcase.to_sym
358
- memo[key_sym] = OPT_CASE_SENSITIVE.include?(key_sym) ? value.strip : value.strip.downcase
370
+ if OPT_NOT_STRING.include?(key_sym)
371
+ memo[key_sym] = value
372
+ else
373
+ value = value.first
374
+ memo[key_sym] = OPT_CASE_SENSITIVE.include?(key_sym) ? value.strip : value.strip.downcase
375
+ end
359
376
  memo
360
377
  end
361
378
 
@@ -369,6 +386,17 @@ module Mongo
369
386
  raise MongoArgumentError, "Invalid value #{value.inspect} for #{key}: #{OPT_ERR[key]}"
370
387
  end
371
388
  end
389
+
390
+ if @authmechanismproperties
391
+ @authmechanismproperties = @authmechanismproperties.split(',').reduce({}) do |prop, pair|
392
+ key, value = pair.split(':')
393
+ if ["true", "false"].include?(value.downcase)
394
+ value = value.downcase == "true"
395
+ end
396
+ prop[key.downcase.to_sym] = value
397
+ prop
398
+ end
399
+ end
372
400
  end
373
401
 
374
402
  def validate_connect
@@ -25,7 +25,8 @@ module Mongo
25
25
  RELEASE_2_4_AND_BEFORE = 0 # Everything before we started tracking.
26
26
  AGG_RETURNS_CURSORS = 1 # The aggregation command may now be requested to return cursors.
27
27
  BATCH_COMMANDS = 2 # insert, update, and delete batch command
28
- MAX_WIRE_VERSION = BATCH_COMMANDS # supported by this client implementation
28
+ MONGODB_2_8 = 3 # listCollections and listIndexes commands, SCRAM-SHA-1 auth mechanism
29
+ MAX_WIRE_VERSION = MONGODB_2_8 # supported by this client implementation
29
30
  MIN_WIRE_VERSION = RELEASE_2_4_AND_BEFORE # supported by this client implementation
30
31
 
31
32
  # Server command headroom
@@ -41,6 +42,7 @@ module Mongo
41
42
  DEFAULT_HOST = 'localhost'
42
43
  DEFAULT_PORT = 27017
43
44
  DEFAULT_DB_NAME = 'test'
45
+ DEFAULT_OP_TIMEOUT = 20
44
46
  GENERIC_OPTS = [:auths, :logger, :connect, :db_name]
45
47
  TIMEOUT_OPTS = [:timeout, :op_timeout, :connect_timeout]
46
48
  SSL_OPTS = [:ssl, :ssl_key, :ssl_cert, :ssl_verify, :ssl_ca_cert, :ssl_key_pass_phrase]
@@ -125,8 +127,8 @@ module Mongo
125
127
  # @option opts [Float] :pool_timeout (5.0) When all of the self.connections a pool are checked out,
126
128
  # this is the number of seconds to wait for a new connection to be released before throwing an exception.
127
129
  # Note: this setting is relevant only for multi-threaded applications.
128
- # @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
129
- # Disabled by default.
130
+ # @option opts [Float] :op_timeout (DEFAULT_OP_TIMEOUT) The number of seconds to wait for a read operation to time out.
131
+ # Set to DEFAULT_OP_TIMEOUT (20) by default. A value of nil may be specified explicitly.
130
132
  # @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
131
133
  # connection attempt.
132
134
  #
@@ -332,26 +334,19 @@ module Mongo
332
334
  # @param password [String] password (applies to 'from' db)
333
335
  #
334
336
  # @note This command only supports the MONGODB-CR authentication mechanism.
335
- def copy_database(from, to, from_host=DEFAULT_HOST, username=nil, password=nil)
336
- oh = BSON::OrderedHash.new
337
- oh[:copydb] = 1
338
- oh[:fromhost] = from_host
339
- oh[:fromdb] = from
340
- oh[:todb] = to
341
- if username || password
342
- unless username && password
343
- raise MongoArgumentError,
344
- 'Both username and password must be supplied for authentication.'
345
- end
346
- nonce_cmd = BSON::OrderedHash.new
347
- nonce_cmd[:copydbgetnonce] = 1
348
- nonce_cmd[:fromhost] = from_host
349
- result = self['admin'].command(nonce_cmd)
350
- oh[:nonce] = result['nonce']
351
- oh[:username] = username
352
- oh[:key] = Mongo::Authentication.auth_key(username, password, oh[:nonce])
337
+ def copy_database(
338
+ from,
339
+ to,
340
+ from_host = DEFAULT_HOST,
341
+ username = nil,
342
+ password = nil,
343
+ mechanism = 'SCRAM-SHA-1'
344
+ )
345
+ if wire_version_feature?(MONGODB_2_8) && mechanism == 'SCRAM-SHA-1'
346
+ copy_db_scram(username, password, from_host, from, to)
347
+ else
348
+ copy_db_mongodb_cr(username, password, from_host, from, to)
353
349
  end
354
- self['admin'].command(oh)
355
350
  end
356
351
 
357
352
  # Checks if a server is alive. This command will return immediately
@@ -446,7 +441,7 @@ module Mongo
446
441
  ping
447
442
  true
448
443
 
449
- rescue ConnectionFailure
444
+ rescue ConnectionFailure, OperationTimeout
450
445
  false
451
446
  end
452
447
 
@@ -634,7 +629,7 @@ module Mongo
634
629
  @pool_timeout = opts.delete(:pool_timeout) || opts.delete(:timeout) || 5.0
635
630
 
636
631
  # Timeout on socket read operation.
637
- @op_timeout = opts.delete(:op_timeout)
632
+ @op_timeout = opts.key?(:op_timeout) ? opts.delete(:op_timeout) : DEFAULT_OP_TIMEOUT
638
633
 
639
634
  # Timeout on socket connect.
640
635
  @connect_timeout = opts.delete(:connect_timeout) || 30
@@ -85,7 +85,8 @@ module Mongo
85
85
  # @option opts [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
86
86
  # this is the number of seconds to wait for a new connection to be released before throwing an exception.
87
87
  # Note: this setting is relevant only for multi-threaded applications.
88
- # @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
88
+ # @option opts [Float] :op_timeout (DEFAULT_OP_TIMEOUT) The number of seconds to wait for a read operation to time out.
89
+ # Set to DEFAULT_OP_TIMEOUT (20) by default. A value of nil may be specified explicitly.
89
90
  # @option opts [Float] :connect_timeout (30) The number of seconds to wait before timing out a
90
91
  # connection attempt.
91
92
  # @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
@@ -17,6 +17,7 @@ require 'shared/authentication/basic_auth_shared'
17
17
  require 'shared/authentication/sasl_plain_shared'
18
18
  require 'shared/authentication/bulk_api_auth_shared'
19
19
  require 'shared/authentication/gssapi_shared'
20
+ require 'shared/authentication/scram_shared'
20
21
 
21
22
 
22
23
  class AuthenticationTest < Test::Unit::TestCase
@@ -25,9 +26,11 @@ class AuthenticationTest < Test::Unit::TestCase
25
26
  include SASLPlainTests
26
27
  include BulkAPIAuthTests
27
28
  include GSSAPITests
29
+ include SCRAMTests
28
30
 
29
31
  def setup
30
32
  @client = standard_connection
33
+ omit("auth not enabled on mongod") unless auth_enabled?(@client)
31
34
  @admin = @client['admin']
32
35
  @version = @client.server_version
33
36
  @db = @client['ruby-test']
@@ -74,6 +74,27 @@ class ClientTest < Test::Unit::TestCase
74
74
  end
75
75
  end
76
76
 
77
+ def test_unix_sock
78
+ # There's an issue with unix sockets on JRuby with 32-bit libc
79
+ # https://jira.codehaus.org/browse/JRUBY-7183
80
+ return if RUBY_PLATFORM =~ /java/
81
+ begin
82
+ assert MongoClient.new("/tmp/mongodb-#{TEST_PORT}.sock")
83
+ rescue Errno::EAFNOSUPPORT
84
+ # System doesn't support UNIX sockets
85
+ end
86
+ end
87
+
88
+ def test_initialize_with_auths
89
+ auth = { :username => TEST_USER,
90
+ :password => TEST_USER_PWD,
91
+ :db_name => TEST_DB,
92
+ :source => TEST_DB}
93
+
94
+ client = MongoClient.new(:auths => Set.new([auth]))
95
+ assert client['test']['test'].find.to_a
96
+ end
97
+
77
98
  def test_connection_uri
78
99
  con = MongoClient.from_uri("mongodb://#{host_port}")
79
100
  assert_equal mongo_host, con.primary_pool.host
@@ -430,6 +451,18 @@ class ClientTest < Test::Unit::TestCase
430
451
  assert !conn.active?
431
452
  end
432
453
 
454
+ def test_operation_timeout_with_active
455
+ conn = standard_connection
456
+ assert conn.active?
457
+ assert_equal Mongo::MongoClient::DEFAULT_OP_TIMEOUT, conn.op_timeout
458
+
459
+ pool = conn.primary_pool
460
+ socket = pool.instance_variable_get(:@thread_ids_to_sockets)[Thread.current.object_id]
461
+
462
+ socket.stubs(:read).raises(Mongo::OperationTimeout)
463
+ assert_equal false, conn.active?
464
+ end
465
+
433
466
  context "Saved authentications" do
434
467
  setup do
435
468
  @client = Mongo::MongoClient.new
@@ -18,6 +18,7 @@ require 'test_helper'
18
18
  class CollectionTest < Test::Unit::TestCase
19
19
 
20
20
  LIMITED_MAX_BSON_SIZE = 1024
21
+ LIMITED_BSON_SIZE_WITH_HEADROOM = LIMITED_MAX_BSON_SIZE + MongoClient::APPEND_HEADROOM
21
22
  LIMITED_MAX_MESSAGE_SIZE = 3 * LIMITED_MAX_BSON_SIZE
22
23
  LIMITED_TEST_HEADROOM = 50
23
24
  LIMITED_VALID_VALUE_SIZE = LIMITED_MAX_BSON_SIZE - LIMITED_TEST_HEADROOM
@@ -29,6 +30,7 @@ class CollectionTest < Test::Unit::TestCase
29
30
  @test = @db.collection("test")
30
31
  @version = @client.server_version
31
32
  @test.remove
33
+ @ismaster = @client['admin'].command(:isMaster => 1)
32
34
  end
33
35
 
34
36
  @@wv0 = Mongo::MongoClient::RELEASE_2_4_AND_BEFORE
@@ -669,21 +671,22 @@ class CollectionTest < Test::Unit::TestCase
669
671
 
670
672
  def limited_collection
671
673
  conn = standard_connection(:connect => false)
674
+ is_master = @ismaster.merge('maxBsonObjectSize' => LIMITED_MAX_BSON_SIZE,
675
+ 'maxMessageSizeBytes' => LIMITED_MAX_BSON_SIZE)
672
676
  admin_db = Object.new
673
- admin_db.expects(:command).returns({
674
- 'ok' => 1,
675
- 'ismaster' => 1,
676
- 'maxBsonObjectSize' => LIMITED_MAX_BSON_SIZE,
677
- 'maxMessageSizeBytes' => LIMITED_MAX_MESSAGE_SIZE
678
- })
679
- conn.expects(:[]).with('admin').returns(admin_db)
677
+ admin_db.expects(:command).returns(is_master)
678
+ conn.expects(:[]).with(TEST_DB).returns(admin_db)
680
679
  conn.connect
681
680
  return conn.db(TEST_DB)["test"]
682
681
  end
683
682
 
684
683
  def test_non_operation_failure_halts_insertion_with_continue_on_error
685
684
  coll = limited_collection
686
- coll.db.connection.stubs(:send_message_with_gle).raises(OperationTimeout).times(1)
685
+ if @client.wire_version_feature?(MongoClient::BATCH_COMMANDS)
686
+ coll.db.stubs(:command).raises(OperationTimeout).times(1)
687
+ else
688
+ coll.db.connection.stubs(:send_message_with_gle).raises(OperationTimeout).times(1)
689
+ end
687
690
  docs = []
688
691
  10.times do
689
692
  docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
@@ -738,6 +741,7 @@ class CollectionTest < Test::Unit::TestCase
738
741
  end
739
742
 
740
743
  def test_chunking_batch_insert_with_continue_on_error
744
+ coll = limited_collection
741
745
  docs = []
742
746
  4.times do
743
747
  docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
@@ -748,9 +752,9 @@ class CollectionTest < Test::Unit::TestCase
748
752
  docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
749
753
  end
750
754
  assert_raise OperationFailure do
751
- limited_collection.insert(docs, :continue_on_error => true)
755
+ coll.insert(docs, :continue_on_error => true)
752
756
  end
753
- assert limited_collection.count >= 6, "write commands need headroom for doc wrapping overhead - count:#{limited_collection.count}"
757
+ assert coll.count >= 6, "write commands need headroom for doc wrapping overhead - count:#{coll.count}"
754
758
  end
755
759
 
756
760
  def test_chunking_batch_insert_without_continue_on_error
@@ -779,49 +783,49 @@ class CollectionTest < Test::Unit::TestCase
779
783
 
780
784
  def test_maximum_document_size
781
785
  assert_raise InvalidDocument do
782
- limited_collection.insert({'foo' => 'a' * LIMITED_MAX_BSON_SIZE})
786
+ limited_collection.insert({'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM})
783
787
  end
784
788
  end
785
789
 
786
790
  def test_maximum_save_size
787
791
  assert limited_collection.save({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE})
788
792
  assert_raise InvalidDocument do
789
- limited_collection.save({'foo' => 'a' * LIMITED_MAX_BSON_SIZE})
793
+ limited_collection.save({'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM})
790
794
  end
791
795
  end
792
796
 
793
797
  def test_maximum_remove_size
794
798
  assert limited_collection.remove({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE})
795
799
  assert_raise InvalidDocument do
796
- limited_collection.remove({'foo' => 'a' * LIMITED_MAX_BSON_SIZE})
800
+ limited_collection.remove({'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM})
797
801
  end
798
802
  end
799
803
 
800
804
  def test_maximum_update_size
801
805
  assert_raise InvalidDocument do
802
806
  limited_collection.update(
803
- {'foo' => 'a' * LIMITED_MAX_BSON_SIZE},
807
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM},
804
808
  {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
805
809
  )
806
810
  end
807
811
 
808
812
  assert_raise InvalidDocument do
809
813
  limited_collection.update(
810
- {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
814
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM},
811
815
  {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}
812
816
  )
813
817
  end
814
818
 
815
819
  assert_raise InvalidDocument do
816
820
  limited_collection.update(
817
- {'foo' => 'a' * LIMITED_MAX_BSON_SIZE},
818
- {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}
821
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM},
822
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM}
819
823
  )
820
824
  end
821
825
 
822
826
  assert limited_collection.update(
823
- {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
824
- {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
827
+ {'foo' => 'a' * (LIMITED_VALID_VALUE_SIZE/2)},
828
+ {'foo' => 'a' * (LIMITED_VALID_VALUE_SIZE/2)}
825
829
  )
826
830
  end
827
831
 
@@ -1323,6 +1327,12 @@ class CollectionTest < Test::Unit::TestCase
1323
1327
  assert_equal desired_results, results
1324
1328
  end
1325
1329
 
1330
+ def test_aggregate_command_using_sym
1331
+ return unless @version > '2.1.1'
1332
+ cmd = BSON::OrderedHash[:aggregate, @test.name, :pipeline, [{'$match' => {:_id => true}}]]
1333
+ assert @db.command(cmd)
1334
+ end
1335
+
1326
1336
  def test_aggregate_pipeline_multiple_operators
1327
1337
  return unless @version > '2.1.1'
1328
1338
  setup_aggregate_data