mongo 1.1.5 → 1.3.0

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 (76) hide show
  1. data/README.md +15 -15
  2. data/Rakefile +38 -17
  3. data/docs/FAQ.md +4 -0
  4. data/docs/HISTORY.md +59 -0
  5. data/docs/RELEASES.md +33 -0
  6. data/docs/REPLICA_SETS.md +13 -16
  7. data/lib/mongo/collection.rb +157 -69
  8. data/lib/mongo/connection.rb +189 -65
  9. data/lib/mongo/cursor.rb +43 -29
  10. data/lib/mongo/db.rb +63 -43
  11. data/lib/mongo/exceptions.rb +4 -1
  12. data/lib/mongo/gridfs/grid.rb +1 -1
  13. data/lib/mongo/gridfs/grid_ext.rb +1 -1
  14. data/lib/mongo/gridfs/grid_file_system.rb +1 -1
  15. data/lib/mongo/gridfs/grid_io.rb +89 -8
  16. data/lib/mongo/gridfs/grid_io_fix.rb +1 -1
  17. data/lib/mongo/repl_set_connection.rb +72 -20
  18. data/lib/mongo/test.rb +20 -0
  19. data/lib/mongo/util/conversions.rb +1 -1
  20. data/lib/mongo/util/core_ext.rb +11 -1
  21. data/lib/mongo/util/pool.rb +67 -15
  22. data/lib/mongo/util/server_version.rb +1 -1
  23. data/lib/mongo/util/support.rb +1 -1
  24. data/lib/mongo/util/uri_parser.rb +127 -13
  25. data/lib/mongo.rb +38 -2
  26. data/test/async/collection_test.rb +224 -0
  27. data/test/async/connection_test.rb +24 -0
  28. data/test/async/cursor_test.rb +162 -0
  29. data/test/async/worker_pool_test.rb +99 -0
  30. data/test/auxillary/fork_test.rb +30 -0
  31. data/test/auxillary/repl_set_auth_test.rb +58 -0
  32. data/test/auxillary/threaded_authentication_test.rb +101 -0
  33. data/test/bson/bson_test.rb +140 -28
  34. data/test/bson/byte_buffer_test.rb +18 -0
  35. data/test/bson/object_id_test.rb +14 -1
  36. data/test/bson/ordered_hash_test.rb +7 -0
  37. data/test/bson/timestamp_test.rb +24 -0
  38. data/test/collection_test.rb +104 -15
  39. data/test/connection_test.rb +78 -2
  40. data/test/conversions_test.rb +10 -11
  41. data/test/cursor_fail_test.rb +1 -1
  42. data/test/cursor_message_test.rb +1 -1
  43. data/test/cursor_test.rb +33 -4
  44. data/test/db_api_test.rb +30 -52
  45. data/test/db_test.rb +3 -3
  46. data/test/grid_file_system_test.rb +0 -1
  47. data/test/grid_io_test.rb +72 -1
  48. data/test/grid_test.rb +16 -16
  49. data/test/load/resque/load.rb +21 -0
  50. data/test/load/resque/processor.rb +26 -0
  51. data/test/load/thin/load.rb +24 -0
  52. data/test/load/unicorn/load.rb +23 -0
  53. data/test/load/unicorn/unicorn.rb +29 -0
  54. data/test/replica_sets/connect_test.rb +11 -1
  55. data/test/replica_sets/connection_string_test.rb +32 -0
  56. data/test/replica_sets/query_secondaries.rb +16 -0
  57. data/test/replica_sets/query_test.rb +10 -0
  58. data/test/replica_sets/replication_ack_test.rb +2 -0
  59. data/test/replica_sets/rs_test_helper.rb +9 -11
  60. data/test/support/hash_with_indifferent_access.rb +0 -13
  61. data/test/support_test.rb +0 -1
  62. data/test/test_helper.rb +27 -8
  63. data/test/tools/auth_repl_set_manager.rb +14 -0
  64. data/test/tools/load.rb +58 -0
  65. data/test/tools/repl_set_manager.rb +34 -9
  66. data/test/tools/sharding_manager.rb +202 -0
  67. data/test/tools/test.rb +3 -12
  68. data/test/unit/collection_test.rb +20 -24
  69. data/test/unit/connection_test.rb +4 -18
  70. data/test/unit/cursor_test.rb +16 -6
  71. data/test/unit/db_test.rb +10 -11
  72. data/test/unit/repl_set_connection_test.rb +0 -23
  73. data/test/unit/safe_test.rb +3 -3
  74. data/test/uri_test.rb +91 -0
  75. metadata +49 -12
  76. data/docs/1.0_UPGRADE.md +0 -21
@@ -1,10 +1,14 @@
1
1
  # encoding:utf-8
2
2
  require './test/test_helper'
3
- require 'complex'
3
+
4
+ if RUBY_VERSION < '1.9'
5
+ require 'complex'
6
+ require 'rational'
7
+ end
4
8
  require 'bigdecimal'
5
- require 'rational'
6
9
 
7
10
  begin
11
+ require 'tzinfo'
8
12
  require 'active_support/core_ext'
9
13
  Time.zone = "Pacific Time (US & Canada)"
10
14
  Zone = Time.zone.now
@@ -67,13 +71,26 @@ class BSONTest < Test::Unit::TestCase
67
71
  assert_doc_pass(doc)
68
72
  end
69
73
 
70
- def test_document_length
71
- doc = {'name' => 'a' * 5 * 1024 * 1024}
74
+ def test_limit_max_bson_size
75
+ doc = {'name' => 'a' * BSON_CODER.max_bson_size}
72
76
  assert_raise InvalidDocument do
73
77
  assert @encoder.serialize(doc)
74
78
  end
75
79
  end
76
80
 
81
+ def test_max_bson_size
82
+ assert BSON_CODER.max_bson_size >= BSON::DEFAULT_MAX_BSON_SIZE
83
+ end
84
+
85
+ def test_update_max_bson_size
86
+ require 'ostruct'
87
+ mock_conn = OpenStruct.new
88
+ size = 7 * 1024 * 1024
89
+ mock_conn.max_bson_size = size
90
+ assert_equal size, BSON_CODER.update_max_bson_size(mock_conn)
91
+ assert_equal size, BSON_CODER.max_bson_size
92
+ end
93
+
77
94
  def test_round_trip
78
95
  doc = {'doc' => 123}
79
96
  @encoder.deserialize(@encoder.serialize(doc))
@@ -103,15 +120,23 @@ class BSONTest < Test::Unit::TestCase
103
120
  end
104
121
  else
105
122
  def test_non_utf8_string
106
- bson = BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
107
- result = BSON::BSON_CODER.deserialize(bson)['str']
108
- assert_equal 'aé', result
109
- assert_equal 'UTF-8', result.encoding.name
123
+ assert_raise BSON::InvalidStringEncoding do
124
+ BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
125
+ end
126
+ end
127
+
128
+ def test_invalid_utf8_string
129
+ str = "123\xD9"
130
+ assert !str.valid_encoding?
131
+ assert_raise BSON::InvalidStringEncoding do
132
+ BSON::BSON_CODER.serialize({'str' => str})
133
+ end
110
134
  end
111
135
 
112
136
  def test_non_utf8_key
113
- bson = BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
114
- assert_equal 'hello', BSON::BSON_CODER.deserialize(bson)['aé']
137
+ assert_raise BSON::InvalidStringEncoding do
138
+ BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
139
+ end
115
140
  end
116
141
 
117
142
  # Based on a test from sqlite3-ruby
@@ -121,14 +146,14 @@ class BSONTest < Test::Unit::TestCase
121
146
  str = "壁に耳あり、障子に目あり"
122
147
  bson = BSON::BSON_CODER.serialize("x" => str)
123
148
 
124
- Encoding.default_internal = 'EUC-JP'
149
+ silently { Encoding.default_internal = 'EUC-JP' }
125
150
  out = BSON::BSON_CODER.deserialize(bson)["x"]
126
151
 
127
152
  assert_equal Encoding.default_internal, out.encoding
128
153
  assert_equal str.encode('EUC-JP'), out
129
154
  assert_equal str, out.encode(str.encoding)
130
155
  ensure
131
- Encoding.default_internal = before_enc
156
+ silently { Encoding.default_internal = before_enc }
132
157
  end
133
158
  end
134
159
 
@@ -137,17 +162,23 @@ class BSONTest < Test::Unit::TestCase
137
162
  assert_doc_pass(doc)
138
163
  end
139
164
 
165
+ def test_code_with_symbol
166
+ assert_raise_error ArgumentError, "BSON::Code must be in the form of a String" do
167
+ Code.new(:fubar)
168
+ end
169
+ end
170
+
140
171
  def test_code_with_scope
141
172
  doc = {'$where' => Code.new('this.a.b < this.b', {'foo' => 1})}
142
173
  assert_doc_pass(doc)
143
174
  end
144
175
 
145
- def test_double
146
- doc = {'doc' => 41.25}
147
- assert_doc_pass(doc)
148
- end
176
+ def test_double
177
+ doc = {'doc' => 41.25}
178
+ assert_doc_pass(doc)
179
+ end
149
180
 
150
- def test_int
181
+ def test_int
151
182
  doc = {'doc' => 42}
152
183
  assert_doc_pass(doc)
153
184
 
@@ -200,6 +231,15 @@ class BSONTest < Test::Unit::TestCase
200
231
  assert_doc_pass(doc)
201
232
  end
202
233
 
234
+ def test_array_keys
235
+ doc = {'doc' => [1, 2, 'a', 'b']}
236
+ bson = @encoder.serialize(doc).to_a
237
+ assert_equal 48, bson[14]
238
+ assert_equal 49, bson[21]
239
+ assert_equal 50, bson[28]
240
+ assert_equal 51, bson[37]
241
+ end
242
+
203
243
  def test_regex
204
244
  doc = {'doc' => /foobar/i}
205
245
  assert_doc_pass(doc)
@@ -218,8 +258,14 @@ class BSONTest < Test::Unit::TestCase
218
258
  assert_in_delta doc['date'], doc2['date'], 0.001
219
259
  end
220
260
 
261
+ def test_date_in_array
262
+ doc = {'date' => [Time.now.utc]}
263
+ bson = @encoder.serialize(doc)
264
+ doc2 = @encoder.deserialize(bson)
265
+ end
266
+
221
267
  def test_date_returns_as_utc
222
- doc = {'date' => Time.now}
268
+ doc = {'date' => Time.now.utc}
223
269
  bson = @encoder.serialize(doc)
224
270
  doc2 = @encoder.deserialize(bson)
225
271
  assert doc2['date'].utc?
@@ -249,7 +295,7 @@ class BSONTest < Test::Unit::TestCase
249
295
  ensure
250
296
  if !invalid_date.is_a? Time
251
297
  assert_equal InvalidDocument, e.class
252
- assert_match /UTC Time/, e.message
298
+ assert_match(/UTC Time/, e.message)
253
299
  end
254
300
  end
255
301
  end
@@ -283,6 +329,20 @@ class BSONTest < Test::Unit::TestCase
283
329
  bin = Binary.new
284
330
  'binstring'.each_byte { |b| bin.put(b) }
285
331
 
332
+ doc = {'bin' => bin}
333
+ bson = @encoder.serialize(doc)
334
+ doc2 = @encoder.deserialize(bson)
335
+ bin2 = doc2['bin']
336
+ assert_kind_of Binary, bin2
337
+ assert_equal 'binstring', bin2.to_s
338
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
339
+ end
340
+
341
+ def test_binary_with_deprecated_subtype
342
+ bin = Binary.new
343
+ 'binstring'.each_byte { |b| bin.put(b) }
344
+ bin.subtype = Binary::SUBTYPE_BYTES
345
+
286
346
  doc = {'bin' => bin}
287
347
  bson = @encoder.serialize(doc)
288
348
  doc2 = @encoder.deserialize(bson)
@@ -300,7 +360,7 @@ class BSONTest < Test::Unit::TestCase
300
360
  bin2 = doc2['bin']
301
361
  assert_kind_of Binary, bin2
302
362
  assert_equal 'somebinarystring', bin2.to_s
303
- assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
363
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
304
364
  end
305
365
 
306
366
  def test_binary_type
@@ -340,7 +400,7 @@ class BSONTest < Test::Unit::TestCase
340
400
  bin2 = doc2['bin']
341
401
  assert_kind_of Binary, bin2
342
402
  assert_equal [1, 2, 3, 4, 5], bin2.to_a
343
- assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
403
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
344
404
  end
345
405
 
346
406
  def test_put_id_first
@@ -365,15 +425,24 @@ class BSONTest < Test::Unit::TestCase
365
425
  if !(RUBY_PLATFORM =~ /java/)
366
426
  def test_timestamp
367
427
  val = {"test" => [4, 20]}
368
- assert_equal val, @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
369
- 0x11, 0x74, 0x65, 0x73,
370
- 0x74, 0x00, 0x04, 0x00,
371
- 0x00, 0x00, 0x14, 0x00,
372
- 0x00, 0x00, 0x00])
373
-
428
+ result = @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
429
+ 0x11, 0x74, 0x65, 0x73,
430
+ 0x74, 0x00, 0x04, 0x00,
431
+ 0x00, 0x00, 0x14, 0x00,
432
+ 0x00, 0x00, 0x00])
433
+
434
+ assert_equal 4, result["test"][0]
435
+ assert_equal 20, result["test"][1]
374
436
  end
375
437
  end
376
438
 
439
+ def test_timestamp_type
440
+ ts = Timestamp.new(5000, 100)
441
+ doc = {:ts => ts}
442
+ bson = @encoder.serialize(doc)
443
+ assert_equal ts, @encoder.deserialize(bson)["ts"]
444
+ end
445
+
377
446
  def test_overflow
378
447
  doc = {"x" => 2**75}
379
448
  assert_raise RangeError do
@@ -411,7 +480,7 @@ class BSONTest < Test::Unit::TestCase
411
480
  rescue => e
412
481
  ensure
413
482
  assert_equal InvalidDocument, e.class
414
- assert_match /Cannot serialize/, e.message
483
+ assert_match(/Cannot serialize/, e.message)
415
484
  end
416
485
  end
417
486
  end
@@ -534,4 +603,47 @@ class BSONTest < Test::Unit::TestCase
534
603
  end
535
604
  end
536
605
 
606
+ def test_invalid_key_names
607
+ assert @encoder.serialize({"hello" => "world"}, true)
608
+ assert @encoder.serialize({"hello" => {"hello" => "world"}}, true)
609
+
610
+ assert @encoder.serialize({"he$llo" => "world"}, true)
611
+ assert @encoder.serialize({"hello" => {"hell$o" => "world"}}, true)
612
+
613
+ assert_raise BSON::InvalidDocument do
614
+ @encoder.serialize({"he\0llo" => "world"}, true)
615
+ end
616
+
617
+ assert_raise BSON::InvalidKeyName do
618
+ @encoder.serialize({"$hello" => "world"}, true)
619
+ end
620
+
621
+ assert_raise BSON::InvalidKeyName do
622
+ @encoder.serialize({"hello" => {"$hello" => "world"}}, true)
623
+ end
624
+
625
+ assert_raise BSON::InvalidKeyName do
626
+ @encoder.serialize({".hello" => "world"}, true)
627
+ end
628
+
629
+ assert_raise BSON::InvalidKeyName do
630
+ @encoder.serialize({"hello" => {".hello" => "world"}}, true)
631
+ end
632
+
633
+ assert_raise BSON::InvalidKeyName do
634
+ @encoder.serialize({"hello." => "world"}, true)
635
+ end
636
+
637
+ assert_raise BSON::InvalidKeyName do
638
+ @encoder.serialize({"hello" => {"hello." => "world"}}, true)
639
+ end
640
+
641
+ assert_raise BSON::InvalidKeyName do
642
+ @encoder.serialize({"hel.lo" => "world"}, true)
643
+ end
644
+
645
+ assert_raise BSON::InvalidKeyName do
646
+ @encoder.serialize({"hello" => {"hel.lo" => "world"}}, true)
647
+ end
648
+ end
537
649
  end
@@ -21,6 +21,13 @@ class ByteBufferTest < Test::Unit::TestCase
21
21
  assert_equal 1, @buf.get
22
22
  end
23
23
 
24
+ def test_unpack
25
+ @buf.put_array([17, 2, 3, 4])
26
+ assert_equal [17, 2, 3, 4], @buf.to_a
27
+ assert_equal ["11020304"], @buf.unpack("H*")
28
+ assert_equal ["11020304"], @buf.to_a("H*")
29
+ end
30
+
24
31
  def test_one_get_returns_array_length_one
25
32
  @buf.put_array([1, 2, 3, 4])
26
33
  @buf.rewind
@@ -186,5 +193,16 @@ class ByteBufferTest < Test::Unit::TestCase
186
193
  @buf.get_int
187
194
  assert !@buf.more?
188
195
  end
196
+
197
+ def test_equality
198
+ @buf = ByteBuffer.new("foo")
199
+ assert_equal @buf, @buf
200
+ assert_equal ByteBuffer.new(""), ByteBuffer.new("")
201
+ assert_equal ByteBuffer.new("123"), ByteBuffer.new("123")
202
+ assert_not_equal ByteBuffer.new("123"), ByteBuffer.new("1234")
203
+ assert_equal @buf, "foo"
204
+ assert_not_equal @buf, 123
205
+ assert_not_equal @buf, nil
206
+ end
189
207
 
190
208
  end
@@ -126,14 +126,27 @@ class ObjectIdTest < Test::Unit::TestCase
126
126
  time = Time.now.utc
127
127
  id = ObjectId.from_time(time)
128
128
 
129
+ assert id.to_a[4, 8].all? {|byte| byte == 0 }
129
130
  assert_equal time.to_i, id.generation_time.to_i
130
131
  end
131
132
 
133
+ def test_from_time_unique
134
+ time = Time.now.utc
135
+ id = ObjectId.from_time(time, :unique => true)
136
+
137
+ mac_id = Digest::MD5.digest(Socket.gethostname)[0, 3].unpack("C3")
138
+ assert_equal id.to_a[4, 3], mac_id
139
+ assert_equal time.to_i, id.generation_time.to_i
140
+
141
+ id2 = ObjectId.new(nil, time)
142
+ assert_equal time.to_i, id2.generation_time.to_i
143
+ end
144
+
132
145
  def test_json
133
146
  id = ObjectId.new
134
147
  assert_equal "{\"$oid\": \"#{id}\"}", id.to_json
135
148
  end
136
-
149
+
137
150
  def test_as_json
138
151
  id = ObjectId.new
139
152
  assert_equal({"$oid" => id.to_s}, id.as_json)
@@ -194,4 +194,11 @@ class OrderedHashTest < Test::Unit::TestCase
194
194
  copy[:foo] = 1
195
195
  assert copy.keys != @oh.keys
196
196
  end
197
+
198
+ def test_dup
199
+ oh2 = @oh.dup
200
+ oh2['f'] = 9
201
+ assert_nil @oh['f']
202
+ assert_equal ['c', 'a', 'z'], @oh.keys
203
+ end
197
204
  end
@@ -0,0 +1,24 @@
1
+ require './test/test_helper'
2
+
3
+ class TiumestampTest < Test::Unit::TestCase
4
+ include Mongo
5
+
6
+ def test_timestamp_equality
7
+ t1 = Timestamp.new(5000, 200)
8
+ t2 = Timestamp.new(5000, 200)
9
+ assert_equal t2, t1
10
+ end
11
+
12
+ 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]
16
+
17
+ array = ts.map {|t| t }
18
+ assert_equal 2, array.length
19
+
20
+ assert_equal 200, array[0]
21
+ assert_equal 5000, array[1]
22
+ end
23
+
24
+ end
@@ -1,7 +1,7 @@
1
1
  require './test/test_helper'
2
2
 
3
3
  class TestCollection < Test::Unit::TestCase
4
- @@connection ||= standard_connection
4
+ @@connection ||= standard_connection(:op_timeout => 2)
5
5
  @@db = @@connection.db(MONGO_TEST_DB)
6
6
  @@test = @@db.collection("test")
7
7
  @@version = @@connection.server_version
@@ -32,11 +32,11 @@ class TestCollection < Test::Unit::TestCase
32
32
  end
33
33
 
34
34
  def test_pk_factory_on_collection
35
- @coll = Collection.new(@@db, 'foo', TestPK)
35
+ @coll = Collection.new('foo', @@db, TestPK)
36
36
  assert_equal TestPK, @coll.pk_factory
37
37
 
38
38
 
39
- @coll2 = Collection.new(@@db, 'foo', :pk => TestPK)
39
+ @coll2 = Collection.new('foo', @@db, :pk => TestPK)
40
40
  assert_equal TestPK, @coll2.pk_factory
41
41
  end
42
42
 
@@ -373,8 +373,8 @@ class TestCollection < Test::Unit::TestCase
373
373
 
374
374
  docs = [{"hello" => "world"}, {"hello" => "world"}]
375
375
  @@test.insert(docs)
376
- docs.each do |doc|
377
- assert(doc.include?(:_id))
376
+ docs.each do |d|
377
+ assert(d.include?(:_id))
378
378
  end
379
379
  end
380
380
 
@@ -417,7 +417,7 @@ class TestCollection < Test::Unit::TestCase
417
417
 
418
418
  m = "function() { emit(this.user_id, 1); }"
419
419
  r = "function(k,vals) { return 1; }"
420
- res = @@test.map_reduce(m, r);
420
+ res = @@test.map_reduce(m, r, :out => 'foo');
421
421
  assert res.find_one({"_id" => 1})
422
422
  assert res.find_one({"_id" => 2})
423
423
  end
@@ -428,7 +428,7 @@ class TestCollection < Test::Unit::TestCase
428
428
 
429
429
  m = Code.new("function() { emit(this.user_id, 1); }")
430
430
  r = Code.new("function(k,vals) { return 1; }")
431
- res = @@test.map_reduce(m, r);
431
+ res = @@test.map_reduce(m, r, :out => 'foo');
432
432
  assert res.find_one({"_id" => 1})
433
433
  assert res.find_one({"_id" => 2})
434
434
  end
@@ -441,7 +441,7 @@ class TestCollection < Test::Unit::TestCase
441
441
 
442
442
  m = Code.new("function() { emit(this.user_id, 1); }")
443
443
  r = Code.new("function(k,vals) { return 1; }")
444
- res = @@test.map_reduce(m, r, :query => {"user_id" => {"$gt" => 1}});
444
+ res = @@test.map_reduce(m, r, :query => {"user_id" => {"$gt" => 1}}, :out => 'foo');
445
445
  assert_equal 2, res.count
446
446
  assert res.find_one({"_id" => 2})
447
447
  assert res.find_one({"_id" => 3})
@@ -450,7 +450,7 @@ class TestCollection < Test::Unit::TestCase
450
450
  def test_map_reduce_with_raw_response
451
451
  m = Code.new("function() { emit(this.user_id, 1); }")
452
452
  r = Code.new("function(k,vals) { return 1; }")
453
- res = @@test.map_reduce(m, r, :raw => true)
453
+ res = @@test.map_reduce(m, r, :raw => true, :out => 'foo')
454
454
  assert res["result"]
455
455
  assert res["counts"]
456
456
  assert res["timeMillis"]
@@ -465,6 +465,36 @@ class TestCollection < Test::Unit::TestCase
465
465
  assert res["counts"]
466
466
  assert res["timeMillis"]
467
467
  end
468
+
469
+
470
+ if @@version >= "1.8.0"
471
+ def test_map_reduce_with_collection_merge
472
+ @@test << {:user_id => 1}
473
+ @@test << {:user_id => 2}
474
+ output_collection = "test-map-coll"
475
+ m = Code.new("function() { emit(this.user_id, {count: 1}); }")
476
+ r = Code.new("function(k,vals) { var sum = 0;" +
477
+ " vals.forEach(function(v) { sum += v.count;} ); return {count: sum}; }")
478
+ res = @@test.map_reduce(m, r, :out => output_collection)
479
+
480
+ @@test.remove
481
+ @@test << {:user_id => 3}
482
+ res = @@test.map_reduce(m, r, :out => {:merge => output_collection})
483
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 1}
484
+
485
+ @@test.remove
486
+ @@test << {:user_id => 3}
487
+ res = @@test.map_reduce(m, r, :out => {:reduce => output_collection})
488
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 2}
489
+
490
+ assert_raise ArgumentError do
491
+ @@test.map_reduce(m, r, :out => {:inline => 1})
492
+ end
493
+
494
+ @@test.map_reduce(m, r, :raw => true, :out => {:inline => 1})
495
+ assert res["results"]
496
+ end
497
+ end
468
498
  end
469
499
 
470
500
  if @@version > "1.3.0"
@@ -565,6 +595,20 @@ class TestCollection < Test::Unit::TestCase
565
595
  assert_equal 1, x
566
596
  end
567
597
 
598
+ def test_find_with_transformer
599
+ klass = Struct.new(:id, :a)
600
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
601
+ cursor = @@test.find({}, :transformer => transformer)
602
+ assert_equal(transformer, cursor.transformer)
603
+ end
604
+
605
+ def test_find_one_with_transformer
606
+ klass = Struct.new(:id, :a)
607
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
608
+ id = @@test.insert('a' => 1)
609
+ doc = @@test.find_one(id, :transformer => transformer)
610
+ assert_instance_of(klass, doc)
611
+ end
568
612
 
569
613
  def test_ensure_index
570
614
  @@test.drop_indexes
@@ -600,6 +644,23 @@ class TestCollection < Test::Unit::TestCase
600
644
  @@test.drop_index("a_1")
601
645
  end
602
646
 
647
+ def test_ensure_index_timeout
648
+ @@db.cache_time = 2
649
+ coll = @@db['ensure_test']
650
+ coll.expects(:generate_indexes).twice
651
+ coll.ensure_index([['a', 1]])
652
+
653
+ # These will be cached
654
+ coll.ensure_index([['a', 1]])
655
+ coll.ensure_index([['a', 1]])
656
+ coll.ensure_index([['a', 1]])
657
+ coll.ensure_index([['a', 1]])
658
+
659
+ sleep(3)
660
+ # This won't be, so generate_indexes will be called twice
661
+ coll.ensure_index([['a', 1]])
662
+ end
663
+
603
664
  context "Grouping" do
604
665
  setup do
605
666
  @@test.remove
@@ -609,15 +670,25 @@ class TestCollection < Test::Unit::TestCase
609
670
  @reduce_function = "function (obj, prev) { prev.count += inc_value; }"
610
671
  end
611
672
 
673
+ should "fail if missing required options" do
674
+ assert_raise MongoArgumentError do
675
+ @@test.group(:initial => {})
676
+ end
677
+
678
+ assert_raise MongoArgumentError do
679
+ @@test.group(:reduce => "foo")
680
+ end
681
+ end
682
+
612
683
  should "group results using eval form" do
613
- assert_equal 1, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 0.5}))[0]["count"]
614
- assert_equal 2, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 1}))[0]["count"]
615
- assert_equal 4, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 2}))[0]["count"]
684
+ assert_equal 1, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 0.5}))[0]["count"]
685
+ assert_equal 2, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}))[0]["count"]
686
+ assert_equal 4, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 2}))[0]["count"]
616
687
  end
617
688
 
618
689
  should "finalize grouped results" do
619
690
  @finalize = "function(doc) {doc.f = doc.count + 200; }"
620
- assert_equal 202, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 1}), @finalize)[0]["f"]
691
+ assert_equal 202, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}), :finalize => @finalize)[0]["f"]
621
692
  end
622
693
  end
623
694
 
@@ -633,7 +704,7 @@ class TestCollection < Test::Unit::TestCase
633
704
  end
634
705
 
635
706
  should "group" do
636
- result = @@test.group([:a], {}, @initial, @reduce_function, nil)
707
+ result = @@test.group(:key => :a, :initial => @initial, :reduce => @reduce_function)
637
708
  assert result.all? { |r| r['count'] == 200 }
638
709
  end
639
710
  end
@@ -652,10 +723,17 @@ class TestCollection < Test::Unit::TestCase
652
723
  end
653
724
 
654
725
  should "group results" do
655
- results = @@test.group(@keyf, {}, @initial, @reduce).sort {|a, b| a['count'] <=> b['count']}
726
+ results = @@test.group(:keyf => @keyf, :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
656
727
  assert results[0]['even'] && results[0]['count'] == 2.0
657
728
  assert results[1]['odd'] && results[1]['count'] == 3.0
658
729
  end
730
+
731
+ should "group filtered results" do
732
+ results = @@test.group(:keyf => @keyf, :cond => {:a => {'$ne' => 2}},
733
+ :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
734
+ assert results[0]['even'] && results[0]['count'] == 1.0
735
+ assert results[1]['odd'] && results[1]['count'] == 3.0
736
+ end
659
737
  end
660
738
 
661
739
  context "A collection with two records" do
@@ -693,6 +771,17 @@ class TestCollection < Test::Unit::TestCase
693
771
  @geo = @@db.collection('geo')
694
772
  end
695
773
 
774
+ should "create index using symbols" do
775
+ @collection.create_index :foo, :name => :bar
776
+ @geo.create_index :goo, :name => :baz
777
+ assert @collection.index_information['bar']
778
+ @collection.drop_index :bar
779
+ assert_nil @collection.index_information['bar']
780
+ assert @geo.index_information['baz']
781
+ @geo.drop_index(:baz)
782
+ assert_nil @geo.index_information['baz']
783
+ end
784
+
696
785
  should "create a geospatial index" do
697
786
  @geo.save({'loc' => [-100, 100]})
698
787
  @geo.create_index([['loc', Mongo::GEO2D]])