mongo-lyon 1.2.4

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 (87) hide show
  1. data/LICENSE.txt +190 -0
  2. data/README.md +344 -0
  3. data/Rakefile +202 -0
  4. data/bin/mongo_console +34 -0
  5. data/docs/1.0_UPGRADE.md +21 -0
  6. data/docs/CREDITS.md +123 -0
  7. data/docs/FAQ.md +116 -0
  8. data/docs/GridFS.md +158 -0
  9. data/docs/HISTORY.md +225 -0
  10. data/docs/REPLICA_SETS.md +72 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo.rb +77 -0
  14. data/lib/mongo/collection.rb +872 -0
  15. data/lib/mongo/connection.rb +875 -0
  16. data/lib/mongo/cursor.rb +449 -0
  17. data/lib/mongo/db.rb +607 -0
  18. data/lib/mongo/exceptions.rb +68 -0
  19. data/lib/mongo/gridfs/grid.rb +106 -0
  20. data/lib/mongo/gridfs/grid_ext.rb +57 -0
  21. data/lib/mongo/gridfs/grid_file_system.rb +145 -0
  22. data/lib/mongo/gridfs/grid_io.rb +394 -0
  23. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  24. data/lib/mongo/repl_set_connection.rb +342 -0
  25. data/lib/mongo/util/conversions.rb +89 -0
  26. data/lib/mongo/util/core_ext.rb +60 -0
  27. data/lib/mongo/util/pool.rb +185 -0
  28. data/lib/mongo/util/server_version.rb +71 -0
  29. data/lib/mongo/util/support.rb +82 -0
  30. data/lib/mongo/util/uri_parser.rb +181 -0
  31. data/lib/mongo/version.rb +3 -0
  32. data/mongo.gemspec +34 -0
  33. data/test/auxillary/1.4_features.rb +166 -0
  34. data/test/auxillary/authentication_test.rb +68 -0
  35. data/test/auxillary/autoreconnect_test.rb +41 -0
  36. data/test/auxillary/repl_set_auth_test.rb +58 -0
  37. data/test/auxillary/slave_connection_test.rb +36 -0
  38. data/test/auxillary/threaded_authentication_test.rb +101 -0
  39. data/test/bson/binary_test.rb +15 -0
  40. data/test/bson/bson_test.rb +614 -0
  41. data/test/bson/byte_buffer_test.rb +190 -0
  42. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  43. data/test/bson/json_test.rb +17 -0
  44. data/test/bson/object_id_test.rb +154 -0
  45. data/test/bson/ordered_hash_test.rb +197 -0
  46. data/test/collection_test.rb +893 -0
  47. data/test/connection_test.rb +303 -0
  48. data/test/conversions_test.rb +120 -0
  49. data/test/cursor_fail_test.rb +75 -0
  50. data/test/cursor_message_test.rb +43 -0
  51. data/test/cursor_test.rb +457 -0
  52. data/test/db_api_test.rb +715 -0
  53. data/test/db_connection_test.rb +15 -0
  54. data/test/db_test.rb +287 -0
  55. data/test/grid_file_system_test.rb +244 -0
  56. data/test/grid_io_test.rb +120 -0
  57. data/test/grid_test.rb +200 -0
  58. data/test/load/thin/load.rb +24 -0
  59. data/test/load/unicorn/load.rb +23 -0
  60. data/test/replica_sets/connect_test.rb +86 -0
  61. data/test/replica_sets/connection_string_test.rb +32 -0
  62. data/test/replica_sets/count_test.rb +35 -0
  63. data/test/replica_sets/insert_test.rb +53 -0
  64. data/test/replica_sets/pooled_insert_test.rb +55 -0
  65. data/test/replica_sets/query_secondaries.rb +96 -0
  66. data/test/replica_sets/query_test.rb +51 -0
  67. data/test/replica_sets/replication_ack_test.rb +66 -0
  68. data/test/replica_sets/rs_test_helper.rb +27 -0
  69. data/test/safe_test.rb +68 -0
  70. data/test/support/hash_with_indifferent_access.rb +199 -0
  71. data/test/support/keys.rb +45 -0
  72. data/test/support_test.rb +19 -0
  73. data/test/test_helper.rb +83 -0
  74. data/test/threading/threading_with_large_pool_test.rb +90 -0
  75. data/test/threading_test.rb +87 -0
  76. data/test/tools/auth_repl_set_manager.rb +14 -0
  77. data/test/tools/repl_set_manager.rb +266 -0
  78. data/test/unit/collection_test.rb +130 -0
  79. data/test/unit/connection_test.rb +98 -0
  80. data/test/unit/cursor_test.rb +99 -0
  81. data/test/unit/db_test.rb +96 -0
  82. data/test/unit/grid_test.rb +49 -0
  83. data/test/unit/pool_test.rb +9 -0
  84. data/test/unit/repl_set_connection_test.rb +72 -0
  85. data/test/unit/safe_test.rb +125 -0
  86. data/test/uri_test.rb +91 -0
  87. metadata +202 -0
@@ -0,0 +1,893 @@
1
+ require './test/test_helper'
2
+
3
+ class TestCollection < Test::Unit::TestCase
4
+ @@connection ||= standard_connection
5
+ @@db = @@connection.db(MONGO_TEST_DB)
6
+ @@test = @@db.collection("test")
7
+ @@version = @@connection.server_version
8
+
9
+ def setup
10
+ @@test.remove
11
+ end
12
+
13
+ def test_optional_pk_factory
14
+ @coll_default_pk = @@db.collection('stuff')
15
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
16
+ @coll_default_pk = @@db.create_collection('more-stuff')
17
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
18
+
19
+ # Create a db with a pk_factory.
20
+ @db = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
21
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db(MONGO_TEST_DB, :pk => Object.new)
22
+ @coll = @db.collection('coll-with-pk')
23
+ assert @coll.pk_factory.is_a?(Object)
24
+
25
+ @coll = @db.create_collection('created_coll_with_pk')
26
+ assert @coll.pk_factory.is_a?(Object)
27
+ end
28
+
29
+ class TestPK
30
+ def self.create_pk
31
+ end
32
+ end
33
+
34
+ def test_pk_factory_on_collection
35
+ @coll = Collection.new('foo', @@db, TestPK)
36
+ assert_equal TestPK, @coll.pk_factory
37
+
38
+
39
+ @coll2 = Collection.new('foo', @@db, :pk => TestPK)
40
+ assert_equal TestPK, @coll2.pk_factory
41
+ end
42
+
43
+ def test_valid_names
44
+ assert_raise Mongo::InvalidNSName do
45
+ @@db["te$t"]
46
+ end
47
+
48
+ assert_raise Mongo::InvalidNSName do
49
+ @@db['$main']
50
+ end
51
+
52
+ assert @@db['$cmd']
53
+ assert @@db['oplog.$main']
54
+ end
55
+
56
+ def test_collection
57
+ assert_kind_of Collection, @@db["test"]
58
+ assert_equal @@db["test"].name(), @@db.collection("test").name()
59
+ assert_equal @@db["test"].name(), @@db[:test].name()
60
+
61
+ assert_kind_of Collection, @@db["test"]["foo"]
62
+ assert_equal @@db["test"]["foo"].name(), @@db.collection("test.foo").name()
63
+ assert_equal @@db["test"]["foo"].name(), @@db["test.foo"].name()
64
+
65
+ @@db["test"]["foo"].remove
66
+ @@db["test"]["foo"].insert("x" => 5)
67
+ assert_equal 5, @@db.collection("test.foo").find_one()["x"]
68
+ end
69
+
70
+ def test_rename_collection
71
+ @@db.drop_collection('foo1')
72
+ @@db.drop_collection('bar1')
73
+
74
+ @col = @@db.create_collection('foo1')
75
+ assert_equal 'foo1', @col.name
76
+
77
+ @col.rename('bar1')
78
+ assert_equal 'bar1', @col.name
79
+ end
80
+
81
+ def test_nil_id
82
+ assert_equal 5, @@test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
83
+ assert_equal 5, @@test.save({"_id" => 5, "foo" => "baz"}, {:safe => true})
84
+ assert_equal nil, @@test.find_one("foo" => "bar")
85
+ assert_equal "baz", @@test.find_one(:_id => 5)["foo"]
86
+ assert_raise OperationFailure do
87
+ @@test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
88
+ end
89
+
90
+ assert_equal nil, @@test.insert({"_id" => nil, "foo" => "bar"}, {:safe => true})
91
+ assert_equal nil, @@test.save({"_id" => nil, "foo" => "baz"}, {:safe => true})
92
+ assert_equal nil, @@test.find_one("foo" => "bar")
93
+ assert_equal "baz", @@test.find_one(:_id => nil)["foo"]
94
+ assert_raise OperationFailure do
95
+ @@test.insert({"_id" => nil, "foo" => "bar"}, {:safe => true})
96
+ end
97
+ assert_raise OperationFailure do
98
+ @@test.insert({:_id => nil, "foo" => "bar"}, {:safe => true})
99
+ end
100
+ end
101
+
102
+ if @@version > "1.1"
103
+ def setup_for_distinct
104
+ @@test.remove
105
+ @@test.insert([{:a => 0, :b => {:c => "a"}},
106
+ {:a => 1, :b => {:c => "b"}},
107
+ {:a => 1, :b => {:c => "c"}},
108
+ {:a => 2, :b => {:c => "a"}},
109
+ {:a => 3},
110
+ {:a => 3}])
111
+ end
112
+
113
+ def test_distinct_queries
114
+ setup_for_distinct
115
+ assert_equal [0, 1, 2, 3], @@test.distinct(:a).sort
116
+ assert_equal ["a", "b", "c"], @@test.distinct("b.c").sort
117
+ end
118
+
119
+ if @@version >= "1.2"
120
+ def test_filter_collection_with_query
121
+ setup_for_distinct
122
+ assert_equal [2, 3], @@test.distinct(:a, {:a => {"$gt" => 1}}).sort
123
+ end
124
+
125
+ def test_filter_nested_objects
126
+ setup_for_distinct
127
+ assert_equal ["a", "b"], @@test.distinct("b.c", {"b.c" => {"$ne" => "c"}}).sort
128
+ end
129
+ end
130
+ end
131
+
132
+ def test_safe_insert
133
+ @@test.create_index("hello", :unique => true)
134
+ a = {"hello" => "world"}
135
+ @@test.insert(a)
136
+ @@test.insert(a)
137
+ assert(@@db.get_last_error['err'].include?("11000"))
138
+
139
+ assert_raise OperationFailure do
140
+ @@test.insert(a, :safe => true)
141
+ end
142
+ end
143
+
144
+ def test_maximum_insert_size
145
+ docs = []
146
+ 16.times do
147
+ docs << {'foo' => 'a' * 1_000_000}
148
+ end
149
+
150
+ assert_raise InvalidOperation do
151
+ @@test.insert(docs)
152
+ end
153
+ end
154
+
155
+ if @@version >= "1.5.1"
156
+ def test_safe_mode_with_advanced_safe_with_invalid_options
157
+ assert_raise_error ArgumentError, "Unknown key(s): wtime" do
158
+ @@test.insert({:foo => 1}, :safe => {:w => 2, :wtime => 1, :fsync => true})
159
+ end
160
+ assert_raise_error ArgumentError, "Unknown key(s): wtime" do
161
+ @@test.update({:foo => 1}, {:foo => 2}, :safe => {:w => 2, :wtime => 1, :fsync => true})
162
+ end
163
+
164
+ assert_raise_error ArgumentError, "Unknown key(s): wtime" do
165
+ @@test.remove({:foo => 2}, :safe => {:w => 2, :wtime => 1, :fsync => true})
166
+ end
167
+ end
168
+ end
169
+
170
+ def test_update
171
+ id1 = @@test.save("x" => 5)
172
+ @@test.update({}, {"$inc" => {"x" => 1}})
173
+ assert_equal 1, @@test.count()
174
+ assert_equal 6, @@test.find_one(:_id => id1)["x"]
175
+
176
+ id2 = @@test.save("x" => 1)
177
+ @@test.update({"x" => 6}, {"$inc" => {"x" => 1}})
178
+ assert_equal 7, @@test.find_one(:_id => id1)["x"]
179
+ assert_equal 1, @@test.find_one(:_id => id2)["x"]
180
+ end
181
+
182
+ if @@version >= "1.1.3"
183
+ def test_multi_update
184
+ @@test.save("num" => 10)
185
+ @@test.save("num" => 10)
186
+ @@test.save("num" => 10)
187
+ assert_equal 3, @@test.count
188
+
189
+ @@test.update({"num" => 10}, {"$set" => {"num" => 100}}, :multi => true)
190
+ @@test.find.each do |doc|
191
+ assert_equal 100, doc["num"]
192
+ end
193
+ end
194
+ end
195
+
196
+ def test_upsert
197
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
198
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
199
+
200
+ assert_equal 1, @@test.count()
201
+ assert_equal 2, @@test.find_one()["count"]
202
+ end
203
+
204
+ if @@version < "1.1.3"
205
+ def test_safe_update
206
+ @@test.create_index("x")
207
+ @@test.insert("x" => 5)
208
+
209
+ @@test.update({}, {"$inc" => {"x" => 1}})
210
+ assert @@db.error?
211
+
212
+ # Can't change an index.
213
+ assert_raise OperationFailure do
214
+ @@test.update({}, {"$inc" => {"x" => 1}}, :safe => true)
215
+ end
216
+ @@test.drop
217
+ end
218
+ else
219
+ def test_safe_update
220
+ @@test.create_index("x", :unique => true)
221
+ @@test.insert("x" => 5)
222
+ @@test.insert("x" => 10)
223
+
224
+ # Can update an indexed collection.
225
+ @@test.update({}, {"$inc" => {"x" => 1}})
226
+ assert !@@db.error?
227
+
228
+ # Can't duplicate an index.
229
+ assert_raise OperationFailure do
230
+ @@test.update({}, {"x" => 10}, :safe => true)
231
+ end
232
+ @@test.drop
233
+ end
234
+ end
235
+
236
+ def test_safe_save
237
+ @@test.create_index("hello", :unique => true)
238
+
239
+ @@test.save("hello" => "world")
240
+ @@test.save("hello" => "world")
241
+
242
+ assert_raise OperationFailure do
243
+ @@test.save({"hello" => "world"}, :safe => true)
244
+ end
245
+ @@test.drop
246
+ end
247
+
248
+ def test_mocked_safe_remove
249
+ @conn = standard_connection
250
+ @db = @conn[MONGO_TEST_DB]
251
+ @test = @db['test-safe-remove']
252
+ @test.save({:a => 20})
253
+ @conn.stubs(:receive).returns([[{'ok' => 0, 'err' => 'failed'}], 1, 0])
254
+
255
+ assert_raise OperationFailure do
256
+ @test.remove({}, :safe => true)
257
+ end
258
+ @test.drop
259
+ end
260
+
261
+ def test_safe_remove
262
+ @conn = standard_connection
263
+ @db = @conn[MONGO_TEST_DB]
264
+ @test = @db['test-safe-remove']
265
+ @test.save({:a => 50})
266
+ assert_equal 1, @test.remove({}, :safe => true)["n"]
267
+ @test.drop
268
+ end
269
+
270
+ def test_remove_return_value
271
+ assert_equal true, @@test.remove({})
272
+ end
273
+
274
+ def test_count
275
+ @@test.drop
276
+
277
+ assert_equal 0, @@test.count
278
+ @@test.save("x" => 1)
279
+ @@test.save("x" => 2)
280
+ assert_equal 2, @@test.count
281
+ end
282
+
283
+ # Note: #size is just an alias for #count.
284
+ def test_size
285
+ @@test.drop
286
+
287
+ assert_equal 0, @@test.count
288
+ assert_equal @@test.size, @@test.count
289
+ @@test.save("x" => 1)
290
+ @@test.save("x" => 2)
291
+ assert_equal @@test.size, @@test.count
292
+ end
293
+
294
+ def test_no_timeout_option
295
+ @@test.drop
296
+
297
+ assert_raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block." do
298
+ @@test.find({}, :timeout => false)
299
+ end
300
+
301
+ @@test.find({}, :timeout => false) do |cursor|
302
+ assert_equal 0, cursor.count
303
+ end
304
+
305
+ @@test.save("x" => 1)
306
+ @@test.save("x" => 2)
307
+ @@test.find({}, :timeout => false) do |cursor|
308
+ assert_equal 2, cursor.count
309
+ end
310
+ end
311
+
312
+ def test_defualt_timeout
313
+ cursor = @@test.find
314
+ assert_equal true, cursor.timeout
315
+ end
316
+
317
+ def test_fields_as_hash
318
+ @@test.save(:a => 1, :b => 1, :c => 1)
319
+
320
+ doc = @@test.find_one({:a => 1}, :fields => {:b => 0})
321
+ assert_nil doc['b']
322
+ assert doc['a']
323
+ assert doc['c']
324
+
325
+ doc = @@test.find_one({:a => 1}, :fields => {:a => 1, :b => 1})
326
+ assert_nil doc['c']
327
+ assert doc['a']
328
+ assert doc['b']
329
+
330
+
331
+ assert_raise Mongo::OperationFailure do
332
+ @@test.find_one({:a => 1}, :fields => {:a => 1, :b => 0})
333
+ end
334
+ end
335
+
336
+ if @@version >= "1.5.1"
337
+ def test_fields_with_slice
338
+ @@test.save({:foo => [1, 2, 3, 4, 5, 6], :test => 'slice'})
339
+
340
+ doc = @@test.find_one({:test => 'slice'}, :fields => {'foo' => {'$slice' => [0, 3]}})
341
+ assert_equal [1, 2, 3], doc['foo']
342
+ @@test.remove
343
+ end
344
+ end
345
+
346
+ def test_find_one
347
+ id = @@test.save("hello" => "world", "foo" => "bar")
348
+
349
+ assert_equal "world", @@test.find_one()["hello"]
350
+ assert_equal @@test.find_one(id), @@test.find_one()
351
+ assert_equal @@test.find_one(nil), @@test.find_one()
352
+ assert_equal @@test.find_one({}), @@test.find_one()
353
+ assert_equal @@test.find_one("hello" => "world"), @@test.find_one()
354
+ assert_equal @@test.find_one(BSON::OrderedHash["hello", "world"]), @@test.find_one()
355
+
356
+ assert @@test.find_one(nil, :fields => ["hello"]).include?("hello")
357
+ assert !@@test.find_one(nil, :fields => ["foo"]).include?("hello")
358
+ assert_equal ["_id"], @@test.find_one(nil, :fields => []).keys()
359
+
360
+ assert_equal nil, @@test.find_one("hello" => "foo")
361
+ assert_equal nil, @@test.find_one(BSON::OrderedHash["hello", "foo"])
362
+ assert_equal nil, @@test.find_one(ObjectId.new)
363
+
364
+ assert_raise TypeError do
365
+ @@test.find_one(6)
366
+ end
367
+ end
368
+
369
+ def test_insert_adds_id
370
+ doc = {"hello" => "world"}
371
+ @@test.insert(doc)
372
+ assert(doc.include?(:_id))
373
+
374
+ docs = [{"hello" => "world"}, {"hello" => "world"}]
375
+ @@test.insert(docs)
376
+ docs.each do |doc|
377
+ assert(doc.include?(:_id))
378
+ end
379
+ end
380
+
381
+ def test_save_adds_id
382
+ doc = {"hello" => "world"}
383
+ @@test.save(doc)
384
+ assert(doc.include?(:_id))
385
+ end
386
+
387
+ def test_optional_find_block
388
+ 10.times do |i|
389
+ @@test.save("i" => i)
390
+ end
391
+
392
+ x = nil
393
+ @@test.find("i" => 2) { |cursor|
394
+ x = cursor.count()
395
+ }
396
+ assert_equal 1, x
397
+
398
+ i = 0
399
+ @@test.find({}, :skip => 5) do |cursor|
400
+ cursor.each do |doc|
401
+ i = i + 1
402
+ end
403
+ end
404
+ assert_equal 5, i
405
+
406
+ c = nil
407
+ @@test.find() do |cursor|
408
+ c = cursor
409
+ end
410
+ assert c.closed?
411
+ end
412
+
413
+ if @@version > "1.1.1"
414
+ def test_map_reduce
415
+ @@test << { "user_id" => 1 }
416
+ @@test << { "user_id" => 2 }
417
+
418
+ m = "function() { emit(this.user_id, 1); }"
419
+ r = "function(k,vals) { return 1; }"
420
+ res = @@test.map_reduce(m, r, :out => 'foo');
421
+ assert res.find_one({"_id" => 1})
422
+ assert res.find_one({"_id" => 2})
423
+ end
424
+
425
+ def test_map_reduce_with_code_objects
426
+ @@test << { "user_id" => 1 }
427
+ @@test << { "user_id" => 2 }
428
+
429
+ m = Code.new("function() { emit(this.user_id, 1); }")
430
+ r = Code.new("function(k,vals) { return 1; }")
431
+ res = @@test.map_reduce(m, r, :out => 'foo');
432
+ assert res.find_one({"_id" => 1})
433
+ assert res.find_one({"_id" => 2})
434
+ end
435
+
436
+ def test_map_reduce_with_options
437
+ @@test.remove
438
+ @@test << { "user_id" => 1 }
439
+ @@test << { "user_id" => 2 }
440
+ @@test << { "user_id" => 3 }
441
+
442
+ m = Code.new("function() { emit(this.user_id, 1); }")
443
+ r = Code.new("function(k,vals) { return 1; }")
444
+ res = @@test.map_reduce(m, r, :query => {"user_id" => {"$gt" => 1}}, :out => 'foo');
445
+ assert_equal 2, res.count
446
+ assert res.find_one({"_id" => 2})
447
+ assert res.find_one({"_id" => 3})
448
+ end
449
+
450
+ def test_map_reduce_with_raw_response
451
+ m = Code.new("function() { emit(this.user_id, 1); }")
452
+ r = Code.new("function(k,vals) { return 1; }")
453
+ res = @@test.map_reduce(m, r, :raw => true, :out => 'foo')
454
+ assert res["result"]
455
+ assert res["counts"]
456
+ assert res["timeMillis"]
457
+ end
458
+
459
+ def test_map_reduce_with_output_collection
460
+ output_collection = "test-map-coll"
461
+ m = Code.new("function() { emit(this.user_id, 1); }")
462
+ r = Code.new("function(k,vals) { return 1; }")
463
+ res = @@test.map_reduce(m, r, :raw => true, :out => output_collection)
464
+ assert_equal output_collection, res["result"]
465
+ assert res["counts"]
466
+ assert res["timeMillis"]
467
+ end
468
+
469
+ def test_map_reduce_with_collection_merge
470
+ @@test << {:user_id => 1}
471
+ @@test << {:user_id => 2}
472
+ output_collection = "test-map-coll"
473
+ m = Code.new("function() { emit(this.user_id, {count: 1}); }")
474
+ r = Code.new("function(k,vals) { var sum = 0;" +
475
+ " vals.forEach(function(v) { sum += v.count;} ); return {count: sum}; }")
476
+ res = @@test.map_reduce(m, r, :out => output_collection)
477
+
478
+ @@test.remove
479
+ @@test << {:user_id => 3}
480
+ res = @@test.map_reduce(m, r, :out => {:merge => output_collection})
481
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 1}
482
+
483
+ @@test.remove
484
+ @@test << {:user_id => 3}
485
+ res = @@test.map_reduce(m, r, :out => {:reduce => output_collection})
486
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 2}
487
+
488
+ assert_raise ArgumentError do
489
+ @@test.map_reduce(m, r, :out => {:inline => 1})
490
+ end
491
+
492
+ @@test.map_reduce(m, r, :raw => true, :out => {:inline => 1})
493
+ assert res["results"]
494
+ end
495
+ end
496
+
497
+ if @@version > "1.3.0"
498
+ def test_find_and_modify
499
+ @@test << { :a => 1, :processed => false }
500
+ @@test << { :a => 2, :processed => false }
501
+ @@test << { :a => 3, :processed => false }
502
+
503
+ @@test.find_and_modify(:query => {}, :sort => [['a', -1]], :update => {"$set" => {:processed => true}})
504
+
505
+ assert @@test.find_one({:a => 3})['processed']
506
+ end
507
+
508
+ def test_find_and_modify_with_invalid_options
509
+ @@test << { :a => 1, :processed => false }
510
+ @@test << { :a => 2, :processed => false }
511
+ @@test << { :a => 3, :processed => false }
512
+
513
+ assert_raise Mongo::OperationFailure do
514
+ @@test.find_and_modify(:blimey => {})
515
+ end
516
+ end
517
+ end
518
+
519
+ if @@version >= "1.3.5"
520
+ def test_coll_stats
521
+ @@test << {:n => 1}
522
+ @@test.create_index("n")
523
+
524
+ assert_equal "#{MONGO_TEST_DB}.test", @@test.stats['ns']
525
+ end
526
+ end
527
+
528
+ def test_saving_dates_pre_epoch
529
+ begin
530
+ @@test.save({'date' => Time.utc(1600)})
531
+ assert_in_delta Time.utc(1600), @@test.find_one()["date"], 2
532
+ rescue ArgumentError
533
+ # See note in test_date_before_epoch (BSONTest)
534
+ end
535
+ end
536
+
537
+ def test_save_symbol_find_string
538
+ @@test.save(:foo => :mike)
539
+
540
+ assert_equal :mike, @@test.find_one(:foo => :mike)["foo"]
541
+ assert_equal :mike, @@test.find_one("foo" => :mike)["foo"]
542
+
543
+ # TODO enable these tests conditionally based on server version (if >1.0)
544
+ # assert_equal :mike, @@test.find_one(:foo => "mike")["foo"]
545
+ # assert_equal :mike, @@test.find_one("foo" => "mike")["foo"]
546
+ end
547
+
548
+ def test_limit_and_skip
549
+ 10.times do |i|
550
+ @@test.save(:foo => i)
551
+ end
552
+
553
+ assert_equal 5, @@test.find({}, :skip => 5).next_document()["foo"]
554
+ assert_equal nil, @@test.find({}, :skip => 10).next_document()
555
+
556
+ assert_equal 5, @@test.find({}, :limit => 5).to_a.length
557
+
558
+ assert_equal 3, @@test.find({}, :skip => 3, :limit => 5).next_document()["foo"]
559
+ assert_equal 5, @@test.find({}, :skip => 3, :limit => 5).to_a.length
560
+ end
561
+
562
+ def test_large_limit
563
+ 2000.times do |i|
564
+ @@test.insert("x" => i, "y" => "mongomongo" * 1000)
565
+ end
566
+
567
+ assert_equal 2000, @@test.count
568
+
569
+ i = 0
570
+ y = 0
571
+ @@test.find({}, :limit => 1900).each do |doc|
572
+ i += 1
573
+ y += doc["x"]
574
+ end
575
+
576
+ assert_equal 1900, i
577
+ assert_equal 1804050, y
578
+ end
579
+
580
+ def test_small_limit
581
+ @@test.insert("x" => "hello world")
582
+ @@test.insert("x" => "goodbye world")
583
+
584
+ assert_equal 2, @@test.count
585
+
586
+ x = 0
587
+ @@test.find({}, :limit => 1).each do |doc|
588
+ x += 1
589
+ assert_equal "hello world", doc["x"]
590
+ end
591
+
592
+ assert_equal 1, x
593
+ end
594
+
595
+
596
+ def test_ensure_index
597
+ @@test.drop_indexes
598
+ @@test.insert("x" => "hello world")
599
+ assert_equal 1, @@test.index_information.keys.count #default index
600
+
601
+ @@test.ensure_index([["x", Mongo::DESCENDING]], {})
602
+ assert_equal 2, @@test.index_information.keys.count
603
+ assert @@test.index_information.keys.include? "x_-1"
604
+
605
+ @@test.ensure_index([["x", Mongo::ASCENDING]])
606
+ assert @@test.index_information.keys.include? "x_1"
607
+
608
+ @@test.ensure_index([["type", 1], ["date", -1]])
609
+ assert @@test.index_information.keys.include? "type_1_date_-1"
610
+
611
+ @@test.drop_index("x_1")
612
+ assert_equal 3, @@test.index_information.keys.count
613
+ @@test.drop_index("x_-1")
614
+ assert_equal 2, @@test.index_information.keys.count
615
+
616
+ @@test.ensure_index([["x", Mongo::DESCENDING]], {})
617
+ assert_equal 3, @@test.index_information.keys.count
618
+ assert @@test.index_information.keys.include? "x_-1"
619
+
620
+ # Make sure that drop_index expires cache properly
621
+ @@test.ensure_index([['a', 1]])
622
+ assert @@test.index_information.keys.include?("a_1")
623
+ @@test.drop_index("a_1")
624
+ assert !@@test.index_information.keys.include?("a_1")
625
+ @@test.ensure_index([['a', 1]])
626
+ assert @@test.index_information.keys.include?("a_1")
627
+ @@test.drop_index("a_1")
628
+ end
629
+
630
+ def test_ensure_index_timeout
631
+ @@db.cache_time = 2
632
+ coll = @@db['ensure_test']
633
+ coll.expects(:generate_indexes).twice
634
+ coll.ensure_index([['a', 1]])
635
+
636
+ # These will be cached
637
+ coll.ensure_index([['a', 1]])
638
+ coll.ensure_index([['a', 1]])
639
+ coll.ensure_index([['a', 1]])
640
+ coll.ensure_index([['a', 1]])
641
+
642
+ sleep(3)
643
+ # This won't be, so generate_indexes will be called twice
644
+ coll.ensure_index([['a', 1]])
645
+ end
646
+
647
+ context "Grouping" do
648
+ setup do
649
+ @@test.remove
650
+ @@test.save("a" => 1)
651
+ @@test.save("b" => 1)
652
+ @initial = {"count" => 0}
653
+ @reduce_function = "function (obj, prev) { prev.count += inc_value; }"
654
+ end
655
+
656
+ should "fail if missing required options" do
657
+ assert_raise MongoArgumentError do
658
+ @@test.group(:initial => {})
659
+ end
660
+
661
+ assert_raise MongoArgumentError do
662
+ @@test.group(:reduce => "foo")
663
+ end
664
+ end
665
+
666
+ should "group results using eval form" do
667
+ assert_equal 1, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 0.5}))[0]["count"]
668
+ assert_equal 2, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}))[0]["count"]
669
+ assert_equal 4, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 2}))[0]["count"]
670
+ end
671
+
672
+ should "finalize grouped results" do
673
+ @finalize = "function(doc) {doc.f = doc.count + 200; }"
674
+ assert_equal 202, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}), :finalize => @finalize)[0]["f"]
675
+ end
676
+ end
677
+
678
+ context "Grouping with key" do
679
+ setup do
680
+ @@test.remove
681
+ @@test.save("a" => 1, "pop" => 100)
682
+ @@test.save("a" => 1, "pop" => 100)
683
+ @@test.save("a" => 2, "pop" => 100)
684
+ @@test.save("a" => 2, "pop" => 100)
685
+ @initial = {"count" => 0, "foo" => 1}
686
+ @reduce_function = "function (obj, prev) { prev.count += obj.pop; }"
687
+ end
688
+
689
+ should "group" do
690
+ result = @@test.group(:key => :a, :initial => @initial, :reduce => @reduce_function)
691
+ assert result.all? { |r| r['count'] == 200 }
692
+ end
693
+ end
694
+
695
+ context "Grouping with a key function" do
696
+ setup do
697
+ @@test.remove
698
+ @@test.save("a" => 1)
699
+ @@test.save("a" => 2)
700
+ @@test.save("a" => 3)
701
+ @@test.save("a" => 4)
702
+ @@test.save("a" => 5)
703
+ @initial = {"count" => 0}
704
+ @keyf = "function (doc) { if(doc.a % 2 == 0) { return {even: true}; } else {return {odd: true}} };"
705
+ @reduce = "function (obj, prev) { prev.count += 1; }"
706
+ end
707
+
708
+ should "group results" do
709
+ results = @@test.group(:keyf => @keyf, :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
710
+ assert results[0]['even'] && results[0]['count'] == 2.0
711
+ assert results[1]['odd'] && results[1]['count'] == 3.0
712
+ end
713
+
714
+ should "group filtered results" do
715
+ results = @@test.group(:keyf => @keyf, :cond => {:a => {'$ne' => 2}},
716
+ :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
717
+ assert results[0]['even'] && results[0]['count'] == 1.0
718
+ assert results[1]['odd'] && results[1]['count'] == 3.0
719
+ end
720
+ end
721
+
722
+ context "A collection with two records" do
723
+ setup do
724
+ @collection = @@db.collection('test-collection')
725
+ @collection.insert({:name => "Jones"})
726
+ @collection.insert({:name => "Smith"})
727
+ end
728
+
729
+ should "have two records" do
730
+ assert_equal 2, @collection.size
731
+ end
732
+
733
+ should "remove the two records" do
734
+ @collection.remove()
735
+ assert_equal 0, @collection.size
736
+ end
737
+
738
+ should "remove all records if an empty document is specified" do
739
+ @collection.remove({})
740
+ assert_equal 0, @collection.find.count
741
+ end
742
+
743
+ should "remove only matching records" do
744
+ @collection.remove({:name => "Jones"})
745
+ assert_equal 1, @collection.size
746
+ end
747
+ end
748
+
749
+ context "Creating indexes " do
750
+ setup do
751
+ @@db.drop_collection('geo')
752
+ @@db.drop_collection('test-collection')
753
+ @collection = @@db.collection('test-collection')
754
+ @geo = @@db.collection('geo')
755
+ end
756
+
757
+ should "create index using symbols" do
758
+ @collection.create_index :foo, :name => :bar
759
+ @geo.create_index :goo, :name => :baz
760
+ assert @collection.index_information['bar']
761
+ @collection.drop_index :bar
762
+ assert_nil @collection.index_information['bar']
763
+ assert @geo.index_information['baz']
764
+ @geo.drop_index(:baz)
765
+ assert_nil @geo.index_information['baz']
766
+ end
767
+
768
+ should "create a geospatial index" do
769
+ @geo.save({'loc' => [-100, 100]})
770
+ @geo.create_index([['loc', Mongo::GEO2D]])
771
+ assert @geo.index_information['loc_2d']
772
+ end
773
+
774
+ should "create a unique index" do
775
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true)
776
+ assert @collection.index_information['a_1']['unique'] == true
777
+ end
778
+
779
+ should "drop duplicates" do
780
+ @collection.insert({:a => 1})
781
+ @collection.insert({:a => 1})
782
+ assert_equal 2, @collection.find({:a => 1}).count
783
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :dropDups => true)
784
+ assert_equal 1, @collection.find({:a => 1}).count
785
+ end
786
+
787
+ should "drop duplicates with ruby-like drop_dups key" do
788
+ @collection.insert({:a => 1})
789
+ @collection.insert({:a => 1})
790
+ assert_equal 2, @collection.find({:a => 1}).count
791
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
792
+ assert_equal 1, @collection.find({:a => 1}).count
793
+ end
794
+
795
+ should "create an index in the background" do
796
+ if @@version > '1.3.1'
797
+ @collection.create_index([['b', Mongo::ASCENDING]], :background => true)
798
+ assert @collection.index_information['b_1']['background'] == true
799
+ else
800
+ assert true
801
+ end
802
+ end
803
+
804
+ should "require an array of arrays" do
805
+ assert_raise MongoArgumentError do
806
+ @collection.create_index(['c', Mongo::ASCENDING])
807
+ end
808
+ end
809
+
810
+ should "enforce proper index types" do
811
+ assert_raise MongoArgumentError do
812
+ @collection.create_index([['c', 'blah']])
813
+ end
814
+ end
815
+
816
+ should "raise an error if index name is greater than 128" do
817
+ assert_raise Mongo::OperationFailure do
818
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
819
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]])
820
+ end
821
+ end
822
+
823
+ should "allow for an alternate name to be specified" do
824
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
825
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]], :name => 'foo_index')
826
+ assert @collection.index_information['foo_index']
827
+ end
828
+
829
+ should "generate indexes in the proper order" do
830
+ @collection.expects(:insert_documents) do |sel, coll, safe|
831
+ assert_equal 'b_1_a_1', sel[:name]
832
+ end
833
+ @collection.create_index([['b', 1], ['a', 1]])
834
+ end
835
+
836
+ should "allow multiple calls to create_index" do
837
+
838
+ end
839
+
840
+ should "allow creation of multiple indexes" do
841
+ assert @collection.create_index([['a', 1]])
842
+ assert @collection.create_index([['a', 1]])
843
+ end
844
+
845
+ context "with an index created" do
846
+ setup do
847
+ @collection.create_index([['b', 1], ['a', 1]])
848
+ end
849
+
850
+ should "return properly ordered index information" do
851
+ assert @collection.index_information['b_1_a_1']
852
+ end
853
+ end
854
+ end
855
+
856
+ context "Capped collections" do
857
+ setup do
858
+ @@db.drop_collection('log')
859
+ @capped = @@db.create_collection('log', :capped => true, :size => 1024)
860
+
861
+ 10.times { |n| @capped.insert({:n => n}) }
862
+ end
863
+
864
+ should "find using a standard cursor" do
865
+ cursor = @capped.find
866
+ 10.times do
867
+ assert cursor.next_document
868
+ end
869
+ assert_nil cursor.next_document
870
+ @capped.insert({:n => 100})
871
+ assert_nil cursor.next_document
872
+ end
873
+
874
+ should "fail tailable cursor on a non-capped collection" do
875
+ col = @@db['regular-collection']
876
+ col.insert({:a => 1000})
877
+ tail = Cursor.new(col, :tailable => true, :order => [['$natural', 1]])
878
+ assert_raise OperationFailure do
879
+ tail.next_document
880
+ end
881
+ end
882
+
883
+ should "find using a tailable cursor" do
884
+ tail = Cursor.new(@capped, :tailable => true, :order => [['$natural', 1]])
885
+ 10.times do
886
+ assert tail.next_document
887
+ end
888
+ assert_nil tail.next_document
889
+ @capped.insert({:n => 100})
890
+ assert tail.next_document
891
+ end
892
+ end
893
+ end