mongo 2.5.0.beta → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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