mongo 1.3.0 → 1.12.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 (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
@@ -0,0 +1,2175 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rbconfig'
16
+ require 'test_helper'
17
+
18
+ class CollectionTest < Test::Unit::TestCase
19
+
20
+ LIMITED_MAX_BSON_SIZE = 1024
21
+ LIMITED_BSON_SIZE_WITH_HEADROOM = LIMITED_MAX_BSON_SIZE + MongoClient::APPEND_HEADROOM
22
+ LIMITED_MAX_MESSAGE_SIZE = 3 * LIMITED_MAX_BSON_SIZE
23
+ LIMITED_TEST_HEADROOM = 50
24
+ LIMITED_VALID_VALUE_SIZE = LIMITED_MAX_BSON_SIZE - LIMITED_TEST_HEADROOM
25
+ LIMITED_INVALID_VALUE_SIZE = LIMITED_MAX_BSON_SIZE + Mongo::MongoClient::COMMAND_HEADROOM + 1
26
+
27
+ def setup
28
+ @client ||= standard_connection(:op_timeout => 10)
29
+ @db = @client.db(TEST_DB)
30
+ @test = @db.collection("test")
31
+ @version = @client.server_version
32
+ @test.remove
33
+ @ismaster = @client['admin'].command(:isMaster => 1)
34
+ end
35
+
36
+ @@wv0 = Mongo::MongoClient::RELEASE_2_4_AND_BEFORE
37
+ @@wv2 = Mongo::MongoClient::BATCH_COMMANDS
38
+ @@a_h = Mongo::MongoClient::APPEND_HEADROOM
39
+ @@s_h = Mongo::MongoClient::SERIALIZE_HEADROOM
40
+
41
+ def max_size_exception_test(client)
42
+ base = [
43
+ #[@@wv0, client.max_bson_size, nil, /xyzzy/], # succeeds standalone, fails whole suite
44
+ ]
45
+ base += max_size_exception_cruby_test(client) unless RUBY_PLATFORM == 'java'
46
+ #base += max_size_exception_jruby_test if RUBY_PLATFORM == 'java'
47
+ base += max_size_exception_commands_test(client) if @version >= '2.5.2'
48
+ base
49
+ end
50
+
51
+ def max_size_exception_cruby_test(client)
52
+ [
53
+ [@@wv0, client.max_bson_size + 1, BSON::InvalidDocument, /Document.* too large/]
54
+ ]
55
+ end
56
+
57
+ def max_size_exception_jruby_test(client)
58
+ [@@wv0, client.max_bson_size + 1, Mongo::OperationFailure, /object to insert too large/]
59
+ end
60
+
61
+ def max_size_exception_commands_test(client)
62
+ [
63
+ #[@@wv2, client.max_bson_size, nil, /xyzzy/], # succeeds standalone, fails whole suite
64
+ [@@wv2, client.max_bson_size + 1, Mongo::OperationFailure, /object to insert too large/],
65
+ [@@wv2, client.max_bson_size + @@s_h, Mongo::OperationFailure, /object to insert too large/],
66
+ [@@wv2, client.max_bson_size + @@a_h, BSON::InvalidDocument, /Document.* too large/]
67
+ ]
68
+ end
69
+
70
+ def generate_sized_doc(size)
71
+ doc = {"_id" => BSON::ObjectId.new, "x" => "y"}
72
+ serialize_doc = BSON::BSON_CODER.serialize(doc, false, false, size)
73
+ doc = {"_id" => BSON::ObjectId.new, "x" => "y" * (1 + size - serialize_doc.size)}
74
+ assert_equal size, BSON::BSON_CODER.serialize(doc, false, false, size).size
75
+ doc
76
+ end
77
+
78
+ def with_max_wire_version(client, wire_version) # does not support replica sets
79
+ if client.wire_version_feature?(wire_version)
80
+ client.class.class_eval(%Q{
81
+ alias :old_max_wire_version :max_wire_version
82
+ def max_wire_version
83
+ #{wire_version}
84
+ end
85
+ })
86
+ yield wire_version
87
+ client.class.class_eval(%Q{
88
+ alias :max_wire_version :old_max_wire_version
89
+ })
90
+ end
91
+ end
92
+
93
+ def test_insert_batch_max_sizes
94
+ max_size_exception_test(@client).each do |wire_version, size, exc, regexp|
95
+ with_max_wire_version(@client, wire_version) do
96
+ @test.remove
97
+ doc = generate_sized_doc(size)
98
+ begin
99
+ @test.insert([doc.dup])
100
+ assert_equal nil, exc
101
+ rescue => e
102
+ assert_equal exc, e.class, "wire_version:#{wire_version}, size:#{size}, exc:#{exc} e:#{e.message.inspect} @version:#{@version}"
103
+ assert_match regexp, e.message
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def test_single_delete_write_command
110
+ return unless @version >= '2.5.4'
111
+ @test.drop
112
+ @test.insert([{ :a => 1 }, { :a => 1 }])
113
+
114
+ command = BSON::OrderedHash['delete', @test.name,
115
+ :deletes, [{ :q => { :a => 1 }, :limit => 1 }],
116
+ :writeConcern, { :w => 1 },
117
+ :ordered, false]
118
+
119
+ result = @db.command(command)
120
+ assert_equal 1, result['n']
121
+ assert_equal 1, result['ok']
122
+ assert_equal 1, @test.count
123
+ end
124
+
125
+ def test_multi_ordered_delete_write_command
126
+ return unless @version >= '2.5.4'
127
+ @test.drop
128
+ @test.insert([{ :a => 1 }, { :a => 1 }])
129
+
130
+ command = BSON::OrderedHash['delete', @test.name,
131
+ :deletes, [{ :q => { :a => 1 }, :limit => 0 }],
132
+ :writeConcern, { :w => 1 },
133
+ :ordered, true]
134
+
135
+ result = @db.command(command)
136
+ assert_equal 2, result['n']
137
+ assert_equal 1, result['ok']
138
+ assert_equal 0, @test.count
139
+ end
140
+
141
+ def test_multi_unordered_delete_write_command
142
+ return unless @version >= '2.5.4'
143
+ @test.drop
144
+ @test.insert([{ :a => 1 }, { :a => 1 }])
145
+
146
+ command = BSON::OrderedHash['delete', @test.name,
147
+ :deletes, [{ :q => { :a => 1 }, :limit => 0 }],
148
+ :writeConcern, { :w => 1 },
149
+ :ordered, false]
150
+
151
+ result = @db.command(command)
152
+ assert_equal 2, result['n']
153
+ assert_equal 1, result['ok']
154
+ assert_equal 0, @test.count
155
+ end
156
+
157
+ def test_delete_write_command_with_no_concern
158
+ return unless @version >= '2.5.4'
159
+ @test.drop
160
+ @test.insert([{ :a => 1 }, { :a => 1 }])
161
+
162
+ command = BSON::OrderedHash['delete', @test.name,
163
+ :deletes, [{ :q => { :a => 1 }, :limit => 0 }],
164
+ :ordered, false]
165
+
166
+ result = @db.command(command)
167
+ assert_equal 2, result['n']
168
+ assert_equal 1, result['ok']
169
+ assert_equal 0, @test.count
170
+ end
171
+
172
+ def test_delete_write_command_with_error
173
+ return unless @version >= '2.5.4'
174
+ @test.drop
175
+ @test.insert([{ :a => 1 }, { :a => 1 }])
176
+
177
+ command = BSON::OrderedHash['delete', @test.name,
178
+ :deletes, [{ :q => { '$set' => { :a => 1 }}, :limit => 0 }],
179
+ :writeConcern, { :w => 1 },
180
+ :ordered, false]
181
+
182
+ assert_raise Mongo::OperationFailure do
183
+ @db.command(command)
184
+ end
185
+ end
186
+
187
+ def test_single_insert_write_command
188
+ return unless @version >= '2.5.4'
189
+ @test.drop
190
+
191
+ command = BSON::OrderedHash['insert', @test.name,
192
+ :documents, [{ :a => 1 }],
193
+ :writeConcern, { :w => 1 },
194
+ :ordered, false]
195
+
196
+ result = @db.command(command)
197
+ assert_equal 1, result['ok']
198
+ assert_equal 1, @test.count
199
+ end
200
+
201
+ def test_multi_ordered_insert_write_command
202
+ return unless @version >= '2.5.4'
203
+ @test.drop
204
+
205
+ command = BSON::OrderedHash['insert', @test.name,
206
+ :documents, [{ :a => 1 }, { :a => 2 }],
207
+ :writeConcern, { :w => 1 },
208
+ :ordered, true]
209
+
210
+ result = @db.command(command)
211
+ assert_equal 1, result['ok']
212
+ assert_equal 2, @test.count
213
+ end
214
+
215
+ def test_multi_unordered_insert_write_command
216
+ return unless @version >= '2.5.4'
217
+ @test.drop
218
+
219
+ command = BSON::OrderedHash['insert', @test.name,
220
+ :documents, [{ :a => 1 }, { :a => 2 }],
221
+ :writeConcern, { :w => 1 },
222
+ :ordered, false]
223
+
224
+ result = @db.command(command)
225
+ assert_equal 1, result['ok']
226
+ assert_equal 2, @test.count
227
+ end
228
+
229
+ def test_insert_write_command_with_no_concern
230
+ return unless @version >= '2.5.4'
231
+ @test.drop
232
+
233
+ command = BSON::OrderedHash['insert', @test.name,
234
+ :documents, [{ :a => 1 }, { :a => 2 }],
235
+ :ordered, false]
236
+
237
+ result = @db.command(command)
238
+ assert_equal 1, result['ok']
239
+ assert_equal 2, @test.count
240
+ end
241
+
242
+ def test_insert_write_command_with_error
243
+ return unless @version >= '2.5.4'
244
+ @test.drop
245
+ @test.ensure_index([[:a, 1]], { :unique => true })
246
+
247
+ command = BSON::OrderedHash['insert', @test.name,
248
+ :documents, [{ :a => 1 }, { :a => 1 }],
249
+ :writeConcern, { :w => 1 },
250
+ :ordered, false]
251
+
252
+ assert_raise Mongo::OperationFailure do
253
+ @db.command(command)
254
+ end
255
+ end
256
+
257
+ def test_single_update_write_command
258
+ return unless @version >= '2.5.4'
259
+ @test.drop
260
+ @test.insert([{ :a => 1 }, { :a => 2 }])
261
+
262
+ command = BSON::OrderedHash['update', @test.name,
263
+ :updates, [{ :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}}],
264
+ :writeConcern, { :w => 1 }]
265
+
266
+ result = @db.command(command)
267
+ assert_equal 1, result['ok']
268
+ assert_equal 1, result['n']
269
+ assert_equal 2, @test.find({ :a => 2 }).count
270
+ end
271
+
272
+ def test_multi_ordered_update_write_command
273
+ return unless @version >= '2.5.4'
274
+ @test.drop
275
+ @test.insert([{ :a => 1 }, { :a => 3 }])
276
+
277
+ command = BSON::OrderedHash['update', @test.name,
278
+ :updates, [
279
+ { :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}},
280
+ { :q => { :a => 3 }, :u => { '$set' => { :a => 4 }}}
281
+ ],
282
+ :writeConcern, { :w => 1 },
283
+ :ordered, true]
284
+
285
+ result = @db.command(command)
286
+ assert_equal 1, result['ok']
287
+ assert_equal 2, result['n']
288
+ assert_equal 1, @test.find({ :a => 2 }).count
289
+ assert_equal 1, @test.find({ :a => 4 }).count
290
+ end
291
+
292
+ def test_multi_unordered_update_write_command
293
+ return unless @version >= '2.5.4'
294
+ @test.drop
295
+ @test.insert([{ :a => 1 }, { :a => 3 }])
296
+
297
+ command = BSON::OrderedHash['update', @test.name,
298
+ :updates, [
299
+ { :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}},
300
+ { :q => { :a => 3 }, :u => { '$set' => { :a => 4 }}}
301
+ ],
302
+ :writeConcern, { :w => 1 },
303
+ :ordered, false]
304
+
305
+ result = @db.command(command)
306
+ assert_equal 1, result['ok']
307
+ assert_equal 2, result['n']
308
+ assert_equal 1, @test.find({ :a => 2 }).count
309
+ assert_equal 1, @test.find({ :a => 4 }).count
310
+ end
311
+
312
+ def test_update_write_command_with_no_concern
313
+ return unless @version >= '2.5.4'
314
+ @test.drop
315
+ @test.insert([{ :a => 1 }, { :a => 3 }])
316
+
317
+ command = BSON::OrderedHash['update', @test.name,
318
+ :updates, [
319
+ { :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}},
320
+ { :q => { :a => 3 }, :u => { '$set' => { :a => 4 }}}
321
+ ],
322
+ :ordered, false]
323
+
324
+ result = @db.command(command)
325
+ assert_equal 1, result['ok']
326
+ assert_equal 2, result['n']
327
+ assert_equal 1, @test.find({ :a => 2 }).count
328
+ assert_equal 1, @test.find({ :a => 4 }).count
329
+ end
330
+
331
+ def test_update_write_command_with_error
332
+ return unless @version >= '2.5.4'
333
+ @test.drop
334
+ @test.ensure_index([[:a, 1]], { :unique => true })
335
+ @test.insert([{ :a => 1 }, { :a => 2 }])
336
+
337
+ command = BSON::OrderedHash['update', @test.name,
338
+ :updates, [
339
+ { :q => { :a => 2 }, :u => { '$set' => { :a => 1 }}}
340
+ ],
341
+ :ordered, false]
342
+
343
+ assert_raise Mongo::OperationFailure do
344
+ @db.command(command)
345
+ end
346
+ end
347
+
348
+ def test_error_code
349
+ coll = @db['test-error-code']
350
+ coll.ensure_index(BSON::OrderedHash[:x, Mongo::ASCENDING], { :unique => true })
351
+ coll.save(:x => 2)
352
+ begin
353
+ coll.save(:x => 2)
354
+ rescue => ex
355
+ assert_not_nil ex.error_code
356
+ end
357
+ coll.drop
358
+ end
359
+
360
+ def test_aggregation_cursor
361
+ return unless @version >= '2.5.1'
362
+ [10, 1000].each do |size|
363
+ @test.drop
364
+ size.times {|i| @test.insert({ :_id => i }) }
365
+ expected_sum = size.times.reduce(:+)
366
+
367
+ cursor = @test.aggregate(
368
+ [{ :$project => {:_id => '$_id'}} ],
369
+ :cursor => {}
370
+ )
371
+
372
+ assert_equal Mongo::Cursor, cursor.class
373
+
374
+ cursor_sum = cursor.reduce(0) do |sum, doc|
375
+ sum += doc['_id']
376
+ end
377
+
378
+ assert_equal expected_sum, cursor_sum
379
+ end
380
+ @test.drop
381
+ end
382
+
383
+ def test_aggregation_array
384
+ return unless @version >= '2.5.1'
385
+ @test.drop
386
+ 100.times {|i| @test.insert({ :_id => i }) }
387
+ agg = @test.aggregate([{ :$project => {:_id => '$_id'}} ])
388
+
389
+ assert agg.kind_of?(Array)
390
+
391
+ @test.drop
392
+ end
393
+
394
+ def test_aggregation_cursor_invalid_ops
395
+ return unless @version >= '2.5.1'
396
+ cursor = @test.aggregate([], :cursor => {})
397
+ assert_raise(Mongo::InvalidOperation) { cursor.rewind! }
398
+ assert_raise(Mongo::InvalidOperation) { cursor.explain }
399
+ assert_raise(Mongo::InvalidOperation) { cursor.count }
400
+ end
401
+
402
+ def test_aggregation_invalid_read_pref
403
+ assert_raise Mongo::MongoArgumentError do
404
+ @test.aggregate([], :read => :invalid_read_pref)
405
+ end
406
+ end
407
+
408
+ def test_aggregation_supports_explain
409
+ return unless @version >= '2.5.3'
410
+ @db.expects(:command).with do |selector, opts|
411
+ opts[:explain] == true
412
+ end.returns({ 'ok' => 1 })
413
+ @test.aggregate([], :explain => true)
414
+ end
415
+
416
+ def test_aggregation_explain_returns_raw_result
417
+ return unless @version >= '2.5.3'
418
+ response = @test.aggregate([], :explain => true)
419
+ assert response['stages']
420
+ end
421
+
422
+ def test_capped_method
423
+ @db.create_collection('normal')
424
+ assert !@db['normal'].capped?
425
+ @db.drop_collection('normal')
426
+
427
+ @db.create_collection('c', :capped => true, :size => 100_000)
428
+ assert @db['c'].capped?
429
+ @db.drop_collection('c')
430
+ end
431
+
432
+ def test_optional_pk_factory
433
+ @coll_default_pk = @db.collection('stuff')
434
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
435
+ @coll_default_pk = @db.create_collection('more-stuff')
436
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
437
+
438
+ # Create a db with a pk_factory.
439
+ client = MongoClient.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
440
+ ENV['MONGO_RUBY_DRIVER_PORT'] || MongoClient::DEFAULT_PORT)
441
+ client[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD)
442
+ db = client.db(TEST_DB, :pk => Object.new)
443
+
444
+ coll = db.collection('coll-with-pk')
445
+ assert coll.pk_factory.is_a?(Object)
446
+
447
+ coll = db.create_collection('created_coll_with_pk')
448
+ assert coll.pk_factory.is_a?(Object)
449
+ end
450
+
451
+ class PKTest
452
+ def self.create_pk
453
+ end
454
+ end
455
+
456
+ def test_pk_factory_on_collection
457
+ silently do
458
+ @coll = Collection.new('foo', @db, PKTest)
459
+ assert_equal PKTest, @coll.pk_factory
460
+ end
461
+
462
+ @coll2 = Collection.new('foo', @db, :pk => PKTest)
463
+ assert_equal PKTest, @coll2.pk_factory
464
+ end
465
+
466
+ def test_valid_names
467
+ assert_raise Mongo::InvalidNSName do
468
+ @db["te$t"]
469
+ end
470
+
471
+ assert_raise Mongo::InvalidNSName do
472
+ @db['$main']
473
+ end
474
+
475
+ assert @db['$cmd']
476
+ assert @db['oplog.$main']
477
+ end
478
+
479
+ def test_collection
480
+ assert_kind_of Collection, @db["test"]
481
+ assert_equal @db["test"].name(), @db.collection("test").name()
482
+ assert_equal @db["test"].name(), @db[:test].name()
483
+
484
+ assert_kind_of Collection, @db["test"]["foo"]
485
+ assert_equal @db["test"]["foo"].name(), @db.collection("test.foo").name()
486
+ assert_equal @db["test"]["foo"].name(), @db["test.foo"].name()
487
+
488
+ @db["test"]["foo"].remove
489
+ @db["test"]["foo"].insert("x" => 5)
490
+ assert_equal 5, @db.collection("test.foo").find_one()["x"]
491
+ end
492
+
493
+ def test_rename_collection
494
+ @db.drop_collection('foo1')
495
+ @db.drop_collection('bar1')
496
+
497
+ @col = @db.create_collection('foo1')
498
+ assert_equal 'foo1', @col.name
499
+
500
+ @col.rename('bar1')
501
+ assert_equal 'bar1', @col.name
502
+ end
503
+
504
+ def test_nil_id
505
+ assert_equal 5, @test.insert({"_id" => 5, "foo" => "bar"})
506
+ assert_equal 5, @test.save({"_id" => 5, "foo" => "baz"})
507
+ assert_equal nil, @test.find_one("foo" => "bar")
508
+ assert_equal "baz", @test.find_one(:_id => 5)["foo"]
509
+ assert_raise OperationFailure do
510
+ @test.insert({"_id" => 5, "foo" => "bar"})
511
+ end
512
+
513
+ assert_equal nil, @test.insert({"_id" => nil, "foo" => "bar"})
514
+ assert_equal nil, @test.save({"_id" => nil, "foo" => "baz"})
515
+ assert_equal nil, @test.find_one("foo" => "bar")
516
+ assert_equal "baz", @test.find_one(:_id => nil)["foo"]
517
+ assert_raise OperationFailure do
518
+ @test.insert({"_id" => nil, "foo" => "bar"})
519
+ end
520
+ assert_raise OperationFailure do
521
+ @test.insert({:_id => nil, "foo" => "bar"})
522
+ end
523
+ end
524
+
525
+ def setup_for_distinct
526
+ return unless @version > "1.1"
527
+ @test.remove
528
+ @test.insert([{:a => 0, :b => {:c => "a"}},
529
+ {:a => 1, :b => {:c => "b"}},
530
+ {:a => 1, :b => {:c => "c"}},
531
+ {:a => 2, :b => {:c => "a"}},
532
+ {:a => 3},
533
+ {:a => 3}])
534
+ end
535
+
536
+ def test_distinct_queries
537
+ return unless @version > "1.1"
538
+ setup_for_distinct
539
+ assert_equal [0, 1, 2, 3], @test.distinct(:a).sort
540
+ assert_equal ["a", "b", "c"], @test.distinct("b.c").sort
541
+ end
542
+
543
+ def test_filter_collection_with_query
544
+ return unless @version >= "1.2"
545
+ setup_for_distinct
546
+ assert_equal [2, 3], @test.distinct(:a, {:a => {"$gt" => 1}}).sort
547
+ end
548
+
549
+ def test_filter_nested_objects
550
+ return unless @version >= "1.2"
551
+ setup_for_distinct
552
+ assert_equal ["a", "b"], @test.distinct("b.c", {"b.c" => {"$ne" => "c"}}).sort
553
+ end
554
+
555
+ def test_safe_insert
556
+ @test.create_index("hello", :unique => true)
557
+ begin
558
+ a = {"hello" => "world"}
559
+ @test.insert(a)
560
+ @test.insert(a, :w => 0)
561
+ assert(@db.get_last_error['err'].include?("11000"))
562
+
563
+ assert_raise OperationFailure do
564
+ @test.insert(a)
565
+ end
566
+ ensure
567
+ @test.drop_indexes
568
+ end
569
+ end
570
+
571
+ def test_bulk_insert
572
+ docs = []
573
+ docs << {:foo => 1}
574
+ docs << {:foo => 2}
575
+ docs << {:foo => 3}
576
+ response = @test.insert(docs)
577
+ assert_equal 3, response.length
578
+ assert response.all? {|id| id.is_a?(BSON::ObjectId)}
579
+ assert_equal 3, @test.count
580
+ end
581
+
582
+ def test_bulk_insert_with_continue_on_error
583
+ if @version >= "2.0"
584
+ @test.create_index([["foo", 1]], :unique => true)
585
+ begin
586
+ docs = []
587
+ docs << {:foo => 1}
588
+ docs << {:foo => 1}
589
+ docs << {:foo => 2}
590
+ docs << {:foo => 3}
591
+ assert_raise OperationFailure do
592
+ @test.insert(docs)
593
+ end
594
+ assert_equal 1, @test.count
595
+ @test.remove
596
+
597
+ docs = []
598
+ docs << {:foo => 1}
599
+ docs << {:foo => 1}
600
+ docs << {:foo => 2}
601
+ docs << {:foo => 3}
602
+ assert_raise OperationFailure do
603
+ @test.insert(docs, :continue_on_error => true)
604
+ end
605
+ assert_equal 3, @test.count
606
+
607
+ @test.remove
608
+ ensure
609
+ @test.drop_index("foo_1")
610
+ end
611
+ end
612
+ end
613
+
614
+ def test_bson_valid_with_collect_on_error
615
+ docs = []
616
+ docs << {:foo => 1}
617
+ docs << {:bar => 1}
618
+ doc_ids, error_docs = @test.insert(docs, :collect_on_error => true)
619
+ assert_equal 2, @test.count
620
+ assert_equal 2, doc_ids.count
621
+ assert_equal error_docs, []
622
+ end
623
+
624
+ def test_bson_invalid_key_serialize_error_with_collect_on_error
625
+ docs = []
626
+ docs << {:foo => 1}
627
+ docs << {:bar => 1}
628
+ invalid_docs = []
629
+ invalid_docs << {'$invalid-key' => 1}
630
+ invalid_docs << {'invalid.key' => 1}
631
+ docs += invalid_docs
632
+ assert_raise BSON::InvalidKeyName do
633
+ @test.insert(docs, :collect_on_error => false)
634
+ end
635
+ assert_equal 2, @test.count
636
+
637
+ doc_ids, error_docs = @test.insert(docs, :collect_on_error => true)
638
+ assert_equal 2, @test.count
639
+ assert_equal 2, doc_ids.count
640
+ assert_equal error_docs, invalid_docs
641
+ end
642
+
643
+ def test_bson_invalid_encoding_serialize_error_with_collect_on_error
644
+ # Broken for current JRuby
645
+ if RUBY_PLATFORM == 'java' then return end
646
+ docs = []
647
+ docs << {:foo => 1}
648
+ docs << {:bar => 1}
649
+ invalid_docs = []
650
+ invalid_docs << {"\223\372\226}" => 1} # non utf8 encoding
651
+ docs += invalid_docs
652
+
653
+ assert_raise BSON::InvalidStringEncoding do
654
+ @test.insert(docs, :collect_on_error => false)
655
+ end
656
+ assert_equal 2, @test.count
657
+
658
+ doc_ids, error_docs = @test.insert(docs, :collect_on_error => true)
659
+ assert_equal 2, @test.count
660
+ assert_equal 2, doc_ids.count
661
+ assert_equal error_docs, invalid_docs
662
+ end
663
+
664
+ def test_insert_one_error_doc_with_collect_on_error
665
+ invalid_doc = {'$invalid-key' => 1}
666
+ invalid_docs = [invalid_doc]
667
+ doc_ids, error_docs = @test.insert(invalid_docs, :collect_on_error => true)
668
+ assert_equal [], doc_ids
669
+ assert_equal [invalid_doc], error_docs
670
+ end
671
+
672
+ def test_insert_empty_docs_raises_exception
673
+ assert_raise OperationFailure do
674
+ @test.insert([])
675
+ end
676
+ end
677
+
678
+ def test_insert_empty_docs_with_collect_on_error_raises_exception
679
+ assert_raise OperationFailure do
680
+ @test.insert([], :collect_on_error => true)
681
+ end
682
+ end
683
+
684
+ def limited_collection
685
+ conn = standard_connection(:connect => false)
686
+ is_master = @ismaster.merge('maxBsonObjectSize' => LIMITED_MAX_BSON_SIZE,
687
+ 'maxMessageSizeBytes' => LIMITED_MAX_BSON_SIZE)
688
+ admin_db = Object.new
689
+ admin_db.expects(:command).returns(is_master)
690
+ conn.expects(:[]).with(TEST_DB).returns(admin_db)
691
+ conn.connect
692
+ return conn.db(TEST_DB)["test"]
693
+ end
694
+
695
+ def test_non_operation_failure_halts_insertion_with_continue_on_error
696
+ coll = limited_collection
697
+ if @client.wire_version_feature?(MongoClient::BATCH_COMMANDS)
698
+ coll.db.stubs(:command).raises(OperationTimeout).times(1)
699
+ else
700
+ coll.db.connection.stubs(:send_message_with_gle).raises(OperationTimeout).times(1)
701
+ end
702
+ docs = []
703
+ 10.times do
704
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
705
+ end
706
+ assert_raise OperationTimeout do
707
+ coll.insert(docs, :continue_on_error => true)
708
+ end
709
+ end
710
+
711
+ def test_chunking_batch_insert
712
+ docs = []
713
+ 10.times do
714
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
715
+ end
716
+ limited_collection.insert(docs)
717
+ assert_equal 10, limited_collection.count
718
+ end
719
+
720
+ def test_chunking_batch_insert_without_collect_on_error
721
+ docs = []
722
+ 4.times do
723
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
724
+ end
725
+ invalid_docs = []
726
+ invalid_docs << {'$invalid-key' => 1} # non utf8 encoding
727
+ docs += invalid_docs
728
+ 4.times do
729
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
730
+ end
731
+ assert_raise BSON::InvalidKeyName do
732
+ limited_collection.insert(docs, :collect_on_error => false)
733
+ end
734
+ end
735
+
736
+ def test_chunking_batch_insert_with_collect_on_error
737
+ # Broken for current JRuby
738
+ if RUBY_PLATFORM == 'java' then return end
739
+ docs = []
740
+ 4.times do
741
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
742
+ end
743
+ invalid_docs = []
744
+ invalid_docs << {'$invalid-key' => 1} # non utf8 encoding
745
+ docs += invalid_docs
746
+ 4.times do
747
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
748
+ end
749
+ doc_ids, error_docs = limited_collection.insert(docs, :collect_on_error => true)
750
+ assert_equal 8, doc_ids.count
751
+ assert_equal doc_ids.count, limited_collection.count
752
+ assert_equal error_docs, invalid_docs
753
+ end
754
+
755
+ def test_chunking_batch_insert_with_continue_on_error
756
+ coll = limited_collection
757
+ docs = []
758
+ 4.times do
759
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
760
+ end
761
+ docs << {'_id' => 'b', 'foo' => 'a'}
762
+ docs << {'_id' => 'b', 'foo' => 'c'}
763
+ 4.times do
764
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
765
+ end
766
+ assert_raise OperationFailure do
767
+ coll.insert(docs, :continue_on_error => true)
768
+ end
769
+ assert coll.count >= 6, "write commands need headroom for doc wrapping overhead - count:#{coll.count}"
770
+ end
771
+
772
+ def test_chunking_batch_insert_without_continue_on_error
773
+ docs = []
774
+ 4.times do
775
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
776
+ end
777
+ docs << {'_id' => 'b', 'foo' => 'a'}
778
+ docs << {'_id' => 'b', 'foo' => 'c'}
779
+ 4.times do
780
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
781
+ end
782
+ assert_raise OperationFailure do
783
+ limited_collection.insert(docs, :continue_on_error => false)
784
+ end
785
+ assert_equal 5, limited_collection.count
786
+ end
787
+
788
+ def test_maximum_insert_size
789
+ docs = []
790
+ 3.times do
791
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
792
+ end
793
+ assert_equal limited_collection.insert(docs).length, 3
794
+ end
795
+
796
+ def test_maximum_document_size
797
+ assert_raise InvalidDocument do
798
+ limited_collection.insert({'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM})
799
+ end
800
+ end
801
+
802
+ def test_maximum_save_size
803
+ assert limited_collection.save({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE})
804
+ assert_raise InvalidDocument do
805
+ limited_collection.save({'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM})
806
+ end
807
+ end
808
+
809
+ def test_maximum_remove_size
810
+ assert limited_collection.remove({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE})
811
+ assert_raise InvalidDocument do
812
+ limited_collection.remove({'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM})
813
+ end
814
+ end
815
+
816
+ def test_maximum_update_size
817
+ assert_raise InvalidDocument do
818
+ limited_collection.update(
819
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM},
820
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
821
+ )
822
+ end
823
+
824
+ assert_raise InvalidDocument do
825
+ limited_collection.update(
826
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM},
827
+ {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}
828
+ )
829
+ end
830
+
831
+ assert_raise InvalidDocument do
832
+ limited_collection.update(
833
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM},
834
+ {'foo' => 'a' * LIMITED_BSON_SIZE_WITH_HEADROOM}
835
+ )
836
+ end
837
+
838
+ assert limited_collection.update(
839
+ {'foo' => 'a' * (LIMITED_VALID_VALUE_SIZE/2)},
840
+ {'foo' => 'a' * (LIMITED_VALID_VALUE_SIZE/2)}
841
+ )
842
+ end
843
+
844
+ def test_maximum_query_size
845
+ assert limited_collection.find({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}).to_a
846
+ assert limited_collection.find(
847
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
848
+ {:fields => {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}}
849
+ ).to_a
850
+
851
+ assert_raise InvalidDocument do
852
+ limited_collection.find({'foo' => 'a' * LIMITED_INVALID_VALUE_SIZE}).to_a
853
+ end
854
+
855
+ assert_raise InvalidDocument do
856
+ limited_collection.find(
857
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
858
+ {:fields => {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}}
859
+ ).to_a
860
+ end
861
+ end
862
+
863
+ #if @version >= "1.5.1"
864
+ # def test_safe_mode_with_advanced_safe_with_invalid_options
865
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
866
+ # @test.insert({:foo => 1}, :w => 2, :wtime => 1, :fsync => true)
867
+ # end
868
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
869
+ # @test.update({:foo => 1}, {:foo => 2}, :w => 2, :wtime => 1, :fsync => true)
870
+ # end
871
+ #
872
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
873
+ # @test.remove({:foo => 2}, :w => 2, :wtime => 1, :fsync => true)
874
+ # end
875
+ # end
876
+ #end
877
+
878
+ def test_safe_mode_with_journal_commit_option
879
+ with_default_journaling(@client) do
880
+ @test.insert({:foo => 1}, :j => true)
881
+ @test.update({:foo => 1}, {:foo => 2}, :j => true)
882
+ @test.remove({:foo => 2}, :j => true)
883
+ end
884
+ end
885
+
886
+ def test_jnote_raises_exception
887
+ return unless @version < "2.5.3"
888
+ with_no_journaling(@client) do
889
+ ex = assert_raise Mongo::WriteConcernError do
890
+ @test.insert({:foo => 1}, :j => true)
891
+ end
892
+ result = ex.result
893
+ assert_true result.has_key?("jnote")
894
+ end
895
+ end
896
+
897
+ def test_wnote_raises_exception_with_err_not_nil
898
+ return unless @version < "2.5.3"
899
+ ex = assert_raise Mongo::WriteConcernError do
900
+ @test.insert({:foo => 1}, :w => 2)
901
+ end
902
+ result = ex.result
903
+ assert_not_nil result["err"]
904
+ assert_true result.has_key?("wnote")
905
+ end
906
+
907
+ def test_update
908
+ id1 = @test.save("x" => 5)
909
+ @test.update({}, {"$inc" => {"x" => 1}})
910
+ assert_equal 1, @test.count()
911
+ assert_equal 6, @test.find_one(:_id => id1)["x"]
912
+
913
+ id2 = @test.save("x" => 1)
914
+ @test.update({"x" => 6}, {"$inc" => {"x" => 1}})
915
+ assert_equal 7, @test.find_one(:_id => id1)["x"]
916
+ assert_equal 1, @test.find_one(:_id => id2)["x"]
917
+ end
918
+
919
+ def test_update_check_keys
920
+ return unless @version < "2.5.3"
921
+ @test.save("x" => 1)
922
+ @test.update({"x" => 1}, {"$set" => {"a.b" => 2}})
923
+ assert_equal 2, @test.find_one("x" => 1)["a"]["b"]
924
+
925
+ assert_raise_error BSON::InvalidKeyName do
926
+ @test.update({"x" => 1}, {"a.b" => 3})
927
+ end
928
+ end
929
+
930
+ def test_multi_update
931
+ return unless @version >= "1.1.3"
932
+ @test.save("num" => 10)
933
+ @test.save("num" => 10)
934
+ @test.save("num" => 10)
935
+ assert_equal 3, @test.count
936
+
937
+ @test.update({"num" => 10}, {"$set" => {"num" => 100}}, :multi => true)
938
+ @test.find.each do |doc|
939
+ assert_equal 100, doc["num"]
940
+ end
941
+ end
942
+
943
+ def test_upsert
944
+ @test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
945
+ @test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
946
+
947
+ assert_equal 1, @test.count()
948
+ assert_equal 2, @test.find_one()["count"]
949
+ end
950
+
951
+ def test_safe_update
952
+ @test.create_index("x", :unique => true)
953
+ @test.insert("x" => 5)
954
+ @test.insert("x" => 10)
955
+
956
+ # Can update an indexed collection.
957
+ @test.update({}, {"$inc" => {"x" => 1}})
958
+ assert !@db.error?
959
+
960
+ # Can't duplicate an index.
961
+ assert_raise OperationFailure do
962
+ @test.update({}, {"x" => 10})
963
+ end
964
+ @test.drop
965
+ end
966
+
967
+ def test_safe_save
968
+ @test.create_index("hello", :unique => true)
969
+
970
+ @test.save("hello" => "world")
971
+ @test.save({"hello" => "world"}, :w => 0)
972
+
973
+ assert_raise OperationFailure do
974
+ @test.save({"hello" => "world"})
975
+ end
976
+ @test.drop
977
+ end
978
+
979
+ def test_mocked_safe_remove
980
+ @client = standard_connection
981
+ @db = @client[TEST_DB]
982
+ @test = @db['test-safe-remove']
983
+ @test.save({:a => 20})
984
+ @client.stubs(:receive).returns([[{'ok' => 0, 'err' => 'failed'}], 1, 0])
985
+
986
+ assert_raise OperationFailure do
987
+ @test.remove({})
988
+ end
989
+ @test.drop
990
+ end
991
+
992
+ def test_safe_remove
993
+ @client = standard_connection
994
+ @db = @client[TEST_DB]
995
+ @test = @db['test-safe-remove']
996
+ @test.remove
997
+ @test.save({:a => 50})
998
+ assert_equal 1, @test.remove({})["n"]
999
+ @test.drop
1000
+ end
1001
+
1002
+ def test_remove_return_value
1003
+ assert_equal true, @test.remove({}, :w => 0)
1004
+ end
1005
+
1006
+ def test_remove_with_limit
1007
+ @test.insert([{:n => 1},{:n => 2},{:n => 3}])
1008
+ @test.remove({}, :limit => 1)
1009
+ assert_equal 2, @test.count
1010
+ @test.remove({}, :limit => 0)
1011
+ assert_equal 0, @test.count
1012
+ end
1013
+
1014
+ def test_count
1015
+ @test.drop
1016
+
1017
+ assert_equal 0, @test.count
1018
+ @test.save(:x => 1)
1019
+ @test.save(:x => 2)
1020
+ assert_equal 2, @test.count
1021
+
1022
+ assert_equal 1, @test.count(:query => {:x => 1})
1023
+ assert_equal 1, @test.count(:limit => 1)
1024
+ assert_equal 0, @test.count(:skip => 2)
1025
+ end
1026
+
1027
+ def test_count_with_hint
1028
+ @test.drop
1029
+ @test.save(:i => 1)
1030
+ @test.save(:i => 2)
1031
+ assert_equal 2, @test.count
1032
+
1033
+ @test.ensure_index(BSON::OrderedHash[:i, Mongo::ASCENDING])
1034
+
1035
+ # Check that a named_hint can be specified
1036
+ assert_equal 1, @test.count(:query => { :i => 1 }, :named_hint => '_id_')
1037
+ assert_equal 2, @test.count(:query => { }, :named_hint => '_id_')
1038
+
1039
+ # Verify that the hint is being sent to the server by providing a bad hint
1040
+ if @version > '2.6'
1041
+ assert_raise Mongo::OperationFailure do
1042
+ @test.count(:query => { :i => 1 }, :hint => 'bad_hint')
1043
+ end
1044
+ else
1045
+ assert_equal 1, @test.count(:query => { :i => 1 }, :hint => 'bad_hint')
1046
+ end
1047
+
1048
+ # Verify that the named_hint is being sent to the server by providing a bad hint
1049
+ if @version > '2.6'
1050
+ assert_raise Mongo::OperationFailure do
1051
+ @test.count(:query => { :i => 1 }, :named_hint => 'bad_hint')
1052
+ end
1053
+ else
1054
+ assert_equal 1, @test.count(:query => { :i => 1 }, :named_hint => 'bad_hint')
1055
+ end
1056
+
1057
+ @test.ensure_index(BSON::OrderedHash[:x, Mongo::ASCENDING], :sparse => true)
1058
+
1059
+ # The sparse index won't have any entries.
1060
+ # Check that count returns 0 when using the hint.
1061
+ expected = @version > '2.6' ? 0 : 1
1062
+ assert_equal expected, @test.count(:query => { :i => 1 }, :hint => { 'x' => 1 })
1063
+ assert_equal expected, @test.count(:query => { :i => 1 }, :hint => 'x')
1064
+ assert_equal expected, @test.count(:query => { :i => 1 }, :named_hint => 'x_1')
1065
+
1066
+ # Verify that the hint / named hint set on the collection is used.
1067
+ @test.hint = { 'x' => 1 }
1068
+ assert_equal expected, @test.count(:query => { :i => 1 })
1069
+
1070
+ @test.hint = 'x'
1071
+ assert_equal expected, @test.count(:query => { :i => 1 })
1072
+
1073
+ # The driver should allow x_1, but the code sets named_hint to @hint without
1074
+ # normalizing.
1075
+ @test.named_hint = 'x'
1076
+ assert_equal expected, @test.count(:query => { :i => 1 })
1077
+
1078
+ assert_equal 2, @test.count(:query => { }, :hint => 'x')
1079
+ assert_equal 2, @test.count(:query => { }, :named_hint => 'x_1')
1080
+ end
1081
+
1082
+ # Note: #size is just an alias for #count.
1083
+ def test_size
1084
+ @test.drop
1085
+
1086
+ assert_equal 0, @test.count
1087
+ assert_equal @test.size, @test.count
1088
+ @test.save("x" => 1)
1089
+ @test.save("x" => 2)
1090
+ assert_equal @test.size, @test.count
1091
+ end
1092
+
1093
+ def test_no_timeout_option
1094
+ @test.drop
1095
+
1096
+ assert_raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block." do
1097
+ @test.find({}, :timeout => false)
1098
+ end
1099
+
1100
+ @test.find({}, :timeout => false) do |cursor|
1101
+ assert_equal 0, cursor.count
1102
+ end
1103
+
1104
+ @test.save("x" => 1)
1105
+ @test.save("x" => 2)
1106
+ @test.find({}, :timeout => false) do |cursor|
1107
+ assert_equal 2, cursor.count
1108
+ end
1109
+ end
1110
+
1111
+ def test_default_timeout
1112
+ cursor = @test.find
1113
+ assert_equal true, cursor.timeout
1114
+ end
1115
+
1116
+ def test_fields_as_hash
1117
+ @test.save(:a => 1, :b => 1, :c => 1)
1118
+
1119
+ doc = @test.find_one({:a => 1}, :fields => {:b => 0})
1120
+ assert_nil doc['b']
1121
+ assert doc['a']
1122
+ assert doc['c']
1123
+
1124
+ doc = @test.find_one({:a => 1}, :fields => {:a => 1, :b => 1})
1125
+ assert_nil doc['c']
1126
+ assert doc['a']
1127
+ assert doc['b']
1128
+
1129
+
1130
+ assert_raise Mongo::OperationFailure do
1131
+ @test.find_one({:a => 1}, :fields => {:a => 1, :b => 0})
1132
+ end
1133
+ end
1134
+
1135
+
1136
+ def test_meta_field_projection
1137
+ return unless @version >= '2.5.5'
1138
+ @test.save({ :t => 'spam eggs and spam'})
1139
+ @test.save({ :t => 'spam'})
1140
+ @test.save({ :t => 'egg sausage and bacon'})
1141
+
1142
+ @test.ensure_index([[:t, 'text']])
1143
+ assert @test.find_one({ :$text => { :$search => 'spam' }},
1144
+ { :fields => [:t, { :score => { :$meta => 'textScore' } }] })
1145
+ end
1146
+
1147
+ def test_sort_by_meta
1148
+ return unless @version >= '2.5.5'
1149
+ @test.save({ :t => 'spam eggs and spam'})
1150
+ @test.save({ :t => 'spam'})
1151
+ @test.save({ :t => 'egg sausage and bacon'})
1152
+
1153
+ @test.ensure_index([[:t, 'text']])
1154
+ assert @test.find({ :$text => { :$search => 'spam' }}).sort([:score, { '$meta' => 'textScore' }])
1155
+ assert @test.find({ :$text => { :$search => 'spam' }}).sort(:score => { '$meta' =>'textScore' })
1156
+ end
1157
+
1158
+ def test_fields_with_slice
1159
+ return unless @version >= "1.5.1"
1160
+ @test.save({:foo => [1, 2, 3, 4, 5, 6], :test => 'slice'})
1161
+
1162
+ doc = @test.find_one({:test => 'slice'}, :fields => {'foo' => {'$slice' => [0, 3]}})
1163
+ assert_equal [1, 2, 3], doc['foo']
1164
+ @test.remove
1165
+ end
1166
+
1167
+ def test_find_one
1168
+ id = @test.save("hello" => "world", "foo" => "bar")
1169
+
1170
+ assert_equal "world", @test.find_one()["hello"]
1171
+ assert_equal @test.find_one(id), @test.find_one()
1172
+ assert_equal @test.find_one(nil), @test.find_one()
1173
+ assert_equal @test.find_one({}), @test.find_one()
1174
+ assert_equal @test.find_one("hello" => "world"), @test.find_one()
1175
+ assert_equal @test.find_one(BSON::OrderedHash["hello", "world"]), @test.find_one()
1176
+
1177
+ assert @test.find_one(nil, :fields => ["hello"]).include?("hello")
1178
+ assert !@test.find_one(nil, :fields => ["foo"]).include?("hello")
1179
+ assert_equal ["_id"], @test.find_one(nil, :fields => []).keys()
1180
+
1181
+ assert_equal nil, @test.find_one("hello" => "foo")
1182
+ assert_equal nil, @test.find_one(BSON::OrderedHash["hello", "foo"])
1183
+ assert_equal nil, @test.find_one(ObjectId.new)
1184
+
1185
+ assert_raise TypeError do
1186
+ @test.find_one(6)
1187
+ end
1188
+ end
1189
+
1190
+ def test_find_one_with_max_time_ms
1191
+ with_forced_timeout(@client) do
1192
+ assert_raise ExecutionTimeout do
1193
+ @test.find_one({}, { :max_time_ms => 100 })
1194
+ end
1195
+ end
1196
+ end
1197
+
1198
+ def test_find_one_with_compile_regex_option
1199
+ regex = /.*/
1200
+ @test.insert('r' => /.*/)
1201
+ assert_kind_of Regexp, @test.find_one({})['r']
1202
+ assert_kind_of Regexp, @test.find_one({}, :compile_regex => true)['r']
1203
+ assert_equal BSON::Regex, @test.find_one({}, :compile_regex => false)['r'].class
1204
+ end
1205
+
1206
+ def test_insert_adds_id
1207
+ doc = {"hello" => "world"}
1208
+ @test.insert(doc)
1209
+ assert(doc.include?(:_id))
1210
+
1211
+ docs = [{"hello" => "world"}, {"hello" => "world"}]
1212
+ @test.insert(docs)
1213
+ docs.each do |d|
1214
+ assert(d.include?(:_id))
1215
+ end
1216
+ end
1217
+
1218
+ def test_save_adds_id
1219
+ doc = {"hello" => "world"}
1220
+ @test.save(doc)
1221
+ assert(doc.include?(:_id))
1222
+ end
1223
+
1224
+ def test_optional_find_block
1225
+ 10.times do |i|
1226
+ @test.save("i" => i)
1227
+ end
1228
+
1229
+ x = nil
1230
+ @test.find("i" => 2) { |cursor|
1231
+ x = cursor.count()
1232
+ }
1233
+ assert_equal 1, x
1234
+
1235
+ i = 0
1236
+ @test.find({}, :skip => 5) do |cursor|
1237
+ cursor.each do |doc|
1238
+ i = i + 1
1239
+ end
1240
+ end
1241
+ assert_equal 5, i
1242
+
1243
+ c = nil
1244
+ @test.find() do |cursor|
1245
+ c = cursor
1246
+ end
1247
+ assert c.closed?
1248
+ end
1249
+
1250
+ def setup_aggregate_data
1251
+ # save some data
1252
+ @test.save( {
1253
+ "_id" => 1,
1254
+ "title" => "this is my title",
1255
+ "author" => "bob",
1256
+ "posted" => Time.utc(2000),
1257
+ "pageViews" => 5 ,
1258
+ "tags" => [ "fun" , "good" , "fun" ],
1259
+ "comments" => [
1260
+ { "author" => "joe", "text" => "this is cool" },
1261
+ { "author" => "sam", "text" => "this is bad" }
1262
+ ],
1263
+ "other" => { "foo" => 5 }
1264
+ } )
1265
+
1266
+ @test.save( {
1267
+ "_id" => 2,
1268
+ "title" => "this is your title",
1269
+ "author" => "dave",
1270
+ "posted" => Time.utc(2001),
1271
+ "pageViews" => 7,
1272
+ "tags" => [ "fun" , "nasty" ],
1273
+ "comments" => [
1274
+ { "author" => "barbara" , "text" => "this is interesting" },
1275
+ { "author" => "jenny", "text" => "i like to play pinball", "votes" => 10 }
1276
+ ],
1277
+ "other" => { "bar" => 14 }
1278
+ })
1279
+
1280
+ @test.save( {
1281
+ "_id" => 3,
1282
+ "title" => "this is some other title",
1283
+ "author" => "jane",
1284
+ "posted" => Time.utc(2002),
1285
+ "pageViews" => 6 ,
1286
+ "tags" => [ "nasty", "filthy" ],
1287
+ "comments" => [
1288
+ { "author" => "will" , "text" => "i don't like the color" } ,
1289
+ { "author" => "jenny" , "text" => "can i get that in green?" }
1290
+ ],
1291
+ "other" => { "bar" => 14 }
1292
+ })
1293
+
1294
+ end
1295
+
1296
+ def test_reponds_to_aggregate
1297
+ return unless @version > '2.1.1'
1298
+ assert_respond_to @test, :aggregate
1299
+ end
1300
+
1301
+ def test_aggregate_requires_arguments
1302
+ return unless @version > '2.1.1'
1303
+ assert_raise MongoArgumentError do
1304
+ @test.aggregate()
1305
+ end
1306
+ end
1307
+
1308
+ def test_aggregate_requires_valid_arguments
1309
+ return unless @version > '2.1.1'
1310
+ assert_raise MongoArgumentError do
1311
+ @test.aggregate({})
1312
+ end
1313
+ end
1314
+
1315
+ def test_aggregate_pipeline_operator_format
1316
+ return unless @version > '2.1.1'
1317
+ assert_raise Mongo::OperationFailure do
1318
+ @test.aggregate([{"$project" => "_id"}])
1319
+ end
1320
+ end
1321
+
1322
+ def test_aggregate_pipeline_operators_using_strings
1323
+ return unless @version > '2.1.1'
1324
+ setup_aggregate_data
1325
+ desired_results = [ {"_id"=>1, "pageViews"=>5, "tags"=>["fun", "good", "fun"]},
1326
+ {"_id"=>2, "pageViews"=>7, "tags"=>["fun", "nasty"]},
1327
+ {"_id"=>3, "pageViews"=>6, "tags"=>["nasty", "filthy"]} ]
1328
+ results = @test.aggregate([{"$project" => {"tags" => 1, "pageViews" => 1}}])
1329
+ assert_equal desired_results, results
1330
+ end
1331
+
1332
+ def test_aggregate_pipeline_operators_using_symbols
1333
+ return unless @version > '2.1.1'
1334
+ setup_aggregate_data
1335
+ desired_results = [ {"_id"=>1, "pageViews"=>5, "tags"=>["fun", "good", "fun"]},
1336
+ {"_id"=>2, "pageViews"=>7, "tags"=>["fun", "nasty"]},
1337
+ {"_id"=>3, "pageViews"=>6, "tags"=>["nasty", "filthy"]} ]
1338
+ results = @test.aggregate([{"$project" => {:tags => 1, :pageViews => 1}}])
1339
+ assert_equal desired_results, results
1340
+ end
1341
+
1342
+ def test_aggregate_command_using_sym
1343
+ return unless @version > '2.1.1'
1344
+ cmd = BSON::OrderedHash[:aggregate, @test.name, :pipeline, [{'$match' => {:_id => true}}]]
1345
+ assert @db.command(cmd)
1346
+ end
1347
+
1348
+ def test_aggregate_pipeline_multiple_operators
1349
+ return unless @version > '2.1.1'
1350
+ setup_aggregate_data
1351
+ results = @test.aggregate([{"$project" => {"tags" => 1, "pageViews" => 1}}, {"$match" => {"pageViews" => 7}}])
1352
+ assert_equal 1, results.length
1353
+ end
1354
+
1355
+ def test_aggregate_pipeline_unwind
1356
+ return unless @version > '2.1.1'
1357
+ setup_aggregate_data
1358
+ desired_results = [ {"_id"=>1, "title"=>"this is my title", "author"=>"bob", "posted"=>Time.utc(2000),
1359
+ "pageViews"=>5, "tags"=>"fun", "comments"=>[{"author"=>"joe", "text"=>"this is cool"},
1360
+ {"author"=>"sam", "text"=>"this is bad"}], "other"=>{"foo"=>5 } },
1361
+ {"_id"=>1, "title"=>"this is my title", "author"=>"bob", "posted"=>Time.utc(2000),
1362
+ "pageViews"=>5, "tags"=>"good", "comments"=>[{"author"=>"joe", "text"=>"this is cool"},
1363
+ {"author"=>"sam", "text"=>"this is bad"}], "other"=>{"foo"=>5 } },
1364
+ {"_id"=>1, "title"=>"this is my title", "author"=>"bob", "posted"=>Time.utc(2000),
1365
+ "pageViews"=>5, "tags"=>"fun", "comments"=>[{"author"=>"joe", "text"=>"this is cool"},
1366
+ {"author"=>"sam", "text"=>"this is bad"}], "other"=>{"foo"=>5 } },
1367
+ {"_id"=>2, "title"=>"this is your title", "author"=>"dave", "posted"=>Time.utc(2001),
1368
+ "pageViews"=>7, "tags"=>"fun", "comments"=>[{"author"=>"barbara", "text"=>"this is interesting"},
1369
+ {"author"=>"jenny", "text"=>"i like to play pinball", "votes"=>10 }], "other"=>{"bar"=>14 } },
1370
+ {"_id"=>2, "title"=>"this is your title", "author"=>"dave", "posted"=>Time.utc(2001),
1371
+ "pageViews"=>7, "tags"=>"nasty", "comments"=>[{"author"=>"barbara", "text"=>"this is interesting"},
1372
+ {"author"=>"jenny", "text"=>"i like to play pinball", "votes"=>10 }], "other"=>{"bar"=>14 } },
1373
+ {"_id"=>3, "title"=>"this is some other title", "author"=>"jane", "posted"=>Time.utc(2002),
1374
+ "pageViews"=>6, "tags"=>"nasty", "comments"=>[{"author"=>"will", "text"=>"i don't like the color"},
1375
+ {"author"=>"jenny", "text"=>"can i get that in green?"}], "other"=>{"bar"=>14 } },
1376
+ {"_id"=>3, "title"=>"this is some other title", "author"=>"jane", "posted"=>Time.utc(2002),
1377
+ "pageViews"=>6, "tags"=>"filthy", "comments"=>[{"author"=>"will", "text"=>"i don't like the color"},
1378
+ {"author"=>"jenny", "text"=>"can i get that in green?"}], "other"=>{"bar"=>14 } }
1379
+ ]
1380
+ results = @test.aggregate([{"$unwind"=> "$tags"}])
1381
+ assert_equal desired_results, results
1382
+ end
1383
+
1384
+ def test_aggregate_with_compile_regex_option
1385
+ return unless @version > '2.1.1'
1386
+ # see SERVER-6470
1387
+ return unless @version >= '2.3.2'
1388
+ @test.insert({ 'r' => /.*/ })
1389
+ result1 = @test.aggregate([])
1390
+ assert_kind_of Regexp, result1.first['r']
1391
+
1392
+ result2 = @test.aggregate([], :compile_regex => false)
1393
+ assert_kind_of BSON::Regex, result2.first['r']
1394
+
1395
+ return unless @version >= '2.5.1'
1396
+ result = @test.aggregate([], :compile_regex => false, :cursor => {})
1397
+ assert_kind_of BSON::Regex, result.first['r']
1398
+ end
1399
+
1400
+ def test_out_aggregate
1401
+ return unless @version >= "2.5.2"
1402
+ out_collection = 'test_out'
1403
+ @db.drop_collection(out_collection)
1404
+ setup_aggregate_data
1405
+ docs = @test.find.to_a
1406
+ pipeline = [{:$out => out_collection}]
1407
+ @test.aggregate(pipeline)
1408
+ assert_equal docs, @db.collection(out_collection).find.to_a
1409
+ end
1410
+
1411
+ def test_out_aggregate_nonprimary_sym_warns
1412
+ return unless @version >= "2.5.2"
1413
+ ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
1414
+ pipeline = [{:$out => 'test_out'}]
1415
+ @test.aggregate(pipeline, :read => :secondary)
1416
+ end
1417
+
1418
+ def test_out_aggregate_nonprimary_string_warns
1419
+ return unless @version >= "2.5.2"
1420
+ ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
1421
+ pipeline = [{'$out' => 'test_out'}]
1422
+ @test.aggregate(pipeline, :read => :secondary)
1423
+ end
1424
+
1425
+ def test_out_aggregate_string_returns_raw_response
1426
+ return unless @version >= "2.5.2"
1427
+ pipeline = [{'$out' => 'test_out'}]
1428
+ response = @test.aggregate(pipeline)
1429
+ assert response.respond_to?(:keys)
1430
+ end
1431
+
1432
+ def test_out_aggregate_sym_returns_raw_response
1433
+ return unless @version >= "2.5.2"
1434
+ pipeline = [{:$out => 'test_out'}]
1435
+ response = @test.aggregate(pipeline)
1436
+ assert response.respond_to?(:keys)
1437
+ end
1438
+
1439
+ def test_map_reduce
1440
+ return unless @version > "1.1.1"
1441
+ @test << { "user_id" => 1 }
1442
+ @test << { "user_id" => 2 }
1443
+
1444
+ m = "function() { emit(this.user_id, 1); }"
1445
+ r = "function(k,vals) { return 1; }"
1446
+ res = @test.map_reduce(m, r, :out => 'foo')
1447
+ assert res.find_one({"_id" => 1})
1448
+ assert res.find_one({"_id" => 2})
1449
+ end
1450
+
1451
+ def test_map_reduce_with_code_objects
1452
+ return unless @version > "1.1.1"
1453
+ @test << { "user_id" => 1 }
1454
+ @test << { "user_id" => 2 }
1455
+
1456
+ m = Code.new("function() { emit(this.user_id, 1); }")
1457
+ r = Code.new("function(k,vals) { return 1; }")
1458
+ res = @test.map_reduce(m, r, :out => 'foo')
1459
+ assert res.find_one({"_id" => 1})
1460
+ assert res.find_one({"_id" => 2})
1461
+ end
1462
+
1463
+ def test_map_reduce_with_options
1464
+ return unless @version > "1.1.1"
1465
+ @test.remove
1466
+ @test << { "user_id" => 1 }
1467
+ @test << { "user_id" => 2 }
1468
+ @test << { "user_id" => 3 }
1469
+
1470
+ m = Code.new("function() { emit(this.user_id, 1); }")
1471
+ r = Code.new("function(k,vals) { return 1; }")
1472
+ res = @test.map_reduce(m, r, :query => {"user_id" => {"$gt" => 1}}, :out => 'foo')
1473
+ assert_equal 2, res.count
1474
+ assert res.find_one({"_id" => 2})
1475
+ assert res.find_one({"_id" => 3})
1476
+ end
1477
+
1478
+ def test_map_reduce_with_raw_response
1479
+ return unless @version > "1.1.1"
1480
+ m = Code.new("function() { emit(this.user_id, 1); }")
1481
+ r = Code.new("function(k,vals) { return 1; }")
1482
+ res = @test.map_reduce(m, r, :raw => true, :out => 'foo')
1483
+ assert res["result"]
1484
+ assert res["counts"]
1485
+ assert res["timeMillis"]
1486
+ end
1487
+
1488
+ def test_map_reduce_with_output_collection
1489
+ return unless @version > "1.1.1"
1490
+ output_collection = "test-map-coll"
1491
+ m = Code.new("function() { emit(this.user_id, 1); }")
1492
+ r = Code.new("function(k,vals) { return 1; }")
1493
+ res = @test.map_reduce(m, r, :raw => true, :out => output_collection)
1494
+ assert_equal output_collection, res["result"]
1495
+ assert res["counts"]
1496
+ assert res["timeMillis"]
1497
+ end
1498
+
1499
+ def test_map_reduce_nonprimary_output_collection_reroutes
1500
+ return unless @version > "1.1.1"
1501
+ output_collection = "test-map-coll"
1502
+ m = Code.new("function() { emit(this.user_id, 1); }")
1503
+ r = Code.new("function(k,vals) { return 1; }")
1504
+ Mongo::ReadPreference.expects(:warn).with(regexp_matches(/rerouted to primary/))
1505
+ res = @test.map_reduce(m, r, :raw => true, :out => output_collection, :read => :secondary)
1506
+ end
1507
+
1508
+ def test_map_reduce_with_collection_merge
1509
+ return unless @version >= "1.8.0"
1510
+ @test << {:user_id => 1}
1511
+ @test << {:user_id => 2}
1512
+ output_collection = "test-map-coll"
1513
+ m = Code.new("function() { emit(this.user_id, {count: 1}); }")
1514
+ r = Code.new("function(k,vals) { var sum = 0;" +
1515
+ " vals.forEach(function(v) { sum += v.count;} ); return {count: sum}; }")
1516
+ res = @test.map_reduce(m, r, :out => output_collection)
1517
+
1518
+ @test.remove
1519
+ @test << {:user_id => 3}
1520
+ res = @test.map_reduce(m, r, :out => {:merge => output_collection})
1521
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 1}
1522
+
1523
+ @test.remove
1524
+ @test << {:user_id => 3}
1525
+ res = @test.map_reduce(m, r, :out => {:reduce => output_collection})
1526
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 2}
1527
+
1528
+ assert_raise ArgumentError do
1529
+ @test.map_reduce(m, r, :out => {:inline => 1})
1530
+ end
1531
+
1532
+ @test.map_reduce(m, r, :raw => true, :out => {:inline => 1})
1533
+ assert res["results"]
1534
+ end
1535
+
1536
+ def test_map_reduce_with_collection_output_to_other_db
1537
+ return unless @version > "1.1.1"
1538
+ @test << {:user_id => 1}
1539
+ @test << {:user_id => 2}
1540
+
1541
+ m = Code.new("function() { emit(this.user_id, 1); }")
1542
+ r = Code.new("function(k,vals) { return 1; }")
1543
+ oh = BSON::OrderedHash.new
1544
+ oh[:replace] = 'foo'
1545
+ oh[:db] = TEST_DB
1546
+ res = @test.map_reduce(m, r, :out => (oh))
1547
+ assert res["result"]
1548
+ assert res["counts"]
1549
+ assert res["timeMillis"]
1550
+ assert res.find.to_a.any? {|doc| doc["_id"] == 2 && doc["value"] == 1}
1551
+ end
1552
+
1553
+ def test_aggregation_allow_disk_use
1554
+ return unless @version >= '2.5.5'
1555
+ @db.expects(:command).with do |selector, opts|
1556
+ opts[:allowDiskUse] == true
1557
+ end.returns({ 'ok' => 1 })
1558
+ @test.aggregate([], :allowDiskUse => true)
1559
+ end
1560
+
1561
+ def test_parallel_scan
1562
+ return unless @version >= '2.5.5'
1563
+ 8000.times { |i| @test.insert({ :_id => i }) }
1564
+
1565
+ lock = Mutex.new
1566
+ doc_ids = Set.new
1567
+ threads = []
1568
+ cursors = @test.parallel_scan(3)
1569
+ cursors.each_with_index do |cursor, i|
1570
+ threads << Thread.new do
1571
+ docs = cursor.to_a
1572
+ lock.synchronize do
1573
+ docs.each do |doc|
1574
+ doc_ids << doc['_id']
1575
+ end
1576
+ end
1577
+ end
1578
+ end
1579
+ threads.each(&:join)
1580
+ assert_equal 8000, doc_ids.count
1581
+ end
1582
+
1583
+ def test_find_and_modify
1584
+ return unless @version > "1.3.0"
1585
+ @test << { :a => 1, :processed => false }
1586
+ @test << { :a => 2, :processed => false }
1587
+ @test << { :a => 3, :processed => false }
1588
+
1589
+ @test.find_and_modify(:query => {},
1590
+ :sort => [['a', -1]],
1591
+ :update => {"$set" => {:processed => true}})
1592
+
1593
+ assert @test.find_one({:a => 3})['processed']
1594
+ end
1595
+
1596
+ def test_find_and_modify_with_invalid_options
1597
+ @test << { :a => 1, :processed => false }
1598
+ @test << { :a => 2, :processed => false }
1599
+ @test << { :a => 3, :processed => false }
1600
+
1601
+ assert_raise Mongo::OperationFailure do
1602
+ @test.find_and_modify(:blimey => {})
1603
+ end
1604
+ end
1605
+
1606
+ def test_find_and_modify_with_full_response
1607
+ @test << { :a => 1, :processed => false }
1608
+ @test << { :a => 2, :processed => false }
1609
+ @test << { :a => 3, :processed => false }
1610
+
1611
+ doc = @test.find_and_modify(:query => {},
1612
+ :sort => [['a', -1]],
1613
+ :update => {"$set" => {:processed => true}},
1614
+ :full_response => true,
1615
+ :new => true)
1616
+
1617
+ assert doc['value']['processed']
1618
+ assert ['ok', 'value', 'lastErrorObject'].all? { |key| doc.key?(key) }
1619
+ end
1620
+
1621
+ def test_coll_stats
1622
+ return unless @version >= "1.3.5"
1623
+ @test << {:n => 1}
1624
+ @test.create_index("n")
1625
+
1626
+ assert_equal "#{TEST_DB}.test", @test.stats['ns']
1627
+ @test.drop
1628
+ end
1629
+
1630
+ def test_saving_dates_pre_epoch
1631
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ then return true end
1632
+ begin
1633
+ @test.save({'date' => Time.utc(1600)})
1634
+ assert_in_delta Time.utc(1600), @test.find_one()["date"], 2
1635
+ rescue ArgumentError
1636
+ # See note in test_date_before_epoch (BSONTest)
1637
+ end
1638
+ end
1639
+
1640
+ def test_save_symbol_find_string
1641
+ @test.save(:foo => :mike)
1642
+
1643
+ assert_equal :mike, @test.find_one(:foo => :mike)["foo"]
1644
+ assert_equal :mike, @test.find_one("foo" => :mike)["foo"]
1645
+
1646
+ # TODO enable these tests conditionally based on server version (if >1.0)
1647
+ # assert_equal :mike, @test.find_one(:foo => "mike")["foo"]
1648
+ # assert_equal :mike, @test.find_one("foo" => "mike")["foo"]
1649
+ end
1650
+
1651
+ def test_batch_size
1652
+ n_docs = 6
1653
+ batch_size = n_docs/2
1654
+ n_docs.times do |i|
1655
+ @test.save(:foo => i)
1656
+ end
1657
+
1658
+ doc_count = 0
1659
+ cursor = @test.find({}, :batch_size => batch_size)
1660
+ cursor.next
1661
+ assert_equal batch_size, cursor.instance_variable_get(:@returned)
1662
+ doc_count += batch_size
1663
+ batch_size.times { cursor.next }
1664
+ assert_equal doc_count + batch_size, cursor.instance_variable_get(:@returned)
1665
+ doc_count += batch_size
1666
+ assert_equal n_docs, doc_count
1667
+ end
1668
+
1669
+ def test_batch_size_with_smaller_limit
1670
+ n_docs = 6
1671
+ batch_size = n_docs/2
1672
+ n_docs.times do |i|
1673
+ @test.insert(:foo => i)
1674
+ end
1675
+
1676
+ cursor = @test.find({}, :batch_size => batch_size, :limit => 2)
1677
+ cursor.next
1678
+ assert_equal 2, cursor.instance_variable_get(:@returned)
1679
+ end
1680
+
1681
+ def test_batch_size_with_larger_limit
1682
+ n_docs = 6
1683
+ batch_size = n_docs/2
1684
+ n_docs.times do |i|
1685
+ @test.insert(:foo => i)
1686
+ end
1687
+
1688
+ doc_count = 0
1689
+ cursor = @test.find({}, :batch_size => batch_size, :limit => n_docs + 5)
1690
+ cursor.next
1691
+ assert_equal batch_size, cursor.instance_variable_get(:@returned)
1692
+ doc_count += batch_size
1693
+ batch_size.times { cursor.next }
1694
+ assert_equal doc_count + batch_size, cursor.instance_variable_get(:@returned)
1695
+ doc_count += batch_size
1696
+ assert_equal n_docs, doc_count
1697
+ end
1698
+
1699
+ def test_batch_size_with_negative_limit
1700
+ n_docs = 6
1701
+ batch_size = n_docs/2
1702
+ n_docs.times do |i|
1703
+ @test.insert(:foo => i)
1704
+ end
1705
+
1706
+ cursor = @test.find({}, :batch_size => batch_size, :limit => -7)
1707
+ cursor.next
1708
+ assert_equal n_docs, cursor.instance_variable_get(:@returned)
1709
+ end
1710
+
1711
+ def test_limit_and_skip
1712
+ 10.times do |i|
1713
+ @test.save(:foo => i)
1714
+ end
1715
+
1716
+ assert_equal 5, @test.find({}, :skip => 5).next_document()["foo"]
1717
+ assert_equal nil, @test.find({}, :skip => 10).next_document()
1718
+
1719
+ assert_equal 5, @test.find({}, :limit => 5).to_a.length
1720
+
1721
+ assert_equal 3, @test.find({}, :skip => 3, :limit => 5).next_document()["foo"]
1722
+ assert_equal 5, @test.find({}, :skip => 3, :limit => 5).to_a.length
1723
+ end
1724
+
1725
+ def test_large_limit
1726
+ 2000.times do |i|
1727
+ @test.insert("x" => i, "y" => "mongomongo" * 1000)
1728
+ end
1729
+
1730
+ assert_equal 2000, @test.count
1731
+
1732
+ i = 0
1733
+ y = 0
1734
+ @test.find({}, :limit => 1900).each do |doc|
1735
+ i += 1
1736
+ y += doc["x"]
1737
+ end
1738
+
1739
+ assert_equal 1900, i
1740
+ assert_equal 1804050, y
1741
+ end
1742
+
1743
+ def test_small_limit
1744
+ @test.insert("x" => "hello world")
1745
+ @test.insert("x" => "goodbye world")
1746
+
1747
+ assert_equal 2, @test.count
1748
+
1749
+ x = 0
1750
+ @test.find({}, :limit => 1).each do |doc|
1751
+ x += 1
1752
+ assert_equal "hello world", doc["x"]
1753
+ end
1754
+
1755
+ assert_equal 1, x
1756
+ end
1757
+
1758
+ def test_find_with_transformer
1759
+ klass = Struct.new(:id, :a)
1760
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
1761
+ cursor = @test.find({}, :transformer => transformer)
1762
+ assert_equal(transformer, cursor.transformer)
1763
+ end
1764
+
1765
+ def test_find_one_with_transformer
1766
+ klass = Struct.new(:id, :a)
1767
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
1768
+ id = @test.insert('a' => 1)
1769
+ doc = @test.find_one(id, :transformer => transformer)
1770
+ assert_instance_of(klass, doc)
1771
+ end
1772
+
1773
+ def test_ensure_index
1774
+ @test.drop_indexes
1775
+ @test.insert("x" => "hello world")
1776
+ assert_equal 1, @test.index_information.keys.count #default index
1777
+
1778
+ @test.ensure_index([["x", Mongo::DESCENDING]], {})
1779
+ assert_equal 2, @test.index_information.keys.count
1780
+ assert @test.index_information.keys.include?("x_-1")
1781
+
1782
+ @test.ensure_index([["x", Mongo::ASCENDING]])
1783
+ assert @test.index_information.keys.include?("x_1")
1784
+
1785
+ @test.ensure_index([["type", 1], ["date", -1]])
1786
+ assert @test.index_information.keys.include?("type_1_date_-1")
1787
+
1788
+ @test.drop_index("x_1")
1789
+ assert_equal 3, @test.index_information.keys.count
1790
+ @test.drop_index("x_-1")
1791
+ assert_equal 2, @test.index_information.keys.count
1792
+
1793
+ @test.ensure_index([["x", Mongo::DESCENDING]], {})
1794
+ assert_equal 3, @test.index_information.keys.count
1795
+ assert @test.index_information.keys.include?("x_-1")
1796
+
1797
+ # Make sure that drop_index expires cache properly
1798
+ @test.ensure_index([['a', 1]])
1799
+ assert @test.index_information.keys.include?("a_1")
1800
+ @test.drop_index("a_1")
1801
+ assert !@test.index_information.keys.include?("a_1")
1802
+ @test.ensure_index([['a', 1]])
1803
+ assert @test.index_information.keys.include?("a_1")
1804
+ @test.drop_index("a_1")
1805
+ @test.drop_indexes
1806
+ end
1807
+
1808
+ def test_ensure_index_timeout
1809
+ @db.cache_time = 1
1810
+ coll = @db['ensure_test']
1811
+ coll.expects(:generate_indexes).twice
1812
+ coll.ensure_index([['a', 1]])
1813
+
1814
+ # These will be cached
1815
+ coll.ensure_index([['a', 1]])
1816
+ coll.ensure_index([['a', 1]])
1817
+ coll.ensure_index([['a', 1]])
1818
+ coll.ensure_index([['a', 1]])
1819
+
1820
+ sleep(1)
1821
+ # This won't be, so generate_indexes will be called twice
1822
+ coll.ensure_index([['a', 1]])
1823
+ coll.drop
1824
+ end
1825
+
1826
+ def test_show_disk_loc
1827
+ return unless @version > '2.0.0'
1828
+ @test.save({:a => 1})
1829
+ @test.save({:a => 2})
1830
+ assert @test.find({:a => 1}, :show_disk_loc => true).show_disk_loc
1831
+ doc = @test.find({:a => 1}, :show_disk_loc => true).next
1832
+ assert (doc['$diskLoc'] || doc['$recordId'])
1833
+ @test.remove
1834
+ end
1835
+
1836
+ def test_max_scan
1837
+ return unless @version > '2.0.0'
1838
+ @test.drop
1839
+ n = 100
1840
+ n.times do |i|
1841
+ @test.save({:_id => i, :x => i % 10})
1842
+ end
1843
+ assert_equal(n, @test.find.to_a.size)
1844
+ assert_equal(50, @test.find({}, :max_scan => 50).to_a.size)
1845
+ assert_equal(10, @test.find({:x => 2}).to_a.size)
1846
+ assert_equal(5, @test.find({:x => 2}, :max_scan => 50).to_a.size)
1847
+ @test.ensure_index([[:x, 1]])
1848
+ assert_equal(10, @test.find({:x => 2}, :max_scan => n).to_a.size)
1849
+ @test.drop
1850
+ end
1851
+
1852
+ context "Grouping" do
1853
+ setup do
1854
+ @test.remove
1855
+ @test.save("a" => 1)
1856
+ @test.save("b" => 1)
1857
+ @initial = {"count" => 0}
1858
+ @reduce_function = "function (obj, prev) { prev.count += inc_value; }"
1859
+ end
1860
+
1861
+ should "fail if missing required options" do
1862
+ assert_raise MongoArgumentError do
1863
+ @test.group(:initial => {})
1864
+ end
1865
+
1866
+ assert_raise MongoArgumentError do
1867
+ @test.group(:reduce => "foo")
1868
+ end
1869
+ end
1870
+
1871
+ should "group results using eval form" do
1872
+ assert_equal 1, @test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 0.5}))[0]["count"]
1873
+ assert_equal 2, @test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}))[0]["count"]
1874
+ assert_equal 4, @test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 2}))[0]["count"]
1875
+ end
1876
+
1877
+ should "finalize grouped results" do
1878
+ @finalize = "function(doc) {doc.f = doc.count + 200; }"
1879
+ assert_equal 202, @test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}), :finalize => @finalize)[0]["f"]
1880
+ end
1881
+ end
1882
+
1883
+ context "Grouping with key" do
1884
+ setup do
1885
+ @test.remove
1886
+ @test.save("a" => 1, "pop" => 100)
1887
+ @test.save("a" => 1, "pop" => 100)
1888
+ @test.save("a" => 2, "pop" => 100)
1889
+ @test.save("a" => 2, "pop" => 100)
1890
+ @initial = {"count" => 0, "foo" => 1}
1891
+ @reduce_function = "function (obj, prev) { prev.count += obj.pop; }"
1892
+ end
1893
+
1894
+ should "group" do
1895
+ result = @test.group(:key => :a, :initial => @initial, :reduce => @reduce_function)
1896
+ assert result.all? { |r| r['count'] == 200 }
1897
+ end
1898
+ end
1899
+
1900
+ context "Grouping with a key function" do
1901
+ setup do
1902
+ @test.remove
1903
+ @test.save("a" => 1)
1904
+ @test.save("a" => 2)
1905
+ @test.save("a" => 3)
1906
+ @test.save("a" => 4)
1907
+ @test.save("a" => 5)
1908
+ @initial = {"count" => 0}
1909
+ @keyf = "function (doc) { if(doc.a % 2 == 0) { return {even: true}; } else {return {odd: true}} };"
1910
+ @reduce = "function (obj, prev) { prev.count += 1; }"
1911
+ end
1912
+
1913
+ should "group results" do
1914
+ results = @test.group(:keyf => @keyf, :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
1915
+ assert results[0]['even'] && results[0]['count'] == 2.0
1916
+ assert results[1]['odd'] && results[1]['count'] == 3.0
1917
+ end
1918
+
1919
+ should "group filtered results" do
1920
+ results = @test.group(:keyf => @keyf, :cond => {:a => {'$ne' => 2}},
1921
+ :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
1922
+ assert results[0]['even'] && results[0]['count'] == 1.0
1923
+ assert results[1]['odd'] && results[1]['count'] == 3.0
1924
+ end
1925
+ end
1926
+
1927
+ context "A collection with two records" do
1928
+ setup do
1929
+ @collection = @db.collection('test-collection')
1930
+ @collection.remove
1931
+ @collection.insert({:name => "Jones"})
1932
+ @collection.insert({:name => "Smith"})
1933
+ end
1934
+
1935
+ should "have two records" do
1936
+ assert_equal 2, @collection.size
1937
+ end
1938
+
1939
+ should "remove the two records" do
1940
+ @collection.remove()
1941
+ assert_equal 0, @collection.size
1942
+ end
1943
+
1944
+ should "remove all records if an empty document is specified" do
1945
+ @collection.remove({})
1946
+ assert_equal 0, @collection.find.count
1947
+ end
1948
+
1949
+ should "remove only matching records" do
1950
+ @collection.remove({:name => "Jones"})
1951
+ assert_equal 1, @collection.size
1952
+ end
1953
+ end
1954
+
1955
+ context "Drop index " do
1956
+ setup do
1957
+ @db.drop_collection('test-collection')
1958
+ @collection = @db.collection('test-collection')
1959
+ end
1960
+
1961
+ should "drop an index" do
1962
+ @collection.create_index([['a', Mongo::ASCENDING]])
1963
+ assert @collection.index_information['a_1']
1964
+ @collection.drop_index([['a', Mongo::ASCENDING]])
1965
+ assert_nil @collection.index_information['a_1']
1966
+ end
1967
+
1968
+ should "drop an index which was given a specific name" do
1969
+ @collection.create_index([['a', Mongo::DESCENDING]], {:name => 'i_will_not_fear'})
1970
+ assert @collection.index_information['i_will_not_fear']
1971
+ @collection.drop_index([['a', Mongo::DESCENDING]])
1972
+ assert_nil @collection.index_information['i_will_not_fear']
1973
+ end
1974
+
1975
+ should "drops an composite index" do
1976
+ @collection.create_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
1977
+ assert @collection.index_information['a_-1_b_1']
1978
+ @collection.drop_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
1979
+ assert_nil @collection.index_information['a_-1_b_1']
1980
+ end
1981
+
1982
+ should "drops an index with symbols" do
1983
+ @collection.create_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
1984
+ assert @collection.index_information['a_-1_b_1']
1985
+ @collection.drop_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
1986
+ assert_nil @collection.index_information['a_-1_b_1']
1987
+ end
1988
+ end
1989
+
1990
+ context "Creating indexes " do
1991
+ setup do
1992
+ @db.drop_collection('geo')
1993
+ @db.drop_collection('test-collection')
1994
+ @collection = @db.collection('test-collection')
1995
+ @geo = @db.collection('geo')
1996
+ end
1997
+
1998
+ should "create index using symbols" do
1999
+ @collection.create_index :foo, :name => :bar
2000
+ @geo.create_index :goo, :name => :baz
2001
+ assert @collection.index_information['bar']
2002
+ @collection.drop_index :bar
2003
+ assert_nil @collection.index_information['bar']
2004
+ assert @geo.index_information['baz']
2005
+ @geo.drop_index(:baz)
2006
+ assert_nil @geo.index_information['baz']
2007
+ end
2008
+
2009
+ #should "create a text index" do
2010
+ # @geo.save({'title' => "some text"})
2011
+ # @geo.create_index([['title', Mongo::TEXT]])
2012
+ # assert @geo.index_information['title_text']
2013
+ #end
2014
+
2015
+ should "create a hashed index" do
2016
+ @geo.save({'a' => 1})
2017
+ @geo.create_index([['a', Mongo::HASHED]])
2018
+ assert @geo.index_information['a_hashed']
2019
+ end
2020
+
2021
+ should "create a geospatial index" do
2022
+ @geo.save({'loc' => [-100, 100]})
2023
+ @geo.create_index([['loc', Mongo::GEO2D]])
2024
+ assert @geo.index_information['loc_2d']
2025
+ end
2026
+
2027
+ should "create a geoHaystack index" do
2028
+ @geo.save({ "_id" => 100, "pos" => { "long" => 126.9, "lat" => 35.2 }, "type" => "restaurant"})
2029
+ @geo.create_index([['pos', Mongo::GEOHAYSTACK], ['type', Mongo::ASCENDING]], :bucket_size => 1)
2030
+ assert @geo.index_information['pos_geoHaystack_type_1']
2031
+ end
2032
+
2033
+ should "create a geo 2dsphere index" do
2034
+ @collection.insert({"coordinates" => [ 5 , 5 ], "type" => "Point"})
2035
+ @geo.create_index([['coordinates', Mongo::GEO2DSPHERE]])
2036
+ assert @geo.index_information['coordinates_2dsphere']
2037
+ end
2038
+
2039
+ should "create a unique index" do
2040
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true)
2041
+ assert @collection.index_information['a_1']['unique'] == true
2042
+ end
2043
+
2044
+ should "drop duplicates" do
2045
+ if @version < '2.7'
2046
+ @collection.insert({:a => 1})
2047
+ @collection.insert({:a => 1})
2048
+ assert_equal 2, @collection.find({:a => 1}).count
2049
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :dropDups => true)
2050
+ assert_equal 1, @collection.find({:a => 1}).count
2051
+ end
2052
+ end
2053
+
2054
+ should "drop duplicates with ruby-like drop_dups key" do
2055
+ if @version < '2.7'
2056
+ @collection.insert({:a => 1})
2057
+ @collection.insert({:a => 1})
2058
+ assert_equal 2, @collection.find({:a => 1}).count
2059
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
2060
+ assert_equal 1, @collection.find({:a => 1}).count
2061
+ end
2062
+ end
2063
+
2064
+ should "drop duplicates with ensure_index and drop_dups key" do
2065
+ if @version < '2.7'
2066
+ @collection.insert({:a => 1})
2067
+ @collection.insert({:a => 1})
2068
+ assert_equal 2, @collection.find({:a => 1}).count
2069
+ @collection.ensure_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
2070
+ assert_equal 1, @collection.find({:a => 1}).count
2071
+ end
2072
+ end
2073
+
2074
+ should "create an index in the background" do
2075
+ if @version > '1.3.1'
2076
+ @collection.create_index([['b', Mongo::ASCENDING]], :background => true)
2077
+ assert @collection.index_information['b_1']['background'] == true
2078
+ else
2079
+ assert true
2080
+ end
2081
+ end
2082
+
2083
+ should "require an array of arrays" do
2084
+ assert_raise MongoArgumentError do
2085
+ @collection.create_index(['c', Mongo::ASCENDING])
2086
+ end
2087
+ end
2088
+
2089
+ should "enforce proper index types" do
2090
+ assert_raise MongoArgumentError do
2091
+ @collection.create_index([['c', 'blah']])
2092
+ end
2093
+ end
2094
+
2095
+ should "raise an error if index name is greater than 128" do
2096
+ assert_raise Mongo::OperationFailure do
2097
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
2098
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]])
2099
+ end
2100
+ end
2101
+
2102
+ should "allow for an alternate name to be specified" do
2103
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
2104
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]], :name => 'foo_index')
2105
+ assert @collection.index_information['foo_index']
2106
+ end
2107
+
2108
+ should "generate indexes in the proper order" do
2109
+ key = BSON::OrderedHash['b', 1, 'a', 1]
2110
+ if @version < '2.5.5'
2111
+ @collection.expects(:send_write) do |type, selector, documents, check_keys, opts, collection_name|
2112
+ assert_equal key, selector[:key]
2113
+ end
2114
+ else
2115
+ @collection.db.expects(:command) do |selector|
2116
+ assert_equal key, selector[:indexes].first[:key]
2117
+ end
2118
+ end
2119
+ @collection.create_index([['b', 1], ['a', 1]])
2120
+ end
2121
+
2122
+ should "allow creation of multiple indexes" do
2123
+ assert @collection.create_index([['a', 1]])
2124
+ assert @collection.create_index([['a', 1]])
2125
+ end
2126
+
2127
+ context "with an index created" do
2128
+ setup do
2129
+ @collection.create_index([['b', 1], ['a', 1]])
2130
+ end
2131
+
2132
+ should "return properly ordered index information" do
2133
+ assert @collection.index_information['b_1_a_1']
2134
+ end
2135
+ end
2136
+ end
2137
+
2138
+ context "Capped collections" do
2139
+ setup do
2140
+ @db.drop_collection('log')
2141
+ @capped = @db.create_collection('log', :capped => true, :size => LIMITED_MAX_BSON_SIZE)
2142
+
2143
+ 10.times { |n| @capped.insert({:n => n}) }
2144
+ end
2145
+
2146
+ should "find using a standard cursor" do
2147
+ cursor = @capped.find
2148
+ 10.times do
2149
+ assert cursor.next_document
2150
+ end
2151
+ assert_nil cursor.next_document
2152
+ @capped.insert({:n => 100})
2153
+ assert_nil cursor.next_document
2154
+ end
2155
+
2156
+ should "fail tailable cursor on a non-capped collection" do
2157
+ col = @db['regular-collection']
2158
+ col.insert({:a => 1000})
2159
+ tail = Cursor.new(col, :tailable => true, :order => [['$natural', 1]])
2160
+ assert_raise OperationFailure do
2161
+ tail.next_document
2162
+ end
2163
+ end
2164
+
2165
+ should "find using a tailable cursor" do
2166
+ tail = Cursor.new(@capped, :tailable => true, :order => [['$natural', 1]])
2167
+ 10.times do
2168
+ assert tail.next_document
2169
+ end
2170
+ assert_nil tail.next_document
2171
+ @capped.insert({:n => 100})
2172
+ assert tail.next_document
2173
+ end
2174
+ end
2175
+ end