mongo 2.8.0 → 2.9.0.rc0

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 (276) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +12 -0
  5. data/lib/mongo.rb +15 -1
  6. data/lib/mongo/address/ipv6.rb +0 -2
  7. data/lib/mongo/auth/scram/conversation.rb +0 -3
  8. data/lib/mongo/bulk_write/result_combiner.rb +12 -2
  9. data/lib/mongo/client.rb +59 -6
  10. data/lib/mongo/cluster.rb +19 -8
  11. data/lib/mongo/cluster/reapers/cursor_reaper.rb +0 -2
  12. data/lib/mongo/cluster/reapers/socket_reaper.rb +12 -9
  13. data/lib/mongo/collection.rb +1 -1
  14. data/lib/mongo/collection/view/aggregation.rb +5 -1
  15. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  16. data/lib/mongo/collection/view/change_stream.rb +30 -10
  17. data/lib/mongo/collection/view/iterable.rb +13 -6
  18. data/lib/mongo/collection/view/map_reduce.rb +12 -10
  19. data/lib/mongo/collection/view/readable.rb +19 -14
  20. data/lib/mongo/cursor.rb +12 -8
  21. data/lib/mongo/database.rb +10 -7
  22. data/lib/mongo/database/view.rb +18 -11
  23. data/lib/mongo/error.rb +2 -2
  24. data/lib/mongo/error/connection_check_out_timeout.rb +49 -0
  25. data/lib/mongo/error/operation_failure.rb +9 -9
  26. data/lib/mongo/error/parser.rb +25 -3
  27. data/lib/mongo/error/pool_closed_error.rb +43 -0
  28. data/lib/mongo/error/sdam_error_detection.rb +18 -0
  29. data/lib/mongo/grid/file/chunk.rb +0 -2
  30. data/lib/mongo/grid/fs_bucket.rb +26 -12
  31. data/lib/mongo/grid/stream/read.rb +36 -21
  32. data/lib/mongo/index/view.rb +11 -7
  33. data/lib/mongo/logger.rb +0 -2
  34. data/lib/mongo/monitoring.rb +31 -0
  35. data/lib/mongo/monitoring/cmap_log_subscriber.rb +53 -0
  36. data/lib/mongo/monitoring/event.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap.rb +25 -0
  38. data/lib/mongo/monitoring/event/cmap/base.rb +28 -0
  39. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +78 -0
  40. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +56 -0
  41. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +63 -0
  42. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +64 -0
  43. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +103 -0
  44. data/lib/mongo/monitoring/event/cmap/connection_created.rb +64 -0
  45. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +64 -0
  46. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +57 -0
  47. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +57 -0
  48. data/lib/mongo/monitoring/event/cmap/pool_created.rb +63 -0
  49. data/lib/mongo/monitoring/event/command_started.rb +12 -3
  50. data/lib/mongo/monitoring/publishable.rb +10 -2
  51. data/lib/mongo/operation.rb +0 -1
  52. data/lib/mongo/operation/find/legacy/result.rb +1 -0
  53. data/lib/mongo/operation/list_collections/result.rb +7 -1
  54. data/lib/mongo/operation/result.rb +10 -1
  55. data/lib/mongo/operation/shared/executable.rb +15 -0
  56. data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +29 -0
  57. data/lib/mongo/operation/shared/specifiable.rb +0 -16
  58. data/lib/mongo/operation/update/legacy/result.rb +1 -0
  59. data/lib/mongo/protocol/compressed.rb +0 -2
  60. data/lib/mongo/protocol/msg.rb +25 -2
  61. data/lib/mongo/retryable.rb +171 -33
  62. data/lib/mongo/server.rb +26 -7
  63. data/lib/mongo/server/app_metadata.rb +0 -2
  64. data/lib/mongo/server/connectable.rb +8 -2
  65. data/lib/mongo/server/connection.rb +83 -13
  66. data/lib/mongo/server/connection_base.rb +1 -1
  67. data/lib/mongo/server/connection_pool.rb +439 -43
  68. data/lib/mongo/server/monitor/connection.rb +4 -1
  69. data/lib/mongo/session.rb +37 -5
  70. data/lib/mongo/session/session_pool.rb +2 -2
  71. data/lib/mongo/socket.rb +0 -2
  72. data/lib/mongo/socket/ssl.rb +0 -2
  73. data/lib/mongo/uri.rb +127 -66
  74. data/lib/mongo/uri/srv_protocol.rb +35 -13
  75. data/lib/mongo/version.rb +1 -1
  76. data/spec/README.md +190 -63
  77. data/spec/integration/change_stream_spec.rb +64 -0
  78. data/spec/integration/command_spec.rb +0 -7
  79. data/spec/integration/error_detection_spec.rb +39 -0
  80. data/spec/integration/read_concern.rb +83 -0
  81. data/spec/integration/retryable_writes_spec.rb +6 -50
  82. data/spec/integration/sdam_error_handling_spec.rb +60 -7
  83. data/spec/integration/ssl_uri_options_spec.rb +24 -0
  84. data/spec/integration/step_down_spec.rb +197 -0
  85. data/spec/lite_spec_helper.rb +4 -0
  86. data/spec/mongo/client_construction_spec.rb +42 -17
  87. data/spec/mongo/client_spec.rb +32 -1
  88. data/spec/mongo/cluster/socket_reaper_spec.rb +2 -2
  89. data/spec/mongo/cluster_spec.rb +36 -2
  90. data/spec/mongo/collection/view/aggregation_spec.rb +2 -0
  91. data/spec/mongo/collection/view/change_stream_spec.rb +28 -28
  92. data/spec/mongo/collection/view/readable_spec.rb +1 -1
  93. data/spec/mongo/collection/view_spec.rb +3 -1
  94. data/spec/mongo/cursor_spec.rb +5 -5
  95. data/spec/mongo/error/parser_spec.rb +61 -1
  96. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  97. data/spec/mongo/monitoring/event/cmap/connection_check_out_failed_spec.rb +23 -0
  98. data/spec/mongo/monitoring/event/cmap/connection_check_out_started_spec.rb +19 -0
  99. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +23 -0
  100. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +23 -0
  101. data/spec/mongo/monitoring/event/cmap/connection_closed_spec.rb +27 -0
  102. data/spec/mongo/monitoring/event/cmap/connection_created_spec.rb +24 -0
  103. data/spec/mongo/monitoring/event/cmap/connection_ready_spec.rb +24 -0
  104. data/spec/mongo/monitoring/event/cmap/pool_cleared_spec.rb +19 -0
  105. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +19 -0
  106. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +26 -0
  107. data/spec/mongo/operation/delete/bulk_spec.rb +1 -6
  108. data/spec/mongo/operation/delete/command_spec.rb +1 -1
  109. data/spec/mongo/operation/delete/op_msg_spec.rb +1 -1
  110. data/spec/mongo/operation/delete_spec.rb +4 -4
  111. data/spec/mongo/operation/insert/bulk_spec.rb +1 -1
  112. data/spec/mongo/operation/insert/command_spec.rb +1 -1
  113. data/spec/mongo/operation/insert/op_msg_spec.rb +1 -1
  114. data/spec/mongo/operation/update/bulk_spec.rb +1 -1
  115. data/spec/mongo/operation/update/command_spec.rb +2 -2
  116. data/spec/mongo/operation/update/op_msg_spec.rb +2 -2
  117. data/spec/mongo/protocol/msg_spec.rb +11 -0
  118. data/spec/mongo/retryable_spec.rb +78 -25
  119. data/spec/mongo/server/connection_pool_spec.rb +661 -126
  120. data/spec/mongo/server/connection_spec.rb +55 -7
  121. data/spec/mongo/server_spec.rb +5 -0
  122. data/spec/mongo/uri/srv_protocol_spec.rb +135 -2
  123. data/spec/mongo/uri_option_parsing_spec.rb +511 -0
  124. data/spec/mongo/uri_spec.rb +42 -6
  125. data/spec/spec_helper.rb +1 -84
  126. data/spec/spec_tests/cmap_spec.rb +50 -0
  127. data/spec/spec_tests/command_monitoring_spec.rb +7 -18
  128. data/spec/spec_tests/crud_spec.rb +3 -49
  129. data/spec/spec_tests/data/cmap/connection-must-have-id.yml +21 -0
  130. data/spec/spec_tests/data/cmap/connection-must-order-ids.yml +21 -0
  131. data/spec/spec_tests/data/cmap/pool-checkin-destroy-closed.yml +24 -0
  132. data/spec/spec_tests/data/cmap/pool-checkin-destroy-stale.yml +24 -0
  133. data/spec/spec_tests/data/cmap/pool-checkin-make-available.yml +21 -0
  134. data/spec/spec_tests/data/cmap/pool-checkin.yml +18 -0
  135. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +13 -0
  136. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +28 -0
  137. data/spec/spec_tests/data/cmap/pool-checkout-multiple.yml +34 -0
  138. data/spec/spec_tests/data/cmap/pool-checkout-no-idle.yml +31 -0
  139. data/spec/spec_tests/data/cmap/pool-checkout-no-stale.yml +29 -0
  140. data/spec/spec_tests/data/cmap/pool-close-destroy-conns.yml +26 -0
  141. data/spec/spec_tests/data/cmap/pool-close.yml +11 -0
  142. data/spec/spec_tests/data/cmap/pool-create-max-size.yml +56 -0
  143. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +27 -0
  144. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +20 -0
  145. data/spec/spec_tests/data/cmap/pool-create.yml +12 -0
  146. data/spec/spec_tests/data/cmap/wait-queue-fairness.yml +94 -0
  147. data/spec/spec_tests/data/cmap/wait-queue-timeout.yml +41 -0
  148. data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +157 -0
  149. data/spec/spec_tests/data/retryable_reads/aggregate.yml +87 -0
  150. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +149 -0
  151. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +61 -0
  152. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +149 -0
  153. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +65 -0
  154. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +153 -0
  155. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +61 -0
  156. data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +150 -0
  157. data/spec/spec_tests/data/retryable_reads/count.yml +64 -0
  158. data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +150 -0
  159. data/spec/spec_tests/data/retryable_reads/countDocuments.yml +64 -0
  160. data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +156 -0
  161. data/spec/spec_tests/data/retryable_reads/distinct.yml +71 -0
  162. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors.yml +148 -0
  163. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount.yml +62 -0
  164. data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +160 -0
  165. data/spec/spec_tests/data/retryable_reads/find.yml +86 -0
  166. data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +154 -0
  167. data/spec/spec_tests/data/retryable_reads/findOne.yml +68 -0
  168. data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +173 -0
  169. data/spec/spec_tests/data/retryable_reads/gridfs-download.yml +79 -0
  170. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +174 -0
  171. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName.yml +79 -0
  172. data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +143 -0
  173. data/spec/spec_tests/data/retryable_reads/listCollectionNames.yml +59 -0
  174. data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +144 -0
  175. data/spec/spec_tests/data/retryable_reads/listCollectionObjects.yml +59 -0
  176. data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +143 -0
  177. data/spec/spec_tests/data/retryable_reads/listCollections.yml +59 -0
  178. data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +143 -0
  179. data/spec/spec_tests/data/retryable_reads/listDatabaseNames.yml +59 -0
  180. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +144 -0
  181. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects.yml +59 -0
  182. data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +144 -0
  183. data/spec/spec_tests/data/retryable_reads/listDatabases.yml +59 -0
  184. data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +144 -0
  185. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +60 -0
  186. data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +145 -0
  187. data/spec/spec_tests/data/retryable_reads/listIndexes.yml +60 -0
  188. data/spec/spec_tests/data/retryable_reads/mapReduce.yml +60 -0
  189. data/spec/spec_tests/data/retryable_writes/bulkWrite-serverErrors.yml +10 -7
  190. data/spec/spec_tests/data/retryable_writes/bulkWrite.yml +15 -22
  191. data/spec/spec_tests/data/retryable_writes/deleteMany.yml +22 -0
  192. data/spec/spec_tests/data/retryable_writes/deleteOne-serverErrors.yml +8 -7
  193. data/spec/spec_tests/data/retryable_writes/deleteOne.yml +5 -8
  194. data/spec/spec_tests/data/retryable_writes/findOneAndDelete-serverErrors.yml +8 -7
  195. data/spec/spec_tests/data/retryable_writes/findOneAndDelete.yml +5 -8
  196. data/spec/spec_tests/data/retryable_writes/findOneAndReplace-serverErrors.yml +8 -7
  197. data/spec/spec_tests/data/retryable_writes/findOneAndReplace.yml +5 -8
  198. data/spec/spec_tests/data/retryable_writes/findOneAndUpdate-serverErrors.yml +8 -7
  199. data/spec/spec_tests/data/retryable_writes/findOneAndUpdate.yml +5 -8
  200. data/spec/spec_tests/data/retryable_writes/insertMany-serverErrors.yml +8 -7
  201. data/spec/spec_tests/data/retryable_writes/insertMany.yml +5 -8
  202. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +10 -45
  203. data/spec/spec_tests/data/retryable_writes/insertOne.yml +5 -8
  204. data/spec/spec_tests/data/retryable_writes/replaceOne-serverErrors.yml +8 -7
  205. data/spec/spec_tests/data/retryable_writes/replaceOne.yml +5 -8
  206. data/spec/spec_tests/data/retryable_writes/updateMany.yml +27 -0
  207. data/spec/spec_tests/data/retryable_writes/updateOne-serverErrors.yml +8 -7
  208. data/spec/spec_tests/data/retryable_writes/updateOne.yml +5 -14
  209. data/spec/spec_tests/data/transactions/abort.yml +7 -2
  210. data/spec/spec_tests/data/transactions/bulk.yml +7 -2
  211. data/spec/spec_tests/data/transactions/causal-consistency.yml +11 -4
  212. data/spec/spec_tests/data/transactions/commit.yml +11 -4
  213. data/spec/spec_tests/data/transactions/count.yml +64 -0
  214. data/spec/spec_tests/data/transactions/delete.yml +7 -2
  215. data/spec/spec_tests/data/transactions/error-labels.yml +8 -2
  216. data/spec/spec_tests/data/transactions/errors.yml +7 -2
  217. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +7 -2
  218. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +7 -2
  219. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +7 -2
  220. data/spec/spec_tests/data/transactions/insert.yml +9 -2
  221. data/spec/spec_tests/data/transactions/isolation.yml +7 -2
  222. data/spec/spec_tests/data/transactions/read-concern.yml +15 -6
  223. data/spec/spec_tests/data/transactions/read-pref.yml +7 -2
  224. data/spec/spec_tests/data/transactions/reads.yml +8 -48
  225. data/spec/spec_tests/data/transactions/retryable-abort.yml +7 -2
  226. data/spec/spec_tests/data/transactions/retryable-commit.yml +7 -2
  227. data/spec/spec_tests/data/transactions/retryable-writes.yml +7 -2
  228. data/spec/spec_tests/data/transactions/run-command.yml +7 -2
  229. data/spec/spec_tests/data/transactions/transaction-options.yml +7 -2
  230. data/spec/spec_tests/data/transactions/update.yml +7 -2
  231. data/spec/spec_tests/data/transactions/write-concern.yml +7 -2
  232. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -1
  233. data/spec/spec_tests/data/transactions_api/callback-commits.yml +6 -1
  234. data/spec/spec_tests/data/transactions_api/callback-retry.yml +6 -1
  235. data/spec/spec_tests/data/transactions_api/commit-retry.yml +6 -1
  236. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +6 -3
  237. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +6 -1
  238. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +6 -1
  239. data/spec/spec_tests/data/transactions_api/commit.yml +6 -1
  240. data/spec/spec_tests/data/transactions_api/transaction-options.yml +6 -1
  241. data/spec/spec_tests/retryable_reads_spec.rb +11 -0
  242. data/spec/spec_tests/retryable_writes_spec.rb +4 -69
  243. data/spec/spec_tests/transactions_api_spec.rb +42 -37
  244. data/spec/spec_tests/transactions_spec.rb +42 -33
  245. data/spec/support/authorization.rb +12 -0
  246. data/spec/support/change_streams/operation.rb +1 -1
  247. data/spec/support/client_registry.rb +20 -0
  248. data/spec/support/cluster_config.rb +16 -15
  249. data/spec/support/cluster_tools.rb +346 -0
  250. data/spec/support/cmap.rb +367 -0
  251. data/spec/support/cmap/verifier.rb +46 -0
  252. data/spec/support/command_monitoring.rb +4 -6
  253. data/spec/support/common_shortcuts.rb +6 -0
  254. data/spec/support/connection_string.rb +2 -2
  255. data/spec/support/crud.rb +171 -184
  256. data/spec/support/crud/operation.rb +43 -0
  257. data/spec/support/crud/outcome.rb +53 -0
  258. data/spec/support/crud/read.rb +102 -12
  259. data/spec/support/crud/requirement.rb +69 -0
  260. data/spec/support/crud/spec.rb +68 -0
  261. data/spec/support/crud/test.rb +141 -0
  262. data/spec/support/crud/verifier.rb +96 -18
  263. data/spec/support/crud/write.rb +18 -3
  264. data/spec/support/event_subscriber.rb +15 -0
  265. data/spec/support/primary_socket.rb +2 -2
  266. data/spec/support/spec_config.rb +89 -20
  267. data/spec/support/transactions.rb +2 -306
  268. data/spec/support/transactions/operation.rb +7 -7
  269. data/spec/support/transactions/spec.rb +28 -0
  270. data/spec/support/transactions/test.rb +191 -0
  271. data/spec/support/utils.rb +123 -0
  272. metadata +202 -9
  273. metadata.gz.sig +0 -0
  274. data/lib/mongo/server/connection_pool/queue.rb +0 -359
  275. data/spec/mongo/server/connection_pool/queue_spec.rb +0 -353
  276. data/spec/support/transactions/verifier.rb +0 -97
@@ -559,14 +559,34 @@ describe Mongo::Server::Connection, retry: 3 do
559
559
  /Got response for request ID \d+ but expected response for request ID \d+/)
560
560
  end
561
561
 
562
- it 'does not affect subsequent requests' do
563
- expect {
564
- connection.dispatch([ query_alice ])
565
- }.to raise_error(Mongo::Error::UnexpectedResponse)
562
+ context 'linting' do
563
+ require_linting
564
+
565
+ it 'marks the connection no longer usable' do
566
+ expect {
567
+ connection.dispatch([ query_alice ])
568
+ }.to raise_error(Mongo::Error::UnexpectedResponse)
566
569
 
567
- docs = connection.dispatch([ query_alice ]).documents
568
- expect(docs).to_not be_empty
569
- expect(docs.first['name']).to eq('alice')
570
+ expect do
571
+ connection.dispatch([ query_alice ]).documents
572
+ end.to raise_error(Mongo::Error::LintError, 'Reconnecting closed connections is no longer supported')
573
+ end
574
+ end
575
+
576
+ context 'not linting' do
577
+ skip_if_linting
578
+
579
+ it 'does not affect subsequent requests but warns' do
580
+ expect(Mongo::Logger.logger).to receive(:warn).once.and_call_original
581
+
582
+ expect {
583
+ connection.dispatch([ query_alice ])
584
+ }.to raise_error(Mongo::Error::UnexpectedResponse)
585
+
586
+ docs = connection.dispatch([ query_alice ]).documents
587
+ expect(docs).to_not be_empty
588
+ expect(docs.first['name']).to eq('alice')
589
+ end
570
590
  end
571
591
  end
572
592
 
@@ -831,6 +851,34 @@ describe Mongo::Server::Connection, retry: 3 do
831
851
  expect(connection.address).to eq(server.address)
832
852
  end
833
853
 
854
+ it 'sets id' do
855
+ expect(connection.id).to eq(1)
856
+ end
857
+
858
+ context 'multiple connections' do
859
+ it 'use incrementing ids' do
860
+ expect(connection.id).to eq(1)
861
+
862
+ second_connection = described_class.new(server, server.options)
863
+ expect(second_connection.id).to eq(2)
864
+ end
865
+ end
866
+
867
+ context 'two pools for different servers' do
868
+ let(:server2) do
869
+ Mongo::Server.new(address, cluster, monitoring, listeners, server_options)
870
+ end
871
+
872
+ it 'ids do not share namespace' do
873
+ server.pool.with_connection do |conn|
874
+ expect(conn.id).to eq(1)
875
+ end
876
+ server2.pool.with_connection do |conn|
877
+ expect(conn.id).to eq(1)
878
+ end
879
+ end
880
+ end
881
+
834
882
  it 'sets the socket to nil' do
835
883
  expect(connection.send(:socket)).to be_nil
836
884
  end
@@ -91,6 +91,11 @@ describe Mongo::Server do
91
91
  expect(server).to receive(:pool).and_return(pool)
92
92
  server.disconnect!
93
93
  end
94
+
95
+ it 'disconnects the connection pool' do
96
+ expect(server.pool).to receive(:disconnect!).once.and_call_original
97
+ server.disconnect!
98
+ end
94
99
  end
95
100
 
96
101
  describe '#initialize' do
@@ -48,6 +48,17 @@ describe Mongo::URI::SRVProtocol do
48
48
  end
49
49
  end
50
50
 
51
+ context 'when the {tld} is empty' do
52
+
53
+ let(:string) { "#{scheme}#{hosts}" }
54
+ let(:hosts) { '10gen.cc./' }
55
+
56
+ it 'raises an error' do
57
+ expect { uri }.to raise_error(Mongo::Error::InvalidURI)
58
+ end
59
+ end
60
+
61
+
51
62
  context 'string is not uri' do
52
63
 
53
64
  let(:string) { 'tyler' }
@@ -403,7 +414,7 @@ describe Mongo::URI::SRVProtocol do
403
414
  context 'wtimeoutMS' do
404
415
  let(:timeout) { 1234 }
405
416
  let(:options) { "w=2&wtimeoutMS=#{timeout}" }
406
- let(:concern) { Mongo::Options::Redacted.new(:w => 2, :timeout => timeout) }
417
+ let(:concern) { Mongo::Options::Redacted.new(:w => 2, :wtimeout => timeout) }
407
418
 
408
419
  it 'sets the write concern options' do
409
420
  expect(uri.uri_options[:write]).to eq(concern)
@@ -911,7 +922,7 @@ describe Mongo::URI::SRVProtocol do
911
922
  let(:options) { "appname=srv_test" }
912
923
 
913
924
  it 'sets the app name on the client' do
914
- expect(client.options[:app_name]).to eq(:srv_test)
925
+ expect(client.options[:app_name]).to eq('srv_test')
915
926
  end
916
927
  end
917
928
 
@@ -941,4 +952,126 @@ describe Mongo::URI::SRVProtocol do
941
952
  end
942
953
  end
943
954
  end
955
+
956
+ describe '#validate_hostname' do
957
+ let(:valid_hostname) do
958
+ end
959
+
960
+ let(:dummy_uri) do
961
+ Mongo::URI::SRVProtocol.new("mongodb+srv://test1.test.build.10gen.cc/")
962
+ end
963
+
964
+ let(:validate) do
965
+ dummy_uri.send(:validate_hostname, hostname)
966
+ end
967
+
968
+ context 'when the hostname is valid' do
969
+ let(:hostname) do
970
+ 'a.b.c'
971
+ end
972
+
973
+ it 'does not raise an error' do
974
+ expect { validate }.not_to raise_error
975
+ end
976
+ end
977
+
978
+ context 'when the hostname has a trailing dot' do
979
+ let(:hostname) do
980
+ "a.b.c."
981
+ end
982
+
983
+ it 'raises an error' do
984
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI, /Hostname cannot end with a dot: a\.b\.c\./)
985
+ end
986
+ end
987
+
988
+ context 'when the hostname is empty' do
989
+ let(:hostname) do
990
+ ''
991
+ end
992
+
993
+ it 'raises an error' do
994
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
995
+ end
996
+ end
997
+
998
+ context 'when the hostname has only one part' do
999
+ let(:hostname) do
1000
+ 'a'
1001
+ end
1002
+
1003
+ it 'raises an error' do
1004
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1005
+ end
1006
+ end
1007
+
1008
+
1009
+ context 'when the hostname has only two parts' do
1010
+ let(:hostname) do
1011
+ 'a.b'
1012
+ end
1013
+
1014
+ it 'raises an error' do
1015
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1016
+ end
1017
+ end
1018
+
1019
+ context 'when the hostname has an empty last part' do
1020
+ let(:hostname) do
1021
+ 'a.b.'
1022
+ end
1023
+
1024
+ it 'it raises an error' do
1025
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1026
+ end
1027
+ end
1028
+
1029
+ context 'when multiple hostnames are specified' do
1030
+ it 'raises an error' do
1031
+ expect do
1032
+ Mongo::URI::SRVProtocol.new("mongodb+srv://a.b.c,d.e.f/")
1033
+ end.to raise_error(Mongo::Error::InvalidURI, /One and only one host is required/)
1034
+ end
1035
+ end
1036
+
1037
+ context 'when the hostname contains a colon' do
1038
+ let(:hostname) do
1039
+ 'a.b.c:27017'
1040
+ end
1041
+
1042
+ it 'raises an error' do
1043
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1044
+ end
1045
+ end
1046
+
1047
+ context 'when the hostname starts with a dot' do
1048
+ let(:hostname) do
1049
+ '.a.b.c'
1050
+ end
1051
+
1052
+ it 'raises an error' do
1053
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1054
+ end
1055
+ end
1056
+
1057
+ context 'when the hostname ends with consecutive dots' do
1058
+ let(:hostname) do
1059
+ 'a.b.c..'
1060
+ end
1061
+
1062
+ it 'raises an error' do
1063
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1064
+ end
1065
+ end
1066
+
1067
+ context 'when the hostname contains consecutive dots in the middle' do
1068
+ let(:hostname) do
1069
+ 'a..b.c'
1070
+ end
1071
+
1072
+ it 'raises an error' do
1073
+ expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1074
+ end
1075
+ end
1076
+ end
944
1077
  end
@@ -0,0 +1,511 @@
1
+ require 'lite_spec_helper'
2
+
3
+ describe Mongo::URI do
4
+
5
+ let(:uri) { described_class.new(string) }
6
+
7
+ shared_examples_for 'parses successfully' do
8
+ it 'returns a Mongo::URI object' do
9
+ expect(uri).to be_a(Mongo::URI)
10
+ end
11
+ end
12
+
13
+ shared_examples_for 'raises parse error' do
14
+ it 'raises InvalidURI' do
15
+ expect do
16
+ uri
17
+ end.to raise_error(Mongo::Error::InvalidURI)
18
+ end
19
+ end
20
+
21
+ shared_examples_for 'a millisecond option' do
22
+
23
+ let(:string) { "mongodb://example.com/?#{uri_option}=123" }
24
+
25
+ it_behaves_like 'parses successfully'
26
+
27
+ it 'is a float' do
28
+ expect(uri.uri_options[ruby_option]).to eq(0.123)
29
+ end
30
+
31
+ context 'a multiple of 1 second' do
32
+ let(:string) { "mongodb://example.com/?#{uri_option}=123000" }
33
+
34
+ it_behaves_like 'parses successfully'
35
+
36
+ it 'is a float' do
37
+ expect(uri.uri_options[ruby_option]).to be_a(Float)
38
+ expect(uri.uri_options[ruby_option]).to eq(123)
39
+ end
40
+ end
41
+ end
42
+
43
+ shared_examples_for 'an integer option' do
44
+
45
+ let(:string) { "mongodb://example.com/?#{uri_option}=123" }
46
+
47
+ it_behaves_like 'parses successfully'
48
+
49
+ it 'is an integer' do
50
+ expect(uri.uri_options[ruby_option]).to eq(123)
51
+ end
52
+
53
+ context 'URL encoded value' do
54
+ let(:string) { "mongodb://example.com/?#{uri_option}=%31%32%33" }
55
+
56
+ it_behaves_like 'parses successfully'
57
+
58
+ it 'is an integer' do
59
+ expect(uri.uri_options[ruby_option]).to eq(123)
60
+ end
61
+ end
62
+ end
63
+
64
+ shared_examples_for 'a boolean option' do
65
+
66
+ context 'is true' do
67
+
68
+ let(:string) { "mongodb://example.com/?#{uri_option}=true" }
69
+
70
+ it_behaves_like 'parses successfully'
71
+
72
+ it 'is a boolean' do
73
+ expect(uri.uri_options[ruby_option]).to be true
74
+ end
75
+ end
76
+
77
+ context 'is TRUE' do
78
+
79
+ let(:string) { "mongodb://example.com/?#{uri_option}=TRUE" }
80
+
81
+ it_behaves_like 'parses successfully'
82
+
83
+ it 'is a boolean' do
84
+ expect(uri.uri_options[ruby_option]).to be true
85
+ end
86
+ end
87
+
88
+ context 'is false' do
89
+
90
+ let(:string) { "mongodb://example.com/?#{uri_option}=false" }
91
+
92
+ it_behaves_like 'parses successfully'
93
+
94
+ it 'is a boolean' do
95
+ expect(uri.uri_options[ruby_option]).to be false
96
+ end
97
+ end
98
+
99
+ context 'is FALSE' do
100
+
101
+ let(:string) { "mongodb://example.com/?#{uri_option}=FALSE" }
102
+
103
+ it_behaves_like 'parses successfully'
104
+
105
+ it 'is a boolean' do
106
+ expect(uri.uri_options[ruby_option]).to be false
107
+ end
108
+ end
109
+ end
110
+
111
+ shared_examples_for 'an inverted boolean option' do
112
+
113
+ let(:string) { "mongodb://example.com/?#{uri_option}=true" }
114
+
115
+ it_behaves_like 'parses successfully'
116
+
117
+ it 'is a boolean' do
118
+ expect(uri.uri_options[ruby_option]).to be false
119
+ end
120
+ end
121
+
122
+ shared_examples_for 'a string option' do
123
+
124
+ let(:string) { "mongodb://example.com/?#{uri_option}=foo" }
125
+
126
+ it_behaves_like 'parses successfully'
127
+
128
+ it 'is a string' do
129
+ expect(uri.uri_options[ruby_option]).to eq('foo')
130
+ end
131
+
132
+ context 'value is a number' do
133
+ let(:string) { "mongodb://example.com/?#{uri_option}=1" }
134
+
135
+ it_behaves_like 'parses successfully'
136
+
137
+ it 'is a string' do
138
+ expect(uri.uri_options[ruby_option]).to eq('1')
139
+ end
140
+ end
141
+ end
142
+
143
+ context 'appName' do
144
+
145
+ let(:uri_option) { 'appName' }
146
+ let(:ruby_option) { :app_name }
147
+
148
+ it_behaves_like 'a string option'
149
+ end
150
+
151
+ context 'authMechanism' do
152
+
153
+ let(:string) { 'mongodb://example.com/?authMechanism=SCRAM-SHA-256' }
154
+
155
+ it_behaves_like 'parses successfully'
156
+
157
+ it 'is a symbol' do
158
+ expect(uri.uri_options[:auth_mech]).to eq(:scram256)
159
+ end
160
+
161
+ context 'lowercase value' do
162
+
163
+ let(:string) { 'mongodb://example.com/?authMechanism=scram-sha-256' }
164
+
165
+ it_behaves_like 'parses successfully'
166
+
167
+ it 'is mapped to auth mechanism' do
168
+ expect(uri.uri_options[:auth_mech]).to eq(:scram256)
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'authMechanismProperties' do
174
+
175
+ let(:string) { 'mongodb://example.com/?authmechanismproperties=SERVICE_REALM:foo,CANONICALIZE_HOST_NAME:TRUE' }
176
+
177
+ it_behaves_like 'parses successfully'
178
+
179
+ it 'parses correctly' do
180
+ expect(uri.uri_options[:auth_mech_properties]).to eq(BSON::Document.new(
181
+ service_realm: 'foo',
182
+ canonicalize_host_name: true,
183
+ ))
184
+ end
185
+ end
186
+
187
+ context 'authSource' do
188
+
189
+ let(:uri_option) { 'authSource' }
190
+ let(:ruby_option) { :auth_source }
191
+
192
+ it_behaves_like 'a string option'
193
+ end
194
+
195
+ context 'compressors' do
196
+
197
+ let(:string) { 'mongodb://example.com/?compressors=snappy,zlib' }
198
+
199
+ it_behaves_like 'parses successfully'
200
+
201
+ it 'is an array of strings string' do
202
+ expect(uri.uri_options[:compressors]).to eq(['snappy', 'zlib'])
203
+ end
204
+ end
205
+
206
+ context 'connect' do
207
+
208
+ let(:string) { 'mongodb://example.com/?connect=sharded' }
209
+
210
+ it_behaves_like 'parses successfully'
211
+
212
+ it 'is a symbol' do
213
+ expect(uri.uri_options[:connect]).to eq(:sharded)
214
+ end
215
+
216
+ context 'invalid value' do
217
+ let(:string) { 'mongodb://example.com/?connect=bogus' }
218
+
219
+ # should raise an error
220
+ it_behaves_like 'parses successfully'
221
+ end
222
+ end
223
+
224
+ context 'connectTimeoutMS' do
225
+
226
+ let(:uri_option) { 'connectTimeoutMS' }
227
+ let(:ruby_option) { :connect_timeout }
228
+
229
+ it_behaves_like 'a millisecond option'
230
+ end
231
+
232
+ context 'fsync' do
233
+
234
+ let(:string) { 'mongodb://example.com/?fsync=true' }
235
+
236
+ it_behaves_like 'parses successfully'
237
+
238
+ it 'is a boolean' do
239
+ expect(uri.uri_options[:write]).to eq(BSON::Document.new(fsync: true))
240
+ end
241
+ end
242
+
243
+ context 'heartbeatFrequencyMS' do
244
+
245
+ let(:uri_option) { 'heartbeatFrequencyMS' }
246
+ let(:ruby_option) { :heartbeat_frequency }
247
+
248
+ it_behaves_like 'a millisecond option'
249
+ end
250
+
251
+ context 'journal' do
252
+
253
+ let(:string) { 'mongodb://example.com/?journal=true' }
254
+
255
+ it_behaves_like 'parses successfully'
256
+
257
+ it 'is a boolean' do
258
+ expect(uri.uri_options[:write]).to eq(BSON::Document.new(j: true))
259
+ end
260
+ end
261
+
262
+ context 'localThresholdMS' do
263
+
264
+ let(:uri_option) { 'localThresholdMS' }
265
+ let(:ruby_option) { :local_threshold }
266
+
267
+ it_behaves_like 'a millisecond option'
268
+ end
269
+
270
+ context 'maxIdleTimeMS' do
271
+
272
+ let(:uri_option) { 'maxIdleTimeMS' }
273
+ let(:ruby_option) { :max_idle_time }
274
+
275
+ it_behaves_like 'a millisecond option'
276
+ end
277
+
278
+ context 'maxStalenessSeconds' do
279
+
280
+ let(:string) { "mongodb://example.com/?maxStalenessSeconds=123" }
281
+
282
+ it_behaves_like 'parses successfully'
283
+
284
+ it 'is an integer' do
285
+ expect(uri.uri_options[:read][:max_staleness]).to be_a(Integer)
286
+ expect(uri.uri_options[:read][:max_staleness]).to eq(123)
287
+ end
288
+
289
+ context '-1 as value' do
290
+ let(:string) { "mongodb://example.com/?maxStalenessSeconds=-1" }
291
+
292
+ it_behaves_like 'parses successfully'
293
+
294
+ it 'is converted to nil' do
295
+ expect(uri.uri_options[:read]).to eq(BSON::Document.new(max_staleness: nil))
296
+ end
297
+ end
298
+ end
299
+
300
+ context 'maxPoolSize' do
301
+
302
+ let(:uri_option) { 'maxPoolSize' }
303
+ let(:ruby_option) { :max_pool_size }
304
+
305
+ it_behaves_like 'an integer option'
306
+ end
307
+
308
+ context 'minPoolSize' do
309
+
310
+ let(:uri_option) { 'minPoolSize' }
311
+ let(:ruby_option) { :min_pool_size }
312
+
313
+ it_behaves_like 'an integer option'
314
+ end
315
+
316
+ context 'readConcernLevel' do
317
+
318
+ let(:string) { 'mongodb://example.com/?readConcernLevel=snapshot' }
319
+
320
+ it_behaves_like 'parses successfully'
321
+
322
+ it 'is a string' do
323
+ expect(uri.uri_options[:read_concern]).to eq(BSON::Document.new(level: 'snapshot'))
324
+ end
325
+ end
326
+
327
+ context 'readPreference' do
328
+
329
+ let(:string) { "mongodb://example.com/?readPreference=nearest" }
330
+
331
+ it_behaves_like 'parses successfully'
332
+
333
+ it 'is a string' do
334
+ expect(uri.uri_options[:read]).to eq(BSON::Document.new(mode: :nearest))
335
+ end
336
+ end
337
+
338
+ context 'readPreferenceTags' do
339
+
340
+ let(:string) { "mongodb://example.com/?readPreferenceTags=dc:ny,rack:1" }
341
+
342
+ it_behaves_like 'parses successfully'
343
+
344
+ it 'parses correctly' do
345
+ expect(uri.uri_options[:read]).to eq(BSON::Document.new(
346
+ tag_sets: [{'dc' => 'ny', 'rack' => '1'}]))
347
+ end
348
+ end
349
+
350
+ context 'replicaSet' do
351
+
352
+ let(:uri_option) { 'replicaSet' }
353
+ let(:ruby_option) { :replica_set }
354
+
355
+ it_behaves_like 'a string option'
356
+ end
357
+
358
+ context 'retryWrites' do
359
+
360
+ let(:uri_option) { 'retryWrites' }
361
+ let(:ruby_option) { :retry_writes }
362
+
363
+ it_behaves_like 'a boolean option'
364
+ end
365
+
366
+ context 'serverSelectionTimeoutMS' do
367
+
368
+ let(:uri_option) { 'serverSelectionTimeoutMS' }
369
+ let(:ruby_option) { :server_selection_timeout }
370
+
371
+ it_behaves_like 'a millisecond option'
372
+ end
373
+
374
+ context 'socketTimeoutMS' do
375
+
376
+ let(:uri_option) { 'socketTimeoutMS' }
377
+ let(:ruby_option) { :socket_timeout }
378
+
379
+ it_behaves_like 'a millisecond option'
380
+ end
381
+
382
+ context 'ssl' do
383
+
384
+ let(:uri_option) { 'ssl' }
385
+ let(:ruby_option) { :ssl }
386
+
387
+ it_behaves_like 'a boolean option'
388
+ end
389
+
390
+ context 'tls' do
391
+
392
+ let(:uri_option) { 'tls' }
393
+ let(:ruby_option) { :ssl }
394
+
395
+ it_behaves_like 'a boolean option'
396
+ end
397
+
398
+ context 'tlsAllowInvalidCertificates' do
399
+
400
+ let(:uri_option) { 'tlsAllowInvalidCertificates' }
401
+ let(:ruby_option) { :ssl_verify_certificate }
402
+
403
+ it_behaves_like 'an inverted boolean option'
404
+ end
405
+
406
+ context 'tlsAllowInvalidHostnames' do
407
+
408
+ let(:uri_option) { 'tlsAllowInvalidHostnames' }
409
+ let(:ruby_option) { :ssl_verify_hostname }
410
+
411
+ it_behaves_like 'an inverted boolean option'
412
+ end
413
+
414
+ context 'tlsCAFile' do
415
+
416
+ let(:uri_option) { 'tlsCAFile' }
417
+ let(:ruby_option) { :ssl_ca_cert }
418
+
419
+ it_behaves_like 'a string option'
420
+ end
421
+
422
+ context 'tlsCertificateKeyFile' do
423
+
424
+ let(:uri_option) { 'tlsCertificateKeyFile' }
425
+ let(:ruby_option) { :ssl_cert }
426
+
427
+ it_behaves_like 'a string option'
428
+ end
429
+
430
+ context 'tlsCertificateKeyFilePassword' do
431
+
432
+ let(:uri_option) { 'tlsCertificateKeyFilePassword' }
433
+ let(:ruby_option) { :ssl_key_pass_phrase }
434
+
435
+ it_behaves_like 'a string option'
436
+ end
437
+
438
+ context 'tlsInsecure' do
439
+
440
+ let(:uri_option) { 'tlsInsecure' }
441
+ let(:ruby_option) { :ssl_verify }
442
+
443
+ it_behaves_like 'an inverted boolean option'
444
+ end
445
+
446
+ context 'w' do
447
+
448
+ context 'integer value' do
449
+ let(:string) { "mongodb://example.com/?w=1" }
450
+
451
+ it_behaves_like 'parses successfully'
452
+
453
+ it 'is an integer' do
454
+ expect(uri.uri_options[:write]).to eq(BSON::Document.new(w: 1))
455
+ end
456
+ end
457
+
458
+ context 'string value' do
459
+ let(:string) { "mongodb://example.com/?w=foo" }
460
+
461
+ it_behaves_like 'parses successfully'
462
+
463
+ it 'is a string' do
464
+ expect(uri.uri_options[:write]).to eq(BSON::Document.new(w: 'foo'))
465
+ end
466
+ end
467
+
468
+ context 'majority' do
469
+ let(:string) { "mongodb://example.com/?w=majority" }
470
+
471
+ it_behaves_like 'parses successfully'
472
+
473
+ it 'is a symbol' do
474
+ expect(uri.uri_options[:write]).to eq(BSON::Document.new(w: :majority))
475
+ end
476
+ end
477
+ end
478
+
479
+ context 'waitQueueTimeoutMS' do
480
+
481
+ let(:uri_option) { 'waitQueueTimeoutMS' }
482
+ let(:ruby_option) { :wait_queue_timeout }
483
+
484
+ it_behaves_like 'a millisecond option'
485
+ end
486
+
487
+ context 'wtimeoutMS' do
488
+
489
+ let(:string) { "mongodb://example.com/?wtimeoutMS=100" }
490
+
491
+ it_behaves_like 'parses successfully'
492
+
493
+ it 'is a float' do
494
+ expect(uri.uri_options[:write]).to eq(BSON::Document.new(wtimeout: 100))
495
+ end
496
+ end
497
+
498
+ context 'zlibCompressionLevel' do
499
+
500
+ let(:uri_option) { 'zlibCompressionLevel' }
501
+ let(:ruby_option) { :zlib_compression_level }
502
+
503
+ let(:string) { "mongodb://example.com/?#{uri_option}=7" }
504
+
505
+ it_behaves_like 'parses successfully'
506
+
507
+ it 'is an integer' do
508
+ expect(uri.uri_options[ruby_option]).to eq(7)
509
+ end
510
+ end
511
+ end