mongo 2.4.3 → 2.5.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -2
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +3 -2
  5. data/lib/mongo/auth/cr.rb +6 -4
  6. data/lib/mongo/auth/cr/conversation.rb +33 -17
  7. data/lib/mongo/auth/ldap.rb +4 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +19 -9
  9. data/lib/mongo/auth/scram.rb +7 -4
  10. data/lib/mongo/auth/scram/conversation.rb +62 -24
  11. data/lib/mongo/auth/user.rb +10 -0
  12. data/lib/mongo/auth/user/view.rb +44 -22
  13. data/lib/mongo/auth/x509.rb +4 -2
  14. data/lib/mongo/auth/x509/conversation.rb +19 -9
  15. data/lib/mongo/bulk_write.rb +33 -27
  16. data/lib/mongo/bulk_write/combineable.rb +5 -0
  17. data/lib/mongo/bulk_write/transformable.rb +2 -0
  18. data/lib/mongo/bulk_write/validatable.rb +4 -0
  19. data/lib/mongo/client.rb +123 -12
  20. data/lib/mongo/cluster.rb +52 -11
  21. data/lib/mongo/cluster/app_metadata.rb +8 -2
  22. data/lib/mongo/cluster/cursor_reaper.rb +0 -1
  23. data/lib/mongo/cluster/topology.rb +1 -1
  24. data/lib/mongo/collection.rb +114 -27
  25. data/lib/mongo/collection/view.rb +8 -2
  26. data/lib/mongo/collection/view/aggregation.rb +11 -7
  27. data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
  28. data/lib/mongo/collection/view/builder/find_command.rb +5 -3
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
  30. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  31. data/lib/mongo/collection/view/change_stream.rb +160 -0
  32. data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
  33. data/lib/mongo/collection/view/iterable.rb +11 -10
  34. data/lib/mongo/collection/view/map_reduce.rb +22 -18
  35. data/lib/mongo/collection/view/readable.rb +51 -37
  36. data/lib/mongo/collection/view/writable.rb +72 -40
  37. data/lib/mongo/cursor.rb +25 -4
  38. data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
  39. data/lib/mongo/database.rb +22 -11
  40. data/lib/mongo/database/view.rb +16 -12
  41. data/lib/mongo/error.rb +5 -0
  42. data/lib/mongo/error/invalid_session.rb +36 -0
  43. data/lib/mongo/error/missing_resume_token.rb +39 -0
  44. data/lib/mongo/error/operation_failure.rb +17 -0
  45. data/lib/mongo/error/parser.rb +3 -2
  46. data/lib/mongo/error/unknown_payload_type.rb +41 -0
  47. data/lib/mongo/error/unsupported_array_filters.rb +51 -0
  48. data/lib/mongo/error/unsupported_message_type.rb +23 -0
  49. data/lib/mongo/grid/fs_bucket.rb +5 -4
  50. data/lib/mongo/grid/stream/read.rb +3 -2
  51. data/lib/mongo/grid/stream/write.rb +2 -2
  52. data/lib/mongo/index/view.rb +35 -25
  53. data/lib/mongo/monitoring/event/secure.rb +14 -0
  54. data/lib/mongo/operation.rb +16 -0
  55. data/lib/mongo/operation/commands.rb +1 -0
  56. data/lib/mongo/operation/commands/aggregate.rb +9 -5
  57. data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
  58. data/lib/mongo/operation/commands/collections_info.rb +6 -6
  59. data/lib/mongo/operation/commands/command.rb +2 -1
  60. data/lib/mongo/operation/commands/create.rb +6 -2
  61. data/lib/mongo/operation/commands/drop.rb +6 -2
  62. data/lib/mongo/operation/commands/drop_database.rb +6 -2
  63. data/lib/mongo/operation/commands/explain.rb +27 -0
  64. data/lib/mongo/operation/commands/explain/result.rb +52 -0
  65. data/lib/mongo/operation/commands/indexes.rb +1 -1
  66. data/lib/mongo/operation/commands/list_collections.rb +1 -1
  67. data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
  68. data/lib/mongo/operation/commands/list_indexes.rb +1 -1
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
  70. data/lib/mongo/operation/commands/map_reduce.rb +8 -4
  71. data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
  72. data/lib/mongo/operation/commands/user_query.rb +1 -1
  73. data/lib/mongo/operation/commands/users_info.rb +6 -2
  74. data/lib/mongo/operation/executable.rb +4 -1
  75. data/lib/mongo/operation/read_preference.rb +10 -5
  76. data/lib/mongo/operation/result.rb +26 -2
  77. data/lib/mongo/operation/specifiable.rb +13 -1
  78. data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
  79. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
  80. data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
  81. data/lib/mongo/operation/write/command/create_index.rb +6 -1
  82. data/lib/mongo/operation/write/command/delete.rb +28 -4
  83. data/lib/mongo/operation/write/command/drop_index.rb +6 -1
  84. data/lib/mongo/operation/write/command/insert.rb +22 -18
  85. data/lib/mongo/operation/write/command/update.rb +24 -9
  86. data/lib/mongo/operation/write/command/writable.rb +14 -1
  87. data/lib/mongo/operation/write/insert.rb +4 -1
  88. data/lib/mongo/operation/write/insert/result.rb +2 -2
  89. data/lib/mongo/operation/write/update.rb +7 -1
  90. data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
  91. data/lib/mongo/protocol.rb +3 -0
  92. data/lib/mongo/protocol/bit_vector.rb +2 -2
  93. data/lib/mongo/protocol/compressed.rb +135 -0
  94. data/lib/mongo/protocol/delete.rb +8 -6
  95. data/lib/mongo/protocol/get_more.rb +8 -6
  96. data/lib/mongo/protocol/insert.rb +8 -6
  97. data/lib/mongo/protocol/kill_cursors.rb +8 -6
  98. data/lib/mongo/protocol/message.rb +31 -3
  99. data/lib/mongo/protocol/msg.rb +172 -0
  100. data/lib/mongo/protocol/query.rb +26 -6
  101. data/lib/mongo/protocol/registry.rb +76 -0
  102. data/lib/mongo/protocol/reply.rb +10 -5
  103. data/lib/mongo/protocol/serializers.rb +224 -0
  104. data/lib/mongo/protocol/update.rb +8 -6
  105. data/lib/mongo/retryable.rb +4 -2
  106. data/lib/mongo/server.rb +6 -3
  107. data/lib/mongo/server/connectable.rb +1 -1
  108. data/lib/mongo/server/connection.rb +30 -8
  109. data/lib/mongo/server/description.rb +25 -1
  110. data/lib/mongo/server/description/features.rb +4 -1
  111. data/lib/mongo/server/monitor.rb +5 -0
  112. data/lib/mongo/server/monitor/connection.rb +50 -2
  113. data/lib/mongo/server_selector/nearest.rb +10 -4
  114. data/lib/mongo/server_selector/primary.rb +20 -0
  115. data/lib/mongo/server_selector/primary_preferred.rb +10 -4
  116. data/lib/mongo/server_selector/secondary.rb +10 -4
  117. data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
  118. data/lib/mongo/session.rb +180 -0
  119. data/lib/mongo/session/server_session.rb +73 -0
  120. data/lib/mongo/session/session_pool.rb +161 -0
  121. data/lib/mongo/uri.rb +11 -0
  122. data/lib/mongo/version.rb +1 -1
  123. data/mongo.gemspec +2 -1
  124. data/spec/mongo/auth/cr_spec.rb +12 -0
  125. data/spec/mongo/auth/ldap_spec.rb +2 -0
  126. data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
  127. data/spec/mongo/auth/scram_spec.rb +25 -1
  128. data/spec/mongo/auth/user/view_spec.rb +268 -76
  129. data/spec/mongo/auth/x509_spec.rb +2 -0
  130. data/spec/mongo/bulk_write_spec.rb +435 -5
  131. data/spec/mongo/client_spec.rb +356 -39
  132. data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
  133. data/spec/mongo/cluster_spec.rb +176 -0
  134. data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
  135. data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
  136. data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
  137. data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
  138. data/spec/mongo/collection/view/readable_spec.rb +3 -12
  139. data/spec/mongo/collection_spec.rb +1048 -42
  140. data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
  141. data/spec/mongo/cursor_spec.rb +2 -2
  142. data/spec/mongo/database_spec.rb +50 -1
  143. data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
  144. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  145. data/spec/mongo/index/view_spec.rb +146 -8
  146. data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
  147. data/spec/mongo/operation/read/query_spec.rb +2 -1
  148. data/spec/mongo/operation/specifiable_spec.rb +2 -2
  149. data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
  150. data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
  151. data/spec/mongo/operation/write/command/update_spec.rb +93 -10
  152. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  153. data/spec/mongo/operation/write/insert_spec.rb +1 -1
  154. data/spec/mongo/operation/write/update_spec.rb +1 -1
  155. data/spec/mongo/protocol/compressed_spec.rb +66 -0
  156. data/spec/mongo/protocol/delete_spec.rb +14 -0
  157. data/spec/mongo/protocol/get_more_spec.rb +14 -0
  158. data/spec/mongo/protocol/insert_spec.rb +14 -0
  159. data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
  160. data/spec/mongo/protocol/msg_spec.rb +499 -0
  161. data/spec/mongo/protocol/query_spec.rb +45 -0
  162. data/spec/mongo/protocol/registry_spec.rb +31 -0
  163. data/spec/mongo/protocol/reply_spec.rb +14 -0
  164. data/spec/mongo/protocol/update_spec.rb +14 -0
  165. data/spec/mongo/retryable_spec.rb +6 -2
  166. data/spec/mongo/sdam_spec.rb +4 -0
  167. data/spec/mongo/server/connection_spec.rb +4 -2
  168. data/spec/mongo/server/description_spec.rb +28 -1
  169. data/spec/mongo/session/server_session_spec.rb +16 -0
  170. data/spec/mongo/session/session_pool_spec.rb +194 -0
  171. data/spec/mongo/uri_spec.rb +31 -2
  172. data/spec/spec_helper.rb +104 -0
  173. data/spec/support/authorization.rb +6 -1
  174. data/spec/support/crud.rb +3 -1
  175. data/spec/support/crud/write.rb +6 -1
  176. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
  177. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
  178. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
  179. data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
  180. data/spec/support/sdam/rs/discover_passives.yml +2 -2
  181. data/spec/support/sdam/rs/discover_primary.yml +1 -1
  182. data/spec/support/sdam/rs/discover_secondary.yml +1 -1
  183. data/spec/support/sdam/rs/discovery.yml +4 -4
  184. data/spec/support/sdam/rs/equal_electionids.yml +1 -0
  185. data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
  186. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
  187. data/spec/support/sdam/rs/ls_timeout.yml +88 -0
  188. data/spec/support/sdam/rs/member_reconfig.yml +2 -2
  189. data/spec/support/sdam/rs/member_standalone.yml +2 -2
  190. data/spec/support/sdam/rs/new_primary.yml +2 -2
  191. data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
  192. data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
  193. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
  194. data/spec/support/sdam/rs/non_rs_member.yml +1 -1
  195. data/spec/support/sdam/rs/normalize_case.yml +1 -1
  196. data/spec/support/sdam/rs/null_election_id.yml +4 -0
  197. data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
  198. data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
  199. data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
  200. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
  201. data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
  202. data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
  203. data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
  204. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
  205. data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
  206. data/spec/support/sdam/rs/response_from_removed.yml +2 -2
  207. data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
  208. data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
  209. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
  210. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
  211. data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
  212. data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
  213. data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
  214. data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
  215. data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
  216. data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
  217. data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
  218. data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
  219. data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
  220. data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  225. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  226. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  227. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  228. data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
  229. data/spec/support/sdam/single/not_ok_response.yml +1 -1
  230. data/spec/support/sdam/single/standalone_removed.yml +1 -1
  231. data/spec/support/sdam/single/unavailable_seed.yml +1 -1
  232. data/spec/support/server_discovery_and_monitoring.rb +4 -0
  233. data/spec/support/shared/session.rb +236 -0
  234. metadata +53 -15
  235. metadata.gz.sig +0 -0
@@ -43,6 +43,10 @@ describe Mongo::Collection::View::MapReduce do
43
43
  {}
44
44
  end
45
45
 
46
+ let(:map_reduce_spec) do
47
+ map_reduce.send(:map_reduce_spec, double('session'))
48
+ end
49
+
46
50
  before do
47
51
  authorized_collection.insert_many(documents)
48
52
  end
@@ -66,6 +70,23 @@ describe Mongo::Collection::View::MapReduce do
66
70
  end
67
71
  end
68
72
 
73
+ context 'when provided a session' do
74
+
75
+ let(:view_options) do
76
+ { session: session }
77
+ end
78
+
79
+ let(:operation) do
80
+ map_reduce.to_a
81
+ end
82
+
83
+ let(:client) do
84
+ authorized_client
85
+ end
86
+
87
+ it_behaves_like 'an operation using a session'
88
+ end
89
+
69
90
  context 'when out is in the options' do
70
91
 
71
92
  after do
@@ -134,19 +155,75 @@ describe Mongo::Collection::View::MapReduce do
134
155
  expect(new_map_reduce.count).to eq(2)
135
156
  end
136
157
 
137
- context 'when another db is specified', if: (!auth_enabled? && list_command_enabled?) do
158
+ context 'when provided a session' do
159
+
160
+ let(:view_options) do
161
+ { session: session }
162
+ end
163
+
164
+ let(:operation) do
165
+ new_map_reduce.to_a
166
+ end
167
+
168
+ let(:client) do
169
+ authorized_client
170
+ end
171
+
172
+ it_behaves_like 'an operation using a session'
173
+ end
174
+
175
+ context 'when the output collection is iterated' do
176
+
177
+ let(:view_options) do
178
+ { session: session }
179
+ end
180
+
181
+ let(:session) do
182
+ client.start_session
183
+ end
184
+
185
+ let(:view) do
186
+ Mongo::Collection::View.new(client[TEST_COLL], selector, view_options)
187
+ end
188
+
189
+ let(:client) do
190
+ authorized_client.with(monitoring: true).tap do |cl|
191
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
192
+ end
193
+ end
194
+
195
+ let(:subscriber) do
196
+ EventSubscriber.new
197
+ end
198
+
199
+ let(:find_command) do
200
+ subscriber.started_events[-1].command
201
+ end
202
+
203
+ before do
204
+ begin; client[TEST_COLL].create; rescue; end
205
+ begin; client.use('another-db')[TEST_COLL].create; rescue; end
206
+ end
207
+
208
+ it 'uses the session when iterating over the output collection', if: sessions_enabled? do
209
+ new_map_reduce.to_a
210
+ expect(find_command["lsid"]).to eq(BSON::Document.new(session.session_id))
211
+ end
212
+ end
213
+
214
+ context 'when another db is specified', if: (sessions_enabled? && !sharded? && !auth_enabled?) do
138
215
 
139
216
  let(:new_map_reduce) do
140
217
  map_reduce.out(db: 'another-db', replace: 'output_collection')
141
218
  end
142
219
 
143
- it 'iterates over the documents in the result' do
220
+ it 'iterates over the documents in the result', if: (sessions_enabled? && !sharded? && !auth_enabled?) do
144
221
  new_map_reduce.each do |document|
145
222
  expect(document[:value]).to_not be_nil
146
223
  end
147
224
  end
148
225
 
149
- it 'fetches the results from the collection' do
226
+ it 'fetches the results from the collection', if: (sessions_enabled? && !sharded? && !auth_enabled?) do
150
227
  expect(new_map_reduce.count).to eq(2)
151
228
  end
152
229
  end
@@ -168,7 +245,7 @@ describe Mongo::Collection::View::MapReduce do
168
245
  expect(new_map_reduce.count).to eq(2)
169
246
  end
170
247
 
171
- context 'when another db is specified', if: (!auth_enabled? && list_command_enabled?) do
248
+ context 'when another db is specified', if: (!auth_enabled? && !sharded? && list_command_enabled?) do
172
249
 
173
250
  let(:new_map_reduce) do
174
251
  map_reduce.out(db: 'another-db', merge: 'output_collection')
@@ -202,7 +279,7 @@ describe Mongo::Collection::View::MapReduce do
202
279
  expect(new_map_reduce.count).to eq(2)
203
280
  end
204
281
 
205
- context 'when another db is specified', if: (!auth_enabled? && list_command_enabled?) do
282
+ context 'when another db is specified', if: (!auth_enabled? && list_command_enabled? && !sharded?) do
206
283
 
207
284
  let(:new_map_reduce) do
208
285
  map_reduce.out(db: 'another-db', reduce: 'output_collection')
@@ -247,7 +324,7 @@ describe Mongo::Collection::View::MapReduce do
247
324
  end
248
325
 
249
326
  it 'includes the selector in the operation spec' do
250
- expect(map_reduce.send(:map_reduce_spec)[:selector][:query]).to eq(selector)
327
+ expect(map_reduce_spec[:selector][:query]).to eq(selector)
251
328
  end
252
329
  end
253
330
 
@@ -264,7 +341,7 @@ describe Mongo::Collection::View::MapReduce do
264
341
  end
265
342
 
266
343
  it 'includes the selector in the operation spec' do
267
- expect(map_reduce.send(:map_reduce_spec)[:selector][:query]).to eq(selector[:$query])
344
+ expect(map_reduce_spec[:selector][:query]).to eq(selector[:$query])
268
345
  end
269
346
  end
270
347
  end
@@ -302,7 +379,7 @@ describe Mongo::Collection::View::MapReduce do
302
379
  end
303
380
 
304
381
  it 'includes the finalize function in the operation spec' do
305
- expect(new_map_reduce.send(:map_reduce_spec)[:selector][:finalize]).to eq(finalize)
382
+ expect(new_map_reduce.send(:map_reduce_spec, double('session'))[:selector][:finalize]).to eq(finalize)
306
383
  end
307
384
  end
308
385
 
@@ -317,7 +394,7 @@ describe Mongo::Collection::View::MapReduce do
317
394
  end
318
395
 
319
396
  it 'includes the js mode value in the operation spec' do
320
- expect(new_map_reduce.send(:map_reduce_spec)[:selector][:jsMode]).to be(true)
397
+ expect(new_map_reduce.send(:map_reduce_spec, double('session'))[:selector][:jsMode]).to be(true)
321
398
  end
322
399
  end
323
400
 
@@ -336,13 +413,13 @@ describe Mongo::Collection::View::MapReduce do
336
413
  end
337
414
 
338
415
  it 'includes the out value in the operation spec' do
339
- expect(new_map_reduce.send(:map_reduce_spec)[:selector][:out]).to eq(location)
416
+ expect(new_map_reduce.send(:map_reduce_spec, double('session'))[:selector][:out]).to eq(location)
340
417
  end
341
418
 
342
419
  context 'when out is not defined' do
343
420
 
344
421
  it 'defaults to inline' do
345
- expect(map_reduce.send(:map_reduce_spec)[:selector][:out]).to eq('inline' => 1)
422
+ expect(map_reduce_spec[:selector][:out]).to eq('inline' => 1)
346
423
  end
347
424
  end
348
425
 
@@ -361,7 +438,7 @@ describe Mongo::Collection::View::MapReduce do
361
438
  end
362
439
 
363
440
  it 'includes the out value in the operation spec' do
364
- expect(map_reduce.send(:map_reduce_spec)[:selector][:out]).to eq(location)
441
+ expect(map_reduce_spec[:selector][:out]).to eq(location)
365
442
  end
366
443
  end
367
444
 
@@ -472,7 +549,7 @@ describe Mongo::Collection::View::MapReduce do
472
549
  end
473
550
 
474
551
  it 'includes the scope object in the operation spec' do
475
- expect(new_map_reduce.send(:map_reduce_spec)[:selector][:scope]).to eq(object)
552
+ expect(new_map_reduce.send(:map_reduce_spec, double('session'))[:selector][:scope]).to eq(object)
476
553
  end
477
554
  end
478
555
 
@@ -491,7 +568,7 @@ describe Mongo::Collection::View::MapReduce do
491
568
  end
492
569
 
493
570
  it 'includes the verbose option in the operation spec' do
494
- expect(new_map_reduce.send(:map_reduce_spec)[:selector][:verbose]).to eq(verbose)
571
+ expect(new_map_reduce.send(:map_reduce_spec, double('session'))[:selector][:verbose]).to eq(verbose)
495
572
  end
496
573
  end
497
574
 
@@ -506,7 +583,7 @@ describe Mongo::Collection::View::MapReduce do
506
583
  end
507
584
 
508
585
  it 'includes the limit in the operation spec' do
509
- expect(map_reduce.send(:map_reduce_spec)[:selector][:limit]).to be(limit)
586
+ expect(map_reduce_spec[:selector][:limit]).to be(limit)
510
587
  end
511
588
  end
512
589
 
@@ -521,7 +598,7 @@ describe Mongo::Collection::View::MapReduce do
521
598
  end
522
599
 
523
600
  it 'includes the sort object in the operation spec' do
524
- expect(map_reduce.send(:map_reduce_spec)[:selector][:sort][:name]).to eq(sort[:name])
601
+ expect(map_reduce_spec[:selector][:sort][:name]).to eq(sort[:name])
525
602
  end
526
603
  end
527
604
 
@@ -533,7 +610,7 @@ describe Mongo::Collection::View::MapReduce do
533
610
 
534
611
  it 'includes the read preference in the spec' do
535
612
  allow(authorized_collection).to receive(:read_preference).and_return(read_preference)
536
- expect(map_reduce.send(:map_reduce_spec)[:read]).to eq(read_preference)
613
+ expect(map_reduce_spec[:read]).to eq(read_preference)
537
614
  end
538
615
  end
539
616
 
@@ -1134,28 +1134,19 @@ describe Mongo::Collection::View::Readable do
1134
1134
 
1135
1135
  describe '#read' do
1136
1136
 
1137
- context 'when providing a hash' do
1138
-
1139
- it 'converts to a read preference' do
1140
- expect(view.read(:mode => :primary_preferred).read).to be_a(
1141
- Mongo::ServerSelector::PrimaryPreferred
1142
- )
1143
- end
1144
- end
1145
-
1146
1137
  context 'when a read pref is specified' do
1147
1138
 
1148
1139
  let(:options) do
1149
- { :read => Mongo::ServerSelector.get(:mode => :secondary) }
1140
+ { :read => { :mode => :secondary } }
1150
1141
  end
1151
1142
 
1152
1143
  let(:new_read) do
1153
- Mongo::ServerSelector.get(:mode => :secondary_preferred)
1144
+ { :mode => :secondary_preferred }
1154
1145
  end
1155
1146
 
1156
1147
  it 'sets the read preference' do
1157
1148
  new_view = view.read(new_read)
1158
- expect(new_view.read).to eq(new_read)
1149
+ expect(new_view.read).to eq(BSON::Document.new(new_read))
1159
1150
  end
1160
1151
 
1161
1152
  it 'returns a new View' do
@@ -123,7 +123,7 @@ describe Mongo::Collection do
123
123
  end
124
124
 
125
125
  it 'sets the new read options on the new collection' do
126
- expect(new_collection.read_preference).to eq(Mongo::ServerSelector.get(new_options[:read]))
126
+ expect(new_collection.read_preference).to eq(new_options[:read])
127
127
  end
128
128
 
129
129
  context 'when the client has a server selection timeout setting' do
@@ -144,7 +144,7 @@ describe Mongo::Collection do
144
144
  end
145
145
 
146
146
  it 'sets the new read options on the new collection' do
147
- expect(new_collection.read_preference).to eq(Mongo::ServerSelector.get(new_options[:read]))
147
+ expect(new_collection.read_preference).to eq(new_options[:read])
148
148
  expect(new_collection.read_preference).not_to eq(client.read_preference)
149
149
  end
150
150
  end
@@ -156,7 +156,7 @@ describe Mongo::Collection do
156
156
  end
157
157
 
158
158
  it 'sets the new read options on the new collection' do
159
- expect(new_collection.read_preference).to eq(Mongo::ServerSelector.get(new_options[:read]))
159
+ expect(new_collection.read_preference).to eq(new_options[:read])
160
160
  end
161
161
 
162
162
  it 'passes the server_selection_timeout setting to the cluster' do
@@ -182,7 +182,7 @@ describe Mongo::Collection do
182
182
  context 'when the client has a write concern set' do
183
183
 
184
184
  let(:client) do
185
- Mongo::Client.new(ADDRESSES, TEST_OPTIONS.merge(write: { w: 10 }))
185
+ Mongo::Client.new(ADDRESSES, TEST_OPTIONS.merge(write: INVALID_WRITE_CONCERN))
186
186
  end
187
187
 
188
188
  it 'sets the new write options on the new collection' do
@@ -205,7 +205,7 @@ describe Mongo::Collection do
205
205
  end
206
206
 
207
207
  it 'sets the new read options on the new collection' do
208
- expect(new_collection.read_preference).to eq(Mongo::ServerSelector.get(new_options[:read]))
208
+ expect(new_collection.read_preference).to eq(new_options[:read])
209
209
  end
210
210
 
211
211
  it 'sets the new write options on the new collection' do
@@ -230,7 +230,7 @@ describe Mongo::Collection do
230
230
  end
231
231
 
232
232
  it 'sets the new read options on the new collection' do
233
- expect(new_collection.read_preference).to eq(Mongo::ServerSelector.get(new_options[:read]))
233
+ expect(new_collection.read_preference).to eq(new_options[:read])
234
234
  expect(new_collection.read_preference).not_to be(client.read_preference)
235
235
  end
236
236
  end
@@ -250,6 +250,96 @@ describe Mongo::Collection do
250
250
  end
251
251
  end
252
252
 
253
+ describe '#read_preference' do
254
+
255
+ let(:collection) do
256
+ described_class.new(authorized_client.database, :users, options)
257
+ end
258
+
259
+ let(:options) { {} }
260
+
261
+ context 'when a read preference is set in the options' do
262
+
263
+ let(:options) do
264
+ { read: { mode: :secondary } }
265
+ end
266
+
267
+ it 'returns the read preference' do
268
+ expect(collection.read_preference).to eq(options[:read])
269
+ end
270
+ end
271
+
272
+ context 'when a read preference is not set in the options' do
273
+
274
+ context 'when the database has a read preference set' do
275
+
276
+ let(:client) do
277
+ authorized_client.with(read: { mode: :secondary_preferred })
278
+ end
279
+
280
+ let(:collection) do
281
+ described_class.new(client.database, :users, options)
282
+ end
283
+
284
+ it 'returns the database read preference' do
285
+ expect(collection.read_preference).to eq(BSON::Document.new({ mode: :secondary_preferred }))
286
+ end
287
+ end
288
+
289
+ context 'when the database does not have a read preference' do
290
+
291
+ it 'returns nil' do
292
+ expect(collection.read_preference).to be_nil
293
+ end
294
+ end
295
+ end
296
+ end
297
+
298
+ describe '#server_selector' do
299
+
300
+ let(:collection) do
301
+ described_class.new(authorized_client.database, :users, options)
302
+ end
303
+
304
+ let(:options) { {} }
305
+
306
+ context 'when a read preference is set in the options' do
307
+
308
+ let(:options) do
309
+ { read: { mode: :secondary } }
310
+ end
311
+
312
+ it 'returns the server selector for that read preference' do
313
+ expect(collection.server_selector).to be_a(Mongo::ServerSelector::Secondary)
314
+ end
315
+ end
316
+
317
+ context 'when a read preference is not set in the options' do
318
+
319
+ context 'when the database has a read preference set' do
320
+
321
+ let(:client) do
322
+ authorized_client.with(read: { mode: :secondary_preferred })
323
+ end
324
+
325
+ let(:collection) do
326
+ described_class.new(client.database, :users, options)
327
+ end
328
+
329
+ it 'returns the server selector for that read preference' do
330
+ expect(collection.server_selector).to be_a(Mongo::ServerSelector::SecondaryPreferred)
331
+ end
332
+ end
333
+
334
+ context 'when the database does not have a read preference' do
335
+
336
+ it 'returns a primary server selector' do
337
+ expect(collection.server_selector).to be_a(Mongo::ServerSelector::Primary)
338
+ end
339
+ end
340
+ end
341
+ end
342
+
253
343
  describe '#capped?' do
254
344
 
255
345
  let(:database) do
@@ -525,9 +615,40 @@ describe Mongo::Collection do
525
615
  it_behaves_like 'a collection command with a collation option'
526
616
  end
527
617
  end
618
+
619
+ context 'when a session is provided' do
620
+
621
+ let(:collection) do
622
+ authorized_client[:specs]
623
+ end
624
+
625
+ let(:operation) do
626
+ collection.create(session: session)
627
+ end
628
+
629
+ let(:session) do
630
+ authorized_client.start_session
631
+ end
632
+
633
+ let(:client) do
634
+ authorized_client
635
+ end
636
+
637
+ let(:failed_operation) do
638
+ authorized_client[:specs, invalid: true].create(session: session)
639
+ end
640
+
641
+ after do
642
+ collection.drop
643
+ end
644
+
645
+ it_behaves_like 'an operation using a session'
646
+ it_behaves_like 'a failed operation using a session'
647
+ end
528
648
  end
529
649
  end
530
650
 
651
+
531
652
  describe '#drop' do
532
653
 
533
654
  let(:database) do
@@ -539,10 +660,37 @@ describe Mongo::Collection do
539
660
  end
540
661
 
541
662
  context 'when the collection exists' do
663
+
542
664
  before do
543
665
  collection.create
544
666
  end
545
667
 
668
+ context 'when a session is provided' do
669
+
670
+ let(:operation) do
671
+ collection.drop(session: session)
672
+ end
673
+
674
+ let(:failed_operation) do
675
+ collection.with(write: INVALID_WRITE_CONCERN).drop(session: session)
676
+ end
677
+
678
+ let(:session) do
679
+ authorized_client.start_session
680
+ end
681
+
682
+ let(:client) do
683
+ authorized_client
684
+ end
685
+
686
+ after do
687
+ collection.drop
688
+ end
689
+
690
+ it_behaves_like 'an operation using a session'
691
+ it_behaves_like 'a failed operation using a session'
692
+ end
693
+
546
694
  context 'when the collection does not have a write concern set' do
547
695
 
548
696
  let!(:response) do
@@ -609,6 +757,19 @@ describe Mongo::Collection do
609
757
 
610
758
  describe '#find' do
611
759
 
760
+ describe 'updating cluster time' do
761
+
762
+ let(:operation) do
763
+ client[TEST_COLL].find.first
764
+ end
765
+
766
+ let(:second_operation) do
767
+ client[TEST_COLL].find.first
768
+ end
769
+
770
+ it_behaves_like 'an operation updating cluster time'
771
+ end
772
+
612
773
  context 'when provided a filter' do
613
774
 
614
775
  let(:view) do
@@ -707,6 +868,28 @@ describe Mongo::Collection do
707
868
  authorized_collection.find({}, options)
708
869
  end
709
870
 
871
+ context 'when a session is provided' do
872
+
873
+ let(:operation) do
874
+ authorized_collection.find({}, session: session).to_a
875
+ end
876
+
877
+ let(:session) do
878
+ authorized_client.start_session
879
+ end
880
+
881
+ let(:failed_operation) do
882
+ authorized_collection.find({ '$._id' => 1 }, session: session).to_a
883
+ end
884
+
885
+ let(:client) do
886
+ authorized_client
887
+ end
888
+
889
+ it_behaves_like 'an operation using a session'
890
+ it_behaves_like 'a failed operation using a session'
891
+ end
892
+
710
893
  context 'when provided :allow_partial_results' do
711
894
 
712
895
  let(:options) do
@@ -859,6 +1042,28 @@ describe Mongo::Collection do
859
1042
  expect(result.inserted_ids.size).to eq(2)
860
1043
  end
861
1044
 
1045
+ context 'when a session is provided' do
1046
+
1047
+ let(:session) do
1048
+ authorized_client.start_session
1049
+ end
1050
+
1051
+ let(:operation) do
1052
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }], session: session)
1053
+ end
1054
+
1055
+ let(:failed_operation) do
1056
+ authorized_collection.insert_many([{ _id: 'test1' }, { _id: 'test1' }], session: session)
1057
+ end
1058
+
1059
+ let(:client) do
1060
+ authorized_client
1061
+ end
1062
+
1063
+ it_behaves_like 'an operation using a session'
1064
+ it_behaves_like 'a failed operation using a session'
1065
+ end
1066
+
862
1067
  context 'when a document contains invalid keys' do
863
1068
 
864
1069
  let(:docs) do
@@ -929,6 +1134,40 @@ describe Mongo::Collection do
929
1134
  end
930
1135
  end
931
1136
 
1137
+ context 'when the documents are sent with OP_MSG', if: op_msg_enabled? do
1138
+
1139
+ let(:client) do
1140
+ authorized_client.with(heartbeat_frequency: 100).tap do |cl|
1141
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
1142
+ end
1143
+ end
1144
+
1145
+ let(:subscriber) do
1146
+ EventSubscriber.new
1147
+ end
1148
+
1149
+ let(:documents) do
1150
+ [{ '_id' => 1, 'name' => '1'*16777191 }, { '_id' => 'y' }]
1151
+ end
1152
+
1153
+ before do
1154
+ client[TEST_COLL].insert_many(documents)
1155
+ end
1156
+
1157
+ after do
1158
+ client.close
1159
+ end
1160
+
1161
+ let(:insert_events) do
1162
+ subscriber.started_events.select { |e| e.command_name == :insert }
1163
+ end
1164
+
1165
+ it 'sends the documents in one OP_MSG' do
1166
+ expect(insert_events.size).to eq(1)
1167
+ expect(insert_events[0].command['documents']).to eq(documents)
1168
+ end
1169
+ end
1170
+
932
1171
  context 'when collection has a validator', if: find_command_enabled? do
933
1172
 
934
1173
  around(:each) do |spec|
@@ -970,7 +1209,7 @@ describe Mongo::Collection do
970
1209
 
971
1210
  let(:result3) do
972
1211
  collection_with_validator.insert_many(
973
- [{ x: 1 }, { x: 2 }], :bypass_document_validation => true)
1212
+ [{ x: 1 }, { x: 2 }], :bypass_document_validation => true)
974
1213
  end
975
1214
 
976
1215
  it 'inserts successfully' do
@@ -983,6 +1222,19 @@ describe Mongo::Collection do
983
1222
 
984
1223
  describe '#insert_one' do
985
1224
 
1225
+ describe 'updating cluster time' do
1226
+
1227
+ let(:operation) do
1228
+ client[TEST_COLL].insert_one({ name: 'testing' })
1229
+ end
1230
+
1231
+ let(:second_operation) do
1232
+ client[TEST_COLL].insert_one({ name: 'testing' })
1233
+ end
1234
+
1235
+ it_behaves_like 'an operation updating cluster time'
1236
+ end
1237
+
986
1238
  let(:result) do
987
1239
  authorized_collection.insert_one({ name: 'testing' })
988
1240
  end
@@ -999,6 +1251,29 @@ describe Mongo::Collection do
999
1251
  expect(result.inserted_id).to_not be_nil
1000
1252
  end
1001
1253
 
1254
+ context 'when a session is provided' do
1255
+
1256
+ let(:session) do
1257
+ authorized_client.start_session
1258
+ end
1259
+
1260
+ let(:operation) do
1261
+ authorized_collection.insert_one({ name: 'testing' }, session: session)
1262
+ end
1263
+
1264
+ let(:failed_operation) do
1265
+ authorized_collection.insert_one({ _id: 'testing' })
1266
+ authorized_collection.insert_one({ _id: 'testing' }, session: session)
1267
+ end
1268
+
1269
+ let(:client) do
1270
+ authorized_client
1271
+ end
1272
+
1273
+ it_behaves_like 'an operation using a session'
1274
+ it_behaves_like 'a failed operation using a session'
1275
+ end
1276
+
1002
1277
  context 'when the document contains invalid keys' do
1003
1278
 
1004
1279
  let(:doc) do
@@ -1098,7 +1373,7 @@ describe Mongo::Collection do
1098
1373
 
1099
1374
  let(:result3) do
1100
1375
  collection_with_validator.insert_one(
1101
- { x: 1 }, :bypass_document_validation => true)
1376
+ { x: 1 }, :bypass_document_validation => true)
1102
1377
  end
1103
1378
 
1104
1379
  it 'inserts successfully' do
@@ -1144,6 +1419,28 @@ describe Mongo::Collection do
1144
1419
  expect(index_names).to include(*'name_1', '_id_')
1145
1420
  end
1146
1421
 
1422
+ context 'when a session is provided' do
1423
+
1424
+ let(:session) do
1425
+ authorized_client.start_session
1426
+ end
1427
+
1428
+ let(:operation) do
1429
+ authorized_collection.indexes(batch_size: batch_size, session: session).collect { |i| i['name'] }
1430
+ end
1431
+
1432
+ let(:failed_operation) do
1433
+ authorized_collection.indexes(batch_size: -100, session: session).collect { |i| i['name'] }
1434
+ end
1435
+
1436
+ let(:client) do
1437
+ authorized_client
1438
+ end
1439
+
1440
+ it_behaves_like 'an operation using a session'
1441
+ it_behaves_like 'a failed operation using a session'
1442
+ end
1443
+
1147
1444
  context 'when batch size is specified' do
1148
1445
 
1149
1446
  let(:batch_size) { 1 }
@@ -1156,6 +1453,19 @@ describe Mongo::Collection do
1156
1453
 
1157
1454
  describe '#aggregate' do
1158
1455
 
1456
+ describe 'updating cluster time' do
1457
+
1458
+ let(:operation) do
1459
+ client[TEST_COLL].aggregate([]).first
1460
+ end
1461
+
1462
+ let(:second_operation) do
1463
+ client[TEST_COLL].aggregate([]).first
1464
+ end
1465
+
1466
+ it_behaves_like 'an operation updating cluster time'
1467
+ end
1468
+
1159
1469
  it 'returns an Aggregation object' do
1160
1470
  expect(authorized_collection.aggregate([])).to be_a(Mongo::Collection::View::Aggregation)
1161
1471
  end
@@ -1170,6 +1480,28 @@ describe Mongo::Collection do
1170
1480
  expect(authorized_collection.aggregate([], options).options).to eq(BSON::Document.new(options))
1171
1481
  end
1172
1482
 
1483
+ context 'when a session is provided' do
1484
+
1485
+ let(:session) do
1486
+ authorized_client.start_session
1487
+ end
1488
+
1489
+ let(:operation) do
1490
+ authorized_collection.aggregate([], session: session).to_a
1491
+ end
1492
+
1493
+ let(:failed_operation) do
1494
+ authorized_collection.aggregate([ { '$invalid' => 1 }], session: session).to_a
1495
+ end
1496
+
1497
+ let(:client) do
1498
+ authorized_client
1499
+ end
1500
+
1501
+ it_behaves_like 'an operation using a session'
1502
+ it_behaves_like 'a failed operation using a session'
1503
+ end
1504
+
1173
1505
  context 'when a hint is provided' do
1174
1506
 
1175
1507
  let(:options) do
@@ -1251,6 +1583,28 @@ describe Mongo::Collection do
1251
1583
  expect(authorized_collection.count({}, limit: 5)).to eq(5)
1252
1584
  end
1253
1585
 
1586
+ context 'when a session is provided' do
1587
+
1588
+ let(:session) do
1589
+ authorized_client.start_session
1590
+ end
1591
+
1592
+ let(:operation) do
1593
+ authorized_collection.count({}, session: session)
1594
+ end
1595
+
1596
+ let(:failed_operation) do
1597
+ authorized_collection.count({ '$._id' => 1 }, session: session)
1598
+ end
1599
+
1600
+ let(:client) do
1601
+ authorized_client
1602
+ end
1603
+
1604
+ it_behaves_like 'an operation using a session'
1605
+ it_behaves_like 'a failed operation using a session'
1606
+ end
1607
+
1254
1608
  context 'when a collation is specified' do
1255
1609
 
1256
1610
  let(:selector) do
@@ -1327,6 +1681,28 @@ describe Mongo::Collection do
1327
1681
  it 'passes the options to the distinct command' do
1328
1682
  expect(authorized_collection.distinct(:field, {}, max_time_ms: 100).sort).to eq([ 'test1', 'test2', 'test3' ])
1329
1683
  end
1684
+
1685
+ context 'when a session is provided' do
1686
+
1687
+ let(:session) do
1688
+ authorized_client.start_session
1689
+ end
1690
+
1691
+ let(:operation) do
1692
+ authorized_collection.distinct(:field, {}, session: session)
1693
+ end
1694
+
1695
+ let(:failed_operation) do
1696
+ authorized_collection.distinct(:field, { '$._id' => 1 }, session: session)
1697
+ end
1698
+
1699
+ let(:client) do
1700
+ authorized_client
1701
+ end
1702
+
1703
+ it_behaves_like 'an operation using a session'
1704
+ it_behaves_like 'a failed operation using a session'
1705
+ end
1330
1706
  end
1331
1707
 
1332
1708
  context 'when a collation is specified' do
@@ -1401,9 +1777,9 @@ describe Mongo::Collection do
1401
1777
 
1402
1778
  before do
1403
1779
  authorized_collection.insert_many([
1404
- { field: 'test1' },
1405
- { field: 'test1' },
1406
- { field: 'test1' }
1780
+ { field: 'test1' },
1781
+ { field: 'test1' },
1782
+ { field: 'test1' }
1407
1783
  ])
1408
1784
  end
1409
1785
 
@@ -1444,6 +1820,28 @@ describe Mongo::Collection do
1444
1820
  end
1445
1821
  end
1446
1822
 
1823
+ context 'when a session is provided' do
1824
+
1825
+ let(:session) do
1826
+ authorized_client.start_session
1827
+ end
1828
+
1829
+ let(:operation) do
1830
+ authorized_collection.delete_one({}, session: session)
1831
+ end
1832
+
1833
+ let(:failed_operation) do
1834
+ authorized_collection.delete_one({ '$._id' => 1}, session: session)
1835
+ end
1836
+
1837
+ let(:client) do
1838
+ authorized_client
1839
+ end
1840
+
1841
+ it_behaves_like 'an operation using a session'
1842
+ it_behaves_like 'a failed operation using a session'
1843
+ end
1844
+
1447
1845
  context 'when a collation is provided' do
1448
1846
 
1449
1847
  let(:selector) do
@@ -1581,6 +1979,28 @@ describe Mongo::Collection do
1581
1979
  end
1582
1980
  end
1583
1981
 
1982
+ context 'when a session is provided' do
1983
+
1984
+ let(:session) do
1985
+ authorized_client.start_session
1986
+ end
1987
+
1988
+ let(:operation) do
1989
+ authorized_collection.delete_many({}, session: session)
1990
+ end
1991
+
1992
+ let(:failed_operation) do
1993
+ authorized_collection.delete_many({ '$._id' => 1}, session: session)
1994
+ end
1995
+
1996
+ let(:client) do
1997
+ authorized_client
1998
+ end
1999
+
2000
+ it_behaves_like 'an operation using a session'
2001
+ it_behaves_like 'a failed operation using a session'
2002
+ end
2003
+
1584
2004
  context 'when a collation is specified' do
1585
2005
 
1586
2006
  let(:selector) do
@@ -1717,16 +2137,42 @@ describe Mongo::Collection do
1717
2137
  }.to raise_error(Mongo::Error::OperationFailure)
1718
2138
  end
1719
2139
 
1720
- context 'when a read concern is provided', if: find_command_enabled? do
2140
+ context 'when a session is provided' do
1721
2141
 
1722
- let(:result) do
1723
- authorized_collection.with(options).parallel_scan(2)
2142
+ let(:cursors) do
2143
+ authorized_collection.parallel_scan(2, session: session)
1724
2144
  end
1725
2145
 
1726
- context 'when the read concern is valid' do
2146
+ let(:session) do
2147
+ authorized_client.start_session
2148
+ end
1727
2149
 
1728
- let(:options) do
1729
- { read_concern: { level: 'local' }}
2150
+ let(:operation) do
2151
+ cursors.reduce(0) { |total, cursor| total + cursor.to_a.size }
2152
+ end
2153
+
2154
+ let(:failed_operation) do
2155
+ authorized_collection.parallel_scan(-2, session: session)
2156
+ end
2157
+
2158
+ let(:client) do
2159
+ authorized_client
2160
+ end
2161
+
2162
+ it_behaves_like 'an operation using a session'
2163
+ it_behaves_like 'a failed operation using a session'
2164
+ end
2165
+
2166
+ context 'when a read concern is provided', if: find_command_enabled? do
2167
+
2168
+ let(:result) do
2169
+ authorized_collection.with(options).parallel_scan(2)
2170
+ end
2171
+
2172
+ context 'when the read concern is valid' do
2173
+
2174
+ let(:options) do
2175
+ { read_concern: { level: 'local' }}
1730
2176
  end
1731
2177
 
1732
2178
  it 'sends the read concern' do
@@ -1787,7 +2233,7 @@ describe Mongo::Collection do
1787
2233
  context 'when the read concern is valid' do
1788
2234
 
1789
2235
  let(:options) do
1790
- { max_time_ms: 2 }
2236
+ { max_time_ms: 5 }
1791
2237
  end
1792
2238
 
1793
2239
  it 'sends the max time ms value' do
@@ -1966,7 +2412,7 @@ describe Mongo::Collection do
1966
2412
 
1967
2413
  let(:result3) do
1968
2414
  collection_with_validator.replace_one(
1969
- { a: 1 }, { x: 1 }, :bypass_document_validation => true)
2415
+ { a: 1 }, { x: 1 }, :bypass_document_validation => true)
1970
2416
  end
1971
2417
 
1972
2418
  it 'replaces successfully' do
@@ -2074,6 +2520,36 @@ describe Mongo::Collection do
2074
2520
  expect(authorized_collection.find(name: 'bang').count).to eq(1)
2075
2521
  end
2076
2522
  end
2523
+
2524
+ context 'when a session is provided' do
2525
+
2526
+ let(:selector) do
2527
+ { name: 'BANG' }
2528
+ end
2529
+
2530
+ before do
2531
+ authorized_collection.insert_one(name: 'bang')
2532
+ end
2533
+
2534
+ let(:session) do
2535
+ authorized_client.start_session
2536
+ end
2537
+
2538
+ let(:operation) do
2539
+ authorized_collection.replace_one(selector, { name: 'doink' }, session: session)
2540
+ end
2541
+
2542
+ let(:failed_operation) do
2543
+ authorized_collection.replace_one({ '$._id' => 1 }, { name: 'doink' }, session: session)
2544
+ end
2545
+
2546
+ let(:client) do
2547
+ authorized_client
2548
+ end
2549
+
2550
+ it_behaves_like 'an operation using a session'
2551
+ it_behaves_like 'a failed operation using a session'
2552
+ end
2077
2553
  end
2078
2554
 
2079
2555
  describe '#update_many' do
@@ -2113,7 +2589,7 @@ describe Mongo::Collection do
2113
2589
 
2114
2590
  let(:response) do
2115
2591
  authorized_collection.update_many(selector, { '$set'=> { field: 'testing' } },
2116
- upsert: false)
2592
+ upsert: false)
2117
2593
  end
2118
2594
 
2119
2595
  let(:updated) do
@@ -2137,7 +2613,7 @@ describe Mongo::Collection do
2137
2613
 
2138
2614
  let!(:response) do
2139
2615
  authorized_collection.update_many(selector, { '$set'=> { field: 'testing' } },
2140
- upsert: true)
2616
+ upsert: true)
2141
2617
  end
2142
2618
 
2143
2619
  let(:updated) do
@@ -2176,6 +2652,106 @@ describe Mongo::Collection do
2176
2652
  end
2177
2653
  end
2178
2654
 
2655
+ context 'when arrayFilters is provided' do
2656
+
2657
+ let(:selector) do
2658
+ { '$or' => [{ _id: 0 }, { _id: 1 }]}
2659
+ end
2660
+
2661
+ context 'when the server supports arrayFilters', if: array_filters_enabled? do
2662
+
2663
+ before do
2664
+ authorized_collection.insert_many([{
2665
+ _id: 0, x: [
2666
+ { y: 1 },
2667
+ { y: 2 },
2668
+ { y: 3 }
2669
+ ]
2670
+ },
2671
+ {
2672
+ _id: 1,
2673
+ x: [
2674
+ { y: 3 },
2675
+ { y: 2 },
2676
+ { y: 1 }
2677
+ ]
2678
+ }])
2679
+ end
2680
+
2681
+ let(:result) do
2682
+ authorized_collection.update_many(selector,
2683
+ { '$set' => { 'x.$[i].y' => 5 } },
2684
+ options)
2685
+ end
2686
+
2687
+ context 'when a Symbol key is used' do
2688
+
2689
+ let(:options) do
2690
+ { array_filters: [{ 'i.y' => 3 }] }
2691
+ end
2692
+
2693
+ it 'applies the arrayFilters' do
2694
+ expect(result.matched_count).to eq(2)
2695
+ expect(result.modified_count).to eq(2)
2696
+
2697
+ docs = authorized_collection.find(selector, sort: { _id: 1 }).to_a
2698
+ expect(docs[0]['x']).to eq ([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 5 }])
2699
+ expect(docs[1]['x']).to eq ([{ 'y' => 5 }, { 'y' => 2 }, { 'y' => 1 }])
2700
+ end
2701
+ end
2702
+
2703
+ context 'when a String key is used' do
2704
+ let(:options) do
2705
+ { 'array_filters' => [{ 'i.y' => 3 }] }
2706
+ end
2707
+
2708
+ it 'applies the arrayFilters' do
2709
+ expect(result.matched_count).to eq(2)
2710
+ expect(result.modified_count).to eq(2)
2711
+
2712
+ docs = authorized_collection.find({}, sort: { _id: 1 }).to_a
2713
+ expect(docs[0]['x']).to eq ([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 5 }])
2714
+ expect(docs[1]['x']).to eq ([{ 'y' => 5 }, { 'y' => 2 }, { 'y' => 1 }])
2715
+ end
2716
+ end
2717
+ end
2718
+
2719
+ context 'when the server does not support arrayFilters', unless: array_filters_enabled? do
2720
+
2721
+ let(:result) do
2722
+ authorized_collection.update_many(selector,
2723
+ { '$set' => { 'x.$[i].y' => 5 } },
2724
+ options)
2725
+ end
2726
+
2727
+ context 'when a Symbol key is used' do
2728
+
2729
+ let(:options) do
2730
+ { array_filters: [{ 'i.y' => 3 }] }
2731
+ end
2732
+
2733
+ it 'raises an exception' do
2734
+ expect {
2735
+ result
2736
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
2737
+ end
2738
+ end
2739
+
2740
+ context 'when a String key is used' do
2741
+
2742
+ let(:options) do
2743
+ { 'array_filters' => [{ 'i.y' => 3 }] }
2744
+ end
2745
+
2746
+ it 'raises an exception' do
2747
+ expect {
2748
+ result
2749
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
2750
+ end
2751
+ end
2752
+ end
2753
+ end
2754
+
2179
2755
  context 'when the updates fail' do
2180
2756
 
2181
2757
  let(:result) do
@@ -2208,7 +2784,7 @@ describe Mongo::Collection do
2208
2784
 
2209
2785
  let(:result) do
2210
2786
  collection_with_validator.update_many(
2211
- { :a => { '$gt' => 0 } }, '$inc' => { :a => 1 } )
2787
+ { :a => { '$gt' => 0 } }, '$inc' => { :a => 1 } )
2212
2788
  end
2213
2789
 
2214
2790
  it 'updates successfully' do
@@ -2222,7 +2798,7 @@ describe Mongo::Collection do
2222
2798
 
2223
2799
  let(:result2) do
2224
2800
  collection_with_validator.update_many(
2225
- { :a => { '$gt' => 0 } }, '$unset' => { :a => '' })
2801
+ { :a => { '$gt' => 0 } }, '$unset' => { :a => '' })
2226
2802
  end
2227
2803
 
2228
2804
  it 'raises OperationFailure' do
@@ -2236,8 +2812,8 @@ describe Mongo::Collection do
2236
2812
 
2237
2813
  let(:result3) do
2238
2814
  collection_with_validator.update_many(
2239
- { :a => { '$gt' => 0 } }, { '$unset' => { :a => '' } },
2240
- :bypass_document_validation => true)
2815
+ { :a => { '$gt' => 0 } }, { '$unset' => { :a => '' } },
2816
+ :bypass_document_validation => true)
2241
2817
  end
2242
2818
 
2243
2819
  it 'updates successfully' do
@@ -2346,6 +2922,37 @@ describe Mongo::Collection do
2346
2922
  expect(result.written_count).to eq(0)
2347
2923
  end
2348
2924
  end
2925
+
2926
+ context 'when a session is provided' do
2927
+
2928
+ let(:selector) do
2929
+ { name: 'BANG' }
2930
+ end
2931
+
2932
+ let(:operation) do
2933
+ authorized_collection.update_many(selector, { '$set' => {other: 'doink'} }, session: session)
2934
+ end
2935
+
2936
+ before do
2937
+ authorized_collection.insert_one(name: 'bang')
2938
+ authorized_collection.insert_one(name: 'baNG')
2939
+ end
2940
+
2941
+ let(:session) do
2942
+ authorized_client.start_session
2943
+ end
2944
+
2945
+ let(:failed_operation) do
2946
+ authorized_collection.update_many({ '$._id' => 1 }, { '$set' => {other: 'doink'} }, session: session)
2947
+ end
2948
+
2949
+ let(:client) do
2950
+ authorized_client
2951
+ end
2952
+
2953
+ it_behaves_like 'an operation using a session'
2954
+ it_behaves_like 'a failed operation using a session'
2955
+ end
2349
2956
  end
2350
2957
 
2351
2958
  describe '#update_one' do
@@ -2385,7 +2992,7 @@ describe Mongo::Collection do
2385
2992
 
2386
2993
  let(:response) do
2387
2994
  authorized_collection.update_one(selector, { '$set'=> { field: 'testing' } },
2388
- upsert: false)
2995
+ upsert: false)
2389
2996
  end
2390
2997
 
2391
2998
  let(:updated) do
@@ -2409,7 +3016,7 @@ describe Mongo::Collection do
2409
3016
 
2410
3017
  let!(:response) do
2411
3018
  authorized_collection.update_one(selector, { '$set'=> { field: 'testing' } },
2412
- upsert: true)
3019
+ upsert: true)
2413
3020
  end
2414
3021
 
2415
3022
  let(:updated) do
@@ -2480,7 +3087,7 @@ describe Mongo::Collection do
2480
3087
 
2481
3088
  let(:result) do
2482
3089
  collection_with_validator.update_one(
2483
- { :a => { '$gt' => 0 } }, '$inc' => { :a => 1 } )
3090
+ { :a => { '$gt' => 0 } }, '$inc' => { :a => 1 } )
2484
3091
  end
2485
3092
 
2486
3093
  it 'updates successfully' do
@@ -2494,7 +3101,7 @@ describe Mongo::Collection do
2494
3101
 
2495
3102
  let(:result2) do
2496
3103
  collection_with_validator.update_one(
2497
- { :a => { '$gt' => 0 } }, '$unset' => { :a => '' })
3104
+ { :a => { '$gt' => 0 } }, '$unset' => { :a => '' })
2498
3105
  end
2499
3106
 
2500
3107
  it 'raises OperationFailure' do
@@ -2508,8 +3115,8 @@ describe Mongo::Collection do
2508
3115
 
2509
3116
  let(:result3) do
2510
3117
  collection_with_validator.update_one(
2511
- { :a => { '$gt' => 0 } }, { '$unset' => { :a => '' } },
2512
- :bypass_document_validation => true)
3118
+ { :a => { '$gt' => 0 } }, { '$unset' => { :a => '' } },
3119
+ :bypass_document_validation => true)
2513
3120
  end
2514
3121
 
2515
3122
  it 'updates successfully' do
@@ -2616,6 +3223,148 @@ describe Mongo::Collection do
2616
3223
  expect(result.written_count).to eq(0)
2617
3224
  end
2618
3225
  end
3226
+
3227
+
3228
+ context 'when arrayFilters is provided' do
3229
+
3230
+ let(:selector) do
3231
+ { _id: 0}
3232
+ end
3233
+
3234
+ context 'when the server supports arrayFilters', if: array_filters_enabled? do
3235
+
3236
+ before do
3237
+ authorized_collection.insert_one(_id: 0, x: [{ y: 1 }, { y: 2 }, {y: 3 }])
3238
+ end
3239
+
3240
+ let(:result) do
3241
+ authorized_collection.update_one(selector,
3242
+ { '$set' => { 'x.$[i].y' => 5 } },
3243
+ options)
3244
+ end
3245
+
3246
+ context 'when a Symbol key is used' do
3247
+
3248
+ let(:options) do
3249
+ { array_filters: [{ 'i.y' => 3 }] }
3250
+ end
3251
+
3252
+ it 'applies the arrayFilters' do
3253
+ expect(result.matched_count).to eq(1)
3254
+ expect(result.modified_count).to eq(1)
3255
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3256
+ end
3257
+ end
3258
+
3259
+ context 'when a String key is used' do
3260
+
3261
+ let(:options) do
3262
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3263
+ end
3264
+
3265
+ it 'applies the arrayFilters' do
3266
+ expect(result.matched_count).to eq(1)
3267
+ expect(result.modified_count).to eq(1)
3268
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3269
+ end
3270
+ end
3271
+ end
3272
+
3273
+ context 'when the server does not support arrayFilters', unless: array_filters_enabled? do
3274
+
3275
+ let(:result) do
3276
+ authorized_collection.update_one(selector,
3277
+ { '$set' => { 'x.$[i].y' => 5 } },
3278
+ options)
3279
+ end
3280
+
3281
+ context 'when a Symbol key is used' do
3282
+
3283
+ let(:options) do
3284
+ { array_filters: [{ 'i.y' => 3 }] }
3285
+ end
3286
+
3287
+ it 'raises an exception' do
3288
+ expect {
3289
+ result
3290
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3291
+ end
3292
+ end
3293
+
3294
+ context 'when a String key is used' do
3295
+
3296
+ let(:options) do
3297
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3298
+ end
3299
+
3300
+ it 'raises an exception' do
3301
+ expect {
3302
+ result
3303
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3304
+ end
3305
+ end
3306
+ end
3307
+ end
3308
+
3309
+ context 'when the documents are sent with OP_MSG', if: op_msg_enabled? do
3310
+
3311
+ let(:client) do
3312
+ authorized_client.with(heartbeat_frequency: 100).tap do |cl|
3313
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
3314
+ end
3315
+ end
3316
+
3317
+ let(:subscriber) do
3318
+ EventSubscriber.new
3319
+ end
3320
+
3321
+ let(:documents) do
3322
+ [{ '_id' => 1, 'name' => '1'*16777191 }, { '_id' => 'y' }]
3323
+ end
3324
+
3325
+ before do
3326
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test1' }])
3327
+ client[TEST_COLL].update_one({ a: 1 }, {'$set' => { 'name' => '1'*16777149 }})
3328
+ end
3329
+
3330
+ after do
3331
+ client.close
3332
+ end
3333
+
3334
+ let(:update_events) do
3335
+ subscriber.started_events.select { |e| e.command_name == :update }
3336
+ end
3337
+
3338
+ it 'sends the documents in one OP_MSG' do
3339
+ expect(update_events.size).to eq(1)
3340
+ end
3341
+ end
3342
+
3343
+ context 'when a session is provided' do
3344
+
3345
+ before do
3346
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test1' }])
3347
+ end
3348
+
3349
+ let(:session) do
3350
+ authorized_client.start_session
3351
+ end
3352
+
3353
+ let(:operation) do
3354
+ authorized_collection.update_one({ field: 'test' }, { '$set'=> { field: 'testing' } }, session: session)
3355
+ end
3356
+
3357
+ let(:failed_operation) do
3358
+ authorized_collection.update_one({ '$._id' => 1 }, { '$set'=> { field: 'testing' } }, session: session)
3359
+ end
3360
+
3361
+ let(:client) do
3362
+ authorized_client
3363
+ end
3364
+
3365
+ it_behaves_like 'an operation using a session'
3366
+ it_behaves_like 'a failed operation using a session'
3367
+ end
2619
3368
  end
2620
3369
 
2621
3370
  describe '#find_one_and_delete' do
@@ -2630,6 +3379,28 @@ describe Mongo::Collection do
2630
3379
 
2631
3380
  context 'when a matching document is found' do
2632
3381
 
3382
+ context 'when a session is provided' do
3383
+
3384
+ let(:operation) do
3385
+ authorized_collection.find_one_and_delete(selector, session: session)
3386
+ end
3387
+
3388
+ let(:failed_operation) do
3389
+ authorized_collection.find_one_and_delete({ '$._id' => 1 }, session: session)
3390
+ end
3391
+
3392
+ let(:session) do
3393
+ authorized_client.start_session
3394
+ end
3395
+
3396
+ let(:client) do
3397
+ authorized_client
3398
+ end
3399
+
3400
+ it_behaves_like 'an operation using a session'
3401
+ it_behaves_like 'a failed operation using a session'
3402
+ end
3403
+
2633
3404
  context 'when no options are provided' do
2634
3405
 
2635
3406
  let!(:document) do
@@ -2830,6 +3601,28 @@ describe Mongo::Collection do
2830
3601
  end
2831
3602
  end
2832
3603
 
3604
+ context 'when a session is provided' do
3605
+
3606
+ let(:operation) do
3607
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, session: session)
3608
+ end
3609
+
3610
+ let(:failed_operation) do
3611
+ authorized_collection.find_one_and_update({ '$._id' => 1 }, { '$set' => { field: 'testing' }}, session: session)
3612
+ end
3613
+
3614
+ let(:session) do
3615
+ authorized_client.start_session
3616
+ end
3617
+
3618
+ let(:client) do
3619
+ authorized_client
3620
+ end
3621
+
3622
+ it_behaves_like 'an operation using a session'
3623
+ it_behaves_like 'a failed operation using a session'
3624
+ end
3625
+
2833
3626
  context 'when no options are provided' do
2834
3627
 
2835
3628
  let(:document) do
@@ -2979,7 +3772,7 @@ describe Mongo::Collection do
2979
3772
 
2980
3773
  let(:result) do
2981
3774
  collection_with_validator.find_one_and_update(
2982
- { a: 1 }, { '$inc' => { :a => 1 } }, :return_document => :after)
3775
+ { a: 1 }, { '$inc' => { :a => 1 } }, :return_document => :after)
2983
3776
  end
2984
3777
 
2985
3778
  it 'updates successfully' do
@@ -2993,7 +3786,7 @@ describe Mongo::Collection do
2993
3786
 
2994
3787
  let(:result2) do
2995
3788
  collection_with_validator.find_one_and_update(
2996
- { a: 1 }, { '$unset' => { :a => '' } }, :return_document => :after)
3789
+ { a: 1 }, { '$unset' => { :a => '' } }, :return_document => :after)
2997
3790
  end
2998
3791
 
2999
3792
  it 'raises OperationFailure' do
@@ -3007,9 +3800,9 @@ describe Mongo::Collection do
3007
3800
 
3008
3801
  let(:result3) do
3009
3802
  collection_with_validator.find_one_and_update(
3010
- { a: 1 }, { '$unset' => { :a => '' } },
3011
- :bypass_document_validation => true,
3012
- :return_document => :after)
3803
+ { a: 1 }, { '$unset' => { :a => '' } },
3804
+ :bypass_document_validation => true,
3805
+ :return_document => :after)
3013
3806
  end
3014
3807
 
3015
3808
  it 'updates successfully' do
@@ -3024,8 +3817,8 @@ describe Mongo::Collection do
3024
3817
  it 'uses the write concern' do
3025
3818
  expect {
3026
3819
  authorized_collection.find_one_and_update(selector,
3027
- { '$set' => { field: 'testing' }},
3028
- write_concern: { w: 2 })
3820
+ { '$set' => { field: 'testing' }},
3821
+ write_concern: { w: 2 })
3029
3822
  }.to raise_error(Mongo::Error::OperationFailure)
3030
3823
  end
3031
3824
  end
@@ -3114,6 +3907,86 @@ describe Mongo::Collection do
3114
3907
  expect(result).to be_nil
3115
3908
  end
3116
3909
  end
3910
+
3911
+ context 'when arrayFilters is provided' do
3912
+
3913
+ let(:selector) do
3914
+ { _id: 0 }
3915
+ end
3916
+
3917
+ context 'when the server supports arrayFilters', if: array_filters_enabled? do
3918
+
3919
+ before do
3920
+ authorized_collection.insert_one(_id: 0, x: [{ y: 1 }, { y: 2 }, { y: 3 }])
3921
+ end
3922
+
3923
+ let(:result) do
3924
+ authorized_collection.find_one_and_update(selector,
3925
+ { '$set' => { 'x.$[i].y' => 5 } },
3926
+ options)
3927
+ end
3928
+
3929
+ context 'when a Symbol key is used' do
3930
+
3931
+ let(:options) do
3932
+ { array_filters: [{ 'i.y' => 3 }] }
3933
+ end
3934
+
3935
+
3936
+ it 'applies the arrayFilters' do
3937
+ expect(result['x']).to eq([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 3 }])
3938
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3939
+ end
3940
+ end
3941
+
3942
+ context 'when a String key is used' do
3943
+
3944
+ let(:options) do
3945
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3946
+ end
3947
+
3948
+ it 'applies the arrayFilters' do
3949
+ expect(result['x']).to eq([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 3 }])
3950
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3951
+ end
3952
+ end
3953
+ end
3954
+
3955
+ context 'when the server selected does not support arrayFilters', unless: array_filters_enabled? do
3956
+
3957
+ let(:result) do
3958
+ authorized_collection.find_one_and_update(selector,
3959
+ { '$set' => { 'x.$[i].y' => 5 } },
3960
+ options)
3961
+ end
3962
+
3963
+ context 'when a Symbol key is used' do
3964
+
3965
+ let(:options) do
3966
+ { array_filters: [{ 'i.y' => 3 }] }
3967
+ end
3968
+
3969
+ it 'raises an exception' do
3970
+ expect {
3971
+ result
3972
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3973
+ end
3974
+ end
3975
+
3976
+ context 'when a String key is used' do
3977
+
3978
+ let(:options) do
3979
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3980
+ end
3981
+
3982
+ it 'raises an exception' do
3983
+ expect {
3984
+ result
3985
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3986
+ end
3987
+ end
3988
+ end
3989
+ end
3117
3990
  end
3118
3991
 
3119
3992
  describe '#find_one_and_replace' do
@@ -3139,6 +4012,28 @@ describe Mongo::Collection do
3139
4012
  end
3140
4013
  end
3141
4014
 
4015
+ context 'when a session is provided' do
4016
+
4017
+ let(:operation) do
4018
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, session: session)
4019
+ end
4020
+
4021
+ let(:failed_operation) do
4022
+ authorized_collection.find_one_and_replace({ '$._id' => 1}, { field: 'testing' }, session: session)
4023
+ end
4024
+
4025
+ let(:session) do
4026
+ authorized_client.start_session
4027
+ end
4028
+
4029
+ let(:client) do
4030
+ authorized_client
4031
+ end
4032
+
4033
+ it_behaves_like 'an operation using a session'
4034
+ it_behaves_like 'a failed operation using a session'
4035
+ end
4036
+
3142
4037
  context 'when return_document options are provided' do
3143
4038
 
3144
4039
  context 'when return_document is :after' do
@@ -3262,7 +4157,7 @@ describe Mongo::Collection do
3262
4157
 
3263
4158
  let(:result) do
3264
4159
  collection_with_validator.find_one_and_replace(
3265
- { a: 1 }, { a: 5 }, :return_document => :after)
4160
+ { a: 1 }, { a: 5 }, :return_document => :after)
3266
4161
  end
3267
4162
 
3268
4163
  it 'replaces successfully when document is valid' do
@@ -3276,7 +4171,7 @@ describe Mongo::Collection do
3276
4171
 
3277
4172
  let(:result2) do
3278
4173
  collection_with_validator.find_one_and_replace(
3279
- { a: 1 }, { x: 5 }, :return_document => :after)
4174
+ { a: 1 }, { x: 5 }, :return_document => :after)
3280
4175
  end
3281
4176
 
3282
4177
  it 'raises OperationFailure' do
@@ -3290,8 +4185,8 @@ describe Mongo::Collection do
3290
4185
 
3291
4186
  let(:result3) do
3292
4187
  collection_with_validator.find_one_and_replace(
3293
- { a: 1 }, { x: 1 }, :bypass_document_validation => true,
3294
- :return_document => :after)
4188
+ { a: 1 }, { x: 1 }, :bypass_document_validation => true,
4189
+ :return_document => :after)
3295
4190
  end
3296
4191
 
3297
4192
  it 'replaces successfully' do
@@ -3398,4 +4293,115 @@ describe Mongo::Collection do
3398
4293
  end
3399
4294
  end
3400
4295
  end
4296
+
4297
+ describe '#watch' do
4298
+
4299
+ context 'when change streams can be tested', if: test_change_streams? do
4300
+
4301
+ let(:change_stream) do
4302
+ authorized_collection.watch
4303
+ end
4304
+
4305
+ let(:enum) do
4306
+ change_stream.to_enum
4307
+ end
4308
+
4309
+ before do
4310
+ change_stream
4311
+ authorized_collection.insert_one(a: 1)
4312
+ end
4313
+
4314
+ context 'when no options are provided' do
4315
+
4316
+ context 'when the operation type is an insert' do
4317
+
4318
+ it 'returns the change' do
4319
+ expect(enum.next[:fullDocument][:a]).to eq(1)
4320
+ end
4321
+ end
4322
+
4323
+ context 'when the operation type is an update' do
4324
+
4325
+ before do
4326
+ authorized_collection.update_one({ a: 1 }, { '$set' => { a: 2 } })
4327
+ end
4328
+
4329
+ let(:change_doc) do
4330
+ enum.next
4331
+ enum.next
4332
+ end
4333
+
4334
+ it 'returns the change' do
4335
+ expect(change_doc[:operationType]).to eq('update')
4336
+ expect(change_doc[:updateDescription][:updatedFields]).to eq('a' => 2)
4337
+ end
4338
+ end
4339
+ end
4340
+
4341
+ context 'when options are provided' do
4342
+
4343
+ context 'when full_document is updateLookup' do
4344
+
4345
+ let(:change_stream) do
4346
+ authorized_collection.watch([], full_document: 'updateLookup').to_enum
4347
+ end
4348
+
4349
+ before do
4350
+ authorized_collection.update_one({ a: 1 }, { '$set' => { a: 2 } })
4351
+ end
4352
+
4353
+ let(:change_doc) do
4354
+ enum.next
4355
+ enum.next
4356
+ end
4357
+
4358
+ it 'returns the change' do
4359
+ expect(change_doc[:operationType]).to eq('update')
4360
+ expect(change_doc[:fullDocument][:a]).to eq(2)
4361
+ end
4362
+ end
4363
+
4364
+ context 'when batch_size is provided' do
4365
+
4366
+ before do
4367
+ authorized_collection.insert_one(a: 2)
4368
+ authorized_collection.insert_one(a: 3)
4369
+ end
4370
+
4371
+ let(:change_stream) do
4372
+ authorized_collection.watch([], batch_size: 2)
4373
+ end
4374
+
4375
+ it 'returns the documents in the batch size specified' do
4376
+ expect(change_stream.instance_variable_get(:@cursor)).to receive(:get_more).once.and_call_original
4377
+ enum.next
4378
+ enum.next
4379
+ end
4380
+ end
4381
+
4382
+ context 'when collation is provided' do
4383
+ # pending 'server support for collation with the $changeStream operator'
4384
+ #
4385
+ # before do
4386
+ # authorized_collection.update_one({ a: 1 }, { '$set' => { a: 2 } })
4387
+ # end
4388
+ #
4389
+ # let(:change_doc) do
4390
+ # change_stream.next
4391
+ # change_stream.next
4392
+ # end
4393
+ #
4394
+ # let(:change_stream) do
4395
+ # authorized_collection.watch([ { '$match' => { operationType: 'UPDATE'}}],
4396
+ # collation: { locale: 'en_US', strength: 2 } ).to_enum
4397
+ # end
4398
+ #
4399
+ # it 'returns the change' do
4400
+ # expect(change_doc[:operationType]).to eq('update')
4401
+ # expect(change_doc[:fullDocument][:a]).to eq(2)
4402
+ # end
4403
+ end
4404
+ end
4405
+ end
4406
+ end
3401
4407
  end