mongo 2.7.2 → 2.8.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -3
  4. data/lib/mongo/address.rb +17 -20
  5. data/lib/mongo/address/ipv4.rb +6 -3
  6. data/lib/mongo/address/ipv6.rb +6 -3
  7. data/lib/mongo/address/unix.rb +5 -2
  8. data/lib/mongo/auth.rb +15 -2
  9. data/lib/mongo/auth/cr/conversation.rb +4 -2
  10. data/lib/mongo/auth/ldap/conversation.rb +4 -2
  11. data/lib/mongo/auth/scram.rb +3 -7
  12. data/lib/mongo/auth/scram/conversation.rb +28 -19
  13. data/lib/mongo/auth/user.rb +45 -10
  14. data/lib/mongo/auth/x509/conversation.rb +4 -2
  15. data/lib/mongo/cluster.rb +9 -17
  16. data/lib/mongo/error.rb +2 -0
  17. data/lib/mongo/error/missing_password.rb +29 -0
  18. data/lib/mongo/error/operation_failure.rb +7 -3
  19. data/lib/mongo/error/parser.rb +2 -1
  20. data/lib/mongo/error/sdam_error_detection.rb +54 -0
  21. data/lib/mongo/operation/aggregate/command.rb +1 -16
  22. data/lib/mongo/operation/aggregate/op_msg.rb +1 -1
  23. data/lib/mongo/operation/collections_info.rb +2 -3
  24. data/lib/mongo/operation/delete/command.rb +2 -15
  25. data/lib/mongo/operation/delete/legacy.rb +1 -16
  26. data/lib/mongo/operation/explain/command.rb +1 -16
  27. data/lib/mongo/operation/explain/legacy.rb +1 -16
  28. data/lib/mongo/operation/find/command.rb +1 -16
  29. data/lib/mongo/operation/find/legacy.rb +1 -16
  30. data/lib/mongo/operation/get_more/command.rb +1 -16
  31. data/lib/mongo/operation/indexes/command.rb +1 -16
  32. data/lib/mongo/operation/indexes/legacy.rb +4 -16
  33. data/lib/mongo/operation/list_collections/command.rb +1 -16
  34. data/lib/mongo/operation/map_reduce/command.rb +1 -16
  35. data/lib/mongo/operation/parallel_scan/command.rb +1 -16
  36. data/lib/mongo/operation/result.rb +3 -0
  37. data/lib/mongo/operation/shared/executable.rb +4 -0
  38. data/lib/mongo/operation/shared/polymorphic_lookup.rb +1 -1
  39. data/lib/mongo/operation/shared/polymorphic_result.rb +8 -1
  40. data/lib/mongo/operation/shared/result/aggregatable.rb +0 -5
  41. data/lib/mongo/operation/update/command.rb +2 -15
  42. data/lib/mongo/operation/update/legacy.rb +1 -16
  43. data/lib/mongo/operation/users_info/command.rb +1 -16
  44. data/lib/mongo/retryable.rb +22 -10
  45. data/lib/mongo/server.rb +10 -1
  46. data/lib/mongo/server/app_metadata.rb +7 -2
  47. data/lib/mongo/server/connectable.rb +0 -6
  48. data/lib/mongo/server/connection.rb +86 -135
  49. data/lib/mongo/server/connection_base.rb +133 -0
  50. data/lib/mongo/server/connection_pool.rb +11 -24
  51. data/lib/mongo/server/connection_pool/queue.rb +41 -41
  52. data/lib/mongo/server/description.rb +1 -1
  53. data/lib/mongo/server/monitor.rb +4 -4
  54. data/lib/mongo/server/monitor/connection.rb +26 -7
  55. data/lib/mongo/server/pending_connection.rb +36 -0
  56. data/lib/mongo/server_selector/selectable.rb +9 -1
  57. data/lib/mongo/session.rb +0 -1
  58. data/lib/mongo/socket.rb +23 -6
  59. data/lib/mongo/socket/ssl.rb +11 -18
  60. data/lib/mongo/socket/tcp.rb +13 -14
  61. data/lib/mongo/socket/unix.rb +9 -27
  62. data/lib/mongo/uri.rb +1 -1
  63. data/lib/mongo/version.rb +1 -1
  64. data/spec/integration/auth_spec.rb +160 -0
  65. data/spec/integration/retryable_writes_spec.rb +55 -58
  66. data/spec/integration/sdam_error_handling_spec.rb +115 -0
  67. data/spec/mongo/address/ipv4_spec.rb +4 -0
  68. data/spec/mongo/address/ipv6_spec.rb +4 -0
  69. data/spec/mongo/auth/scram/conversation_spec.rb +6 -5
  70. data/spec/mongo/auth/scram/negotiation_spec.rb +25 -36
  71. data/spec/mongo/auth/scram_spec.rb +2 -2
  72. data/spec/mongo/auth/user_spec.rb +97 -0
  73. data/spec/mongo/client_construction_spec.rb +1 -1
  74. data/spec/mongo/error/operation_failure_spec.rb +125 -1
  75. data/spec/mongo/retryable_spec.rb +17 -8
  76. data/spec/mongo/server/connection_pool/queue_spec.rb +24 -10
  77. data/spec/mongo/server/connection_pool_spec.rb +30 -117
  78. data/spec/mongo/server/connection_spec.rb +147 -25
  79. data/spec/mongo/server/description_spec.rb +0 -14
  80. data/spec/mongo/server/monitor/connection_spec.rb +22 -0
  81. data/spec/mongo/server_selector_spec.rb +1 -0
  82. data/spec/mongo/server_spec.rb +6 -6
  83. data/spec/mongo/socket/ssl_spec.rb +48 -116
  84. data/spec/mongo/socket/tcp_spec.rb +22 -0
  85. data/spec/mongo/socket/unix_spec.rb +9 -9
  86. data/spec/mongo/socket_spec.rb +15 -3
  87. data/spec/spec_tests/server_selection_spec.rb +2 -0
  88. data/spec/support/client_registry.rb +8 -2
  89. data/spec/support/common_shortcuts.rb +20 -1
  90. data/spec/support/constraints.rb +10 -2
  91. data/spec/support/lite_constraints.rb +8 -0
  92. data/spec/support/spec_config.rb +9 -1
  93. metadata +14 -4
  94. metadata.gz.sig +0 -0
@@ -40,13 +40,14 @@ module Mongo
40
40
  # @return [ TCP ] The connected socket instance.
41
41
  #
42
42
  # @since 2.0.0
43
- def connect!(connect_timeout = nil)
44
- Timeout.timeout(connect_timeout, Error::SocketTimeoutError) do
43
+ def connect!
44
+ Timeout.timeout(options[:connect_timeout], Error::SocketTimeoutError) do
45
45
  socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
46
46
  handle_errors { socket.connect(::Socket.pack_sockaddr_in(port, host)) }
47
47
  self
48
48
  end
49
49
  end
50
+ private :connect!
50
51
 
51
52
  # Initializes a new TCP socket.
52
53
  #
@@ -58,23 +59,21 @@ module Mongo
58
59
  # @param [ Integer ] port The port number.
59
60
  # @param [ Float ] timeout The socket timeout value.
60
61
  # @param [ Integer ] family The socket family.
62
+ # @param [ Hash ] options The options.
63
+ #
64
+ # @option options [ Float ] :connect_timeout Connect timeout.
61
65
  #
62
66
  # @since 2.0.0
63
- def initialize(host, port, timeout, family)
64
- @host, @port, @timeout = host, port, timeout
67
+ def initialize(host, port, timeout, family, options = {})
68
+ @host, @port, @timeout, @options = host, port, timeout, options
65
69
  super(family)
70
+ connect!
66
71
  end
67
72
 
68
- # This object does not wrap another socket so it's always connectable.
69
- #
70
- # @example Is the socket connectable?
71
- # socket.connectable?
72
- #
73
- # @return [ true, false ] If the socket is connectable.
74
- #
75
- # @since 2.2.5
76
- def connectable?
77
- true
73
+ private
74
+
75
+ def address
76
+ "#{host}:#{port} (no TLS)"
78
77
  end
79
78
  end
80
79
  end
@@ -26,21 +26,6 @@ module Mongo
26
26
  # @return [ Float ] timeout The socket timeout.
27
27
  attr_reader :timeout
28
28
 
29
- # Establishes a socket connection.
30
- #
31
- # @example Connect the socket.
32
- # sock.connect!
33
- #
34
- # @note This method mutates the object by setting the socket
35
- # internally.
36
- #
37
- # @return [ Unix ] The connected socket instance.
38
- #
39
- # @since 2.0.0
40
- def connect!(connect_timeout = nil)
41
- self
42
- end
43
-
44
29
  # Initializes a new Unix socket.
45
30
  #
46
31
  # @example Create the Unix socket.
@@ -48,24 +33,21 @@ module Mongo
48
33
  #
49
34
  # @param [ String ] path The path.
50
35
  # @param [ Float ] timeout The socket timeout value.
36
+ # @param [ Hash ] options The options.
37
+ #
38
+ # @option options [ Float ] :connect_timeout Connect timeout (unused).
51
39
  #
52
40
  # @since 2.0.0
53
- def initialize(path, timeout)
54
- @path, @timeout = path, timeout
41
+ def initialize(path, timeout, options = {})
42
+ @path, @timeout, @options = path, timeout, options
55
43
  @socket = ::UNIXSocket.new(path)
56
44
  set_socket_options(@socket)
57
45
  end
58
46
 
59
- # This socket can only be used if the unix socket (@socket) has been created.
60
- #
61
- # @example Is the socket connectable?
62
- # socket.connectable?
63
- #
64
- # @return [ true, false ] If the socket is connectable.
65
- #
66
- # @since 2.2.5
67
- def connectable?
68
- !!@socket
47
+ private
48
+
49
+ def address
50
+ path
69
51
  end
70
52
  end
71
53
  end
@@ -155,7 +155,7 @@ module Mongo
155
155
  # @since 2.1.0
156
156
  UNESCAPED_UNIX_SOCKET = "UNIX domain sockets must be urlencoded.".freeze
157
157
 
158
- # Error details for a non-urlencoded auth database name.
158
+ # Error details for a non-urlencoded auth databsae name.
159
159
  #
160
160
  # @since 2.1.0
161
161
  UNESCAPED_DATABASE = "Auth database must be urlencoded.".freeze
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.7.2'.freeze
20
+ VERSION = '2.8.0.rc0'.freeze
21
21
  end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Auth' do
4
+ describe 'Unauthorized exception message' do
5
+ let(:server) do
6
+ authorized_client.cluster.next_primary
7
+ end
8
+
9
+ let(:connection) do
10
+ Mongo::Server::Connection.new(server, options)
11
+ end
12
+
13
+ before(:all) do
14
+ # If auth is configured, the test suite uses the configured user
15
+ # and does not create its own users. However, the configured user may
16
+ # not have the auth mechanisms we need. Therefore we create a user
17
+ # for this test without specifying auth mechanisms, which gets us
18
+ # server default (scram for 4.0, scram & scram256 for 4.2).
19
+
20
+ users = ClientRegistry.instance.global_client('root_authorized').use(:admin).database.users
21
+ unless users.info('existing_user').empty?
22
+ users.remove('existing_user')
23
+ end
24
+ users.create('existing_user', password: 'password')
25
+ end
26
+
27
+ context 'user mechanism not provided' do
28
+
29
+ context 'user does not exist' do
30
+ let(:options) { SpecConfig.instance.ssl_options.merge(
31
+ user: 'nonexistent_user') }
32
+
33
+ before do
34
+ expect(connection.app_metadata.send(:document)[:saslSupportedMechs]).to eq('admin.nonexistent_user')
35
+ end
36
+
37
+ context 'scram-sha-1 only server' do
38
+ min_server_fcv '3.0'
39
+ max_server_version '3.6'
40
+
41
+ it 'indicates scram-sha-1 was used' do
42
+ expect do
43
+ connection.connect!
44
+ end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
45
+ end
46
+ end
47
+
48
+ context 'scram-sha-256 server' do
49
+ min_server_fcv '4.0'
50
+
51
+ # An existing user on 4.0+ will negotiate scram-sha-256.
52
+ # A non-existing user on 4.0+ will negotiate scram-sha-1.
53
+ it 'indicates scram-sha-1 was used' do
54
+ expect do
55
+ connection.connect!
56
+ end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'user exists' do
62
+ let(:options) { SpecConfig.instance.ssl_options.merge(
63
+ user: 'existing_user', password: 'bogus') }
64
+
65
+ before do
66
+ expect(connection.app_metadata.send(:document)[:saslSupportedMechs]).to eq("admin.existing_user")
67
+ end
68
+
69
+ context 'scram-sha-1 only server' do
70
+ min_server_fcv '3.0'
71
+ max_server_version '3.6'
72
+
73
+ it 'indicates scram-sha-1 was used' do
74
+ expect do
75
+ connection.connect!
76
+ end.to raise_error(Mongo::Auth::Unauthorized, "User existing_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)")
77
+ end
78
+ end
79
+
80
+ context 'scram-sha-256 server' do
81
+ min_server_fcv '4.0'
82
+
83
+ # An existing user on 4.0+ will negotiate scram-sha-256.
84
+ # A non-existing user on 4.0+ will negotiate scram-sha-1.
85
+ it 'indicates scram-sha-256 was used' do
86
+ expect do
87
+ connection.connect!
88
+ end.to raise_error(Mongo::Auth::Unauthorized, "User existing_user (mechanism: scram256) is not authorized to access admin (used mechanism: SCRAM-SHA-256)")
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'user mechanism is provided' do
95
+ min_server_fcv '3.0'
96
+
97
+ context 'scram-sha-1 requested' do
98
+ let(:options) { SpecConfig.instance.ssl_options.merge(
99
+ user: 'nonexistent_user', auth_mech: :scram) }
100
+
101
+ it 'indicates scram-sha-1 was requested and used' do
102
+ expect do
103
+ connection.connect!
104
+ end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
105
+ end
106
+ end
107
+
108
+ context 'scram-sha-256 requested' do
109
+ min_server_fcv '4.0'
110
+
111
+ let(:options) { SpecConfig.instance.ssl_options.merge(
112
+ user: 'nonexistent_user', auth_mech: :scram256) }
113
+
114
+ it 'indicates scram-sha-256 was requested and used' do
115
+ expect do
116
+ connection.connect!
117
+ end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram256) is not authorized to access admin (used mechanism: SCRAM-SHA-256)')
118
+ end
119
+ end
120
+ end
121
+
122
+ context 'attempting to connect to a non-tls server with tls' do
123
+ require_no_ssl
124
+
125
+ let(:options) { {ssl: true} }
126
+
127
+ it 'reports host, port and tls status' do
128
+ begin
129
+ connection.connect!
130
+ rescue Mongo::Error::SocketError => exc
131
+ end
132
+ expect(exc).not_to be nil
133
+ expect(exc.message).to include('OpenSSL::SSL::SSLError')
134
+ expect(exc.message).to include(server.address.to_s)
135
+ expect(exc.message).to include('TLS')
136
+ expect(exc.message).not_to include('no TLS')
137
+ end
138
+ end
139
+
140
+ context 'attempting to connect to a tls server without tls' do
141
+ require_ssl
142
+
143
+ let(:options) { {} }
144
+
145
+ it 'reports host, port and tls status' do
146
+ begin
147
+ connection.connect!
148
+ rescue Mongo::Error::SocketError => exc
149
+ end
150
+ expect(exc).not_to be nil
151
+ expect(exc.message).not_to include('OpenSSL::SSL::SSLError')
152
+ addresses = Socket.getaddrinfo(server.address.host, nil)
153
+ expect(addresses.any? do |address|
154
+ exc.message.include?("#{address[2]}:#{server.address.port}")
155
+ end).to be true
156
+ expect(exc.message).to include('no TLS')
157
+ end
158
+ end
159
+ end
160
+ end
@@ -1,5 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
+ # The tests raise OperationFailure in socket reads. This is done for
4
+ # convenience to make the tests uniform between socket errors and operation
5
+ # failures; in reality a socket read will never raise OperationFailure as
6
+ # wire protocol parsing code raises this exception. For the purposes of
7
+ # testing retryable writes, it is acceptable to raise OperationFailure in
8
+ # socket reads because both exceptions end up getting handled in the same
9
+ # place by retryable writes code. The SDAM error handling test specifically
10
+ # checks server state (i.e. being marked unknown) and scanning behavior
11
+ # that is performed by the wire protocol code; this test omits scan assertions
12
+ # as otherwise it quickly becomes unwieldy.
3
13
  describe 'Retryable writes integration tests' do
4
14
  include PrimarySocket
5
15
 
@@ -7,21 +17,33 @@ describe 'Retryable writes integration tests' do
7
17
  authorized_collection.delete_many
8
18
  end
9
19
 
20
+ let(:check_collection) do
21
+ # Verify data in the collection using another client instance to avoid
22
+ # having the verification read trigger cluster scans on the writing client
23
+ subscribed_client[TEST_COLL]
24
+ end
25
+
26
+ let(:primary_connection) do
27
+ client.database.command(ping: 1)
28
+ expect(primary_server.pool.send(:queue).pool_size).to eq(1)
29
+ expect(primary_server.pool.send(:queue).queue_size).to eq(1)
30
+ primary_server.pool.send(:queue).queue.first
31
+ end
32
+
10
33
  shared_examples_for 'an operation that is retried' do
11
34
 
12
- context 'when the operation fails on the first attempt' do
35
+ context 'when the operation fails on the first attempt and succeeds on the second attempt' do
13
36
 
14
37
  before do
15
- # Note that for writes, server.connectable? is called, refreshing the socket
16
- allow(primary_server).to receive(:connectable?).and_return(true)
17
- expect(primary_socket).to receive(:write).and_raise(error)
38
+ wait_for_all_servers(client.cluster)
39
+
40
+ allow(primary_socket).to receive(:write).and_raise(error)
18
41
  end
19
42
 
20
43
  context 'when the error is retryable' do
21
44
 
22
45
  before do
23
46
  expect(Mongo::Logger.logger).to receive(:warn).once.and_call_original
24
- expect(client.cluster).to receive(:scan!)
25
47
  end
26
48
 
27
49
  context 'when the error is a SocketError' do
@@ -54,6 +76,10 @@ describe 'Retryable writes integration tests' do
54
76
  Mongo::Error::OperationFailure.new('not master')
55
77
  end
56
78
 
79
+ let(:reply) do
80
+ make_not_master_reply
81
+ end
82
+
57
83
  it 'retries writes' do
58
84
  operation
59
85
  expect(expectation).to eq(successful_retry_value)
@@ -66,13 +92,13 @@ describe 'Retryable writes integration tests' do
66
92
  context 'when the error is a non-retryable OperationFailure' do
67
93
 
68
94
  let(:error) do
69
- Mongo::Error::OperationFailure.new('other error')
95
+ Mongo::Error::OperationFailure.new('other error', code: 123)
70
96
  end
71
97
 
72
98
  it 'does not retry writes' do
73
99
  expect {
74
100
  operation
75
- }.to raise_error(error)
101
+ }.to raise_error(Mongo::Error::OperationFailure, /other error/)
76
102
  expect(expectation).to eq(unsuccessful_retry_value)
77
103
  end
78
104
  end
@@ -82,8 +108,6 @@ describe 'Retryable writes integration tests' do
82
108
  context 'when the operation fails on the first attempt and again on the second attempt' do
83
109
 
84
110
  before do
85
- # Note that for writes, server.connectable? is called, refreshing the socket
86
- allow(primary_server).to receive(:connectable?).and_return(true)
87
111
  allow(primary_socket).to receive(:write).and_raise(error)
88
112
  end
89
113
 
@@ -138,9 +162,12 @@ describe 'Retryable writes integration tests' do
138
162
  end
139
163
  end
140
164
 
141
- [Mongo::Error::SocketError,
142
- Mongo::Error::SocketTimeoutError,
143
- Mongo::Error::OperationFailure.new('not master')].each do |retryable_error|
165
+ [
166
+ Mongo::Error::SocketError,
167
+ Mongo::Error::SocketTimeoutError,
168
+ Mongo::Error::OperationFailure.new('not master'),
169
+ Mongo::Error::OperationFailure.new('node is recovering'),
170
+ ].each do |retryable_error|
144
171
 
145
172
  context "when the first error is a #{retryable_error}" do
146
173
 
@@ -149,6 +176,7 @@ describe 'Retryable writes integration tests' do
149
176
  end
150
177
 
151
178
  before do
179
+ wait_for_all_servers(client.cluster)
152
180
  bad_socket = primary_connection.address.socket(primary_connection.socket_timeout,
153
181
  primary_connection.send(:ssl_options))
154
182
  good_socket = primary_connection.address.socket(primary_connection.socket_timeout,
@@ -163,12 +191,6 @@ describe 'Retryable writes integration tests' do
163
191
  Mongo::Error::SocketError
164
192
  end
165
193
 
166
- before do
167
- # server selector can call scan! until it finds a server,
168
- # hence more than two scan! calls may be issued
169
- expect(client.cluster).to receive(:scan!).at_least(:twice).and_call_original
170
- end
171
-
172
194
  it 'does not retry writes and raises the second error' do
173
195
  expect {
174
196
  operation
@@ -179,12 +201,6 @@ describe 'Retryable writes integration tests' do
179
201
 
180
202
  context 'when the second error is a SocketTimeoutError' do
181
203
 
182
- before do
183
- # server selector can call scan! until it finds a server,
184
- # hence more than two scan! calls may be issued
185
- expect(client.cluster).to receive(:scan!).at_least(:twice).and_call_original
186
- end
187
-
188
204
  let(:second_error) do
189
205
  Mongo::Error::SocketTimeoutError
190
206
  end
@@ -199,12 +215,6 @@ describe 'Retryable writes integration tests' do
199
215
 
200
216
  context 'when the second error is a retryable OperationFailure' do
201
217
 
202
- before do
203
- # server selector can call scan! until it finds a server,
204
- # hence more than two scan! calls may be issued
205
- expect(client.cluster).to receive(:scan!).at_least(:twice).and_call_original
206
- end
207
-
208
218
  let(:second_error) do
209
219
  Mongo::Error::OperationFailure.new('not master')
210
220
  end
@@ -219,10 +229,6 @@ describe 'Retryable writes integration tests' do
219
229
 
220
230
  context 'when the second error is a non-retryable OperationFailure' do
221
231
 
222
- before do
223
- expect(client.cluster).to receive(:scan!).once
224
- end
225
-
226
232
  let(:second_error) do
227
233
  Mongo::Error::OperationFailure.new('other error')
228
234
  end
@@ -260,10 +266,7 @@ describe 'Retryable writes integration tests' do
260
266
  end
261
267
 
262
268
  before do
263
- # Note that for writes, server.connectable? is called, refreshing the socket
264
- allow(primary_server).to receive(:connectable?).and_return(true)
265
- expect(primary_socket).to receive(:write).and_raise(Mongo::Error::SocketError)
266
- expect(client.cluster).not_to receive(:scan!)
269
+ expect(primary_socket).to receive(:write).exactly(:once).and_raise(Mongo::Error::SocketError)
267
270
  end
268
271
 
269
272
  it 'does not retry writes' do
@@ -285,10 +288,7 @@ describe 'Retryable writes integration tests' do
285
288
  end
286
289
 
287
290
  before do
288
- # Note that for writes, server.connectable? is called, refreshing the socket
289
- allow(primary_server).to receive(:connectable?).and_return(true)
290
291
  expect(primary_socket).to receive(:write).and_raise(Mongo::Error::SocketError)
291
- expect(client.cluster).not_to receive(:scan!)
292
292
  end
293
293
 
294
294
  it 'does not retry writes' do
@@ -369,11 +369,7 @@ describe 'Retryable writes integration tests' do
369
369
  context 'when the client has retry_writes set to false' do
370
370
 
371
371
  let!(:client) do
372
- authorized_client.with(retry_writes: false)
373
- end
374
-
375
- after do
376
- client.close
372
+ authorized_client_without_retry_writes
377
373
  end
378
374
 
379
375
  context 'when the collection has write concern acknowledged' do
@@ -405,6 +401,7 @@ describe 'Retryable writes integration tests' do
405
401
  end
406
402
 
407
403
  context 'when the client has retry_writes not set' do
404
+ require_no_retry_writes
408
405
 
409
406
  let!(:client) do
410
407
  authorized_client
@@ -446,7 +443,7 @@ describe 'Retryable writes integration tests' do
446
443
  end
447
444
 
448
445
  let(:expectation) do
449
- collection.find(a: 1).count
446
+ check_collection.find(a: 1).count
450
447
  end
451
448
 
452
449
  let(:successful_retry_value) do
@@ -472,7 +469,7 @@ describe 'Retryable writes integration tests' do
472
469
  end
473
470
 
474
471
  let(:expectation) do
475
- collection.find(a: 1).count
472
+ check_collection.find(a: 1).count
476
473
  end
477
474
 
478
475
  let(:successful_retry_value) do
@@ -498,7 +495,7 @@ describe 'Retryable writes integration tests' do
498
495
  end
499
496
 
500
497
  let(:expectation) do
501
- collection.find(a: 1).count
498
+ check_collection.find(a: 1).count
502
499
  end
503
500
 
504
501
  let(:successful_retry_value) do
@@ -524,7 +521,7 @@ describe 'Retryable writes integration tests' do
524
521
  end
525
522
 
526
523
  let(:expectation) do
527
- collection.find(a: 1).count
524
+ check_collection.find(a: 1).count
528
525
  end
529
526
 
530
527
  let(:successful_retry_value) do
@@ -550,7 +547,7 @@ describe 'Retryable writes integration tests' do
550
547
  end
551
548
 
552
549
  let(:expectation) do
553
- collection.find(a: 1).count
550
+ check_collection.find(a: 1).count
554
551
  end
555
552
 
556
553
  let(:successful_retry_value) do
@@ -576,7 +573,7 @@ describe 'Retryable writes integration tests' do
576
573
  end
577
574
 
578
575
  let(:expectation) do
579
- collection.find(a: 3).count
576
+ check_collection.find(a: 3).count
580
577
  end
581
578
 
582
579
  let(:successful_retry_value) do
@@ -602,7 +599,7 @@ describe 'Retryable writes integration tests' do
602
599
  end
603
600
 
604
601
  let(:expectation) do
605
- collection.find(a: 1).count
602
+ check_collection.find(a: 1).count
606
603
  end
607
604
 
608
605
  let(:successful_retry_value) do
@@ -629,7 +626,7 @@ describe 'Retryable writes integration tests' do
629
626
  end
630
627
 
631
628
  let(:expectation) do
632
- collection.find(a: 1).count
629
+ check_collection.find(a: 1).count
633
630
  end
634
631
 
635
632
  let(:unsuccessful_retry_value) do
@@ -652,7 +649,7 @@ describe 'Retryable writes integration tests' do
652
649
  end
653
650
 
654
651
  let(:expectation) do
655
- collection.find(a: 1).count
652
+ check_collection.find(a: 1).count
656
653
  end
657
654
 
658
655
  let(:unsuccessful_retry_value) do
@@ -676,7 +673,7 @@ describe 'Retryable writes integration tests' do
676
673
  end
677
674
 
678
675
  let(:expectation) do
679
- collection.find(a: 1).count
676
+ check_collection.find(a: 1).count
680
677
  end
681
678
 
682
679
  let(:successful_retry_value) do
@@ -703,7 +700,7 @@ describe 'Retryable writes integration tests' do
703
700
  end
704
701
 
705
702
  let(:expectation) do
706
- collection.find(a: 1).count
703
+ check_collection.find(a: 1).count
707
704
  end
708
705
 
709
706
  let(:unsuccessful_retry_value) do
@@ -726,7 +723,7 @@ describe 'Retryable writes integration tests' do
726
723
  end
727
724
 
728
725
  let(:expectation) do
729
- collection.find(a: 1).count
726
+ check_collection.find(a: 1).count
730
727
  end
731
728
 
732
729
  let(:unsuccessful_retry_value) do