mongo 2.5.3 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (394) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +1 -1
  5. data/README.md +3 -2
  6. data/lib/mongo.rb +2 -2
  7. data/lib/mongo/address.rb +10 -2
  8. data/lib/mongo/address/ipv4.rb +1 -1
  9. data/lib/mongo/address/ipv6.rb +26 -5
  10. data/lib/mongo/address/unix.rb +1 -1
  11. data/lib/mongo/auth.rb +10 -3
  12. data/lib/mongo/auth/cr.rb +4 -1
  13. data/lib/mongo/auth/cr/conversation.rb +4 -1
  14. data/lib/mongo/auth/ldap.rb +1 -1
  15. data/lib/mongo/auth/ldap/conversation.rb +1 -1
  16. data/lib/mongo/auth/roles.rb +1 -1
  17. data/lib/mongo/auth/scram.rb +24 -7
  18. data/lib/mongo/auth/scram/conversation.rb +52 -19
  19. data/lib/mongo/auth/stringprep.rb +114 -0
  20. data/lib/mongo/auth/stringprep/profiles/sasl.rb +73 -0
  21. data/lib/mongo/auth/stringprep/tables.rb +3232 -0
  22. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +174 -0
  23. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1170 -0
  24. data/lib/mongo/auth/user.rb +14 -3
  25. data/lib/mongo/auth/user/view.rb +1 -1
  26. data/lib/mongo/auth/x509.rb +1 -1
  27. data/lib/mongo/auth/x509/conversation.rb +1 -1
  28. data/lib/mongo/bson.rb +1 -1
  29. data/lib/mongo/bulk_write.rb +8 -8
  30. data/lib/mongo/bulk_write/combineable.rb +1 -1
  31. data/lib/mongo/bulk_write/ordered_combiner.rb +1 -1
  32. data/lib/mongo/bulk_write/result.rb +1 -1
  33. data/lib/mongo/bulk_write/result_combiner.rb +4 -4
  34. data/lib/mongo/bulk_write/transformable.rb +1 -1
  35. data/lib/mongo/bulk_write/unordered_combiner.rb +1 -1
  36. data/lib/mongo/bulk_write/validatable.rb +1 -1
  37. data/lib/mongo/client.rb +115 -24
  38. data/lib/mongo/cluster.rb +17 -10
  39. data/lib/mongo/cluster/app_metadata.rb +7 -1
  40. data/lib/mongo/cluster/periodic_executor.rb +1 -1
  41. data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -1
  42. data/lib/mongo/cluster/topology.rb +12 -2
  43. data/lib/mongo/cluster/topology/replica_set.rb +9 -1
  44. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  45. data/lib/mongo/cluster/topology/single.rb +1 -1
  46. data/lib/mongo/cluster/topology/unknown.rb +1 -1
  47. data/lib/mongo/collection.rb +75 -19
  48. data/lib/mongo/collection/view.rb +1 -1
  49. data/lib/mongo/collection/view/aggregation.rb +1 -1
  50. data/lib/mongo/collection/view/builder.rb +1 -1
  51. data/lib/mongo/collection/view/builder/aggregation.rb +3 -3
  52. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  53. data/lib/mongo/collection/view/builder/flags.rb +1 -1
  54. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  55. data/lib/mongo/collection/view/builder/modifiers.rb +1 -1
  56. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  57. data/lib/mongo/collection/view/change_stream.rb +193 -17
  58. data/lib/mongo/collection/view/change_stream/retryable.rb +3 -20
  59. data/lib/mongo/collection/view/explainable.rb +1 -1
  60. data/lib/mongo/collection/view/immutable.rb +1 -1
  61. data/lib/mongo/collection/view/iterable.rb +2 -2
  62. data/lib/mongo/collection/view/map_reduce.rb +1 -1
  63. data/lib/mongo/collection/view/readable.rb +108 -29
  64. data/lib/mongo/collection/view/writable.rb +3 -3
  65. data/lib/mongo/cursor.rb +44 -4
  66. data/lib/mongo/cursor/builder.rb +1 -1
  67. data/lib/mongo/cursor/builder/get_more_command.rb +1 -1
  68. data/lib/mongo/cursor/builder/kill_cursors_command.rb +1 -1
  69. data/lib/mongo/cursor/builder/op_get_more.rb +1 -1
  70. data/lib/mongo/cursor/builder/op_kill_cursors.rb +1 -1
  71. data/lib/mongo/database.rb +46 -3
  72. data/lib/mongo/database/view.rb +11 -11
  73. data/lib/mongo/dbref.rb +1 -1
  74. data/lib/mongo/error.rb +57 -1
  75. data/lib/mongo/error/bulk_write_error.rb +2 -2
  76. data/lib/mongo/error/change_stream_resumable.rb +37 -0
  77. data/lib/mongo/error/closed_stream.rb +1 -1
  78. data/lib/mongo/error/extra_file_chunk.rb +1 -1
  79. data/lib/mongo/error/failed_stringprep_validation.rb +38 -0
  80. data/lib/mongo/error/file_not_found.rb +1 -1
  81. data/lib/mongo/error/insufficient_iteration_count.rb +38 -0
  82. data/lib/mongo/error/invalid_application_name.rb +1 -1
  83. data/lib/mongo/error/invalid_bulk_operation.rb +1 -1
  84. data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -1
  85. data/lib/mongo/error/invalid_collection_name.rb +1 -1
  86. data/lib/mongo/error/invalid_database_name.rb +1 -1
  87. data/lib/mongo/error/invalid_document.rb +1 -1
  88. data/lib/mongo/error/invalid_file.rb +1 -1
  89. data/lib/mongo/error/invalid_file_revision.rb +1 -1
  90. data/lib/mongo/error/invalid_min_pool_size.rb +1 -1
  91. data/lib/mongo/error/invalid_nonce.rb +1 -1
  92. data/lib/mongo/error/invalid_read_option.rb +35 -0
  93. data/lib/mongo/error/invalid_replacement_document.rb +1 -1
  94. data/lib/mongo/error/invalid_server_preference.rb +1 -1
  95. data/lib/mongo/error/invalid_session.rb +1 -1
  96. data/lib/mongo/error/invalid_signature.rb +1 -1
  97. data/lib/mongo/error/invalid_transaction_operation.rb +82 -0
  98. data/lib/mongo/error/invalid_txt_record.rb +1 -1
  99. data/lib/mongo/error/invalid_update_document.rb +1 -1
  100. data/lib/mongo/error/invalid_uri.rb +1 -1
  101. data/lib/mongo/error/invalid_write_concern.rb +1 -1
  102. data/lib/mongo/error/max_bson_size.rb +1 -1
  103. data/lib/mongo/error/max_message_size.rb +1 -1
  104. data/lib/mongo/error/mismatched_domain.rb +1 -1
  105. data/lib/mongo/error/missing_file_chunk.rb +1 -1
  106. data/lib/mongo/error/missing_resume_token.rb +1 -1
  107. data/lib/mongo/error/multi_index_drop.rb +1 -1
  108. data/lib/mongo/error/need_primary_server.rb +1 -1
  109. data/lib/mongo/error/no_server_available.rb +1 -1
  110. data/lib/mongo/error/no_srv_records.rb +1 -1
  111. data/lib/mongo/error/operation_failure.rb +108 -14
  112. data/lib/mongo/error/parser.rb +50 -1
  113. data/lib/mongo/error/socket_error.rb +5 -2
  114. data/lib/mongo/error/socket_timeout_error.rb +5 -2
  115. data/lib/mongo/error/unchangeable_collection_option.rb +1 -1
  116. data/lib/mongo/error/unexpected_chunk_length.rb +1 -1
  117. data/lib/mongo/error/unexpected_response.rb +1 -1
  118. data/lib/mongo/error/unknown_payload_type.rb +1 -1
  119. data/lib/mongo/error/unsupported_array_filters.rb +1 -1
  120. data/lib/mongo/error/unsupported_collation.rb +1 -1
  121. data/lib/mongo/error/unsupported_features.rb +1 -1
  122. data/lib/mongo/error/unsupported_message_type.rb +1 -1
  123. data/lib/mongo/error/write_retryable.rb +27 -0
  124. data/lib/mongo/event.rb +10 -9
  125. data/lib/mongo/event/base.rb +33 -0
  126. data/lib/mongo/event/description_changed.rb +2 -2
  127. data/lib/mongo/event/listeners.rb +1 -1
  128. data/lib/mongo/event/member_discovered.rb +4 -2
  129. data/lib/mongo/event/primary_elected.rb +2 -2
  130. data/lib/mongo/event/publisher.rb +1 -1
  131. data/lib/mongo/event/standalone_discovered.rb +2 -2
  132. data/lib/mongo/event/subscriber.rb +1 -1
  133. data/lib/mongo/grid.rb +1 -1
  134. data/lib/mongo/grid/file.rb +1 -1
  135. data/lib/mongo/grid/file/chunk.rb +3 -3
  136. data/lib/mongo/grid/file/info.rb +26 -3
  137. data/lib/mongo/grid/fs_bucket.rb +1 -1
  138. data/lib/mongo/grid/stream.rb +1 -1
  139. data/lib/mongo/grid/stream/read.rb +1 -1
  140. data/lib/mongo/grid/stream/write.rb +1 -1
  141. data/lib/mongo/index.rb +1 -1
  142. data/lib/mongo/index/view.rb +1 -1
  143. data/lib/mongo/loggable.rb +1 -1
  144. data/lib/mongo/logger.rb +1 -1
  145. data/lib/mongo/monitoring.rb +99 -62
  146. data/lib/mongo/monitoring/command_log_subscriber.rb +2 -2
  147. data/lib/mongo/monitoring/event.rb +2 -1
  148. data/lib/mongo/monitoring/event/command_failed.rb +19 -6
  149. data/lib/mongo/monitoring/event/command_started.rb +14 -3
  150. data/lib/mongo/monitoring/event/command_succeeded.rb +5 -3
  151. data/lib/mongo/monitoring/event/secure.rb +1 -1
  152. data/lib/mongo/monitoring/event/server_closed.rb +2 -2
  153. data/lib/mongo/monitoring/event/server_description_changed.rb +2 -2
  154. data/lib/mongo/monitoring/event/server_opening.rb +11 -2
  155. data/lib/mongo/monitoring/event/topology_changed.rb +13 -2
  156. data/lib/mongo/monitoring/event/topology_closed.rb +2 -2
  157. data/lib/mongo/monitoring/event/topology_opening.rb +11 -2
  158. data/lib/mongo/monitoring/publishable.rb +10 -6
  159. data/lib/mongo/monitoring/sdam_log_subscriber.rb +1 -1
  160. data/lib/mongo/monitoring/server_closed_log_subscriber.rb +1 -1
  161. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +1 -1
  162. data/lib/mongo/monitoring/server_opening_log_subscriber.rb +1 -1
  163. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  164. data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +1 -1
  165. data/lib/mongo/operation/aggregate/op_msg.rb +3 -0
  166. data/lib/mongo/operation/create/op_msg.rb +9 -0
  167. data/lib/mongo/operation/create_index/op_msg.rb +9 -0
  168. data/lib/mongo/operation/create_user/command.rb +1 -1
  169. data/lib/mongo/operation/create_user/op_msg.rb +10 -1
  170. data/lib/mongo/operation/delete/op_msg.rb +3 -0
  171. data/lib/mongo/operation/distinct/op_msg.rb +9 -0
  172. data/lib/mongo/operation/drop/op_msg.rb +9 -0
  173. data/lib/mongo/operation/drop_database/op_msg.rb +9 -0
  174. data/lib/mongo/operation/drop_index/op_msg.rb +9 -0
  175. data/lib/mongo/operation/explain/op_msg.rb +3 -0
  176. data/lib/mongo/operation/find/op_msg.rb +3 -0
  177. data/lib/mongo/operation/get_more.rb +1 -1
  178. data/lib/mongo/operation/get_more/command.rb +1 -1
  179. data/lib/mongo/operation/get_more/legacy.rb +1 -1
  180. data/lib/mongo/operation/get_more/op_msg.rb +3 -0
  181. data/lib/mongo/operation/indexes/op_msg.rb +3 -0
  182. data/lib/mongo/operation/indexes/result.rb +1 -1
  183. data/lib/mongo/operation/insert/bulk_result.rb +32 -2
  184. data/lib/mongo/operation/insert/op_msg.rb +3 -0
  185. data/lib/mongo/operation/insert/result.rb +1 -1
  186. data/lib/mongo/operation/kill_cursors/op_msg.rb +9 -0
  187. data/lib/mongo/operation/list_collections/op_msg.rb +3 -0
  188. data/lib/mongo/operation/list_collections/result.rb +5 -1
  189. data/lib/mongo/operation/map_reduce/op_msg.rb +3 -0
  190. data/lib/mongo/operation/map_reduce/result.rb +1 -1
  191. data/lib/mongo/operation/parallel_scan/op_msg.rb +3 -0
  192. data/lib/mongo/operation/remove_user/op_msg.rb +9 -0
  193. data/lib/mongo/operation/result.rb +27 -14
  194. data/lib/mongo/operation/shared/executable.rb +1 -0
  195. data/lib/mongo/operation/shared/sessions_supported.rb +78 -7
  196. data/lib/mongo/operation/shared/specifiable.rb +18 -2
  197. data/lib/mongo/operation/shared/write_concern_supported.rb +1 -1
  198. data/lib/mongo/operation/update/op_msg.rb +3 -0
  199. data/lib/mongo/operation/update_user/command.rb +1 -1
  200. data/lib/mongo/operation/update_user/op_msg.rb +10 -1
  201. data/lib/mongo/operation/users_info/op_msg.rb +3 -0
  202. data/lib/mongo/options.rb +1 -1
  203. data/lib/mongo/options/mapper.rb +1 -1
  204. data/lib/mongo/options/redacted.rb +1 -1
  205. data/lib/mongo/protocol/bit_vector.rb +1 -1
  206. data/lib/mongo/protocol/compressed.rb +1 -1
  207. data/lib/mongo/protocol/delete.rb +1 -1
  208. data/lib/mongo/protocol/get_more.rb +7 -7
  209. data/lib/mongo/protocol/insert.rb +1 -1
  210. data/lib/mongo/protocol/kill_cursors.rb +1 -1
  211. data/lib/mongo/protocol/message.rb +5 -5
  212. data/lib/mongo/protocol/msg.rb +9 -7
  213. data/lib/mongo/protocol/query.rb +1 -1
  214. data/lib/mongo/protocol/registry.rb +1 -1
  215. data/lib/mongo/protocol/reply.rb +10 -10
  216. data/lib/mongo/protocol/serializers.rb +1 -1
  217. data/lib/mongo/protocol/update.rb +1 -1
  218. data/lib/mongo/retryable.rb +22 -14
  219. data/lib/mongo/server.rb +1 -1
  220. data/lib/mongo/server/connectable.rb +1 -1
  221. data/lib/mongo/server/connection.rb +16 -4
  222. data/lib/mongo/server/connection_pool.rb +1 -1
  223. data/lib/mongo/server/connection_pool/queue.rb +1 -1
  224. data/lib/mongo/server/context.rb +1 -1
  225. data/lib/mongo/server/description.rb +14 -2
  226. data/lib/mongo/server/description/features.rb +10 -9
  227. data/lib/mongo/server/description/inspector.rb +1 -1
  228. data/lib/mongo/server/description/inspector/description_changed.rb +1 -1
  229. data/lib/mongo/server/description/inspector/member_discovered.rb +1 -1
  230. data/lib/mongo/server/description/inspector/primary_elected.rb +1 -1
  231. data/lib/mongo/server/description/inspector/standalone_discovered.rb +1 -1
  232. data/lib/mongo/server/monitor.rb +15 -3
  233. data/lib/mongo/server/monitor/connection.rb +1 -1
  234. data/lib/mongo/server_selector.rb +1 -1
  235. data/lib/mongo/server_selector/nearest.rb +1 -1
  236. data/lib/mongo/server_selector/primary.rb +1 -1
  237. data/lib/mongo/server_selector/primary_preferred.rb +1 -1
  238. data/lib/mongo/server_selector/secondary.rb +1 -1
  239. data/lib/mongo/server_selector/secondary_preferred.rb +1 -1
  240. data/lib/mongo/server_selector/selectable.rb +7 -2
  241. data/lib/mongo/session.rb +389 -12
  242. data/lib/mongo/session/server_session.rb +7 -2
  243. data/lib/mongo/session/session_pool.rb +1 -1
  244. data/lib/mongo/socket.rb +1 -1
  245. data/lib/mongo/socket/ssl.rb +1 -1
  246. data/lib/mongo/socket/tcp.rb +1 -1
  247. data/lib/mongo/socket/unix.rb +1 -1
  248. data/lib/mongo/uri.rb +6 -4
  249. data/lib/mongo/uri/srv_protocol.rb +1 -1
  250. data/lib/mongo/version.rb +2 -2
  251. data/lib/mongo/write_concern.rb +1 -1
  252. data/lib/mongo/write_concern/acknowledged.rb +1 -1
  253. data/lib/mongo/write_concern/normalizable.rb +1 -1
  254. data/lib/mongo/write_concern/unacknowledged.rb +1 -1
  255. data/mongo.gemspec +4 -1
  256. data/spec/atlas/atlas_connectivity_spec.rb +54 -0
  257. data/spec/integration/bulk_insert_spec.rb +78 -0
  258. data/spec/integration/change_stream_spec.rb +365 -0
  259. data/spec/integration/command_monitoring_spec.rb +92 -0
  260. data/spec/lite_spec_helper.rb +63 -0
  261. data/spec/mongo/address/ipv6_spec.rb +29 -1
  262. data/spec/mongo/address_spec.rb +34 -0
  263. data/spec/mongo/auth/scram/conversation_spec.rb +326 -120
  264. data/spec/mongo/auth/scram/negotiation_spec.rb +574 -0
  265. data/spec/mongo/auth/scram_spec.rb +107 -38
  266. data/spec/mongo/auth/stringprep/profiles/sasl_spec.rb +113 -0
  267. data/spec/mongo/auth/stringprep_spec.rb +188 -0
  268. data/spec/mongo/auth/user/view_spec.rb +5 -2
  269. data/spec/mongo/auth/user_spec.rb +1 -1
  270. data/spec/mongo/bulk_write/result_spec.rb +120 -0
  271. data/spec/mongo/bulk_write_spec.rb +42 -2
  272. data/spec/mongo/client_spec.rb +121 -9
  273. data/spec/mongo/cluster/app_metadata_spec.rb +14 -1
  274. data/spec/mongo/cluster/topology_spec.rb +1 -23
  275. data/spec/mongo/collection/view/change_stream_spec.rb +62 -180
  276. data/spec/mongo/collection_spec.rb +45 -12
  277. data/spec/mongo/cursor/builder/get_more_command_spec.rb +7 -7
  278. data/spec/mongo/cursor_spec.rb +2 -2
  279. data/spec/mongo/database_spec.rb +3 -3
  280. data/spec/mongo/docs_examples_spec.rb +194 -0
  281. data/spec/mongo/error/operation_failure_spec.rb +152 -0
  282. data/spec/mongo/error/parser_spec.rb +127 -0
  283. data/spec/mongo/grid/fs_bucket_spec.rb +32 -0
  284. data/spec/mongo/grid/stream/write_spec.rb +40 -1
  285. data/spec/mongo/monitoring/event/command_failed_spec.rb +30 -0
  286. data/spec/mongo/monitoring/event/command_started_spec.rb +26 -4
  287. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +29 -7
  288. data/spec/mongo/monitoring_spec.rb +28 -3
  289. data/spec/mongo/protocol/get_more_spec.rb +2 -2
  290. data/spec/mongo/retryable_spec.rb +252 -34
  291. data/spec/mongo/retryable_writes_spec.rb +468 -544
  292. data/spec/mongo/server/connection_spec.rb +5 -5
  293. data/spec/mongo/server/description_spec.rb +23 -6
  294. data/spec/mongo/session/server_session_spec.rb +2 -2
  295. data/spec/mongo/session/session_pool_spec.rb +2 -2
  296. data/spec/mongo/transactions_examples_spec.rb +227 -0
  297. data/spec/mongo/transactions_spec.rb +44 -0
  298. data/spec/spec_helper.rb +135 -49
  299. data/spec/spec_tests/change_streams_spec.rb +42 -0
  300. data/spec/{mongo → spec_tests}/command_monitoring_spec.rb +8 -2
  301. data/spec/{mongo → spec_tests}/connection_string_spec.rb +1 -1
  302. data/spec/{mongo → spec_tests}/crud_spec.rb +5 -7
  303. data/spec/{mongo → spec_tests}/dns_seedlist_discovery_spec.rb +1 -1
  304. data/spec/{mongo → spec_tests}/gridfs_spec.rb +0 -0
  305. data/spec/{mongo → spec_tests}/max_staleness_spec.rb +0 -0
  306. data/spec/spec_tests/retryable_writes_spec.rb +78 -0
  307. data/spec/{mongo → spec_tests}/sdam_monitoring_spec.rb +4 -3
  308. data/spec/{mongo → spec_tests}/sdam_spec.rb +7 -7
  309. data/spec/{mongo → spec_tests}/server_selection_rtt_spec.rb +0 -0
  310. data/spec/{mongo → spec_tests}/server_selection_spec.rb +0 -0
  311. data/spec/support/authorization.rb +18 -6
  312. data/spec/support/change_streams.rb +265 -0
  313. data/spec/support/change_streams/operation.rb +62 -0
  314. data/spec/support/change_streams_tests/change-streams-errors.yml +53 -0
  315. data/spec/support/change_streams_tests/change-streams.yml +299 -0
  316. data/spec/support/command_monitoring.rb +1 -1
  317. data/spec/support/command_monitoring/bulkWrite.yml +4 -28
  318. data/spec/support/command_monitoring/command.yml +19 -0
  319. data/spec/support/command_monitoring/find.yml +17 -19
  320. data/spec/support/command_monitoring/insertMany.yml +2 -8
  321. data/spec/support/command_monitoring/unacknowledgedBulkWrite.yml +34 -0
  322. data/spec/support/connection_string.rb +1 -1
  323. data/spec/support/constraints.rb +56 -0
  324. data/spec/support/crud.rb +9 -4
  325. data/spec/support/crud/read.rb +24 -3
  326. data/spec/support/crud/write.rb +7 -2
  327. data/spec/support/crud_tests/read/count-collation.yml +12 -2
  328. data/spec/support/crud_tests/read/count.yml +43 -5
  329. data/spec/support/gridfs.rb +1 -1
  330. data/spec/support/primary_socket.rb +21 -0
  331. data/spec/support/retryable_writes_tests/bulkWrite-serverErrors.yml +90 -0
  332. data/spec/support/retryable_writes_tests/bulkWrite.yml +99 -1
  333. data/spec/support/retryable_writes_tests/deleteOne-serverErrors.yml +50 -0
  334. data/spec/support/retryable_writes_tests/deleteOne.yml +10 -1
  335. data/spec/support/retryable_writes_tests/findOneAndDelete-serverErrors.yml +50 -0
  336. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +39 -30
  337. data/spec/support/retryable_writes_tests/findOneAndReplace-serverErrors.yml +54 -0
  338. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +9 -0
  339. data/spec/support/retryable_writes_tests/findOneAndUpdate-serverErrors.yml +54 -0
  340. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +9 -0
  341. data/spec/support/retryable_writes_tests/insertMany-serverErrors.yml +59 -0
  342. data/spec/support/retryable_writes_tests/insertMany.yml +11 -6
  343. data/spec/support/retryable_writes_tests/insertOne-serverErrors.yml +471 -0
  344. data/spec/support/retryable_writes_tests/insertOne.yml +9 -0
  345. data/spec/support/retryable_writes_tests/replaceOne-serverErrors.yml +58 -0
  346. data/spec/support/retryable_writes_tests/replaceOne.yml +9 -0
  347. data/spec/support/retryable_writes_tests/updateOne-serverErrors.yml +58 -0
  348. data/spec/support/retryable_writes_tests/updateOne.yml +71 -53
  349. data/spec/support/sdam/rs/normalize_case_me.yml +100 -0
  350. data/spec/support/sdam/sharded/compatible.yml +38 -0
  351. data/spec/support/sdam/sharded/mongos_disconnect.yml +9 -3
  352. data/spec/support/sdam/sharded/multiple_mongoses.yml +6 -2
  353. data/spec/support/sdam/sharded/non_mongos_removed.yml +6 -2
  354. data/spec/support/sdam/sharded/too_new.yml +36 -0
  355. data/spec/support/sdam/sharded/too_old.yml +36 -0
  356. data/spec/support/sdam/single/compatible.yml +26 -0
  357. data/spec/support/sdam/single/direct_connection_external_ip.yml +3 -1
  358. data/spec/support/sdam/single/direct_connection_mongos.yml +3 -1
  359. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +3 -1
  360. data/spec/support/sdam/single/direct_connection_rsprimary.yml +3 -1
  361. data/spec/support/sdam/single/direct_connection_rssecondary.yml +3 -1
  362. data/spec/support/sdam/single/direct_connection_slave.yml +3 -1
  363. data/spec/support/sdam/single/direct_connection_standalone.yml +3 -1
  364. data/spec/support/sdam/single/not_ok_response.yml +6 -2
  365. data/spec/support/sdam/single/standalone_removed.yml +3 -1
  366. data/spec/support/sdam/single/too_new.yml +26 -0
  367. data/spec/support/sdam/single/too_old.yml +24 -0
  368. data/spec/support/shared/session.rb +107 -0
  369. data/spec/support/transactions.rb +391 -0
  370. data/spec/support/transactions/operation.rb +373 -0
  371. data/spec/support/transactions_tests/abort.yml +403 -0
  372. data/spec/support/transactions_tests/bulk.yml +267 -0
  373. data/spec/support/transactions_tests/causal-consistency.yml +173 -0
  374. data/spec/support/transactions_tests/commit.yml +593 -0
  375. data/spec/support/transactions_tests/delete.yml +184 -0
  376. data/spec/support/transactions_tests/error-labels.yml +948 -0
  377. data/spec/support/transactions_tests/errors.yml +125 -0
  378. data/spec/support/transactions_tests/findOneAndDelete.yml +126 -0
  379. data/spec/support/transactions_tests/findOneAndReplace.yml +140 -0
  380. data/spec/support/transactions_tests/findOneAndUpdate.yml +228 -0
  381. data/spec/support/transactions_tests/insert.yml +264 -0
  382. data/spec/support/transactions_tests/isolation.yml +125 -0
  383. data/spec/support/transactions_tests/read-pref.yml +340 -0
  384. data/spec/support/transactions_tests/reads.yml +298 -0
  385. data/spec/support/transactions_tests/retryable-abort.yml +1292 -0
  386. data/spec/support/transactions_tests/retryable-commit.yml +1332 -0
  387. data/spec/support/transactions_tests/retryable-writes.yml +208 -0
  388. data/spec/support/transactions_tests/run-command.yml +189 -0
  389. data/spec/support/transactions_tests/transaction-options.yml +877 -0
  390. data/spec/support/transactions_tests/update.yml +246 -0
  391. data/spec/support/transactions_tests/write-concern.yml +236 -0
  392. metadata +494 -359
  393. metadata.gz.sig +0 -0
  394. data/lib/csasl/csasl.bundle +0 -0
@@ -95,4 +95,131 @@ describe Mongo::Error::Parser do
95
95
  end
96
96
  end
97
97
  end
98
+
99
+ describe '#code' do
100
+ let(:parser) do
101
+ described_class.new(document)
102
+ end
103
+
104
+ context 'when document contains code' do
105
+ let(:document) do
106
+ { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
107
+ end
108
+
109
+ it 'returns the code' do
110
+ expect(parser.code).to eq(10107)
111
+ end
112
+ end
113
+
114
+ context 'when document does not contain code' do
115
+ let(:document) do
116
+ { 'ok' => 0, 'errmsg' => 'not master' }
117
+ end
118
+
119
+ it 'returns nil' do
120
+ expect(parser.code).to eq(nil)
121
+ end
122
+ end
123
+
124
+ context 'when the document contains a writeConcernError with a code' do
125
+
126
+ let(:document) do
127
+ { 'writeConcernError' => { 'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
128
+ end
129
+
130
+ it 'returns the code' do
131
+ expect(parser.code).to eq(100)
132
+ end
133
+ end
134
+
135
+ context 'when the document contains a writeConcernError without a code' do
136
+
137
+ let(:document) do
138
+ { 'writeConcernError' => { 'errmsg' => 'Not enough data-bearing nodes' } }
139
+ end
140
+
141
+ it 'returns nil' do
142
+ expect(parser.code).to be nil
143
+ end
144
+ end
145
+ end
146
+
147
+ describe '#code_name' do
148
+ let(:parser) do
149
+ described_class.new(document)
150
+ end
151
+
152
+ context 'when document contains code name' do
153
+ let(:document) do
154
+ { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
155
+ end
156
+
157
+ it 'returns the code name' do
158
+ expect(parser.code_name).to eq('NotMaster')
159
+ end
160
+ end
161
+
162
+ context 'when document does not contain code name' do
163
+ let(:document) do
164
+ { 'ok' => 0, 'errmsg' => 'not master' }
165
+ end
166
+
167
+ it 'returns nil' do
168
+ expect(parser.code_name).to eq(nil)
169
+ end
170
+ end
171
+
172
+ context 'when the document contains a writeConcernError with a code' do
173
+
174
+ let(:document) do
175
+ { 'writeConcernError' => { 'code' => 100, 'codeName' => 'CannotSatisfyWriteConcern',
176
+ 'errmsg' => 'Not enough data-bearing nodes' } }
177
+ end
178
+
179
+ it 'returns the code name' do
180
+ expect(parser.code_name).to eq('CannotSatisfyWriteConcern')
181
+ end
182
+ end
183
+
184
+ context 'when the document contains a writeConcernError without a code' do
185
+
186
+ let(:document) do
187
+ { 'writeConcernError' => { 'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
188
+ end
189
+
190
+ it 'returns nil' do
191
+ expect(parser.code_name).to be nil
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '#document' do
197
+ let(:parser) do
198
+ described_class.new(document)
199
+ end
200
+
201
+ let(:document) do
202
+ { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
203
+ end
204
+
205
+ it 'returns the document' do
206
+ expect(parser.document).to eq(document)
207
+ end
208
+ end
209
+
210
+ describe '#replies' do
211
+ let(:parser) do
212
+ described_class.new(document)
213
+ end
214
+
215
+ context 'when there are no replies' do
216
+ let(:document) do
217
+ { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
218
+ end
219
+
220
+ it 'returns nil' do
221
+ expect(parser.replies).to eq(nil)
222
+ end
223
+ end
224
+ end
98
225
  end
@@ -98,6 +98,38 @@ describe Mongo::Grid::FSBucket do
98
98
  expect(stream.write_concern.options).to eq(Mongo::WriteConcern.get(options[:write]).options)
99
99
  end
100
100
  end
101
+
102
+ context 'when disable_md5 is not specified' do
103
+
104
+ it 'does not set the option on the write stream' do
105
+ expect(stream.options[:disable_md5]).to be_nil
106
+ end
107
+ end
108
+
109
+ context 'when disable_md5 is specified' do
110
+
111
+ context 'when disable_md5 is true' do
112
+
113
+ let(:options) do
114
+ { disable_md5: true }
115
+ end
116
+
117
+ it 'passes the option to the write stream' do
118
+ expect(stream.options[:disable_md5]).to be(true)
119
+ end
120
+ end
121
+
122
+ context 'when disable_md5 is false' do
123
+
124
+ let(:options) do
125
+ { disable_md5: false }
126
+ end
127
+
128
+ it 'passes the option to the write stream' do
129
+ expect(stream.options[:disable_md5]).to be(false)
130
+ end
131
+ end
132
+ end
101
133
  end
102
134
  end
103
135
  end
@@ -27,7 +27,7 @@ describe Mongo::Grid::FSBucket::Stream::Write do
27
27
  end
28
28
 
29
29
  let(:options) do
30
- { filename: filename }.merge(extra_options)
30
+ { filename: filename }.merge(extra_options).merge(fs.options)
31
31
  end
32
32
 
33
33
  after do
@@ -53,6 +53,45 @@ describe Mongo::Grid::FSBucket::Stream::Write do
53
53
  expect(stream.close).to be_a(BSON::ObjectId)
54
54
  end
55
55
 
56
+ context 'when the fs does not have disable_md5 specified' do
57
+
58
+ it 'sets an md5 for the file' do
59
+ stream.send(:file_info).to_bson
60
+ expect(stream.send(:file_info).document[:md5].size).to eq(32)
61
+ end
62
+ end
63
+
64
+ context 'when the fs has disable_md5 specified' do
65
+
66
+ before do
67
+ stream.send(:file_info).to_bson
68
+ end
69
+
70
+ context 'when disable_md5 is true' do
71
+
72
+ let(:fs_options) do
73
+ { disable_md5: true }
74
+ end
75
+
76
+ it 'does not set an md5 for the file' do
77
+ expect(stream.send(:file_info).document.has_key?(:md5)). to be(false)
78
+ expect(stream.send(:file_info).document[:md5]). to be_nil
79
+ end
80
+ end
81
+
82
+ context 'when disabled_md5 is false' do
83
+
84
+ let(:fs_options) do
85
+ { disable_md5: false }
86
+ end
87
+
88
+ it 'sets an md5 for the file' do
89
+ stream.send(:file_info).to_bson
90
+ expect(stream.send(:file_info).document[:md5].size).to eq(32)
91
+ end
92
+ end
93
+ end
94
+
56
95
  context 'when the fs has a write concern', if: standalone? do
57
96
 
58
97
  let(:fs_options) do
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Monitoring::Event::CommandFailed do
4
+
5
+ let(:address) do
6
+ Mongo::Address.new('127.0.0.1:27017')
7
+ end
8
+
9
+ describe '#command_name' do
10
+ context 'when command_name is given as a string' do
11
+ let(:event) do
12
+ described_class.new('find', 'admin', address, 1, 2, 'Uh oh', nil, 0.5)
13
+ end
14
+
15
+ it 'is a string' do
16
+ expect(event.command_name).to eql('find')
17
+ end
18
+ end
19
+
20
+ context 'when command_name is given as a symbol' do
21
+ let(:event) do
22
+ described_class.new(:find, 'admin', address, 1, 2, 'Uh oh', nil, 0.5)
23
+ end
24
+
25
+ it 'is a string' do
26
+ expect(event.command_name).to eql('find')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -2,11 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongo::Monitoring::Event::CommandStarted do
4
4
 
5
- describe '#initialize' do
5
+ let(:address) do
6
+ Mongo::Address.new('127.0.0.1:27017')
7
+ end
6
8
 
7
- let(:address) do
8
- Mongo::Address.new('127.0.0.1:27017')
9
- end
9
+ describe '#initialize' do
10
10
 
11
11
  let(:command) do
12
12
  BSON::Document.new(test: 'value')
@@ -23,4 +23,26 @@ describe Mongo::Monitoring::Event::CommandStarted do
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ describe '#command_name' do
28
+ context 'when command_name is given as a string' do
29
+ let(:event) do
30
+ described_class.new('find', 'admin', address, 1, 2, {})
31
+ end
32
+
33
+ it 'is a string' do
34
+ expect(event.command_name).to eql('find')
35
+ end
36
+ end
37
+
38
+ context 'when command_name is given as a symbol' do
39
+ let(:event) do
40
+ described_class.new(:find, 'admin', address, 1, 2, {})
41
+ end
42
+
43
+ it 'is a string' do
44
+ expect(event.command_name).to eql('find')
45
+ end
46
+ end
47
+ end
26
48
  end
@@ -2,15 +2,15 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongo::Monitoring::Event::CommandSucceeded do
4
4
 
5
- describe '#initialize' do
5
+ let(:address) do
6
+ Mongo::Address.new('127.0.0.1:27017')
7
+ end
6
8
 
7
- let(:address) do
8
- Mongo::Address.new('127.0.0.1:27017')
9
- end
9
+ let(:reply) do
10
+ BSON::Document.new(test: 'value')
11
+ end
10
12
 
11
- let(:reply) do
12
- BSON::Document.new(test: 'value')
13
- end
13
+ describe '#initialize' do
14
14
 
15
15
  context 'when the reply should be redacted' do
16
16
 
@@ -23,4 +23,26 @@ describe Mongo::Monitoring::Event::CommandSucceeded do
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ describe '#command_name' do
28
+ context 'when command_name is given as a string' do
29
+ let(:event) do
30
+ described_class.new('find', 'admin', address, 1, 2, reply, 0.5)
31
+ end
32
+
33
+ it 'is a string' do
34
+ expect(event.command_name).to eql('find')
35
+ end
36
+ end
37
+
38
+ context 'when command_name is given as a symbol' do
39
+ let(:event) do
40
+ described_class.new(:find, 'admin', address, 1, 2, reply, 0.5)
41
+ end
42
+
43
+ it 'is a string' do
44
+ expect(event.command_name).to eql('find')
45
+ end
46
+ end
47
+ end
26
48
  end
@@ -85,12 +85,37 @@ describe Mongo::Monitoring do
85
85
  double('subscriber')
86
86
  end
87
87
 
88
- before do
88
+ it 'subscribes to the topic' do
89
89
  monitoring.subscribe('topic', subscriber)
90
+ expect(monitoring.subscribers['topic']).to eq([ subscriber ])
90
91
  end
91
92
 
92
- it 'subscribes to the topic' do
93
- expect(monitoring.subscribers['topic']).to eq([ subscriber ])
93
+ it 'subscribes to the topic twice' do
94
+ monitoring.subscribe('topic', subscriber)
95
+ monitoring.subscribe('topic', subscriber)
96
+ expect(monitoring.subscribers['topic']).to eq([ subscriber, subscriber ])
97
+ end
98
+ end
99
+
100
+ describe '#unsubscribe' do
101
+
102
+ let(:monitoring) do
103
+ described_class.new(monitoring: false)
104
+ end
105
+
106
+ let(:subscriber) do
107
+ double('subscriber')
108
+ end
109
+
110
+ it 'unsubscribes from the topic' do
111
+ monitoring.subscribe('topic', subscriber)
112
+ monitoring.unsubscribe('topic', subscriber)
113
+ expect(monitoring.subscribers['topic']).to eq([ ])
114
+ end
115
+
116
+ it 'unsubscribes from the topic when not subscribed' do
117
+ monitoring.unsubscribe('topic', subscriber)
118
+ expect(monitoring.subscribers['topic']).to eq([ ])
94
119
  end
95
120
  end
96
121
 
@@ -30,7 +30,7 @@ describe Mongo::Protocol::GetMore do
30
30
 
31
31
  describe '#==' do
32
32
 
33
- context 'when the other is a getmore' do
33
+ context 'when the other is a getMore' do
34
34
 
35
35
  context 'when the fields are equal' do
36
36
  let(:other) do
@@ -83,7 +83,7 @@ describe Mongo::Protocol::GetMore do
83
83
  end
84
84
  end
85
85
 
86
- context 'when the other is not a getmore' do
86
+ context 'when the other is not a getMore' do
87
87
  let(:other) do
88
88
  expect(message).not_to eq('test')
89
89
  end
@@ -1,41 +1,74 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Mongo::Retryable do
3
+ class RetryableTestConsumer
4
+ include Mongo::Retryable
4
5
 
5
- let(:klass) do
6
- Class.new do
7
- include Mongo::Retryable
6
+ attr_reader :cluster
7
+ attr_reader :operation
8
8
 
9
- attr_reader :cluster
10
- attr_reader :operation
9
+ def initialize(operation, cluster)
10
+ @operation = operation
11
+ @cluster = cluster
12
+ end
11
13
 
12
- def initialize(operation, cluster)
13
- @operation = operation
14
- @cluster = cluster
15
- end
14
+ def max_read_retries
15
+ cluster.max_read_retries
16
+ end
16
17
 
17
- def max_read_retries
18
- cluster.max_read_retries
19
- end
18
+ def read_retry_interval
19
+ cluster.read_retry_interval
20
+ end
20
21
 
21
- def read_retry_interval
22
- cluster.read_retry_interval
23
- end
22
+ def read
23
+ read_with_retry do
24
+ operation.execute
25
+ end
26
+ end
24
27
 
25
- def read
26
- read_with_retry do
27
- operation.execute
28
- end
29
- end
28
+ def write
29
+ # This passes a nil session and therefore triggers
30
+ # legacy_write_with_retry code path
31
+ write_with_retry(session, write_concern) do
32
+ operation.execute
33
+ end
34
+ end
30
35
 
31
- def write
32
- write_with_retry(nil, nil) do
33
- operation.execute
34
- end
35
- end
36
+ def retry_write_allowed_as_configured?
37
+ retry_write_allowed?(session, write_concern)
38
+ end
39
+ end
40
+
41
+ class LegacyRetryableTestConsumer < RetryableTestConsumer
42
+ def session
43
+ nil
44
+ end
45
+
46
+ def write_concern
47
+ nil
48
+ end
49
+ end
50
+
51
+ class ModernRetryableTestConsumer < LegacyRetryableTestConsumer
52
+ include RSpec::Mocks::ExampleMethods
53
+
54
+ def session
55
+ double('session').tap do |session|
56
+ expect(session).to receive(:retry_writes?).and_return(true)
57
+
58
+ # mock everything else that is in the way
59
+ i = 1
60
+ allow(session).to receive(:next_txn_num) { i += 1 }
61
+ allow(session).to receive(:in_transaction?).and_return(false)
36
62
  end
37
63
  end
38
64
 
65
+ def write_concern
66
+ nil
67
+ end
68
+ end
69
+
70
+ describe Mongo::Retryable do
71
+
39
72
  let(:operation) do
40
73
  double('operation')
41
74
  end
@@ -49,7 +82,7 @@ describe Mongo::Retryable do
49
82
  end
50
83
 
51
84
  let(:retryable) do
52
- klass.new(operation, cluster)
85
+ LegacyRetryableTestConsumer.new(operation, cluster)
53
86
  end
54
87
 
55
88
  describe '#read_with_retry' do
@@ -132,7 +165,7 @@ describe Mongo::Retryable do
132
165
  context 'when the operation failure is retryable' do
133
166
 
134
167
  let(:error) do
135
- Mongo::Error::OperationFailure.new('no master')
168
+ Mongo::Error::OperationFailure.new('not master')
136
169
  end
137
170
 
138
171
  context 'when the retry succeeds' do
@@ -173,7 +206,12 @@ describe Mongo::Retryable do
173
206
  end
174
207
  end
175
208
 
176
- describe '#write_with_retry' do
209
+ describe '#write_with_retry - legacy' do
210
+
211
+ before do
212
+ # Quick sanity check that the expected code path is being exercised
213
+ expect(retryable.retry_write_allowed_as_configured?).to be false
214
+ end
177
215
 
178
216
  context 'when no exception occurs' do
179
217
 
@@ -186,32 +224,174 @@ describe Mongo::Retryable do
186
224
  end
187
225
  end
188
226
 
227
+ shared_examples 'executes the operation twice' do
228
+ it 'executes the operation twice' do
229
+ expect(retryable.write).to be true
230
+ end
231
+ end
232
+
189
233
  context 'when a not master error occurs' do
190
234
 
191
235
  before do
192
- expect(operation).to receive(:execute).and_raise(Mongo::Error::OperationFailure.new('not master')).ordered
236
+ expect(operation).to receive(:execute).and_raise(
237
+ Mongo::Error::OperationFailure.new('not master')).ordered
193
238
  expect(cluster).to receive(:scan!).and_return(true).ordered
194
239
  expect(operation).to receive(:execute).and_return(true).ordered
195
240
  end
196
241
 
197
- it 'executes the operation twice' do
198
- expect(retryable.write).to be true
242
+ it_behaves_like 'executes the operation twice'
243
+ end
244
+
245
+ context 'when a node is recovering error occurs' do
246
+
247
+ before do
248
+ expect(operation).to receive(:execute).and_raise(
249
+ Mongo::Error::OperationFailure.new('node is recovering')).ordered
250
+ expect(cluster).to receive(:scan!).and_return(true).ordered
251
+ expect(operation).to receive(:execute).and_return(true).ordered
199
252
  end
253
+
254
+ it_behaves_like 'executes the operation twice'
200
255
  end
201
256
 
202
- context 'when a not primary error occurs' do
257
+ context 'when a retryable error occurs with a code' do
203
258
 
204
259
  before do
205
- expect(operation).to receive(:execute).and_raise(Mongo::Error::OperationFailure.new('Not primary')).ordered
260
+ expect(operation).to receive(:execute).and_raise(
261
+ Mongo::Error::OperationFailure.new('message missing', nil,
262
+ :code => 91, :code_name => 'ShutdownInProgress')).ordered
206
263
  expect(cluster).to receive(:scan!).and_return(true).ordered
207
264
  expect(operation).to receive(:execute).and_return(true).ordered
208
265
  end
209
266
 
267
+ it_behaves_like 'executes the operation twice'
268
+ end
269
+
270
+ context 'when a normal operation failure occurs' do
271
+
272
+ before do
273
+ expect(operation).to receive(:execute).and_raise(Mongo::Error::OperationFailure).ordered
274
+ end
275
+
276
+ it 'raises an exception' do
277
+ expect {
278
+ retryable.write
279
+ }.to raise_error(Mongo::Error::OperationFailure)
280
+ end
281
+ end
282
+
283
+ context 'when a socket error occurs' do
284
+
285
+ before do
286
+ expect(operation).to receive(:execute).and_raise(
287
+ Mongo::Error::SocketError.new('socket error')).ordered
288
+ end
289
+
290
+ it 'raises an exception' do
291
+ expect {
292
+ retryable.write
293
+ }.to raise_error(Mongo::Error::SocketError)
294
+ end
295
+ end
296
+
297
+ context 'when a socket timeout occurs' do
298
+
299
+ before do
300
+ expect(operation).to receive(:execute).and_raise(
301
+ Mongo::Error::SocketTimeoutError.new('socket timeout')).ordered
302
+ end
303
+
304
+ it 'raises an exception' do
305
+ expect {
306
+ retryable.write
307
+ }.to raise_error(Mongo::Error::SocketTimeoutError)
308
+ end
309
+ end
310
+
311
+ context 'when a non-retryable exception occurs' do
312
+
313
+ before do
314
+ expect(operation).to receive(:execute).and_raise(
315
+ Mongo::Error::UnsupportedCollation.new('unsupported collation')).ordered
316
+ end
317
+
318
+ it 'raises an exception' do
319
+ expect {
320
+ retryable.write
321
+ }.to raise_error(Mongo::Error::UnsupportedCollation)
322
+ end
323
+ end
324
+
325
+ end
326
+
327
+ describe '#write_with_retry - modern' do
328
+
329
+ let(:retryable) do
330
+ ModernRetryableTestConsumer.new(operation, cluster)
331
+ end
332
+
333
+ before do
334
+ # Quick sanity check that the expected code path is being exercised
335
+ expect(retryable.retry_write_allowed_as_configured?).to be true
336
+
337
+ allow(server_selector).to receive(:retry_writes?).and_return(true)
338
+ allow(cluster).to receive(:scan!)
339
+ end
340
+
341
+ context 'when no exception occurs' do
342
+
343
+ before do
344
+ expect(operation).to receive(:execute).and_return(true)
345
+ end
346
+
347
+ it 'executes the operation once' do
348
+ expect(retryable.write).to be true
349
+ end
350
+ end
351
+
352
+ shared_examples 'executes the operation twice' do
210
353
  it 'executes the operation twice' do
211
354
  expect(retryable.write).to be true
212
355
  end
213
356
  end
214
357
 
358
+ context 'when a not master error occurs' do
359
+
360
+ before do
361
+ expect(operation).to receive(:execute).and_raise(
362
+ Mongo::Error::OperationFailure.new('not master')).ordered
363
+ expect(cluster).to receive(:scan!).and_return(true).ordered
364
+ expect(operation).to receive(:execute).and_return(true).ordered
365
+ end
366
+
367
+ it_behaves_like 'executes the operation twice'
368
+ end
369
+
370
+ context 'when a node is recovering error occurs' do
371
+
372
+ before do
373
+ expect(operation).to receive(:execute).and_raise(
374
+ Mongo::Error::OperationFailure.new('node is recovering')).ordered
375
+ expect(cluster).to receive(:scan!).and_return(true).ordered
376
+ expect(operation).to receive(:execute).and_return(true).ordered
377
+ end
378
+
379
+ it_behaves_like 'executes the operation twice'
380
+ end
381
+
382
+ context 'when a retryable error occurs with a code' do
383
+
384
+ before do
385
+ expect(operation).to receive(:execute).and_raise(
386
+ Mongo::Error::OperationFailure.new('message missing', nil,
387
+ :code => 91, :code_name => 'ShutdownInProgress')).ordered
388
+ expect(cluster).to receive(:scan!).and_return(true).ordered
389
+ expect(operation).to receive(:execute).and_return(true).ordered
390
+ end
391
+
392
+ it_behaves_like 'executes the operation twice'
393
+ end
394
+
215
395
  context 'when a normal operation failure occurs' do
216
396
 
217
397
  before do
@@ -225,5 +405,43 @@ describe Mongo::Retryable do
225
405
  end
226
406
  end
227
407
 
408
+ context 'when a socket error occurs' do
409
+
410
+ before do
411
+ expect(operation).to receive(:execute).and_raise(
412
+ Mongo::Error::SocketError.new('socket error')).ordered
413
+ expect(cluster).to receive(:scan!).and_return(true).ordered
414
+ expect(operation).to receive(:execute).and_return(true).ordered
415
+ end
416
+
417
+ it_behaves_like 'executes the operation twice'
418
+ end
419
+
420
+ context 'when a socket timeout occurs' do
421
+
422
+ before do
423
+ expect(operation).to receive(:execute).and_raise(
424
+ Mongo::Error::SocketTimeoutError.new('socket timeout')).ordered
425
+ expect(cluster).to receive(:scan!).and_return(true).ordered
426
+ expect(operation).to receive(:execute).and_return(true).ordered
427
+ end
428
+
429
+ it_behaves_like 'executes the operation twice'
430
+ end
431
+
432
+ context 'when a non-retryable exception occurs' do
433
+
434
+ before do
435
+ expect(operation).to receive(:execute).and_raise(
436
+ Mongo::Error::UnsupportedCollation.new('unsupported collation')).ordered
437
+ end
438
+
439
+ it 'raises an exception' do
440
+ expect {
441
+ retryable.write
442
+ }.to raise_error(Mongo::Error::UnsupportedCollation)
443
+ end
444
+ end
445
+
228
446
  end
229
447
  end