mongo 1.0 → 1.1.5

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 (95) hide show
  1. data/LICENSE.txt +1 -13
  2. data/{README.rdoc → README.md} +129 -149
  3. data/Rakefile +94 -58
  4. data/bin/mongo_console +21 -0
  5. data/docs/1.0_UPGRADE.md +21 -0
  6. data/docs/CREDITS.md +123 -0
  7. data/docs/FAQ.md +112 -0
  8. data/docs/GridFS.md +158 -0
  9. data/docs/HISTORY.md +185 -0
  10. data/docs/REPLICA_SETS.md +75 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo/collection.rb +225 -105
  14. data/lib/mongo/connection.rb +374 -315
  15. data/lib/mongo/cursor.rb +122 -77
  16. data/lib/mongo/db.rb +109 -85
  17. data/lib/mongo/exceptions.rb +6 -0
  18. data/lib/mongo/gridfs/grid.rb +19 -11
  19. data/lib/mongo/gridfs/grid_ext.rb +36 -9
  20. data/lib/mongo/gridfs/grid_file_system.rb +15 -9
  21. data/lib/mongo/gridfs/grid_io.rb +49 -16
  22. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  23. data/lib/mongo/repl_set_connection.rb +290 -0
  24. data/lib/mongo/util/conversions.rb +3 -1
  25. data/lib/mongo/util/core_ext.rb +17 -4
  26. data/lib/mongo/util/pool.rb +125 -0
  27. data/lib/mongo/util/server_version.rb +2 -0
  28. data/lib/mongo/util/support.rb +12 -0
  29. data/lib/mongo/util/uri_parser.rb +71 -0
  30. data/lib/mongo.rb +23 -7
  31. data/{mongo-ruby-driver.gemspec → mongo.gemspec} +9 -7
  32. data/test/auxillary/1.4_features.rb +2 -2
  33. data/test/auxillary/authentication_test.rb +1 -1
  34. data/test/auxillary/autoreconnect_test.rb +1 -1
  35. data/test/{slave_connection_test.rb → auxillary/slave_connection_test.rb} +6 -6
  36. data/test/bson/binary_test.rb +15 -0
  37. data/test/bson/bson_test.rb +537 -0
  38. data/test/bson/byte_buffer_test.rb +190 -0
  39. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  40. data/test/bson/json_test.rb +17 -0
  41. data/test/bson/object_id_test.rb +141 -0
  42. data/test/bson/ordered_hash_test.rb +197 -0
  43. data/test/collection_test.rb +195 -15
  44. data/test/connection_test.rb +93 -56
  45. data/test/conversions_test.rb +1 -1
  46. data/test/cursor_fail_test.rb +75 -0
  47. data/test/cursor_message_test.rb +43 -0
  48. data/test/cursor_test.rb +93 -32
  49. data/test/db_api_test.rb +28 -55
  50. data/test/db_connection_test.rb +2 -3
  51. data/test/db_test.rb +45 -40
  52. data/test/grid_file_system_test.rb +14 -6
  53. data/test/grid_io_test.rb +36 -7
  54. data/test/grid_test.rb +54 -10
  55. data/test/replica_sets/connect_test.rb +84 -0
  56. data/test/replica_sets/count_test.rb +35 -0
  57. data/test/{replica → replica_sets}/insert_test.rb +17 -14
  58. data/test/replica_sets/pooled_insert_test.rb +55 -0
  59. data/test/replica_sets/query_secondaries.rb +80 -0
  60. data/test/replica_sets/query_test.rb +41 -0
  61. data/test/replica_sets/replication_ack_test.rb +64 -0
  62. data/test/replica_sets/rs_test_helper.rb +29 -0
  63. data/test/safe_test.rb +68 -0
  64. data/test/support/hash_with_indifferent_access.rb +199 -0
  65. data/test/support/keys.rb +45 -0
  66. data/test/support_test.rb +19 -0
  67. data/test/test_helper.rb +53 -15
  68. data/test/threading/{test_threading_large_pool.rb → threading_with_large_pool_test.rb} +2 -2
  69. data/test/threading_test.rb +2 -2
  70. data/test/tools/repl_set_manager.rb +241 -0
  71. data/test/tools/test.rb +13 -0
  72. data/test/unit/collection_test.rb +70 -7
  73. data/test/unit/connection_test.rb +18 -39
  74. data/test/unit/cursor_test.rb +7 -8
  75. data/test/unit/db_test.rb +14 -17
  76. data/test/unit/grid_test.rb +49 -0
  77. data/test/unit/pool_test.rb +9 -0
  78. data/test/unit/repl_set_connection_test.rb +82 -0
  79. data/test/unit/safe_test.rb +125 -0
  80. metadata +132 -51
  81. data/bin/bson_benchmark.rb +0 -59
  82. data/bin/fail_if_no_c.rb +0 -11
  83. data/examples/admin.rb +0 -43
  84. data/examples/capped.rb +0 -22
  85. data/examples/cursor.rb +0 -48
  86. data/examples/gridfs.rb +0 -44
  87. data/examples/index_test.rb +0 -126
  88. data/examples/info.rb +0 -31
  89. data/examples/queries.rb +0 -70
  90. data/examples/simple.rb +0 -24
  91. data/examples/strict.rb +0 -35
  92. data/examples/types.rb +0 -36
  93. data/test/replica/count_test.rb +0 -34
  94. data/test/replica/pooled_insert_test.rb +0 -54
  95. data/test/replica/query_test.rb +0 -39
@@ -0,0 +1,80 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require './test/replica_sets/rs_test_helper'
3
+
4
+ # NOTE: This test expects a replica set of three nodes to be running
5
+ # on the local host.
6
+ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
7
+ include Mongo
8
+
9
+ def setup
10
+ @conn = ReplSetConnection.new([RS.host, RS.ports[0]], :read_secondary => true)
11
+ @db = @conn.db(MONGO_TEST_DB)
12
+ @db.drop_collection("test-sets")
13
+ end
14
+
15
+ def teardown
16
+ RS.restart_killed_nodes
17
+ end
18
+
19
+ def test_con
20
+ assert @conn.primary_pool, "No primary pool!"
21
+ assert @conn.read_pool, "No read pool!"
22
+ assert @conn.primary_pool.port != @conn.read_pool.port,
23
+ "Primary port and read port at the same!"
24
+ end
25
+
26
+ def test_query_secondaries
27
+ @coll = @db.collection("test-sets", :safe => {:w => 3, :wtimeout => 10000})
28
+ @coll.save({:a => 20})
29
+ @coll.save({:a => 30})
30
+ @coll.save({:a => 40})
31
+ results = []
32
+ @coll.find.each {|r| results << r["a"]}
33
+ assert results.include?(20)
34
+ assert results.include?(30)
35
+ assert results.include?(40)
36
+
37
+ RS.kill_primary
38
+
39
+ results = []
40
+ rescue_connection_failure do
41
+ @coll.find.each {|r| results << r}
42
+ [20, 30, 40].each do |a|
43
+ assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
44
+ end
45
+ end
46
+ end
47
+
48
+ def test_kill_primary
49
+ @coll = @db.collection("test-sets", :safe => {:w => 3, :wtimeout => 10000})
50
+ @coll.save({:a => 20})
51
+ @coll.save({:a => 30})
52
+ assert_equal 2, @coll.find.to_a.length
53
+
54
+ # Should still be able to read immediately after killing master node
55
+ RS.kill_primary
56
+ assert_equal 2, @coll.find.to_a.length
57
+ end
58
+
59
+ def test_kill_secondary
60
+ @coll = @db.collection("test-sets", {:safe => {:w => 3, :wtimeout => 10000}})
61
+ @coll.save({:a => 20})
62
+ @coll.save({:a => 30})
63
+ assert_equal 2, @coll.find.to_a.length
64
+
65
+ read_node = RS.get_node_from_port(@conn.read_pool.port)
66
+ RS.kill(read_node)
67
+
68
+ # Should fail immediately on next read
69
+ assert_raise ConnectionFailure do
70
+ @coll.find.to_a.length
71
+ end
72
+
73
+ # Should eventually reconnect and be able to read
74
+ rescue_connection_failure do
75
+ length = @coll.find.to_a.length
76
+ assert_equal 2, length
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,41 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require './test/replica_sets/rs_test_helper'
3
+
4
+ # NOTE: This test expects a replica set of three nodes to be running
5
+ # on the local host.
6
+ class ReplicaSetQueryTest < Test::Unit::TestCase
7
+ include Mongo
8
+
9
+ def setup
10
+ @conn = ReplSetConnection.new([RS.host, RS.ports[0]])
11
+ @db = @conn.db(MONGO_TEST_DB)
12
+ @db.drop_collection("test-sets")
13
+ @coll = @db.collection("test-sets")
14
+ end
15
+
16
+ def teardown
17
+ RS.restart_killed_nodes
18
+ end
19
+
20
+ def test_query
21
+ @coll.save({:a => 20}, :safe => {:w => 3})
22
+ @coll.save({:a => 30}, :safe => {:w => 3})
23
+ @coll.save({:a => 40}, :safe => {:w => 3})
24
+ results = []
25
+ @coll.find.each {|r| results << r}
26
+ [20, 30, 40].each do |a|
27
+ assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
28
+ end
29
+
30
+ RS.kill_primary
31
+
32
+ results = []
33
+ rescue_connection_failure do
34
+ @coll.find.each {|r| results << r}
35
+ [20, 30, 40].each do |a|
36
+ assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,64 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require './test/replica_sets/rs_test_helper'
3
+
4
+ # NOTE: This test expects a replica set of three nodes to be running on local host.
5
+ class ReplicaSetAckTest < Test::Unit::TestCase
6
+ include Mongo
7
+
8
+ def setup
9
+ RS.ensure_up
10
+
11
+ @conn = ReplSetConnection.new([RS.host, RS.ports[0]])
12
+
13
+ @slave1 = Connection.new(@conn.secondary_pools[0].host,
14
+ @conn.secondary_pools[0].port, :slave_ok => true)
15
+
16
+ @db = @conn.db(MONGO_TEST_DB)
17
+ @db.drop_collection("test-sets")
18
+ @col = @db.collection("test-sets")
19
+ end
20
+
21
+ def test_safe_mode_with_w_failure
22
+ assert_raise_error OperationFailure, "timeout" do
23
+ @col.insert({:foo => 1}, :safe => {:w => 4, :wtimeout => 1, :fsync => true})
24
+ end
25
+ assert_raise_error OperationFailure, "timeout" do
26
+ @col.update({:foo => 1}, {:foo => 2}, :safe => {:w => 4, :wtimeout => 1, :fsync => true})
27
+ end
28
+ assert_raise_error OperationFailure, "timeout" do
29
+ @col.remove({:foo => 2}, :safe => {:w => 4, :wtimeout => 1, :fsync => true})
30
+ end
31
+ end
32
+
33
+ def test_safe_mode_replication_ack
34
+ @col.insert({:baz => "bar"}, :safe => {:w => 2, :wtimeout => 5000})
35
+
36
+ assert @col.insert({:foo => "0" * 5000}, :safe => {:w => 2, :wtimeout => 5000})
37
+ assert_equal 2, @slave1[MONGO_TEST_DB]["test-sets"].count
38
+
39
+ assert @col.update({:baz => "bar"}, {:baz => "foo"}, :safe => {:w => 2, :wtimeout => 5000})
40
+ assert @slave1[MONGO_TEST_DB]["test-sets"].find_one({:baz => "foo"})
41
+
42
+ assert @col.remove({}, :safe => {:w => 2, :wtimeout => 5000})
43
+ assert_equal 0, @slave1[MONGO_TEST_DB]["test-sets"].count
44
+ end
45
+
46
+ def test_last_error_responses
47
+ 20.times { @col.insert({:baz => "bar"}) }
48
+ response = @db.get_last_error(:w => 2, :wtimeout => 5000)
49
+ assert response['ok'] == 1
50
+ assert response['lastOp']
51
+
52
+ @col.update({}, {:baz => "foo"}, :multi => true)
53
+ response = @db.get_last_error(:w => 2, :wtimeout => 5000)
54
+ assert response['ok'] == 1
55
+ assert response['lastOp']
56
+
57
+ @col.remove({})
58
+ response = @db.get_last_error(:w => 2, :wtimeout => 5000)
59
+ assert response['ok'] == 1
60
+ assert response['n'] == 20
61
+ assert response['lastOp']
62
+ end
63
+
64
+ end
@@ -0,0 +1,29 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require './test/test_helper'
3
+ require './test/tools/repl_set_manager'
4
+
5
+ unless defined? RS
6
+ RS = ReplSetManager.new
7
+ RS.start_set
8
+ end
9
+
10
+ class Test::Unit::TestCase
11
+
12
+ # Generic code for rescuing connection failures and retrying operations.
13
+ # This could be combined with some timeout functionality.
14
+ def rescue_connection_failure(max_retries=60)
15
+ success = false
16
+ tries = 0
17
+ while !success && tries < max_retries
18
+ begin
19
+ yield
20
+ success = true
21
+ rescue Mongo::ConnectionFailure
22
+ puts "Rescue attempt #{tries}\n"
23
+ tries += 1
24
+ sleep(1)
25
+ end
26
+ end
27
+ end
28
+
29
+ end
data/test/safe_test.rb ADDED
@@ -0,0 +1,68 @@
1
+ require './test/test_helper'
2
+ include Mongo
3
+
4
+ class SafeTest < Test::Unit::TestCase
5
+ context "Safe mode propogation: " do
6
+ setup do
7
+ @con = standard_connection(:safe => {:w => 1})
8
+ @db = @con[MONGO_TEST_DB]
9
+ @col = @db['test-safe']
10
+ @col.create_index([[:a, 1]], :unique => true)
11
+ @col.remove
12
+ end
13
+
14
+ should "propogate safe option on insert" do
15
+ @col.insert({:a => 1})
16
+
17
+ assert_raise_error(OperationFailure, "duplicate key") do
18
+ @col.insert({:a => 1})
19
+ end
20
+ end
21
+
22
+ should "allow safe override on insert" do
23
+ @col.insert({:a => 1})
24
+ @col.insert({:a => 1}, :safe => false)
25
+ end
26
+
27
+ should "propogate safe option on update" do
28
+ @col.insert({:a => 1})
29
+ @col.insert({:a => 2})
30
+
31
+ assert_raise_error(OperationFailure, "duplicate key") do
32
+ @col.update({:a => 2}, {:a => 1})
33
+ end
34
+ end
35
+
36
+ should "allow safe override on update" do
37
+ @col.insert({:a => 1})
38
+ @col.insert({:a => 2})
39
+ @col.update({:a => 2}, {:a => 1}, :safe => false)
40
+ end
41
+ end
42
+
43
+ context "Safe error objects" do
44
+ setup do
45
+ @con = standard_connection
46
+ @db = @con[MONGO_TEST_DB]
47
+ @col = @db['test']
48
+ @col.remove
49
+ @col.insert({:a => 1})
50
+ @col.insert({:a => 1})
51
+ @col.insert({:a => 1})
52
+ end
53
+
54
+ should "return object on update" do
55
+ response = @col.update({:a => 1}, {"$set" => {:a => 2}},
56
+ :multi => true, :safe => true)
57
+
58
+ assert response['updatedExisting']
59
+ assert_equal 3, response['n']
60
+ end
61
+
62
+ should "return object on remove" do
63
+ response = @col.remove({}, :safe => true)
64
+ assert_equal 3, response['n']
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,199 @@
1
+ # Note: HashWithIndifferentAccess is so commonly used
2
+ # that we always need to make sure that the driver works
3
+ # with it.
4
+ #require File.join(File.dirname(__FILE__), 'keys.rb')
5
+
6
+ # This class has dubious semantics and we only have it so that
7
+ # people can write params[:key] instead of params['key']
8
+ # and they get the same value for both keys.
9
+
10
+ class Hash
11
+ # Return a new hash with all keys converted to strings.
12
+ def stringify_keys
13
+ dup.stringify_keys!
14
+ end
15
+
16
+ # Destructively convert all keys to strings.
17
+ def stringify_keys!
18
+ keys.each do |key|
19
+ self[key.to_s] = delete(key)
20
+ end
21
+ self
22
+ end
23
+
24
+ # Return a new hash with all keys converted to symbols, as long as
25
+ # they respond to +to_sym+.
26
+ def symbolize_keys
27
+ dup.symbolize_keys!
28
+ end
29
+
30
+ # Destructively convert all keys to symbols, as long as they respond
31
+ # to +to_sym+.
32
+ def symbolize_keys!
33
+ keys.each do |key|
34
+ self[(key.to_sym rescue key) || key] = delete(key)
35
+ end
36
+ self
37
+ end
38
+
39
+ alias_method :to_options, :symbolize_keys
40
+ #alias_method :to_options!, :symbolize_keys!
41
+
42
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
43
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
44
+ # as keys, this will fail.
45
+ #
46
+ # ==== Examples
47
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
48
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
49
+ # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
50
+ def assert_valid_keys(*valid_keys)
51
+ unknown_keys = keys - [valid_keys].flatten
52
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
53
+ end
54
+ end
55
+
56
+ module ActiveSupport
57
+ class HashWithIndifferentAccess < Hash
58
+ def extractable_options?
59
+ true
60
+ end
61
+
62
+ def initialize(constructor = {})
63
+ if constructor.is_a?(Hash)
64
+ super()
65
+ update(constructor)
66
+ else
67
+ super(constructor)
68
+ end
69
+ end
70
+
71
+ def default(key = nil)
72
+ if key.is_a?(Symbol) && include?(key = key.to_s)
73
+ self[key]
74
+ else
75
+ super
76
+ end
77
+ end
78
+
79
+ def self.new_from_hash_copying_default(hash)
80
+ ActiveSupport::HashWithIndifferentAccess.new(hash).tap do |new_hash|
81
+ new_hash.default = hash.default
82
+ end
83
+ end
84
+
85
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
86
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
87
+
88
+ # Assigns a new value to the hash:
89
+ #
90
+ # hash = HashWithIndifferentAccess.new
91
+ # hash[:key] = "value"
92
+ #
93
+ def []=(key, value)
94
+ regular_writer(convert_key(key), convert_value(value))
95
+ end
96
+
97
+ # Updates the instantized hash with values from the second:
98
+ #
99
+ # hash_1 = HashWithIndifferentAccess.new
100
+ # hash_1[:key] = "value"
101
+ #
102
+ # hash_2 = HashWithIndifferentAccess.new
103
+ # hash_2[:key] = "New Value!"
104
+ #
105
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
106
+ #
107
+ def update(other_hash)
108
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
109
+ self
110
+ end
111
+
112
+ alias_method :merge!, :update
113
+
114
+ # Checks the hash for a key matching the argument passed in:
115
+ #
116
+ # hash = HashWithIndifferentAccess.new
117
+ # hash["key"] = "value"
118
+ # hash.key? :key # => true
119
+ # hash.key? "key" # => true
120
+ #
121
+ def key?(key)
122
+ super(convert_key(key))
123
+ end
124
+
125
+ alias_method :include?, :key?
126
+ alias_method :has_key?, :key?
127
+ alias_method :member?, :key?
128
+
129
+ # Fetches the value for the specified key, same as doing hash[key]
130
+ def fetch(key, *extras)
131
+ super(convert_key(key), *extras)
132
+ end
133
+
134
+ # Returns an array of the values at the specified indices:
135
+ #
136
+ # hash = HashWithIndifferentAccess.new
137
+ # hash[:a] = "x"
138
+ # hash[:b] = "y"
139
+ # hash.values_at("a", "b") # => ["x", "y"]
140
+ #
141
+ def values_at(*indices)
142
+ indices.collect {|key| self[convert_key(key)]}
143
+ end
144
+
145
+ # Returns an exact copy of the hash.
146
+ def dup
147
+ HashWithIndifferentAccess.new(self)
148
+ end
149
+
150
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
151
+ # Does not overwrite the existing hash.
152
+ def merge(hash)
153
+ self.dup.update(hash)
154
+ end
155
+
156
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
157
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
158
+ def reverse_merge(other_hash)
159
+ super self.class.new_from_hash_copying_default(other_hash)
160
+ end
161
+
162
+ def reverse_merge!(other_hash)
163
+ replace(reverse_merge( other_hash ))
164
+ end
165
+
166
+ # Removes a specified key from the hash.
167
+ def delete(key)
168
+ super(convert_key(key))
169
+ end
170
+
171
+ def stringify_keys!; self end
172
+ def stringify_keys; dup end
173
+ def symbolize_keys; to_hash.symbolize_keys end
174
+ def to_options!; self end
175
+
176
+ # Convert to a Hash with String keys.
177
+ def to_hash
178
+ Hash.new(default).merge!(self)
179
+ end
180
+
181
+ protected
182
+ def convert_key(key)
183
+ key.kind_of?(Symbol) ? key.to_s : key
184
+ end
185
+
186
+ def convert_value(value)
187
+ case value
188
+ when Hash
189
+ self.class.new_from_hash_copying_default(value)
190
+ when Array
191
+ value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e }
192
+ else
193
+ value
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
@@ -0,0 +1,45 @@
1
+ class Hash
2
+ # Return a new hash with all keys converted to strings.
3
+ def stringify_keys
4
+ dup.stringify_keys!
5
+ end
6
+
7
+ # Destructively convert all keys to strings.
8
+ def stringify_keys!
9
+ keys.each do |key|
10
+ self[key.to_s] = delete(key)
11
+ end
12
+ self
13
+ end
14
+
15
+ # Return a new hash with all keys converted to symbols, as long as
16
+ # they respond to +to_sym+.
17
+ def symbolize_keys
18
+ dup.symbolize_keys!
19
+ end
20
+
21
+ # Destructively convert all keys to symbols, as long as they respond
22
+ # to +to_sym+.
23
+ def symbolize_keys!
24
+ keys.each do |key|
25
+ self[(key.to_sym rescue key) || key] = delete(key)
26
+ end
27
+ self
28
+ end
29
+
30
+ alias_method :to_options, :symbolize_keys
31
+ #alias_method :to_options!, :symbolize_keys!
32
+
33
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
34
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
35
+ # as keys, this will fail.
36
+ #
37
+ # ==== Examples
38
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
39
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
40
+ # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
41
+ def assert_valid_keys(*valid_keys)
42
+ unknown_keys = keys - [valid_keys].flatten
43
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ require './test/test_helper'
2
+
3
+ class SupportTest < Test::Unit::TestCase
4
+ include Mongo
5
+
6
+ def test_command_response_succeeds
7
+ assert Support.ok?('ok' => 1)
8
+ assert Support.ok?('ok' => 1.0)
9
+ assert Support.ok?('ok' => true)
10
+ end
11
+
12
+ def test_command_response_fails
13
+ assert !Support.ok?('ok' => 0)
14
+ assert !Support.ok?('ok' => 0.0)
15
+ assert !Support.ok?('ok' => 0.0)
16
+ assert !Support.ok?('ok' => 'str')
17
+ assert !Support.ok?('ok' => false)
18
+ end
19
+ end
data/test/test_helper.rb CHANGED
@@ -19,27 +19,65 @@ MSG
19
19
  exit
20
20
  end
21
21
 
22
- require 'bson_ext/cbson' if ENV['C_EXT']
22
+ require 'bson_ext/cbson' if !(RUBY_PLATFORM =~ /java/) && ENV['C_EXT']
23
23
 
24
- MONGO_TEST_DB = 'mongo-ruby-test'
24
+ unless defined? MONGO_TEST_DB
25
+ MONGO_TEST_DB = 'ruby-test-db'
26
+ end
27
+
28
+ unless defined? TEST_PORT
29
+ TEST_PORT = ENV['MONGO_RUBY_DRIVER_PORT'] ? ENV['MONGO_RUBY_DRIVER_PORT'].to_i : Mongo::Connection::DEFAULT_PORT
30
+ end
31
+
32
+ unless defined? TEST_HOST
33
+ TEST_HOST = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
34
+ end
25
35
 
26
- # NOTE: most tests assume that MongoDB is running.
27
36
  class Test::Unit::TestCase
28
37
  include Mongo
29
38
  include BSON
30
39
 
31
- # Generic code for rescuing connection failures and retrying operations.
32
- # This could be combined with some timeout functionality.
33
- def rescue_connection_failure
34
- success = false
35
- while !success
36
- begin
37
- yield
38
- success = true
39
- rescue Mongo::ConnectionFailure
40
- puts "Rescuing"
41
- sleep(1)
42
- end
40
+ def self.standard_connection(options={})
41
+ Connection.new(TEST_HOST, TEST_PORT, options)
42
+ end
43
+
44
+ def standard_connection(options={})
45
+ self.class.standard_connection(options)
46
+ end
47
+
48
+ def self.host_port
49
+ "#{mongo_host}:#{mongo_port}"
50
+ end
51
+
52
+ def self.mongo_host
53
+ TEST_HOST
54
+ end
55
+
56
+ def self.mongo_port
57
+ TEST_PORT
58
+ end
59
+
60
+ def host_port
61
+ self.class.host_port
62
+ end
63
+
64
+ def mongo_host
65
+ self.class.mongo_host
66
+ end
67
+
68
+ def mongo_port
69
+ self.class.mongo_port
70
+ end
71
+
72
+
73
+ def assert_raise_error(klass, message)
74
+ begin
75
+ yield
76
+ rescue => e
77
+ assert_equal klass, e.class
78
+ assert e.message.include?(message), "#{e.message} does not include #{message}."
79
+ else
80
+ flunk "Expected assertion #{klass} but none was raised."
43
81
  end
44
82
  end
45
83
  end
@@ -1,4 +1,4 @@
1
- require 'test/test_helper'
1
+ require './test/test_helper'
2
2
 
3
3
  # Essentialy the same as test_threading.rb but with an expanded pool for
4
4
  # testing multiple connections.
@@ -6,7 +6,7 @@ class TestThreadingLargePool < Test::Unit::TestCase
6
6
 
7
7
  include Mongo
8
8
 
9
- @@db = Connection.new('localhost', 27017, :pool_size => 50, :timeout => 60).db(MONGO_TEST_DB)
9
+ @@db = standard_connection(:pool_size => 50, :timeout => 60).db(MONGO_TEST_DB)
10
10
  @@coll = @@db.collection('thread-test-collection')
11
11
 
12
12
  def set_up_safe_data
@@ -1,10 +1,10 @@
1
- require 'test/test_helper'
1
+ require './test/test_helper'
2
2
 
3
3
  class TestThreading < Test::Unit::TestCase
4
4
 
5
5
  include Mongo
6
6
 
7
- @@db = Connection.new('localhost', 27017, :pool_size => 1, :timeout => 30).db(MONGO_TEST_DB)
7
+ @@db = standard_connection(:pool_size => 1, :timeout => 30).db(MONGO_TEST_DB)
8
8
  @@coll = @@db.collection('thread-test-collection')
9
9
 
10
10
  def set_up_safe_data