mongo 2.14.0.rc1 → 2.16.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (1230) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +5 -2
  4. data/Rakefile +46 -18
  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 +4 -1
  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 +109 -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 +59 -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 +91 -34
  101. data/lib/mongo/database/view.rb +6 -3
  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 +8 -0
  248. data/lib/mongo/operation/collections_info/result.rb +19 -1
  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 +13 -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 +120 -17
  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 +41 -13
  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 +31 -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 +8 -3
  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 +11 -8
  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 +4 -1
  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 +12 -5
  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 +65 -8
  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 +33 -19
  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 +28 -6
  527. data/spec/integration/sdam_events_spec.rb +54 -31
  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 +26 -7
  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 +17 -6
  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 +4 -1
  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 +8 -5
  581. data/spec/mongo/caching_cursor_spec.rb +3 -0
  582. data/spec/mongo/client_construction_spec.rb +475 -65
  583. data/spec/mongo/client_encryption_spec.rb +19 -10
  584. data/spec/mongo/client_spec.rb +33 -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 +13 -10
  589. data/spec/mongo/cluster/topology/sharded_spec.rb +6 -3
  590. data/spec/mongo/cluster/topology/single_spec.rb +6 -3
  591. data/spec/mongo/cluster/topology/unknown_spec.rb +4 -1
  592. data/spec/mongo/cluster/topology_spec.rb +4 -1
  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 +8 -3
  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 +69 -0
  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 -4848
  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 -5
  614. data/spec/mongo/crypt/binding/binary_spec.rb +3 -5
  615. data/spec/mongo/crypt/binding/context_spec.rb +4 -6
  616. data/spec/mongo/crypt/binding/helpers_spec.rb +3 -5
  617. data/spec/mongo/crypt/binding/mongocrypt_spec.rb +4 -6
  618. data/spec/mongo/crypt/binding/status_spec.rb +3 -5
  619. data/spec/mongo/crypt/binding/version_spec.rb +3 -5
  620. data/spec/mongo/crypt/binding_unloaded_spec.rb +3 -0
  621. data/spec/mongo/crypt/data_key_context_spec.rb +4 -1
  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 -5
  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 +154 -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 +4 -1
  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 +4 -1
  677. data/spec/mongo/monitoring/event/topology_changed_spec.rb +4 -1
  678. data/spec/mongo/monitoring/event/topology_closed_spec.rb +4 -1
  679. data/spec/mongo/monitoring/event/topology_opening_spec.rb +4 -1
  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 +7 -4
  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 +5 -2
  699. data/spec/mongo/operation/insert/op_msg_spec.rb +7 -4
  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 +9 -4
  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 +5 -2
  711. data/spec/mongo/operation/update/op_msg_spec.rb +7 -4
  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 +10 -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 +168 -57
  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 +625 -594
  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 +7 -3
  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 +46 -3
  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 +5 -2
  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 -19
  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 +4 -1
  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 +8 -4
  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 +253 -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/LICENSE +20 -0
  809. data/spec/shared/bin/get-mongodb-download-url +17 -0
  810. data/spec/shared/bin/s3-copy +45 -0
  811. data/spec/shared/bin/s3-upload +69 -0
  812. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  813. data/spec/shared/lib/mrss/cluster_config.rb +226 -0
  814. data/spec/shared/lib/mrss/constraints.rb +385 -0
  815. data/spec/shared/lib/mrss/docker_runner.rb +271 -0
  816. data/spec/shared/lib/mrss/event_subscriber.rb +200 -0
  817. data/spec/shared/lib/mrss/lite_constraints.rb +191 -0
  818. data/spec/shared/lib/mrss/server_version_registry.rb +120 -0
  819. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  820. data/spec/shared/lib/mrss/utils.rb +15 -0
  821. data/spec/shared/share/Dockerfile.erb +323 -0
  822. data/spec/shared/share/haproxy-1.conf +16 -0
  823. data/spec/shared/share/haproxy-2.conf +17 -0
  824. data/spec/shared/shlib/distro.sh +73 -0
  825. data/spec/shared/shlib/server.sh +367 -0
  826. data/spec/shared/shlib/set_env.sh +131 -0
  827. data/spec/solo/clean_exit_spec.rb +24 -0
  828. data/spec/spec_helper.rb +11 -4
  829. data/spec/spec_tests/auth_spec.rb +30 -13
  830. data/spec/spec_tests/change_streams_spec.rb +4 -1
  831. data/spec/spec_tests/change_streams_unified_spec.rb +13 -0
  832. data/spec/spec_tests/client_side_encryption_spec.rb +3 -0
  833. data/spec/spec_tests/cmap_spec.rb +7 -1
  834. data/spec/spec_tests/collection_management_spec.rb +13 -0
  835. data/spec/spec_tests/command_monitoring_spec.rb +32 -27
  836. data/spec/spec_tests/command_monitoring_unified_spec.rb +13 -0
  837. data/spec/spec_tests/connection_string_spec.rb +3 -0
  838. data/spec/spec_tests/crud_spec.rb +3 -0
  839. data/spec/spec_tests/crud_unified_spec.rb +13 -0
  840. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +5 -5
  841. data/spec/spec_tests/data/change_streams/{change-streams-resume-whitelist.yml → change-streams-resume-allowlist.yml} +58 -58
  842. data/spec/spec_tests/data/change_streams/change-streams-resume-errorLabels.yml +46 -46
  843. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  844. data/spec/spec_tests/data/change_streams_unified/change-streams.yml +72 -0
  845. data/spec/spec_tests/data/collection_management/timeseries-collection.yml +129 -0
  846. data/spec/spec_tests/data/command_monitoring/find.yml +9 -9
  847. data/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml +340 -0
  848. data/spec/spec_tests/data/crud/read/aggregate-collation.yml +2 -1
  849. data/spec/spec_tests/data/crud/read/aggregate-out.yml +1 -0
  850. data/spec/spec_tests/data/crud/read/count-collation.yml +2 -1
  851. data/spec/spec_tests/data/crud/read/distinct-collation.yml +2 -1
  852. data/spec/spec_tests/data/crud/read/find-collation.yml +2 -1
  853. data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +2 -1
  854. data/spec/spec_tests/data/crud/write/bulkWrite.yml +26 -22
  855. data/spec/spec_tests/data/crud/write/deleteMany-collation.yml +2 -1
  856. data/spec/spec_tests/data/crud/write/deleteOne-collation.yml +2 -1
  857. data/spec/spec_tests/data/crud/write/findOneAndDelete-collation.yml +3 -2
  858. data/spec/spec_tests/data/crud/write/findOneAndReplace-collation.yml +2 -1
  859. data/spec/spec_tests/data/crud/write/findOneAndUpdate-collation.yml +3 -2
  860. data/spec/spec_tests/data/crud/write/insertMany.yml +26 -22
  861. data/spec/spec_tests/data/crud/write/replaceOne-collation.yml +3 -2
  862. data/spec/spec_tests/data/crud/write/updateMany-collation.yml +2 -1
  863. data/spec/spec_tests/data/crud/write/updateOne-collation.yml +2 -1
  864. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  865. data/spec/spec_tests/data/crud_unified/updateWithPipelines.yml +305 -0
  866. data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +1 -0
  867. data/spec/spec_tests/data/crud_v2/db-aggregate.yml +1 -0
  868. data/spec/spec_tests/data/load_balancers/event-monitoring.yml +99 -0
  869. data/spec/spec_tests/data/load_balancers/lb-connection-establishment.yml +36 -0
  870. data/spec/spec_tests/data/load_balancers/non-lb-connection-establishment.yml +56 -0
  871. data/spec/spec_tests/data/load_balancers/server-selection.yml +50 -0
  872. data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +1 -1
  873. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +7 -6
  874. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +2 -1
  875. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +7 -6
  876. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +2 -1
  877. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +7 -6
  878. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +2 -1
  879. data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +1 -1
  880. data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +1 -1
  881. data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +1 -1
  882. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  883. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  884. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  885. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +3 -1
  886. data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +1 -1
  887. data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +1 -1
  888. data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +1 -1
  889. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +1 -1
  890. data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +1 -1
  891. data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +1 -1
  892. data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +1 -1
  893. data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +1 -1
  894. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +1 -1
  895. data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +1 -1
  896. data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +1 -1
  897. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  898. data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +1 -1
  899. data/spec/spec_tests/data/retryable_reads/mapReduce.yml +3 -1
  900. data/spec/spec_tests/data/retryable_writes/bulkWrite.yml +30 -24
  901. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +5 -4
  902. data/spec/spec_tests/data/sdam/errors/error_handling_handshake.yml +2 -1
  903. data/spec/spec_tests/data/sdam/errors/non-stale-network-error.yml +2 -1
  904. data/spec/spec_tests/data/sdam/errors/non-stale-network-timeout-error.yml +2 -1
  905. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml +2 -1
  906. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml +2 -1
  907. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml +61 -0
  908. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-greater-NotMasterNoSlaveOk.yml → non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml} +5 -4
  909. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-greater-NotMasterOrSecondary.yml → non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml} +5 -4
  910. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-greater-NotMaster.yml → non-stale-topologyVersion-greater-NotWritablePrimary.yml} +5 -4
  911. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml +2 -1
  912. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml +2 -1
  913. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml +2 -1
  914. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml +2 -1
  915. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml +52 -0
  916. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-missing-NotMasterNoSlaveOk.yml → non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml} +5 -4
  917. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-missing-NotMasterOrSecondary.yml → non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml} +5 -4
  918. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-missing-NotMaster.yml → non-stale-topologyVersion-missing-NotWritablePrimary.yml} +5 -4
  919. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml +2 -1
  920. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml +2 -1
  921. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml +2 -1
  922. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml +2 -1
  923. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml +61 -0
  924. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-proccessId-changed-NotMasterNoSlaveOk.yml → non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml} +5 -4
  925. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-proccessId-changed-NotMasterOrSecondary.yml → non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml} +5 -4
  926. data/spec/spec_tests/data/sdam/errors/{non-stale-topologyVersion-proccessId-changed-NotMaster.yml → non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml} +5 -4
  927. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml +2 -1
  928. data/spec/spec_tests/data/sdam/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml +2 -1
  929. data/spec/spec_tests/data/sdam/errors/post-42-InterruptedAtShutdown.yml +2 -1
  930. data/spec/spec_tests/data/sdam/errors/post-42-InterruptedDueToReplStateChange.yml +2 -1
  931. data/spec/spec_tests/data/sdam/errors/post-42-LegacyNotPrimary.yml +47 -0
  932. data/spec/spec_tests/data/sdam/errors/{post-42-NotMasterNoSlaveOk.yml → post-42-NotPrimaryNoSecondaryOk.yml} +5 -4
  933. data/spec/spec_tests/data/sdam/errors/{post-42-NotMasterOrSecondary.yml → post-42-NotPrimaryOrSecondary.yml} +5 -4
  934. data/spec/spec_tests/data/sdam/errors/{post-42-NotMaster.yml → post-42-NotWritablePrimary.yml} +5 -4
  935. data/spec/spec_tests/data/sdam/errors/post-42-PrimarySteppedDown.yml +2 -1
  936. data/spec/spec_tests/data/sdam/errors/post-42-ShutdownInProgress.yml +2 -1
  937. data/spec/spec_tests/data/sdam/errors/pre-42-InterruptedAtShutdown.yml +2 -1
  938. data/spec/spec_tests/data/sdam/errors/pre-42-InterruptedDueToReplStateChange.yml +2 -1
  939. data/spec/spec_tests/data/sdam/errors/{pre-42-NotMaster.yml → pre-42-LegacyNotPrimary.yml} +6 -5
  940. data/spec/spec_tests/data/sdam/errors/pre-42-NotPrimaryNoSecondaryOk.yml +47 -0
  941. data/spec/spec_tests/data/sdam/errors/{pre-42-NotMasterOrSecondary.yml → pre-42-NotPrimaryOrSecondary.yml} +5 -4
  942. data/spec/spec_tests/data/sdam/errors/{pre-42-NotMasterNoSlaveOk.yml → pre-42-NotWritablePrimary.yml} +6 -5
  943. data/spec/spec_tests/data/sdam/errors/pre-42-PrimarySteppedDown.yml +2 -1
  944. data/spec/spec_tests/data/sdam/errors/pre-42-ShutdownInProgress.yml +2 -1
  945. data/spec/spec_tests/data/sdam/errors/prefer-error-code.yml +54 -0
  946. data/spec/spec_tests/data/sdam/errors/stale-generation-InterruptedAtShutdown.yml +4 -2
  947. data/spec/spec_tests/data/sdam/errors/stale-generation-InterruptedDueToReplStateChange.yml +4 -2
  948. data/spec/spec_tests/data/sdam/errors/{stale-generation-NotMasterNoSlaveOk.yml → stale-generation-NotPrimaryNoSecondaryOk.yml} +7 -5
  949. data/spec/spec_tests/data/sdam/errors/{stale-generation-NotMasterOrSecondary.yml → stale-generation-NotPrimaryOrSecondary.yml} +7 -5
  950. data/spec/spec_tests/data/sdam/errors/{stale-generation-NotMaster.yml → stale-generation-NotWritablePrimary.yml} +7 -5
  951. data/spec/spec_tests/data/sdam/errors/stale-generation-PrimarySteppedDown.yml +4 -2
  952. data/spec/spec_tests/data/sdam/errors/stale-generation-ShutdownInProgress.yml +4 -2
  953. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml +4 -2
  954. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml +4 -2
  955. data/spec/spec_tests/data/sdam/errors/{stale-generation-afterHandshakeCompletes-NotMaster.yml → stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml} +8 -6
  956. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml +91 -0
  957. data/spec/spec_tests/data/sdam/errors/{stale-generation-afterHandshakeCompletes-NotMasterOrSecondary.yml → stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml} +7 -5
  958. data/spec/spec_tests/data/sdam/errors/{stale-generation-afterHandshakeCompletes-NotMasterNoSlaveOk.yml → stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml} +8 -6
  959. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml +4 -2
  960. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml +4 -2
  961. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-network.yml +6 -4
  962. data/spec/spec_tests/data/sdam/errors/stale-generation-afterHandshakeCompletes-timeout.yml +6 -4
  963. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml +4 -2
  964. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml +4 -2
  965. data/spec/spec_tests/data/sdam/errors/{stale-generation-beforeHandshakeCompletes-NotMaster.yml → stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml} +8 -6
  966. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml +91 -0
  967. data/spec/spec_tests/data/sdam/errors/{stale-generation-beforeHandshakeCompletes-NotMasterOrSecondary.yml → stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml} +7 -5
  968. data/spec/spec_tests/data/sdam/errors/{stale-generation-beforeHandshakeCompletes-NotMasterNoSlaveOk.yml → stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml} +8 -6
  969. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml +4 -2
  970. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml +4 -2
  971. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-network.yml +6 -4
  972. data/spec/spec_tests/data/sdam/errors/stale-generation-beforeHandshakeCompletes-timeout.yml +6 -4
  973. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-InterruptedAtShutdown.yml +2 -1
  974. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml +2 -1
  975. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-LegacyNotPrimary.yml +65 -0
  976. data/spec/spec_tests/data/sdam/errors/{stale-topologyVersion-NotMasterNoSlaveOk.yml → stale-topologyVersion-NotPrimaryNoSecondaryOk.yml} +7 -6
  977. data/spec/spec_tests/data/sdam/errors/{stale-topologyVersion-NotMasterOrSecondary.yml → stale-topologyVersion-NotPrimaryOrSecondary.yml} +7 -6
  978. data/spec/spec_tests/data/sdam/errors/{stale-topologyVersion-NotMaster.yml → stale-topologyVersion-NotWritablePrimary.yml} +7 -6
  979. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-PrimarySteppedDown.yml +2 -1
  980. data/spec/spec_tests/data/sdam/errors/stale-topologyVersion-ShutdownInProgress.yml +2 -1
  981. data/spec/spec_tests/data/sdam/errors/write_errors_ignored.yml +42 -0
  982. data/spec/spec_tests/data/sdam/load-balanced/discover_load_balancer.yml +25 -0
  983. data/spec/spec_tests/data/sdam/rs/compatible.yml +4 -2
  984. data/spec/spec_tests/data/sdam/rs/compatible_unknown.yml +2 -1
  985. data/spec/spec_tests/data/sdam/rs/discover_arbiters.yml +2 -1
  986. data/spec/spec_tests/data/sdam/rs/discover_arbiters_replicaset.yml +2 -1
  987. data/spec/spec_tests/data/sdam/rs/discover_ghost.yml +2 -1
  988. data/spec/spec_tests/data/sdam/rs/discover_ghost_replicaset.yml +2 -1
  989. data/spec/spec_tests/data/sdam/rs/discover_hidden.yml +2 -1
  990. data/spec/spec_tests/data/sdam/rs/discover_hidden_replicaset.yml +2 -1
  991. data/spec/spec_tests/data/sdam/rs/discover_passives.yml +4 -2
  992. data/spec/spec_tests/data/sdam/rs/discover_passives_replicaset.yml +4 -2
  993. data/spec/spec_tests/data/sdam/rs/discover_primary.yml +2 -1
  994. data/spec/spec_tests/data/sdam/rs/discover_primary_replicaset.yml +2 -1
  995. data/spec/spec_tests/data/sdam/rs/discover_rsother.yml +2 -1
  996. data/spec/spec_tests/data/sdam/rs/discover_rsother_replicaset.yml +4 -2
  997. data/spec/spec_tests/data/sdam/rs/discover_secondary.yml +2 -1
  998. data/spec/spec_tests/data/sdam/rs/discover_secondary_replicaset.yml +2 -1
  999. data/spec/spec_tests/data/sdam/rs/discovery.yml +8 -4
  1000. data/spec/spec_tests/data/sdam/rs/equal_electionids.yml +4 -2
  1001. data/spec/spec_tests/data/sdam/rs/hosts_differ_from_seeds.yml +2 -1
  1002. data/spec/spec_tests/data/sdam/rs/incompatible_arbiter.yml +3 -1
  1003. data/spec/spec_tests/data/sdam/rs/incompatible_ghost.yml +3 -1
  1004. data/spec/spec_tests/data/sdam/rs/incompatible_other.yml +3 -1
  1005. data/spec/spec_tests/data/sdam/rs/ls_timeout.yml +12 -6
  1006. data/spec/spec_tests/data/sdam/rs/member_reconfig.yml +4 -2
  1007. data/spec/spec_tests/data/sdam/rs/member_standalone.yml +4 -2
  1008. data/spec/spec_tests/data/sdam/rs/new_primary.yml +4 -2
  1009. data/spec/spec_tests/data/sdam/rs/new_primary_new_electionid.yml +6 -3
  1010. data/spec/spec_tests/data/sdam/rs/new_primary_new_setversion.yml +6 -3
  1011. data/spec/spec_tests/data/sdam/rs/new_primary_wrong_set_name.yml +4 -2
  1012. data/spec/spec_tests/data/sdam/rs/non_rs_member.yml +1 -0
  1013. data/spec/spec_tests/data/sdam/rs/normalize_case.yml +2 -1
  1014. data/spec/spec_tests/data/sdam/rs/normalize_case_me.yml +4 -2
  1015. data/spec/spec_tests/data/sdam/rs/null_election_id.yml +8 -4
  1016. data/spec/spec_tests/data/sdam/rs/primary_becomes_ghost.yml +4 -2
  1017. data/spec/spec_tests/data/sdam/rs/primary_becomes_mongos.yml +4 -2
  1018. data/spec/spec_tests/data/sdam/rs/primary_becomes_standalone.yml +2 -1
  1019. data/spec/spec_tests/data/sdam/rs/primary_changes_set_name.yml +4 -2
  1020. data/spec/spec_tests/data/sdam/rs/primary_disconnect.yml +2 -1
  1021. data/spec/spec_tests/data/sdam/rs/primary_disconnect_electionid.yml +10 -5
  1022. data/spec/spec_tests/data/sdam/rs/primary_disconnect_setversion.yml +10 -5
  1023. data/spec/spec_tests/data/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +4 -2
  1024. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +2 -1
  1025. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me_not_removed.yml +5 -3
  1026. data/spec/spec_tests/data/sdam/rs/primary_reports_new_member.yml +8 -4
  1027. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +4 -2
  1028. data/spec/spec_tests/data/sdam/rs/primary_wrong_set_name.yml +2 -1
  1029. data/spec/spec_tests/data/sdam/rs/repeated.yml +9 -5
  1030. data/spec/spec_tests/data/sdam/rs/replicaset_rsnp.yml +2 -1
  1031. data/spec/spec_tests/data/sdam/rs/response_from_removed.yml +4 -2
  1032. data/spec/spec_tests/data/sdam/rs/sec_not_auth.yml +4 -2
  1033. data/spec/spec_tests/data/sdam/rs/secondary_ignore_ok_0.yml +4 -2
  1034. data/spec/spec_tests/data/sdam/rs/secondary_mismatched_me.yml +2 -1
  1035. data/spec/spec_tests/data/sdam/rs/secondary_wrong_set_name.yml +2 -1
  1036. data/spec/spec_tests/data/sdam/rs/secondary_wrong_set_name_with_primary.yml +4 -2
  1037. data/spec/spec_tests/data/sdam/rs/setversion_without_electionid.yml +4 -2
  1038. data/spec/spec_tests/data/sdam/rs/stepdown_change_set_name.yml +4 -2
  1039. data/spec/spec_tests/data/sdam/rs/too_new.yml +4 -2
  1040. data/spec/spec_tests/data/sdam/rs/too_old.yml +4 -2
  1041. data/spec/spec_tests/data/sdam/rs/topology_version_equal.yml +4 -2
  1042. data/spec/spec_tests/data/sdam/rs/topology_version_greater.yml +10 -5
  1043. data/spec/spec_tests/data/sdam/rs/topology_version_less.yml +4 -2
  1044. data/spec/spec_tests/data/sdam/rs/unexpected_mongos.yml +2 -1
  1045. data/spec/spec_tests/data/sdam/rs/use_setversion_without_electionid.yml +6 -3
  1046. data/spec/spec_tests/data/sdam/rs/wrong_set_name.yml +2 -1
  1047. data/spec/spec_tests/data/sdam/sharded/compatible.yml +4 -2
  1048. data/spec/spec_tests/data/sdam/sharded/discover_single_mongos.yml +2 -1
  1049. data/spec/spec_tests/data/sdam/sharded/ls_timeout_mongos.yml +9 -5
  1050. data/spec/spec_tests/data/sdam/sharded/mongos_disconnect.yml +6 -3
  1051. data/spec/spec_tests/data/sdam/sharded/multiple_mongoses.yml +4 -2
  1052. data/spec/spec_tests/data/sdam/sharded/non_mongos_removed.yml +4 -2
  1053. data/spec/spec_tests/data/sdam/sharded/too_new.yml +4 -2
  1054. data/spec/spec_tests/data/sdam/sharded/too_old.yml +4 -2
  1055. data/spec/spec_tests/data/sdam/single/compatible.yml +2 -1
  1056. data/spec/spec_tests/data/sdam/single/direct_connection_external_ip.yml +2 -1
  1057. data/spec/spec_tests/data/sdam/single/direct_connection_mongos.yml +2 -1
  1058. data/spec/spec_tests/data/sdam/single/direct_connection_replicaset.yml +2 -1
  1059. data/spec/spec_tests/data/sdam/single/direct_connection_rsarbiter.yml +2 -1
  1060. data/spec/spec_tests/data/sdam/single/direct_connection_rsprimary.yml +2 -1
  1061. data/spec/spec_tests/data/sdam/single/direct_connection_rssecondary.yml +2 -1
  1062. data/spec/spec_tests/data/sdam/single/direct_connection_standalone.yml +2 -1
  1063. data/spec/spec_tests/data/sdam/single/direct_connection_wrong_set_name.yml +4 -2
  1064. data/spec/spec_tests/data/sdam/single/discover_standalone.yml +2 -1
  1065. data/spec/spec_tests/data/sdam/single/ls_timeout_standalone.yml +2 -1
  1066. data/spec/spec_tests/data/sdam/single/not_ok_response.yml +5 -3
  1067. data/spec/spec_tests/data/sdam/single/standalone_removed.yml +2 -1
  1068. data/spec/spec_tests/data/sdam/single/{direct_connection_slave.yml → standalone_using_legacy_hello.yml} +3 -3
  1069. data/spec/spec_tests/data/sdam/single/too_new.yml +2 -1
  1070. data/spec/spec_tests/data/sdam/single/too_old.yml +2 -1
  1071. data/spec/spec_tests/data/sdam/single/too_old_then_upgraded.yml +4 -2
  1072. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +1 -1
  1073. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +2 -2
  1074. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +2 -0
  1075. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +2 -0
  1076. data/spec/spec_tests/data/sdam_integration/{isMaster-command-error.yml → hello-command-error.yml} +22 -30
  1077. data/spec/spec_tests/data/sdam_integration/{isMaster-network-error.yml → hello-network-error.yml} +12 -16
  1078. data/spec/spec_tests/data/sdam_integration/{isMaster-timeout.yml → hello-timeout.yml} +9 -13
  1079. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +2 -0
  1080. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +2 -0
  1081. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +14 -3
  1082. data/spec/spec_tests/data/sdam_monitoring/discovered_standalone.yml +1 -1
  1083. data/spec/spec_tests/data/sdam_monitoring/load_balancer.yml +65 -0
  1084. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_no_primary.yml +2 -1
  1085. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary.yml +2 -1
  1086. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_removal.yml +3 -2
  1087. data/spec/spec_tests/data/sdam_monitoring/required_replica_set.yml +2 -1
  1088. data/spec/spec_tests/data/sdam_monitoring/standalone.yml +1 -1
  1089. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +2 -2
  1090. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-directConnection.yml +13 -0
  1091. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-replicaSet-errors.yml +6 -0
  1092. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-true-multiple-hosts.yml +5 -0
  1093. data/spec/spec_tests/data/seed_list_discovery/load-balanced/loadBalanced-true-txt.yml +10 -0
  1094. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/direct-connection-false.yml +0 -0
  1095. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/direct-connection-true.yml +0 -0
  1096. data/spec/spec_tests/data/seed_list_discovery/replica-set/encoded-userinfo-and-db.yml +15 -0
  1097. data/spec/spec_tests/data/seed_list_discovery/replica-set/loadBalanced-false-txt.yml +10 -0
  1098. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/longer-parent-in-return.yml +0 -0
  1099. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/misformatted-option.yml +0 -0
  1100. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/no-results.yml +0 -0
  1101. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/not-enough-parts.yml +0 -0
  1102. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/one-result-default-port.yml +0 -0
  1103. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/one-txt-record-multiple-strings.yml +0 -0
  1104. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/one-txt-record.yml +0 -0
  1105. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch1.yml +0 -0
  1106. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch2.yml +0 -0
  1107. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch3.yml +0 -0
  1108. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch4.yml +0 -0
  1109. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/parent-part-mismatch5.yml +0 -0
  1110. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/returned-parent-too-short.yml +0 -0
  1111. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/returned-parent-wrong.yml +0 -0
  1112. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/two-results-default-port.yml +0 -0
  1113. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/two-results-nonstandard-port.yml +0 -0
  1114. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/two-txt-records.yml +0 -0
  1115. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-not-allowed-option.yml +0 -0
  1116. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-with-overridden-ssl-option.yml +0 -0
  1117. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-with-overridden-uri-option.yml +0 -0
  1118. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/txt-record-with-unallowed-option.yml +0 -0
  1119. data/spec/spec_tests/data/seed_list_discovery/replica-set/uri-with-admin-database.yml +13 -0
  1120. data/spec/spec_tests/data/seed_list_discovery/replica-set/uri-with-auth.yml +12 -0
  1121. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/uri-with-port.yml +0 -0
  1122. data/spec/spec_tests/data/{dns_seedlist_discovery → seed_list_discovery/replica-set}/uri-with-two-hosts.yml +0 -0
  1123. data/spec/spec_tests/data/transactions/error-labels.yml +3 -0
  1124. data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +3 -0
  1125. data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +2 -0
  1126. data/spec/spec_tests/data/transactions/pin-mongos.yml +6 -3
  1127. data/spec/spec_tests/data/transactions/retryable-abort-errorLabels.yml +2 -0
  1128. data/spec/spec_tests/data/transactions/retryable-abort.yml +2 -0
  1129. data/spec/spec_tests/data/transactions/retryable-commit-errorLabels.yml +2 -0
  1130. data/spec/spec_tests/data/transactions/retryable-commit.yml +2 -0
  1131. data/spec/spec_tests/data/transactions/retryable-writes.yml +2 -0
  1132. data/spec/spec_tests/data/transactions_unified/mongos-unpin.yml +172 -0
  1133. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  1134. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  1135. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  1136. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  1137. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  1138. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  1139. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  1140. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  1141. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  1142. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  1143. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  1144. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  1145. data/spec/spec_tests/data/uri_options/connection-options.yml +60 -0
  1146. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +417 -0
  1147. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +411 -0
  1148. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +75 -0
  1149. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  1150. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +46 -0
  1151. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +128 -0
  1152. data/spec/spec_tests/gridfs_spec.rb +3 -0
  1153. data/spec/spec_tests/load_balancers_spec.rb +15 -0
  1154. data/spec/spec_tests/max_staleness_spec.rb +3 -0
  1155. data/spec/spec_tests/read_write_concern_connection_string_spec.rb +3 -0
  1156. data/spec/spec_tests/read_write_concern_document_spec.rb +3 -0
  1157. data/spec/spec_tests/read_write_concern_operaton_spec.rb +3 -0
  1158. data/spec/spec_tests/retryable_reads_spec.rb +5 -2
  1159. data/spec/spec_tests/retryable_writes_spec.rb +10 -7
  1160. data/spec/spec_tests/sdam_integration_spec.rb +4 -1
  1161. data/spec/spec_tests/sdam_monitoring_spec.rb +14 -6
  1162. data/spec/spec_tests/sdam_spec.rb +5 -2
  1163. data/spec/spec_tests/seed_list_discovery_spec.rb +118 -0
  1164. data/spec/spec_tests/server_selection_rtt_spec.rb +3 -0
  1165. data/spec/spec_tests/server_selection_spec.rb +3 -0
  1166. data/spec/spec_tests/transactions_api_spec.rb +3 -0
  1167. data/spec/spec_tests/transactions_spec.rb +3 -0
  1168. data/spec/spec_tests/transactions_unified_spec.rb +13 -0
  1169. data/spec/spec_tests/unified_spec.rb +18 -0
  1170. data/spec/spec_tests/uri_options_spec.rb +21 -2
  1171. data/spec/spec_tests/versioned_api_spec.rb +13 -0
  1172. data/spec/stress/cleanup_spec.rb +3 -0
  1173. data/spec/stress/connection_pool_stress_spec.rb +3 -0
  1174. data/spec/stress/connection_pool_timing_spec.rb +3 -0
  1175. data/spec/stress/fork_reconnect_stress_spec.rb +8 -9
  1176. data/spec/stress/push_monitor_close_spec.rb +44 -0
  1177. data/spec/support/authorization.rb +3 -0
  1178. data/spec/support/aws_utils/base.rb +3 -0
  1179. data/spec/support/aws_utils/inspector.rb +3 -0
  1180. data/spec/support/aws_utils/orchestrator.rb +3 -0
  1181. data/spec/support/aws_utils/provisioner.rb +3 -0
  1182. data/spec/support/aws_utils.rb +3 -0
  1183. data/spec/support/background_thread_registry.rb +4 -1
  1184. data/spec/support/certificates/README.md +3 -2
  1185. data/spec/support/certificates/atlas-ocsp-ca.crt +107 -25
  1186. data/spec/support/certificates/atlas-ocsp.crt +156 -40
  1187. data/spec/support/client_registry.rb +12 -5
  1188. data/spec/support/client_registry_macros.rb +7 -4
  1189. data/spec/support/cluster_tools.rb +4 -1
  1190. data/spec/support/common_shortcuts.rb +39 -7
  1191. data/spec/support/constraints.rb +3 -270
  1192. data/spec/support/crypt.rb +3 -0
  1193. data/spec/support/dns.rb +3 -0
  1194. data/spec/support/json_ext_formatter.rb +3 -0
  1195. data/spec/support/keyword_struct.rb +3 -0
  1196. data/spec/support/local_resource_registry.rb +3 -0
  1197. data/spec/support/matchers.rb +17 -1
  1198. data/spec/support/monitoring_ext.rb +3 -0
  1199. data/spec/support/primary_socket.rb +3 -0
  1200. data/spec/support/sdam_formatter_integration.rb +3 -0
  1201. data/spec/support/session_registry.rb +3 -0
  1202. data/spec/{mongo/server/app_metadata_shared.rb → support/shared/app_metadata.rb} +39 -8
  1203. data/spec/support/shared/auth_context.rb +16 -0
  1204. data/spec/support/shared/protocol.rb +3 -0
  1205. data/spec/support/shared/scram_conversation.rb +3 -0
  1206. data/spec/support/shared/server_selector.rb +6 -3
  1207. data/spec/support/shared/session.rb +7 -4
  1208. data/spec/support/spec_config.rb +98 -22
  1209. data/spec/support/spec_setup.rb +51 -38
  1210. data/spec/support/using_hash.rb +31 -0
  1211. data/spec/support/utils.rb +88 -5
  1212. data.tar.gz.sig +2 -1
  1213. metadata +1223 -1007
  1214. metadata.gz.sig +0 -0
  1215. data/lib/mongo/collection/view/builder/find_command.rb +0 -170
  1216. data/lib/mongo/collection/view/builder/op_query.rb +0 -91
  1217. data/lib/mongo/cursor/builder/get_more_command.rb +0 -77
  1218. data/lib/mongo/cursor/builder/kill_cursors_command.rb +0 -108
  1219. data/lib/mongo/cursor/builder/op_get_more.rb +0 -61
  1220. data/lib/mongo/cursor/builder/op_kill_cursors.rb +0 -103
  1221. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  1222. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  1223. data/lib/mongo/server/context.rb +0 -69
  1224. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -61
  1225. data/spec/spec_tests/dns_seedlist_discovery_spec.rb +0 -76
  1226. data/spec/support/child_process_helper.rb +0 -78
  1227. data/spec/support/cluster_config.rb +0 -207
  1228. data/spec/support/event_subscriber.rb +0 -212
  1229. data/spec/support/lite_constraints.rb +0 -165
  1230. data/spec/support/spec_organizer.rb +0 -129
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'lite_spec_helper'
2
5
 
3
6
  describe Mongo::Cluster::Topology do
@@ -111,7 +114,7 @@ describe Mongo::Cluster::Topology do
111
114
  end
112
115
 
113
116
  describe '#logical_session_timeout' do
114
- skip_if_linting
117
+ require_no_linting
115
118
 
116
119
  let(:listeners) do
117
120
  Mongo::Event::Listeners.new
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Cluster do
@@ -38,6 +41,34 @@ describe Mongo::Cluster do
38
41
  end
39
42
  end
40
43
 
44
+ context 'when topology is load-balanced' do
45
+ require_topology :load_balanced
46
+
47
+ it 'emits SDAM events' do
48
+ allow(monitoring).to receive(:succeeded)
49
+
50
+ register_cluster(
51
+ described_class.new(
52
+ SpecConfig.instance.addresses,
53
+ monitoring,
54
+ SpecConfig.instance.test_options
55
+ )
56
+ )
57
+
58
+ expect(monitoring).to have_received(:succeeded).with(
59
+ Mongo::Monitoring::TOPOLOGY_OPENING, any_args
60
+ )
61
+ expect(monitoring).to have_received(:succeeded).with(
62
+ Mongo::Monitoring::TOPOLOGY_CHANGED, any_args
63
+ ).twice
64
+ expect(monitoring).to have_received(:succeeded).with(
65
+ Mongo::Monitoring::SERVER_OPENING, any_args
66
+ )
67
+ expect(monitoring).to have_received(:succeeded).with(
68
+ Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, any_args
69
+ )
70
+ end
71
+ end
41
72
  end
42
73
 
43
74
  describe '#==' do
@@ -274,27 +305,11 @@ describe Mongo::Cluster do
274
305
 
275
306
  describe '#add' do
276
307
 
277
- context 'when topology is Single' do
278
-
279
- let(:cluster) { cluster_with_semaphore }
280
-
281
- let(:topology) do
282
- Mongo::Cluster::Topology::Single.new({}, cluster)
283
- end
284
-
285
- before do
286
- cluster.add('a')
287
- end
288
-
289
- it 'does not add discovered servers to the cluster' do
290
- expect(cluster.servers[0].address.seed).to_not eq('a')
291
- end
292
- end
293
-
294
308
  context 'topology is Sharded' do
309
+ require_topology :sharded
295
310
 
296
311
  let(:topology) do
297
- Mongo::Cluster::Topology::Single.new({}, cluster)
312
+ Mongo::Cluster::Topology::Sharded.new({}, cluster)
298
313
  end
299
314
 
300
315
  before do
@@ -309,6 +324,7 @@ describe Mongo::Cluster do
309
324
  expect(server).not_to be nil
310
325
 
311
326
  expect(server.last_scan).to be nil
327
+ expect(server.last_scan_monotime).to be nil
312
328
  end
313
329
  end
314
330
  end
@@ -346,7 +362,7 @@ describe Mongo::Cluster do
346
362
  end
347
363
 
348
364
  let(:monitoring) { Mongo::Monitoring.new }
349
- let(:subscriber) { EventSubscriber.new }
365
+ let(:subscriber) { Mrss::EventSubscriber.new }
350
366
 
351
367
  it 'publishes server closed event once' do
352
368
  monitoring.subscribe(Mongo::Monitoring::SERVER_CLOSED, subscriber)
@@ -368,6 +384,7 @@ describe Mongo::Cluster do
368
384
  end
369
385
 
370
386
  before do
387
+ cluster.next_primary
371
388
  cluster.servers.each do |server|
372
389
  expect(server).to receive(:reconnect!).and_call_original
373
390
  end
@@ -436,7 +453,7 @@ describe Mongo::Cluster do
436
453
  end
437
454
 
438
455
  let(:primary_candidates) do
439
- if cluster.single?
456
+ if cluster.single? || cluster.load_balanced?
440
457
  cluster.servers
441
458
  elsif cluster.sharded?
442
459
  cluster.servers
@@ -622,7 +639,17 @@ describe Mongo::Cluster do
622
639
  monitoring_io: false, server_selection_timeout: 0.183))
623
640
  end
624
641
 
625
- it_behaves_like 'does not support sessions'
642
+ context 'in load-balanced topology' do
643
+ require_topology :load_balanced
644
+
645
+ it_behaves_like 'supports sessions'
646
+ end
647
+
648
+ context 'not in load-balanced topology' do
649
+ require_topology :single, :replica_set, :sharded
650
+
651
+ it_behaves_like 'does not support sessions'
652
+ end
626
653
  end
627
654
 
628
655
  context 'when client has contacted servers and then disconnected' do
@@ -772,6 +799,9 @@ describe Mongo::Cluster do
772
799
  let(:default_address) { SpecConfig.instance.addresses.first }
773
800
 
774
801
  context 'cluster has unknown servers' do
802
+ # Servers are never unknown in load-balanced topology.
803
+ require_topology :single, :replica_set, :sharded
804
+
775
805
  it 'includes unknown servers' do
776
806
  cluster.servers_list.each do |server|
777
807
  expect(server).to be_unknown
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'lite_spec_helper'
2
5
 
3
6
  describe Mongo::ClusterTime do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::Aggregation do
@@ -232,7 +235,7 @@ describe Mongo::Collection::View::Aggregation do
232
235
  { session: session }
233
236
  end
234
237
 
235
- let(:subscriber) { EventSubscriber.new }
238
+ let(:subscriber) { Mrss::EventSubscriber.new }
236
239
 
237
240
  let(:client) do
238
241
  authorized_client.tap do |client|
@@ -1,3 +1,9 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ # TODO convert, move or delete these tests as part of RUBY-2706.
5
+
6
+ =begin
1
7
  require 'lite_spec_helper'
2
8
 
3
9
  describe Mongo::Collection::View::Builder::FindCommand do
@@ -519,3 +525,4 @@ describe Mongo::Collection::View::Builder::FindCommand do
519
525
  end
520
526
  end
521
527
  end
528
+ =end
@@ -1,3 +1,9 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ # TODO convert, move or delete these tests as part of RUBY-2706.
5
+
6
+ =begin
1
7
  require 'spec_helper'
2
8
 
3
9
  describe Mongo::Collection::View::Builder::OpQuery do
@@ -152,3 +158,4 @@ describe Mongo::Collection::View::Builder::OpQuery do
152
158
  end
153
159
  end
154
160
  end
161
+ =end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::ChangeStream do
@@ -44,8 +47,10 @@ describe Mongo::Collection::View::ChangeStream do
44
47
  end
45
48
 
46
49
  let(:connection_description) do
47
- Mongo::Server::Description.new(double('description address'),
48
- 'minWireVersion' => 0, 'maxWireVersion' => 2)
50
+ Mongo::Server::Description.new(
51
+ double('description address'),
52
+ { 'minWireVersion' => 0, 'maxWireVersion' => 2 }
53
+ )
49
54
  end
50
55
 
51
56
  let(:result) do
@@ -183,7 +188,7 @@ describe Mongo::Collection::View::ChangeStream do
183
188
 
184
189
  context 'when a killCursors command is issued for the cursor' do
185
190
  context 'using Enumerable' do
186
- only_mri
191
+ require_mri
187
192
 
188
193
  before do
189
194
  change_stream
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::ChangeStream do
@@ -369,6 +372,19 @@ describe Mongo::Collection::View::ChangeStream do
369
372
 
370
373
  describe '#close' do
371
374
 
375
+ context 'ignores any exceptions or errors' do
376
+ [
377
+ Mongo::Error::OperationFailure,
378
+ Mongo::Error::SocketError,
379
+ Mongo::Error::SocketTimeoutError
380
+ ].each do |err|
381
+ it "ignores #{err}" do
382
+ expect(cursor).to receive(:close).and_raise(err)
383
+ change_stream.close
384
+ end
385
+ end
386
+ end
387
+
372
388
  context 'when documents have not been retrieved and the stream is closed' do
373
389
 
374
390
  before do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::Explainable do
@@ -82,7 +85,7 @@ describe Mongo::Collection::View::Explainable do
82
85
  it 'triggers server error' do
83
86
  lambda do
84
87
  explain
85
- end.should raise_error(Mongo::Error::OperationFailure, /verbosity string must be/)
88
+ end.should raise_error(Mongo::Error::OperationFailure, /verbosity string must be|value .* for field .*verbosity.* is not a valid value/)
86
89
  end
87
90
  end
88
91
 
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::Immutable do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::Iterable do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::MapReduce do
@@ -239,7 +242,7 @@ describe Mongo::Collection::View::MapReduce do
239
242
  Mongo::Collection::View.new(client[TEST_COLL], selector, view_options)
240
243
  end
241
244
 
242
- let(:subscriber) { EventSubscriber.new }
245
+ let(:subscriber) { Mrss::EventSubscriber.new }
243
246
 
244
247
  let(:client) do
245
248
  authorized_client.tap do |client|
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::Readable do
@@ -602,6 +605,72 @@ describe Mongo::Collection::View::Readable do
602
605
  end
603
606
  end
604
607
 
608
+ describe "#estimated_document_count" do
609
+
610
+ let(:result) do
611
+ view.estimated_document_count(options)
612
+ end
613
+
614
+ context 'when limit is set' do
615
+ it 'raises an error' do
616
+ expect {
617
+ view.limit(5).estimated_document_count(options)
618
+ }.to raise_error(ArgumentError, "Cannot call estimated_document_count when querying with limit")
619
+ end
620
+ end
621
+
622
+ context 'when skip is set' do
623
+ it 'raises an error' do
624
+ expect {
625
+ view.skip(5).estimated_document_count(options)
626
+ }.to raise_error(ArgumentError, "Cannot call estimated_document_count when querying with skip")
627
+ end
628
+ end
629
+
630
+ context 'when collection has documents' do
631
+ let(:documents) do
632
+ (1..10).map{ |i| { field: "test#{i}" }}
633
+ end
634
+
635
+ before do
636
+ authorized_collection.delete_many
637
+ authorized_collection.insert_many(documents)
638
+ end
639
+
640
+ context 'when a selector is provided' do
641
+
642
+ let(:selector) do
643
+ { field: 'test1' }
644
+ end
645
+
646
+ it 'raises an error' do
647
+ expect {
648
+ result
649
+ }.to raise_error(ArgumentError, "Cannot call estimated_document_count when querying with a filter")
650
+ end
651
+ end
652
+
653
+ context 'when no selector is provided' do
654
+
655
+ it 'returns the estimated count of matching documents' do
656
+ expect(view.estimated_document_count).to eq(10)
657
+ end
658
+ end
659
+ end
660
+
661
+ context 'when collection does not exist' do
662
+
663
+ let(:view) do
664
+ Mongo::Collection::View.new(
665
+ authorized_client['nonexistent-collection-for-estimated-document-count'], selector, options)
666
+ end
667
+
668
+ it 'returns 0' do
669
+ view.estimated_document_count.should == 0
670
+ end
671
+ end
672
+ end
673
+
605
674
  describe '#distinct' do
606
675
 
607
676
  context 'when incorporating read concern' do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View::Writable do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'spec_helper'
2
5
 
3
6
  describe Mongo::Collection::View do
@@ -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