ReplSetConnection updates

This commit is contained in:
Kyle Banker 2010-12-13 14:07:32 -05:00
parent 08b7cddc81
commit 27b410f869
11 changed files with 212 additions and 285 deletions

View File

@ -92,47 +92,18 @@ module Mongo
# #
# @core connections # @core connections
def initialize(host=nil, port=nil, options={}) def initialize(host=nil, port=nil, options={})
@auths = options.fetch(:auths, [])
@host_to_try = format_pair(host, port) @host_to_try = format_pair(host, port)
# Host and port of current master. # Host and port of current master.
@host = @port = nil @host = @port = nil
# Lock for request ids.
@id_lock = Mutex.new
# Pool size and timeout.
@pool_size = options[:pool_size] || 1
@timeout = options[:timeout] || 5.0
# Mutex for synchronizing pool access
@connection_mutex = Mutex.new
# Global safe option. This is false by default.
@safe = options[:safe] || false
# Create a mutex when a new key, in this case a socket,
# is added to the hash.
@safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
# Condition variable for signal and wait
@queue = ConditionVariable.new
# slave_ok can be true only if one node is specified # slave_ok can be true only if one node is specified
@slave_ok = options[:slave_ok] @slave_ok = options[:slave_ok]
@primary = nil setup(options)
# Connection pool for primay node
@primary_pool = nil
@logger = options[:logger] || nil
should_connect = options.fetch(:connect, true)
connect if should_connect
end end
# DEPRECATED # DEPRECATED
# #
# Initialize a connection to a MongoDB replica set using an array of seed nodes. # Initialize a connection to a MongoDB replica set using an array of seed nodes.
@ -160,19 +131,10 @@ module Mongo
# #
# @deprecated # @deprecated
def self.multi(nodes, opts={}) def self.multi(nodes, opts={})
unless nodes.length > 0 && nodes.all? {|n| n.is_a? Array} warn "Connection.multi is now deprecated. Please use ReplSetConnection.new instead."
raise MongoArgumentError, "Connection.multi requires at least one node to be specified."
end
# Block returns an array, the first element being an array of nodes and the second an array nodes << opts
# of authorizations for the database. ReplSetConnection.new(*nodes)
new(nil, nil, opts) do |con|
nodes.map do |node|
con.instance_variable_set(:@replica_set, true)
con.instance_variable_set(:@read_secondary, true) if opts[:read_secondary]
con.pair_val_to_connection(node)
end
end
end end
# Initialize a connection to MongoDB using the MongoDB URI spec: # Initialize a connection to MongoDB using the MongoDB URI spec:
@ -191,6 +153,8 @@ module Mongo
elsif nodes.length > 1 elsif nodes.length > 1
nodes << opts nodes << opts
ReplSetConnection.new(*nodes) ReplSetConnection.new(*nodes)
else
raise MongoArgumentError, "No nodes specified. Please ensure that you've provided at least one node."
end end
end end
@ -500,38 +464,6 @@ module Mongo
@primary_pool = nil @primary_pool = nil
end end
## Configuration helper methods
# Returns a host-port pair.
#
# @return [Array]
#
# @private
def format_pair(host, port)
case host
when String
[host, port ? port.to_i : DEFAULT_PORT]
when nil
['localhost', DEFAULT_PORT]
end
end
# Convert an argument containing a host name string and a
# port number integer into a [host, port] pair array.
#
# @private
def pair_val_to_connection(a)
case a
when nil
['localhost', DEFAULT_PORT]
when String
[a, DEFAULT_PORT]
when Integer
['localhost', a]
when Array
a
end
end
# Checkout a socket for reading (i.e., a secondary node). # Checkout a socket for reading (i.e., a secondary node).
def checkout_reader def checkout_reader
@ -567,6 +499,77 @@ module Mongo
end end
end end
protected
# Generic initialization code.
# @protected
def setup(options)
# Authentication objects
@auths = options.fetch(:auths, [])
# Lock for request ids.
@id_lock = Mutex.new
# Pool size and timeout.
@pool_size = options[:pool_size] || 1
@timeout = options[:timeout] || 5.0
# Mutex for synchronizing pool access
@connection_mutex = Mutex.new
# Global safe option. This is false by default.
@safe = options[:safe] || false
# Create a mutex when a new key, in this case a socket,
# is added to the hash.
@safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
# Condition variable for signal and wait
@queue = ConditionVariable.new
# Connection pool for primay node
@primary = nil
@primary_pool = nil
@logger = options[:logger] || nil
should_connect = options.fetch(:connect, true)
connect if should_connect
end
## Configuration helper methods
# Returns a host-port pair.
#
# @return [Array]
#
# @private
def format_pair(host, port)
case host
when String
[host, port ? port.to_i : DEFAULT_PORT]
when nil
['localhost', DEFAULT_PORT]
end
end
# Convert an argument containing a host name string and a
# port number integer into a [host, port] pair array.
#
# @private
def pair_val_to_connection(a)
case a
when nil
['localhost', DEFAULT_PORT]
when String
[a, DEFAULT_PORT]
when Integer
['localhost', a]
when Array
a
end
end
private private
# If a ConnectionFailure is raised, this method will be called # If a ConnectionFailure is raised, this method will be called
@ -584,7 +587,7 @@ module Mongo
# apply any saved authentication. # apply any saved authentication.
# TODO: simplify # TODO: simplify
def is_primary?(config) def is_primary?(config)
config && (config['ismaster'] == 1 || config['ismaster'] == true) || !@replica_set && @slave_ok config && (config['ismaster'] == 1 || config['ismaster'] == true) || @slave_ok
end end
def check_is_master(node) def check_is_master(node)

View File

@ -19,19 +19,25 @@
module Mongo module Mongo
# Instantiates and manages connections to MongoDB. # Instantiates and manages connections to MongoDB.
class ReplSetConnection class ReplSetConnection < Connection
attr_reader :nodes, :secondaries, :arbiters, :read_pool, :secondary_pools attr_reader :nodes, :secondaries, :arbiters, :read_pool, :secondary_pools
def initialize(*args) def initialize(*args)
if args.last.is_a?(Hash) if args.last.is_a?(Hash)
options = args.pop opts = args.pop
else
opts = {}
end end
unless args.length > 0
raise MongoArgumentError, "A ReplSetConnection requires at least one node."
end
# Get seed nodes
@nodes = args @nodes = args
# Replica set name # Replica set name
@replica_set_name = options[:rs_name] @replica_set = opts[:rs_name]
# Cache the various node types when connecting to a replica set. # Cache the various node types when connecting to a replica set.
@secondaries = [] @secondaries = []
@ -41,7 +47,10 @@ module Mongo
@secondary_pools = [] @secondary_pools = []
@read_pool = nil @read_pool = nil
super # Are we allowing reads from secondaries?
@read_secondary = opts.fetch(:read_secondary, false)
setup(opts)
end end
# Create a new socket and attempt to connect to master. # Create a new socket and attempt to connect to master.
@ -86,6 +95,7 @@ module Mongo
# If a ConnectionFailure is raised, this method will be called # If a ConnectionFailure is raised, this method will be called
# to close the connection and reset connection values. # to close the connection and reset connection values.
# TODO: what's the point of this method?
def reset_connection def reset_connection
super super
@secondaries = [] @secondaries = []
@ -124,7 +134,12 @@ module Mongo
config config
end end
# Primary, when connecting to a replica can, can only be a true primary node.
# (And not a slave, which is possible when connecting with the standard
# Connection class.
def is_primary?(config)
config && (config['ismaster'] == 1 || config['ismaster'] == true)
end
# Pick a node randomly from the set of possible secondaries. # Pick a node randomly from the set of possible secondaries.
def pick_secondary_for_read def pick_secondary_for_read
@ -135,15 +150,15 @@ module Mongo
# Make sure that we're connected to the expected replica set. # Make sure that we're connected to the expected replica set.
def check_set_name(config, socket) def check_set_name(config, socket)
if @replica_set_name if @replica_set
config = self['admin'].command({:replSetGetStatus => 1}, config = self['admin'].command({:replSetGetStatus => 1},
:sock => socket, :check_response => false) :sock => socket, :check_response => false)
if !Mongo::Support.ok?(config) if !Mongo::Support.ok?(config)
raise ReplicaSetConnectionError, config['errmsg'] raise ReplicaSetConnectionError, config['errmsg']
elsif config['set'] != @replica_set_name elsif config['set'] != @replica_set
raise ReplicaSetConnectionError, raise ReplicaSetConnectionError,
"Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set_name}'" "Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set}'"
end end
end end
end end

View File

@ -9,15 +9,15 @@ class ConnectTest < Test::Unit::TestCase
include Mongo include Mongo
def test_connect_bad_name def test_connect_bad_name
assert_raise_error(ReplicaSetConnectionError, "expected 'wrong-repl-set-name'") do assert_raise_error(ReplicaSetReplSetConnectionError, "expected 'wrong-repl-set-name'") do
Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]], ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
:rs_name => "wrong-repl-set-name") [TEST_HOST, TEST_PORT + 2], :rs_name => "wrong-repl-set-name")
end end
end end
def test_connect def test_connect
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]], @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
:name => "foo") [TEST_HOST, TEST_PORT + 2], :name => "foo")
assert @conn.connected? assert @conn.connected?
end end
@ -25,7 +25,8 @@ class ConnectTest < Test::Unit::TestCase
puts "Please kill the node at #{TEST_PORT}." puts "Please kill the node at #{TEST_PORT}."
gets gets
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
assert @conn.connected? assert @conn.connected?
end end
@ -33,7 +34,8 @@ class ConnectTest < Test::Unit::TestCase
puts "Please kill the node at #{TEST_PORT + 1}." puts "Please kill the node at #{TEST_PORT + 1}."
gets gets
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
assert @conn.connected? assert @conn.connected?
end end
@ -41,7 +43,8 @@ class ConnectTest < Test::Unit::TestCase
puts "Please kill the node at #{TEST_PORT + 2}." puts "Please kill the node at #{TEST_PORT + 2}."
gets gets
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
assert @conn.connected? assert @conn.connected?
end end
end end

View File

@ -9,7 +9,8 @@ class ReplicaSetCountTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@coll = @db.collection("test-sets") @coll = @db.collection("test-sets")

View File

@ -9,7 +9,8 @@ class ReplicaSetInsertTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@coll = @db.collection("test-sets") @coll = @db.collection("test-sets")

View File

@ -9,7 +9,8 @@ class ReplicaSetNodeTypeTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@coll = @db.collection("test-sets") @coll = @db.collection("test-sets")

View File

@ -9,8 +9,8 @@ class ReplicaSetPooledInsertTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]], @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
:pool_size => 10, :timeout => 5) [TEST_HOST, TEST_PORT + 2], :pool_size => 10, :timeout => 5)
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@coll = @db.collection("test-sets") @coll = @db.collection("test-sets")

View File

@ -9,7 +9,7 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT]], :read_secondary => true) @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], :read_secondary => true)
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@coll = @db.collection("test-sets", :safe => {:w => 2, :wtimeout => 100}) @coll = @db.collection("test-sets", :safe => {:w => 2, :wtimeout => 100})

View File

@ -9,7 +9,8 @@ class ReplicaSetQueryTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@coll = @db.collection("test-sets") @coll = @db.collection("test-sets")

View File

@ -8,11 +8,13 @@ class ReplicaSetAckTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]]) @conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
master = [@conn.primary_pool.host, @conn.primary_pool.port] master = [@conn.primary_pool.host, @conn.primary_pool.port]
@slave1 = Mongo::Connection.new(@conn.secondary_pools[0].host, @conn.secondary_pools[0].port, :slave_ok => true) @slave1 = Connection.new(@conn.secondary_pools[0].host,
@conn.secondary_pools[0].port, :slave_ok => true)
@db = @conn.db(MONGO_TEST_DB) @db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets") @db.drop_collection("test-sets")
@ -37,7 +39,6 @@ class ReplicaSetAckTest < Test::Unit::TestCase
assert @col.insert({:foo => "0" * 10000}, :safe => {:w => 2, :wtimeout => 1000}) assert @col.insert({:foo => "0" * 10000}, :safe => {:w => 2, :wtimeout => 1000})
assert_equal 2, @slave1[MONGO_TEST_DB]["test-sets"].count assert_equal 2, @slave1[MONGO_TEST_DB]["test-sets"].count
assert @col.update({:baz => "bar"}, {:baz => "foo"}, :safe => {:w => 2, :wtimeout => 1000}) assert @col.update({:baz => "bar"}, {:baz => "foo"}, :safe => {:w => 2, :wtimeout => 1000})
assert @slave1[MONGO_TEST_DB]["test-sets"].find_one({:baz => "foo"}) assert @slave1[MONGO_TEST_DB]["test-sets"].find_one({:baz => "foo"})

View File

@ -1,181 +1,82 @@
require './test/test_helper' require './test/test_helper'
include Mongo include Mongo
#class ConnectionTest < Test::Unit::TestCase class ReplSetConnectionTest < Test::Unit::TestCase
# context "Initialization: " do context "Initialization: " do
# setup do setup do
# def new_mock_socket(host='localhost', port=27017) def new_mock_socket(host='localhost', port=27017)
# socket = Object.new socket = Object.new
# socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
# socket.stubs(:close) socket.stubs(:close)
# socket socket
# end end
#
# def new_mock_db def new_mock_db
# db = Object.new db = Object.new
# end end
# end end
#
# context "given a single node" do context "connecting to a replica set" do
# setup do setup do
# @conn = Connection.new('localhost', 27017, :connect => false) TCPSocket.stubs(:new).returns(new_mock_socket('localhost', 27017))
# TCPSocket.stubs(:new).returns(new_mock_socket) @conn = ReplSetConnection.new(['localhost', 27017], :connect => false, :read_secondary => true)
#
# admin_db = new_mock_db admin_db = new_mock_db
# admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1}) @hosts = ['localhost:27018', 'localhost:27019', 'localhost:27020']
# @conn.expects(:[]).with('admin').returns(admin_db)
# @conn.connect admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts}).
# end then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
# then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
# should "set localhost and port to master" do then.returns({'ok' => 1, 'ismaster' => 0, 'arbiterOnly' => 1})
# assert_equal 'localhost', @conn.primary_pool.host
# assert_equal 27017, @conn.primary_pool.port @conn.stubs(:[]).with('admin').returns(admin_db)
# end @conn.connect
# end
# should "set connection pool to 1" do
# assert_equal 1, @conn.primary_pool.size should "store the hosts returned from the ismaster command" do
# end assert_equal 'localhost', @conn.primary_pool.host
# assert_equal 27017, @conn.primary_pool.port
# should "default slave_ok to false" do
# assert !@conn.slave_ok? assert_equal 'localhost', @conn.secondary_pools[0].host
# end assert_equal 27018, @conn.secondary_pools[0].port
# end
# assert_equal 'localhost', @conn.secondary_pools[1].host
# context "connecting to a replica set" do assert_equal 27019, @conn.secondary_pools[1].port
# setup do
# TCPSocket.stubs(:new).returns(new_mock_socket('localhost', 27017)) assert_equal 2, @conn.secondary_pools.length
# @conn = Connection.multi([['localhost', 27017]], :connect => false, :read_secondary => true) end
# end
# admin_db = new_mock_db
# @hosts = ['localhost:27018', 'localhost:27019', 'localhost:27020'] context "connecting to a replica set and providing seed nodes" do
# setup do
# admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts}). TCPSocket.stubs(:new).returns(new_mock_socket)
# then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}). @conn = ReplSetConnection.new(['localhost', 27017], ['localhost', 27019], :connect => false)
# then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
# then.returns({'ok' => 1, 'ismaster' => 0, 'arbiterOnly' => 1}) admin_db = new_mock_db
# @hosts = ['localhost:27017', 'localhost:27018', 'localhost:27019']
# @conn.stubs(:[]).with('admin').returns(admin_db) admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts})
# @conn.connect @conn.stubs(:[]).with('admin').returns(admin_db)
# end @conn.connect
# end
# should "store the hosts returned from the ismaster command" do end
# assert_equal 'localhost', @conn.primary_pool.host
# assert_equal 27017, @conn.primary_pool.port context "initializing with a mongodb uri" do
#
# assert_equal 'localhost', @conn.secondary_pools[0].host should "parse a uri specifying multiple nodes" do
# assert_equal 27018, @conn.secondary_pools[0].port @conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
# assert_equal ['localhost', 27017], @conn.nodes[0]
# assert_equal 'localhost', @conn.secondary_pools[1].host assert_equal ['mydb.com', 27018], @conn.nodes[1]
# assert_equal 27019, @conn.secondary_pools[1].port end
#
# assert_equal 2, @conn.secondary_pools.length should "parse a uri specifying multiple nodes with auth" do
# end @conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
# end assert_equal ['localhost', 27017], @conn.nodes[0]
# assert_equal ['mydb.com', 27018], @conn.nodes[1]
# context "connecting to a replica set and providing seed nodes" do auth_hash = {'username' => 'kyle', 'password' => 's3cr3t', 'db_name' => 'app'}
# setup do assert_equal auth_hash, @conn.auths[0]
# TCPSocket.stubs(:new).returns(new_mock_socket) auth_hash = {'username' => 'mickey', 'password' => 'm0u5e', 'db_name' => 'dsny'}
# @conn = Connection.multi([['localhost', 27017], ['localhost', 27019]], :connect => false) assert_equal auth_hash, @conn.auths[1]
# end
# admin_db = new_mock_db end
# @hosts = ['localhost:27017', 'localhost:27018', 'localhost:27019'] end
# admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts}) end
# @conn.stubs(:[]).with('admin').returns(admin_db)
# @conn.connect
# end
#
# should "not store any hosts redundantly" do
# end
# end
#
# context "initializing a paired connection" do
# should "require left and right nodes" do
# assert_raise MongoArgumentError do
# Connection.multi(['localhost', 27018], :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.multi(['localhost', 27018], :connect => false)
# end
# end
#
# should "store both nodes" do
# @conn = Connection.multi([['localhost', 27017], ['localhost', 27018]], :connect => false)
#
# assert_equal ['localhost', 27017], @conn.nodes[0]
# assert_equal ['localhost', 27018], @conn.nodes[1]
# end
# end
#
# context "initializing with a mongodb uri" do
# should "parse a simple uri" do
# @conn = Connection.from_uri("mongodb://localhost", :connect => false)
# assert_equal ['localhost', 27017], @conn.primary
# end
#
# should "allow a complex host names" do
# host_name = "foo.bar-12345.org"
# @conn = Connection.from_uri("mongodb://#{host_name}", :connect => false)
# assert_equal [host_name, 27017], @conn.primary
# end
#
# should "parse a uri specifying multiple nodes" do
# #@conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
# #assert_equal ['localhost', 27017], @conn.nodes[0]
# #assert_equal ['mydb.com', 27018], @conn.nodes[1]
# end
#
# should "parse a uri specifying multiple nodes with auth" do
# #@conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
# #assert_equal ['localhost', 27017], @conn.nodes[0]
# #assert_equal ['mydb.com', 27018], @conn.nodes[1]
# #auth_hash = {'username' => 'kyle', 'password' => 's3cr3t', 'db_name' => 'app'}
# #assert_equal auth_hash, @conn.auths[0]
# #auth_hash = {'username' => 'mickey', 'password' => 'm0u5e', 'db_name' => 'dsny'}
# #assert_equal auth_hash, @conn.auths[1]
# end
#
# should "parse a uri with a hyphen & underscore in the username or password" do
# @conn = Connection.from_uri("mongodb://hyphen-user_name:p-s_s@localhost:27017/db", :connect => false)
# assert_equal ['localhost', 27017], @conn.nodes[0]
# auth_hash = { 'db_name' => 'db', 'username' => 'hyphen-user_name', "password" => 'p-s_s' }
# assert_equal auth_hash, @conn.auths[0]
# end
#
# should "attempt to connect" do
# TCPSocket.stubs(:new).returns(new_mock_socket)
# @conn = Connection.from_uri("mongodb://localhost", :connect => false)
#
# admin_db = new_mock_db
# admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
# @conn.expects(:[]).with('admin').returns(admin_db)
# @conn.expects(:apply_saved_authentication)
# @conn.connect
# end
#
# should "raise an error on invalid uris" do
# assert_raise MongoArgumentError do
# Connection.from_uri("mongo://localhost", :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://localhost:abc", :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://localhost:27017, my.db.com:27018, ", :connect => false)
# end
# end
#
# should "require all of username, password, and database if any one is specified" do
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://localhost/db", :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://kyle:password@localhost", :connect => false)
# end
# end
# end
# end
#end