mongo 2.5.0.beta → 2.5.0

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 (172) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/address.rb +1 -1
  5. data/lib/mongo/address/unix.rb +1 -1
  6. data/lib/mongo/auth/user.rb +0 -5
  7. data/lib/mongo/auth/user/view.rb +4 -4
  8. data/lib/mongo/bulk_write.rb +60 -32
  9. data/lib/mongo/client.rb +44 -8
  10. data/lib/mongo/cluster.rb +14 -12
  11. data/lib/mongo/cluster/periodic_executor.rb +106 -0
  12. data/lib/mongo/cluster/{cursor_reaper.rb → reapers/cursor_reaper.rb} +5 -37
  13. data/lib/mongo/cluster/reapers/socket_reaper.rb +59 -0
  14. data/lib/mongo/collection.rb +9 -6
  15. data/lib/mongo/collection/view.rb +2 -2
  16. data/lib/mongo/collection/view/builder/aggregation.rb +2 -1
  17. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  18. data/lib/mongo/collection/view/change_stream.rb +14 -1
  19. data/lib/mongo/collection/view/map_reduce.rb +30 -13
  20. data/lib/mongo/collection/view/readable.rb +5 -5
  21. data/lib/mongo/collection/view/writable.rb +98 -51
  22. data/lib/mongo/error.rb +3 -0
  23. data/lib/mongo/error/invalid_txt_record.rb +27 -0
  24. data/lib/mongo/error/invalid_uri.rb +7 -6
  25. data/lib/mongo/error/mismatched_domain.rb +27 -0
  26. data/lib/mongo/error/no_srv_records.rb +26 -0
  27. data/lib/mongo/error/unsupported_features.rb +0 -18
  28. data/lib/mongo/index/view.rb +2 -2
  29. data/lib/mongo/operation.rb +1 -0
  30. data/lib/mongo/operation/causally_consistent.rb +33 -0
  31. data/lib/mongo/operation/commands.rb +2 -1
  32. data/lib/mongo/operation/commands/aggregate.rb +2 -7
  33. data/lib/mongo/operation/commands/count.rb +27 -0
  34. data/lib/mongo/operation/commands/distinct.rb +27 -0
  35. data/lib/mongo/operation/commands/find.rb +3 -1
  36. data/lib/mongo/operation/commands/map_reduce.rb +1 -0
  37. data/lib/mongo/operation/commands/parallel_scan.rb +1 -0
  38. data/lib/mongo/operation/specifiable.rb +12 -0
  39. data/lib/mongo/operation/uses_command_op_msg.rb +36 -5
  40. data/lib/mongo/operation/write.rb +0 -5
  41. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -8
  42. data/lib/mongo/operation/write/bulk/mergable.rb +2 -0
  43. data/lib/mongo/operation/write/command/create_index.rb +19 -0
  44. data/lib/mongo/operation/write/command/create_user.rb +19 -0
  45. data/lib/mongo/operation/write/command/delete.rb +1 -2
  46. data/lib/mongo/operation/write/command/drop_index.rb +19 -0
  47. data/lib/mongo/operation/write/command/insert.rb +1 -2
  48. data/lib/mongo/operation/write/command/remove_user.rb +19 -0
  49. data/lib/mongo/operation/write/command/update.rb +1 -2
  50. data/lib/mongo/operation/write/command/update_user.rb +19 -0
  51. data/lib/mongo/operation/write/write_command_enabled.rb +1 -3
  52. data/lib/mongo/protocol/compressed.rb +2 -1
  53. data/lib/mongo/protocol/serializers.rb +6 -6
  54. data/lib/mongo/retryable.rb +48 -5
  55. data/lib/mongo/server.rb +15 -0
  56. data/lib/mongo/server/connection.rb +21 -1
  57. data/lib/mongo/server/connection_pool.rb +3 -0
  58. data/lib/mongo/server/connection_pool/queue.rb +50 -5
  59. data/lib/mongo/server/description.rb +11 -3
  60. data/lib/mongo/server/description/features.rb +26 -7
  61. data/lib/mongo/session.rb +133 -6
  62. data/lib/mongo/session/server_session.rb +30 -0
  63. data/lib/mongo/session/session_pool.rb +20 -20
  64. data/lib/mongo/uri.rb +88 -44
  65. data/lib/mongo/uri/srv_protocol.rb +158 -0
  66. data/lib/mongo/version.rb +1 -1
  67. data/lib/mongo/write_concern/normalizable.rb +12 -0
  68. data/mongo.gemspec +1 -2
  69. data/spec/mongo/address_spec.rb +12 -0
  70. data/spec/mongo/auth/user/view_spec.rb +1 -5
  71. data/spec/mongo/bulk_write_spec.rb +232 -401
  72. data/spec/mongo/change_stream_examples_spec.rb +150 -0
  73. data/spec/mongo/client_spec.rb +142 -2
  74. data/spec/mongo/cluster/cursor_reaper_spec.rb +0 -70
  75. data/spec/mongo/cluster/socket_reaper_spec.rb +32 -0
  76. data/spec/mongo/cluster_spec.rb +11 -7
  77. data/spec/mongo/collection/view/aggregation_spec.rb +46 -1
  78. data/spec/mongo/collection/view/builder/find_command_spec.rb +15 -0
  79. data/spec/mongo/collection/view/change_stream_spec.rb +79 -12
  80. data/spec/mongo/collection/view/map_reduce_spec.rb +120 -4
  81. data/spec/mongo/collection/view/readable_spec.rb +23 -5
  82. data/spec/mongo/collection_spec.rb +292 -102
  83. data/spec/mongo/command_monitoring_spec.rb +26 -32
  84. data/spec/mongo/crud_spec.rb +1 -1
  85. data/spec/mongo/cursor_spec.rb +2 -3
  86. data/spec/mongo/database_spec.rb +30 -14
  87. data/spec/mongo/dns_seedlist_discovery_spec.rb +94 -0
  88. data/spec/mongo/grid/fs_bucket_spec.rb +1 -1
  89. data/spec/mongo/grid/stream/write_spec.rb +1 -1
  90. data/spec/mongo/index/view_spec.rb +8 -46
  91. data/spec/mongo/operation/write/bulk/delete_spec.rb +2 -2
  92. data/spec/mongo/operation/write/bulk/insert_spec.rb +2 -10
  93. data/spec/mongo/operation/write/{create_index_spec.rb → command/create_index_spec.rb} +2 -6
  94. data/spec/mongo/operation/write/command/delete_spec.rb +35 -7
  95. data/spec/mongo/operation/write/{drop_index_spec.rb → command/drop_index_spec.rb} +1 -1
  96. data/spec/mongo/operation/write/command/insert_spec.rb +37 -6
  97. data/spec/mongo/operation/write/{remove_user_spec.rb → command/remove_user_spec.rb} +2 -6
  98. data/spec/mongo/operation/write/command/update_spec.rb +34 -7
  99. data/spec/mongo/operation/write/{update_user_spec.rb → command/update_user_spec.rb} +1 -1
  100. data/spec/mongo/operation/write/create_user_spec.rb +1 -1
  101. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  102. data/spec/mongo/operation/write/insert_spec.rb +2 -10
  103. data/spec/mongo/operation/write/update_spec.rb +3 -15
  104. data/spec/mongo/retryable_spec.rb +1 -1
  105. data/spec/mongo/retryable_writes_spec.rb +815 -0
  106. data/spec/mongo/server/connection_pool/queue_spec.rb +35 -2
  107. data/spec/mongo/server/connection_pool_spec.rb +234 -1
  108. data/spec/mongo/server/connection_spec.rb +10 -6
  109. data/spec/mongo/server/description/features_spec.rb +51 -37
  110. data/spec/mongo/server/description_spec.rb +6 -3
  111. data/spec/mongo/server_spec.rb +87 -0
  112. data/spec/mongo/session/server_session_spec.rb +43 -0
  113. data/spec/mongo/session/session_pool_spec.rb +63 -27
  114. data/spec/mongo/session_spec.rb +247 -0
  115. data/spec/mongo/shell_examples_spec.rb +2 -2
  116. data/spec/mongo/uri/srv_protocol_spec.rb +933 -0
  117. data/spec/mongo/uri_spec.rb +42 -3
  118. data/spec/mongo/write_concern/acknowledged_spec.rb +11 -0
  119. data/spec/mongo/write_concern/unacknowledged_spec.rb +11 -0
  120. data/spec/spec_helper.rb +11 -25
  121. data/spec/support/authorization.rb +2 -1
  122. data/spec/support/connection_string.rb +8 -4
  123. data/spec/support/crud.rb +38 -24
  124. data/spec/support/crud/write.rb +30 -3
  125. data/spec/support/crud_tests/read/aggregate-out.yml +21 -0
  126. data/spec/support/crud_tests/write/bulkWrite-arrayFilters.yml +44 -0
  127. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +1 -1
  128. data/spec/support/crud_tests/write/insertMany.yml +1 -3
  129. data/spec/support/crud_tests/write/replaceOne.yml +1 -1
  130. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +1 -1
  131. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +1 -1
  132. data/spec/support/dns_seedlist_discovery_tests/longer-parent-in-return.yml +11 -0
  133. data/spec/support/dns_seedlist_discovery_tests/misformatted-option.yml +5 -0
  134. data/spec/support/dns_seedlist_discovery_tests/no-results.yml +5 -0
  135. data/spec/support/dns_seedlist_discovery_tests/not-enough-parts.yml +5 -0
  136. data/spec/support/dns_seedlist_discovery_tests/one-result-default-port.yml +10 -0
  137. data/spec/support/dns_seedlist_discovery_tests/one-txt-record-multiple-strings.yml +10 -0
  138. data/spec/support/dns_seedlist_discovery_tests/one-txt-record.yml +11 -0
  139. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch1.yml +5 -0
  140. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch2.yml +5 -0
  141. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch3.yml +5 -0
  142. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch4.yml +5 -0
  143. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch5.yml +5 -0
  144. data/spec/support/dns_seedlist_discovery_tests/returned-parent-too-short.yml +5 -0
  145. data/spec/support/dns_seedlist_discovery_tests/returned-parent-wrong.yml +5 -0
  146. data/spec/support/dns_seedlist_discovery_tests/two-results-default-port.yml +11 -0
  147. data/spec/support/dns_seedlist_discovery_tests/two-results-nonstandard-port.yml +11 -0
  148. data/spec/support/dns_seedlist_discovery_tests/two-txt-records.yml +5 -0
  149. data/spec/support/dns_seedlist_discovery_tests/txt-record-not-allowed-option.yml +5 -0
  150. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-ssl-option.yml +11 -0
  151. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-uri-option.yml +11 -0
  152. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-unallowed-option.yml +5 -0
  153. data/spec/support/dns_seedlist_discovery_tests/uri-with-port.yml +5 -0
  154. data/spec/support/dns_seedlist_discovery_tests/uri-with-two-hosts.yml +5 -0
  155. data/spec/support/retryable_writes_tests/bulkWrite.yml +305 -0
  156. data/spec/support/retryable_writes_tests/deleteOne.yml +51 -0
  157. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +52 -0
  158. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +57 -0
  159. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +56 -0
  160. data/spec/support/retryable_writes_tests/insertMany.yml +72 -0
  161. data/spec/support/retryable_writes_tests/insertOne.yml +55 -0
  162. data/spec/support/retryable_writes_tests/replaceOne.yml +60 -0
  163. data/spec/support/retryable_writes_tests/updateOne.yml +120 -0
  164. data/spec/support/shared/session.rb +525 -24
  165. metadata +437 -350
  166. metadata.gz.sig +0 -0
  167. data/lib/mongo/operation/commands/user_query.rb +0 -72
  168. data/lib/mongo/operation/write/create_index.rb +0 -67
  169. data/lib/mongo/operation/write/create_user.rb +0 -50
  170. data/lib/mongo/operation/write/drop_index.rb +0 -63
  171. data/lib/mongo/operation/write/remove_user.rb +0 -48
  172. data/lib/mongo/operation/write/update_user.rb +0 -50
@@ -14,7 +14,7 @@ describe Mongo::Server::Description do
14
14
  'arbiters' => [
15
15
  '127.0.0.1:27120'
16
16
  ],
17
- 'primary' => '127.0.0.1:27019',
17
+ 'primary' => authorized_primary.address.to_s,
18
18
  'tags' => { 'rack' => 'a' },
19
19
  'me' => '127.0.0.1:27019',
20
20
  'maxBsonObjectSize' => 16777216,
@@ -25,12 +25,14 @@ describe Mongo::Server::Description do
25
25
  'localTime' => Time.now,
26
26
  'lastWrite' => { 'lastWriteDate' => Time.now },
27
27
  'logicalSessionTimeoutMinutes' => 7,
28
+ 'operationTime' => 1,
29
+ '$clusterTime' => 1,
28
30
  'ok' => 1
29
31
  }
30
32
  end
31
33
 
32
34
  let(:address) do
33
- Mongo::Address.new('127.0.0.1:27017')
35
+ Mongo::Address.new(authorized_primary.address.to_s)
34
36
  end
35
37
 
36
38
  let(:monitoring) do
@@ -868,7 +870,8 @@ describe Mongo::Server::Description do
868
870
  described_class.new(address, replica.merge(
869
871
  'localTime' => 1,
870
872
  'lastWrite' => { 'lastWriteDate' => 1 },
871
- 'operationTime' => 1
873
+ 'operationTime' => 2,
874
+ '$clusterTime' => 2
872
875
  ))
873
876
  end
874
877
 
@@ -201,4 +201,91 @@ describe Mongo::Server do
201
201
  expect(server.reconnect!).to be(true)
202
202
  end
203
203
  end
204
+
205
+ describe 'retry_writes?' do
206
+
207
+ let(:server) do
208
+ described_class.new(address, cluster, monitoring, listeners, TEST_OPTIONS)
209
+ end
210
+
211
+ before do
212
+ allow(server).to receive(:features).and_return(features)
213
+ end
214
+
215
+ context 'when the server version is less than 3.6' do
216
+
217
+ let(:features) do
218
+ double('features', sessions_enabled?: false)
219
+ end
220
+
221
+ context 'when the server has a logical_session_timeout value' do
222
+
223
+ before do
224
+ allow(server).to receive(:logical_session_timeout).and_return(true)
225
+ end
226
+
227
+ it 'returns false' do
228
+ expect(server.retry_writes?).to be(false)
229
+ end
230
+ end
231
+
232
+ context 'when the server does not have a logical_session_timeout value' do
233
+
234
+ before do
235
+ allow(server).to receive(:logical_session_timeout).and_return(nil)
236
+ end
237
+
238
+ it 'returns false' do
239
+ expect(server.retry_writes?).to be(false)
240
+ end
241
+ end
242
+ end
243
+
244
+ context 'when the server version is at least 3.6' do
245
+
246
+ let(:features) do
247
+ double('features', sessions_enabled?: true)
248
+ end
249
+
250
+ context 'when the server has a logical_session_timeout value' do
251
+
252
+ before do
253
+ allow(server).to receive(:logical_session_timeout).and_return(true)
254
+ end
255
+
256
+ context 'when the server is a standalone' do
257
+
258
+ before do
259
+ allow(server).to receive(:standalone?).and_return(true)
260
+ end
261
+
262
+ it 'returns false' do
263
+ expect(server.retry_writes?).to be(false)
264
+ end
265
+ end
266
+
267
+ context 'when the server is not a standalone' do
268
+
269
+ before do
270
+ allow(server).to receive(:standalone?).and_return(true)
271
+ end
272
+
273
+ it 'returns false' do
274
+ expect(server.retry_writes?).to be(false)
275
+ end
276
+ end
277
+ end
278
+
279
+ context 'when the server does not have a logical_session_timeout value' do
280
+
281
+ before do
282
+ allow(server).to receive(:logical_session_timeout).and_return(nil)
283
+ end
284
+
285
+ it 'returns false' do
286
+ expect(server.retry_writes?).to be(false)
287
+ end
288
+ end
289
+ end
290
+ end
204
291
  end
@@ -9,8 +9,51 @@ describe Mongo::Session::ServerSession do
9
9
  end
10
10
 
11
11
  it 'sets a UUID as the session id' do
12
+ expect(described_class.new.instance_variable_get(:@session_id)).to be_a(BSON::Document)
12
13
  expect(described_class.new.session_id).to be_a(BSON::Document)
13
14
  expect(described_class.new.session_id[:id]).to be_a(BSON::Binary)
14
15
  end
15
16
  end
17
+
18
+ describe '#next_txn_number' do
19
+
20
+ it 'advances and returns the next transaction number' do
21
+ expect(described_class.new.next_txn_num).to be(0)
22
+ end
23
+
24
+ context 'when the method is called multiple times' do
25
+
26
+ let(:server_session) do
27
+ described_class.new
28
+ end
29
+
30
+ before do
31
+ server_session.next_txn_num
32
+ server_session.next_txn_num
33
+ end
34
+
35
+ it 'advances and returns the next transaction number' do
36
+ expect(server_session.next_txn_num).to be(2)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#inspect' do
42
+
43
+ let(:session) do
44
+ described_class.new
45
+ end
46
+
47
+ it 'includes the Ruby object_id in the formatted string' do
48
+ expect(session.inspect).to include(session.object_id.to_s)
49
+ end
50
+
51
+ it 'includes the session_id in the formatted string' do
52
+ expect(session.inspect).to include(session.session_id.to_s)
53
+ end
54
+
55
+ it 'includes the last_use in the formatted string' do
56
+ expect(session.inspect).to include(session.last_use.to_s)
57
+ end
58
+ end
16
59
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Mongo::Session::SessionPool do
3
+ describe Mongo::Session::SessionPool, if: test_sessions? do
4
4
 
5
5
  describe '.create' do
6
6
 
@@ -32,7 +32,27 @@ describe Mongo::Session::SessionPool do
32
32
  end
33
33
  end
34
34
 
35
- describe 'checkout', if: sessions_enabled? do
35
+ describe '#inspect' do
36
+
37
+ let(:pool) do
38
+ described_class.new(authorized_client)
39
+ end
40
+
41
+ before do
42
+ s = pool.checkout
43
+ pool.checkin(s)
44
+ end
45
+
46
+ it 'includes the Ruby object_id in the formatted string' do
47
+ expect(pool.inspect).to include(pool.object_id.to_s)
48
+ end
49
+
50
+ it 'includes the pool size in the formatted string' do
51
+ expect(pool.inspect).to include('current_size=1')
52
+ end
53
+ end
54
+
55
+ describe 'checkout' do
36
56
 
37
57
  let(:pool) do
38
58
  described_class.new(authorized_client)
@@ -115,7 +135,7 @@ describe Mongo::Session::SessionPool do
115
135
  end
116
136
  end
117
137
 
118
- describe '#end_sessions', if: sessions_enabled? do
138
+ describe '#end_sessions' do
119
139
 
120
140
  let(:pool) do
121
141
  described_class.create(client)
@@ -139,44 +159,60 @@ describe Mongo::Session::SessionPool do
139
159
  EventSubscriber.new
140
160
  end
141
161
 
142
- before do
143
- client.database.command(ping: 1)
144
- pool.checkin(session_a)
145
- pool.checkin(session_b)
146
- pool.end_sessions
147
- end
148
-
149
162
  after do
150
163
  client.close
151
164
  end
152
165
 
153
- let(:end_sessions_command) do
154
- subscriber.started_events.find { |c| c.command_name == :endSessions}
155
- end
166
+ context 'when the number of ids is not larger than 10,000' do
156
167
 
157
- it 'sends the endSessions command with all the session ids' do
158
- expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_a.session_id))
159
- expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_b.session_id))
160
- end
168
+ before do
169
+ client.database.command(ping: 1)
170
+ pool.checkin(session_a)
171
+ pool.checkin(session_b)
172
+ end
173
+
174
+ let!(:cluster_time) do
175
+ client.cluster.cluster_time
176
+ end
161
177
 
162
- context 'when talking to a mongos', if: sessions_enabled? && sharded? do
178
+ let(:end_sessions_command) do
179
+ pool.end_sessions
180
+ subscriber.started_events.find { |c| c.command_name == :endSessions}
181
+ end
163
182
 
164
183
  it 'sends the endSessions command with all the session ids' do
165
- expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_a.session_id))
166
- expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_b.session_id))
167
- expect(end_sessions_command.command[:$clusterTime]).to eq(client.cluster.cluster_time)
184
+ end_sessions_command
185
+ expect(end_sessions_command.command[:endSessions]).to include(BSON::Document.new(session_a.session_id))
186
+ expect(end_sessions_command.command[:endSessions]).to include(BSON::Document.new(session_b.session_id))
187
+ end
188
+
189
+ context 'when talking to a replica set or mongos' do
190
+
191
+ it 'sends the endSessions command with all the session ids and cluster time' do
192
+ end_sessions_command
193
+ expect(end_sessions_command.command[:endSessions]).to include(BSON::Document.new(session_a.session_id))
194
+ expect(end_sessions_command.command[:endSessions]).to include(BSON::Document.new(session_b.session_id))
195
+ expect(end_sessions_command.command[:$clusterTime]).to eq(client.cluster.cluster_time)
196
+ end
168
197
  end
169
198
  end
170
199
 
171
200
  context 'when the number of ids is larger than 10_000' do
172
201
 
202
+ let(:ids) do
203
+ 10_001.times.map do |i|
204
+ bytes = [SecureRandom.uuid.gsub(/\-/, '')].pack('H*')
205
+ BSON::Document.new(id: BSON::Binary.new(bytes, :uuid))
206
+ end
207
+ end
208
+
173
209
  before do
174
210
  queue = []
175
- 10_001.times do |i|
176
- queue << double('session', session_id: i)
211
+ ids.each do |id|
212
+ queue << double('session', session_id: id)
177
213
  end
178
214
  pool.instance_variable_set(:@queue, queue)
179
- expect(Mongo::Operation::Commands::Command).to receive(:new).at_least(:twice)
215
+ expect(Mongo::Operation::Commands::Command).to receive(:new).at_least(:twice).and_call_original
180
216
  end
181
217
 
182
218
  let(:end_sessions_commands) do
@@ -185,9 +221,9 @@ describe Mongo::Session::SessionPool do
185
221
 
186
222
  it 'sends the command more than once' do
187
223
  pool.end_sessions
188
- # expect(end_sessions_commands.size).to eq(2)
189
- # expect(end_sessions_commands[0].command[:ids]).to eq([*0...10_000])
190
- # expect(end_sessions_commands[1].command[:ids]).to eq([10_000])
224
+ expect(end_sessions_commands.size).to eq(2)
225
+ expect(end_sessions_commands[0].command[:endSessions]).to eq(ids[0...10_000])
226
+ expect(end_sessions_commands[1].command[:endSessions]).to eq([ids[10_000]])
191
227
  end
192
228
  end
193
229
  end
@@ -0,0 +1,247 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Session, if: test_sessions? do
4
+
5
+ let(:session) do
6
+ authorized_client.start_session(options)
7
+ end
8
+
9
+ let(:options) do
10
+ {}
11
+ end
12
+
13
+ describe '#initialize' do
14
+
15
+ context 'when options are provided' do
16
+
17
+ it 'duplicates and freezes the options' do
18
+ expect(session.options).not_to be(options)
19
+ expect(session.options.frozen?).to be(true)
20
+ end
21
+ end
22
+
23
+ it 'sets a server session with an id' do
24
+ expect(session.session_id).to be_a(BSON::Document)
25
+ end
26
+
27
+ it 'sets the cluster time to nil' do
28
+ expect(session.cluster_time).to be(nil)
29
+ end
30
+
31
+ it 'sets the client' do
32
+ expect(session.client).to be(authorized_client)
33
+ end
34
+ end
35
+
36
+ describe '#inspect' do
37
+
38
+ it 'includes the Ruby object_id in the formatted string' do
39
+ expect(session.inspect).to include(session.object_id.to_s)
40
+ end
41
+
42
+ it 'includes the session_id in the formatted string' do
43
+ expect(session.inspect).to include(session.session_id.to_s)
44
+ end
45
+
46
+ context 'when options are provided' do
47
+
48
+ let(:options) do
49
+ { causal_consistency: true }
50
+ end
51
+
52
+ it 'includes the options in the formatted string' do
53
+ expect(session.inspect).to include({ causal_consistency: true }.to_s)
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '#advance_cluster_time' do
59
+
60
+ let(:new_cluster_time) do
61
+ { 'clusterTime' => BSON::Timestamp.new(0, 5) }
62
+ end
63
+
64
+ context 'when the session does not have a cluster time' do
65
+
66
+ before do
67
+ session.advance_cluster_time(new_cluster_time)
68
+ end
69
+
70
+ it 'sets the new cluster time' do
71
+ expect(session.cluster_time).to eq(new_cluster_time)
72
+ end
73
+ end
74
+
75
+ context 'when the session already has a cluster time' do
76
+
77
+ context 'when the original cluster time is less than the new cluster time' do
78
+
79
+ let(:original_cluster_time) do
80
+ { 'clusterTime' => BSON::Timestamp.new(0, 1) }
81
+ end
82
+
83
+ before do
84
+ session.instance_variable_set(:@cluster_time, original_cluster_time)
85
+ session.advance_cluster_time(new_cluster_time)
86
+ end
87
+
88
+ it 'sets the new cluster time' do
89
+ expect(session.cluster_time).to eq(new_cluster_time)
90
+ end
91
+ end
92
+
93
+ context 'when the original cluster time is equal or greater than the new cluster time' do
94
+
95
+ let(:original_cluster_time) do
96
+ { 'clusterTime' => BSON::Timestamp.new(0, 6) }
97
+ end
98
+
99
+ before do
100
+ session.instance_variable_set(:@cluster_time, original_cluster_time)
101
+ session.advance_cluster_time(new_cluster_time)
102
+ end
103
+
104
+ it 'does not update the cluster time' do
105
+ expect(session.cluster_time).to eq(original_cluster_time)
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#advance_operation_time' do
112
+
113
+ let(:new_operation_time) do
114
+ BSON::Timestamp.new(0, 5)
115
+ end
116
+
117
+ context 'when the session does not have an operation time' do
118
+
119
+ before do
120
+ session.advance_operation_time(new_operation_time)
121
+ end
122
+
123
+ it 'sets the new operation time' do
124
+ expect(session.operation_time).to eq(new_operation_time)
125
+ end
126
+ end
127
+
128
+ context 'when the session already has an operation time' do
129
+
130
+ context 'when the original operation time is less than the new operation time' do
131
+
132
+ let(:original_operation_time) do
133
+ BSON::Timestamp.new(0, 1)
134
+ end
135
+
136
+ before do
137
+ session.instance_variable_set(:@operation_time, original_operation_time)
138
+ session.advance_operation_time(new_operation_time)
139
+ end
140
+
141
+ it 'sets the new operation time' do
142
+ expect(session.operation_time).to eq(new_operation_time)
143
+ end
144
+ end
145
+
146
+ context 'when the original operation time is equal or greater than the new operation time' do
147
+
148
+ let(:original_operation_time) do
149
+ BSON::Timestamp.new(0, 6)
150
+ end
151
+
152
+ before do
153
+ session.instance_variable_set(:@operation_time, original_operation_time)
154
+ session.advance_operation_time(new_operation_time)
155
+ end
156
+
157
+ it 'does not update the operation time' do
158
+ expect(session.operation_time).to eq(original_operation_time)
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ describe 'ended?' do
165
+
166
+ context 'when the session has not been ended' do
167
+
168
+ it 'returns false' do
169
+ expect(session.ended?).to be(false)
170
+ end
171
+ end
172
+
173
+ context 'when the session has been ended' do
174
+
175
+ before do
176
+ session.end_session
177
+ end
178
+
179
+ it 'returns true' do
180
+ expect(session.ended?).to be(true)
181
+ end
182
+ end
183
+ end
184
+
185
+ describe 'end_session' do
186
+
187
+ let!(:server_session) do
188
+ session.instance_variable_get(:@server_session)
189
+ end
190
+
191
+ let(:client_session_pool) do
192
+ session.client.instance_variable_get(:@session_pool)
193
+ end
194
+
195
+ it 'returns the server session to the client session pool' do
196
+ session.end_session
197
+ expect(client_session_pool.instance_variable_get(:@queue)).to include(server_session)
198
+ end
199
+
200
+ context 'when #end_session is called multiple times' do
201
+
202
+ before do
203
+ session.end_session
204
+ end
205
+
206
+ it 'returns nil' do
207
+ expect(session.end_session).to be_nil
208
+ end
209
+ end
210
+ end
211
+
212
+ describe '#retry_writes?', if: test_sessions? do
213
+
214
+ context 'when the option is set to true' do
215
+
216
+ let(:client) do
217
+ authorized_client.with(retry_writes: true)
218
+ end
219
+
220
+ it 'returns true' do
221
+ expect(client.start_session.retry_writes?).to be(true)
222
+ end
223
+ end
224
+
225
+ context 'when the option is set to false' do
226
+
227
+ let(:client) do
228
+ authorized_client.with(retry_writes: false)
229
+ end
230
+
231
+ it 'returns false' do
232
+ expect(client.start_session.retry_writes?).to be(false)
233
+ end
234
+ end
235
+
236
+ context 'when the option is not defined' do
237
+
238
+ let(:client) do
239
+ authorized_client
240
+ end
241
+
242
+ it 'returns false' do
243
+ expect(client.start_session.retry_writes?).to be(false)
244
+ end
245
+ end
246
+ end
247
+ end