mongo 2.4.3 → 2.5.0.beta

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 (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