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
@@ -27,8 +27,7 @@ module Mongo
27
27
  #
28
28
  # Uses RSpec matchers and raises expectation failures if there is a
29
29
  # mismatch.
30
- def verify_collection_data(actual_collection_data)
31
- expected_collection_data = test_instance.outcome_collection_data
30
+ def verify_collection_data(expected_collection_data, actual_collection_data)
32
31
  if expected_collection_data.nil?
33
32
  expect(actual_collection_data).to be nil
34
33
  elsif expected_collection_data.empty?
@@ -48,14 +47,18 @@ module Mongo
48
47
  #
49
48
  # Uses RSpec matchers and raises expectation failures if there is a
50
49
  # mismatch.
51
- def verify_operation_result(actual)
52
- expected = test_instance.outcome['result']
50
+ def verify_operation_result(expected, actual)
53
51
  if expected.is_a?(Array)
54
52
  if expected.empty?
55
53
  expect(actual).to be_empty
56
54
  else
57
55
  expected.each_with_index do |expected_elt, i|
58
- verify_result(expected_elt, actual[i])
56
+ # If the YAML spec test does not define a result,
57
+ # do not assert the operation's result - the operation may
58
+ # have produced a result, the test just does not care what it is
59
+ if expected_elt
60
+ verify_result(expected_elt, actual[i])
61
+ end
59
62
  end
60
63
  end
61
64
  else
@@ -63,6 +66,50 @@ module Mongo
63
66
  end
64
67
  end
65
68
 
69
+ def verify_command_started_event_count(expected_events, actual_events)
70
+ expect(actual_events.length).to eq(expected_events.length)
71
+ end
72
+
73
+ def verify_command_started_event(expected_events, actual_events, i)
74
+ expect(expected_events.length).to be > i
75
+ expect(actual_events.length).to be > i
76
+
77
+ expectation = expected_events[i]
78
+ actual_event = actual_events[i]['command_started_event'].dup
79
+
80
+ expect(expectation.keys).to eq(%w(command_started_event))
81
+ expected_event = expectation['command_started_event'].dup
82
+ # Retryable reads tests' YAML assertions omit some of the keys
83
+ # that are included in the actual command events.
84
+ # Transactions and transactions API tests specify all keys
85
+ # in YAML that are present in actual command events.
86
+ actual_event.keys.each do |key|
87
+ unless expected_event.key?(key)
88
+ actual_event.delete(key)
89
+ end
90
+ end
91
+ expect(actual_event).not_to be nil
92
+ expect(actual_event.keys).to eq(expected_event.keys)
93
+
94
+ expected_command = expected_event.delete('command')
95
+ actual_command = actual_event.delete('command')
96
+
97
+ # Hash#compact is ruby 2.4+
98
+ expected_presence = expected_command.select { |k, v| !v.nil? }
99
+ expected_absence = expected_command.select { |k, v| v.nil? }
100
+
101
+ expected_presence.each do |k, v|
102
+ expect(k => actual_command[k]).to eq(k => v)
103
+ end
104
+
105
+ expected_absence.each do |k, v|
106
+ expect(actual_command).not_to have_key(k)
107
+ end
108
+
109
+ # this compares remaining fields in events after command is removed
110
+ expect(actual_event).to eq(expected_event)
111
+ end
112
+
66
113
  private
67
114
 
68
115
  def verify_result(expected, actual)
@@ -70,28 +117,59 @@ module Mongo
70
117
  when nil
71
118
  expect(actual).to be nil
72
119
  when Hash
73
- actual.each do |k, v|
74
- ok = expected[k] == v || handle_upserted_id(k, expected[k], v) || handle_inserted_ids(k, expected[k], v)
75
- expect(ok).to be true
120
+ expected.each do |k, v|
121
+ case k
122
+ when 'errorContains'
123
+ expect(actual['errorContains']).to include(v)
124
+ when 'errorLabelsContain'
125
+ v.each do |label|
126
+ expect(actual['errorLabels']).to include(label)
127
+ end
128
+ when 'errorLabelsOmit'
129
+ v.each do |label|
130
+ if actual['errorLabels']
131
+ expect(actual['errorLabels']).not_to include(label)
132
+ end
133
+ end
134
+ else
135
+ verify_hash_items_equal(expected, actual, k)
136
+ end
76
137
  end
77
- when Integer
138
+ else
78
139
  expect(actual).to eq(expected)
79
140
  end
80
141
  end
81
142
 
82
- def handle_upserted_id(field, expected_id, actual_id)
83
- return true if expected_id.nil?
84
- if field == 'upsertedId'
85
- if expected_id.is_a?(Integer)
86
- actual_id.is_a?(BSON::ObjectId) || actual_id.nil?
143
+ def verify_hash_items_equal(expected, actual, k)
144
+ expect(actual).to be_a(Hash)
145
+
146
+ if expected[k] == actual[k]
147
+ return
148
+ end
149
+
150
+ if %w(deletedCount matchedCount modifiedCount upsertedCount).include?(k)
151
+ # Some tests assert that some of these counts are zero.
152
+ # The driver may omit the respective key, which is fine.
153
+ if expected[k] == 0
154
+ expect([0, nil]).to include(actual[k])
155
+ return
87
156
  end
88
157
  end
89
- end
90
158
 
91
- def handle_inserted_ids(field, expected, actual)
92
- if field == 'insertedIds'
93
- expected.values == actual
159
+ if %w(insertedIds upsertedIds).include?(k)
160
+ if expected[k] == {}
161
+ # Like with the counts, allow a response to not specify the
162
+ # ids in question if the expectation is for an empty id map.
163
+ expect([nil, []]).to include(actual[k])
164
+ else
165
+ expect(actual[k]).to eq(expected[k].values)
166
+ end
167
+ return
94
168
  end
169
+
170
+ # This should produce a meaningful error message,
171
+ # even though we do not actually require that expected[k] == actual[k]
172
+ expect({k => expected[k]}).to eq({k => actual[k]})
95
173
  end
96
174
  end
97
175
  end
@@ -61,15 +61,28 @@ module Mongo
61
61
 
62
62
  # Instantiate the operation.
63
63
  #
64
- # @return [ Hash ] spec The operation spec.
64
+ # @param [ Hash ] spec The operation specification.
65
+ # @param [ Hash ] outcome_spec The outcome specification.
66
+ # If not provided, outcome is taken out of operation specification.
65
67
  #
66
68
  # @since 2.0.0
67
- def initialize(spec)
69
+ def initialize(spec, outcome_spec = nil)
68
70
  @spec = spec
69
71
  @name = spec['name']
72
+ @outcome = Outcome.new(outcome_spec || spec)
70
73
  end
71
74
 
72
- # Whether the operation is expected to have restuls.
75
+ attr_reader :outcome
76
+
77
+ def object
78
+ 'collection'
79
+ end
80
+
81
+ def verify_collection_name
82
+ 'crud_spec_test'
83
+ end
84
+
85
+ # Whether the operation is expected to have results.
73
86
  #
74
87
  # @example Whether the operation is expected to have results.
75
88
  # operation.has_results?
@@ -103,8 +116,10 @@ module Mongo
103
116
  return_doc = {}
104
117
  return_doc['deletedCount'] = result.deleted_count if result.deleted_count
105
118
  return_doc['insertedIds'] = result.inserted_ids if result.inserted_ids
119
+ return_doc['upsertedIds'] = result.upserted_ids if result.upserted_ids
106
120
  return_doc['upsertedId'] = result.upserted_id if upsert
107
121
  return_doc['upsertedCount'] = result.upserted_count if result.upserted_count
122
+ return_doc['insertedCount'] = result.inserted_count if result.inserted_count
108
123
  return_doc['matchedCount'] = result.matched_count if result.matched_count
109
124
  return_doc['modifiedCount'] = result.modified_count if result.modified_count
110
125
  return_doc
@@ -20,6 +20,8 @@ class EventSubscriber
20
20
  # @since 2.5.0
21
21
  attr_reader :failed_events
22
22
 
23
+ attr_reader :published_events
24
+
23
25
  # Cache the succeeded event.
24
26
  #
25
27
  # @param [ Event ] event The event.
@@ -53,6 +55,18 @@ class EventSubscriber
53
55
  end
54
56
  end
55
57
 
58
+ def select_published_events(cls)
59
+ @published_events.select do |event|
60
+ event.is_a?(cls)
61
+ end
62
+ end
63
+
64
+ def published(event)
65
+ @mutex.synchronize do
66
+ @published_events << event
67
+ end
68
+ end
69
+
56
70
  # Clear all cached events.
57
71
  #
58
72
  # @since 2.5.1
@@ -60,6 +74,7 @@ class EventSubscriber
60
74
  @started_events = []
61
75
  @succeeded_events = []
62
76
  @failed_events = []
77
+ @published_events = []
63
78
  self
64
79
  end
65
80
 
@@ -7,9 +7,9 @@ module PrimarySocket
7
7
  end
8
8
 
9
9
  let(:primary_connection) do
10
- connection = primary_server.pool.checkout
10
+ connection = primary_server.pool.check_out
11
11
  connection.connect!
12
- primary_server.pool.checkin(connection)
12
+ primary_server.pool.check_in(connection)
13
13
  connection
14
14
  end
15
15
 
@@ -10,10 +10,14 @@ class SpecConfig
10
10
  if @uri_options[:replica_set]
11
11
  @addresses = @mongodb_uri.servers
12
12
  @connect_options = { connect: :replica_set, replica_set: @uri_options[:replica_set] }
13
- elsif ENV['TOPOLOGY'] == 'sharded_cluster'
14
- @addresses = [ @mongodb_uri.servers.first ] # See SERVER-16836 for why we can only use one host:port
13
+ elsif @uri_options[:connect] == :sharded || ENV['TOPOLOGY'] == 'sharded_cluster'
14
+ # See SERVER-16836 for why we can only use one host:port
15
+ if @mongodb_uri.servers.length > 1
16
+ warn "Using only the first mongos (#{@mongodb_uri.servers.first})"
17
+ end
18
+ @addresses = [ @mongodb_uri.servers.first ]
15
19
  @connect_options = { connect: :sharded }
16
- else
20
+ elsif @uri_options[:connect] == :direct
17
21
  @addresses = @mongodb_uri.servers
18
22
  @connect_options = { connect: :direct }
19
23
  end
@@ -22,7 +26,7 @@ class SpecConfig
22
26
  else
23
27
  @ssl = @uri_options[:ssl]
24
28
  end
25
- else
29
+ elsif ENV['MONGODB_ADDRESSES']
26
30
  @addresses = ENV['MONGODB_ADDRESSES'] ? ENV['MONGODB_ADDRESSES'].split(',').freeze : [ '127.0.0.1:27017' ].freeze
27
31
  if ENV['RS_ENABLED']
28
32
  @connect_options = { connect: :replica_set, replica_set: ENV['RS_NAME'] }
@@ -32,6 +36,36 @@ class SpecConfig
32
36
  @connect_options = { connect: :direct }
33
37
  end
34
38
  end
39
+
40
+ if @addresses.nil?
41
+ # Discover deployment topology
42
+ if @mongodb_uri
43
+ # TLS options need to be merged for evergreen due to
44
+ # https://github.com/10gen/mongo-orchestration/issues/268
45
+ client = Mongo::Client.new(@mongodb_uri.servers, Mongo::Options::Redacted.new(
46
+ server_selection_timeout: 5,
47
+ ).merge(@mongodb_uri.uri_options).merge(ssl_options))
48
+ @addresses = @mongodb_uri.servers
49
+ else
50
+ client = Mongo::Client.new(['localhost:27017'], server_selection_timeout: 5)
51
+ @addresses = client.cluster.servers_list.map do |server|
52
+ server.address.to_s
53
+ end
54
+ end
55
+ client.cluster.next_primary
56
+ case client.cluster.topology.class.name
57
+ when /Replica/
58
+ @connect_options = { connect: :replica_set, replica_set: client.cluster.topology.replica_set_name }
59
+ when /Sharded/
60
+ @connect_options = { connect: :sharded }
61
+ when /Single/
62
+ @connect_options = { connect: :direct }
63
+ when /Unknown/
64
+ raise "Could not detect topology because the test client failed to connect to MongoDB deployment"
65
+ else
66
+ raise "Weird topology #{client.cluster.topology}"
67
+ end
68
+ end
35
69
  end
36
70
 
37
71
  attr_reader :uri_options, :addresses, :connect_options
@@ -77,8 +111,39 @@ class SpecConfig
77
111
  end
78
112
  end
79
113
 
114
+ def retry_reads
115
+ uri_option_or_env_var(:retry_reads, 'RETRY_READS')
116
+ end
117
+
118
+ def retry_writes
119
+ uri_option_or_env_var(:retry_writes, 'RETRY_WRITES')
120
+ end
121
+
122
+ def uri_option_or_env_var(driver_option_symbol, env_var_key)
123
+ case uri_options[driver_option_symbol]
124
+ when true
125
+ true
126
+ when false
127
+ false
128
+ else
129
+ case (ENV[env_var_key] || '').downcase
130
+ when 'yes', 'true', 'on', '1'
131
+ true
132
+ when 'no', 'false', 'off', '0'
133
+ false
134
+ else
135
+ nil
136
+ end
137
+ end
138
+ end
139
+
80
140
  def retry_writes?
81
- %w(yes true on 1).include?((ENV['RETRY_WRITES'] || '').downcase)
141
+ if retry_writes == false
142
+ false
143
+ else
144
+ # Current default is to retry writes
145
+ true
146
+ end
82
147
  end
83
148
 
84
149
  def ssl?
@@ -102,17 +167,18 @@ class SpecConfig
102
167
  connect_options[:connect] == :replica_set
103
168
  end
104
169
 
105
- # Derived data
106
-
107
- # The write concern to use in the tests.
108
- def write_concern
109
- if connect_replica_set?
110
- {w: 2}
111
- else
112
- {w: 1}
113
- end
170
+ def print_summary
171
+ puts "Connection options: #{test_options}"
172
+ client = ClientRegistry.instance.global_client('basic')
173
+ client.cluster.next_primary
174
+ puts <<-EOT
175
+ Topology: #{client.cluster.topology.class}
176
+ connect: #{connect_options[:connect]}
177
+ EOT
114
178
  end
115
179
 
180
+ # Derived data
181
+
116
182
  def any_port
117
183
  addresses.first.split(':')[1] || '27017'
118
184
  end
@@ -141,6 +207,14 @@ class SpecConfig
141
207
  end
142
208
  end
143
209
 
210
+ def client_cert_key_pem
211
+ if drivers_tools?
212
+ ENV['DRIVER_TOOLS_CLIENT_CERT_KEY_PEM']
213
+ else
214
+ "#{ssl_certs_dir}/client.pem"
215
+ end
216
+ end
217
+
144
218
  # The default test database for all specs.
145
219
  def test_db
146
220
  'ruby-driver'.freeze
@@ -178,18 +252,13 @@ class SpecConfig
178
252
  end
179
253
 
180
254
  def retry_writes_options
181
- if retry_writes?
182
- {retry_writes: true}
183
- else
184
- {}
185
- end
255
+ {retry_writes: retry_writes}
186
256
  end
187
257
 
188
258
  # Base test options.
189
259
  def base_test_options
190
260
  {
191
261
  max_pool_size: 1,
192
- write: write_concern,
193
262
  heartbeat_frequency: 20,
194
263
  max_read_retries: 5,
195
264
  # The test suite seems to perform a number of operations
@@ -13,309 +13,5 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require 'support/transactions/operation'
16
- require 'support/transactions/verifier'
17
-
18
- module Mongo
19
- module Transactions
20
-
21
- # Represents a Transactions specification test.
22
- #
23
- # @since 2.6.0
24
- class Spec
25
-
26
- # The name of the collection to run the tests against.
27
- #
28
- # @since 2.6.0
29
- COLLECTION_NAME = 'transactions-tests'.freeze
30
-
31
- # @return [ String ] description The spec description.
32
- #
33
- # @since 2.6.0
34
- attr_reader :description
35
-
36
- # Instantiate the new spec.
37
- #
38
- # @example Create the spec.
39
- # Spec.new(file)
40
- #
41
- # @param [ String ] file The name of the file.
42
- #
43
- # @since 2.6.0
44
- def initialize(file)
45
- file = File.new(file)
46
- contents = ERB.new(file.read).result
47
- @spec = YAML.load(contents)
48
- file.close
49
- @description = File.basename(file)
50
- @data = @spec['data']
51
- @transaction_tests = @spec['tests']
52
- end
53
-
54
- # Get a list of TransactionTests for each test definition.
55
- #
56
- # @example Get the list of TransactionTests.
57
- # spec.tests
58
- #
59
- # @return [ Array<TransactionsTest> ] The list of TransactionTests.
60
- #
61
- # @since 2.6.0
62
- def tests
63
- @transaction_tests.map do |test|
64
- Proc.new { Mongo::Transactions::TransactionsTest.new(@data, test, self) }
65
- end.compact
66
- end
67
-
68
- def database_name
69
- @spec['database_name']
70
- end
71
-
72
- def collection_name
73
- @spec['collection_name']
74
- end
75
-
76
- def min_server_version
77
- @spec['minServerVersion']
78
- end
79
- end
80
-
81
- # Represents a single transaction test.
82
- #
83
- # @since 2.6.0
84
- class TransactionsTest
85
-
86
- # The test description.
87
- #
88
- # @return [ String ] description The test description.
89
- #
90
- # @since 2.6.0
91
- attr_reader :description
92
-
93
- # The expected command monitoring events
94
- #
95
- # @since 2.6.0
96
- attr_reader :expectations
97
-
98
- attr_reader :expected_results
99
- attr_reader :skip_reason
100
-
101
- # Instantiate the new CRUDTest.
102
- #
103
- # @example Create the test.
104
- # TransactionTest.new(data, test)
105
- #
106
- # @param [ Array<Hash> ] data The documents the collection
107
- # must have before the test runs.
108
- # @param [ Hash ] test The test specification.
109
- # @param [ Hash ] spec The top level YAML specification.
110
- #
111
- # @since 2.6.0
112
- def initialize(data, test, spec)
113
- test = IceNine.deep_freeze(test)
114
- @spec = spec
115
- @data = data
116
- @description = test['description']
117
- @client_options = convert_client_options(test['clientOptions'] || {})
118
- @session_options = snakeize_hash(test['sessionOptions'] || {})
119
- @fail_point = test['failPoint']
120
- @operations = test['operations']
121
- @expectations = test['expectations']
122
- @skip_reason = test['skipReason']
123
- @outcome = test['outcome']
124
- @expected_results = @operations.map do |o|
125
- result = o['result']
126
- next result unless result.class == Hash
127
-
128
- # Change maps of result ids to arrays of ids
129
- result.dup.tap do |r|
130
- r.each do |k, v|
131
- next unless ['insertedIds', 'upsertedIds'].include?(k)
132
- r[k] = v.to_a.sort_by(&:first).map(&:last)
133
- end
134
- end
135
- end
136
- end
137
-
138
- def support_client
139
- @support_client ||= ClientRegistry.instance.global_client('root_authorized').use(@spec.database_name)
140
- end
141
-
142
- def admin_support_client
143
- @admin_support_client ||= support_client.use('admin')
144
- end
145
-
146
- def test_client
147
- @test_client ||= ClientRegistry.instance.global_client('authorized_without_retry_writes').with(
148
- @client_options.merge(
149
- database: @spec.database_name,
150
- app_name: 'this is used solely to force the new client to create its own cluster'))
151
- end
152
-
153
- def event_subscriber
154
- @event_subscriber ||= EventSubscriber.new
155
- end
156
-
157
- # Run the test.
158
- #
159
- # @example Run the test.
160
- # test.run
161
- #
162
- # @return [ Result ] The result of running the test.
163
- #
164
- # @since 2.6.0
165
- def run
166
- test_client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
167
-
168
- results = @ops.map do |op|
169
- op.execute(@collection)
170
- end
171
-
172
- session0_id = @session0.session_id
173
- session1_id = @session1.session_id
174
-
175
- @session0.end_session
176
- @session1.end_session
177
-
178
- events = event_subscriber.started_events.map do |e|
179
-
180
- # Convert txnNumber field from a BSON integer to an extended JSON int64
181
- if e.command['txnNumber']
182
- e.command['txnNumber'] = {
183
- '$numberLong' => e.command['txnNumber'].instance_variable_get(:@integer).to_s
184
- }
185
- end
186
-
187
- # Replace the session id placeholders with the actual session ids.
188
- e.command['lsid'] = 'session0' if e.command['lsid'] == session0_id
189
- e.command['lsid'] = 'session1' if e.command['lsid'] == session1_id
190
-
191
-
192
- # The spec files don't include these fields, so we delete them.
193
- e.command.delete('$readPreference')
194
- e.command.delete('bypassDocumentValidation')
195
- e.command.delete('$clusterTime')
196
-
197
-
198
- if e.command['readConcern']
199
- # The spec test use an afterClusterTime value of 42 to indicate that we need to assert
200
- # that the field exists in the actual read concern rather than comparing the value, so
201
- # we replace any afterClusterTime value with 42.
202
- if e.command['readConcern']['afterClusterTime']
203
- e.command['readConcern']['afterClusterTime'] = 42
204
- end
205
-
206
- # Convert the readConcern level from a symbol to a string.
207
- if e.command['readConcern']['level']
208
- e.command['readConcern']['level'] = e.command['readConcern']['level'].to_s
209
- end
210
- end
211
-
212
- # This write concern is sent for some server topologies/configurations, but not all, so it
213
- # doesn't appear in the expected events.
214
- e.command.delete('writeConcern') if e.command['writeConcern'] == { 'w' => 2 }
215
-
216
- # The spec tests use 42 as a placeholder value for any getMore cursorId.
217
- e.command['getMore'] = { '$numberLong' => '42' } if e.command['getMore']
218
-
219
- # Remove fields if empty
220
- e.command.delete('cursor') if e.command['cursor'] && e.command['cursor'].empty?
221
- e.command.delete('filter') if e.command['filter'] && e.command['filter'].empty?
222
- e.command.delete('query') if e.command['query'] && e.command['query'].empty?
223
-
224
- {
225
- 'command_started_event' => {
226
- 'command' => order_hash(e.command),
227
- 'command_name' => e.command_name.to_s,
228
- 'database_name' => e.database_name
229
- }
230
- }
231
- end
232
-
233
- # Remove any events from authentication commands.
234
- events.reject! { |c| c['command_started_event']['command_name'].start_with?('sasl') }
235
-
236
- if @fail_point
237
- admin_support_client.command(configureFailPoint: 'failCommand', mode: 'off')
238
- end
239
-
240
- events.map! do |event|
241
- event['command_started_event'] = order_hash(event['command_started_event'])
242
- end
243
-
244
- {
245
- results: results,
246
- contents: @collection.find.to_a,
247
- events: events,
248
- }
249
- end
250
-
251
- def setup_test
252
- begin
253
- admin_support_client.command(killAllSessions: [])
254
- rescue Mongo::Error
255
- end
256
-
257
- coll = support_client[@spec.collection_name]
258
- coll.database.drop
259
- coll.with(write: { w: :majority }).drop
260
- support_client.command(create: @spec.collection_name, writeConcern: { w: :majority })
261
-
262
- coll.with(write: { w: :majority }).insert_many(@data) unless @data.empty?
263
- admin_support_client.command(@fail_point) if @fail_point
264
-
265
- @collection = test_client[@spec.collection_name]
266
-
267
- @session0 = test_client.start_session(@session_options[:session0] || {})
268
- @session1 = test_client.start_session(@session_options[:session1] || {})
269
-
270
- @ops = @operations.map do |op|
271
- Operation.new(op, @session0, @session1)
272
- end
273
- end
274
-
275
- def teardown_test
276
- if @admin_support_client
277
- @admin_support_client.close
278
- end
279
- if @test_client
280
- @test_client.close
281
- end
282
- end
283
-
284
- # The expected data in the collection as an outcome after running this test.
285
- #
286
- # @example Get the outcome collection data
287
- # test.outcome_collection_data
288
- #
289
- # @return [ Array<Hash> ] The list of documents expected to be in the collection
290
- # after running this test.
291
- #
292
- # @since 2.6.0
293
- def outcome_collection_data
294
- @outcome['collection']['data'] if @outcome && @outcome['collection']
295
- end
296
-
297
- def order_hash(hash)
298
- Hash[hash.to_a.sort]
299
- end
300
-
301
- private
302
-
303
- def convert_client_options(client_options)
304
- client_options.reduce({}) do |opts, kv|
305
- case kv.first
306
- when 'readConcernLevel'
307
- kv = [:read_concern, { 'level' => kv.last }]
308
- when 'readPreference'
309
- kv = [:read, { 'mode' => kv.last }]
310
- when 'w'
311
- kv = [:write, { w: kv.last }]
312
- else
313
- kv[0] = camel_to_snake(kv[0])
314
- end
315
-
316
- opts.tap { |o| o[kv.first] = kv.last }
317
- end
318
- end
319
- end
320
- end
321
- end
16
+ require 'support/transactions/spec'
17
+ require 'support/transactions/test'