mongo 2.13.0.beta1 → 2.13.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -12,7 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'runners/transactions/context'
16
15
  require 'runners/transactions/operation'
17
16
  require 'runners/transactions/spec'
18
17
  require 'runners/transactions/test'
@@ -26,21 +25,22 @@ def define_transactions_spec_tests(test_paths)
26
25
  context(spec.description) do
27
26
 
28
27
  define_spec_tests_with_requirements(spec) do |req|
28
+
29
29
  spec.tests.each do |test|
30
30
 
31
- before do
32
- if ClusterConfig.instance.topology == :sharded
33
- if test.multiple_mongoses? && SpecConfig.instance.addresses.length == 1
34
- skip "Test requires multiple mongoses"
35
- elsif !test.multiple_mongoses? && SpecConfig.instance.addresses.length > 1
36
- # Many transaction spec tests that do not specifically deal with
37
- # sharded transactions fail when run against a multi-mongos cluster
38
- skip "Test does not specify multiple mongoses"
31
+ context(test.description) do
32
+
33
+ before(:all) do
34
+ if ClusterConfig.instance.topology == :sharded
35
+ if test.multiple_mongoses? && SpecConfig.instance.addresses.length == 1
36
+ skip "Test requires multiple mongoses"
37
+ elsif !test.multiple_mongoses? && SpecConfig.instance.addresses.length > 1
38
+ # Many transaction spec tests that do not specifically deal with
39
+ # sharded transactions fail when run against a multi-mongos cluster
40
+ skip "Test does not specify multiple mongoses"
41
+ end
39
42
  end
40
43
  end
41
- end
42
-
43
- context(test.description) do
44
44
 
45
45
  if test.skip_reason
46
46
  before(:all) do
@@ -21,23 +21,8 @@ module Mongo
21
21
  arguments && arguments['session'] || object =~ /session/
22
22
  end
23
23
 
24
- def execute(target, session0, session1, active_session=nil)
25
- session = case arguments && arguments['session']
26
- when 'session0'
27
- session0
28
- when 'session1'
29
- session1
30
- else
31
- # active session could be nil
32
- active_session
33
- end
34
-
35
- context = Context.new(
36
- session0,
37
- session1,
38
- session)
39
-
40
- op_name = Utils.underscore(name).to_sym
24
+ def execute(target, context)
25
+ op_name = ::Utils.underscore(name).to_sym
41
26
  if op_name == :with_transaction
42
27
  args = [target]
43
28
  else
@@ -53,9 +38,14 @@ module Mongo
53
38
  result['error'] = false
54
39
  end
55
40
  end
41
+
56
42
  result
57
43
  rescue Mongo::Error::OperationFailure => e
58
- err_doc = e.instance_variable_get(:@result).send(:first_document)
44
+ result = e.instance_variable_get(:@result)
45
+ if result.nil?
46
+ raise "OperationFailure had nil result: #{e}"
47
+ end
48
+ err_doc = result.send(:first_document)
59
49
  error_code_name = err_doc['codeName'] || err_doc['writeConcernError'] && err_doc['writeConcernError']['codeName']
60
50
  if error_code_name.nil?
61
51
  # Sometimes the server does not return the error code name,
@@ -102,13 +92,13 @@ module Mongo
102
92
  command_value = cmd.delete(command_name)
103
93
  cmd = { command_name.to_sym => command_value }.merge(cmd)
104
94
 
105
- opts = Utils.snakeize_hash(context.transform_arguments(options)).dup
95
+ opts = ::Utils.snakeize_hash(transformed_options(context)).dup
106
96
  opts[:read] = opts.delete(:read_preference)
107
97
  database.command(cmd, opts).documents.first
108
98
  end
109
99
 
110
100
  def start_transaction(session, context)
111
- session.start_transaction(Utils.convert_operation_options(arguments['options']))
101
+ session.start_transaction(::Utils.convert_operation_options(arguments['options']))
112
102
  nil
113
103
  end
114
104
 
@@ -128,7 +118,7 @@ module Mongo
128
118
  end
129
119
 
130
120
  if arguments['options']
131
- options = Utils.snakeize_hash(arguments['options'])
121
+ options = ::Utils.snakeize_hash(arguments['options'])
132
122
  else
133
123
  options = nil
134
124
  end
@@ -136,7 +126,7 @@ module Mongo
136
126
  callback['operations'].each do |op_spec|
137
127
  op = Operation.new(@crud_test, op_spec)
138
128
  target = @crud_test.resolve_target(@crud_test.test_client, op)
139
- rv = op.execute(target, context.session0, context.session1, session)
129
+ rv = op.execute(target, context)
140
130
  if rv && rv['exception']
141
131
  raise rv['exception']
142
132
  end
@@ -151,7 +141,7 @@ module Mongo
151
141
  end
152
142
 
153
143
  def targeted_fail_point(collection, context)
154
- args = context.transform_arguments(options)
144
+ args = transformed_options(context)
155
145
  session = args[:session]
156
146
  unless session.pinned_server
157
147
  raise ArgumentError, 'Targeted fail point requires session to be pinned to a server'
@@ -169,7 +159,7 @@ module Mongo
169
159
  end
170
160
 
171
161
  def assert_session_pinned(collection, context)
172
- args = context.transform_arguments(options)
162
+ args = transformed_options(context)
173
163
  session = args[:session]
174
164
  unless session.pinned_server
175
165
  raise ArgumentError, 'Expected session to be pinned'
@@ -177,12 +167,148 @@ module Mongo
177
167
  end
178
168
 
179
169
  def assert_session_unpinned(collection, context)
180
- args = context.transform_arguments(options)
170
+ args = transformed_options(context)
181
171
  session = args[:session]
182
172
  if session.pinned_server
183
173
  raise ArgumentError, 'Expected session to not be pinned'
184
174
  end
185
175
  end
176
+
177
+ def wait_for_event(client, context)
178
+ deadline = Time.now + 5
179
+ loop do
180
+ events = _select_events(context)
181
+ if events.length >= arguments['count']
182
+ break
183
+ end
184
+ if Time.now >= deadline
185
+ raise "Did not receive an event matching #{arguments} in 5 seconds; received #{events.length} but expected #{arguments['count']} events"
186
+ else
187
+ sleep 0.1
188
+ end
189
+ end
190
+ end
191
+
192
+ def assert_event_count(client, context)
193
+ events = _select_events(context)
194
+ unless events.length == arguments['count']
195
+ raise "Exppected #{arguments['count']} #{arguments['event']} events, but have #{events.length}"
196
+ end
197
+ end
198
+
199
+ def _select_events(context)
200
+ case arguments['event']
201
+ when 'ServerMarkedUnknownEvent'
202
+ context.sdam_subscriber.all_events.select do |event|
203
+ event.is_a?(Mongo::Monitoring::Event::ServerDescriptionChanged) &&
204
+ event.new_description.unknown?
205
+ end
206
+ else
207
+ context.sdam_subscriber.all_events.select do |event|
208
+ event.class.name.sub(/.*::/, '') == arguments['event'].sub(/Event$/, '')
209
+ end
210
+ end
211
+ end
212
+
213
+ class ThreadContext
214
+ def initialize
215
+ @operations = Queue.new
216
+ @unexpected_operation_results = []
217
+ end
218
+
219
+ def stop?
220
+ !!@stop
221
+ end
222
+
223
+ def signal_stop
224
+ @stop = true
225
+ end
226
+
227
+ attr_reader :operations
228
+ attr_reader :unexpected_operation_results
229
+ end
230
+
231
+ def start_thread(client, context)
232
+ thread_context = ThreadContext.new
233
+ thread = Thread.new do
234
+ loop do
235
+ begin
236
+ op_spec = thread_context.operations.pop(true)
237
+ op = Operation.new(@crud_test, op_spec)
238
+ target = @crud_test.resolve_target(@crud_test.test_client, op)
239
+ result = op.execute(target, context)
240
+ if op_spec['error']
241
+ unless result['error']
242
+ thread_context.unexpected_operation_results << result
243
+ end
244
+ else
245
+ if result['error']
246
+ thread_context.unexpected_operation_results << result
247
+ end
248
+ end
249
+ rescue ThreadError
250
+ # Queue is empty
251
+ end
252
+ if thread_context.stop?
253
+ break
254
+ else
255
+ sleep 1
256
+ end
257
+ end
258
+ end
259
+ class << thread
260
+ attr_accessor :context
261
+ end
262
+ thread.context = thread_context
263
+ unless context.threads
264
+ context.threads ||= {}
265
+ end
266
+ context.threads[arguments['name']] = thread
267
+ end
268
+
269
+ def run_on_thread(client, context)
270
+ thread = context.threads.fetch(arguments['name'])
271
+ thread.context.operations << arguments['operation']
272
+ end
273
+
274
+ def wait_for_thread(client, context)
275
+ thread = context.threads.fetch(arguments['name'])
276
+ thread.context.signal_stop
277
+ thread.join
278
+ unless thread.context.unexpected_operation_results.empty?
279
+ raise "Thread #{arguments['name']} had #{thread.context.unexpected_operation_results}.length unexpected operation results"
280
+ end
281
+ end
282
+
283
+ def wait(client, context)
284
+ sleep arguments['ms'] / 1000.0
285
+ end
286
+
287
+ def record_primary(client, context)
288
+ context.primary_address = client.cluster.next_primary.address
289
+ end
290
+
291
+ def run_admin_command(support_client, context)
292
+ support_client.use('admin').database.command(arguments['command'])
293
+ end
294
+
295
+ def wait_for_primary_change(client, context)
296
+ timeout = if arguments['timeoutMS']
297
+ arguments['timeoutMS'] / 1000.0
298
+ else
299
+ 10
300
+ end
301
+ deadline = Time.now + timeout
302
+ loop do
303
+ client.cluster.scan!
304
+ if client.cluster.next_primary.address != context.primary_address
305
+ break
306
+ end
307
+ if Time.now >= deadline
308
+ raise "Failed to change primary in #{timeout} seconds"
309
+ end
310
+ end
311
+ end
186
312
  end
187
313
  end
188
314
  end
@@ -41,13 +41,20 @@ module Mongo
41
41
  @spec = crud_spec
42
42
  @data = data
43
43
  @description = test['description']
44
- @client_options = Utils.convert_client_options(test['clientOptions'] || {})
44
+ @client_options = {
45
+ # Disable legacy read & write retries, so that when spec tests
46
+ # disable modern retries we do not retry at all instead of using
47
+ # legacy retries which is contrary to what the tests want.
48
+ max_read_retries: 0,
49
+ max_write_retries: 0,
50
+ app_name: 'Tx spec - test client',
51
+ }.update(::Utils.convert_client_options(test['clientOptions'] || {}))
45
52
 
46
53
  @fail_point_command = test['failPoint']
47
54
 
48
55
  @session_options = if opts = test['sessionOptions']
49
56
  Hash[opts.map do |session_name, options|
50
- [session_name.to_sym, Utils.convert_operation_options(options)]
57
+ [session_name.to_sym, ::Utils.convert_operation_options(options)]
51
58
  end]
52
59
  else
53
60
  {}
@@ -107,15 +114,35 @@ module Mongo
107
114
  end
108
115
 
109
116
  def test_client
110
- @test_client ||= ClientRegistry.instance.global_client(
111
- 'authorized_without_retry_writes'
112
- ).with(@client_options.merge(
113
- database: @spec.database_name,
114
- ))
117
+ @test_client ||= begin
118
+ sdam_proc = lambda do |test_client|
119
+ test_client.subscribe(Mongo::Monitoring::COMMAND, command_subscriber)
120
+
121
+ test_client.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, sdam_subscriber)
122
+ test_client.subscribe(Mongo::Monitoring::SERVER_OPENING, sdam_subscriber)
123
+ test_client.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, sdam_subscriber)
124
+ test_client.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, sdam_subscriber)
125
+ test_client.subscribe(Mongo::Monitoring::SERVER_CLOSED, sdam_subscriber)
126
+ test_client.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, sdam_subscriber)
127
+ test_client.subscribe(Mongo::Monitoring::CONNECTION_POOL, sdam_subscriber)
128
+ end
129
+
130
+ ClientRegistry.instance.new_local_client(
131
+ SpecConfig.instance.addresses,
132
+ SpecConfig.instance.authorized_test_options.merge(
133
+ database: @spec.database_name,
134
+ auth_source: SpecConfig.instance.auth_options[:auth_source] || 'admin',
135
+ sdam_proc: sdam_proc,
136
+ ).merge(@client_options))
137
+ end
115
138
  end
116
139
 
117
- def event_subscriber
118
- @event_subscriber ||= EventSubscriber.new
140
+ def command_subscriber
141
+ @command_subscriber ||= EventSubscriber.new
142
+ end
143
+
144
+ def sdam_subscriber
145
+ @sdam_subscriber ||= EventSubscriber.new(name: 'sdam subscriber')
119
146
  end
120
147
 
121
148
  # Run the test.
@@ -127,17 +154,33 @@ module Mongo
127
154
  #
128
155
  # @since 2.6.0
129
156
  def run
130
- test_client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
157
+ @threads = {}
131
158
 
132
159
  results = @operations.map do |op|
133
160
  target = resolve_target(test_client, op)
134
161
  if op.needs_session?
135
- op.execute(target, session0, session1)
162
+ context = CRUD::Context.new(
163
+ session0: session0,
164
+ session1: session1,
165
+ sdam_subscriber: sdam_subscriber,
166
+ threads: @threads,
167
+ primary_address: @primary_address,
168
+ )
136
169
  else
137
170
  # Hack to support write concern operations tests, which are
138
171
  # defined to use transactions format but target pre-3.6 servers
139
172
  # that do not support sessions
140
- op.execute(target || support_client, nil, nil)
173
+ target ||= support_client
174
+ context = CRUD::Context.new(
175
+ sdam_subscriber: sdam_subscriber,
176
+ threads: @threads,
177
+ primary_address: @primary_address,
178
+ )
179
+ end
180
+
181
+ op.execute(target, context).tap do
182
+ @threads = context.threads
183
+ @primary_address = context.primary_address
141
184
  end
142
185
  end
143
186
 
@@ -147,7 +190,7 @@ module Mongo
147
190
  @session0&.end_session
148
191
  @session1&.end_session
149
192
 
150
- actual_events = Utils.yamlify_command_events(event_subscriber.started_events)
193
+ actual_events = ::Utils.yamlify_command_events(command_subscriber.started_events)
151
194
  actual_events = actual_events.reject do |event|
152
195
  event['command_started_event']['command']['endSessions']
153
196
  end
@@ -169,7 +212,7 @@ module Mongo
169
212
  contents: @result_collection.with(
170
213
  read: {mode: 'primary'},
171
214
  read_concern: { level: 'local' },
172
- ).find.to_a,
215
+ ).find.sort(_id: 1).to_a,
173
216
  events: actual_events,
174
217
  }
175
218
  end
@@ -216,9 +259,10 @@ module Mongo
216
259
 
217
260
  coll.insert_many(@data) unless @data.empty?
218
261
 
219
- $distinct_ran ||= if description =~ /distinct/ || @operations.any? { |op| op.name == 'distinct' }
262
+ $distinct_ran ||= {}
263
+ $distinct_ran[@spec.database_name] ||= if description =~ /distinct/ || @operations.any? { |op| op.name == 'distinct' }
220
264
  mongos_each_direct_client do |direct_client|
221
- direct_client['test'].distinct('foo').to_a
265
+ direct_client.use(@spec.database_name)['test'].distinct('foo').to_a
222
266
  end
223
267
  end
224
268
 
@@ -20,7 +20,7 @@ describe 'Command Monitoring Events' do
20
20
  end
21
21
 
22
22
  let(:subscriber) do
23
- Mongo::CommandMonitoring::TestSubscriber.new
23
+ EventSubscriber.new
24
24
  end
25
25
 
26
26
  let(:monitoring) do
@@ -32,25 +32,35 @@ describe 'Command Monitoring Events' do
32
32
  authorized_client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
33
33
  end
34
34
 
35
- after do
36
- monitoring.subscribers[Mongo::Monitoring::COMMAND].delete(subscriber)
37
- authorized_collection.find.delete_many
38
- end
39
-
40
- test.expectations.each do |expectation|
35
+ test.expectations.each_with_index do |expectation, index|
41
36
 
42
37
  it "generates a #{expectation.event_name} for #{expectation.command_name}" do
43
38
  begin
44
- test.run(authorized_collection)
45
- event = subscriber.send(expectation.event_type)[expectation.command_name]
46
- expect(event).to send(expectation.matcher, expectation)
39
+ test.run(authorized_collection, subscriber)
40
+ check_event(subscriber, index, expectation)
47
41
  rescue Mongo::Error::OperationFailure, Mongo::Error::BulkWriteError
48
- event = subscriber.send(expectation.event_type)[expectation.command_name]
49
- expect(event).to send(expectation.matcher, expectation)
42
+ check_event(subscriber, index, expectation)
50
43
  end
51
44
  end
52
45
  end
53
46
  end
54
47
  end
55
48
  end
49
+
50
+ def check_event(subscriber, index, expectation)
51
+ subscriber.all_events.length.should > index
52
+ # TODO move this filtering into EventSubscriber
53
+ events = subscriber.all_events.reject do |event|
54
+ (
55
+ event.is_a?(Mongo::Monitoring::Event::CommandStarted) ||
56
+ event.is_a?(Mongo::Monitoring::Event::CommandSucceeded) ||
57
+ event.is_a?(Mongo::Monitoring::Event::CommandFailed)
58
+ ) &&
59
+ %w(authenticate getnonce saslStart saslContinue).include?(event.command_name)
60
+ end
61
+ actual_event = events[index]
62
+ expected_event_type = expectation.event_type.sub(/_event$/, '')
63
+ Utils.underscore(actual_event.class.name.sub(/.*::/, '')).to_s.should == expected_event_type
64
+ expect(actual_event).to send(expectation.matcher, expectation)
65
+ end
56
66
  end