mongo 2.8.0 → 2.9.0.rc0

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