mongo 2.13.0.beta1 → 2.13.0.rc1

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 (170) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -5
  4. data/Rakefile +15 -9
  5. data/lib/mongo.rb +4 -2
  6. data/lib/mongo/auth/aws/request.rb +4 -2
  7. data/lib/mongo/bulk_write.rb +1 -0
  8. data/lib/mongo/client.rb +143 -21
  9. data/lib/mongo/cluster.rb +53 -17
  10. data/lib/mongo/cluster/sdam_flow.rb +13 -10
  11. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
  12. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  13. data/lib/mongo/cluster/topology/single.rb +1 -1
  14. data/lib/mongo/collection.rb +17 -13
  15. data/lib/mongo/collection/view/readable.rb +3 -1
  16. data/lib/mongo/collection/view/writable.rb +41 -5
  17. data/lib/mongo/database.rb +31 -4
  18. data/lib/mongo/database/view.rb +19 -4
  19. data/lib/mongo/distinguishing_semaphore.rb +55 -0
  20. data/lib/mongo/error.rb +1 -0
  21. data/lib/mongo/error/invalid_session.rb +2 -1
  22. data/lib/mongo/error/operation_failure.rb +6 -0
  23. data/lib/mongo/error/sessions_not_supported.rb +35 -0
  24. data/lib/mongo/event/base.rb +6 -0
  25. data/lib/mongo/grid/file.rb +5 -0
  26. data/lib/mongo/grid/file/chunk.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +15 -13
  28. data/lib/mongo/grid/stream/write.rb +9 -3
  29. data/lib/mongo/monitoring.rb +38 -0
  30. data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
  31. data/lib/mongo/monitoring/event/command_failed.rb +11 -0
  32. data/lib/mongo/monitoring/event/command_started.rb +37 -2
  33. data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
  34. data/lib/mongo/monitoring/event/server_closed.rb +1 -1
  35. data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
  36. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
  37. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
  38. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
  39. data/lib/mongo/monitoring/event/server_opening.rb +1 -1
  40. data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
  41. data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
  42. data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
  43. data/lib/mongo/monitoring/publishable.rb +6 -3
  44. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
  45. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  46. data/lib/mongo/protocol/message.rb +36 -8
  47. data/lib/mongo/protocol/msg.rb +14 -0
  48. data/lib/mongo/protocol/serializers.rb +5 -2
  49. data/lib/mongo/server.rb +10 -3
  50. data/lib/mongo/server/connection.rb +4 -4
  51. data/lib/mongo/server/connection_base.rb +3 -1
  52. data/lib/mongo/server/description.rb +5 -0
  53. data/lib/mongo/server/monitor.rb +76 -44
  54. data/lib/mongo/server/monitor/connection.rb +55 -7
  55. data/lib/mongo/server/pending_connection.rb +14 -4
  56. data/lib/mongo/server/push_monitor.rb +173 -0
  57. data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
  58. data/lib/mongo/server_selector.rb +0 -1
  59. data/lib/mongo/server_selector/base.rb +579 -1
  60. data/lib/mongo/server_selector/nearest.rb +1 -6
  61. data/lib/mongo/server_selector/primary.rb +1 -6
  62. data/lib/mongo/server_selector/primary_preferred.rb +7 -10
  63. data/lib/mongo/server_selector/secondary.rb +1 -6
  64. data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
  65. data/lib/mongo/session.rb +2 -0
  66. data/lib/mongo/socket.rb +20 -8
  67. data/lib/mongo/socket/ssl.rb +1 -1
  68. data/lib/mongo/socket/tcp.rb +1 -1
  69. data/lib/mongo/topology_version.rb +9 -0
  70. data/lib/mongo/utils.rb +62 -0
  71. data/lib/mongo/version.rb +1 -1
  72. data/spec/README.aws-auth.md +2 -2
  73. data/spec/integration/awaited_ismaster_spec.rb +28 -0
  74. data/spec/integration/change_stream_examples_spec.rb +6 -2
  75. data/spec/integration/check_clean_slate_spec.rb +16 -0
  76. data/spec/integration/client_construction_spec.rb +1 -0
  77. data/spec/integration/connect_single_rs_name_spec.rb +5 -2
  78. data/spec/integration/connection_spec.rb +7 -4
  79. data/spec/integration/crud_spec.rb +4 -4
  80. data/spec/integration/docs_examples_spec.rb +6 -0
  81. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  82. data/spec/integration/heartbeat_events_spec.rb +4 -23
  83. data/spec/integration/read_concern_spec.rb +1 -1
  84. data/spec/integration/retryable_errors_spec.rb +1 -1
  85. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -2
  86. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
  87. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
  88. data/spec/integration/sdam_error_handling_spec.rb +37 -15
  89. data/spec/integration/sdam_events_spec.rb +77 -6
  90. data/spec/integration/sdam_prose_spec.rb +64 -0
  91. data/spec/integration/server_monitor_spec.rb +25 -1
  92. data/spec/integration/size_limit_spec.rb +7 -3
  93. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  94. data/spec/integration/ssl_uri_options_spec.rb +2 -2
  95. data/spec/integration/zlib_compression_spec.rb +25 -0
  96. data/spec/lite_spec_helper.rb +12 -5
  97. data/spec/mongo/auth/aws/request_spec.rb +76 -0
  98. data/spec/mongo/auth/scram_spec.rb +1 -1
  99. data/spec/mongo/client_construction_spec.rb +207 -0
  100. data/spec/mongo/client_spec.rb +38 -3
  101. data/spec/mongo/cluster/topology/replica_set_spec.rb +52 -9
  102. data/spec/mongo/cluster/topology/single_spec.rb +4 -2
  103. data/spec/mongo/cluster_spec.rb +34 -35
  104. data/spec/mongo/collection/view/change_stream_resume_spec.rb +6 -6
  105. data/spec/mongo/collection_spec.rb +500 -0
  106. data/spec/mongo/database_spec.rb +245 -8
  107. data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
  108. data/spec/mongo/error/operation_failure_spec.rb +40 -0
  109. data/spec/mongo/index/view_spec.rb +2 -2
  110. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
  111. data/spec/mongo/protocol/msg_spec.rb +10 -0
  112. data/spec/mongo/semaphore_spec.rb +51 -0
  113. data/spec/mongo/server/connection_auth_spec.rb +2 -2
  114. data/spec/mongo/server_selector/nearest_spec.rb +23 -23
  115. data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
  116. data/spec/mongo/server_selector/primary_spec.rb +9 -9
  117. data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
  118. data/spec/mongo/server_selector/secondary_spec.rb +18 -18
  119. data/spec/mongo/server_selector_spec.rb +4 -4
  120. data/spec/mongo/session_spec.rb +35 -0
  121. data/spec/runners/change_streams/test.rb +2 -2
  122. data/spec/runners/cmap.rb +1 -1
  123. data/spec/runners/command_monitoring.rb +3 -34
  124. data/spec/runners/crud/context.rb +9 -5
  125. data/spec/runners/crud/operation.rb +59 -27
  126. data/spec/runners/crud/spec.rb +0 -8
  127. data/spec/runners/crud/test.rb +1 -1
  128. data/spec/runners/sdam.rb +2 -2
  129. data/spec/runners/server_selection.rb +242 -28
  130. data/spec/runners/transactions.rb +12 -12
  131. data/spec/runners/transactions/operation.rb +151 -25
  132. data/spec/runners/transactions/test.rb +60 -16
  133. data/spec/spec_tests/command_monitoring_spec.rb +22 -12
  134. data/spec/spec_tests/crud_spec.rb +1 -1
  135. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -8
  136. data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
  137. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
  138. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
  139. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
  140. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
  141. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
  142. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
  143. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
  144. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
  145. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
  146. data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
  147. data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
  148. data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
  149. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
  150. data/spec/spec_tests/max_staleness_spec.rb +4 -142
  151. data/spec/spec_tests/retryable_reads_spec.rb +2 -2
  152. data/spec/spec_tests/sdam_integration_spec.rb +13 -0
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
  154. data/spec/spec_tests/server_selection_spec.rb +4 -116
  155. data/spec/stress/cleanup_spec.rb +17 -2
  156. data/spec/stress/connection_pool_stress_spec.rb +10 -8
  157. data/spec/support/child_process_helper.rb +78 -0
  158. data/spec/support/client_registry.rb +1 -0
  159. data/spec/support/cluster_config.rb +4 -0
  160. data/spec/support/event_subscriber.rb +123 -33
  161. data/spec/support/keyword_struct.rb +26 -0
  162. data/spec/support/shared/server_selector.rb +13 -1
  163. data/spec/support/spec_config.rb +38 -13
  164. data/spec/support/spec_organizer.rb +129 -0
  165. data/spec/support/spec_setup.rb +1 -1
  166. data/spec/support/utils.rb +46 -0
  167. metadata +992 -942
  168. metadata.gz.sig +0 -0
  169. data/lib/mongo/server_selector/selectable.rb +0 -560
  170. data/spec/runners/sdam_monitoring.rb +0 -89
@@ -595,6 +595,21 @@ describe Mongo::Client do
595
595
  end
596
596
 
597
597
  context 'when name_only is true' do
598
+ min_server_fcv '3.6'
599
+
600
+ let(:command) do
601
+ Utils.get_command_event(root_authorized_client, 'listDatabases') do |client|
602
+ client.list_databases({}, true)
603
+ end.command
604
+ end
605
+
606
+ it 'sends the command with the nameOnly flag set to true' do
607
+ expect(command[:nameOnly]).to be(true)
608
+ end
609
+ end
610
+
611
+ context 'when authorized_databases is provided' do
612
+ min_server_fcv '4.0'
598
613
 
599
614
  let(:client_options) do
600
615
  root_authorized_client.options.merge(heartbeat_frequency: 100, monitoring: true)
@@ -614,12 +629,32 @@ describe Mongo::Client do
614
629
  subscriber.started_events.find { |c| c.command_name == 'listDatabases' }.command
615
630
  end
616
631
 
632
+ let(:authDb) do
633
+ { authorized_databases: true }
634
+ end
635
+
636
+ let(:noAuthDb) do
637
+ { authorized_databases: false }
638
+ end
639
+
617
640
  before do
618
- client.list_databases({}, true)
641
+ client.list_databases({}, true, authDb)
642
+ client.list_databases({}, true, noAuthDb)
619
643
  end
620
644
 
621
- it 'sends the command with the nameOnly flag set to true' do
622
- expect(command[:nameOnly]).to be(true)
645
+ let(:events) do
646
+ subscriber.command_started_events('listDatabases')
647
+ end
648
+
649
+ it 'sends the command with the authorizedDatabases flag set to true' do
650
+ expect(events.length).to eq(2)
651
+ command = events.first.command
652
+ expect(command[:authorizedDatabases]).to be(true)
653
+ end
654
+
655
+ it 'sends the command with the authorizedDatabases flag set to nil' do
656
+ command = events.last.command
657
+ expect(command[:authorizedDatabases]).to be_nil
623
658
  end
624
659
  end
625
660
  end
@@ -150,7 +150,13 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
150
150
  end
151
151
 
152
152
  let(:cluster) do
153
- double('cluster', servers: servers, single?: false, sharded?: false, unknown?: false)
153
+ double('cluster',
154
+ servers: servers,
155
+ single?: false,
156
+ replica_set?: true,
157
+ sharded?: false,
158
+ unknown?: false,
159
+ )
154
160
  end
155
161
 
156
162
  context 'when the read preference is primary' do
@@ -162,7 +168,11 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
162
168
  context 'when a primary exists' do
163
169
 
164
170
  let(:servers) do
165
- [ double('server', primary?: true) ]
171
+ [ double('server',
172
+ primary?: true,
173
+ # for runs with linting enabled
174
+ average_round_trip_time: 42,
175
+ ) ]
166
176
  end
167
177
 
168
178
  it 'returns true' do
@@ -191,7 +201,12 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
191
201
  context 'when a primary exists' do
192
202
 
193
203
  let(:servers) do
194
- [ double('server', primary?: true, secondary?: false) ]
204
+ [ double('server',
205
+ primary?: true,
206
+ secondary?: false,
207
+ # for runs with linting enabled
208
+ average_round_trip_time: 42,
209
+ ) ]
195
210
  end
196
211
 
197
212
  it 'returns true' do
@@ -260,7 +275,12 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
260
275
  context 'when a secondary does not exist' do
261
276
 
262
277
  let(:servers) do
263
- [ double('server', secondary?: false, primary?: true) ]
278
+ [ double('server',
279
+ secondary?: false,
280
+ primary?: true,
281
+ # for runs with linting enabled
282
+ average_round_trip_time: 42,
283
+ ) ]
264
284
  end
265
285
 
266
286
  it 'returns true' do
@@ -289,7 +309,12 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
289
309
  context 'when a primary exists' do
290
310
 
291
311
  let(:servers) do
292
- [ double('server', primary?: true, secondary?: false) ]
312
+ [ double('server',
313
+ primary?: true,
314
+ secondary?: false,
315
+ # for runs with linting enabled
316
+ average_round_trip_time: 42,
317
+ ) ]
293
318
  end
294
319
 
295
320
  it 'returns true' do
@@ -319,15 +344,28 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
319
344
  context 'when a primary server exists' do
320
345
 
321
346
  let(:primary) do
322
- double('server', :primary? => true)
347
+ double('server',
348
+ :primary? => true,
349
+ # for runs with linting enabled
350
+ average_round_trip_time: 42,
351
+ )
323
352
  end
324
353
 
325
354
  let(:secondary) do
326
- double('server', :primary? => false)
355
+ double('server',
356
+ :primary? => false,
357
+ # for runs with linting enabled
358
+ average_round_trip_time: 42,
359
+ )
327
360
  end
328
361
 
329
362
  let(:cluster) do
330
- double('cluster', servers: [ primary, secondary ])
363
+ double('cluster',
364
+ single?: false,
365
+ replica_set?: true,
366
+ sharded?: false,
367
+ servers: [ primary, secondary ],
368
+ )
331
369
  end
332
370
 
333
371
  it 'returns true' do
@@ -342,7 +380,12 @@ describe Mongo::Cluster::Topology::ReplicaSetNoPrimary do
342
380
  end
343
381
 
344
382
  let(:cluster) do
345
- double('cluster', servers: [ server ])
383
+ double('cluster',
384
+ single?: false,
385
+ replica_set?: true,
386
+ sharded?: false,
387
+ servers: [ server ],
388
+ )
346
389
  end
347
390
 
348
391
  it 'returns false' do
@@ -87,8 +87,10 @@ describe Mongo::Cluster::Topology::Single do
87
87
  topology.servers([ mongos, standalone, standalone_two, replica_set ])
88
88
  end
89
89
 
90
- it 'returns only the first standalone server' do
91
- expect(servers).to eq([ standalone ])
90
+ it 'returns all data-bearing non-unknown servers' do
91
+ # mongos and replica_set do not have ok: 1 in their descriptions,
92
+ # and are considered unknown.
93
+ expect(servers).to eq([ standalone, standalone_two ])
92
94
  end
93
95
  end
94
96
 
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'runners/sdam_monitoring'
3
2
 
4
3
  describe Mongo::Cluster do
5
4
 
@@ -344,13 +343,13 @@ describe Mongo::Cluster do
344
343
  end
345
344
 
346
345
  let(:monitoring) { Mongo::Monitoring.new }
347
- let(:subscriber) { Mongo::SDAMMonitoring::TestSubscriber.new }
346
+ let(:subscriber) { EventSubscriber.new }
348
347
 
349
348
  it 'publishes server closed event once' do
350
349
  monitoring.subscribe(Mongo::Monitoring::SERVER_CLOSED, subscriber)
351
350
  expect(cluster.disconnect!).to be(true)
352
351
  expect(subscriber.first_event('server_closed_event')).not_to be nil
353
- subscriber.events.clear
352
+ subscriber.succeeded_events.clear
354
353
  expect(cluster.disconnect!).to be(true)
355
354
  expect(subscriber.first_event('server_closed_event')).to be nil
356
355
  end
@@ -595,7 +594,23 @@ describe Mongo::Cluster do
595
594
  end
596
595
  end
597
596
 
598
- describe '#sessions_supported?' do
597
+ describe '#validate_session_support!' do
598
+ shared_examples 'supports sessions' do
599
+ it 'supports sessions' do
600
+ lambda do
601
+ cluster.validate_session_support!
602
+ end.should_not raise_error
603
+ end
604
+ end
605
+
606
+ shared_examples 'does not support sessions' do
607
+ it 'does not support sessions' do
608
+ lambda do
609
+ cluster.validate_session_support!
610
+ end.should raise_error(Mongo::Error::SessionsNotSupported)
611
+ end
612
+ end
613
+
599
614
  context 'when client has not contacted any servers' do
600
615
 
601
616
  let(:cluster) do
@@ -604,9 +619,7 @@ describe Mongo::Cluster do
604
619
  monitoring_io: false, server_selection_timeout: 0.183))
605
620
  end
606
621
 
607
- it 'is false' do
608
- expect(cluster.send(:sessions_supported?)).to be false
609
- end
622
+ it_behaves_like 'does not support sessions'
610
623
  end
611
624
 
612
625
  context 'when client has contacted servers and then disconnected' do
@@ -629,22 +642,20 @@ describe Mongo::Cluster do
629
642
  cluster.servers_list.map(&:unknown!)
630
643
  end
631
644
 
632
- it 'is true' do
633
- expect(cluster.send(:sessions_supported?)).to be true
634
- end
645
+ it_behaves_like 'supports sessions'
635
646
  end
636
647
 
637
648
  context 'in server < 3.6' do
638
649
  max_server_version '3.4'
639
650
 
651
+ let(:cluster) { client.cluster }
652
+
640
653
  context 'in single topology' do
641
654
  require_topology :single
642
655
 
643
656
  let(:client) { ClientRegistry.instance.global_client('authorized') }
644
657
 
645
- it 'is false' do
646
- expect(client.cluster.send(:sessions_supported?)).to be false
647
- end
658
+ it_behaves_like 'does not support sessions'
648
659
  end
649
660
 
650
661
  context 'in single topology with replica set name set' do
@@ -656,9 +667,7 @@ describe Mongo::Cluster do
656
667
  connect: :direct, replica_set: ClusterConfig.instance.replica_set_name))
657
668
  end
658
669
 
659
- it 'is false' do
660
- expect(client.cluster.send(:sessions_supported?)).to be false
661
- end
670
+ it_behaves_like 'does not support sessions'
662
671
  end
663
672
 
664
673
  context 'in replica set topology' do
@@ -666,9 +675,7 @@ describe Mongo::Cluster do
666
675
 
667
676
  let(:client) { ClientRegistry.instance.global_client('authorized') }
668
677
 
669
- it 'is false' do
670
- expect(client.cluster.send(:sessions_supported?)).to be false
671
- end
678
+ it_behaves_like 'does not support sessions'
672
679
  end
673
680
 
674
681
  context 'in sharded topology' do
@@ -676,15 +683,15 @@ describe Mongo::Cluster do
676
683
 
677
684
  let(:client) { ClientRegistry.instance.global_client('authorized') }
678
685
 
679
- it 'is false' do
680
- expect(client.cluster.send(:sessions_supported?)).to be false
681
- end
686
+ it_behaves_like 'does not support sessions'
682
687
  end
683
688
  end
684
689
 
685
690
  context 'in server 3.6+' do
686
691
  min_server_fcv '3.6'
687
692
 
693
+ let(:cluster) { client.cluster }
694
+
688
695
  context 'in single topology' do
689
696
  require_topology :single
690
697
 
@@ -692,10 +699,8 @@ describe Mongo::Cluster do
692
699
 
693
700
  # Contrary to the session spec, 3.6 and 4.0 standalone servers
694
701
  # report a logical session timeout and thus are considered to
695
- # support sessions
696
- it 'is true' do
697
- expect(client.cluster.send(:sessions_supported?)).to be true
698
- end
702
+ # support sessions.
703
+ it_behaves_like 'supports sessions'
699
704
  end
700
705
 
701
706
  context 'in single topology with replica set name set' do
@@ -707,9 +712,7 @@ describe Mongo::Cluster do
707
712
  connect: :direct, replica_set: ClusterConfig.instance.replica_set_name))
708
713
  end
709
714
 
710
- it 'is true' do
711
- expect(client.cluster.send(:sessions_supported?)).to be true
712
- end
715
+ it_behaves_like 'supports sessions'
713
716
  end
714
717
 
715
718
  context 'in replica set topology' do
@@ -717,9 +720,7 @@ describe Mongo::Cluster do
717
720
 
718
721
  let(:client) { ClientRegistry.instance.global_client('authorized') }
719
722
 
720
- it 'is true' do
721
- expect(client.cluster.send(:sessions_supported?)).to be true
722
- end
723
+ it_behaves_like 'supports sessions'
723
724
  end
724
725
 
725
726
  context 'in sharded topology' do
@@ -727,9 +728,7 @@ describe Mongo::Cluster do
727
728
 
728
729
  let(:client) { ClientRegistry.instance.global_client('authorized') }
729
730
 
730
- it 'is true' do
731
- expect(client.cluster.send(:sessions_supported?)).to be true
732
- end
731
+ it_behaves_like 'supports sessions'
733
732
  end
734
733
  end
735
734
  end
@@ -196,7 +196,7 @@ describe Mongo::Collection::View::ChangeStream do
196
196
  change_stream.to_enum
197
197
  end
198
198
 
199
- it 'propagates cursor not found error' do
199
+ it 'resumes on a cursor not found error' do
200
200
  original_cursor_id = cursor.id
201
201
 
202
202
  client.use(:admin).command({
@@ -204,9 +204,9 @@ describe Mongo::Collection::View::ChangeStream do
204
204
  cursors: [cursor.id]
205
205
  })
206
206
 
207
- lambda do
207
+ expect do
208
208
  enum.next
209
- end.should raise_error(Mongo::Error::OperationFailure, /cursor.*not found/)
209
+ end.not_to raise_error
210
210
  end
211
211
  end
212
212
 
@@ -218,7 +218,7 @@ describe Mongo::Collection::View::ChangeStream do
218
218
  collection.insert_one(a:2)
219
219
  end
220
220
 
221
- it 'propagates cursor not found error' do
221
+ it 'resumes on a cursor not found error' do
222
222
  original_cursor_id = cursor.id
223
223
 
224
224
  client.use(:admin).command({
@@ -226,9 +226,9 @@ describe Mongo::Collection::View::ChangeStream do
226
226
  cursors: [cursor.id]
227
227
  })
228
228
 
229
- lambda do
229
+ expect do
230
230
  change_stream.try_next
231
- end.should raise_error(Mongo::Error::OperationFailure, /cursor.*not found/)
231
+ end.not_to raise_error
232
232
  end
233
233
  end
234
234
  end
@@ -941,6 +941,30 @@ describe Mongo::Collection do
941
941
  it_behaves_like 'a failed operation using a session'
942
942
  end
943
943
  end
944
+
945
+ context 'when collation has a strength' do
946
+ min_server_fcv '3.4'
947
+
948
+ let(:band_collection) do
949
+ described_class.new(database, :bands)
950
+ end
951
+
952
+ before do
953
+ band_collection.delete_many
954
+ band_collection.insert_many([{ name: "Depeche Mode" }, { name: "New Order" }])
955
+ end
956
+
957
+ let(:options) do
958
+ { collation: { locale: 'en_US', strength: 2 } }
959
+ end
960
+ let(:band_result) do
961
+ band_collection.find({ name: 'DEPECHE MODE' }, options)
962
+ end
963
+
964
+ it 'finds Capitalize from UPPER CASE' do
965
+ expect(band_result.count_documents).to eq(1)
966
+ end
967
+ end
944
968
  end
945
969
 
946
970
  describe '#drop' do
@@ -1601,6 +1625,40 @@ describe Mongo::Collection do
1601
1625
  expect(result.inserted_count).to be(0)
1602
1626
  end
1603
1627
  end
1628
+
1629
+ context 'when various options passed in' do
1630
+ # w: 2 requires a replica set
1631
+ require_topology :replica_set
1632
+
1633
+ # https://jira.mongodb.org/browse/RUBY-2306
1634
+ min_server_fcv '3.6'
1635
+
1636
+ let(:session) do
1637
+ authorized_client.start_session
1638
+ end
1639
+
1640
+ let(:events) do
1641
+ subscriber.command_started_events('insert')
1642
+ end
1643
+
1644
+ let(:collection) do
1645
+ authorized_collection.with(write_concern: {w: 2})
1646
+ end
1647
+
1648
+ let!(:command) do
1649
+ Utils.get_command_event(authorized_client, 'insert') do |client|
1650
+ collection.insert_many([{ name: 'test1' }, { name: 'test2' }], session: session,
1651
+ write_concern: {w: 1}, bypass_document_validation: true)
1652
+ end.command
1653
+ end
1654
+
1655
+ it 'inserts many successfully with correct options sent to server' do
1656
+ expect(events.length).to eq(1)
1657
+ expect(command[:writeConcern]).to_not be_nil
1658
+ expect(command[:writeConcern][:w]).to eq(1)
1659
+ expect(command[:bypassDocumentValidation]).to be(true)
1660
+ end
1661
+ end
1604
1662
  end
1605
1663
 
1606
1664
  describe '#insert_one' do
@@ -1683,6 +1741,37 @@ describe Mongo::Collection do
1683
1741
  it_behaves_like 'an implicit session with an unacknowledged write'
1684
1742
  end
1685
1743
 
1744
+ context 'when various options passed in' do
1745
+ # https://jira.mongodb.org/browse/RUBY-2306
1746
+ min_server_fcv '3.6'
1747
+
1748
+ let(:session) do
1749
+ authorized_client.start_session
1750
+ end
1751
+
1752
+ let(:events) do
1753
+ subscriber.command_started_events('insert')
1754
+ end
1755
+
1756
+ let(:collection) do
1757
+ authorized_collection.with(write_concern: {w: 3})
1758
+ end
1759
+
1760
+ let!(:command) do
1761
+ Utils.get_command_event(authorized_client, 'insert') do |client|
1762
+ collection.insert_one({name: 'test1'}, session: session, write_concern: {w: 1},
1763
+ bypass_document_validation: true)
1764
+ end.command
1765
+ end
1766
+
1767
+ it 'inserts one successfully with correct options sent to server' do
1768
+ expect(events.length).to eq(1)
1769
+ expect(command[:writeConcern]).to_not be_nil
1770
+ expect(command[:writeConcern][:w]).to eq(1)
1771
+ expect(command[:bypassDocumentValidation]).to be(true)
1772
+ end
1773
+ end
1774
+
1686
1775
  context 'when the document contains invalid keys' do
1687
1776
 
1688
1777
  let(:doc) do
@@ -1791,6 +1880,52 @@ describe Mongo::Collection do
1791
1880
  end
1792
1881
  end
1793
1882
 
1883
+ describe '#bulk_write' do
1884
+
1885
+ context 'when various options passed in' do
1886
+ min_server_fcv '3.2'
1887
+ require_topology :replica_set
1888
+
1889
+ # https://jira.mongodb.org/browse/RUBY-2306
1890
+ min_server_fcv '3.6'
1891
+
1892
+ let(:requests) do
1893
+ [
1894
+ { insert_one: { name: "anne" }},
1895
+ { insert_one: { name: "bob" }},
1896
+ { insert_one: { name: "charlie" }}
1897
+ ]
1898
+ end
1899
+
1900
+ let(:session) do
1901
+ authorized_client.start_session
1902
+ end
1903
+
1904
+ let!(:command) do
1905
+ Utils.get_command_event(authorized_client, 'insert') do |client|
1906
+ collection.bulk_write(requests, session: session, write_concern: {w: 1},
1907
+ bypass_document_validation: true)
1908
+ end.command
1909
+ end
1910
+
1911
+ let(:events) do
1912
+ subscriber.command_started_events('insert')
1913
+ end
1914
+
1915
+ let(:collection) do
1916
+ authorized_collection.with(write_concern: {w: 2})
1917
+ end
1918
+
1919
+ it 'inserts successfully with correct options sent to server' do
1920
+ expect(collection.count).to eq(3)
1921
+ expect(events.length).to eq(1)
1922
+ expect(command[:writeConcern]).to_not be_nil
1923
+ expect(command[:writeConcern][:w]).to eq(1)
1924
+ expect(command[:bypassDocumentValidation]).to eq(true)
1925
+ end
1926
+ end
1927
+ end
1928
+
1794
1929
  describe '#inspect' do
1795
1930
 
1796
1931
  it 'includes the object id' do
@@ -2000,6 +2135,39 @@ describe Mongo::Collection do
2000
2135
  end
2001
2136
 
2002
2137
  describe '#count_documents' do
2138
+
2139
+ before do
2140
+ authorized_collection.delete_many
2141
+ end
2142
+
2143
+ context 'no argument provided' do
2144
+
2145
+ context 'when collection is empty' do
2146
+ it 'returns 0 matching documents' do
2147
+ expect(authorized_collection.count_documents).to eq(0)
2148
+ end
2149
+ end
2150
+
2151
+ context 'when collection is not empty' do
2152
+
2153
+ let(:documents) do
2154
+ documents = []
2155
+ 1.upto(10) do |index|
2156
+ documents << { key: 'a', _id: "in#{index}" }
2157
+ end
2158
+ documents
2159
+ end
2160
+
2161
+ before do
2162
+ authorized_collection.insert_many(documents)
2163
+ end
2164
+
2165
+ it 'returns 10 matching documents' do
2166
+ expect(authorized_collection.count_documents).to eq(10)
2167
+ end
2168
+ end
2169
+ end
2170
+
2003
2171
  context 'when transactions are enabled' do
2004
2172
  require_wired_tiger
2005
2173
  require_transaction_support
@@ -2478,6 +2646,48 @@ describe Mongo::Collection do
2478
2646
  expect(authorized_collection.find(name: 'bang').count).to eq(1)
2479
2647
  end
2480
2648
  end
2649
+
2650
+ context 'when various options passed in' do
2651
+ # w: 2 requires a replica set
2652
+ require_topology :replica_set
2653
+
2654
+ # https://jira.mongodb.org/browse/RUBY-2306
2655
+ min_server_fcv '3.6'
2656
+
2657
+ before do
2658
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }])
2659
+ end
2660
+
2661
+ let(:selector) do
2662
+ {name: 'test2'}
2663
+ end
2664
+
2665
+ let(:session) do
2666
+ authorized_client.start_session
2667
+ end
2668
+
2669
+ let(:events) do
2670
+ subscriber.command_started_events('delete')
2671
+ end
2672
+
2673
+ let(:collection) do
2674
+ authorized_collection.with(write_concern: {w: 2})
2675
+ end
2676
+
2677
+ let!(:command) do
2678
+ Utils.get_command_event(authorized_client, 'delete') do |client|
2679
+ collection.delete_one(selector, session: session, write_concern: {w: 1},
2680
+ bypass_document_validation: true)
2681
+ end.command
2682
+ end
2683
+
2684
+ it 'deletes one successfully with correct options sent to server' do
2685
+ expect(events.length).to eq(1)
2686
+ expect(command[:writeConcern]).to_not be_nil
2687
+ expect(command[:writeConcern][:w]).to eq(1)
2688
+ expect(command[:bypassDocumentValidation]).to eq(true)
2689
+ end
2690
+ end
2481
2691
  end
2482
2692
 
2483
2693
  describe '#delete_many' do
@@ -2668,6 +2878,48 @@ describe Mongo::Collection do
2668
2878
  expect(authorized_collection.find(name: 'bang').count).to eq(2)
2669
2879
  end
2670
2880
  end
2881
+
2882
+ context 'when various options passed in' do
2883
+ # w: 2 requires a replica set
2884
+ require_topology :replica_set
2885
+
2886
+ # https://jira.mongodb.org/browse/RUBY-2306
2887
+ min_server_fcv '3.6'
2888
+
2889
+ before do
2890
+ collection.insert_many([{ name: 'test1' }, { name: 'test2' }, { name: 'test3'}])
2891
+ end
2892
+
2893
+ let(:selector) do
2894
+ {name: 'test1'}
2895
+ end
2896
+
2897
+ let(:session) do
2898
+ authorized_client.start_session
2899
+ end
2900
+
2901
+ let(:events) do
2902
+ subscriber.command_started_events('delete')
2903
+ end
2904
+
2905
+ let(:collection) do
2906
+ authorized_collection.with(write_concern: {w: 1})
2907
+ end
2908
+
2909
+ let!(:command) do
2910
+ Utils.get_command_event(authorized_client, 'delete') do |client|
2911
+ collection.delete_many(selector, session: session, write_concern: {w: 2},
2912
+ bypass_document_validation: true)
2913
+ end.command
2914
+ end
2915
+
2916
+ it 'deletes many successfully with correct options sent to server' do
2917
+ expect(events.length).to eq(1)
2918
+ expect(command[:writeConcern]).to_not be_nil
2919
+ expect(command[:writeConcern][:w]).to eq(2)
2920
+ expect(command[:bypassDocumentValidation]).to be(true)
2921
+ end
2922
+ end
2671
2923
  end
2672
2924
 
2673
2925
  describe '#parallel_scan' do
@@ -3174,6 +3426,51 @@ describe Mongo::Collection do
3174
3426
 
3175
3427
  it_behaves_like 'an implicit session with an unacknowledged write'
3176
3428
  end
3429
+
3430
+ context 'when various options passed in' do
3431
+ # w: 2 requires a replica set
3432
+ require_topology :replica_set
3433
+
3434
+ # https://jira.mongodb.org/browse/RUBY-2306
3435
+ min_server_fcv '3.6'
3436
+
3437
+ before do
3438
+ authorized_collection.insert_one({field: 'test1'})
3439
+ end
3440
+
3441
+ let(:session) do
3442
+ authorized_client.start_session
3443
+ end
3444
+
3445
+ let(:events) do
3446
+ subscriber.command_started_events('update')
3447
+ end
3448
+
3449
+ let(:collection) do
3450
+ authorized_collection.with(write_concern: {w: 3})
3451
+ end
3452
+
3453
+ let(:updated) do
3454
+ collection.find(field: 'test4').first
3455
+ end
3456
+
3457
+ let!(:command) do
3458
+ Utils.get_command_event(authorized_client, 'update') do |client|
3459
+ collection.replace_one(selector, { field: 'test4'},
3460
+ session: session, :return_document => :after, write_concern: {w: 2},
3461
+ upsert: true, bypass_document_validation: true)
3462
+ end.command
3463
+ end
3464
+
3465
+ it 'replaced one successfully with correct options sent to server' do
3466
+ expect(updated[:field]).to eq('test4')
3467
+ expect(events.length).to eq(1)
3468
+ expect(command[:writeConcern]).to_not be_nil
3469
+ expect(command[:writeConcern][:w]).to eq(2)
3470
+ expect(command[:bypassDocumentValidation]).to be(true)
3471
+ expect(command[:updates][0][:upsert]).to be(true)
3472
+ end
3473
+ end
3177
3474
  end
3178
3475
 
3179
3476
  describe '#update_many' do
@@ -3596,6 +3893,45 @@ describe Mongo::Collection do
3596
3893
 
3597
3894
  it_behaves_like 'an implicit session with an unacknowledged write'
3598
3895
  end
3896
+
3897
+ context 'when various options passed in' do
3898
+ # w: 2 requires a replica set
3899
+ require_topology :replica_set
3900
+
3901
+ # https://jira.mongodb.org/browse/RUBY-2306
3902
+ min_server_fcv '3.6'
3903
+
3904
+ before do
3905
+ collection.insert_many([{ field: 'test' }, { field: 'test2' }], session: session)
3906
+ end
3907
+
3908
+ let(:session) do
3909
+ authorized_client.start_session
3910
+ end
3911
+
3912
+ let(:collection) do
3913
+ authorized_collection.with(write_concern: {w: 1})
3914
+ end
3915
+
3916
+ let(:events) do
3917
+ subscriber.command_started_events('update')
3918
+ end
3919
+
3920
+ let!(:command) do
3921
+ Utils.get_command_event(authorized_client, 'update') do |client|
3922
+ collection.update_many(selector, {'$set'=> { field: 'testing' }}, session: session,
3923
+ write_concern: {w: 2}, bypass_document_validation: true, upsert: true)
3924
+ end.command
3925
+ end
3926
+
3927
+ it 'updates many successfully with correct options sent to server' do
3928
+ expect(events.length).to eq(1)
3929
+ expect(collection.options[:write_concern]).to eq(w: 1)
3930
+ expect(command[:writeConcern][:w]).to eq(2)
3931
+ expect(command[:bypassDocumentValidation]).to be(true)
3932
+ expect(command[:updates][0][:upsert]).to be(true)
3933
+ end
3934
+ end
3599
3935
  end
3600
3936
 
3601
3937
  describe '#update_one' do
@@ -4014,6 +4350,47 @@ describe Mongo::Collection do
4014
4350
 
4015
4351
  it_behaves_like 'an implicit session with an unacknowledged write'
4016
4352
  end
4353
+
4354
+ context 'when various options passed in' do
4355
+ # w: 2 requires a replica set
4356
+ require_topology :replica_set
4357
+
4358
+ # https://jira.mongodb.org/browse/RUBY-2306
4359
+ min_server_fcv '3.6'
4360
+
4361
+ before do
4362
+ collection.insert_many([{ field: 'test1' }, { field: 'test2' }], session: session)
4363
+ end
4364
+
4365
+ let(:session) do
4366
+ authorized_client.start_session
4367
+ end
4368
+
4369
+ let(:collection) do
4370
+ authorized_collection.with(write_concern: {w: 1})
4371
+ end
4372
+
4373
+ let(:events) do
4374
+ subscriber.command_started_events('update')
4375
+ end
4376
+
4377
+ let!(:command) do
4378
+ Utils.get_command_event(authorized_client, 'update') do |client|
4379
+ collection.update_one(selector, { '$set'=> { field: 'testing' } }, session: session,
4380
+ write_concern: {w: 2}, bypass_document_validation: true, :return_document => :after,
4381
+ upsert: true)
4382
+ end.command
4383
+ end
4384
+
4385
+ it 'updates one successfully with correct options sent to server' do
4386
+ expect(events.length).to eq(1)
4387
+ expect(command[:writeConcern]).to_not be_nil
4388
+ expect(command[:writeConcern][:w]).to eq(2)
4389
+ expect(collection.options[:write_concern]).to eq(w:1)
4390
+ expect(command[:bypassDocumentValidation]).to be(true)
4391
+ expect(command[:updates][0][:upsert]).to be(true)
4392
+ end
4393
+ end
4017
4394
  end
4018
4395
 
4019
4396
  describe '#find_one_and_delete' do
@@ -4231,6 +4608,46 @@ describe Mongo::Collection do
4231
4608
  expect(result).to be_nil
4232
4609
  end
4233
4610
  end
4611
+
4612
+ context 'when various options passed in' do
4613
+ # w: 2 requires a replica set
4614
+ require_topology :replica_set
4615
+
4616
+ # https://jira.mongodb.org/browse/RUBY-2306
4617
+ min_server_fcv '3.6'
4618
+
4619
+ before do
4620
+ authorized_collection.delete_many
4621
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }])
4622
+ end
4623
+
4624
+ let(:collection) do
4625
+ authorized_collection.with(write_concern: {w: 2})
4626
+ end
4627
+
4628
+ let(:session) do
4629
+ authorized_client.start_session
4630
+ end
4631
+
4632
+ let!(:command) do
4633
+ Utils.get_command_event(authorized_client, 'findAndModify') do |client|
4634
+ collection.find_one_and_delete(selector, session: session, write_concern: {w: 2},
4635
+ bypass_document_validation: true, max_time_ms: 300)
4636
+ end.command
4637
+ end
4638
+
4639
+ let(:events) do
4640
+ subscriber.command_started_events('findAndModify')
4641
+ end
4642
+
4643
+ it 'finds and deletes successfully with correct options sent to server' do
4644
+ expect(events.length).to eq(1)
4645
+ expect(command[:writeConcern]).to_not be_nil
4646
+ expect(command[:writeConcern][:w]).to eq(2)
4647
+ expect(command[:bypassDocumentValidation]).to eq(true)
4648
+ expect(command[:maxTimeMS]).to eq(300)
4649
+ end
4650
+ end
4234
4651
  end
4235
4652
 
4236
4653
  describe '#find_one_and_update' do
@@ -4652,6 +5069,51 @@ describe Mongo::Collection do
4652
5069
  end
4653
5070
  end
4654
5071
  end
5072
+
5073
+ context 'when various options passed in' do
5074
+ # w: 2 requires a replica set
5075
+ require_topology :replica_set
5076
+
5077
+ # https://jira.mongodb.org/browse/RUBY-2306
5078
+ min_server_fcv '3.6'
5079
+
5080
+ let(:session) do
5081
+ authorized_client.start_session
5082
+ end
5083
+
5084
+ let(:events) do
5085
+ subscriber.command_started_events('findAndModify')
5086
+ end
5087
+
5088
+ let(:collection) do
5089
+ authorized_collection.with(write_concern: {w: 2})
5090
+ end
5091
+
5092
+ let(:selector) do
5093
+ {field: 'test1'}
5094
+ end
5095
+
5096
+ before do
5097
+ collection.insert_one({field: 'test1'}, session: session)
5098
+ end
5099
+
5100
+ let!(:command) do
5101
+ Utils.get_command_event(authorized_client, 'findAndModify') do |client|
5102
+ collection.find_one_and_update(selector, { '$set' => {field: 'testing'}},
5103
+ :return_document => :after, write_concern: {w: 1}, upsert: true,
5104
+ bypass_document_validation: true, max_time_ms: 100, session: session)
5105
+ end.command
5106
+ end
5107
+
5108
+ it 'find and updates successfully with correct options sent to server' do
5109
+ expect(events.length).to eq(1)
5110
+ expect(command[:writeConcern]).to_not be_nil
5111
+ expect(command[:writeConcern][:w]).to eq(1)
5112
+ expect(command[:upsert]).to eq(true)
5113
+ expect(command[:bypassDocumentValidation]).to be(true)
5114
+ expect(command[:maxTimeMS]).to eq(100)
5115
+ end
5116
+ end
4655
5117
  end
4656
5118
 
4657
5119
  describe '#find_one_and_replace' do
@@ -4965,6 +5427,44 @@ describe Mongo::Collection do
4965
5427
  expect(result).to be_nil
4966
5428
  end
4967
5429
  end
5430
+
5431
+ context 'when various options passed in' do
5432
+ # https://jira.mongodb.org/browse/RUBY-2306
5433
+ min_server_fcv '3.6'
5434
+
5435
+ before do
5436
+ authorized_collection.insert_one({field: 'test1'})
5437
+ end
5438
+
5439
+ let(:session) do
5440
+ authorized_client.start_session
5441
+ end
5442
+
5443
+ let(:events) do
5444
+ subscriber.command_started_events('findAndModify')
5445
+ end
5446
+
5447
+ let(:collection) do
5448
+ authorized_collection.with(write_concern: { w: 2 })
5449
+ end
5450
+
5451
+ let!(:command) do
5452
+ Utils.get_command_event(authorized_client, 'findAndModify') do |client|
5453
+ collection.find_one_and_replace(selector, { '$set' => {field: 'test5'}},
5454
+ :return_document => :after, write_concern: {w: 1}, session: session,
5455
+ upsert: true, bypass_document_validation: false, max_time_ms: 200)
5456
+ end.command
5457
+ end
5458
+
5459
+ it 'find and replaces successfully with correct options sent to server' do
5460
+ expect(events.length).to eq(1)
5461
+ expect(command[:writeConcern]).to_not be_nil
5462
+ expect(command[:writeConcern][:w]).to eq(1)
5463
+ expect(command[:upsert]).to be(true)
5464
+ expect(command[:bypassDocumentValidation]).to be_nil
5465
+ expect(command[:maxTimeMS]).to eq(200)
5466
+ end
5467
+ end
4968
5468
  end
4969
5469
 
4970
5470
  describe '#watch' do