mongo 2.14.0.rc1 → 2.15.1

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