mongo 2.14.0 → 2.16.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 (1225) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +5 -2
  4. data/Rakefile +8 -15
  5. data/lib/mongo/active_support.rb +3 -0
  6. data/lib/mongo/address/ipv4.rb +3 -0
  7. data/lib/mongo/address/ipv6.rb +3 -0
  8. data/lib/mongo/address/unix.rb +3 -0
  9. data/lib/mongo/address/validator.rb +3 -0
  10. data/lib/mongo/address.rb +3 -0
  11. data/lib/mongo/auth/aws/conversation.rb +4 -4
  12. data/lib/mongo/auth/aws/credentials_retriever.rb +3 -0
  13. data/lib/mongo/auth/aws/request.rb +3 -0
  14. data/lib/mongo/auth/aws.rb +3 -0
  15. data/lib/mongo/auth/base.rb +17 -8
  16. data/lib/mongo/auth/conversation_base.rb +35 -0
  17. data/lib/mongo/auth/cr/conversation.rb +9 -29
  18. data/lib/mongo/auth/cr.rb +3 -0
  19. data/lib/mongo/auth/credential_cache.rb +3 -0
  20. data/lib/mongo/auth/gssapi/conversation.rb +7 -15
  21. data/lib/mongo/auth/gssapi.rb +3 -0
  22. data/lib/mongo/auth/ldap/conversation.rb +6 -14
  23. data/lib/mongo/auth/ldap.rb +3 -0
  24. data/lib/mongo/auth/roles.rb +3 -0
  25. data/lib/mongo/auth/sasl_conversation_base.rb +4 -13
  26. data/lib/mongo/auth/scram/conversation.rb +3 -0
  27. data/lib/mongo/auth/scram.rb +5 -2
  28. data/lib/mongo/auth/scram256/conversation.rb +3 -0
  29. data/lib/mongo/auth/scram256.rb +3 -0
  30. data/lib/mongo/auth/scram_conversation_base.rb +10 -34
  31. data/lib/mongo/auth/stringprep/profiles/sasl.rb +3 -0
  32. data/lib/mongo/auth/stringprep/tables.rb +3 -0
  33. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +2 -2
  34. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1 -1
  35. data/lib/mongo/auth/stringprep.rb +4 -1
  36. data/lib/mongo/auth/user/view.rb +19 -9
  37. data/lib/mongo/auth/user.rb +3 -0
  38. data/lib/mongo/auth/x509/conversation.rb +7 -25
  39. data/lib/mongo/auth/x509.rb +3 -0
  40. data/lib/mongo/auth.rb +4 -1
  41. data/lib/mongo/background_thread.rb +16 -2
  42. data/lib/mongo/bson.rb +3 -0
  43. data/lib/mongo/bulk_write/combineable.rb +3 -0
  44. data/lib/mongo/bulk_write/ordered_combiner.rb +3 -0
  45. data/lib/mongo/bulk_write/result.rb +3 -0
  46. data/lib/mongo/bulk_write/result_combiner.rb +3 -0
  47. data/lib/mongo/bulk_write/transformable.rb +3 -0
  48. data/lib/mongo/bulk_write/unordered_combiner.rb +3 -0
  49. data/lib/mongo/bulk_write/validatable.rb +3 -0
  50. data/lib/mongo/bulk_write.rb +26 -20
  51. data/lib/mongo/caching_cursor.rb +3 -0
  52. data/lib/mongo/client.rb +131 -12
  53. data/lib/mongo/client_encryption.rb +3 -0
  54. data/lib/mongo/cluster/periodic_executor.rb +7 -3
  55. data/lib/mongo/cluster/reapers/cursor_reaper.rb +80 -40
  56. data/lib/mongo/cluster/reapers/socket_reaper.rb +3 -0
  57. data/lib/mongo/cluster/sdam_flow.rb +26 -3
  58. data/lib/mongo/cluster/topology/base.rb +16 -9
  59. data/lib/mongo/cluster/topology/load_balanced.rb +102 -0
  60. data/lib/mongo/cluster/topology/no_replica_set_options.rb +3 -0
  61. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -0
  62. data/lib/mongo/cluster/topology/replica_set_with_primary.rb +3 -0
  63. data/lib/mongo/cluster/topology/sharded.rb +3 -0
  64. data/lib/mongo/cluster/topology/single.rb +3 -0
  65. data/lib/mongo/cluster/topology/unknown.rb +3 -0
  66. data/lib/mongo/cluster/topology.rb +31 -8
  67. data/lib/mongo/cluster.rb +156 -77
  68. data/lib/mongo/cluster_time.rb +3 -0
  69. data/lib/mongo/collection/view/aggregation.rb +9 -11
  70. data/lib/mongo/collection/view/builder/aggregation.rb +9 -5
  71. data/lib/mongo/collection/view/builder/map_reduce.rb +15 -49
  72. data/lib/mongo/collection/view/builder.rb +3 -4
  73. data/lib/mongo/collection/view/change_stream/retryable.rb +3 -0
  74. data/lib/mongo/collection/view/change_stream.rb +5 -2
  75. data/lib/mongo/collection/view/explainable.rb +3 -0
  76. data/lib/mongo/collection/view/immutable.rb +3 -0
  77. data/lib/mongo/collection/view/iterable.rb +62 -25
  78. data/lib/mongo/collection/view/map_reduce.rb +44 -17
  79. data/lib/mongo/collection/view/readable.rb +105 -65
  80. data/lib/mongo/collection/view/writable.rb +187 -181
  81. data/lib/mongo/collection/view.rb +18 -21
  82. data/lib/mongo/collection.rb +54 -28
  83. data/lib/mongo/crypt/auto_decryption_context.rb +3 -0
  84. data/lib/mongo/crypt/auto_encrypter.rb +4 -1
  85. data/lib/mongo/crypt/auto_encryption_context.rb +3 -0
  86. data/lib/mongo/crypt/binary.rb +3 -0
  87. data/lib/mongo/crypt/binding.rb +4 -1
  88. data/lib/mongo/crypt/context.rb +3 -0
  89. data/lib/mongo/crypt/data_key_context.rb +3 -0
  90. data/lib/mongo/crypt/encryption_io.rb +3 -0
  91. data/lib/mongo/crypt/explicit_decryption_context.rb +3 -0
  92. data/lib/mongo/crypt/explicit_encrypter.rb +3 -0
  93. data/lib/mongo/crypt/explicit_encryption_context.rb +3 -0
  94. data/lib/mongo/crypt/handle.rb +3 -0
  95. data/lib/mongo/crypt/hooks.rb +3 -0
  96. data/lib/mongo/crypt/kms_context.rb +3 -0
  97. data/lib/mongo/crypt/status.rb +3 -0
  98. data/lib/mongo/crypt.rb +3 -0
  99. data/lib/mongo/cursor/kill_spec.rb +38 -0
  100. data/lib/mongo/cursor.rb +76 -31
  101. data/lib/mongo/database/view.rb +5 -2
  102. data/lib/mongo/database.rb +26 -6
  103. data/lib/mongo/dbref.rb +3 -0
  104. data/lib/mongo/distinguishing_semaphore.rb +3 -0
  105. data/lib/mongo/error/auth_error.rb +3 -0
  106. data/lib/mongo/error/bad_load_balancer_target.rb +26 -0
  107. data/lib/mongo/error/bulk_write_error.rb +20 -3
  108. data/lib/mongo/error/change_stream_resumable.rb +3 -0
  109. data/lib/mongo/error/closed_stream.rb +3 -0
  110. data/lib/mongo/error/connection_check_out_timeout.rb +3 -0
  111. data/lib/mongo/error/connection_perished.rb +3 -0
  112. data/lib/mongo/error/credential_check_error.rb +3 -0
  113. data/lib/mongo/error/crypt_error.rb +3 -0
  114. data/lib/mongo/error/extra_file_chunk.rb +3 -0
  115. data/lib/mongo/error/failed_string_prep_validation.rb +3 -0
  116. data/lib/mongo/error/file_not_found.rb +3 -0
  117. data/lib/mongo/error/handshake_error.rb +3 -0
  118. data/lib/mongo/error/insufficient_iteration_count.rb +3 -0
  119. data/lib/mongo/error/internal_driver_error.rb +25 -0
  120. data/lib/mongo/error/invalid_address.rb +3 -0
  121. data/lib/mongo/error/invalid_application_name.rb +3 -0
  122. data/lib/mongo/error/invalid_bulk_operation.rb +3 -0
  123. data/lib/mongo/error/invalid_bulk_operation_type.rb +3 -0
  124. data/lib/mongo/error/invalid_collection_name.rb +3 -0
  125. data/lib/mongo/error/invalid_cursor_operation.rb +3 -0
  126. data/lib/mongo/error/invalid_database_name.rb +3 -0
  127. data/lib/mongo/error/invalid_document.rb +3 -0
  128. data/lib/mongo/error/invalid_file.rb +3 -0
  129. data/lib/mongo/error/invalid_file_revision.rb +3 -0
  130. data/lib/mongo/error/invalid_min_pool_size.rb +3 -0
  131. data/lib/mongo/error/invalid_nonce.rb +3 -0
  132. data/lib/mongo/error/invalid_read_concern.rb +3 -0
  133. data/lib/mongo/error/invalid_read_option.rb +3 -0
  134. data/lib/mongo/error/invalid_replacement_document.rb +3 -0
  135. data/lib/mongo/error/invalid_server_auth_host.rb +3 -0
  136. data/lib/mongo/error/invalid_server_auth_response.rb +3 -0
  137. data/lib/mongo/error/invalid_server_preference.rb +3 -0
  138. data/lib/mongo/error/invalid_session.rb +3 -0
  139. data/lib/mongo/error/invalid_signature.rb +3 -0
  140. data/lib/mongo/error/invalid_transaction_operation.rb +3 -0
  141. data/lib/mongo/error/invalid_txt_record.rb +3 -0
  142. data/lib/mongo/error/invalid_update_document.rb +3 -0
  143. data/lib/mongo/error/invalid_uri.rb +3 -0
  144. data/lib/mongo/error/invalid_write_concern.rb +3 -0
  145. data/lib/mongo/error/kms_error.rb +3 -0
  146. data/lib/mongo/error/lint_error.rb +3 -0
  147. data/lib/mongo/error/max_bson_size.rb +3 -0
  148. data/lib/mongo/error/max_message_size.rb +3 -0
  149. data/lib/mongo/error/mismatched_domain.rb +3 -0
  150. data/lib/mongo/error/missing_file_chunk.rb +3 -0
  151. data/lib/mongo/error/missing_password.rb +3 -0
  152. data/lib/mongo/error/missing_resume_token.rb +3 -0
  153. data/lib/mongo/error/missing_scram_server_signature.rb +3 -0
  154. data/lib/mongo/error/missing_service_id.rb +26 -0
  155. data/lib/mongo/error/mongocryptd_spawn_error.rb +3 -0
  156. data/lib/mongo/error/multi_index_drop.rb +3 -0
  157. data/lib/mongo/error/need_primary_server.rb +3 -0
  158. data/lib/mongo/error/no_server_available.rb +3 -0
  159. data/lib/mongo/error/no_service_connection_available.rb +49 -0
  160. data/lib/mongo/error/no_srv_records.rb +3 -0
  161. data/lib/mongo/error/notable.rb +10 -0
  162. data/lib/mongo/error/operation_failure.rb +26 -4
  163. data/lib/mongo/error/parser.rb +69 -13
  164. data/lib/mongo/error/pool_closed_error.rb +3 -0
  165. data/lib/mongo/error/raise_original_error.rb +3 -0
  166. data/lib/mongo/error/sdam_error_detection.rb +16 -5
  167. data/lib/mongo/error/server_api_conflict.rb +26 -0
  168. data/lib/mongo/error/server_api_not_supported.rb +27 -0
  169. data/lib/mongo/error/server_certificate_revoked.rb +3 -0
  170. data/lib/mongo/error/session_ended.rb +3 -0
  171. data/lib/mongo/error/sessions_not_supported.rb +3 -0
  172. data/lib/mongo/error/socket_error.rb +3 -0
  173. data/lib/mongo/error/socket_timeout_error.rb +3 -0
  174. data/lib/mongo/error/unchangeable_collection_option.rb +3 -0
  175. data/lib/mongo/error/unexpected_chunk_length.rb +3 -0
  176. data/lib/mongo/error/unexpected_response.rb +3 -0
  177. data/lib/mongo/error/unknown_payload_type.rb +3 -0
  178. data/lib/mongo/error/unmet_dependency.rb +24 -0
  179. data/lib/mongo/error/unsupported_array_filters.rb +3 -0
  180. data/lib/mongo/error/unsupported_collation.rb +3 -0
  181. data/lib/mongo/error/unsupported_features.rb +3 -0
  182. data/lib/mongo/error/unsupported_message_type.rb +3 -0
  183. data/lib/mongo/error/unsupported_option.rb +3 -0
  184. data/lib/mongo/error/write_retryable.rb +3 -0
  185. data/lib/mongo/error.rb +15 -1
  186. data/lib/mongo/event/base.rb +3 -0
  187. data/lib/mongo/event/listeners.rb +3 -0
  188. data/lib/mongo/event/publisher.rb +3 -0
  189. data/lib/mongo/event/subscriber.rb +3 -0
  190. data/lib/mongo/event.rb +3 -0
  191. data/lib/mongo/grid/file/chunk.rb +4 -1
  192. data/lib/mongo/grid/file/info.rb +3 -0
  193. data/lib/mongo/grid/file.rb +3 -0
  194. data/lib/mongo/grid/fs_bucket.rb +86 -46
  195. data/lib/mongo/grid/stream/read.rb +22 -7
  196. data/lib/mongo/grid/stream/write.rb +3 -0
  197. data/lib/mongo/grid/stream.rb +3 -0
  198. data/lib/mongo/grid.rb +3 -0
  199. data/lib/mongo/id.rb +10 -5
  200. data/lib/mongo/index/view.rb +46 -52
  201. data/lib/mongo/index.rb +3 -0
  202. data/lib/mongo/lint.rb +3 -0
  203. data/lib/mongo/loggable.rb +3 -0
  204. data/lib/mongo/logger.rb +3 -0
  205. data/lib/mongo/monitoring/cmap_log_subscriber.rb +3 -0
  206. data/lib/mongo/monitoring/command_log_subscriber.rb +3 -0
  207. data/lib/mongo/monitoring/event/cmap/base.rb +3 -0
  208. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +3 -0
  209. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +3 -0
  210. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +3 -0
  211. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +3 -0
  212. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +3 -0
  213. data/lib/mongo/monitoring/event/cmap/connection_created.rb +3 -0
  214. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +3 -0
  215. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +10 -4
  216. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +3 -0
  217. data/lib/mongo/monitoring/event/cmap/pool_created.rb +3 -0
  218. data/lib/mongo/monitoring/event/cmap.rb +3 -0
  219. data/lib/mongo/monitoring/event/command_failed.rb +32 -5
  220. data/lib/mongo/monitoring/event/command_started.rb +22 -2
  221. data/lib/mongo/monitoring/event/command_succeeded.rb +29 -3
  222. data/lib/mongo/monitoring/event/secure.rb +39 -5
  223. data/lib/mongo/monitoring/event/server_closed.rb +3 -0
  224. data/lib/mongo/monitoring/event/server_description_changed.rb +4 -1
  225. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +32 -18
  226. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +3 -0
  227. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +30 -16
  228. data/lib/mongo/monitoring/event/server_opening.rb +3 -0
  229. data/lib/mongo/monitoring/event/topology_changed.rb +3 -0
  230. data/lib/mongo/monitoring/event/topology_closed.rb +3 -0
  231. data/lib/mongo/monitoring/event/topology_opening.rb +3 -0
  232. data/lib/mongo/monitoring/event.rb +3 -0
  233. data/lib/mongo/monitoring/publishable.rb +34 -12
  234. data/lib/mongo/monitoring/sdam_log_subscriber.rb +3 -0
  235. data/lib/mongo/monitoring/server_closed_log_subscriber.rb +3 -0
  236. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +3 -0
  237. data/lib/mongo/monitoring/server_opening_log_subscriber.rb +3 -0
  238. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +3 -0
  239. data/lib/mongo/monitoring/topology_closed_log_subscriber.rb +3 -0
  240. data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +3 -0
  241. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +3 -0
  242. data/lib/mongo/monitoring.rb +20 -8
  243. data/lib/mongo/operation/aggregate/command.rb +11 -0
  244. data/lib/mongo/operation/aggregate/op_msg.rb +3 -0
  245. data/lib/mongo/operation/aggregate/result.rb +3 -0
  246. data/lib/mongo/operation/aggregate.rb +3 -0
  247. data/lib/mongo/operation/collections_info/command.rb +5 -2
  248. data/lib/mongo/operation/collections_info/result.rb +3 -0
  249. data/lib/mongo/operation/collections_info.rb +21 -1
  250. data/lib/mongo/operation/command/command.rb +3 -0
  251. data/lib/mongo/operation/command/op_msg.rb +3 -0
  252. data/lib/mongo/operation/command.rb +3 -0
  253. data/lib/mongo/operation/context.rb +120 -0
  254. data/lib/mongo/operation/count/command.rb +9 -0
  255. data/lib/mongo/operation/count/op_msg.rb +9 -0
  256. data/lib/mongo/operation/count.rb +3 -0
  257. data/lib/mongo/operation/create/command.rb +10 -1
  258. data/lib/mongo/operation/create/op_msg.rb +10 -0
  259. data/lib/mongo/operation/create.rb +3 -0
  260. data/lib/mongo/operation/create_index/command.rb +20 -1
  261. data/lib/mongo/operation/create_index/op_msg.rb +20 -4
  262. data/lib/mongo/operation/create_index.rb +3 -0
  263. data/lib/mongo/operation/create_user/command.rb +3 -0
  264. data/lib/mongo/operation/create_user/op_msg.rb +3 -0
  265. data/lib/mongo/operation/create_user.rb +3 -0
  266. data/lib/mongo/operation/delete/bulk_result.rb +3 -0
  267. data/lib/mongo/operation/delete/command.rb +9 -3
  268. data/lib/mongo/operation/delete/legacy.rb +12 -2
  269. data/lib/mongo/operation/delete/op_msg.rb +11 -1
  270. data/lib/mongo/operation/delete/result.rb +3 -0
  271. data/lib/mongo/operation/delete.rb +3 -0
  272. data/lib/mongo/operation/distinct/command.rb +9 -0
  273. data/lib/mongo/operation/distinct/op_msg.rb +10 -0
  274. data/lib/mongo/operation/distinct.rb +3 -0
  275. data/lib/mongo/operation/drop/command.rb +3 -0
  276. data/lib/mongo/operation/drop/op_msg.rb +3 -0
  277. data/lib/mongo/operation/drop.rb +3 -0
  278. data/lib/mongo/operation/drop_database/command.rb +3 -0
  279. data/lib/mongo/operation/drop_database/op_msg.rb +3 -0
  280. data/lib/mongo/operation/drop_database.rb +3 -0
  281. data/lib/mongo/operation/drop_index/command.rb +3 -0
  282. data/lib/mongo/operation/drop_index/op_msg.rb +3 -0
  283. data/lib/mongo/operation/drop_index.rb +3 -0
  284. data/lib/mongo/operation/explain/command.rb +16 -1
  285. data/lib/mongo/operation/explain/legacy.rb +15 -5
  286. data/lib/mongo/operation/explain/op_msg.rb +12 -1
  287. data/lib/mongo/operation/explain/result.rb +3 -0
  288. data/lib/mongo/operation/explain.rb +3 -0
  289. data/lib/mongo/operation/find/builder/command.rb +110 -0
  290. data/lib/mongo/{collection/view → operation/find}/builder/flags.rb +13 -14
  291. data/lib/mongo/operation/find/builder/legacy.rb +123 -0
  292. data/lib/mongo/{collection/view → operation/find}/builder/modifiers.rb +34 -25
  293. data/lib/mongo/{cursor → operation/find}/builder.rb +7 -4
  294. data/lib/mongo/operation/find/command.rb +12 -0
  295. data/lib/mongo/operation/find/legacy/result.rb +3 -0
  296. data/lib/mongo/operation/find/legacy.rb +13 -1
  297. data/lib/mongo/operation/find/op_msg.rb +15 -0
  298. data/lib/mongo/operation/find/result.rb +3 -0
  299. data/lib/mongo/operation/find.rb +4 -0
  300. data/lib/mongo/operation/get_more/command.rb +4 -0
  301. data/lib/mongo/operation/get_more/command_builder.rb +38 -0
  302. data/lib/mongo/operation/get_more/legacy.rb +3 -0
  303. data/lib/mongo/operation/get_more/op_msg.rb +4 -0
  304. data/lib/mongo/operation/get_more/result.rb +3 -0
  305. data/lib/mongo/operation/get_more.rb +4 -0
  306. data/lib/mongo/operation/indexes/command.rb +3 -0
  307. data/lib/mongo/operation/indexes/legacy.rb +3 -0
  308. data/lib/mongo/operation/indexes/op_msg.rb +3 -0
  309. data/lib/mongo/operation/indexes/result.rb +3 -0
  310. data/lib/mongo/operation/indexes.rb +18 -1
  311. data/lib/mongo/operation/insert/bulk_result.rb +3 -0
  312. data/lib/mongo/operation/insert/command.rb +5 -2
  313. data/lib/mongo/operation/insert/legacy.rb +5 -2
  314. data/lib/mongo/operation/insert/op_msg.rb +5 -2
  315. data/lib/mongo/operation/insert/result.rb +3 -0
  316. data/lib/mongo/operation/insert.rb +3 -0
  317. data/lib/mongo/operation/kill_cursors/command.rb +11 -0
  318. data/lib/mongo/operation/kill_cursors/command_builder.rb +35 -0
  319. data/lib/mongo/operation/kill_cursors/legacy.rb +5 -1
  320. data/lib/mongo/operation/kill_cursors/op_msg.rb +13 -0
  321. data/lib/mongo/operation/kill_cursors.rb +4 -0
  322. data/lib/mongo/operation/list_collections/command.rb +3 -0
  323. data/lib/mongo/operation/list_collections/op_msg.rb +3 -0
  324. data/lib/mongo/operation/list_collections/result.rb +7 -1
  325. data/lib/mongo/operation/list_collections.rb +3 -0
  326. data/lib/mongo/operation/map_reduce/command.rb +11 -0
  327. data/lib/mongo/operation/map_reduce/op_msg.rb +4 -1
  328. data/lib/mongo/operation/map_reduce/result.rb +3 -0
  329. data/lib/mongo/operation/map_reduce.rb +3 -0
  330. data/lib/mongo/operation/op_msg_base.rb +3 -0
  331. data/lib/mongo/operation/parallel_scan/command.rb +4 -2
  332. data/lib/mongo/operation/parallel_scan/op_msg.rb +3 -0
  333. data/lib/mongo/operation/parallel_scan/result.rb +3 -0
  334. data/lib/mongo/operation/parallel_scan.rb +3 -0
  335. data/lib/mongo/operation/remove_user/command.rb +3 -0
  336. data/lib/mongo/operation/remove_user/op_msg.rb +3 -0
  337. data/lib/mongo/operation/remove_user.rb +3 -0
  338. data/lib/mongo/operation/result.rb +5 -0
  339. data/lib/mongo/operation/shared/bypass_document_validation.rb +3 -0
  340. data/lib/mongo/operation/shared/causal_consistency_supported.rb +3 -0
  341. data/lib/mongo/operation/shared/executable.rb +39 -15
  342. data/lib/mongo/operation/shared/executable_no_validate.rb +5 -2
  343. data/lib/mongo/operation/shared/executable_transaction_label.rb +3 -0
  344. data/lib/mongo/operation/shared/idable.rb +3 -0
  345. data/lib/mongo/operation/shared/limited.rb +3 -0
  346. data/lib/mongo/operation/shared/object_id_generator.rb +3 -0
  347. data/lib/mongo/operation/shared/op_msg_or_command.rb +4 -7
  348. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +4 -7
  349. data/lib/mongo/operation/shared/polymorphic_lookup.rb +3 -0
  350. data/lib/mongo/operation/shared/polymorphic_operation.rb +42 -0
  351. data/lib/mongo/operation/shared/polymorphic_result.rb +3 -0
  352. data/lib/mongo/operation/shared/read_preference_supported.rb +44 -37
  353. data/lib/mongo/operation/shared/response_handling.rb +27 -23
  354. data/lib/mongo/operation/shared/result/aggregatable.rb +3 -0
  355. data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +3 -0
  356. data/lib/mongo/operation/shared/sessions_supported.rb +31 -16
  357. data/lib/mongo/operation/shared/specifiable.rb +14 -29
  358. data/lib/mongo/operation/shared/validatable.rb +87 -0
  359. data/lib/mongo/operation/shared/write.rb +12 -19
  360. data/lib/mongo/operation/shared/write_concern_supported.rb +3 -0
  361. data/lib/mongo/operation/update/bulk_result.rb +3 -0
  362. data/lib/mongo/operation/update/command.rb +9 -3
  363. data/lib/mongo/operation/update/legacy/result.rb +3 -0
  364. data/lib/mongo/operation/update/legacy.rb +22 -11
  365. data/lib/mongo/operation/update/op_msg.rb +10 -4
  366. data/lib/mongo/operation/update/result.rb +3 -0
  367. data/lib/mongo/operation/update.rb +3 -0
  368. data/lib/mongo/operation/update_user/command.rb +3 -0
  369. data/lib/mongo/operation/update_user/op_msg.rb +3 -0
  370. data/lib/mongo/operation/update_user.rb +3 -0
  371. data/lib/mongo/operation/users_info/command.rb +3 -0
  372. data/lib/mongo/operation/users_info/op_msg.rb +3 -0
  373. data/lib/mongo/operation/users_info/result.rb +3 -0
  374. data/lib/mongo/operation/users_info.rb +3 -0
  375. data/lib/mongo/operation/write_command/command.rb +51 -0
  376. data/lib/mongo/operation/write_command/op_msg.rb +43 -0
  377. data/lib/mongo/operation/write_command.rb +32 -0
  378. data/lib/mongo/operation.rb +15 -2
  379. data/lib/mongo/options/mapper.rb +3 -0
  380. data/lib/mongo/options/redacted.rb +3 -0
  381. data/lib/mongo/options.rb +3 -0
  382. data/lib/mongo/protocol/bit_vector.rb +3 -0
  383. data/lib/mongo/protocol/compressed.rb +54 -5
  384. data/lib/mongo/protocol/delete.rb +3 -0
  385. data/lib/mongo/protocol/get_more.rb +3 -0
  386. data/lib/mongo/protocol/insert.rb +3 -0
  387. data/lib/mongo/protocol/kill_cursors.rb +3 -0
  388. data/lib/mongo/protocol/message.rb +23 -2
  389. data/lib/mongo/protocol/msg.rb +39 -16
  390. data/lib/mongo/protocol/query.rb +53 -32
  391. data/lib/mongo/protocol/registry.rb +3 -0
  392. data/lib/mongo/protocol/reply.rb +3 -0
  393. data/lib/mongo/protocol/serializers.rb +3 -0
  394. data/lib/mongo/protocol/update.rb +3 -0
  395. data/lib/mongo/protocol.rb +3 -0
  396. data/lib/mongo/query_cache.rb +33 -0
  397. data/lib/mongo/retryable.rb +4 -1
  398. data/lib/mongo/semaphore.rb +3 -0
  399. data/lib/mongo/server/app_metadata.rb +89 -33
  400. data/lib/mongo/server/connection.rb +34 -4
  401. data/lib/mongo/server/connection_base.rb +47 -21
  402. data/lib/mongo/server/connection_common.rb +71 -1
  403. data/lib/mongo/server/connection_pool/generation_manager.rb +71 -0
  404. data/lib/mongo/server/connection_pool/populator.rb +3 -0
  405. data/lib/mongo/server/connection_pool.rb +108 -30
  406. data/lib/mongo/server/description/features.rb +30 -25
  407. data/lib/mongo/server/description/load_balancer.rb +33 -0
  408. data/lib/mongo/server/description.rb +112 -16
  409. data/lib/mongo/server/monitor/app_metadata.rb +4 -1
  410. data/lib/mongo/server/monitor/connection.rb +56 -47
  411. data/lib/mongo/server/monitor.rb +28 -14
  412. data/lib/mongo/server/pending_connection.rb +75 -42
  413. data/lib/mongo/server/push_monitor/connection.rb +3 -0
  414. data/lib/mongo/server/push_monitor.rb +38 -17
  415. data/lib/mongo/server/round_trip_time_averager.rb +6 -3
  416. data/lib/mongo/server.rb +97 -41
  417. data/lib/mongo/server_selector/base.rb +11 -4
  418. data/lib/mongo/server_selector/nearest.rb +6 -4
  419. data/lib/mongo/server_selector/primary.rb +6 -4
  420. data/lib/mongo/server_selector/primary_preferred.rb +6 -4
  421. data/lib/mongo/server_selector/secondary.rb +6 -4
  422. data/lib/mongo/server_selector/secondary_preferred.rb +8 -11
  423. data/lib/mongo/server_selector.rb +3 -0
  424. data/lib/mongo/session/server_session.rb +3 -0
  425. data/lib/mongo/session/session_pool.rb +18 -2
  426. data/lib/mongo/session.rb +39 -12
  427. data/lib/mongo/socket/ocsp_cache.rb +3 -0
  428. data/lib/mongo/socket/ocsp_verifier.rb +15 -38
  429. data/lib/mongo/socket/ssl.rb +11 -0
  430. data/lib/mongo/socket/tcp.rb +3 -0
  431. data/lib/mongo/socket/unix.rb +3 -0
  432. data/lib/mongo/socket.rb +36 -6
  433. data/lib/mongo/srv/monitor.rb +3 -11
  434. data/lib/mongo/srv/resolver.rb +3 -0
  435. data/lib/mongo/srv/result.rb +3 -0
  436. data/lib/mongo/srv.rb +3 -0
  437. data/lib/mongo/timeout.rb +3 -0
  438. data/lib/mongo/topology_version.rb +4 -1
  439. data/lib/mongo/uri/options_mapper.rb +42 -0
  440. data/lib/mongo/uri/srv_protocol.rb +9 -8
  441. data/lib/mongo/uri.rb +21 -0
  442. data/lib/mongo/utils.rb +38 -0
  443. data/lib/mongo/version.rb +4 -1
  444. data/lib/mongo/write_concern/acknowledged.rb +3 -0
  445. data/lib/mongo/write_concern/base.rb +3 -0
  446. data/lib/mongo/write_concern/unacknowledged.rb +3 -0
  447. data/lib/mongo/write_concern.rb +3 -0
  448. data/lib/mongo.rb +26 -0
  449. data/mongo.gemspec +1 -1
  450. data/spec/README.md +24 -1
  451. data/spec/atlas/atlas_connectivity_spec.rb +3 -0
  452. data/spec/atlas/operations_spec.rb +3 -0
  453. data/spec/integration/auth_spec.rb +58 -15
  454. data/spec/integration/awaited_ismaster_spec.rb +9 -6
  455. data/spec/integration/aws_auth_request_spec.rb +3 -0
  456. data/spec/integration/aws_credentials_retriever_spec.rb +3 -0
  457. data/spec/integration/bson_symbol_spec.rb +4 -1
  458. data/spec/integration/bulk_insert_spec.rb +3 -0
  459. data/spec/integration/bulk_write_error_message_spec.rb +41 -0
  460. data/spec/integration/bulk_write_spec.rb +4 -1
  461. data/spec/integration/change_stream_examples_spec.rb +3 -0
  462. data/spec/integration/change_stream_spec.rb +10 -7
  463. data/spec/integration/check_clean_slate_spec.rb +3 -0
  464. data/spec/integration/client_authentication_options_spec.rb +21 -8
  465. data/spec/integration/client_connectivity_spec.rb +4 -1
  466. data/spec/integration/client_construction_aws_auth_spec.rb +3 -0
  467. data/spec/integration/client_construction_spec.rb +57 -0
  468. data/spec/integration/client_side_encryption/auto_encryption_bulk_writes_spec.rb +4 -1
  469. data/spec/integration/client_side_encryption/auto_encryption_command_monitoring_spec.rb +4 -1
  470. data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +3 -0
  471. data/spec/integration/client_side_encryption/auto_encryption_old_wire_version_spec.rb +3 -0
  472. data/spec/integration/client_side_encryption/auto_encryption_reconnect_spec.rb +3 -0
  473. data/spec/integration/client_side_encryption/auto_encryption_spec.rb +3 -0
  474. data/spec/integration/client_side_encryption/bson_size_limit_spec.rb +4 -1
  475. data/spec/integration/client_side_encryption/bypass_mongocryptd_spawn_spec.rb +4 -1
  476. data/spec/integration/client_side_encryption/client_close_spec.rb +3 -0
  477. data/spec/integration/client_side_encryption/corpus_spec.rb +3 -0
  478. data/spec/integration/client_side_encryption/custom_endpoint_spec.rb +3 -0
  479. data/spec/integration/client_side_encryption/data_key_spec.rb +4 -1
  480. data/spec/integration/client_side_encryption/explicit_encryption_spec.rb +3 -0
  481. data/spec/integration/client_side_encryption/external_key_vault_spec.rb +3 -0
  482. data/spec/integration/client_side_encryption/views_spec.rb +3 -0
  483. data/spec/integration/client_spec.rb +7 -2
  484. data/spec/integration/client_update_spec.rb +3 -0
  485. data/spec/integration/collection_indexes_prose_spec.rb +3 -0
  486. data/spec/integration/command_monitoring_spec.rb +64 -26
  487. data/spec/integration/command_spec.rb +4 -1
  488. data/spec/integration/connect_single_rs_name_spec.rb +6 -3
  489. data/spec/integration/connection_pool_populator_spec.rb +3 -0
  490. data/spec/integration/connection_spec.rb +58 -36
  491. data/spec/integration/crud_spec.rb +205 -1
  492. data/spec/integration/cursor_pinning_spec.rb +121 -0
  493. data/spec/integration/cursor_reaping_spec.rb +11 -4
  494. data/spec/integration/docs_examples_spec.rb +11 -1
  495. data/spec/integration/error_detection_spec.rb +3 -0
  496. data/spec/integration/fork_reconnect_spec.rb +9 -7
  497. data/spec/integration/get_more_spec.rb +4 -1
  498. data/spec/integration/grid_fs_bucket_spec.rb +4 -1
  499. data/spec/integration/heartbeat_events_spec.rb +8 -5
  500. data/spec/integration/map_reduce_spec.rb +77 -0
  501. data/spec/integration/mmapv1_spec.rb +3 -0
  502. data/spec/integration/mongos_pinning_spec.rb +3 -0
  503. data/spec/integration/ocsp_connectivity_spec.rb +3 -0
  504. data/spec/integration/ocsp_verifier_cache_spec.rb +3 -0
  505. data/spec/integration/ocsp_verifier_spec.rb +29 -8
  506. data/spec/integration/operation_failure_code_spec.rb +4 -1
  507. data/spec/integration/operation_failure_message_spec.rb +90 -0
  508. data/spec/integration/query_cache_spec.rb +50 -2
  509. data/spec/integration/query_cache_transactions_spec.rb +4 -1
  510. data/spec/integration/read_concern_spec.rb +4 -1
  511. data/spec/integration/read_preference_spec.rb +4 -1
  512. data/spec/integration/reconnect_spec.rb +34 -13
  513. data/spec/integration/retryable_errors_spec.rb +4 -1
  514. data/spec/integration/retryable_writes/retryable_writes_36_and_older_spec.rb +4 -1
  515. data/spec/integration/retryable_writes/retryable_writes_40_and_newer_spec.rb +4 -1
  516. data/spec/integration/retryable_writes/shared/adds_diagnostics.rb +3 -0
  517. data/spec/integration/retryable_writes/shared/does_not_support_retries.rb +3 -0
  518. data/spec/integration/retryable_writes/shared/only_supports_legacy_retries.rb +3 -0
  519. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +3 -0
  520. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -0
  521. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +3 -0
  522. data/spec/integration/retryable_writes/shared/supports_legacy_retries.rb +3 -0
  523. data/spec/integration/retryable_writes/shared/supports_modern_retries.rb +3 -0
  524. data/spec/integration/retryable_writes/shared/supports_retries.rb +3 -0
  525. data/spec/integration/retryable_writes_errors_spec.rb +3 -0
  526. data/spec/integration/sdam_error_handling_spec.rb +11 -6
  527. data/spec/integration/sdam_events_spec.rb +50 -30
  528. data/spec/integration/sdam_prose_spec.rb +5 -2
  529. data/spec/integration/secondary_reads_spec.rb +102 -0
  530. data/spec/integration/server_description_spec.rb +3 -0
  531. data/spec/integration/server_monitor_spec.rb +5 -1
  532. data/spec/integration/server_selection_spec.rb +3 -0
  533. data/spec/integration/server_selector_spec.rb +25 -5
  534. data/spec/integration/server_spec.rb +5 -0
  535. data/spec/integration/shell_examples_spec.rb +3 -0
  536. data/spec/integration/size_limit_spec.rb +3 -0
  537. data/spec/integration/snappy_compression_spec.rb +28 -0
  538. data/spec/integration/srv_monitoring_spec.rb +5 -2
  539. data/spec/integration/srv_spec.rb +3 -0
  540. data/spec/integration/ssl_uri_options_spec.rb +3 -0
  541. data/spec/integration/step_down_spec.rb +4 -1
  542. data/spec/integration/time_zone_querying_spec.rb +3 -0
  543. data/spec/integration/transaction_pinning_spec.rb +120 -0
  544. data/spec/integration/transactions_api_examples_spec.rb +3 -0
  545. data/spec/integration/transactions_examples_spec.rb +9 -0
  546. data/spec/integration/truncated_utf8_spec.rb +26 -0
  547. data/spec/integration/versioned_api_examples_spec.rb +114 -0
  548. data/spec/integration/x509_auth_spec.rb +5 -2
  549. data/spec/integration/zlib_compression_spec.rb +4 -1
  550. data/spec/integration/zstd_compression_spec.rb +29 -0
  551. data/spec/kerberos/kerberos_spec.rb +3 -0
  552. data/spec/lite_spec_helper.rb +12 -3
  553. data/spec/mongo/address/ipv4_spec.rb +3 -0
  554. data/spec/mongo/address/ipv6_spec.rb +3 -0
  555. data/spec/mongo/address/unix_spec.rb +4 -0
  556. data/spec/mongo/address/validator_spec.rb +3 -0
  557. data/spec/mongo/address_spec.rb +18 -11
  558. data/spec/mongo/auth/aws/request_region_spec.rb +3 -0
  559. data/spec/mongo/auth/aws/request_spec.rb +3 -0
  560. data/spec/mongo/auth/cr_spec.rb +5 -3
  561. data/spec/mongo/auth/gssapi/conversation_spec.rb +3 -0
  562. data/spec/mongo/auth/invalid_mechanism_spec.rb +3 -0
  563. data/spec/mongo/auth/ldap/conversation_spec.rb +4 -1
  564. data/spec/mongo/auth/ldap_spec.rb +10 -4
  565. data/spec/mongo/auth/scram/conversation_spec.rb +3 -0
  566. data/spec/mongo/auth/scram256/conversation_spec.rb +3 -0
  567. data/spec/mongo/auth/scram_negotiation_spec.rb +4 -1
  568. data/spec/mongo/auth/scram_spec.rb +5 -3
  569. data/spec/mongo/auth/stringprep/profiles/sasl_spec.rb +3 -0
  570. data/spec/mongo/auth/stringprep_spec.rb +3 -0
  571. data/spec/mongo/auth/user/view_spec.rb +4 -1
  572. data/spec/mongo/auth/user_spec.rb +3 -0
  573. data/spec/mongo/auth/x509/conversation_spec.rb +6 -3
  574. data/spec/mongo/auth/x509_spec.rb +5 -3
  575. data/spec/mongo/auth_spec.rb +3 -0
  576. data/spec/mongo/bson_spec.rb +3 -0
  577. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +3 -0
  578. data/spec/mongo/bulk_write/result_spec.rb +3 -0
  579. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +3 -0
  580. data/spec/mongo/bulk_write_spec.rb +6 -3
  581. data/spec/mongo/caching_cursor_spec.rb +3 -0
  582. data/spec/mongo/client_construction_spec.rb +472 -62
  583. data/spec/mongo/client_encryption_spec.rb +3 -0
  584. data/spec/mongo/client_spec.rb +26 -4
  585. data/spec/mongo/cluster/cursor_reaper_spec.rb +39 -21
  586. data/spec/mongo/cluster/periodic_executor_spec.rb +6 -1
  587. data/spec/mongo/cluster/socket_reaper_spec.rb +3 -0
  588. data/spec/mongo/cluster/topology/replica_set_spec.rb +12 -9
  589. data/spec/mongo/cluster/topology/sharded_spec.rb +5 -2
  590. data/spec/mongo/cluster/topology/single_spec.rb +5 -2
  591. data/spec/mongo/cluster/topology/unknown_spec.rb +3 -0
  592. data/spec/mongo/cluster/topology_spec.rb +3 -0
  593. data/spec/mongo/cluster_spec.rb +51 -21
  594. data/spec/mongo/cluster_time_spec.rb +3 -0
  595. data/spec/mongo/collection/view/aggregation_spec.rb +4 -1
  596. data/spec/mongo/collection/view/builder/find_command_spec.rb +7 -0
  597. data/spec/mongo/collection/view/builder/op_query_spec.rb +7 -0
  598. data/spec/mongo/collection/view/change_stream_resume_spec.rb +7 -2
  599. data/spec/mongo/collection/view/change_stream_spec.rb +16 -0
  600. data/spec/mongo/collection/view/explainable_spec.rb +4 -1
  601. data/spec/mongo/collection/view/immutable_spec.rb +3 -0
  602. data/spec/mongo/collection/view/iterable_spec.rb +3 -0
  603. data/spec/mongo/collection/view/map_reduce_spec.rb +4 -1
  604. data/spec/mongo/collection/view/readable_spec.rb +51 -18
  605. data/spec/mongo/collection/view/writable_spec.rb +3 -0
  606. data/spec/mongo/collection/view_spec.rb +3 -0
  607. data/spec/mongo/collection_crud_spec.rb +4365 -0
  608. data/spec/mongo/collection_ddl_spec.rb +537 -0
  609. data/spec/mongo/collection_spec.rb +9 -4860
  610. data/spec/mongo/crypt/auto_decryption_context_spec.rb +3 -0
  611. data/spec/mongo/crypt/auto_encrypter_spec.rb +3 -0
  612. data/spec/mongo/crypt/auto_encryption_context_spec.rb +3 -0
  613. data/spec/mongo/crypt/binary_spec.rb +3 -0
  614. data/spec/mongo/crypt/binding/binary_spec.rb +3 -0
  615. data/spec/mongo/crypt/binding/context_spec.rb +3 -0
  616. data/spec/mongo/crypt/binding/helpers_spec.rb +3 -0
  617. data/spec/mongo/crypt/binding/mongocrypt_spec.rb +3 -0
  618. data/spec/mongo/crypt/binding/status_spec.rb +3 -0
  619. data/spec/mongo/crypt/binding/version_spec.rb +3 -0
  620. data/spec/mongo/crypt/binding_unloaded_spec.rb +3 -0
  621. data/spec/mongo/crypt/data_key_context_spec.rb +3 -0
  622. data/spec/mongo/crypt/encryption_io_spec.rb +3 -0
  623. data/spec/mongo/crypt/explicit_decryption_context_spec.rb +3 -0
  624. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +3 -0
  625. data/spec/mongo/crypt/handle_spec.rb +3 -0
  626. data/spec/mongo/crypt/helpers/mongo_crypt_spec_helper.rb +3 -0
  627. data/spec/mongo/crypt/status_spec.rb +3 -0
  628. data/spec/mongo/cursor/builder/get_more_command_spec.rb +11 -2
  629. data/spec/mongo/cursor/builder/op_get_more_spec.rb +11 -2
  630. data/spec/mongo/cursor_spec.rb +89 -12
  631. data/spec/mongo/database_spec.rb +90 -25
  632. data/spec/mongo/dbref_spec.rb +3 -0
  633. data/spec/mongo/distinguishing_semaphore_spec.rb +3 -0
  634. data/spec/mongo/error/bulk_write_error_spec.rb +6 -3
  635. data/spec/mongo/error/crypt_error_spec.rb +3 -0
  636. data/spec/mongo/error/max_bson_size_spec.rb +3 -0
  637. data/spec/mongo/error/no_server_available_spec.rb +3 -0
  638. data/spec/mongo/error/notable_spec.rb +3 -0
  639. data/spec/mongo/error/operation_failure_heavy_spec.rb +4 -1
  640. data/spec/mongo/error/operation_failure_spec.rb +94 -31
  641. data/spec/mongo/error/parser_spec.rb +40 -6
  642. data/spec/mongo/error/unsupported_option_spec.rb +3 -0
  643. data/spec/mongo/event/publisher_spec.rb +3 -0
  644. data/spec/mongo/event/subscriber_spec.rb +3 -0
  645. data/spec/mongo/grid/file/chunk_spec.rb +7 -4
  646. data/spec/mongo/grid/file/info_spec.rb +3 -0
  647. data/spec/mongo/grid/file_spec.rb +4 -1
  648. data/spec/mongo/grid/fs_bucket_spec.rb +40 -17
  649. data/spec/mongo/grid/stream/read_spec.rb +33 -10
  650. data/spec/mongo/grid/stream/write_spec.rb +6 -9
  651. data/spec/mongo/grid/stream_spec.rb +4 -1
  652. data/spec/mongo/id_spec.rb +3 -0
  653. data/spec/mongo/index/view_spec.rb +13 -4
  654. data/spec/mongo/lint_spec.rb +3 -0
  655. data/spec/mongo/logger_spec.rb +3 -0
  656. data/spec/mongo/monitoring/command_log_subscriber_spec.rb +3 -0
  657. data/spec/mongo/monitoring/event/cmap/connection_check_out_failed_spec.rb +3 -0
  658. data/spec/mongo/monitoring/event/cmap/connection_check_out_started_spec.rb +3 -0
  659. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +3 -0
  660. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +3 -0
  661. data/spec/mongo/monitoring/event/cmap/connection_closed_spec.rb +3 -0
  662. data/spec/mongo/monitoring/event/cmap/connection_created_spec.rb +3 -0
  663. data/spec/mongo/monitoring/event/cmap/connection_ready_spec.rb +3 -0
  664. data/spec/mongo/monitoring/event/cmap/pool_cleared_spec.rb +3 -0
  665. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +3 -0
  666. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +3 -0
  667. data/spec/mongo/monitoring/event/command_failed_spec.rb +59 -2
  668. data/spec/mongo/monitoring/event/command_started_spec.rb +3 -0
  669. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +46 -6
  670. data/spec/mongo/monitoring/event/secure_spec.rb +28 -4
  671. data/spec/mongo/monitoring/event/server_closed_spec.rb +3 -0
  672. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +3 -0
  673. data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +4 -1
  674. data/spec/mongo/monitoring/event/server_heartbeat_started_spec.rb +3 -0
  675. data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +4 -1
  676. data/spec/mongo/monitoring/event/server_opening_spec.rb +3 -0
  677. data/spec/mongo/monitoring/event/topology_changed_spec.rb +3 -0
  678. data/spec/mongo/monitoring/event/topology_closed_spec.rb +3 -0
  679. data/spec/mongo/monitoring/event/topology_opening_spec.rb +3 -0
  680. data/spec/mongo/monitoring_spec.rb +3 -0
  681. data/spec/mongo/operation/aggregate/result_spec.rb +7 -2
  682. data/spec/mongo/operation/aggregate_spec.rb +5 -1
  683. data/spec/mongo/operation/collections_info_spec.rb +7 -1
  684. data/spec/mongo/operation/command_spec.rb +11 -5
  685. data/spec/mongo/operation/create_index_spec.rb +9 -3
  686. data/spec/mongo/operation/create_user_spec.rb +9 -3
  687. data/spec/mongo/operation/delete/bulk_spec.rb +12 -6
  688. data/spec/mongo/operation/delete/command_spec.rb +3 -0
  689. data/spec/mongo/operation/delete/op_msg_spec.rb +4 -1
  690. data/spec/mongo/operation/delete_spec.rb +14 -7
  691. data/spec/mongo/operation/drop_index_spec.rb +9 -2
  692. data/spec/mongo/{collection/view → operation/find}/builder/flags_spec.rb +5 -2
  693. data/spec/mongo/{collection/view → operation/find}/builder/modifiers_spec.rb +5 -2
  694. data/spec/mongo/operation/find/legacy_spec.rb +8 -2
  695. data/spec/mongo/operation/get_more_spec.rb +6 -1
  696. data/spec/mongo/operation/indexes_spec.rb +8 -1
  697. data/spec/mongo/operation/insert/bulk_spec.rb +14 -8
  698. data/spec/mongo/operation/insert/command_spec.rb +3 -0
  699. data/spec/mongo/operation/insert/op_msg_spec.rb +4 -1
  700. data/spec/mongo/operation/insert_spec.rb +18 -12
  701. data/spec/mongo/operation/kill_cursors_spec.rb +7 -1
  702. data/spec/mongo/operation/limited_spec.rb +3 -0
  703. data/spec/mongo/operation/map_reduce_spec.rb +8 -2
  704. data/spec/mongo/operation/read_preference_legacy_spec.rb +39 -42
  705. data/spec/mongo/operation/read_preference_op_msg_spec.rb +8 -3
  706. data/spec/mongo/operation/remove_user_spec.rb +9 -3
  707. data/spec/mongo/operation/result_spec.rb +10 -5
  708. data/spec/mongo/operation/specifiable_spec.rb +3 -0
  709. data/spec/mongo/operation/update/bulk_spec.rb +13 -7
  710. data/spec/mongo/operation/update/command_spec.rb +3 -0
  711. data/spec/mongo/operation/update/op_msg_spec.rb +4 -1
  712. data/spec/mongo/operation/update_spec.rb +13 -7
  713. data/spec/mongo/operation/update_user_spec.rb +7 -1
  714. data/spec/mongo/options/redacted_spec.rb +3 -0
  715. data/spec/mongo/protocol/compressed_spec.rb +29 -12
  716. data/spec/mongo/protocol/delete_spec.rb +3 -0
  717. data/spec/mongo/protocol/get_more_spec.rb +3 -0
  718. data/spec/mongo/protocol/insert_spec.rb +3 -0
  719. data/spec/mongo/protocol/kill_cursors_spec.rb +3 -0
  720. data/spec/mongo/protocol/msg_spec.rb +4 -1
  721. data/spec/mongo/protocol/query_spec.rb +6 -3
  722. data/spec/mongo/protocol/registry_spec.rb +3 -0
  723. data/spec/mongo/protocol/reply_spec.rb +3 -0
  724. data/spec/mongo/protocol/update_spec.rb +3 -0
  725. data/spec/mongo/query_cache_middleware_spec.rb +55 -0
  726. data/spec/mongo/query_cache_spec.rb +9 -2
  727. data/spec/mongo/retryable_spec.rb +6 -2
  728. data/spec/mongo/semaphore_spec.rb +3 -0
  729. data/spec/mongo/server/app_metadata_spec.rb +46 -21
  730. data/spec/mongo/server/connection_auth_spec.rb +6 -9
  731. data/spec/mongo/server/connection_common_spec.rb +75 -0
  732. data/spec/mongo/server/connection_pool/populator_spec.rb +6 -1
  733. data/spec/mongo/server/connection_pool_spec.rb +77 -8
  734. data/spec/mongo/server/connection_spec.rb +167 -56
  735. data/spec/mongo/server/description/features_spec.rb +3 -0
  736. data/spec/mongo/server/description_query_methods_spec.rb +4 -1
  737. data/spec/mongo/server/description_spec.rb +624 -611
  738. data/spec/mongo/server/monitor/app_metadata_spec.rb +3 -1
  739. data/spec/mongo/server/monitor/connection_spec.rb +57 -7
  740. data/spec/mongo/server/monitor_spec.rb +26 -14
  741. data/spec/mongo/server/round_trip_time_averager_spec.rb +3 -0
  742. data/spec/mongo/server_selector/nearest_spec.rb +8 -2
  743. data/spec/mongo/server_selector/primary_preferred_spec.rb +8 -2
  744. data/spec/mongo/server_selector/primary_spec.rb +8 -2
  745. data/spec/mongo/server_selector/secondary_preferred_spec.rb +14 -8
  746. data/spec/mongo/server_selector/secondary_spec.rb +8 -2
  747. data/spec/mongo/server_selector_spec.rb +5 -1
  748. data/spec/mongo/server_spec.rb +18 -2
  749. data/spec/mongo/session/server_session_spec.rb +3 -0
  750. data/spec/mongo/session/session_pool_spec.rb +45 -10
  751. data/spec/mongo/session_spec.rb +3 -0
  752. data/spec/mongo/session_transaction_spec.rb +17 -37
  753. data/spec/mongo/socket/ssl_spec.rb +43 -0
  754. data/spec/mongo/socket/tcp_spec.rb +3 -0
  755. data/spec/mongo/socket/unix_spec.rb +4 -0
  756. data/spec/mongo/socket_spec.rb +5 -2
  757. data/spec/mongo/srv/monitor_spec.rb +3 -0
  758. data/spec/mongo/srv/result_spec.rb +3 -0
  759. data/spec/mongo/timeout_spec.rb +3 -0
  760. data/spec/mongo/tls_context_hooks_spec.rb +40 -0
  761. data/spec/mongo/uri/srv_protocol_spec.rb +4 -0
  762. data/spec/mongo/uri_option_parsing_spec.rb +41 -5
  763. data/spec/mongo/uri_spec.rb +3 -0
  764. data/spec/mongo/utils_spec.rb +17 -0
  765. data/spec/mongo/write_concern/acknowledged_spec.rb +3 -0
  766. data/spec/mongo/write_concern/unacknowledged_spec.rb +3 -0
  767. data/spec/mongo/write_concern_spec.rb +3 -0
  768. data/spec/runners/auth.rb +22 -1
  769. data/spec/runners/change_streams/outcome.rb +3 -0
  770. data/spec/runners/change_streams/spec.rb +3 -0
  771. data/spec/runners/change_streams/test.rb +4 -1
  772. data/spec/runners/cmap/verifier.rb +3 -0
  773. data/spec/runners/cmap.rb +4 -1
  774. data/spec/runners/command_monitoring.rb +3 -0
  775. data/spec/runners/connection_string.rb +10 -7
  776. data/spec/runners/crud/context.rb +3 -0
  777. data/spec/runners/crud/operation.rb +8 -3
  778. data/spec/runners/crud/outcome.rb +3 -0
  779. data/spec/runners/crud/requirement.rb +68 -3
  780. data/spec/runners/crud/spec.rb +3 -0
  781. data/spec/runners/crud/test.rb +3 -0
  782. data/spec/runners/crud/test_base.rb +3 -0
  783. data/spec/runners/crud/verifier.rb +11 -0
  784. data/spec/runners/crud.rb +23 -1
  785. data/spec/runners/gridfs.rb +3 -0
  786. data/spec/runners/read_write_concern_document.rb +3 -0
  787. data/spec/runners/sdam/verifier.rb +3 -0
  788. data/spec/runners/sdam.rb +8 -4
  789. data/spec/runners/server_selection.rb +3 -0
  790. data/spec/runners/server_selection_rtt.rb +4 -1
  791. data/spec/runners/transactions/operation.rb +16 -2
  792. data/spec/runners/transactions/spec.rb +3 -0
  793. data/spec/runners/transactions/test.rb +6 -2
  794. data/spec/runners/transactions.rb +3 -0
  795. data/spec/runners/unified/assertions.rb +281 -0
  796. data/spec/runners/unified/change_stream_operations.rb +29 -0
  797. data/spec/runners/unified/crud_operations.rb +206 -0
  798. data/spec/runners/unified/ddl_operations.rb +106 -0
  799. data/spec/runners/unified/entity_map.rb +42 -0
  800. data/spec/runners/unified/error.rb +28 -0
  801. data/spec/runners/unified/event_subscriber.rb +104 -0
  802. data/spec/runners/unified/exceptions.rb +24 -0
  803. data/spec/runners/unified/grid_fs_operations.rb +58 -0
  804. data/spec/runners/unified/support_operations.rb +261 -0
  805. data/spec/runners/unified/test.rb +426 -0
  806. data/spec/runners/unified/test_group.rb +31 -0
  807. data/spec/runners/unified.rb +99 -0
  808. data/spec/shared/bin/get-mongodb-download-url +17 -0
  809. data/spec/shared/bin/s3-copy +45 -0
  810. data/spec/shared/bin/s3-upload +69 -0
  811. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  812. data/spec/shared/lib/mrss/constraints.rb +92 -10
  813. data/spec/shared/lib/mrss/docker_runner.rb +271 -0
  814. data/spec/shared/lib/mrss/event_subscriber.rb +200 -0
  815. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  816. data/spec/shared/lib/mrss/server_version_registry.rb +120 -0
  817. data/spec/shared/lib/mrss/spec_organizer.rb +32 -2
  818. data/spec/shared/lib/mrss/utils.rb +15 -0
  819. data/spec/shared/share/Dockerfile.erb +323 -0
  820. data/spec/shared/share/haproxy-1.conf +16 -0
  821. data/spec/shared/share/haproxy-2.conf +17 -0
  822. data/spec/shared/shlib/distro.sh +73 -0
  823. data/spec/shared/shlib/server.sh +367 -0
  824. data/spec/shared/shlib/set_env.sh +131 -0
  825. data/spec/solo/clean_exit_spec.rb +24 -0
  826. data/spec/spec_helper.rb +8 -3
  827. data/spec/spec_tests/auth_spec.rb +30 -13
  828. data/spec/spec_tests/change_streams_spec.rb +4 -1
  829. data/spec/spec_tests/change_streams_unified_spec.rb +13 -0
  830. data/spec/spec_tests/client_side_encryption_spec.rb +3 -0
  831. data/spec/spec_tests/cmap_spec.rb +7 -1
  832. data/spec/spec_tests/collection_management_spec.rb +13 -0
  833. data/spec/spec_tests/command_monitoring_spec.rb +32 -27
  834. data/spec/spec_tests/command_monitoring_unified_spec.rb +13 -0
  835. data/spec/spec_tests/connection_string_spec.rb +3 -0
  836. data/spec/spec_tests/crud_spec.rb +3 -0
  837. data/spec/spec_tests/crud_unified_spec.rb +13 -0
  838. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +5 -5
  839. data/spec/spec_tests/data/change_streams/{change-streams-resume-whitelist.yml → change-streams-resume-allowlist.yml} +58 -58
  840. data/spec/spec_tests/data/change_streams/change-streams-resume-errorLabels.yml +46 -46
  841. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  842. data/spec/spec_tests/data/change_streams_unified/change-streams.yml +72 -0
  843. data/spec/spec_tests/data/collection_management/timeseries-collection.yml +129 -0
  844. data/spec/spec_tests/data/command_monitoring/find.yml +9 -9
  845. data/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml +340 -0
  846. data/spec/spec_tests/data/crud/read/aggregate-collation.yml +2 -1
  847. data/spec/spec_tests/data/crud/read/aggregate-out.yml +1 -0
  848. data/spec/spec_tests/data/crud/read/count-collation.yml +2 -1
  849. data/spec/spec_tests/data/crud/read/distinct-collation.yml +2 -1
  850. data/spec/spec_tests/data/crud/read/find-collation.yml +2 -1
  851. data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +2 -1
  852. data/spec/spec_tests/data/crud/write/bulkWrite.yml +26 -22
  853. data/spec/spec_tests/data/crud/write/deleteMany-collation.yml +2 -1
  854. data/spec/spec_tests/data/crud/write/deleteOne-collation.yml +2 -1
  855. data/spec/spec_tests/data/crud/write/findOneAndDelete-collation.yml +3 -2
  856. data/spec/spec_tests/data/crud/write/findOneAndReplace-collation.yml +2 -1
  857. data/spec/spec_tests/data/crud/write/findOneAndUpdate-collation.yml +3 -2
  858. data/spec/spec_tests/data/crud/write/insertMany.yml +26 -22
  859. data/spec/spec_tests/data/crud/write/replaceOne-collation.yml +3 -2
  860. data/spec/spec_tests/data/crud/write/updateMany-collation.yml +2 -1
  861. data/spec/spec_tests/data/crud/write/updateOne-collation.yml +2 -1
  862. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  863. data/spec/spec_tests/data/crud_unified/updateWithPipelines.yml +305 -0
  864. data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +1 -0
  865. data/spec/spec_tests/data/crud_v2/db-aggregate.yml +1 -0
  866. data/spec/spec_tests/data/load_balancers/event-monitoring.yml +99 -0
  867. data/spec/spec_tests/data/load_balancers/lb-connection-establishment.yml +36 -0
  868. data/spec/spec_tests/data/load_balancers/non-lb-connection-establishment.yml +56 -0
  869. data/spec/spec_tests/data/load_balancers/server-selection.yml +50 -0
  870. data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +1 -1
  871. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +7 -6
  872. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +2 -1
  873. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +7 -6
  874. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +2 -1
  875. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +7 -6
  876. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +2 -1
  877. data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +1 -1
  878. data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +1 -1
  879. data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +1 -1
  880. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  881. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  882. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  883. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +3 -1
  884. data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +1 -1
  885. data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +1 -1
  886. data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +1 -1
  887. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +1 -1
  888. data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +1 -1
  889. data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +1 -1
  890. data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +1 -1
  891. data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +1 -1
  892. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +1 -1
  893. data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +1 -1
  894. data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +1 -1
  895. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  896. data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +1 -1
  897. data/spec/spec_tests/data/retryable_reads/mapReduce.yml +3 -1
  898. data/spec/spec_tests/data/retryable_writes/bulkWrite.yml +30 -24
  899. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +5 -4
  900. data/spec/spec_tests/data/sdam/errors/error_handling_handshake.yml +2 -1
  901. data/spec/spec_tests/data/sdam/errors/non-stale-network-error.yml +2 -1
  902. data/spec/spec_tests/data/sdam/errors/non-stale-network-timeout-error.yml +2 -1
  903. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml +2 -1
  904. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml +2 -1
  905. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml +61 -0
  906. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-greater-NotMasterNoSlaveOk.yml → non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml} +5 -4
  907. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-greater-NotMasterOrSecondary.yml → non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml} +5 -4
  908. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-greater-NotMaster.yml → non-stale-topologyVersion-greater-NotWritablePrimary.yml} +5 -4
  909. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml +2 -1
  910. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml +2 -1
  911. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml +2 -1
  912. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml +2 -1
  913. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml +52 -0
  914. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-missing-NotMasterNoSlaveOk.yml → non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml} +5 -4
  915. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-missing-NotMasterOrSecondary.yml → non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml} +5 -4
  916. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-missing-NotMaster.yml → non-stale-topologyVersion-missing-NotWritablePrimary.yml} +5 -4
  917. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml +2 -1
  918. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml +2 -1
  919. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml +2 -1
  920. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml +2 -1
  921. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml +61 -0
  922. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-proccessId-changed-NotMasterNoSlaveOk.yml → non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml} +5 -4
  923. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-proccessId-changed-NotMasterOrSecondary.yml → non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml} +5 -4
  924. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-proccessId-changed-NotMaster.yml → non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml} +5 -4
  925. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml +2 -1
  926. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml +2 -1
  927. data/spec/spec_tests/data/sdam/errors/post-42-InterruptedAtShutdown.yml +2 -1
  928. data/spec/spec_tests/data/sdam/errors/post-42-InterruptedDueToReplStateChange.yml +2 -1
  929. data/spec/spec_tests/data/sdam/errors/post-42-LegacyNotPrimary.yml +47 -0
  930. data/spec/spec_tests/data/sdam/errors/{post-42-NotMasterNoSlaveOk.yml → post-42-NotPrimaryNoSecondaryOk.yml} +5 -4
  931. data/spec/spec_tests/data/sdam/errors/{post-42-NotMasterOrSecondary.yml → post-42-NotPrimaryOrSecondary.yml} +5 -4
  932. data/spec/spec_tests/data/sdam/errors/{post-42-NotMaster.yml → post-42-NotWritablePrimary.yml} +5 -4
  933. data/spec/spec_tests/data/sdam/errors/post-42-PrimarySteppedDown.yml +2 -1
  934. data/spec/spec_tests/data/sdam/errors/post-42-ShutdownInProgress.yml +2 -1
  935. data/spec/spec_tests/data/sdam/errors/pre-42-InterruptedAtShutdown.yml +2 -1
  936. data/spec/spec_tests/data/sdam/errors/pre-42-InterruptedDueToReplStateChange.yml +2 -1
  937. data/spec/spec_tests/data/sdam/errors/{pre-42-NotMaster.yml → pre-42-LegacyNotPrimary.yml} +6 -5
  938. data/spec/spec_tests/data/sdam/errors/pre-42-NotPrimaryNoSecondaryOk.yml +47 -0
  939. data/spec/spec_tests/data/sdam/errors/{pre-42-NotMasterOrSecondary.yml → pre-42-NotPrimaryOrSecondary.yml} +5 -4
  940. data/spec/spec_tests/data/sdam/errors/{pre-42-NotMasterNoSlaveOk.yml → pre-42-NotWritablePrimary.yml} +6 -5
  941. data/spec/spec_tests/data/sdam/errors/pre-42-PrimarySteppedDown.yml +2 -1
  942. data/spec/spec_tests/data/sdam/errors/pre-42-ShutdownInProgress.yml +2 -1
  943. data/spec/spec_tests/data/sdam/errors/prefer-error-code.yml +54 -0
  944. data/spec/spec_tests/data/sdam/errors/stale-generation-InterruptedAtShutdown.yml +4 -2
  945. data/spec/spec_tests/data/sdam/errors/stale-generation-InterruptedDueToReplStateChange.yml +4 -2
  946. data/spec/spec_tests/data/sdam/errors/{stale-generation-NotMasterNoSlaveOk.yml → stale-generation-NotPrimaryNoSecondaryOk.yml} +7 -5
  947. data/spec/spec_tests/data/sdam/errors/{stale-generation-NotMasterOrSecondary.yml → stale-generation-NotPrimaryOrSecondary.yml} +7 -5
  948. data/spec/spec_tests/data/sdam/errors/{stale-generation-NotMaster.yml → stale-generation-NotWritablePrimary.yml} +7 -5
  949. data/spec/spec_tests/data/sdam/errors/stale-generation-PrimarySteppedDown.yml +4 -2
  950. data/spec/spec_tests/data/sdam/errors/stale-generation-ShutdownInProgress.yml +4 -2
  951. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml +4 -2
  952. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml +4 -2
  953. data/spec/spec_tests/data/sdam/errors/{stale-generation-afterHandshakeCompletes-NotMaster.yml → stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml} +8 -6
  954. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml +91 -0
  955. data/spec/spec_tests/data/sdam/errors/{stale-generation-afterHandshakeCompletes-NotMasterOrSecondary.yml → stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml} +7 -5
  956. data/spec/spec_tests/data/sdam/errors/{stale-generation-afterHandshakeCompletes-NotMasterNoSlaveOk.yml → stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml} +8 -6
  957. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml +4 -2
  958. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml +4 -2
  959. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-network.yml +6 -4
  960. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-timeout.yml +6 -4
  961. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml +4 -2
  962. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml +4 -2
  963. data/spec/spec_tests/data/sdam/errors/{stale-generation-beforeHandshakeCompletes-NotMaster.yml → stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml} +8 -6
  964. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml +91 -0
  965. data/spec/spec_tests/data/sdam/errors/{stale-generation-beforeHandshakeCompletes-NotMasterOrSecondary.yml → stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml} +7 -5
  966. data/spec/spec_tests/data/sdam/errors/{stale-generation-beforeHandshakeCompletes-NotMasterNoSlaveOk.yml → stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml} +8 -6
  967. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml +4 -2
  968. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml +4 -2
  969. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-network.yml +6 -4
  970. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-timeout.yml +6 -4
  971. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-InterruptedAtShutdown.yml +2 -1
  972. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml +2 -1
  973. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-LegacyNotPrimary.yml +65 -0
  974. data/spec/spec_tests/data/sdam/errors/{stale-topologyVersion-NotMasterNoSlaveOk.yml → stale-topologyVersion-NotPrimaryNoSecondaryOk.yml} +7 -6
  975. data/spec/spec_tests/data/sdam/errors/{stale-topologyVersion-NotMasterOrSecondary.yml → stale-topologyVersion-NotPrimaryOrSecondary.yml} +7 -6
  976. data/spec/spec_tests/data/sdam/errors/{stale-topologyVersion-NotMaster.yml → stale-topologyVersion-NotWritablePrimary.yml} +7 -6
  977. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-PrimarySteppedDown.yml +2 -1
  978. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-ShutdownInProgress.yml +2 -1
  979. data/spec/spec_tests/data/sdam/errors/write_errors_ignored.yml +42 -0
  980. data/spec/spec_tests/data/sdam/load-balanced/discover_load_balancer.yml +25 -0
  981. data/spec/spec_tests/data/sdam/rs/compatible.yml +4 -2
  982. data/spec/spec_tests/data/sdam/rs/compatible_unknown.yml +2 -1
  983. data/spec/spec_tests/data/sdam/rs/discover_arbiters.yml +2 -1
  984. data/spec/spec_tests/data/sdam/rs/discover_arbiters_replicaset.yml +2 -1
  985. data/spec/spec_tests/data/sdam/rs/discover_ghost.yml +2 -1
  986. data/spec/spec_tests/data/sdam/rs/discover_ghost_replicaset.yml +2 -1
  987. data/spec/spec_tests/data/sdam/rs/discover_hidden.yml +2 -1
  988. data/spec/spec_tests/data/sdam/rs/discover_hidden_replicaset.yml +2 -1
  989. data/spec/spec_tests/data/sdam/rs/discover_passives.yml +4 -2
  990. data/spec/spec_tests/data/sdam/rs/discover_passives_replicaset.yml +4 -2
  991. data/spec/spec_tests/data/sdam/rs/discover_primary.yml +2 -1
  992. data/spec/spec_tests/data/sdam/rs/discover_primary_replicaset.yml +2 -1
  993. data/spec/spec_tests/data/sdam/rs/discover_rsother.yml +2 -1
  994. data/spec/spec_tests/data/sdam/rs/discover_rsother_replicaset.yml +4 -2
  995. data/spec/spec_tests/data/sdam/rs/discover_secondary.yml +2 -1
  996. data/spec/spec_tests/data/sdam/rs/discover_secondary_replicaset.yml +2 -1
  997. data/spec/spec_tests/data/sdam/rs/discovery.yml +8 -4
  998. data/spec/spec_tests/data/sdam/rs/equal_electionids.yml +4 -2
  999. data/spec/spec_tests/data/sdam/rs/hosts_differ_from_seeds.yml +2 -1
  1000. data/spec/spec_tests/data/sdam/rs/incompatible_arbiter.yml +3 -1
  1001. data/spec/spec_tests/data/sdam/rs/incompatible_ghost.yml +3 -1
  1002. data/spec/spec_tests/data/sdam/rs/incompatible_other.yml +3 -1
  1003. data/spec/spec_tests/data/sdam/rs/ls_timeout.yml +12 -6
  1004. data/spec/spec_tests/data/sdam/rs/member_reconfig.yml +4 -2
  1005. data/spec/spec_tests/data/sdam/rs/member_standalone.yml +4 -2
  1006. data/spec/spec_tests/data/sdam/rs/new_primary.yml +4 -2
  1007. data/spec/spec_tests/data/sdam/rs/new_primary_new_electionid.yml +6 -3
  1008. data/spec/spec_tests/data/sdam/rs/new_primary_new_setversion.yml +6 -3
  1009. data/spec/spec_tests/data/sdam/rs/new_primary_wrong_set_name.yml +4 -2
  1010. data/spec/spec_tests/data/sdam/rs/non_rs_member.yml +1 -0
  1011. data/spec/spec_tests/data/sdam/rs/normalize_case.yml +2 -1
  1012. data/spec/spec_tests/data/sdam/rs/normalize_case_me.yml +4 -2
  1013. data/spec/spec_tests/data/sdam/rs/null_election_id.yml +8 -4
  1014. data/spec/spec_tests/data/sdam/rs/primary_becomes_ghost.yml +4 -2
  1015. data/spec/spec_tests/data/sdam/rs/primary_becomes_mongos.yml +4 -2
  1016. data/spec/spec_tests/data/sdam/rs/primary_becomes_standalone.yml +2 -1
  1017. data/spec/spec_tests/data/sdam/rs/primary_changes_set_name.yml +4 -2
  1018. data/spec/spec_tests/data/sdam/rs/primary_disconnect.yml +2 -1
  1019. data/spec/spec_tests/data/sdam/rs/primary_disconnect_electionid.yml +10 -5
  1020. data/spec/spec_tests/data/sdam/rs/primary_disconnect_setversion.yml +10 -5
  1021. data/spec/spec_tests/data/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +4 -2
  1022. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +2 -1
  1023. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me_not_removed.yml +5 -3
  1024. data/spec/spec_tests/data/sdam/rs/primary_reports_new_member.yml +8 -4
  1025. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +4 -2
  1026. data/spec/spec_tests/data/sdam/rs/primary_wrong_set_name.yml +2 -1
  1027. data/spec/spec_tests/data/sdam/rs/repeated.yml +9 -5
  1028. data/spec/spec_tests/data/sdam/rs/replicaset_rsnp.yml +2 -1
  1029. data/spec/spec_tests/data/sdam/rs/response_from_removed.yml +4 -2
  1030. data/spec/spec_tests/data/sdam/rs/sec_not_auth.yml +4 -2
  1031. data/spec/spec_tests/data/sdam/rs/secondary_ignore_ok_0.yml +4 -2
  1032. data/spec/spec_tests/data/sdam/rs/secondary_mismatched_me.yml +2 -1
  1033. data/spec/spec_tests/data/sdam/rs/secondary_wrong_set_name.yml +2 -1
  1034. data/spec/spec_tests/data/sdam/rs/secondary_wrong_set_name_with_primary.yml +4 -2
  1035. data/spec/spec_tests/data/sdam/rs/setversion_without_electionid.yml +4 -2
  1036. data/spec/spec_tests/data/sdam/rs/stepdown_change_set_name.yml +4 -2
  1037. data/spec/spec_tests/data/sdam/rs/too_new.yml +4 -2
  1038. data/spec/spec_tests/data/sdam/rs/too_old.yml +4 -2
  1039. data/spec/spec_tests/data/sdam/rs/topology_version_equal.yml +4 -2
  1040. data/spec/spec_tests/data/sdam/rs/topology_version_greater.yml +10 -5
  1041. data/spec/spec_tests/data/sdam/rs/topology_version_less.yml +4 -2
  1042. data/spec/spec_tests/data/sdam/rs/unexpected_mongos.yml +2 -1
  1043. data/spec/spec_tests/data/sdam/rs/use_setversion_without_electionid.yml +6 -3
  1044. data/spec/spec_tests/data/sdam/rs/wrong_set_name.yml +2 -1
  1045. data/spec/spec_tests/data/sdam/sharded/compatible.yml +4 -2
  1046. data/spec/spec_tests/data/sdam/sharded/discover_single_mongos.yml +2 -1
  1047. data/spec/spec_tests/data/sdam/sharded/ls_timeout_mongos.yml +9 -5
  1048. data/spec/spec_tests/data/sdam/sharded/mongos_disconnect.yml +6 -3
  1049. data/spec/spec_tests/data/sdam/sharded/multiple_mongoses.yml +4 -2
  1050. data/spec/spec_tests/data/sdam/sharded/non_mongos_removed.yml +4 -2
  1051. data/spec/spec_tests/data/sdam/sharded/too_new.yml +4 -2
  1052. data/spec/spec_tests/data/sdam/sharded/too_old.yml +4 -2
  1053. data/spec/spec_tests/data/sdam/single/compatible.yml +2 -1
  1054. data/spec/spec_tests/data/sdam/single/direct_connection_external_ip.yml +2 -1
  1055. data/spec/spec_tests/data/sdam/single/direct_connection_mongos.yml +2 -1
  1056. data/spec/spec_tests/data/sdam/single/direct_connection_replicaset.yml +2 -1
  1057. data/spec/spec_tests/data/sdam/single/direct_connection_rsarbiter.yml +2 -1
  1058. data/spec/spec_tests/data/sdam/single/direct_connection_rsprimary.yml +2 -1
  1059. data/spec/spec_tests/data/sdam/single/direct_connection_rssecondary.yml +2 -1
  1060. data/spec/spec_tests/data/sdam/single/direct_connection_standalone.yml +2 -1
  1061. data/spec/spec_tests/data/sdam/single/direct_connection_wrong_set_name.yml +4 -2
  1062. data/spec/spec_tests/data/sdam/single/discover_standalone.yml +2 -1
  1063. data/spec/spec_tests/data/sdam/single/ls_timeout_standalone.yml +2 -1
  1064. data/spec/spec_tests/data/sdam/single/not_ok_response.yml +5 -3
  1065. data/spec/spec_tests/data/sdam/single/standalone_removed.yml +2 -1
  1066. data/spec/spec_tests/data/sdam/single/{direct_connection_slave.yml → standalone_using_legacy_hello.yml} +3 -3
  1067. data/spec/spec_tests/data/sdam/single/too_new.yml +2 -1
  1068. data/spec/spec_tests/data/sdam/single/too_old.yml +2 -1
  1069. data/spec/spec_tests/data/sdam/single/too_old_then_upgraded.yml +4 -2
  1070. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +1 -1
  1071. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +2 -2
  1072. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +2 -0
  1073. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +2 -0
  1074. data/spec/spec_tests/data/sdam_integration/{isMaster-command-error.yml → hello-command-error.yml} +22 -30
  1075. data/spec/spec_tests/data/sdam_integration/{isMaster-network-error.yml → hello-network-error.yml} +12 -16
  1076. data/spec/spec_tests/data/sdam_integration/{isMaster-timeout.yml → hello-timeout.yml} +9 -13
  1077. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +2 -0
  1078. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +2 -0
  1079. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +14 -3
  1080. data/spec/spec_tests/data/sdam_monitoring/discovered_standalone.yml +1 -1
  1081. data/spec/spec_tests/data/sdam_monitoring/load_balancer.yml +65 -0
  1082. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_no_primary.yml +2 -1
  1083. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary.yml +2 -1
  1084. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_removal.yml +3 -2
  1085. data/spec/spec_tests/data/sdam_monitoring/required_replica_set.yml +2 -1
  1086. data/spec/spec_tests/data/sdam_monitoring/standalone.yml +1 -1
  1087. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +2 -2
  1088. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-directConnection.yml +13 -0
  1089. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-replicaSet-errors.yml +6 -0
  1090. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-true-multiple-hosts.yml +5 -0
  1091. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-true-txt.yml +10 -0
  1092. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/direct-connection-false.yml +0 -0
  1093. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/direct-connection-true.yml +0 -0
  1094. data/spec/spec_tests/data/seed_list_discovery/replica-set/encoded-userinfo-and-db.yml +15 -0
  1095. data/spec/spec_tests/data/seed_list_discovery/replica-set/loadBalanced-false-txt.yml +10 -0
  1096. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/longer-parent-in-return.yml +0 -0
  1097. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/misformatted-option.yml +0 -0
  1098. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/no-results.yml +0 -0
  1099. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/not-enough-parts.yml +0 -0
  1100. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/one-result-default-port.yml +0 -0
  1101. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/one-txt-record-multiple-strings.yml +0 -0
  1102. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/one-txt-record.yml +0 -0
  1103. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch1.yml +0 -0
  1104. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch2.yml +0 -0
  1105. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch3.yml +0 -0
  1106. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch4.yml +0 -0
  1107. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch5.yml +0 -0
  1108. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/returned-parent-too-short.yml +0 -0
  1109. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/returned-parent-wrong.yml +0 -0
  1110. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/two-results-default-port.yml +0 -0
  1111. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/two-results-nonstandard-port.yml +0 -0
  1112. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/two-txt-records.yml +0 -0
  1113. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-not-allowed-option.yml +0 -0
  1114. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-with-overridden-ssl-option.yml +0 -0
  1115. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-with-overridden-uri-option.yml +0 -0
  1116. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-with-unallowed-option.yml +0 -0
  1117. data/spec/spec_tests/data/seed_list_discovery/replica-set/uri-with-admin-database.yml +13 -0
  1118. data/spec/spec_tests/data/seed_list_discovery/replica-set/uri-with-auth.yml +12 -0
  1119. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/uri-with-port.yml +0 -0
  1120. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/uri-with-two-hosts.yml +0 -0
  1121. data/spec/spec_tests/data/transactions/error-labels.yml +3 -0
  1122. data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +3 -0
  1123. data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +2 -0
  1124. data/spec/spec_tests/data/transactions/pin-mongos.yml +6 -3
  1125. data/spec/spec_tests/data/transactions/retryable-abort-errorLabels.yml +2 -0
  1126. data/spec/spec_tests/data/transactions/retryable-abort.yml +2 -0
  1127. data/spec/spec_tests/data/transactions/retryable-commit-errorLabels.yml +2 -0
  1128. data/spec/spec_tests/data/transactions/retryable-commit.yml +2 -0
  1129. data/spec/spec_tests/data/transactions/retryable-writes.yml +2 -0
  1130. data/spec/spec_tests/data/transactions_unified/mongos-unpin.yml +172 -0
  1131. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  1132. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  1133. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  1134. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  1135. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  1136. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  1137. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  1138. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  1139. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  1140. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  1141. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  1142. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  1143. data/spec/spec_tests/data/uri_options/connection-options.yml +60 -0
  1144. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +417 -0
  1145. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +411 -0
  1146. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +75 -0
  1147. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  1148. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +46 -0
  1149. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +128 -0
  1150. data/spec/spec_tests/gridfs_spec.rb +3 -0
  1151. data/spec/spec_tests/load_balancers_spec.rb +15 -0
  1152. data/spec/spec_tests/max_staleness_spec.rb +3 -0
  1153. data/spec/spec_tests/read_write_concern_connection_string_spec.rb +3 -0
  1154. data/spec/spec_tests/read_write_concern_document_spec.rb +3 -0
  1155. data/spec/spec_tests/read_write_concern_operaton_spec.rb +3 -0
  1156. data/spec/spec_tests/retryable_reads_spec.rb +5 -2
  1157. data/spec/spec_tests/retryable_writes_spec.rb +10 -7
  1158. data/spec/spec_tests/sdam_integration_spec.rb +4 -1
  1159. data/spec/spec_tests/sdam_monitoring_spec.rb +14 -6
  1160. data/spec/spec_tests/sdam_spec.rb +5 -2
  1161. data/spec/spec_tests/seed_list_discovery_spec.rb +118 -0
  1162. data/spec/spec_tests/server_selection_rtt_spec.rb +3 -0
  1163. data/spec/spec_tests/server_selection_spec.rb +3 -0
  1164. data/spec/spec_tests/transactions_api_spec.rb +3 -0
  1165. data/spec/spec_tests/transactions_spec.rb +3 -0
  1166. data/spec/spec_tests/transactions_unified_spec.rb +13 -0
  1167. data/spec/spec_tests/unified_spec.rb +18 -0
  1168. data/spec/spec_tests/uri_options_spec.rb +21 -2
  1169. data/spec/spec_tests/versioned_api_spec.rb +13 -0
  1170. data/spec/stress/cleanup_spec.rb +3 -0
  1171. data/spec/stress/connection_pool_stress_spec.rb +3 -0
  1172. data/spec/stress/connection_pool_timing_spec.rb +3 -0
  1173. data/spec/stress/fork_reconnect_stress_spec.rb +7 -8
  1174. data/spec/stress/push_monitor_close_spec.rb +44 -0
  1175. data/spec/support/authorization.rb +3 -0
  1176. data/spec/support/aws_utils/base.rb +3 -0
  1177. data/spec/support/aws_utils/inspector.rb +3 -0
  1178. data/spec/support/aws_utils/orchestrator.rb +3 -0
  1179. data/spec/support/aws_utils/provisioner.rb +3 -0
  1180. data/spec/support/aws_utils.rb +3 -0
  1181. data/spec/support/background_thread_registry.rb +4 -1
  1182. data/spec/support/certificates/README.md +3 -2
  1183. data/spec/support/certificates/atlas-ocsp-ca.crt +107 -25
  1184. data/spec/support/certificates/atlas-ocsp.crt +156 -40
  1185. data/spec/support/client_registry.rb +12 -5
  1186. data/spec/support/client_registry_macros.rb +7 -4
  1187. data/spec/support/cluster_tools.rb +4 -1
  1188. data/spec/support/common_shortcuts.rb +39 -7
  1189. data/spec/support/constraints.rb +3 -0
  1190. data/spec/support/crypt.rb +3 -0
  1191. data/spec/support/dns.rb +3 -0
  1192. data/spec/support/json_ext_formatter.rb +3 -0
  1193. data/spec/support/keyword_struct.rb +3 -0
  1194. data/spec/support/local_resource_registry.rb +3 -0
  1195. data/spec/support/matchers.rb +17 -1
  1196. data/spec/support/monitoring_ext.rb +3 -0
  1197. data/spec/support/primary_socket.rb +3 -0
  1198. data/spec/support/sdam_formatter_integration.rb +3 -0
  1199. data/spec/support/session_registry.rb +3 -0
  1200. data/spec/{mongo/server/app_metadata_shared.rb → support/shared/app_metadata.rb} +38 -7
  1201. data/spec/support/shared/auth_context.rb +16 -0
  1202. data/spec/support/shared/protocol.rb +3 -0
  1203. data/spec/support/shared/scram_conversation.rb +3 -0
  1204. data/spec/support/shared/server_selector.rb +6 -3
  1205. data/spec/support/shared/session.rb +7 -4
  1206. data/spec/support/spec_config.rb +99 -23
  1207. data/spec/support/spec_setup.rb +51 -38
  1208. data/spec/support/using_hash.rb +31 -0
  1209. data/spec/support/utils.rb +69 -5
  1210. data.tar.gz.sig +0 -0
  1211. metadata +1265 -1053
  1212. metadata.gz.sig +0 -0
  1213. data/lib/mongo/collection/view/builder/find_command.rb +0 -170
  1214. data/lib/mongo/collection/view/builder/op_query.rb +0 -91
  1215. data/lib/mongo/cursor/builder/get_more_command.rb +0 -77
  1216. data/lib/mongo/cursor/builder/kill_cursors_command.rb +0 -108
  1217. data/lib/mongo/cursor/builder/op_get_more.rb +0 -61
  1218. data/lib/mongo/cursor/builder/op_kill_cursors.rb +0 -103
  1219. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  1220. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  1221. data/lib/mongo/server/context.rb +0 -69
  1222. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -61
  1223. data/spec/spec_tests/dns_seedlist_discovery_spec.rb +0 -76
  1224. data/spec/support/cluster_config.rb +0 -207
  1225. data/spec/support/event_subscriber.rb +0 -212
@@ -0,0 +1,4365 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ describe Mongo::Collection do
7
+
8
+ let(:subscriber) { Mrss::EventSubscriber.new }
9
+
10
+ let(:client) do
11
+ authorized_client.tap do |client|
12
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
13
+ end
14
+ end
15
+
16
+ let(:authorized_collection) { client['collection_spec'] }
17
+
18
+ before do
19
+ authorized_client['collection_spec'].drop
20
+ end
21
+
22
+ let(:collection_invalid_write_concern) do
23
+ authorized_collection.client.with(write: INVALID_WRITE_CONCERN)[authorized_collection.name]
24
+ end
25
+
26
+ let(:collection_with_validator) do
27
+ authorized_client[:validating]
28
+ end
29
+
30
+ describe '#find' do
31
+
32
+ describe 'updating cluster time' do
33
+
34
+ let(:operation) do
35
+ client[TEST_COLL].find.first
36
+ end
37
+
38
+ let(:operation_with_session) do
39
+ client[TEST_COLL].find({}, session: session).first
40
+ end
41
+
42
+ let(:second_operation) do
43
+ client[TEST_COLL].find({}, session: session).first
44
+ end
45
+
46
+ it_behaves_like 'an operation updating cluster time'
47
+ end
48
+
49
+ context 'when provided a filter' do
50
+
51
+ let(:view) do
52
+ authorized_collection.find(name: 1)
53
+ end
54
+
55
+ it 'returns a authorized_collection view for the filter' do
56
+ expect(view.filter).to eq('name' => 1)
57
+ end
58
+ end
59
+
60
+ context 'when provided no filter' do
61
+
62
+ let(:view) do
63
+ authorized_collection.find
64
+ end
65
+
66
+ it 'returns a authorized_collection view with an empty filter' do
67
+ expect(view.filter).to be_empty
68
+ end
69
+ end
70
+
71
+ context 'when providing a bad filter' do
72
+
73
+ let(:view) do
74
+ authorized_collection.find('$or' => [])
75
+ end
76
+
77
+ it 'raises an exception when iterating' do
78
+ expect {
79
+ view.to_a
80
+ }.to raise_exception(Mongo::Error::OperationFailure)
81
+ end
82
+ end
83
+
84
+ context 'when iterating the authorized_collection view' do
85
+
86
+ before do
87
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test2' }])
88
+ end
89
+
90
+ let(:view) do
91
+ authorized_collection.find
92
+ end
93
+
94
+ it 'iterates over the documents' do
95
+ view.each do |document|
96
+ expect(document).to_not be_nil
97
+ end
98
+ end
99
+ end
100
+
101
+ context 'when the user is not authorized' do
102
+ require_auth
103
+
104
+ let(:view) do
105
+ unauthorized_collection.find
106
+ end
107
+
108
+ it 'iterates over the documents' do
109
+ expect {
110
+ view.each{ |document| document }
111
+ }.to raise_error(Mongo::Error::OperationFailure)
112
+ end
113
+ end
114
+
115
+ context 'when documents contain potential error message fields' do
116
+
117
+ [ 'errmsg', 'error', Mongo::Operation::Result::OK ].each do |field|
118
+
119
+ context "when the document contains a '#{field}' field" do
120
+
121
+ let(:value) do
122
+ 'testing'
123
+ end
124
+
125
+ let(:view) do
126
+ authorized_collection.find
127
+ end
128
+
129
+ before do
130
+ authorized_collection.insert_one({ field => value })
131
+ end
132
+
133
+ it 'iterates over the documents' do
134
+ view.each do |document|
135
+ expect(document[field]).to eq(value)
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ context 'when provided options' do
143
+
144
+ context 'when a session is provided' do
145
+ require_wired_tiger
146
+
147
+ let(:operation) do
148
+ authorized_collection.find({}, session: session).to_a
149
+ end
150
+
151
+ let(:session) do
152
+ authorized_client.start_session
153
+ end
154
+
155
+ let(:failed_operation) do
156
+ client[authorized_collection.name].find({ '$._id' => 1 }, session: session).to_a
157
+ end
158
+
159
+ let(:client) do
160
+ authorized_client
161
+ end
162
+
163
+ it_behaves_like 'an operation using a session'
164
+ it_behaves_like 'a failed operation using a session'
165
+ end
166
+
167
+ context 'session id' do
168
+ min_server_fcv '3.6'
169
+ require_topology :replica_set, :sharded
170
+ require_wired_tiger
171
+
172
+ let(:options) do
173
+ { session: session }
174
+ end
175
+
176
+ let(:session) do
177
+ client.start_session
178
+ end
179
+
180
+ let(:view) do
181
+ Mongo::Collection::View.new(client[TEST_COLL], selector, view_options)
182
+ end
183
+
184
+ let(:command) do
185
+ client[TEST_COLL].find({}, session: session).explain
186
+ subscriber.started_events.find { |c| c.command_name == 'explain' }.command
187
+ end
188
+
189
+ it 'sends the session id' do
190
+ expect(command['lsid']).to eq(session.session_id)
191
+ end
192
+ end
193
+
194
+ context 'when a session supporting causal consistency is used' do
195
+ require_wired_tiger
196
+
197
+ let(:operation) do
198
+ collection.find({}, session: session).to_a
199
+ end
200
+
201
+ let(:command) do
202
+ operation
203
+ subscriber.started_events.find { |cmd| cmd.command_name == 'find' }.command
204
+ end
205
+
206
+ it_behaves_like 'an operation supporting causally consistent reads'
207
+ end
208
+
209
+ let(:view) do
210
+ authorized_collection.find({}, options)
211
+ end
212
+
213
+ context 'when provided :allow_partial_results' do
214
+
215
+ let(:options) do
216
+ { allow_partial_results: true }
217
+ end
218
+
219
+ it 'returns a view with :allow_partial_results set' do
220
+ expect(view.options[:allow_partial_results]).to be(options[:allow_partial_results])
221
+ end
222
+ end
223
+
224
+ context 'when provided :batch_size' do
225
+
226
+ let(:options) do
227
+ { batch_size: 100 }
228
+ end
229
+
230
+ it 'returns a view with :batch_size set' do
231
+ expect(view.options[:batch_size]).to eq(options[:batch_size])
232
+ end
233
+ end
234
+
235
+ context 'when provided :comment' do
236
+
237
+ let(:options) do
238
+ { comment: 'slow query' }
239
+ end
240
+
241
+ it 'returns a view with :comment set' do
242
+ expect(view.modifiers[:$comment]).to eq(options[:comment])
243
+ end
244
+ end
245
+
246
+ context 'when provided :cursor_type' do
247
+
248
+ let(:options) do
249
+ { cursor_type: :tailable }
250
+ end
251
+
252
+ it 'returns a view with :cursor_type set' do
253
+ expect(view.options[:cursor_type]).to eq(options[:cursor_type])
254
+ end
255
+ end
256
+
257
+ context 'when provided :max_time_ms' do
258
+
259
+ let(:options) do
260
+ { max_time_ms: 500 }
261
+ end
262
+
263
+ it 'returns a view with :max_time_ms set' do
264
+ expect(view.modifiers[:$maxTimeMS]).to eq(options[:max_time_ms])
265
+ end
266
+ end
267
+
268
+ context 'when provided :modifiers' do
269
+
270
+ let(:options) do
271
+ { modifiers: { '$orderby' => Mongo::Index::ASCENDING } }
272
+ end
273
+
274
+ it 'returns a view with modifiers set' do
275
+ expect(view.modifiers).to eq(options[:modifiers])
276
+ end
277
+
278
+ it 'dups the modifiers hash' do
279
+ expect(view.modifiers).not_to be(options[:modifiers])
280
+ end
281
+ end
282
+
283
+ context 'when provided :no_cursor_timeout' do
284
+
285
+ let(:options) do
286
+ { no_cursor_timeout: true }
287
+ end
288
+
289
+ it 'returns a view with :no_cursor_timeout set' do
290
+ expect(view.options[:no_cursor_timeout]).to eq(options[:no_cursor_timeout])
291
+ end
292
+ end
293
+
294
+ context 'when provided :oplog_replay' do
295
+
296
+ let(:options) do
297
+ { oplog_replay: false }
298
+ end
299
+
300
+ it 'returns a view with :oplog_replay set' do
301
+ expect(view.options[:oplog_replay]).to eq(options[:oplog_replay])
302
+ end
303
+ end
304
+
305
+ context 'when provided :projection' do
306
+
307
+ let(:options) do
308
+ { projection: { 'x' => 1 } }
309
+ end
310
+
311
+ it 'returns a view with :projection set' do
312
+ expect(view.options[:projection]).to eq(options[:projection])
313
+ end
314
+ end
315
+
316
+ context 'when provided :skip' do
317
+
318
+ let(:options) do
319
+ { skip: 5 }
320
+ end
321
+
322
+ it 'returns a view with :skip set' do
323
+ expect(view.options[:skip]).to eq(options[:skip])
324
+ end
325
+ end
326
+
327
+ context 'when provided :sort' do
328
+
329
+ let(:options) do
330
+ { sort: { 'x' => Mongo::Index::ASCENDING } }
331
+ end
332
+
333
+ it 'returns a view with :sort set' do
334
+ expect(view.modifiers[:$orderby]).to eq(options[:sort])
335
+ end
336
+ end
337
+
338
+ context 'when provided :collation' do
339
+
340
+ let(:options) do
341
+ { collation: { 'locale' => 'en_US' } }
342
+ end
343
+
344
+ it 'returns a view with :collation set' do
345
+ expect(view.options[:collation]).to eq(options[:collation])
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ describe '#insert_many' do
352
+
353
+ let(:result) do
354
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }])
355
+ end
356
+
357
+ it 'inserts the documents into the collection' do
358
+ expect(result.inserted_count).to eq(2)
359
+ end
360
+
361
+ it 'contains the ids in the result' do
362
+ expect(result.inserted_ids.size).to eq(2)
363
+ end
364
+
365
+ context 'when a session is provided' do
366
+
367
+ let(:session) do
368
+ authorized_client.start_session
369
+ end
370
+
371
+ let(:operation) do
372
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }], session: session)
373
+ end
374
+
375
+ let(:failed_operation) do
376
+ authorized_collection.insert_many([{ _id: 'test1' }, { _id: 'test1' }], session: session)
377
+ end
378
+
379
+ let(:client) do
380
+ authorized_client
381
+ end
382
+
383
+ it_behaves_like 'an operation using a session'
384
+ it_behaves_like 'a failed operation using a session'
385
+ end
386
+
387
+ context 'when unacknowledged writes is used with an explicit session' do
388
+
389
+ let(:collection_with_unacknowledged_write_concern) do
390
+ authorized_collection.with(write: { w: 0 })
391
+ end
392
+
393
+ let(:operation) do
394
+ collection_with_unacknowledged_write_concern.insert_many([{ name: 'test1' }, { name: 'test2' }], session: session)
395
+ end
396
+
397
+ it_behaves_like 'an explicit session with an unacknowledged write'
398
+ end
399
+
400
+ context 'when unacknowledged writes is used with an implicit session' do
401
+
402
+ let(:collection_with_unacknowledged_write_concern) do
403
+ client.with(write: { w: 0 })[TEST_COLL]
404
+ end
405
+
406
+ let(:operation) do
407
+ collection_with_unacknowledged_write_concern.insert_many([{ name: 'test1' }, { name: 'test2' }])
408
+ end
409
+
410
+ it_behaves_like 'an implicit session with an unacknowledged write'
411
+ end
412
+
413
+ context 'when a document contains invalid keys' do
414
+
415
+ let(:docs) do
416
+ [ { 'first.name' => 'test1' }, { name: 'test2' } ]
417
+ end
418
+
419
+ it 'raises a BSON::String::IllegalKey exception' do
420
+ expect {
421
+ authorized_collection.insert_many(docs)
422
+ }.to raise_exception(BSON::String::IllegalKey)
423
+ end
424
+ end
425
+
426
+ context 'when the client has a custom id generator' do
427
+
428
+ let(:generator) do
429
+ Class.new do
430
+ def generate
431
+ 1
432
+ end
433
+ end.new
434
+ end
435
+
436
+ let(:custom_client) do
437
+ authorized_client.with(id_generator: generator)
438
+ end
439
+
440
+ let(:custom_collection) do
441
+ custom_client['custom_id_generator_test_collection']
442
+ end
443
+
444
+ before do
445
+ custom_collection.delete_many
446
+ custom_collection.insert_many([{ name: 'testing' }])
447
+ expect(custom_collection.count).to eq(1)
448
+ end
449
+
450
+ it 'inserts with the custom id' do
451
+ expect(custom_collection.count).to eq(1)
452
+ expect(custom_collection.find.first[:_id]).to eq(1)
453
+ end
454
+ end
455
+
456
+ context 'when the inserts fail' do
457
+
458
+ let(:result) do
459
+ authorized_collection.insert_many([{ _id: 1 }, { _id: 1 }])
460
+ end
461
+
462
+ it 'raises an BulkWriteError' do
463
+ expect {
464
+ result
465
+ }.to raise_exception(Mongo::Error::BulkWriteError)
466
+ end
467
+ end
468
+
469
+ context "when the documents exceed the max bson size" do
470
+
471
+ let(:documents) do
472
+ [{ '_id' => 1, 'name' => '1'*17000000 }]
473
+ end
474
+
475
+ it 'raises a MaxBSONSize error' do
476
+ expect {
477
+ authorized_collection.insert_many(documents)
478
+ }.to raise_error(Mongo::Error::MaxBSONSize)
479
+ end
480
+ end
481
+
482
+ context 'when the documents are sent with OP_MSG' do
483
+ min_server_fcv '3.6'
484
+
485
+ let(:documents) do
486
+ [{ '_id' => 1, 'name' => '1'*16777191 }, { '_id' => 'y' }]
487
+ end
488
+
489
+ before do
490
+ authorized_collection.insert_many(documents)
491
+ end
492
+
493
+ let(:insert_events) do
494
+ subscriber.started_events.select { |e| e.command_name == 'insert' }
495
+ end
496
+
497
+ it 'sends the documents in one OP_MSG' do
498
+ expect(insert_events.size).to eq(1)
499
+ expect(insert_events[0].command['documents']).to eq(documents)
500
+ end
501
+ end
502
+
503
+ context 'when collection has a validator' do
504
+ min_server_fcv '3.2'
505
+
506
+ around(:each) do |spec|
507
+ authorized_client[:validating].drop
508
+ authorized_client[:validating,
509
+ :validator => { :a => { '$exists' => true } }].tap do |c|
510
+ c.create
511
+ end
512
+ spec.run
513
+ collection_with_validator.drop
514
+ end
515
+
516
+ context 'when the document is valid' do
517
+
518
+ let(:result) do
519
+ collection_with_validator.insert_many([{ a: 1 }, { a: 2 }])
520
+ end
521
+
522
+ it 'inserts successfully' do
523
+ expect(result.inserted_count).to eq(2)
524
+ end
525
+ end
526
+
527
+ context 'when the document is invalid' do
528
+
529
+ context 'when bypass_document_validation is not set' do
530
+
531
+ let(:result2) do
532
+ collection_with_validator.insert_many([{ x: 1 }, { x: 2 }])
533
+ end
534
+
535
+ it 'raises a BulkWriteError' do
536
+ expect {
537
+ result2
538
+ }.to raise_exception(Mongo::Error::BulkWriteError)
539
+ end
540
+ end
541
+
542
+ context 'when bypass_document_validation is true' do
543
+
544
+ let(:result3) do
545
+ collection_with_validator.insert_many(
546
+ [{ x: 1 }, { x: 2 }], :bypass_document_validation => true)
547
+ end
548
+
549
+ it 'inserts successfully' do
550
+ expect(result3.inserted_count).to eq(2)
551
+ end
552
+ end
553
+ end
554
+ end
555
+
556
+ context 'when unacknowledged writes is used' do
557
+
558
+ let(:collection_with_unacknowledged_write_concern) do
559
+ authorized_collection.with(write: { w: 0 })
560
+ end
561
+
562
+ let(:result) do
563
+ collection_with_unacknowledged_write_concern.insert_many([{ _id: 1 }, { _id: 1 }])
564
+ end
565
+
566
+ it 'does not raise an exception' do
567
+ expect(result.inserted_count).to be(0)
568
+ end
569
+ end
570
+
571
+ context 'when various options passed in' do
572
+ # w: 2 requires a replica set
573
+ require_topology :replica_set
574
+
575
+ # https://jira.mongodb.org/browse/RUBY-2306
576
+ min_server_fcv '3.6'
577
+
578
+ let(:session) do
579
+ authorized_client.start_session
580
+ end
581
+
582
+ let(:events) do
583
+ subscriber.command_started_events('insert')
584
+ end
585
+
586
+ let(:collection) do
587
+ authorized_collection.with(write_concern: {w: 2})
588
+ end
589
+
590
+ let!(:command) do
591
+ Utils.get_command_event(authorized_client, 'insert') do |client|
592
+ collection.insert_many([{ name: 'test1' }, { name: 'test2' }], session: session,
593
+ write_concern: {w: 1}, bypass_document_validation: true)
594
+ end.command
595
+ end
596
+
597
+ it 'inserts many successfully with correct options sent to server' do
598
+ expect(events.length).to eq(1)
599
+ expect(command[:writeConcern]).to_not be_nil
600
+ expect(command[:writeConcern][:w]).to eq(1)
601
+ expect(command[:bypassDocumentValidation]).to be(true)
602
+ end
603
+ end
604
+ end
605
+
606
+ describe '#insert_one' do
607
+
608
+ describe 'updating cluster time' do
609
+
610
+ let(:operation) do
611
+ client[TEST_COLL].insert_one({ name: 'testing' })
612
+ end
613
+
614
+ let(:operation_with_session) do
615
+ client[TEST_COLL].insert_one({ name: 'testing' }, session: session)
616
+ end
617
+
618
+ let(:second_operation) do
619
+ client[TEST_COLL].insert_one({ name: 'testing' }, session: session)
620
+ end
621
+
622
+ it_behaves_like 'an operation updating cluster time'
623
+ end
624
+
625
+ let(:result) do
626
+ authorized_collection.insert_one({ name: 'testing' })
627
+ end
628
+
629
+ it 'inserts the document into the collection'do
630
+ expect(result.written_count).to eq(1)
631
+ end
632
+
633
+ it 'contains the id in the result' do
634
+ expect(result.inserted_id).to_not be_nil
635
+ end
636
+
637
+ context 'when a session is provided' do
638
+
639
+ let(:session) do
640
+ authorized_client.start_session
641
+ end
642
+
643
+ let(:operation) do
644
+ authorized_collection.insert_one({ name: 'testing' }, session: session)
645
+ end
646
+
647
+ let(:failed_operation) do
648
+ authorized_collection.insert_one({ _id: 'testing' })
649
+ authorized_collection.insert_one({ _id: 'testing' }, session: session)
650
+ end
651
+
652
+ let(:client) do
653
+ authorized_client
654
+ end
655
+
656
+ it_behaves_like 'an operation using a session'
657
+ it_behaves_like 'a failed operation using a session'
658
+ end
659
+
660
+ context 'when unacknowledged writes is used with an explicit session' do
661
+
662
+ let(:collection_with_unacknowledged_write_concern) do
663
+ authorized_collection.with(write: { w: 0 })
664
+ end
665
+
666
+ let(:operation) do
667
+ collection_with_unacknowledged_write_concern.insert_one({ name: 'testing' }, session: session)
668
+ end
669
+
670
+ it_behaves_like 'an explicit session with an unacknowledged write'
671
+ end
672
+
673
+ context 'when unacknowledged writes is used with an implicit session' do
674
+
675
+ let(:collection_with_unacknowledged_write_concern) do
676
+ client.with(write: { w: 0 })[TEST_COLL]
677
+ end
678
+
679
+ let(:operation) do
680
+ collection_with_unacknowledged_write_concern.insert_one({ name: 'testing' })
681
+ end
682
+
683
+ it_behaves_like 'an implicit session with an unacknowledged write'
684
+ end
685
+
686
+ context 'when various options passed in' do
687
+ # https://jira.mongodb.org/browse/RUBY-2306
688
+ min_server_fcv '3.6'
689
+
690
+ let(:session) do
691
+ authorized_client.start_session
692
+ end
693
+
694
+ let(:events) do
695
+ subscriber.command_started_events('insert')
696
+ end
697
+
698
+ let(:collection) do
699
+ authorized_collection.with(write_concern: {w: 3})
700
+ end
701
+
702
+ let!(:command) do
703
+ Utils.get_command_event(authorized_client, 'insert') do |client|
704
+ collection.insert_one({name: 'test1'}, session: session, write_concern: {w: 1},
705
+ bypass_document_validation: true)
706
+ end.command
707
+ end
708
+
709
+ it 'inserts one successfully with correct options sent to server' do
710
+ expect(events.length).to eq(1)
711
+ expect(command[:writeConcern]).to_not be_nil
712
+ expect(command[:writeConcern][:w]).to eq(1)
713
+ expect(command[:bypassDocumentValidation]).to be(true)
714
+ end
715
+ end
716
+
717
+ context 'when the document contains invalid keys' do
718
+
719
+ let(:doc) do
720
+ { 'testing.test' => 'value' }
721
+ end
722
+
723
+ it 'raises a BSON::String::IllegalKey exception' do
724
+ expect {
725
+ authorized_collection.insert_one(doc)
726
+ }.to raise_exception(BSON::String::IllegalKey)
727
+ end
728
+ end
729
+
730
+ context 'when the document is nil' do
731
+ let(:result) do
732
+ authorized_collection.insert_one(nil)
733
+ end
734
+
735
+ it 'raises an ArgumentError' do
736
+ expect {
737
+ result
738
+ }.to raise_error(ArgumentError, "Document to be inserted cannot be nil")
739
+ end
740
+ end
741
+
742
+ context 'when the insert fails' do
743
+
744
+ let(:result) do
745
+ authorized_collection.insert_one(_id: 1)
746
+ authorized_collection.insert_one(_id: 1)
747
+ end
748
+
749
+ it 'raises an OperationFailure' do
750
+ expect {
751
+ result
752
+ }.to raise_exception(Mongo::Error::OperationFailure)
753
+ end
754
+ end
755
+
756
+ context 'when the client has a custom id generator' do
757
+
758
+ let(:generator) do
759
+ Class.new do
760
+ def generate
761
+ 1
762
+ end
763
+ end.new
764
+ end
765
+
766
+ let(:custom_client) do
767
+ authorized_client.with(id_generator: generator)
768
+ end
769
+
770
+ let(:custom_collection) do
771
+ custom_client[TEST_COLL]
772
+ end
773
+
774
+ before do
775
+ custom_collection.delete_many
776
+ custom_collection.insert_one({ name: 'testing' })
777
+ end
778
+
779
+ it 'inserts with the custom id' do
780
+ expect(custom_collection.find.first[:_id]).to eq(1)
781
+ end
782
+ end
783
+
784
+ context 'when collection has a validator' do
785
+ min_server_fcv '3.2'
786
+
787
+ around(:each) do |spec|
788
+ authorized_client[:validating,
789
+ :validator => { :a => { '$exists' => true } }].tap do |c|
790
+ c.create
791
+ end
792
+ spec.run
793
+ collection_with_validator.drop
794
+ end
795
+
796
+ context 'when the document is valid' do
797
+
798
+ let(:result) do
799
+ collection_with_validator.insert_one({ a: 1 })
800
+ end
801
+
802
+ it 'inserts successfully' do
803
+ expect(result.written_count).to eq(1)
804
+ end
805
+ end
806
+
807
+ context 'when the document is invalid' do
808
+
809
+ context 'when bypass_document_validation is not set' do
810
+
811
+ let(:result2) do
812
+ collection_with_validator.insert_one({ x: 1 })
813
+ end
814
+
815
+ it 'raises a OperationFailure' do
816
+ expect {
817
+ result2
818
+ }.to raise_exception(Mongo::Error::OperationFailure)
819
+ end
820
+ end
821
+
822
+ context 'when bypass_document_validation is true' do
823
+
824
+ let(:result3) do
825
+ collection_with_validator.insert_one(
826
+ { x: 1 }, :bypass_document_validation => true)
827
+ end
828
+
829
+ it 'inserts successfully' do
830
+ expect(result3.written_count).to eq(1)
831
+ end
832
+ end
833
+ end
834
+ end
835
+ end
836
+
837
+ describe '#bulk_write' do
838
+
839
+ context 'when various options passed in' do
840
+ min_server_fcv '3.2'
841
+ require_topology :replica_set
842
+
843
+ # https://jira.mongodb.org/browse/RUBY-2306
844
+ min_server_fcv '3.6'
845
+
846
+ let(:requests) do
847
+ [
848
+ { insert_one: { name: "anne" }},
849
+ { insert_one: { name: "bob" }},
850
+ { insert_one: { name: "charlie" }}
851
+ ]
852
+ end
853
+
854
+ let(:session) do
855
+ authorized_client.start_session
856
+ end
857
+
858
+ let!(:command) do
859
+ Utils.get_command_event(authorized_client, 'insert') do |client|
860
+ collection.bulk_write(requests, session: session, write_concern: {w: 1},
861
+ bypass_document_validation: true)
862
+ end.command
863
+ end
864
+
865
+ let(:events) do
866
+ subscriber.command_started_events('insert')
867
+ end
868
+
869
+ let(:collection) do
870
+ authorized_collection.with(write_concern: {w: 2})
871
+ end
872
+
873
+ it 'inserts successfully with correct options sent to server' do
874
+ expect(collection.count).to eq(3)
875
+ expect(events.length).to eq(1)
876
+ expect(command[:writeConcern]).to_not be_nil
877
+ expect(command[:writeConcern][:w]).to eq(1)
878
+ expect(command[:bypassDocumentValidation]).to eq(true)
879
+ end
880
+ end
881
+ end
882
+
883
+ describe '#aggregate' do
884
+
885
+ describe 'updating cluster time' do
886
+
887
+ let(:operation) do
888
+ client[TEST_COLL].aggregate([]).first
889
+ end
890
+
891
+ let(:operation_with_session) do
892
+ client[TEST_COLL].aggregate([], session: session).first
893
+ end
894
+
895
+ let(:second_operation) do
896
+ client[TEST_COLL].aggregate([], session: session).first
897
+ end
898
+
899
+ it_behaves_like 'an operation updating cluster time'
900
+ end
901
+
902
+ context 'when a session supporting causal consistency is used' do
903
+ require_wired_tiger
904
+
905
+ let(:operation) do
906
+ collection.aggregate([], session: session).first
907
+ end
908
+
909
+ let(:command) do
910
+ operation
911
+ subscriber.started_events.find { |cmd| cmd.command_name == 'aggregate' }.command
912
+ end
913
+
914
+ it_behaves_like 'an operation supporting causally consistent reads'
915
+ end
916
+
917
+ it 'returns an Aggregation object' do
918
+ expect(authorized_collection.aggregate([])).to be_a(Mongo::Collection::View::Aggregation)
919
+ end
920
+
921
+ context 'when options are provided' do
922
+
923
+ let(:options) do
924
+ { :allow_disk_use => true, :bypass_document_validation => true }
925
+ end
926
+
927
+ it 'sets the options on the Aggregation object' do
928
+ expect(authorized_collection.aggregate([], options).options).to eq(BSON::Document.new(options))
929
+ end
930
+
931
+ context 'when the :comment option is provided' do
932
+
933
+ let(:options) do
934
+ { :comment => 'testing' }
935
+ end
936
+
937
+ it 'sets the options on the Aggregation object' do
938
+ expect(authorized_collection.aggregate([], options).options).to eq(BSON::Document.new(options))
939
+ end
940
+ end
941
+
942
+ context 'when a session is provided' do
943
+
944
+ let(:session) do
945
+ authorized_client.start_session
946
+ end
947
+
948
+ let(:operation) do
949
+ authorized_collection.aggregate([], session: session).to_a
950
+ end
951
+
952
+ let(:failed_operation) do
953
+ authorized_collection.aggregate([ { '$invalid' => 1 }], session: session).to_a
954
+ end
955
+
956
+ let(:client) do
957
+ authorized_client
958
+ end
959
+
960
+ it_behaves_like 'an operation using a session'
961
+ it_behaves_like 'a failed operation using a session'
962
+ end
963
+
964
+ context 'when a hint is provided' do
965
+
966
+ let(:options) do
967
+ { 'hint' => { 'y' => 1 } }
968
+ end
969
+
970
+ it 'sets the options on the Aggregation object' do
971
+ expect(authorized_collection.aggregate([], options).options).to eq(options)
972
+ end
973
+ end
974
+
975
+ context 'when collation is provided' do
976
+
977
+ before do
978
+ authorized_collection.insert_many([ { name: 'bang' }, { name: 'bang' }])
979
+ end
980
+
981
+ let(:pipeline) do
982
+ [{ "$match" => { "name" => "BANG" } }]
983
+ end
984
+
985
+ let(:options) do
986
+ { collation: { locale: 'en_US', strength: 2 } }
987
+ end
988
+
989
+ let(:result) do
990
+ authorized_collection.aggregate(pipeline, options).collect { |doc| doc['name']}
991
+ end
992
+
993
+ context 'when the server selected supports collations' do
994
+ min_server_fcv '3.4'
995
+
996
+ it 'applies the collation' do
997
+ expect(result).to eq(['bang', 'bang'])
998
+ end
999
+ end
1000
+
1001
+ context 'when the server selected does not support collations' do
1002
+ max_server_version '3.2'
1003
+
1004
+ it 'raises an exception' do
1005
+ expect {
1006
+ result
1007
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1008
+ end
1009
+
1010
+ context 'when a String key is used' do
1011
+
1012
+ let(:options) do
1013
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1014
+ end
1015
+
1016
+ it 'raises an exception' do
1017
+ expect {
1018
+ result
1019
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1020
+ end
1021
+ end
1022
+ end
1023
+ end
1024
+ end
1025
+ end
1026
+
1027
+ describe '#count_documents' do
1028
+
1029
+ before do
1030
+ authorized_collection.delete_many
1031
+ end
1032
+
1033
+ context 'no argument provided' do
1034
+
1035
+ context 'when collection is empty' do
1036
+ it 'returns 0 matching documents' do
1037
+ expect(authorized_collection.count_documents).to eq(0)
1038
+ end
1039
+ end
1040
+
1041
+ context 'when collection is not empty' do
1042
+
1043
+ let(:documents) do
1044
+ documents = []
1045
+ 1.upto(10) do |index|
1046
+ documents << { key: 'a', _id: "in#{index}" }
1047
+ end
1048
+ documents
1049
+ end
1050
+
1051
+ before do
1052
+ authorized_collection.insert_many(documents)
1053
+ end
1054
+
1055
+ it 'returns 10 matching documents' do
1056
+ expect(authorized_collection.count_documents).to eq(10)
1057
+ end
1058
+ end
1059
+ end
1060
+
1061
+ context 'when transactions are enabled' do
1062
+ require_wired_tiger
1063
+ require_transaction_support
1064
+
1065
+ before do
1066
+ # Ensure that the collection is created
1067
+ authorized_collection.insert_one(x: 1)
1068
+ authorized_collection.delete_many({})
1069
+ end
1070
+
1071
+ let(:session) do
1072
+ authorized_client.start_session
1073
+ end
1074
+
1075
+ it 'successfully starts a transaction and executes a transaction' do
1076
+ session.start_transaction
1077
+ expect(
1078
+ session.instance_variable_get(:@state)
1079
+ ).to eq(Mongo::Session::STARTING_TRANSACTION_STATE)
1080
+
1081
+ expect(authorized_collection.count_documents({}, { session: session })).to eq(0)
1082
+ expect(
1083
+ session.instance_variable_get(:@state)
1084
+ ).to eq(Mongo::Session::TRANSACTION_IN_PROGRESS_STATE)
1085
+
1086
+ authorized_collection.insert_one({ x: 1 }, { session: session })
1087
+ expect(authorized_collection.count_documents({}, { session: session })).to eq(1)
1088
+
1089
+ session.commit_transaction
1090
+ expect(
1091
+ session.instance_variable_get(:@state)
1092
+ ).to eq(Mongo::Session::TRANSACTION_COMMITTED_STATE)
1093
+ end
1094
+ end
1095
+ end
1096
+
1097
+ describe '#count' do
1098
+
1099
+ let(:documents) do
1100
+ (1..10).map{ |i| { field: "test#{i}" }}
1101
+ end
1102
+
1103
+ before do
1104
+ authorized_collection.insert_many(documents)
1105
+ end
1106
+
1107
+ it 'returns an integer count' do
1108
+ expect(authorized_collection.count).to eq(10)
1109
+ end
1110
+
1111
+ context 'when options are provided' do
1112
+
1113
+ it 'passes the options to the count' do
1114
+ expect(authorized_collection.count({}, limit: 5)).to eq(5)
1115
+ end
1116
+
1117
+ context 'when a session is provided' do
1118
+ require_wired_tiger
1119
+
1120
+ let(:session) do
1121
+ authorized_client.start_session
1122
+ end
1123
+
1124
+ let(:operation) do
1125
+ authorized_collection.count({}, session: session)
1126
+ end
1127
+
1128
+ let(:failed_operation) do
1129
+ authorized_collection.count({ '$._id' => 1 }, session: session)
1130
+ end
1131
+
1132
+ let(:client) do
1133
+ authorized_client
1134
+ end
1135
+
1136
+ it_behaves_like 'an operation using a session'
1137
+ it_behaves_like 'a failed operation using a session'
1138
+ end
1139
+
1140
+ context 'when a session supporting causal consistency is used' do
1141
+ require_wired_tiger
1142
+
1143
+ let(:operation) do
1144
+ collection.count({}, session: session)
1145
+ end
1146
+
1147
+ let(:command) do
1148
+ operation
1149
+ subscriber.started_events.find { |cmd| cmd.command_name == 'count' }.command
1150
+ end
1151
+
1152
+ it_behaves_like 'an operation supporting causally consistent reads'
1153
+ end
1154
+
1155
+ context 'when a collation is specified' do
1156
+
1157
+ let(:selector) do
1158
+ { name: 'BANG' }
1159
+ end
1160
+
1161
+ let(:result) do
1162
+ authorized_collection.count(selector, options)
1163
+ end
1164
+
1165
+ before do
1166
+ authorized_collection.insert_one(name: 'bang')
1167
+ end
1168
+
1169
+ let(:options) do
1170
+ { collation: { locale: 'en_US', strength: 2 } }
1171
+ end
1172
+
1173
+ context 'when the server selected supports collations' do
1174
+ min_server_fcv '3.4'
1175
+
1176
+ it 'applies the collation to the count' do
1177
+ expect(result).to eq(1)
1178
+ end
1179
+ end
1180
+
1181
+ context 'when the server selected does not support collations' do
1182
+ max_server_version '3.2'
1183
+
1184
+ it 'raises an exception' do
1185
+ expect {
1186
+ result
1187
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1188
+ end
1189
+
1190
+ context 'when a String key is used' do
1191
+
1192
+ let(:options) do
1193
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1194
+ end
1195
+
1196
+ it 'raises an exception' do
1197
+ expect {
1198
+ result
1199
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1200
+ end
1201
+ end
1202
+ end
1203
+ end
1204
+ end
1205
+ end
1206
+
1207
+ describe '#distinct' do
1208
+
1209
+ let(:documents) do
1210
+ (1..3).map{ |i| { field: "test#{i}" }}
1211
+ end
1212
+
1213
+ before do
1214
+ authorized_collection.insert_many(documents)
1215
+ end
1216
+
1217
+ it 'returns the distinct values' do
1218
+ expect(authorized_collection.distinct(:field).sort).to eq([ 'test1', 'test2', 'test3' ])
1219
+ end
1220
+
1221
+ context 'when a selector is provided' do
1222
+
1223
+ it 'returns the distinct values' do
1224
+ expect(authorized_collection.distinct(:field, field: 'test1')).to eq([ 'test1' ])
1225
+ end
1226
+ end
1227
+
1228
+ context 'when options are provided' do
1229
+
1230
+ it 'passes the options to the distinct command' do
1231
+ expect(authorized_collection.distinct(:field, {}, max_time_ms: 100).sort).to eq([ 'test1', 'test2', 'test3' ])
1232
+ end
1233
+
1234
+ context 'when a session is provided' do
1235
+ require_wired_tiger
1236
+
1237
+ let(:session) do
1238
+ authorized_client.start_session
1239
+ end
1240
+
1241
+ let(:operation) do
1242
+ authorized_collection.distinct(:field, {}, session: session)
1243
+ end
1244
+
1245
+ let(:failed_operation) do
1246
+ authorized_collection.distinct(:field, { '$._id' => 1 }, session: session)
1247
+ end
1248
+
1249
+ let(:client) do
1250
+ authorized_client
1251
+ end
1252
+
1253
+ it_behaves_like 'an operation using a session'
1254
+ it_behaves_like 'a failed operation using a session'
1255
+ end
1256
+ end
1257
+
1258
+ context 'when a session supporting causal consistency is used' do
1259
+ require_wired_tiger
1260
+
1261
+ let(:operation) do
1262
+ collection.distinct(:field, {}, session: session)
1263
+ end
1264
+
1265
+ let(:command) do
1266
+ operation
1267
+ subscriber.started_events.find { |cmd| cmd.command_name == 'distinct' }.command
1268
+ end
1269
+
1270
+ it_behaves_like 'an operation supporting causally consistent reads'
1271
+ end
1272
+
1273
+ context 'when a collation is specified' do
1274
+
1275
+ let(:result) do
1276
+ authorized_collection.distinct(:name, {}, options)
1277
+ end
1278
+
1279
+ before do
1280
+ authorized_collection.insert_one(name: 'bang')
1281
+ authorized_collection.insert_one(name: 'BANG')
1282
+ end
1283
+
1284
+ let(:options) do
1285
+ { collation: { locale: 'en_US', strength: 2 } }
1286
+ end
1287
+
1288
+ context 'when the server selected supports collations' do
1289
+ min_server_fcv '3.4'
1290
+
1291
+ it 'applies the collation to the distinct' do
1292
+ expect(result).to eq(['bang'])
1293
+ end
1294
+ end
1295
+
1296
+ context 'when the server selected does not support collations' do
1297
+ max_server_version '3.2'
1298
+
1299
+ it 'raises an exception' do
1300
+ expect {
1301
+ result
1302
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1303
+ end
1304
+
1305
+ context 'when a String key is used' do
1306
+
1307
+ let(:options) do
1308
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1309
+ end
1310
+
1311
+ it 'raises an exception' do
1312
+ expect {
1313
+ result
1314
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1315
+ end
1316
+ end
1317
+ end
1318
+ end
1319
+
1320
+ context 'when a collation is not specified' do
1321
+
1322
+ let(:result) do
1323
+ authorized_collection.distinct(:name)
1324
+ end
1325
+
1326
+ before do
1327
+ authorized_collection.insert_one(name: 'bang')
1328
+ authorized_collection.insert_one(name: 'BANG')
1329
+ end
1330
+
1331
+ it 'does not apply the collation to the distinct' do
1332
+ expect(result).to match_array(['bang', 'BANG'])
1333
+ end
1334
+ end
1335
+ end
1336
+
1337
+ describe '#delete_one' do
1338
+
1339
+ context 'when a selector was provided' do
1340
+
1341
+ let(:selector) do
1342
+ { field: 'test1' }
1343
+ end
1344
+
1345
+ before do
1346
+ authorized_collection.insert_many([
1347
+ { field: 'test1' },
1348
+ { field: 'test1' },
1349
+ { field: 'test1' }
1350
+ ])
1351
+ end
1352
+
1353
+ let(:response) do
1354
+ authorized_collection.delete_one(selector)
1355
+ end
1356
+
1357
+ it 'deletes the first matching document in the collection' do
1358
+ expect(response.deleted_count).to eq(1)
1359
+ end
1360
+ end
1361
+
1362
+ context 'when no selector was provided' do
1363
+
1364
+ before do
1365
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test2' }])
1366
+ end
1367
+
1368
+ let(:response) do
1369
+ authorized_collection.delete_one
1370
+ end
1371
+
1372
+ it 'deletes the first document in the collection' do
1373
+ expect(response.deleted_count).to eq(1)
1374
+ end
1375
+ end
1376
+
1377
+ context 'when the delete fails' do
1378
+ require_topology :single
1379
+
1380
+ let(:result) do
1381
+ collection_invalid_write_concern.delete_one
1382
+ end
1383
+
1384
+ it 'raises an OperationFailure' do
1385
+ expect {
1386
+ result
1387
+ }.to raise_exception(Mongo::Error::OperationFailure)
1388
+ end
1389
+ end
1390
+
1391
+ context 'when a session is provided' do
1392
+
1393
+ let(:session) do
1394
+ authorized_client.start_session
1395
+ end
1396
+
1397
+ let(:operation) do
1398
+ authorized_collection.delete_one({}, session: session)
1399
+ end
1400
+
1401
+ let(:failed_operation) do
1402
+ authorized_collection.delete_one({ '$._id' => 1}, session: session)
1403
+ end
1404
+
1405
+ let(:client) do
1406
+ authorized_client
1407
+ end
1408
+
1409
+ it_behaves_like 'an operation using a session'
1410
+ it_behaves_like 'a failed operation using a session'
1411
+ end
1412
+
1413
+ context 'when unacknowledged writes is used' do
1414
+
1415
+ let(:collection_with_unacknowledged_write_concern) do
1416
+ authorized_collection.with(write: { w: 0 })
1417
+ end
1418
+
1419
+ let(:operation) do
1420
+ collection_with_unacknowledged_write_concern.delete_one({}, session: session)
1421
+ end
1422
+
1423
+ it_behaves_like 'an explicit session with an unacknowledged write'
1424
+ end
1425
+
1426
+ context 'when unacknowledged writes is used with an implicit session' do
1427
+
1428
+ let(:collection_with_unacknowledged_write_concern) do
1429
+ client.with(write: { w: 0 })[TEST_COLL]
1430
+ end
1431
+
1432
+ let(:operation) do
1433
+ collection_with_unacknowledged_write_concern.delete_one
1434
+ end
1435
+
1436
+ it_behaves_like 'an implicit session with an unacknowledged write'
1437
+ end
1438
+
1439
+ context 'when a collation is provided' do
1440
+
1441
+ let(:selector) do
1442
+ { name: 'BANG' }
1443
+ end
1444
+
1445
+ let(:result) do
1446
+ authorized_collection.delete_one(selector, options)
1447
+ end
1448
+
1449
+ before do
1450
+ authorized_collection.insert_one(name: 'bang')
1451
+ end
1452
+
1453
+ let(:options) do
1454
+ { collation: { locale: 'en_US', strength: 2 } }
1455
+ end
1456
+
1457
+ context 'when the server selected supports collations' do
1458
+ min_server_fcv '3.4'
1459
+
1460
+ it 'applies the collation' do
1461
+ expect(result.written_count).to eq(1)
1462
+ expect(authorized_collection.find(name: 'bang').count).to eq(0)
1463
+ end
1464
+
1465
+ context 'when unacknowledged writes is used' do
1466
+
1467
+ let(:collection_with_unacknowledged_write_concern) do
1468
+ authorized_collection.with(write: { w: 0 })
1469
+ end
1470
+
1471
+ let(:result) do
1472
+ collection_with_unacknowledged_write_concern.delete_one(selector, options)
1473
+ end
1474
+
1475
+ it 'raises an exception' do
1476
+ expect {
1477
+ result
1478
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1479
+ end
1480
+
1481
+ context 'when a String key is used' do
1482
+
1483
+ let(:options) do
1484
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1485
+ end
1486
+
1487
+ it 'raises an exception' do
1488
+ expect {
1489
+ result
1490
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1491
+ end
1492
+ end
1493
+ end
1494
+ end
1495
+
1496
+ context 'when the server selected does not support collations' do
1497
+ max_server_version '3.2'
1498
+
1499
+ it 'raises an exception' do
1500
+ expect {
1501
+ result
1502
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1503
+ end
1504
+
1505
+ context 'when a String key is used' do
1506
+
1507
+ let(:options) do
1508
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1509
+ end
1510
+
1511
+ it 'raises an exception' do
1512
+ expect {
1513
+ result
1514
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1515
+ end
1516
+ end
1517
+ end
1518
+ end
1519
+
1520
+ context 'when collation is not specified' do
1521
+
1522
+ let(:selector) do
1523
+ { name: 'BANG' }
1524
+ end
1525
+
1526
+ let(:result) do
1527
+ authorized_collection.delete_one(selector)
1528
+ end
1529
+
1530
+ before do
1531
+ authorized_collection.insert_one(name: 'bang')
1532
+ end
1533
+
1534
+ it 'does not apply the collation' do
1535
+ expect(result.written_count).to eq(0)
1536
+ expect(authorized_collection.find(name: 'bang').count).to eq(1)
1537
+ end
1538
+ end
1539
+
1540
+ context 'when various options passed in' do
1541
+ # w: 2 requires a replica set
1542
+ require_topology :replica_set
1543
+
1544
+ # https://jira.mongodb.org/browse/RUBY-2306
1545
+ min_server_fcv '3.6'
1546
+
1547
+ before do
1548
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }])
1549
+ end
1550
+
1551
+ let(:selector) do
1552
+ {name: 'test2'}
1553
+ end
1554
+
1555
+ let(:session) do
1556
+ authorized_client.start_session
1557
+ end
1558
+
1559
+ let(:events) do
1560
+ subscriber.command_started_events('delete')
1561
+ end
1562
+
1563
+ let(:collection) do
1564
+ authorized_collection.with(write_concern: {w: 2})
1565
+ end
1566
+
1567
+ let!(:command) do
1568
+ Utils.get_command_event(authorized_client, 'delete') do |client|
1569
+ collection.delete_one(selector, session: session, write_concern: {w: 1},
1570
+ bypass_document_validation: true)
1571
+ end.command
1572
+ end
1573
+
1574
+ it 'deletes one successfully with correct options sent to server' do
1575
+ expect(events.length).to eq(1)
1576
+ expect(command[:writeConcern]).to_not be_nil
1577
+ expect(command[:writeConcern][:w]).to eq(1)
1578
+ expect(command[:bypassDocumentValidation]).to eq(true)
1579
+ end
1580
+ end
1581
+ end
1582
+
1583
+ describe '#delete_many' do
1584
+
1585
+ before do
1586
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test2' }])
1587
+ end
1588
+
1589
+ context 'when a selector was provided' do
1590
+
1591
+ let(:selector) do
1592
+ { field: 'test1' }
1593
+ end
1594
+
1595
+ it 'deletes the matching documents in the collection' do
1596
+ expect(authorized_collection.delete_many(selector).deleted_count).to eq(1)
1597
+ end
1598
+ end
1599
+
1600
+ context 'when no selector was provided' do
1601
+
1602
+ it 'deletes all the documents in the collection' do
1603
+ expect(authorized_collection.delete_many.deleted_count).to eq(2)
1604
+ end
1605
+ end
1606
+
1607
+ context 'when the deletes fail' do
1608
+ require_topology :single
1609
+
1610
+ let(:result) do
1611
+ collection_invalid_write_concern.delete_many
1612
+ end
1613
+
1614
+ it 'raises an OperationFailure' do
1615
+ expect {
1616
+ result
1617
+ }.to raise_exception(Mongo::Error::OperationFailure)
1618
+ end
1619
+ end
1620
+
1621
+ context 'when a session is provided' do
1622
+
1623
+ let(:session) do
1624
+ authorized_client.start_session
1625
+ end
1626
+
1627
+ let(:operation) do
1628
+ authorized_collection.delete_many({}, session: session)
1629
+ end
1630
+
1631
+ let(:failed_operation) do
1632
+ authorized_collection.delete_many({ '$._id' => 1}, session: session)
1633
+ end
1634
+
1635
+ let(:client) do
1636
+ authorized_client
1637
+ end
1638
+
1639
+ it_behaves_like 'an operation using a session'
1640
+ it_behaves_like 'a failed operation using a session'
1641
+ end
1642
+
1643
+ context 'when unacknowledged writes are used with an explicit session' do
1644
+
1645
+ let(:collection_with_unacknowledged_write_concern) do
1646
+ authorized_collection.with(write: { w: 0 })
1647
+ end
1648
+
1649
+ let(:operation) do
1650
+ collection_with_unacknowledged_write_concern.delete_many({ '$._id' => 1}, session: session)
1651
+ end
1652
+
1653
+ it_behaves_like 'an explicit session with an unacknowledged write'
1654
+ end
1655
+
1656
+ context 'when unacknowledged writes are used with an implicit session' do
1657
+
1658
+ let(:collection_with_unacknowledged_write_concern) do
1659
+ client.with(write: { w: 0 })[TEST_COLL]
1660
+ end
1661
+
1662
+ let(:operation) do
1663
+ collection_with_unacknowledged_write_concern.delete_many({ '$._id' => 1 })
1664
+ end
1665
+
1666
+ it_behaves_like 'an implicit session with an unacknowledged write'
1667
+ end
1668
+
1669
+ context 'when a collation is specified' do
1670
+
1671
+ let(:selector) do
1672
+ { name: 'BANG' }
1673
+ end
1674
+
1675
+ let(:result) do
1676
+ authorized_collection.delete_many(selector, options)
1677
+ end
1678
+
1679
+ before do
1680
+ authorized_collection.insert_one(name: 'bang')
1681
+ authorized_collection.insert_one(name: 'bang')
1682
+ end
1683
+
1684
+ let(:options) do
1685
+ { collation: { locale: 'en_US', strength: 2 } }
1686
+ end
1687
+
1688
+ context 'when the server selected supports collations' do
1689
+ min_server_fcv '3.4'
1690
+
1691
+ it 'applies the collation' do
1692
+ expect(result.written_count).to eq(2)
1693
+ expect(authorized_collection.find(name: 'bang').count).to eq(0)
1694
+ end
1695
+
1696
+ context 'when unacknowledged writes is used' do
1697
+
1698
+ let(:collection_with_unacknowledged_write_concern) do
1699
+ authorized_collection.with(write: { w: 0 })
1700
+ end
1701
+
1702
+ let(:result) do
1703
+ collection_with_unacknowledged_write_concern.delete_many(selector, options)
1704
+ end
1705
+
1706
+ it 'raises an exception' do
1707
+ expect {
1708
+ result
1709
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1710
+ end
1711
+
1712
+ context 'when a String key is used' do
1713
+
1714
+ let(:options) do
1715
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1716
+ end
1717
+
1718
+ it 'raises an exception' do
1719
+ expect {
1720
+ result
1721
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1722
+ end
1723
+ end
1724
+ end
1725
+ end
1726
+
1727
+ context 'when the server selected does not support collations' do
1728
+ max_server_version '3.2'
1729
+
1730
+ it 'raises an exception' do
1731
+ expect {
1732
+ result
1733
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1734
+ end
1735
+
1736
+ context 'when a String key is used' do
1737
+
1738
+ let(:options) do
1739
+ { 'collation' => { locale: 'en_US', strength: 2 } }
1740
+ end
1741
+
1742
+ it 'raises an exception' do
1743
+ expect {
1744
+ result
1745
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
1746
+ end
1747
+ end
1748
+ end
1749
+ end
1750
+
1751
+ context 'when a collation is not specified' do
1752
+
1753
+ let(:selector) do
1754
+ { name: 'BANG' }
1755
+ end
1756
+
1757
+ let(:result) do
1758
+ authorized_collection.delete_many(selector)
1759
+ end
1760
+
1761
+ before do
1762
+ authorized_collection.insert_one(name: 'bang')
1763
+ authorized_collection.insert_one(name: 'bang')
1764
+ end
1765
+
1766
+ it 'does not apply the collation' do
1767
+ expect(result.written_count).to eq(0)
1768
+ expect(authorized_collection.find(name: 'bang').count).to eq(2)
1769
+ end
1770
+ end
1771
+
1772
+ context 'when various options passed in' do
1773
+ # w: 2 requires a replica set
1774
+ require_topology :replica_set
1775
+
1776
+ # https://jira.mongodb.org/browse/RUBY-2306
1777
+ min_server_fcv '3.6'
1778
+
1779
+ before do
1780
+ collection.insert_many([{ name: 'test1' }, { name: 'test2' }, { name: 'test3'}])
1781
+ end
1782
+
1783
+ let(:selector) do
1784
+ {name: 'test1'}
1785
+ end
1786
+
1787
+ let(:session) do
1788
+ authorized_client.start_session
1789
+ end
1790
+
1791
+ let(:events) do
1792
+ subscriber.command_started_events('delete')
1793
+ end
1794
+
1795
+ let(:collection) do
1796
+ authorized_collection.with(write_concern: {w: 1})
1797
+ end
1798
+
1799
+ let!(:command) do
1800
+ Utils.get_command_event(authorized_client, 'delete') do |client|
1801
+ collection.delete_many(selector, session: session, write_concern: {w: 2},
1802
+ bypass_document_validation: true)
1803
+ end.command
1804
+ end
1805
+
1806
+ it 'deletes many successfully with correct options sent to server' do
1807
+ expect(events.length).to eq(1)
1808
+ expect(command[:writeConcern]).to_not be_nil
1809
+ expect(command[:writeConcern][:w]).to eq(2)
1810
+ expect(command[:bypassDocumentValidation]).to be(true)
1811
+ end
1812
+ end
1813
+ end
1814
+
1815
+ describe '#parallel_scan' do
1816
+ max_server_version '4.0'
1817
+ require_topology :single, :replica_set
1818
+
1819
+ let(:documents) do
1820
+ (1..200).map do |i|
1821
+ { name: "testing-scan-#{i}" }
1822
+ end
1823
+ end
1824
+
1825
+ before do
1826
+ authorized_collection.insert_many(documents)
1827
+ end
1828
+
1829
+ let(:cursors) do
1830
+ authorized_collection.parallel_scan(2)
1831
+ end
1832
+
1833
+ it 'returns an array of cursors' do
1834
+ cursors.each do |cursor|
1835
+ expect(cursor.class).to be(Mongo::Cursor)
1836
+ end
1837
+ end
1838
+
1839
+ it 'returns the correct number of documents' do
1840
+ expect(
1841
+ cursors.reduce(0) { |total, cursor| total + cursor.to_a.size }
1842
+ ).to eq(200)
1843
+ end
1844
+
1845
+ context 'when a session is provided' do
1846
+ require_wired_tiger
1847
+
1848
+ let(:cursors) do
1849
+ authorized_collection.parallel_scan(2, session: session)
1850
+ end
1851
+
1852
+ let(:operation) do
1853
+ cursors.reduce(0) { |total, cursor| total + cursor.to_a.size }
1854
+ end
1855
+
1856
+ let(:failed_operation) do
1857
+ authorized_collection.parallel_scan(-2, session: session)
1858
+ end
1859
+
1860
+ let(:client) do
1861
+ authorized_client
1862
+ end
1863
+
1864
+ it_behaves_like 'an operation using a session'
1865
+ it_behaves_like 'a failed operation using a session'
1866
+ end
1867
+
1868
+ context 'when a session is not provided' do
1869
+ let(:collection) { client['test'] }
1870
+
1871
+ let(:cursors) do
1872
+ collection.parallel_scan(2)
1873
+ end
1874
+
1875
+ let(:operation) do
1876
+ cursors.reduce(0) { |total, cursor| total + cursor.to_a.size }
1877
+ end
1878
+
1879
+ let(:failed_operation) do
1880
+ collection.parallel_scan(-2)
1881
+ end
1882
+
1883
+ let(:command) do
1884
+ operation
1885
+ event = subscriber.started_events.find { |cmd| cmd.command_name == 'parallelCollectionScan' }
1886
+ expect(event).not_to be_nil
1887
+ event.command
1888
+ end
1889
+
1890
+ it_behaves_like 'an operation not using a session'
1891
+ it_behaves_like 'a failed operation not using a session'
1892
+ end
1893
+
1894
+ context 'when a session supporting causal consistency is used' do
1895
+ require_wired_tiger
1896
+
1897
+ before do
1898
+ collection.drop
1899
+ collection.create
1900
+ end
1901
+
1902
+ let(:cursors) do
1903
+ collection.parallel_scan(2, session: session)
1904
+ end
1905
+
1906
+ let(:operation) do
1907
+ cursors.reduce(0) { |total, cursor| total + cursor.to_a.size }
1908
+ end
1909
+
1910
+ let(:command) do
1911
+ operation
1912
+ event = subscriber.started_events.find { |cmd| cmd.command_name == 'parallelCollectionScan' }
1913
+ expect(event).not_to be_nil
1914
+ event.command
1915
+ end
1916
+
1917
+ it_behaves_like 'an operation supporting causally consistent reads'
1918
+ end
1919
+
1920
+ context 'when a read concern is provided' do
1921
+ require_wired_tiger
1922
+ min_server_fcv '3.2'
1923
+
1924
+ let(:result) do
1925
+ authorized_collection.with(options).parallel_scan(2)
1926
+ end
1927
+
1928
+ context 'when the read concern is valid' do
1929
+
1930
+ let(:options) do
1931
+ { read_concern: { level: 'local' }}
1932
+ end
1933
+
1934
+ it 'sends the read concern' do
1935
+ expect { result }.to_not raise_error
1936
+ end
1937
+ end
1938
+
1939
+ context 'when the read concern is not valid' do
1940
+
1941
+ let(:options) do
1942
+ { read_concern: { level: 'idontknow' }}
1943
+ end
1944
+
1945
+ it 'raises an exception' do
1946
+ expect {
1947
+ result
1948
+ }.to raise_error(Mongo::Error::OperationFailure)
1949
+ end
1950
+ end
1951
+ end
1952
+
1953
+ context 'when the collection has a read preference' do
1954
+ require_topology :single, :replica_set
1955
+
1956
+ before do
1957
+ allow(collection.client.cluster).to receive(:single?).and_return(false)
1958
+ end
1959
+
1960
+ let(:client) do
1961
+ authorized_client.with(server_selection_timeout: 0.2)
1962
+ end
1963
+
1964
+ let(:collection) do
1965
+ client[authorized_collection.name,
1966
+ read: { :mode => :secondary, :tag_sets => [{ 'non' => 'existent' }] }]
1967
+ end
1968
+
1969
+ let(:result) do
1970
+ collection.parallel_scan(2)
1971
+ end
1972
+
1973
+ it 'uses that read preference' do
1974
+ expect {
1975
+ result
1976
+ }.to raise_exception(Mongo::Error::NoServerAvailable)
1977
+ end
1978
+ end
1979
+
1980
+ context 'when a max time ms value is provided' do
1981
+ require_topology :single, :replica_set
1982
+
1983
+ let(:result) do
1984
+ authorized_collection.parallel_scan(2, options)
1985
+ end
1986
+
1987
+ context 'when the read concern is valid' do
1988
+
1989
+ let(:options) do
1990
+ { max_time_ms: 5 }
1991
+ end
1992
+
1993
+ it 'sends the max time ms value' do
1994
+ expect { result }.to_not raise_error
1995
+ end
1996
+ end
1997
+
1998
+ context 'when the max time ms is not valid' do
1999
+
2000
+ let(:options) do
2001
+ { max_time_ms: 0.1 }
2002
+ end
2003
+
2004
+ it 'raises an exception' do
2005
+ expect {
2006
+ result
2007
+ }.to raise_error(Mongo::Error::OperationFailure)
2008
+ end
2009
+ end
2010
+ end
2011
+ end
2012
+
2013
+ describe '#replace_one' do
2014
+
2015
+ let(:selector) do
2016
+ { field: 'test1' }
2017
+ end
2018
+
2019
+ context 'when a selector was provided' do
2020
+
2021
+ before do
2022
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test1' }])
2023
+ end
2024
+
2025
+ let!(:response) do
2026
+ authorized_collection.replace_one(selector, { field: 'testing' })
2027
+ end
2028
+
2029
+ let(:updated) do
2030
+ authorized_collection.find(field: 'testing').first
2031
+ end
2032
+
2033
+ it 'updates the first matching document in the collection' do
2034
+ expect(response.modified_count).to eq(1)
2035
+ end
2036
+
2037
+ it 'updates the documents in the collection' do
2038
+ expect(updated[:field]).to eq('testing')
2039
+ end
2040
+ end
2041
+
2042
+ context 'when upsert is false' do
2043
+
2044
+ let!(:response) do
2045
+ authorized_collection.replace_one(selector, { field: 'test1' }, upsert: false)
2046
+ end
2047
+
2048
+ let(:updated) do
2049
+ authorized_collection.find(field: 'test1').to_a
2050
+ end
2051
+
2052
+ it 'reports that no documents were written' do
2053
+ expect(response.modified_count).to eq(0)
2054
+ end
2055
+
2056
+ it 'does not insert the document' do
2057
+ expect(updated).to be_empty
2058
+ end
2059
+ end
2060
+
2061
+ context 'when upsert is true' do
2062
+
2063
+ let!(:response) do
2064
+ authorized_collection.replace_one(selector, { field: 'test1' }, upsert: true)
2065
+ end
2066
+
2067
+ let(:updated) do
2068
+ authorized_collection.find(field: 'test1').first
2069
+ end
2070
+
2071
+ it 'reports that a document was written' do
2072
+ expect(response.written_count).to eq(1)
2073
+ end
2074
+
2075
+ it 'inserts the document' do
2076
+ expect(updated[:field]).to eq('test1')
2077
+ end
2078
+ end
2079
+
2080
+ context 'when upsert is not specified' do
2081
+
2082
+ let!(:response) do
2083
+ authorized_collection.replace_one(selector, { field: 'test1' })
2084
+ end
2085
+
2086
+ let(:updated) do
2087
+ authorized_collection.find(field: 'test1').to_a
2088
+ end
2089
+
2090
+ it 'reports that no documents were written' do
2091
+ expect(response.modified_count).to eq(0)
2092
+ end
2093
+
2094
+ it 'does not insert the document' do
2095
+ expect(updated).to be_empty
2096
+ end
2097
+ end
2098
+
2099
+ context 'when the replace fails' do
2100
+
2101
+ let(:result) do
2102
+ authorized_collection.replace_one(selector, { '$s' => 'test1' })
2103
+ end
2104
+
2105
+ it 'raises an OperationFailure' do
2106
+ expect {
2107
+ result
2108
+ }.to raise_exception(Mongo::Error::OperationFailure)
2109
+ end
2110
+ end
2111
+
2112
+ context 'when collection has a validator' do
2113
+ min_server_fcv '3.2'
2114
+
2115
+ around(:each) do |spec|
2116
+ collection_with_validator.drop
2117
+ authorized_client[:validating,
2118
+ :validator => { :a => { '$exists' => true } }].tap do |c|
2119
+ c.create
2120
+ end
2121
+ spec.run
2122
+ collection_with_validator.drop
2123
+ end
2124
+
2125
+ before do
2126
+ collection_with_validator.insert_one({ a: 1 })
2127
+ end
2128
+
2129
+ context 'when the document is valid' do
2130
+
2131
+ let(:result) do
2132
+ collection_with_validator.replace_one({ a: 1 }, { a: 5 })
2133
+ end
2134
+
2135
+ it 'replaces successfully' do
2136
+ expect(result.modified_count).to eq(1)
2137
+ end
2138
+ end
2139
+
2140
+ context 'when the document is invalid' do
2141
+
2142
+ context 'when bypass_document_validation is not set' do
2143
+
2144
+ let(:result2) do
2145
+ collection_with_validator.replace_one({ a: 1 }, { x: 5 })
2146
+ end
2147
+
2148
+ it 'raises OperationFailure' do
2149
+ expect {
2150
+ result2
2151
+ }.to raise_exception(Mongo::Error::OperationFailure)
2152
+ end
2153
+ end
2154
+
2155
+ context 'when bypass_document_validation is true' do
2156
+
2157
+ let(:result3) do
2158
+ collection_with_validator.replace_one(
2159
+ { a: 1 }, { x: 1 }, :bypass_document_validation => true)
2160
+ end
2161
+
2162
+ it 'replaces successfully' do
2163
+ expect(result3.written_count).to eq(1)
2164
+ end
2165
+ end
2166
+ end
2167
+ end
2168
+
2169
+ context 'when a collation is specified' do
2170
+
2171
+ let(:selector) do
2172
+ { name: 'BANG' }
2173
+ end
2174
+
2175
+ let(:result) do
2176
+ authorized_collection.replace_one(selector, { name: 'doink' }, options)
2177
+ end
2178
+
2179
+ before do
2180
+ authorized_collection.insert_one(name: 'bang')
2181
+ end
2182
+
2183
+ let(:options) do
2184
+ { collation: { locale: 'en_US', strength: 2 } }
2185
+ end
2186
+
2187
+ context 'when the server selected supports collations' do
2188
+ min_server_fcv '3.4'
2189
+
2190
+ it 'applies the collation' do
2191
+ expect(result.written_count).to eq(1)
2192
+ expect(authorized_collection.find(name: 'doink').count).to eq(1)
2193
+ end
2194
+
2195
+ context 'when unacknowledged writes is used' do
2196
+
2197
+ let(:collection_with_unacknowledged_write_concern) do
2198
+ authorized_collection.with(write: { w: 0 })
2199
+ end
2200
+
2201
+ let(:result) do
2202
+ collection_with_unacknowledged_write_concern.replace_one(selector, { name: 'doink' }, options)
2203
+ end
2204
+
2205
+ it 'raises an exception' do
2206
+ expect {
2207
+ result
2208
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2209
+ end
2210
+
2211
+ context 'when a String key is used' do
2212
+
2213
+ let(:options) do
2214
+ { 'collation' => { locale: 'en_US', strength: 2 } }
2215
+ end
2216
+
2217
+ it 'raises an exception' do
2218
+ expect {
2219
+ result
2220
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2221
+ end
2222
+ end
2223
+ end
2224
+ end
2225
+
2226
+ context 'when the server selected does not support collations' do
2227
+ max_server_version '3.2'
2228
+
2229
+ it 'raises an exception' do
2230
+ expect {
2231
+ result
2232
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2233
+ end
2234
+
2235
+ context 'when a String key is used' do
2236
+
2237
+ let(:options) do
2238
+ { 'collation' => { locale: 'en_US', strength: 2 } }
2239
+ end
2240
+
2241
+ it 'raises an exception' do
2242
+ expect {
2243
+ result
2244
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2245
+ end
2246
+ end
2247
+ end
2248
+ end
2249
+
2250
+ context 'when a collation is not specified' do
2251
+
2252
+ let(:selector) do
2253
+ { name: 'BANG' }
2254
+ end
2255
+
2256
+ let(:result) do
2257
+ authorized_collection.replace_one(selector, { name: 'doink' })
2258
+ end
2259
+
2260
+ before do
2261
+ authorized_collection.insert_one(name: 'bang')
2262
+ end
2263
+
2264
+ it 'does not apply the collation' do
2265
+ expect(result.written_count).to eq(0)
2266
+ expect(authorized_collection.find(name: 'bang').count).to eq(1)
2267
+ end
2268
+ end
2269
+
2270
+ context 'when a session is provided' do
2271
+
2272
+ let(:selector) do
2273
+ { name: 'BANG' }
2274
+ end
2275
+
2276
+ before do
2277
+ authorized_collection.insert_one(name: 'bang')
2278
+ end
2279
+
2280
+ let(:session) do
2281
+ authorized_client.start_session
2282
+ end
2283
+
2284
+ let(:operation) do
2285
+ authorized_collection.replace_one(selector, { name: 'doink' }, session: session)
2286
+ end
2287
+
2288
+ let(:failed_operation) do
2289
+ authorized_collection.replace_one({ '$._id' => 1 }, { name: 'doink' }, session: session)
2290
+ end
2291
+
2292
+ let(:client) do
2293
+ authorized_client
2294
+ end
2295
+
2296
+ it_behaves_like 'an operation using a session'
2297
+ it_behaves_like 'a failed operation using a session'
2298
+ end
2299
+
2300
+ context 'when unacknowledged writes is used with an explicit session' do
2301
+
2302
+ let(:collection_with_unacknowledged_write_concern) do
2303
+ authorized_collection.with(write: { w: 0 })
2304
+ end
2305
+
2306
+ let(:operation) do
2307
+ collection_with_unacknowledged_write_concern.replace_one({ a: 1 }, { x: 5 }, session: session)
2308
+ end
2309
+
2310
+ it_behaves_like 'an explicit session with an unacknowledged write'
2311
+ end
2312
+
2313
+ context 'when unacknowledged writes is used with an implicit session' do
2314
+
2315
+ let(:collection_with_unacknowledged_write_concern) do
2316
+ client.with(write: { w: 0 })[TEST_COLL]
2317
+ end
2318
+
2319
+ let(:operation) do
2320
+ collection_with_unacknowledged_write_concern.replace_one({ a: 1 }, { x: 5 })
2321
+ end
2322
+
2323
+ it_behaves_like 'an implicit session with an unacknowledged write'
2324
+ end
2325
+
2326
+ context 'when various options passed in' do
2327
+ # w: 2 requires a replica set
2328
+ require_topology :replica_set
2329
+
2330
+ # https://jira.mongodb.org/browse/RUBY-2306
2331
+ min_server_fcv '3.6'
2332
+
2333
+ before do
2334
+ authorized_collection.insert_one({field: 'test1'})
2335
+ end
2336
+
2337
+ let(:session) do
2338
+ authorized_client.start_session
2339
+ end
2340
+
2341
+ let(:events) do
2342
+ subscriber.command_started_events('update')
2343
+ end
2344
+
2345
+ let(:collection) do
2346
+ authorized_collection.with(write_concern: {w: 3})
2347
+ end
2348
+
2349
+ let(:updated) do
2350
+ collection.find(field: 'test4').first
2351
+ end
2352
+
2353
+ let!(:command) do
2354
+ Utils.get_command_event(authorized_client, 'update') do |client|
2355
+ collection.replace_one(selector, { field: 'test4'},
2356
+ session: session, :return_document => :after, write_concern: {w: 2},
2357
+ upsert: true, bypass_document_validation: true)
2358
+ end.command
2359
+ end
2360
+
2361
+ it 'replaced one successfully with correct options sent to server' do
2362
+ expect(updated[:field]).to eq('test4')
2363
+ expect(events.length).to eq(1)
2364
+ expect(command[:writeConcern]).to_not be_nil
2365
+ expect(command[:writeConcern][:w]).to eq(2)
2366
+ expect(command[:bypassDocumentValidation]).to be(true)
2367
+ expect(command[:updates][0][:upsert]).to be(true)
2368
+ end
2369
+ end
2370
+ end
2371
+
2372
+ describe '#update_many' do
2373
+
2374
+ let(:selector) do
2375
+ { field: 'test' }
2376
+ end
2377
+
2378
+ context 'when a selector was provided' do
2379
+
2380
+ before do
2381
+ authorized_collection.insert_many([{ field: 'test' }, { field: 'test' }])
2382
+ end
2383
+
2384
+ let!(:response) do
2385
+ authorized_collection.update_many(selector, '$set'=> { field: 'testing' })
2386
+ end
2387
+
2388
+ let(:updated) do
2389
+ authorized_collection.find(field: 'testing').to_a.last
2390
+ end
2391
+
2392
+ it 'returns the number updated' do
2393
+ expect(response.modified_count).to eq(2)
2394
+ end
2395
+
2396
+ it 'updates the documents in the collection' do
2397
+ expect(updated[:field]).to eq('testing')
2398
+ end
2399
+ end
2400
+
2401
+ context 'when upsert is false' do
2402
+
2403
+ let(:response) do
2404
+ authorized_collection.update_many(selector, { '$set'=> { field: 'testing' } },
2405
+ upsert: false)
2406
+ end
2407
+
2408
+ let(:updated) do
2409
+ authorized_collection.find.to_a
2410
+ end
2411
+
2412
+ it 'reports that no documents were updated' do
2413
+ expect(response.modified_count).to eq(0)
2414
+ end
2415
+
2416
+ it 'updates no documents in the collection' do
2417
+ expect(updated).to be_empty
2418
+ end
2419
+ end
2420
+
2421
+ context 'when upsert is true' do
2422
+
2423
+ let!(:response) do
2424
+ authorized_collection.update_many(selector, { '$set'=> { field: 'testing' } },
2425
+ upsert: true)
2426
+ end
2427
+
2428
+ let(:updated) do
2429
+ authorized_collection.find.to_a.last
2430
+ end
2431
+
2432
+ it 'reports that a document was written' do
2433
+ expect(response.written_count).to eq(1)
2434
+ end
2435
+
2436
+ it 'inserts a document into the collection' do
2437
+ expect(updated[:field]).to eq('testing')
2438
+ end
2439
+ end
2440
+
2441
+ context 'when upsert is not specified' do
2442
+
2443
+ let(:response) do
2444
+ authorized_collection.update_many(selector, { '$set'=> { field: 'testing' } })
2445
+ end
2446
+
2447
+ let(:updated) do
2448
+ authorized_collection.find.to_a
2449
+ end
2450
+
2451
+ it 'reports that no documents were updated' do
2452
+ expect(response.modified_count).to eq(0)
2453
+ end
2454
+
2455
+ it 'updates no documents in the collection' do
2456
+ expect(updated).to be_empty
2457
+ end
2458
+ end
2459
+
2460
+ context 'when arrayFilters is provided' do
2461
+
2462
+ let(:selector) do
2463
+ { '$or' => [{ _id: 0 }, { _id: 1 }]}
2464
+ end
2465
+
2466
+ context 'when the server supports arrayFilters' do
2467
+ min_server_fcv '3.6'
2468
+
2469
+ before do
2470
+ authorized_collection.insert_many([{
2471
+ _id: 0, x: [
2472
+ { y: 1 },
2473
+ { y: 2 },
2474
+ { y: 3 }
2475
+ ]
2476
+ },
2477
+ {
2478
+ _id: 1,
2479
+ x: [
2480
+ { y: 3 },
2481
+ { y: 2 },
2482
+ { y: 1 }
2483
+ ]
2484
+ }])
2485
+ end
2486
+
2487
+ let(:result) do
2488
+ authorized_collection.update_many(selector,
2489
+ { '$set' => { 'x.$[i].y' => 5 } },
2490
+ options)
2491
+ end
2492
+
2493
+ context 'when a Symbol key is used' do
2494
+
2495
+ let(:options) do
2496
+ { array_filters: [{ 'i.y' => 3 }] }
2497
+ end
2498
+
2499
+ it 'applies the arrayFilters' do
2500
+ expect(result.matched_count).to eq(2)
2501
+ expect(result.modified_count).to eq(2)
2502
+
2503
+ docs = authorized_collection.find(selector, sort: { _id: 1 }).to_a
2504
+ expect(docs[0]['x']).to eq ([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 5 }])
2505
+ expect(docs[1]['x']).to eq ([{ 'y' => 5 }, { 'y' => 2 }, { 'y' => 1 }])
2506
+ end
2507
+ end
2508
+
2509
+ context 'when a String key is used' do
2510
+ let(:options) do
2511
+ { 'array_filters' => [{ 'i.y' => 3 }] }
2512
+ end
2513
+
2514
+ it 'applies the arrayFilters' do
2515
+ expect(result.matched_count).to eq(2)
2516
+ expect(result.modified_count).to eq(2)
2517
+
2518
+ docs = authorized_collection.find({}, sort: { _id: 1 }).to_a
2519
+ expect(docs[0]['x']).to eq ([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 5 }])
2520
+ expect(docs[1]['x']).to eq ([{ 'y' => 5 }, { 'y' => 2 }, { 'y' => 1 }])
2521
+ end
2522
+ end
2523
+ end
2524
+
2525
+ context 'when the server does not support arrayFilters' do
2526
+ max_server_version '3.4'
2527
+
2528
+ let(:result) do
2529
+ authorized_collection.update_many(selector,
2530
+ { '$set' => { 'x.$[i].y' => 5 } },
2531
+ options)
2532
+ end
2533
+
2534
+ context 'when a Symbol key is used' do
2535
+
2536
+ let(:options) do
2537
+ { array_filters: [{ 'i.y' => 3 }] }
2538
+ end
2539
+
2540
+ it 'raises an exception' do
2541
+ expect {
2542
+ result
2543
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
2544
+ end
2545
+ end
2546
+
2547
+ context 'when a String key is used' do
2548
+
2549
+ let(:options) do
2550
+ { 'array_filters' => [{ 'i.y' => 3 }] }
2551
+ end
2552
+
2553
+ it 'raises an exception' do
2554
+ expect {
2555
+ result
2556
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
2557
+ end
2558
+ end
2559
+ end
2560
+ end
2561
+
2562
+ context 'when the updates fail' do
2563
+
2564
+ let(:result) do
2565
+ authorized_collection.update_many(selector, { '$s'=> { field: 'testing' } })
2566
+ end
2567
+
2568
+ it 'raises an OperationFailure' do
2569
+ expect {
2570
+ result
2571
+ }.to raise_exception(Mongo::Error::OperationFailure)
2572
+ end
2573
+ end
2574
+
2575
+ context 'when collection has a validator' do
2576
+ min_server_fcv '3.2'
2577
+
2578
+ around(:each) do |spec|
2579
+ authorized_client[:validating,
2580
+ :validator => { :a => { '$exists' => true } }].tap do |c|
2581
+ c.create
2582
+ end
2583
+ spec.run
2584
+ collection_with_validator.drop
2585
+ end
2586
+
2587
+ before do
2588
+ collection_with_validator.insert_many([{ a: 1 }, { a: 2 }])
2589
+ end
2590
+
2591
+ context 'when the document is valid' do
2592
+
2593
+ let(:result) do
2594
+ collection_with_validator.update_many(
2595
+ { :a => { '$gt' => 0 } }, '$inc' => { :a => 1 } )
2596
+ end
2597
+
2598
+ it 'updates successfully' do
2599
+ expect(result.modified_count).to eq(2)
2600
+ end
2601
+ end
2602
+
2603
+ context 'when the document is invalid' do
2604
+
2605
+ context 'when bypass_document_validation is not set' do
2606
+
2607
+ let(:result2) do
2608
+ collection_with_validator.update_many(
2609
+ { :a => { '$gt' => 0 } }, '$unset' => { :a => '' })
2610
+ end
2611
+
2612
+ it 'raises OperationFailure' do
2613
+ expect {
2614
+ result2
2615
+ }.to raise_exception(Mongo::Error::OperationFailure)
2616
+ end
2617
+ end
2618
+
2619
+ context 'when bypass_document_validation is true' do
2620
+
2621
+ let(:result3) do
2622
+ collection_with_validator.update_many(
2623
+ { :a => { '$gt' => 0 } }, { '$unset' => { :a => '' } },
2624
+ :bypass_document_validation => true)
2625
+ end
2626
+
2627
+ it 'updates successfully' do
2628
+ expect(result3.written_count).to eq(2)
2629
+ end
2630
+ end
2631
+ end
2632
+ end
2633
+
2634
+ context 'when a collation is specified' do
2635
+
2636
+ let(:selector) do
2637
+ { name: 'BANG' }
2638
+ end
2639
+
2640
+ let(:result) do
2641
+ authorized_collection.update_many(selector, { '$set' => { other: 'doink' } }, options)
2642
+ end
2643
+
2644
+ before do
2645
+ authorized_collection.insert_one(name: 'bang')
2646
+ authorized_collection.insert_one(name: 'baNG')
2647
+ end
2648
+
2649
+ let(:options) do
2650
+ { collation: { locale: 'en_US', strength: 2 } }
2651
+ end
2652
+
2653
+ context 'when the server selected supports collations' do
2654
+ min_server_fcv '3.4'
2655
+
2656
+ it 'applies the collation' do
2657
+ expect(result.written_count).to eq(2)
2658
+ expect(authorized_collection.find(other: 'doink').count).to eq(2)
2659
+ end
2660
+
2661
+ context 'when unacknowledged writes is used' do
2662
+
2663
+ let(:collection_with_unacknowledged_write_concern) do
2664
+ authorized_collection.with(write: { w: 0 })
2665
+ end
2666
+
2667
+ let(:result) do
2668
+ collection_with_unacknowledged_write_concern.update_many(selector, { '$set' => { other: 'doink' } }, options)
2669
+ end
2670
+
2671
+ it 'raises an exception' do
2672
+ expect {
2673
+ result
2674
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2675
+ end
2676
+
2677
+ context 'when a String key is used' do
2678
+
2679
+ let(:options) do
2680
+ { 'collation' => { locale: 'en_US', strength: 2 } }
2681
+ end
2682
+
2683
+ it 'raises an exception' do
2684
+ expect {
2685
+ result
2686
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2687
+ end
2688
+ end
2689
+ end
2690
+ end
2691
+
2692
+ context 'when the server selected does not support collations' do
2693
+ max_server_version '3.2'
2694
+
2695
+ it 'raises an exception' do
2696
+ expect {
2697
+ result
2698
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2699
+ end
2700
+
2701
+ context 'when a String key is used' do
2702
+
2703
+ let(:options) do
2704
+ { 'collation' => { locale: 'en_US', strength: 2 } }
2705
+ end
2706
+
2707
+ it 'raises an exception' do
2708
+ expect {
2709
+ result
2710
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
2711
+ end
2712
+ end
2713
+ end
2714
+ end
2715
+
2716
+ context 'when collation is not specified' do
2717
+
2718
+ let(:selector) do
2719
+ {name: 'BANG'}
2720
+ end
2721
+
2722
+ let(:result) do
2723
+ authorized_collection.update_many(selector, { '$set' => {other: 'doink'} })
2724
+ end
2725
+
2726
+ before do
2727
+ authorized_collection.insert_one(name: 'bang')
2728
+ authorized_collection.insert_one(name: 'baNG')
2729
+ end
2730
+
2731
+ it 'does not apply the collation' do
2732
+ expect(result.written_count).to eq(0)
2733
+ end
2734
+ end
2735
+
2736
+ context 'when a session is provided' do
2737
+
2738
+ let(:selector) do
2739
+ { name: 'BANG' }
2740
+ end
2741
+
2742
+ let(:operation) do
2743
+ authorized_collection.update_many(selector, { '$set' => {other: 'doink'} }, session: session)
2744
+ end
2745
+
2746
+ before do
2747
+ authorized_collection.insert_one(name: 'bang')
2748
+ authorized_collection.insert_one(name: 'baNG')
2749
+ end
2750
+
2751
+ let(:session) do
2752
+ authorized_client.start_session
2753
+ end
2754
+
2755
+ let(:failed_operation) do
2756
+ authorized_collection.update_many({ '$._id' => 1 }, { '$set' => {other: 'doink'} }, session: session)
2757
+ end
2758
+
2759
+ let(:client) do
2760
+ authorized_client
2761
+ end
2762
+
2763
+ it_behaves_like 'an operation using a session'
2764
+ it_behaves_like 'a failed operation using a session'
2765
+ end
2766
+
2767
+ context 'when unacknowledged writes is used with an explicit session' do
2768
+
2769
+ let(:collection_with_unacknowledged_write_concern) do
2770
+ authorized_collection.with(write: { w: 0 })
2771
+ end
2772
+
2773
+ let(:operation) do
2774
+ collection_with_unacknowledged_write_concern.update_many({a: 1}, { '$set' => {x: 1} }, session: session)
2775
+ end
2776
+
2777
+ it_behaves_like 'an explicit session with an unacknowledged write'
2778
+ end
2779
+
2780
+ context 'when unacknowledged writes is used with an implicit session' do
2781
+
2782
+ let(:collection_with_unacknowledged_write_concern) do
2783
+ client.with(write: { w: 0 })[TEST_COLL]
2784
+ end
2785
+
2786
+ let(:operation) do
2787
+ collection_with_unacknowledged_write_concern.update_many({a: 1}, {'$set' => {x: 1}})
2788
+ end
2789
+
2790
+ it_behaves_like 'an implicit session with an unacknowledged write'
2791
+ end
2792
+
2793
+ context 'when various options passed in' do
2794
+ # w: 2 requires a replica set
2795
+ require_topology :replica_set
2796
+
2797
+ # https://jira.mongodb.org/browse/RUBY-2306
2798
+ min_server_fcv '3.6'
2799
+
2800
+ before do
2801
+ collection.insert_many([{ field: 'test' }, { field: 'test2' }], session: session)
2802
+ end
2803
+
2804
+ let(:session) do
2805
+ authorized_client.start_session
2806
+ end
2807
+
2808
+ let(:collection) do
2809
+ authorized_collection.with(write_concern: {w: 1})
2810
+ end
2811
+
2812
+ let(:events) do
2813
+ subscriber.command_started_events('update')
2814
+ end
2815
+
2816
+ let!(:command) do
2817
+ Utils.get_command_event(authorized_client, 'update') do |client|
2818
+ collection.update_many(selector, {'$set'=> { field: 'testing' }}, session: session,
2819
+ write_concern: {w: 2}, bypass_document_validation: true, upsert: true)
2820
+ end.command
2821
+ end
2822
+
2823
+ it 'updates many successfully with correct options sent to server' do
2824
+ expect(events.length).to eq(1)
2825
+ expect(collection.options[:write_concern]).to eq(w: 1)
2826
+ expect(command[:writeConcern][:w]).to eq(2)
2827
+ expect(command[:bypassDocumentValidation]).to be(true)
2828
+ expect(command[:updates][0][:upsert]).to be(true)
2829
+ end
2830
+ end
2831
+ end
2832
+
2833
+ describe '#update_one' do
2834
+
2835
+ let(:selector) do
2836
+ { field: 'test1' }
2837
+ end
2838
+
2839
+ context 'when a selector was provided' do
2840
+
2841
+ before do
2842
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test1' }])
2843
+ end
2844
+
2845
+ let!(:response) do
2846
+ authorized_collection.update_one(selector, '$set'=> { field: 'testing' })
2847
+ end
2848
+
2849
+ let(:updated) do
2850
+ authorized_collection.find(field: 'testing').first
2851
+ end
2852
+
2853
+ it 'updates the first matching document in the collection' do
2854
+ expect(response.modified_count).to eq(1)
2855
+ end
2856
+
2857
+ it 'updates the documents in the collection' do
2858
+ expect(updated[:field]).to eq('testing')
2859
+ end
2860
+ end
2861
+
2862
+ context 'when upsert is false' do
2863
+
2864
+ let(:response) do
2865
+ authorized_collection.update_one(selector, { '$set'=> { field: 'testing' } },
2866
+ upsert: false)
2867
+ end
2868
+
2869
+ let(:updated) do
2870
+ authorized_collection.find.to_a
2871
+ end
2872
+
2873
+ it 'reports that no documents were updated' do
2874
+ expect(response.modified_count).to eq(0)
2875
+ end
2876
+
2877
+ it 'updates no documents in the collection' do
2878
+ expect(updated).to be_empty
2879
+ end
2880
+ end
2881
+
2882
+ context 'when upsert is true' do
2883
+
2884
+ let!(:response) do
2885
+ authorized_collection.update_one(selector, { '$set'=> { field: 'testing' } },
2886
+ upsert: true)
2887
+ end
2888
+
2889
+ let(:updated) do
2890
+ authorized_collection.find.first
2891
+ end
2892
+
2893
+ it 'reports that a document was written' do
2894
+ expect(response.written_count).to eq(1)
2895
+ end
2896
+
2897
+ it 'inserts a document into the collection' do
2898
+ expect(updated[:field]).to eq('testing')
2899
+ end
2900
+ end
2901
+
2902
+ context 'when upsert is not specified' do
2903
+
2904
+ let(:response) do
2905
+ authorized_collection.update_one(selector, { '$set'=> { field: 'testing' } })
2906
+ end
2907
+
2908
+ let(:updated) do
2909
+ authorized_collection.find.to_a
2910
+ end
2911
+
2912
+ it 'reports that no documents were updated' do
2913
+ expect(response.modified_count).to eq(0)
2914
+ end
2915
+
2916
+ it 'updates no documents in the collection' do
2917
+ expect(updated).to be_empty
2918
+ end
2919
+ end
2920
+
2921
+ context 'when the update fails' do
2922
+
2923
+ let(:result) do
2924
+ authorized_collection.update_one(selector, { '$s'=> { field: 'testing' } })
2925
+ end
2926
+
2927
+ it 'raises an OperationFailure' do
2928
+ expect {
2929
+ result
2930
+ }.to raise_exception(Mongo::Error::OperationFailure)
2931
+ end
2932
+ end
2933
+
2934
+ context 'when collection has a validator' do
2935
+ min_server_fcv '3.2'
2936
+
2937
+ around(:each) do |spec|
2938
+ authorized_client[:validating,
2939
+ :validator => { :a => { '$exists' => true } }].tap do |c|
2940
+ c.create
2941
+ end
2942
+ spec.run
2943
+ collection_with_validator.drop
2944
+ end
2945
+
2946
+ before do
2947
+ collection_with_validator.insert_one({ a: 1 })
2948
+ end
2949
+
2950
+ context 'when the document is valid' do
2951
+
2952
+ let(:result) do
2953
+ collection_with_validator.update_one(
2954
+ { :a => { '$gt' => 0 } }, '$inc' => { :a => 1 } )
2955
+ end
2956
+
2957
+ it 'updates successfully' do
2958
+ expect(result.modified_count).to eq(1)
2959
+ end
2960
+ end
2961
+
2962
+ context 'when the document is invalid' do
2963
+
2964
+ context 'when bypass_document_validation is not set' do
2965
+
2966
+ let(:result2) do
2967
+ collection_with_validator.update_one(
2968
+ { :a => { '$gt' => 0 } }, '$unset' => { :a => '' })
2969
+ end
2970
+
2971
+ it 'raises OperationFailure' do
2972
+ expect {
2973
+ result2
2974
+ }.to raise_exception(Mongo::Error::OperationFailure)
2975
+ end
2976
+ end
2977
+
2978
+ context 'when bypass_document_validation is true' do
2979
+
2980
+ let(:result3) do
2981
+ collection_with_validator.update_one(
2982
+ { :a => { '$gt' => 0 } }, { '$unset' => { :a => '' } },
2983
+ :bypass_document_validation => true)
2984
+ end
2985
+
2986
+ it 'updates successfully' do
2987
+ expect(result3.written_count).to eq(1)
2988
+ end
2989
+ end
2990
+ end
2991
+ end
2992
+
2993
+ context 'when there is a collation specified' do
2994
+
2995
+ let(:selector) do
2996
+ { name: 'BANG' }
2997
+ end
2998
+
2999
+ let(:result) do
3000
+ authorized_collection.update_one(selector, { '$set' => { other: 'doink' } }, options)
3001
+ end
3002
+
3003
+ before do
3004
+ authorized_collection.insert_one(name: 'bang')
3005
+ end
3006
+
3007
+ let(:options) do
3008
+ { collation: { locale: 'en_US', strength: 2 } }
3009
+ end
3010
+
3011
+ context 'when the server selected supports collations' do
3012
+ min_server_fcv '3.4'
3013
+
3014
+ it 'applies the collation' do
3015
+ expect(result.written_count).to eq(1)
3016
+ expect(authorized_collection.find(other: 'doink').count).to eq(1)
3017
+ end
3018
+
3019
+ context 'when unacknowledged writes is used' do
3020
+
3021
+ let(:collection_with_unacknowledged_write_concern) do
3022
+ authorized_collection.with(write: { w: 0 })
3023
+ end
3024
+
3025
+ let(:result) do
3026
+ collection_with_unacknowledged_write_concern.update_one(selector, { '$set' => { other: 'doink' } }, options)
3027
+ end
3028
+
3029
+ it 'raises an exception' do
3030
+ expect {
3031
+ result
3032
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3033
+ end
3034
+
3035
+ context 'when a String key is used' do
3036
+
3037
+ let(:options) do
3038
+ { 'collation' => { locale: 'en_US', strength: 2 } }
3039
+ end
3040
+
3041
+ it 'raises an exception' do
3042
+ expect {
3043
+ result
3044
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3045
+ end
3046
+ end
3047
+ end
3048
+ end
3049
+
3050
+ context 'when the server selected does not support collations' do
3051
+ max_server_version '3.2'
3052
+
3053
+ it 'raises an exception' do
3054
+ expect {
3055
+ result
3056
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3057
+ end
3058
+
3059
+ context 'when a String key is used' do
3060
+
3061
+ let(:options) do
3062
+ { 'collation' => { locale: 'en_US', strength: 2 } }
3063
+ end
3064
+
3065
+ it 'raises an exception' do
3066
+ expect {
3067
+ result
3068
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3069
+ end
3070
+ end
3071
+ end
3072
+ end
3073
+
3074
+ context 'when a collation is not specified' do
3075
+
3076
+ let(:selector) do
3077
+ { name: 'BANG' }
3078
+ end
3079
+
3080
+ let(:result) do
3081
+ authorized_collection.update_one(selector, { '$set' => { other: 'doink' } })
3082
+ end
3083
+
3084
+ before do
3085
+ authorized_collection.insert_one(name: 'bang')
3086
+ end
3087
+
3088
+ it 'does not apply the collation' do
3089
+ expect(result.written_count).to eq(0)
3090
+ end
3091
+ end
3092
+
3093
+
3094
+ context 'when arrayFilters is provided' do
3095
+
3096
+ let(:selector) do
3097
+ { _id: 0}
3098
+ end
3099
+
3100
+ context 'when the server supports arrayFilters' do
3101
+ min_server_fcv '3.6'
3102
+
3103
+ before do
3104
+ authorized_collection.insert_one(_id: 0, x: [{ y: 1 }, { y: 2 }, {y: 3 }])
3105
+ end
3106
+
3107
+ let(:result) do
3108
+ authorized_collection.update_one(selector,
3109
+ { '$set' => { 'x.$[i].y' => 5 } },
3110
+ options)
3111
+ end
3112
+
3113
+ context 'when a Symbol key is used' do
3114
+
3115
+ let(:options) do
3116
+ { array_filters: [{ 'i.y' => 3 }] }
3117
+ end
3118
+
3119
+ it 'applies the arrayFilters' do
3120
+ expect(result.matched_count).to eq(1)
3121
+ expect(result.modified_count).to eq(1)
3122
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3123
+ end
3124
+ end
3125
+
3126
+ context 'when a String key is used' do
3127
+
3128
+ let(:options) do
3129
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3130
+ end
3131
+
3132
+ it 'applies the arrayFilters' do
3133
+ expect(result.matched_count).to eq(1)
3134
+ expect(result.modified_count).to eq(1)
3135
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3136
+ end
3137
+ end
3138
+ end
3139
+
3140
+ context 'when the server does not support arrayFilters' do
3141
+ max_server_version '3.4'
3142
+
3143
+ let(:result) do
3144
+ authorized_collection.update_one(selector,
3145
+ { '$set' => { 'x.$[i].y' => 5 } },
3146
+ options)
3147
+ end
3148
+
3149
+ context 'when a Symbol key is used' do
3150
+
3151
+ let(:options) do
3152
+ { array_filters: [{ 'i.y' => 3 }] }
3153
+ end
3154
+
3155
+ it 'raises an exception' do
3156
+ expect {
3157
+ result
3158
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3159
+ end
3160
+ end
3161
+
3162
+ context 'when a String key is used' do
3163
+
3164
+ let(:options) do
3165
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3166
+ end
3167
+
3168
+ it 'raises an exception' do
3169
+ expect {
3170
+ result
3171
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3172
+ end
3173
+ end
3174
+ end
3175
+ end
3176
+
3177
+ context 'when the documents are sent with OP_MSG' do
3178
+ min_server_fcv '3.6'
3179
+
3180
+ let(:documents) do
3181
+ [{ '_id' => 1, 'name' => '1'*16777191 }, { '_id' => 'y' }]
3182
+ end
3183
+
3184
+ before do
3185
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test1' }])
3186
+ client[TEST_COLL].update_one({ a: 1 }, {'$set' => { 'name' => '1'*16777149 }})
3187
+ end
3188
+
3189
+ let(:update_events) do
3190
+ subscriber.started_events.select { |e| e.command_name == 'update' }
3191
+ end
3192
+
3193
+ it 'sends the documents in one OP_MSG' do
3194
+ expect(update_events.size).to eq(1)
3195
+ end
3196
+ end
3197
+
3198
+ context 'when a session is provided' do
3199
+
3200
+ before do
3201
+ authorized_collection.insert_many([{ field: 'test1' }, { field: 'test1' }])
3202
+ end
3203
+
3204
+ let(:session) do
3205
+ authorized_client.start_session
3206
+ end
3207
+
3208
+ let(:operation) do
3209
+ authorized_collection.update_one({ field: 'test' }, { '$set'=> { field: 'testing' } }, session: session)
3210
+ end
3211
+
3212
+ let(:failed_operation) do
3213
+ authorized_collection.update_one({ '$._id' => 1 }, { '$set'=> { field: 'testing' } }, session: session)
3214
+ end
3215
+
3216
+ let(:client) do
3217
+ authorized_client
3218
+ end
3219
+
3220
+ it_behaves_like 'an operation using a session'
3221
+ it_behaves_like 'a failed operation using a session'
3222
+ end
3223
+
3224
+ context 'when unacknowledged writes is used with an explicit session' do
3225
+
3226
+ let(:collection_with_unacknowledged_write_concern) do
3227
+ authorized_collection.with(write: { w: 0 })
3228
+ end
3229
+
3230
+ let(:operation) do
3231
+ collection_with_unacknowledged_write_concern.update_one({ a: 1 }, { '$set' => { x: 1 } }, session: session)
3232
+ end
3233
+
3234
+ it_behaves_like 'an explicit session with an unacknowledged write'
3235
+ end
3236
+
3237
+ context 'when unacknowledged writes is used with an implicit session' do
3238
+
3239
+ let(:collection_with_unacknowledged_write_concern) do
3240
+ client.with(write: { w: 0 })[TEST_COLL]
3241
+ end
3242
+
3243
+ let(:operation) do
3244
+ collection_with_unacknowledged_write_concern.update_one({ a: 1 }, { '$set' => { x: 1 }})
3245
+ end
3246
+
3247
+ it_behaves_like 'an implicit session with an unacknowledged write'
3248
+ end
3249
+
3250
+ context 'when various options passed in' do
3251
+ # w: 2 requires a replica set
3252
+ require_topology :replica_set
3253
+
3254
+ # https://jira.mongodb.org/browse/RUBY-2306
3255
+ min_server_fcv '3.6'
3256
+
3257
+ before do
3258
+ collection.insert_many([{ field: 'test1' }, { field: 'test2' }], session: session)
3259
+ end
3260
+
3261
+ let(:session) do
3262
+ authorized_client.start_session
3263
+ end
3264
+
3265
+ let(:collection) do
3266
+ authorized_collection.with(write_concern: {w: 1})
3267
+ end
3268
+
3269
+ let(:events) do
3270
+ subscriber.command_started_events('update')
3271
+ end
3272
+
3273
+ let!(:command) do
3274
+ Utils.get_command_event(authorized_client, 'update') do |client|
3275
+ collection.update_one(selector, { '$set'=> { field: 'testing' } }, session: session,
3276
+ write_concern: {w: 2}, bypass_document_validation: true, :return_document => :after,
3277
+ upsert: true)
3278
+ end.command
3279
+ end
3280
+
3281
+ it 'updates one successfully with correct options sent to server' do
3282
+ expect(events.length).to eq(1)
3283
+ expect(command[:writeConcern]).to_not be_nil
3284
+ expect(command[:writeConcern][:w]).to eq(2)
3285
+ expect(collection.options[:write_concern]).to eq(w:1)
3286
+ expect(command[:bypassDocumentValidation]).to be(true)
3287
+ expect(command[:updates][0][:upsert]).to be(true)
3288
+ end
3289
+ end
3290
+ end
3291
+
3292
+ describe '#find_one_and_delete' do
3293
+
3294
+ before do
3295
+ authorized_collection.insert_many([{ field: 'test1' }])
3296
+ end
3297
+
3298
+ let(:selector) do
3299
+ { field: 'test1' }
3300
+ end
3301
+
3302
+ context 'when a matching document is found' do
3303
+
3304
+ context 'when a session is provided' do
3305
+
3306
+ let(:operation) do
3307
+ authorized_collection.find_one_and_delete(selector, session: session)
3308
+ end
3309
+
3310
+ let(:failed_operation) do
3311
+ authorized_collection.find_one_and_delete({ '$._id' => 1 }, session: session)
3312
+ end
3313
+
3314
+ let(:session) do
3315
+ authorized_client.start_session
3316
+ end
3317
+
3318
+ let(:client) do
3319
+ authorized_client
3320
+ end
3321
+
3322
+ it_behaves_like 'an operation using a session'
3323
+ it_behaves_like 'a failed operation using a session'
3324
+ end
3325
+
3326
+ context 'when no options are provided' do
3327
+
3328
+ let!(:document) do
3329
+ authorized_collection.find_one_and_delete(selector)
3330
+ end
3331
+
3332
+ it 'deletes the document from the database' do
3333
+ expect(authorized_collection.find.to_a).to be_empty
3334
+ end
3335
+
3336
+ it 'returns the document' do
3337
+ expect(document['field']).to eq('test1')
3338
+ end
3339
+ end
3340
+
3341
+ context 'when a projection is provided' do
3342
+
3343
+ let!(:document) do
3344
+ authorized_collection.find_one_and_delete(selector, projection: { _id: 1 })
3345
+ end
3346
+
3347
+ it 'deletes the document from the database' do
3348
+ expect(authorized_collection.find.to_a).to be_empty
3349
+ end
3350
+
3351
+ it 'returns the document with limited fields' do
3352
+ expect(document['field']).to be_nil
3353
+ expect(document['_id']).to_not be_nil
3354
+ end
3355
+ end
3356
+
3357
+ context 'when a sort is provided' do
3358
+
3359
+ let!(:document) do
3360
+ authorized_collection.find_one_and_delete(selector, sort: { field: 1 })
3361
+ end
3362
+
3363
+ it 'deletes the document from the database' do
3364
+ expect(authorized_collection.find.to_a).to be_empty
3365
+ end
3366
+
3367
+ it 'returns the document with limited fields' do
3368
+ expect(document['field']).to eq('test1')
3369
+ end
3370
+ end
3371
+
3372
+ context 'when max_time_ms is provided' do
3373
+
3374
+ it 'includes the max_time_ms value in the command' do
3375
+ expect {
3376
+ authorized_collection.find_one_and_delete(selector, max_time_ms: 0.1)
3377
+ }.to raise_error(Mongo::Error::OperationFailure)
3378
+ end
3379
+ end
3380
+ end
3381
+
3382
+ context 'when no matching document is found' do
3383
+
3384
+ let(:selector) do
3385
+ { field: 'test5' }
3386
+ end
3387
+
3388
+ let!(:document) do
3389
+ authorized_collection.find_one_and_delete(selector)
3390
+ end
3391
+
3392
+ it 'returns nil' do
3393
+ expect(document).to be_nil
3394
+ end
3395
+ end
3396
+
3397
+ context 'when the operation fails' do
3398
+
3399
+ let(:result) do
3400
+ authorized_collection.find_one_and_delete(selector, max_time_ms: 0.1)
3401
+ end
3402
+
3403
+ it 'raises an OperationFailure' do
3404
+ expect {
3405
+ result
3406
+ }.to raise_exception(Mongo::Error::OperationFailure)
3407
+ end
3408
+ end
3409
+
3410
+ context 'when write_concern is provided' do
3411
+ min_server_fcv '3.2'
3412
+ require_topology :single
3413
+
3414
+ it 'uses the write concern' do
3415
+ expect {
3416
+ authorized_collection.find_one_and_delete(selector,
3417
+ write_concern: { w: 2 })
3418
+ }.to raise_error(Mongo::Error::OperationFailure)
3419
+ end
3420
+ end
3421
+
3422
+ context 'when the collection has a write concern' do
3423
+ min_server_fcv '3.2'
3424
+ require_topology :single
3425
+
3426
+ let(:collection) do
3427
+ authorized_collection.with(write: { w: 2 })
3428
+ end
3429
+
3430
+ it 'uses the write concern' do
3431
+ expect {
3432
+ collection.find_one_and_delete(selector,
3433
+ write_concern: { w: 2 })
3434
+ }.to raise_error(Mongo::Error::OperationFailure)
3435
+ end
3436
+ end
3437
+
3438
+ context 'when collation is specified' do
3439
+
3440
+ let(:selector) do
3441
+ { name: 'BANG' }
3442
+ end
3443
+
3444
+ let(:result) do
3445
+ authorized_collection.find_one_and_delete(selector, options)
3446
+ end
3447
+
3448
+ before do
3449
+ authorized_collection.insert_one(name: 'bang')
3450
+ end
3451
+
3452
+ let(:options) do
3453
+ { collation: { locale: 'en_US', strength: 2 } }
3454
+ end
3455
+
3456
+ context 'when the server selected supports collations' do
3457
+ min_server_fcv '3.4'
3458
+
3459
+ it 'applies the collation' do
3460
+ expect(result['name']).to eq('bang')
3461
+ expect(authorized_collection.find(name: 'bang').count).to eq(0)
3462
+ end
3463
+ end
3464
+
3465
+ context 'when the server selected does not support collations' do
3466
+ max_server_version '3.2'
3467
+
3468
+ it 'raises an exception' do
3469
+ expect {
3470
+ result
3471
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3472
+ end
3473
+
3474
+ context 'when a String key is used' do
3475
+
3476
+ let(:options) do
3477
+ { 'collation' => { locale: 'en_US', strength: 2 } }
3478
+ end
3479
+
3480
+ it 'raises an exception' do
3481
+ expect {
3482
+ result
3483
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3484
+ end
3485
+ end
3486
+ end
3487
+ end
3488
+
3489
+ context 'when collation is not specified' do
3490
+
3491
+ let(:selector) do
3492
+ { name: 'BANG' }
3493
+ end
3494
+
3495
+ let(:result) do
3496
+ authorized_collection.find_one_and_delete(selector)
3497
+ end
3498
+
3499
+ before do
3500
+ authorized_collection.insert_one(name: 'bang')
3501
+ end
3502
+
3503
+ it 'does not apply the collation' do
3504
+ expect(result).to be_nil
3505
+ end
3506
+ end
3507
+
3508
+ context 'when various options passed in' do
3509
+ # w: 2 requires a replica set
3510
+ require_topology :replica_set
3511
+
3512
+ # https://jira.mongodb.org/browse/RUBY-2306
3513
+ min_server_fcv '3.6'
3514
+
3515
+ before do
3516
+ authorized_collection.delete_many
3517
+ authorized_collection.insert_many([{ name: 'test1' }, { name: 'test2' }])
3518
+ end
3519
+
3520
+ let(:collection) do
3521
+ authorized_collection.with(write_concern: {w: 2})
3522
+ end
3523
+
3524
+ let(:session) do
3525
+ authorized_client.start_session
3526
+ end
3527
+
3528
+ let!(:command) do
3529
+ Utils.get_command_event(authorized_client, 'findAndModify') do |client|
3530
+ collection.find_one_and_delete(selector, session: session, write_concern: {w: 2},
3531
+ bypass_document_validation: true, max_time_ms: 300)
3532
+ end.command
3533
+ end
3534
+
3535
+ let(:events) do
3536
+ subscriber.command_started_events('findAndModify')
3537
+ end
3538
+
3539
+ it 'finds and deletes successfully with correct options sent to server' do
3540
+ expect(events.length).to eq(1)
3541
+ expect(command[:writeConcern]).to_not be_nil
3542
+ expect(command[:writeConcern][:w]).to eq(2)
3543
+ expect(command[:bypassDocumentValidation]).to eq(true)
3544
+ expect(command[:maxTimeMS]).to eq(300)
3545
+ end
3546
+ end
3547
+ end
3548
+
3549
+ describe '#find_one_and_update' do
3550
+
3551
+ let(:selector) do
3552
+ { field: 'test1' }
3553
+ end
3554
+
3555
+ before do
3556
+ authorized_collection.insert_many([{ field: 'test1' }])
3557
+ end
3558
+
3559
+ context 'when a matching document is found' do
3560
+
3561
+ context 'when no options are provided' do
3562
+
3563
+ let(:document) do
3564
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }})
3565
+ end
3566
+
3567
+ it 'returns the original document' do
3568
+ expect(document['field']).to eq('test1')
3569
+ end
3570
+ end
3571
+
3572
+ context 'when a session is provided' do
3573
+
3574
+ let(:operation) do
3575
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, session: session)
3576
+ end
3577
+
3578
+ let(:failed_operation) do
3579
+ authorized_collection.find_one_and_update({ '$._id' => 1 }, { '$set' => { field: 'testing' }}, session: session)
3580
+ end
3581
+
3582
+ let(:session) do
3583
+ authorized_client.start_session
3584
+ end
3585
+
3586
+ let(:client) do
3587
+ authorized_client
3588
+ end
3589
+
3590
+ it_behaves_like 'an operation using a session'
3591
+ it_behaves_like 'a failed operation using a session'
3592
+ end
3593
+
3594
+ context 'when no options are provided' do
3595
+
3596
+ let(:document) do
3597
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }})
3598
+ end
3599
+
3600
+ it 'returns the original document' do
3601
+ expect(document['field']).to eq('test1')
3602
+ end
3603
+ end
3604
+
3605
+ context 'when return_document options are provided' do
3606
+
3607
+ context 'when return_document is :after' do
3608
+
3609
+ let(:document) do
3610
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, :return_document => :after)
3611
+ end
3612
+
3613
+ it 'returns the new document' do
3614
+ expect(document['field']).to eq('testing')
3615
+ end
3616
+ end
3617
+
3618
+ context 'when return_document is :before' do
3619
+
3620
+ let(:document) do
3621
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, :return_document => :before)
3622
+ end
3623
+
3624
+ it 'returns the original document' do
3625
+ expect(document['field']).to eq('test1')
3626
+ end
3627
+ end
3628
+ end
3629
+
3630
+ context 'when a projection is provided' do
3631
+
3632
+ let(:document) do
3633
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, projection: { _id: 1 })
3634
+ end
3635
+
3636
+ it 'returns the document with limited fields' do
3637
+ expect(document['field']).to be_nil
3638
+ expect(document['_id']).to_not be_nil
3639
+ end
3640
+ end
3641
+
3642
+ context 'when a sort is provided' do
3643
+
3644
+ let(:document) do
3645
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, sort: { field: 1 })
3646
+ end
3647
+
3648
+ it 'returns the original document' do
3649
+ expect(document['field']).to eq('test1')
3650
+ end
3651
+ end
3652
+ end
3653
+
3654
+ context 'when max_time_ms is provided' do
3655
+
3656
+ it 'includes the max_time_ms value in the command' do
3657
+ expect {
3658
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, max_time_ms: 0.1)
3659
+ }.to raise_error(Mongo::Error::OperationFailure)
3660
+ end
3661
+ end
3662
+
3663
+ context 'when no matching document is found' do
3664
+
3665
+ let(:selector) do
3666
+ { field: 'test5' }
3667
+ end
3668
+
3669
+ let(:document) do
3670
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }})
3671
+ end
3672
+
3673
+ it 'returns nil' do
3674
+ expect(document).to be_nil
3675
+ end
3676
+ end
3677
+
3678
+ context 'when no matching document is found' do
3679
+
3680
+ context 'when no upsert options are provided' do
3681
+
3682
+ let(:selector) do
3683
+ { field: 'test5' }
3684
+ end
3685
+
3686
+ let(:document) do
3687
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }})
3688
+ end
3689
+
3690
+ it 'returns nil' do
3691
+ expect(document).to be_nil
3692
+ end
3693
+ end
3694
+
3695
+ context 'when upsert options are provided' do
3696
+
3697
+ let(:selector) do
3698
+ { field: 'test5' }
3699
+ end
3700
+
3701
+ let(:document) do
3702
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, :upsert => true, :return_document => :after)
3703
+ end
3704
+
3705
+ it 'returns the new document' do
3706
+ expect(document['field']).to eq('testing')
3707
+ end
3708
+ end
3709
+ end
3710
+
3711
+ context 'when the operation fails' do
3712
+
3713
+ let(:result) do
3714
+ authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, max_time_ms: 0.1)
3715
+ end
3716
+
3717
+ it 'raises an OperationFailure' do
3718
+ expect {
3719
+ result
3720
+ }.to raise_exception(Mongo::Error::OperationFailure)
3721
+ end
3722
+ end
3723
+
3724
+ context 'when collection has a validator' do
3725
+ min_server_fcv '3.2'
3726
+
3727
+ around(:each) do |spec|
3728
+ authorized_client[:validating].drop
3729
+ authorized_client[:validating,
3730
+ :validator => { :a => { '$exists' => true } }].tap do |c|
3731
+ c.create
3732
+ end
3733
+ spec.run
3734
+ collection_with_validator.drop
3735
+ end
3736
+
3737
+ before do
3738
+ collection_with_validator.insert_one({ a: 1 })
3739
+ end
3740
+
3741
+ context 'when the document is valid' do
3742
+
3743
+ let(:result) do
3744
+ collection_with_validator.find_one_and_update(
3745
+ { a: 1 }, { '$inc' => { :a => 1 } }, :return_document => :after)
3746
+ end
3747
+
3748
+ it 'updates successfully' do
3749
+ expect(result['a']).to eq(2)
3750
+ end
3751
+ end
3752
+
3753
+ context 'when the document is invalid' do
3754
+
3755
+ context 'when bypass_document_validation is not set' do
3756
+
3757
+ let(:result2) do
3758
+ collection_with_validator.find_one_and_update(
3759
+ { a: 1 }, { '$unset' => { :a => '' } }, :return_document => :after)
3760
+ end
3761
+
3762
+ it 'raises OperationFailure' do
3763
+ expect {
3764
+ result2
3765
+ }.to raise_exception(Mongo::Error::OperationFailure)
3766
+ end
3767
+ end
3768
+
3769
+ context 'when bypass_document_validation is true' do
3770
+
3771
+ let(:result3) do
3772
+ collection_with_validator.find_one_and_update(
3773
+ { a: 1 }, { '$unset' => { :a => '' } },
3774
+ :bypass_document_validation => true,
3775
+ :return_document => :after)
3776
+ end
3777
+
3778
+ it 'updates successfully' do
3779
+ expect(result3['a']).to be_nil
3780
+ end
3781
+ end
3782
+ end
3783
+ end
3784
+
3785
+ context 'when write_concern is provided' do
3786
+ min_server_fcv '3.2'
3787
+ require_topology :single
3788
+
3789
+ it 'uses the write concern' do
3790
+ expect {
3791
+ authorized_collection.find_one_and_update(selector,
3792
+ { '$set' => { field: 'testing' }},
3793
+ write_concern: { w: 2 })
3794
+ }.to raise_error(Mongo::Error::OperationFailure)
3795
+ end
3796
+ end
3797
+
3798
+ context 'when the collection has a write concern' do
3799
+ min_server_fcv '3.2'
3800
+ require_topology :single
3801
+
3802
+ let(:collection) do
3803
+ authorized_collection.with(write: { w: 2 })
3804
+ end
3805
+
3806
+ it 'uses the write concern' do
3807
+ expect {
3808
+ collection.find_one_and_update(selector,
3809
+ { '$set' => { field: 'testing' }},
3810
+ write_concern: { w: 2 })
3811
+ }.to raise_error(Mongo::Error::OperationFailure)
3812
+ end
3813
+ end
3814
+
3815
+ context 'when a collation is specified' do
3816
+
3817
+ let(:selector) do
3818
+ { name: 'BANG' }
3819
+ end
3820
+
3821
+ let(:result) do
3822
+ authorized_collection.find_one_and_update(selector,
3823
+ { '$set' => { other: 'doink' } },
3824
+ options)
3825
+ end
3826
+
3827
+ before do
3828
+ authorized_collection.insert_one(name: 'bang')
3829
+ end
3830
+
3831
+ let(:options) do
3832
+ { collation: { locale: 'en_US', strength: 2 } }
3833
+ end
3834
+
3835
+ context 'when the server selected supports collations' do
3836
+ min_server_fcv '3.4'
3837
+
3838
+ it 'applies the collation' do
3839
+ expect(result['name']).to eq('bang')
3840
+ expect(authorized_collection.find({ name: 'bang' }, limit: -1).first['other']).to eq('doink')
3841
+ end
3842
+ end
3843
+
3844
+ context 'when the server selected does not support collations' do
3845
+ max_server_version '3.2'
3846
+
3847
+ it 'raises an exception' do
3848
+ expect {
3849
+ result
3850
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3851
+ end
3852
+
3853
+ context 'when a String key is used' do
3854
+
3855
+ let(:options) do
3856
+ { 'collation' => { locale: 'en_US', strength: 2 } }
3857
+ end
3858
+
3859
+ it 'raises an exception' do
3860
+ expect {
3861
+ result
3862
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
3863
+ end
3864
+ end
3865
+ end
3866
+ end
3867
+
3868
+ context 'when there is no collation specified' do
3869
+
3870
+ let(:selector) do
3871
+ { name: 'BANG' }
3872
+ end
3873
+
3874
+ let(:result) do
3875
+ authorized_collection.find_one_and_update(selector, { '$set' => { other: 'doink' } })
3876
+ end
3877
+
3878
+ before do
3879
+ authorized_collection.insert_one(name: 'bang')
3880
+ end
3881
+
3882
+ it 'does not apply the collation' do
3883
+ expect(result).to be_nil
3884
+ end
3885
+ end
3886
+
3887
+ context 'when arrayFilters is provided' do
3888
+
3889
+ let(:selector) do
3890
+ { _id: 0 }
3891
+ end
3892
+
3893
+ context 'when the server supports arrayFilters' do
3894
+ min_server_fcv '3.6'
3895
+
3896
+ before do
3897
+ authorized_collection.insert_one(_id: 0, x: [{ y: 1 }, { y: 2 }, { y: 3 }])
3898
+ end
3899
+
3900
+ let(:result) do
3901
+ authorized_collection.find_one_and_update(selector,
3902
+ { '$set' => { 'x.$[i].y' => 5 } },
3903
+ options)
3904
+ end
3905
+
3906
+ context 'when a Symbol key is used' do
3907
+
3908
+ let(:options) do
3909
+ { array_filters: [{ 'i.y' => 3 }] }
3910
+ end
3911
+
3912
+
3913
+ it 'applies the arrayFilters' do
3914
+ expect(result['x']).to eq([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 3 }])
3915
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3916
+ end
3917
+ end
3918
+
3919
+ context 'when a String key is used' do
3920
+
3921
+ let(:options) do
3922
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3923
+ end
3924
+
3925
+ it 'applies the arrayFilters' do
3926
+ expect(result['x']).to eq([{ 'y' => 1 }, { 'y' => 2 }, { 'y' => 3 }])
3927
+ expect(authorized_collection.find(selector).first['x'].last['y']).to eq(5)
3928
+ end
3929
+ end
3930
+ end
3931
+
3932
+ context 'when the server selected does not support arrayFilters' do
3933
+ max_server_version '3.4'
3934
+
3935
+ let(:result) do
3936
+ authorized_collection.find_one_and_update(selector,
3937
+ { '$set' => { 'x.$[i].y' => 5 } },
3938
+ options)
3939
+ end
3940
+
3941
+ context 'when a Symbol key is used' do
3942
+
3943
+ let(:options) do
3944
+ { array_filters: [{ 'i.y' => 3 }] }
3945
+ end
3946
+
3947
+ it 'raises an exception' do
3948
+ expect {
3949
+ result
3950
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3951
+ end
3952
+ end
3953
+
3954
+ context 'when a String key is used' do
3955
+
3956
+ let(:options) do
3957
+ { 'array_filters' => [{ 'i.y' => 3 }] }
3958
+ end
3959
+
3960
+ it 'raises an exception' do
3961
+ expect {
3962
+ result
3963
+ }.to raise_exception(Mongo::Error::UnsupportedArrayFilters)
3964
+ end
3965
+ end
3966
+ end
3967
+ end
3968
+
3969
+ context 'when various options passed in' do
3970
+ # w: 2 requires a replica set
3971
+ require_topology :replica_set
3972
+
3973
+ # https://jira.mongodb.org/browse/RUBY-2306
3974
+ min_server_fcv '3.6'
3975
+
3976
+ let(:session) do
3977
+ authorized_client.start_session
3978
+ end
3979
+
3980
+ let(:events) do
3981
+ subscriber.command_started_events('findAndModify')
3982
+ end
3983
+
3984
+ let(:collection) do
3985
+ authorized_collection.with(write_concern: {w: 2})
3986
+ end
3987
+
3988
+ let(:selector) do
3989
+ {field: 'test1'}
3990
+ end
3991
+
3992
+ before do
3993
+ collection.insert_one({field: 'test1'}, session: session)
3994
+ end
3995
+
3996
+ let!(:command) do
3997
+ Utils.get_command_event(authorized_client, 'findAndModify') do |client|
3998
+ collection.find_one_and_update(selector, { '$set' => {field: 'testing'}},
3999
+ :return_document => :after, write_concern: {w: 1}, upsert: true,
4000
+ bypass_document_validation: true, max_time_ms: 100, session: session)
4001
+ end.command
4002
+ end
4003
+
4004
+ it 'find and updates successfully with correct options sent to server' do
4005
+ expect(events.length).to eq(1)
4006
+ expect(command[:writeConcern]).to_not be_nil
4007
+ expect(command[:writeConcern][:w]).to eq(1)
4008
+ expect(command[:upsert]).to eq(true)
4009
+ expect(command[:bypassDocumentValidation]).to be(true)
4010
+ expect(command[:maxTimeMS]).to eq(100)
4011
+ end
4012
+ end
4013
+ end
4014
+
4015
+ describe '#find_one_and_replace' do
4016
+
4017
+ before do
4018
+ authorized_collection.insert_many([{ field: 'test1', other: 'sth' }])
4019
+ end
4020
+
4021
+ let(:selector) do
4022
+ { field: 'test1' }
4023
+ end
4024
+
4025
+ context 'when a matching document is found' do
4026
+
4027
+ context 'when no options are provided' do
4028
+
4029
+ let(:document) do
4030
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' })
4031
+ end
4032
+
4033
+ it 'returns the original document' do
4034
+ expect(document['field']).to eq('test1')
4035
+ end
4036
+ end
4037
+
4038
+ context 'when a session is provided' do
4039
+
4040
+ let(:operation) do
4041
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, session: session)
4042
+ end
4043
+
4044
+ let(:failed_operation) do
4045
+ authorized_collection.find_one_and_replace({ '$._id' => 1}, { field: 'testing' }, session: session)
4046
+ end
4047
+
4048
+ let(:session) do
4049
+ authorized_client.start_session
4050
+ end
4051
+
4052
+ let(:client) do
4053
+ authorized_client
4054
+ end
4055
+
4056
+ it_behaves_like 'an operation using a session'
4057
+ it_behaves_like 'a failed operation using a session'
4058
+ end
4059
+
4060
+ context 'when return_document options are provided' do
4061
+
4062
+ context 'when return_document is :after' do
4063
+
4064
+ let(:document) do
4065
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, :return_document => :after)
4066
+ end
4067
+
4068
+ it 'returns the new document' do
4069
+ expect(document['field']).to eq('testing')
4070
+ end
4071
+ end
4072
+
4073
+ context 'when return_document is :before' do
4074
+
4075
+ let(:document) do
4076
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, :return_document => :before)
4077
+ end
4078
+
4079
+ it 'returns the original document' do
4080
+ expect(document['field']).to eq('test1')
4081
+ end
4082
+ end
4083
+ end
4084
+
4085
+ context 'when a projection is provided' do
4086
+
4087
+ let(:document) do
4088
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, projection: { _id: 1 })
4089
+ end
4090
+
4091
+ it 'returns the document with limited fields' do
4092
+ expect(document['field']).to be_nil
4093
+ expect(document['_id']).to_not be_nil
4094
+ end
4095
+ end
4096
+
4097
+ context 'when a sort is provided' do
4098
+
4099
+ let(:document) do
4100
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, :sort => { field: 1 })
4101
+ end
4102
+
4103
+ it 'returns the original document' do
4104
+ expect(document['field']).to eq('test1')
4105
+ end
4106
+ end
4107
+ end
4108
+
4109
+ context 'when no matching document is found' do
4110
+
4111
+ context 'when no upsert options are provided' do
4112
+
4113
+ let(:selector) do
4114
+ { field: 'test5' }
4115
+ end
4116
+
4117
+ let(:document) do
4118
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' })
4119
+ end
4120
+
4121
+ it 'returns nil' do
4122
+ expect(document).to be_nil
4123
+ end
4124
+ end
4125
+
4126
+ context 'when upsert options are provided' do
4127
+
4128
+ let(:selector) do
4129
+ { field: 'test5' }
4130
+ end
4131
+
4132
+ let(:document) do
4133
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, :upsert => true, :return_document => :after)
4134
+ end
4135
+
4136
+ it 'returns the new document' do
4137
+ expect(document['field']).to eq('testing')
4138
+ end
4139
+ end
4140
+ end
4141
+
4142
+ context 'when max_time_ms is provided' do
4143
+
4144
+ it 'includes the max_time_ms value in the command' do
4145
+ expect {
4146
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, max_time_ms: 0.1)
4147
+ }.to raise_error(Mongo::Error::OperationFailure)
4148
+ end
4149
+ end
4150
+
4151
+ context 'when the operation fails' do
4152
+
4153
+ let(:result) do
4154
+ authorized_collection.find_one_and_replace(selector, { field: 'testing' }, max_time_ms: 0.1)
4155
+ end
4156
+
4157
+ it 'raises an OperationFailure' do
4158
+ expect {
4159
+ result
4160
+ }.to raise_exception(Mongo::Error::OperationFailure)
4161
+ end
4162
+ end
4163
+
4164
+ context 'when collection has a validator' do
4165
+ min_server_fcv '3.2'
4166
+
4167
+ around(:each) do |spec|
4168
+ authorized_client[:validating].drop
4169
+ authorized_client[:validating,
4170
+ :validator => { :a => { '$exists' => true } }].tap do |c|
4171
+ c.create
4172
+ end
4173
+ spec.run
4174
+ collection_with_validator.drop
4175
+ end
4176
+
4177
+ before do
4178
+ collection_with_validator.insert_one({ a: 1 })
4179
+ end
4180
+
4181
+ context 'when the document is valid' do
4182
+
4183
+ let(:result) do
4184
+ collection_with_validator.find_one_and_replace(
4185
+ { a: 1 }, { a: 5 }, :return_document => :after)
4186
+ end
4187
+
4188
+ it 'replaces successfully when document is valid' do
4189
+ expect(result[:a]).to eq(5)
4190
+ end
4191
+ end
4192
+
4193
+ context 'when the document is invalid' do
4194
+
4195
+ context 'when bypass_document_validation is not set' do
4196
+
4197
+ let(:result2) do
4198
+ collection_with_validator.find_one_and_replace(
4199
+ { a: 1 }, { x: 5 }, :return_document => :after)
4200
+ end
4201
+
4202
+ it 'raises OperationFailure' do
4203
+ expect {
4204
+ result2
4205
+ }.to raise_exception(Mongo::Error::OperationFailure)
4206
+ end
4207
+ end
4208
+
4209
+ context 'when bypass_document_validation is true' do
4210
+
4211
+ let(:result3) do
4212
+ collection_with_validator.find_one_and_replace(
4213
+ { a: 1 }, { x: 1 }, :bypass_document_validation => true,
4214
+ :return_document => :after)
4215
+ end
4216
+
4217
+ it 'replaces successfully' do
4218
+ expect(result3[:x]).to eq(1)
4219
+ expect(result3[:a]).to be_nil
4220
+ end
4221
+ end
4222
+ end
4223
+ end
4224
+
4225
+ context 'when write_concern is provided' do
4226
+ min_server_fcv '3.2'
4227
+ require_topology :single
4228
+
4229
+ it 'uses the write concern' do
4230
+ expect {
4231
+ authorized_collection.find_one_and_replace(selector,
4232
+ { field: 'testing' },
4233
+ write_concern: { w: 2 })
4234
+ }.to raise_error(Mongo::Error::OperationFailure)
4235
+ end
4236
+ end
4237
+
4238
+ context 'when the collection has a write concern' do
4239
+ min_server_fcv '3.2'
4240
+ require_topology :single
4241
+
4242
+ let(:collection) do
4243
+ authorized_collection.with(write: { w: 2 })
4244
+ end
4245
+
4246
+ it 'uses the write concern' do
4247
+ expect {
4248
+ collection.find_one_and_replace(selector,
4249
+ { field: 'testing' },
4250
+ write_concern: { w: 2 })
4251
+ }.to raise_error(Mongo::Error::OperationFailure)
4252
+ end
4253
+ end
4254
+
4255
+ context 'when collation is provided' do
4256
+
4257
+ let(:selector) do
4258
+ { name: 'BANG' }
4259
+ end
4260
+
4261
+ let(:result) do
4262
+ authorized_collection.find_one_and_replace(selector,
4263
+ { name: 'doink' },
4264
+ options)
4265
+ end
4266
+
4267
+ before do
4268
+ authorized_collection.insert_one(name: 'bang')
4269
+ end
4270
+
4271
+ let(:options) do
4272
+ { collation: { locale: 'en_US', strength: 2 } }
4273
+ end
4274
+
4275
+ context 'when the server selected supports collations' do
4276
+ min_server_fcv '3.4'
4277
+
4278
+ it 'applies the collation' do
4279
+ expect(result['name']).to eq('bang')
4280
+ expect(authorized_collection.find(name: 'doink').count).to eq(1)
4281
+ end
4282
+ end
4283
+
4284
+ context 'when the server selected does not support collations' do
4285
+ max_server_version '3.2'
4286
+
4287
+ it 'raises an exception' do
4288
+ expect {
4289
+ result
4290
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
4291
+ end
4292
+
4293
+ context 'when a String key is used' do
4294
+
4295
+ let(:options) do
4296
+ { 'collation' => { locale: 'en_US', strength: 2 } }
4297
+ end
4298
+
4299
+ it 'raises an exception' do
4300
+ expect {
4301
+ result
4302
+ }.to raise_exception(Mongo::Error::UnsupportedCollation)
4303
+ end
4304
+ end
4305
+ end
4306
+ end
4307
+
4308
+ context 'when collation is not specified' do
4309
+
4310
+ let(:selector) do
4311
+ { name: 'BANG' }
4312
+ end
4313
+
4314
+ let(:result) do
4315
+ authorized_collection.find_one_and_replace(selector, { name: 'doink' })
4316
+ end
4317
+
4318
+ before do
4319
+ authorized_collection.insert_one(name: 'bang')
4320
+ end
4321
+
4322
+ it 'does not apply the collation' do
4323
+ expect(result).to be_nil
4324
+ end
4325
+ end
4326
+
4327
+ context 'when various options passed in' do
4328
+ # https://jira.mongodb.org/browse/RUBY-2306
4329
+ min_server_fcv '3.6'
4330
+
4331
+ before do
4332
+ authorized_collection.insert_one({field: 'test1'})
4333
+ end
4334
+
4335
+ let(:session) do
4336
+ authorized_client.start_session
4337
+ end
4338
+
4339
+ let(:events) do
4340
+ subscriber.command_started_events('findAndModify')
4341
+ end
4342
+
4343
+ let(:collection) do
4344
+ authorized_collection.with(write_concern: { w: 2 })
4345
+ end
4346
+
4347
+ let!(:command) do
4348
+ Utils.get_command_event(authorized_client, 'findAndModify') do |client|
4349
+ collection.find_one_and_replace(selector, { '$set' => {field: 'test5'}},
4350
+ :return_document => :after, write_concern: {w: 1}, session: session,
4351
+ upsert: true, bypass_document_validation: false, max_time_ms: 200)
4352
+ end.command
4353
+ end
4354
+
4355
+ it 'find and replaces successfully with correct options sent to server' do
4356
+ expect(events.length).to eq(1)
4357
+ expect(command[:writeConcern]).to_not be_nil
4358
+ expect(command[:writeConcern][:w]).to eq(1)
4359
+ expect(command[:upsert]).to be(true)
4360
+ expect(command[:bypassDocumentValidation]).to be false
4361
+ expect(command[:maxTimeMS]).to eq(200)
4362
+ end
4363
+ end
4364
+ end
4365
+ end