mongo 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/README.md +9 -6
  2. data/Rakefile +3 -4
  3. data/docs/HISTORY.md +20 -2
  4. data/docs/READ_PREFERENCE.md +39 -0
  5. data/docs/RELEASES.md +1 -1
  6. data/docs/REPLICA_SETS.md +23 -2
  7. data/docs/TAILABLE_CURSORS.md +51 -0
  8. data/docs/TUTORIAL.md +4 -4
  9. data/docs/WRITE_CONCERN.md +5 -2
  10. data/lib/mongo.rb +7 -22
  11. data/lib/mongo/collection.rb +96 -29
  12. data/lib/mongo/connection.rb +107 -62
  13. data/lib/mongo/cursor.rb +136 -57
  14. data/lib/mongo/db.rb +26 -5
  15. data/lib/mongo/exceptions.rb +17 -1
  16. data/lib/mongo/gridfs/grid.rb +1 -1
  17. data/lib/mongo/repl_set_connection.rb +273 -156
  18. data/lib/mongo/util/logging.rb +42 -0
  19. data/lib/mongo/util/node.rb +183 -0
  20. data/lib/mongo/util/pool.rb +76 -13
  21. data/lib/mongo/util/pool_manager.rb +208 -0
  22. data/lib/mongo/util/ssl_socket.rb +38 -0
  23. data/lib/mongo/util/support.rb +9 -1
  24. data/lib/mongo/util/timeout.rb +42 -0
  25. data/lib/mongo/version.rb +3 -0
  26. data/mongo.gemspec +2 -2
  27. data/test/bson/binary_test.rb +1 -1
  28. data/test/bson/bson_string_test.rb +30 -0
  29. data/test/bson/bson_test.rb +6 -3
  30. data/test/bson/byte_buffer_test.rb +1 -1
  31. data/test/bson/hash_with_indifferent_access_test.rb +1 -1
  32. data/test/bson/json_test.rb +1 -1
  33. data/test/bson/object_id_test.rb +2 -18
  34. data/test/bson/ordered_hash_test.rb +38 -3
  35. data/test/bson/test_helper.rb +46 -0
  36. data/test/bson/timestamp_test.rb +32 -10
  37. data/test/collection_test.rb +89 -3
  38. data/test/connection_test.rb +35 -20
  39. data/test/cursor_test.rb +63 -2
  40. data/test/db_test.rb +12 -2
  41. data/test/pool_test.rb +21 -0
  42. data/test/replica_sets/connect_test.rb +26 -13
  43. data/test/replica_sets/connection_string_test.rb +1 -4
  44. data/test/replica_sets/count_test.rb +1 -0
  45. data/test/replica_sets/insert_test.rb +1 -0
  46. data/test/replica_sets/pooled_insert_test.rb +4 -1
  47. data/test/replica_sets/query_secondaries.rb +2 -1
  48. data/test/replica_sets/query_test.rb +2 -1
  49. data/test/replica_sets/read_preference_test.rb +43 -0
  50. data/test/replica_sets/refresh_test.rb +123 -0
  51. data/test/replica_sets/replication_ack_test.rb +9 -4
  52. data/test/replica_sets/rs_test_helper.rb +2 -2
  53. data/test/timeout_test.rb +14 -0
  54. data/test/tools/repl_set_manager.rb +134 -23
  55. data/test/unit/collection_test.rb +6 -8
  56. data/test/unit/connection_test.rb +4 -4
  57. data/test/unit/cursor_test.rb +23 -5
  58. data/test/unit/db_test.rb +2 -0
  59. data/test/unit/grid_test.rb +2 -0
  60. data/test/unit/node_test.rb +73 -0
  61. data/test/unit/pool_manager_test.rb +47 -0
  62. data/test/unit/read_test.rb +101 -0
  63. metadata +214 -138
  64. data/lib/mongo/test.rb +0 -20
  65. data/test/async/collection_test.rb +0 -224
  66. data/test/async/connection_test.rb +0 -24
  67. data/test/async/cursor_test.rb +0 -162
  68. data/test/async/worker_pool_test.rb +0 -99
  69. data/test/load/resque/load.rb +0 -21
  70. data/test/load/resque/processor.rb +0 -26
  71. data/test/load/unicorn/unicorn.rb +0 -29
  72. data/test/tools/load.rb +0 -58
  73. data/test/tools/sharding_manager.rb +0 -202
  74. data/test/tools/test.rb +0 -4
  75. data/test/unit/repl_set_connection_test.rb +0 -59
@@ -0,0 +1,38 @@
1
+ require 'openssl'
2
+
3
+ module Mongo
4
+
5
+ # A basic wrapper over Ruby's SSLSocket that initiates
6
+ # a TCP connection over SSL and then provides an basic interface
7
+ # mirroring Ruby's TCPSocket, vis., TCPSocket#send and TCPSocket#read.
8
+ class SSLSocket
9
+
10
+ def initialize(host, port)
11
+ @socket = ::TCPSocket.new(host, port)
12
+ @ssl = OpenSSL::SSL::SSLSocket.new(@socket)
13
+ @ssl.sync_close = true
14
+ @ssl.connect
15
+ end
16
+
17
+ def setsockopt(key, value, n)
18
+ @socket.setsockopt(key, value, n)
19
+ end
20
+
21
+ # Write to the SSL socket.
22
+ #
23
+ # @param buffer a buffer to send.
24
+ # @param flags socket flags. Because Ruby's SSL
25
+ def send(buffer, flags=0)
26
+ @ssl.syswrite(buffer)
27
+ end
28
+
29
+ def read(length, buffer)
30
+ @ssl.sysread(length, buffer)
31
+ end
32
+
33
+ def close
34
+ @ssl.close
35
+ end
36
+
37
+ end
38
+ end
@@ -44,7 +44,6 @@ module Mongo
44
44
  Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
45
45
  end
46
46
 
47
-
48
47
  def validate_db_name(db_name)
49
48
  unless [String, Symbol].include?(db_name.class)
50
49
  raise TypeError, "db_name must be a string or symbol"
@@ -59,6 +58,15 @@ module Mongo
59
58
  db_name
60
59
  end
61
60
 
61
+ def validate_read_preference(value)
62
+ if [:primary, :secondary, nil].include?(value)
63
+ return true
64
+ else
65
+ raise MongoArgumentError, "#{value} is not a valid read preference. " +
66
+ "Please specify either :primary or :secondary."
67
+ end
68
+ end
69
+
62
70
  def format_order_clause(order)
63
71
  case order
64
72
  when String, Symbol then string_as_sort_parameters(order)
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+ module Mongo #:nodoc:
19
+ module TimeoutWrapper
20
+ extend self
21
+
22
+ def timeout_lib=(lib)
23
+ @@timeout_lib = lib
24
+ end
25
+
26
+ def timeout_lib
27
+ @@timeout_lib
28
+ end
29
+
30
+ def timeout(delay, &block)
31
+ if timeout_lib
32
+ begin
33
+ timeout_lib.timeout(delay, &block)
34
+ rescue ::Timeout::Error
35
+ raise Mongo::OperationTimeout
36
+ end
37
+ else
38
+ yield
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module Mongo
2
+ VERSION = "1.4.0"
3
+ end
@@ -1,4 +1,4 @@
1
- require "./lib/mongo"
1
+ require File.expand_path('../lib/mongo/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'mongo'
@@ -30,5 +30,5 @@ Gem::Specification.new do |s|
30
30
  s.email = 'mongodb-dev@googlegroups.com'
31
31
  s.homepage = 'http://www.mongodb.org'
32
32
 
33
- s.add_dependency(%q<bson>, [">= #{Mongo::VERSION}"])
33
+ s.add_dependency('bson', Mongo::VERSION)
34
34
  end
@@ -1,5 +1,5 @@
1
1
  # encoding:utf-8
2
- require './test/test_helper'
2
+ require './test/bson/test_helper'
3
3
 
4
4
  class BinaryTest < Test::Unit::TestCase
5
5
  context "Inspecting" do
@@ -0,0 +1,30 @@
1
+ # encoding:utf-8
2
+ require './test/bson/test_helper'
3
+ require 'complex'
4
+ require 'bigdecimal'
5
+ require 'rational'
6
+
7
+ class BSONTest < Test::Unit::TestCase
8
+
9
+ include BSON
10
+
11
+ def setup
12
+ @encoder = BSON::BSON_CODER
13
+ end
14
+
15
+ def assert_doc_pass(doc, options={})
16
+ bson = @encoder.serialize(doc)
17
+ if options[:debug]
18
+ puts "DEBUGGING DOC:"
19
+ p bson.to_a
20
+ puts "DESERIALIZES TO:"
21
+ end
22
+ assert_equal @encoder.serialize(doc).to_a, bson.to_a
23
+ assert_equal doc, @encoder.deserialize(bson)
24
+ end
25
+
26
+ def test_string
27
+ assert_doc_pass({:a => "hello"})
28
+ end
29
+
30
+ end
@@ -1,5 +1,6 @@
1
1
  # encoding:utf-8
2
- require './test/test_helper'
2
+ require './test/bson/test_helper'
3
+ require 'set'
3
4
 
4
5
  if RUBY_VERSION < '1.9'
5
6
  require 'complex'
@@ -436,8 +437,10 @@ class BSONTest < Test::Unit::TestCase
436
437
  0x00, 0x00, 0x14, 0x00,
437
438
  0x00, 0x00, 0x00])
438
439
 
439
- assert_equal 4, result["test"][0]
440
- assert_equal 20, result["test"][1]
440
+ silently do
441
+ assert_equal 4, result["test"][0]
442
+ assert_equal 20, result["test"][1]
443
+ end
441
444
  end
442
445
  end
443
446
 
@@ -1,5 +1,5 @@
1
1
  # encoding: binary
2
- require './test/test_helper'
2
+ require './test/bson/test_helper'
3
3
 
4
4
  class ByteBufferTest < Test::Unit::TestCase
5
5
  include BSON
@@ -1,5 +1,5 @@
1
1
  # encoding:utf-8
2
- require './test/test_helper'
2
+ require './test/bson/test_helper'
3
3
  require './test/support/hash_with_indifferent_access'
4
4
 
5
5
  class HashWithIndifferentAccessTest < Test::Unit::TestCase
@@ -1,4 +1,4 @@
1
- require './test/test_helper'
1
+ require './test/bson/test_helper'
2
2
  require 'rubygems'
3
3
  require 'json'
4
4
 
@@ -1,12 +1,9 @@
1
- require './test/test_helper'
1
+ require './test/bson/test_helper'
2
2
  require 'rubygems'
3
3
  require 'json'
4
4
 
5
5
  class ObjectIdTest < Test::Unit::TestCase
6
6
 
7
- include Mongo
8
- include BSON
9
-
10
7
  def setup
11
8
  @o = ObjectId.new
12
9
  end
@@ -65,18 +62,6 @@ class ObjectIdTest < Test::Unit::TestCase
65
62
  assert_equal "BSON::ObjectId('#{@o.to_s}')", @o.inspect
66
63
  end
67
64
 
68
- def test_save_and_restore
69
- db = standard_connection.db(MONGO_TEST_DB)
70
- coll = db.collection('test')
71
-
72
- coll.remove
73
- coll << {'a' => 1, '_id' => @o}
74
-
75
- row = coll.find().collect.first
76
- assert_equal 1, row['a']
77
- assert_equal @o, row['_id']
78
- end
79
-
80
65
  def test_from_string
81
66
  hex_str = @o.to_s
82
67
  o2 = ObjectId.from_string(hex_str)
@@ -134,8 +119,7 @@ class ObjectIdTest < Test::Unit::TestCase
134
119
  time = Time.now.utc
135
120
  id = ObjectId.from_time(time, :unique => true)
136
121
 
137
- mac_id = Digest::MD5.digest(Socket.gethostname)[0, 3].unpack("C3")
138
- assert_equal id.to_a[4, 3], mac_id
122
+ assert_equal id.to_a[4, 3], ObjectId.machine_id.unpack("C3")
139
123
  assert_equal time.to_i, id.generation_time.to_i
140
124
 
141
125
  id2 = ObjectId.new(nil, time)
@@ -1,4 +1,4 @@
1
- require './test/test_helper'
1
+ require './test/bson/test_helper'
2
2
 
3
3
  class OrderedHashTest < Test::Unit::TestCase
4
4
 
@@ -38,6 +38,7 @@ class OrderedHashTest < Test::Unit::TestCase
38
38
  same_doc = BSON::OrderedHash.new
39
39
  same_doc['_id'] = 'ab12'
40
40
  same_doc['name'] = 'test'
41
+
41
42
  list << doc
42
43
  list << same_doc
43
44
 
@@ -45,6 +46,23 @@ class OrderedHashTest < Test::Unit::TestCase
45
46
  assert_equal 1, list.uniq.size
46
47
  end
47
48
 
49
+ if !(RUBY_VERSION =~ /1.8.6/)
50
+ def test_compatibility_with_hash
51
+ list = []
52
+ doc = BSON::OrderedHash.new
53
+ doc['_id'] = 'ab12'
54
+ doc['name'] = 'test'
55
+
56
+ doc2 = {}
57
+ doc2['_id'] = 'ab12'
58
+ doc2['name'] = 'test'
59
+ list << doc
60
+ list << doc2
61
+
62
+ assert_equal 1, list.uniq.size
63
+ end
64
+ end
65
+
48
66
  def test_equality
49
67
  a = BSON::OrderedHash.new
50
68
  a['x'] = 1
@@ -76,6 +94,21 @@ class OrderedHashTest < Test::Unit::TestCase
76
94
  assert_equal @ordered_keys, @oh.keys
77
95
  end
78
96
 
97
+ def test_replace
98
+ h1 = BSON::OrderedHash.new
99
+ h1[:a] = 1
100
+ h1[:b] = 2
101
+
102
+ h2 = BSON::OrderedHash.new
103
+ h2[:c] = 3
104
+ h2[:d] = 4
105
+ h1.replace(h2)
106
+
107
+ assert_equal [:c, :d], h1.keys
108
+ assert_equal [3, 4], h1.values
109
+ assert h1.keys.object_id != h2.keys.object_id
110
+ end
111
+
79
112
  def test_to_a_order_preserved
80
113
  assert_equal @ordered_keys, @oh.to_a.map {|m| m.first}
81
114
  end
@@ -158,8 +191,10 @@ class OrderedHashTest < Test::Unit::TestCase
158
191
  assert_equal [1, 2, 3, 'foo'], noob.values
159
192
  end
160
193
 
161
- def test_inspect_retains_order
162
- assert_equal '{"c"=>1, "a"=>2, "z"=>3}', @oh.inspect
194
+ if RUBY_VERSION < "1.9.2"
195
+ def test_inspect_retains_order
196
+ assert_equal "#<BSON::OrderedHash:0x#{@oh.object_id.to_s(16)} {\"c\"=>1, \"a\"=>2, \"z\"=>3}>", @oh.inspect
197
+ end
163
198
  end
164
199
 
165
200
  def test_clear
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'bson')
2
+ require 'rubygems' if RUBY_VERSION < '1.9.0' && ENV['C_EXT']
3
+ require 'test/unit'
4
+
5
+ def silently
6
+ warn_level = $VERBOSE
7
+ $VERBOSE = nil
8
+ result = yield
9
+ $VERBOSE = warn_level
10
+ result
11
+ end
12
+
13
+ begin
14
+ require 'rubygems' if RUBY_VERSION < "1.9.0" && !ENV['C_EXT']
15
+ silently { require 'shoulda' }
16
+ silently { require 'mocha' }
17
+ rescue LoadError
18
+ puts <<MSG
19
+
20
+ This test suite requires shoulda and mocha.
21
+ You can install them as follows:
22
+ gem install shoulda
23
+ gem install mocha
24
+
25
+ MSG
26
+
27
+ exit
28
+ end
29
+
30
+ require 'bson_ext/cbson' if !(RUBY_PLATFORM =~ /java/) && ENV['C_EXT']
31
+
32
+ class Test::Unit::TestCase
33
+ include BSON
34
+
35
+ def assert_raise_error(klass, message)
36
+ begin
37
+ yield
38
+ rescue => e
39
+ assert_equal klass, e.class
40
+ assert e.message.include?(message), "#{e.message} does not include #{message}."
41
+ else
42
+ flunk "Expected assertion #{klass} but none was raised."
43
+ end
44
+ end
45
+
46
+ end
@@ -1,7 +1,6 @@
1
- require './test/test_helper'
1
+ require './test/bson/test_helper'
2
2
 
3
- class TiumestampTest < Test::Unit::TestCase
4
- include Mongo
3
+ class TimestampTest < Test::Unit::TestCase
5
4
 
6
5
  def test_timestamp_equality
7
6
  t1 = Timestamp.new(5000, 200)
@@ -9,16 +8,39 @@ class TiumestampTest < Test::Unit::TestCase
9
8
  assert_equal t2, t1
10
9
  end
11
10
 
11
+ def test_timestamp_range
12
+ t = 1;
13
+ while(t < 1_000_000_000 ) do
14
+ ts = Timestamp.new(t, 0)
15
+ doc = {:ts => ts}
16
+ bson = BSON::BSON_CODER.serialize(doc)
17
+ assert_equal doc[:ts], BSON::BSON_CODER.deserialize(bson)['ts']
18
+ t = t * 10
19
+ end
20
+ end
21
+
22
+ def test_timestamp_32bit_compatibility
23
+ max_32bit_fixnum = (1 << 30) - 1
24
+ test_val = max_32bit_fixnum + 10
25
+
26
+ ts = Timestamp.new(test_val, test_val)
27
+ doc = {:ts => ts}
28
+ bson = BSON::BSON_CODER.serialize(doc)
29
+ assert_equal doc[:ts], BSON::BSON_CODER.deserialize(bson)['ts']
30
+ end
31
+
12
32
  def test_implements_array_for_backward_compatibility
13
- ts = Timestamp.new(5000, 200)
14
- assert_equal 200, ts[0]
15
- assert_equal 5000, ts[1]
33
+ silently do
34
+ ts = Timestamp.new(5000, 200)
35
+ assert_equal 200, ts[0]
36
+ assert_equal 5000, ts[1]
16
37
 
17
- array = ts.map {|t| t }
18
- assert_equal 2, array.length
38
+ array = ts.map {|t| t }
39
+ assert_equal 2, array.length
19
40
 
20
- assert_equal 200, array[0]
21
- assert_equal 5000, array[1]
41
+ assert_equal 200, array[0]
42
+ assert_equal 5000, array[1]
43
+ end
22
44
  end
23
45
 
24
46
  end
@@ -1,7 +1,7 @@
1
1
  require './test/test_helper'
2
2
 
3
3
  class TestCollection < Test::Unit::TestCase
4
- @@connection ||= standard_connection(:op_timeout => 2)
4
+ @@connection ||= standard_connection(:op_timeout => 10)
5
5
  @@db = @@connection.db(MONGO_TEST_DB)
6
6
  @@test = @@db.collection("test")
7
7
  @@version = @@connection.server_version
@@ -10,6 +10,16 @@ class TestCollection < Test::Unit::TestCase
10
10
  @@test.remove
11
11
  end
12
12
 
13
+ def test_capped_method
14
+ @@db.create_collection('normal')
15
+ assert !@@db['normal'].capped?
16
+ @@db.drop_collection('normal')
17
+
18
+ @@db.create_collection('c', :capped => true, :size => 100_000)
19
+ assert @@db['c'].capped?
20
+ @@db.drop_collection('c')
21
+ end
22
+
13
23
  def test_optional_pk_factory
14
24
  @coll_default_pk = @@db.collection('stuff')
15
25
  assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
@@ -141,6 +151,35 @@ class TestCollection < Test::Unit::TestCase
141
151
  end
142
152
  end
143
153
 
154
+ def test_bulk_insert_with_continue_on_error
155
+ if @@version >= "2.0"
156
+ @@test.create_index([["foo", 1]], :unique => true)
157
+ docs = []
158
+ docs << {:foo => 1}
159
+ docs << {:foo => 1}
160
+ docs << {:foo => 2}
161
+ docs << {:foo => 3}
162
+ assert_raise OperationFailure do
163
+ @@test.insert(docs, :safe => true)
164
+ end
165
+ assert_equal 1, @@test.count
166
+ @@test.remove
167
+
168
+ docs = []
169
+ docs << {:foo => 1}
170
+ docs << {:foo => 1}
171
+ docs << {:foo => 2}
172
+ docs << {:foo => 3}
173
+ assert_raise OperationFailure do
174
+ @@test.insert(docs, :safe => true, :continue_on_error => true)
175
+ end
176
+ assert_equal 3, @@test.count
177
+
178
+ @@test.remove
179
+ @@test.drop_index("foo_1")
180
+ end
181
+ end
182
+
144
183
  def test_maximum_insert_size
145
184
  docs = []
146
185
  16.times do
@@ -275,9 +314,13 @@ class TestCollection < Test::Unit::TestCase
275
314
  @@test.drop
276
315
 
277
316
  assert_equal 0, @@test.count
278
- @@test.save("x" => 1)
279
- @@test.save("x" => 2)
317
+ @@test.save(:x => 1)
318
+ @@test.save(:x => 2)
280
319
  assert_equal 2, @@test.count
320
+
321
+ assert_equal 1, @@test.count(:query => {:x => 1})
322
+ assert_equal 1, @@test.count(:limit => 1)
323
+ assert_equal 0, @@test.count(:skip => 2)
281
324
  end
282
325
 
283
326
  # Note: #size is just an alias for #count.
@@ -762,6 +805,41 @@ class TestCollection < Test::Unit::TestCase
762
805
  assert_equal 1, @collection.size
763
806
  end
764
807
  end
808
+
809
+ context "Drop index " do
810
+ setup do
811
+ @@db.drop_collection('test-collection')
812
+ @collection = @@db.collection('test-collection')
813
+ end
814
+
815
+ should "drop an index" do
816
+ @collection.create_index([['a', Mongo::ASCENDING]])
817
+ assert @collection.index_information['a_1']
818
+ @collection.drop_index([['a', Mongo::ASCENDING]])
819
+ assert_nil @collection.index_information['a_1']
820
+ end
821
+
822
+ should "drop an index which was given a specific name" do
823
+ @collection.create_index([['a', Mongo::DESCENDING]], {:name => 'i_will_not_fear'})
824
+ assert @collection.index_information['i_will_not_fear']
825
+ @collection.drop_index([['a', Mongo::DESCENDING]])
826
+ assert_nil @collection.index_information['i_will_not_fear']
827
+ end
828
+
829
+ should "drops an composite index" do
830
+ @collection.create_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
831
+ assert @collection.index_information['a_-1_b_1']
832
+ @collection.drop_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
833
+ assert_nil @collection.index_information['a_-1_b_1']
834
+ end
835
+
836
+ should "drops an index with symbols" do
837
+ @collection.create_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
838
+ assert @collection.index_information['a_-1_b_1']
839
+ @collection.drop_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
840
+ assert_nil @collection.index_information['a_-1_b_1']
841
+ end
842
+ end
765
843
 
766
844
  context "Creating indexes " do
767
845
  setup do
@@ -809,6 +887,14 @@ class TestCollection < Test::Unit::TestCase
809
887
  assert_equal 1, @collection.find({:a => 1}).count
810
888
  end
811
889
 
890
+ should "drop duplicates with ensure_index and drop_dups key" do
891
+ @collection.insert({:a => 1})
892
+ @collection.insert({:a => 1})
893
+ assert_equal 2, @collection.find({:a => 1}).count
894
+ @collection.ensure_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
895
+ assert_equal 1, @collection.find({:a => 1}).count
896
+ end
897
+
812
898
  should "create an index in the background" do
813
899
  if @@version > '1.3.1'
814
900
  @collection.create_index([['b', Mongo::ASCENDING]], :background => true)