mongo 2.9.2 → 2.10.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +1 -0
  5. data/lib/mongo/auth/user/view.rb +4 -4
  6. data/lib/mongo/bulk_write.rb +14 -8
  7. data/lib/mongo/bulk_write/result.rb +1 -1
  8. data/lib/mongo/bulk_write/result_combiner.rb +2 -2
  9. data/lib/mongo/bulk_write/transformable.rb +17 -9
  10. data/lib/mongo/client.rb +107 -16
  11. data/lib/mongo/cluster.rb +47 -25
  12. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
  13. data/lib/mongo/cluster_time.rb +139 -0
  14. data/lib/mongo/collection.rb +84 -25
  15. data/lib/mongo/collection/view.rb +7 -3
  16. data/lib/mongo/collection/view/aggregation.rb +4 -4
  17. data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
  18. data/lib/mongo/collection/view/builder/find_command.rb +4 -1
  19. data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
  20. data/lib/mongo/collection/view/change_stream.rb +54 -66
  21. data/lib/mongo/collection/view/iterable.rb +2 -2
  22. data/lib/mongo/collection/view/map_reduce.rb +6 -4
  23. data/lib/mongo/collection/view/readable.rb +36 -16
  24. data/lib/mongo/collection/view/writable.rb +68 -22
  25. data/lib/mongo/cursor.rb +87 -20
  26. data/lib/mongo/database.rb +47 -43
  27. data/lib/mongo/database/view.rb +54 -11
  28. data/lib/mongo/error.rb +13 -4
  29. data/lib/mongo/error/invalid_write_concern.rb +2 -2
  30. data/lib/mongo/error/operation_failure.rb +65 -11
  31. data/lib/mongo/error/parser.rb +41 -8
  32. data/lib/mongo/grid/fs_bucket.rb +26 -6
  33. data/lib/mongo/grid/stream/read.rb +9 -2
  34. data/lib/mongo/grid/stream/write.rb +21 -5
  35. data/lib/mongo/index/view.rb +3 -3
  36. data/lib/mongo/lint.rb +10 -3
  37. data/lib/mongo/operation.rb +2 -0
  38. data/lib/mongo/operation/aggregate/result.rb +19 -6
  39. data/lib/mongo/operation/collections_info.rb +1 -1
  40. data/lib/mongo/operation/get_more/result.rb +9 -0
  41. data/lib/mongo/operation/list_collections/command.rb +1 -3
  42. data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
  43. data/lib/mongo/operation/parallel_scan/command.rb +4 -1
  44. data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
  45. data/lib/mongo/operation/result.rb +27 -4
  46. data/lib/mongo/operation/shared/executable.rb +19 -5
  47. data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
  48. data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
  49. data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
  50. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  51. data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
  52. data/lib/mongo/operation/shared/specifiable.rb +40 -0
  53. data/lib/mongo/operation/shared/unpinnable.rb +39 -0
  54. data/lib/mongo/operation/shared/write.rb +1 -1
  55. data/lib/mongo/protocol/update.rb +6 -2
  56. data/lib/mongo/retryable.rb +79 -39
  57. data/lib/mongo/server/connection.rb +10 -3
  58. data/lib/mongo/server/description.rb +25 -1
  59. data/lib/mongo/server/monitor/connection.rb +1 -1
  60. data/lib/mongo/server_selector.rb +10 -0
  61. data/lib/mongo/server_selector/selectable.rb +172 -32
  62. data/lib/mongo/session.rb +654 -581
  63. data/lib/mongo/session/session_pool.rb +1 -1
  64. data/lib/mongo/socket.rb +7 -28
  65. data/lib/mongo/socket/ssl.rb +26 -1
  66. data/lib/mongo/socket/tcp.rb +3 -0
  67. data/lib/mongo/socket/unix.rb +3 -0
  68. data/lib/mongo/uri.rb +112 -265
  69. data/lib/mongo/uri/srv_protocol.rb +4 -1
  70. data/lib/mongo/version.rb +1 -1
  71. data/lib/mongo/write_concern.rb +10 -29
  72. data/lib/mongo/write_concern/acknowledged.rb +12 -0
  73. data/lib/mongo/write_concern/base.rb +17 -13
  74. data/lib/mongo/write_concern/unacknowledged.rb +12 -0
  75. data/spec/atlas/atlas_connectivity_spec.rb +7 -37
  76. data/spec/atlas/operations_spec.rb +25 -0
  77. data/spec/integration/change_stream_examples_spec.rb +45 -31
  78. data/spec/integration/change_stream_spec.rb +305 -5
  79. data/spec/integration/client_spec.rb +44 -0
  80. data/spec/integration/command_monitoring_spec.rb +1 -0
  81. data/spec/integration/command_spec.rb +7 -1
  82. data/spec/integration/mmapv1_spec.rb +28 -0
  83. data/spec/integration/mongos_pinning_spec.rb +34 -0
  84. data/spec/integration/operation_failure_code_spec.rb +2 -2
  85. data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
  86. data/spec/integration/read_preference_spec.rb +485 -0
  87. data/spec/integration/retryable_writes_spec.rb +8 -19
  88. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  89. data/spec/integration/sdam_events_spec.rb +2 -2
  90. data/spec/integration/server_description_spec.rb +14 -17
  91. data/spec/integration/server_selector_spec.rb +7 -3
  92. data/spec/integration/server_spec.rb +48 -0
  93. data/spec/integration/ssl_uri_options_spec.rb +1 -1
  94. data/spec/integration/step_down_spec.rb +10 -4
  95. data/spec/integration/transactions_examples_spec.rb +11 -10
  96. data/spec/lite_spec_helper.rb +19 -16
  97. data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
  98. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
  99. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
  100. data/spec/mongo/bulk_write_spec.rb +12 -2
  101. data/spec/mongo/client_construction_spec.rb +160 -8
  102. data/spec/mongo/client_spec.rb +5 -4
  103. data/spec/mongo/cluster_spec.rb +6 -6
  104. data/spec/mongo/cluster_time_spec.rb +148 -0
  105. data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
  106. data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
  107. data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
  108. data/spec/mongo/collection/view/readable_spec.rb +4 -4
  109. data/spec/mongo/collection_spec.rb +331 -14
  110. data/spec/mongo/cursor_spec.rb +117 -5
  111. data/spec/mongo/database_spec.rb +240 -8
  112. data/spec/mongo/error/operation_failure_spec.rb +47 -1
  113. data/spec/mongo/error/parser_spec.rb +160 -23
  114. data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
  115. data/spec/mongo/operation/result_spec.rb +27 -0
  116. data/spec/mongo/operation/update/bulk_spec.rb +1 -0
  117. data/spec/mongo/retryable_spec.rb +2 -0
  118. data/spec/mongo/server/app_metadata_spec.rb +2 -2
  119. data/spec/mongo/server/connection_spec.rb +13 -17
  120. data/spec/mongo/server/monitor/connection_spec.rb +13 -10
  121. data/spec/mongo/server_selector_spec.rb +34 -2
  122. data/spec/mongo/session/session_pool_spec.rb +14 -3
  123. data/spec/mongo/session_spec.rb +3 -3
  124. data/spec/mongo/session_transaction_spec.rb +4 -3
  125. data/spec/mongo/socket/ssl_spec.rb +19 -5
  126. data/spec/mongo/socket_spec.rb +1 -62
  127. data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
  128. data/spec/mongo/uri_option_parsing_spec.rb +94 -8
  129. data/spec/mongo/uri_spec.rb +23 -10
  130. data/spec/mongo/write_concern_spec.rb +56 -3
  131. data/spec/spec_tests/change_streams_spec.rb +2 -1
  132. data/spec/spec_tests/cmap_spec.rb +1 -1
  133. data/spec/spec_tests/crud_spec.rb +12 -2
  134. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
  135. data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
  136. data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
  137. data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
  138. data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
  139. data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
  140. data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
  141. data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
  142. data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
  143. data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
  144. data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
  145. data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
  146. data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
  147. data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
  148. data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
  149. data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
  150. data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
  151. data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
  152. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
  153. data/spec/spec_tests/data/transactions/abort.yml +3 -0
  154. data/spec/spec_tests/data/transactions/bulk.yml +3 -8
  155. data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
  156. data/spec/spec_tests/data/transactions/commit.yml +3 -1
  157. data/spec/spec_tests/data/transactions/count.yml +3 -0
  158. data/spec/spec_tests/data/transactions/delete.yml +3 -0
  159. data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
  160. data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
  161. data/spec/spec_tests/data/transactions/errors.yml +3 -0
  162. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
  163. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
  164. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
  165. data/spec/spec_tests/data/transactions/insert.yml +3 -0
  166. data/spec/spec_tests/data/transactions/isolation.yml +3 -0
  167. data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
  168. data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
  169. data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
  170. data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
  171. data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
  172. data/spec/spec_tests/data/transactions/reads.yml +3 -0
  173. data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
  174. data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
  175. data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
  176. data/spec/spec_tests/data/transactions/run-command.yml +3 -0
  177. data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
  178. data/spec/spec_tests/data/transactions/update.yml +3 -8
  179. data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
  180. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
  181. data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
  182. data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
  183. data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
  184. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
  185. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
  186. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
  187. data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
  188. data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
  189. data/spec/spec_tests/retryable_reads_spec.rb +5 -2
  190. data/spec/spec_tests/retryable_writes_spec.rb +5 -2
  191. data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
  192. data/spec/spec_tests/sdam_spec.rb +2 -2
  193. data/spec/spec_tests/transactions_api_spec.rb +1 -67
  194. data/spec/spec_tests/transactions_spec.rb +2 -66
  195. data/spec/support/authorization.rb +4 -0
  196. data/spec/support/change_streams.rb +30 -10
  197. data/spec/support/change_streams/operation.rb +27 -0
  198. data/spec/support/client_registry.rb +44 -25
  199. data/spec/support/cluster_config.rb +25 -14
  200. data/spec/support/cluster_tools.rb +32 -10
  201. data/spec/support/command_monitoring.rb +1 -1
  202. data/spec/support/common_shortcuts.rb +30 -0
  203. data/spec/support/connection_string.rb +8 -3
  204. data/spec/support/constraints.rb +34 -0
  205. data/spec/support/crud.rb +31 -16
  206. data/spec/support/crud/context.rb +23 -0
  207. data/spec/support/crud/operation.rb +311 -14
  208. data/spec/support/crud/spec.rb +2 -1
  209. data/spec/support/crud/test.rb +24 -27
  210. data/spec/support/crud/test_base.rb +22 -0
  211. data/spec/support/crud/verifier.rb +15 -1
  212. data/spec/support/event_subscriber.rb +12 -0
  213. data/spec/support/sdam_formatter_integration.rb +12 -6
  214. data/spec/support/shared/server_selector.rb +10 -0
  215. data/spec/support/shared/session.rb +13 -12
  216. data/spec/support/spec_config.rb +32 -22
  217. data/spec/support/spec_setup.rb +2 -2
  218. data/spec/support/transactions.rb +87 -0
  219. data/spec/support/transactions/context.rb +33 -0
  220. data/spec/support/transactions/operation.rb +99 -349
  221. data/spec/support/transactions/spec.rb +1 -3
  222. data/spec/support/transactions/test.rb +110 -49
  223. data/spec/support/utils.rb +74 -1
  224. metadata +52 -10
  225. metadata.gz.sig +0 -0
  226. data/spec/support/crud/read.rb +0 -265
  227. data/spec/support/crud/write.rb +0 -284
@@ -29,12 +29,12 @@ class SpecSetup
29
29
  raise
30
30
  end
31
31
  end
32
- admin_unauthorized_client.close
32
+ admin_unauthorized_client.close(true)
33
33
 
34
34
  # Adds the test user to the test database with permissions on all
35
35
  # databases that will be used in the test suite.
36
36
  create_user(admin_authorized_test_client, SpecConfig.instance.test_user)
37
- admin_authorized_test_client.close
37
+ admin_authorized_test_client.close(true)
38
38
  end
39
39
 
40
40
  def create_user(client, user)
@@ -12,6 +12,93 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require 'support/transactions/context'
15
16
  require 'support/transactions/operation'
16
17
  require 'support/transactions/spec'
17
18
  require 'support/transactions/test'
19
+
20
+ def define_transactions_spec_tests(test_paths)
21
+
22
+ test_paths.each do |file|
23
+
24
+ spec = Mongo::Transactions::Spec.new(file)
25
+
26
+ context(spec.description) do
27
+ require_wired_tiger
28
+
29
+ define_spec_tests_with_requirements(spec) do |req|
30
+ spec.tests.each do |test|
31
+
32
+ before do
33
+ if test.multiple_mongoses?
34
+ unless SpecConfig.instance.addresses.length > 1
35
+ skip "Test requires multiple mongoses"
36
+ end
37
+ else
38
+ # Many transaction spec tests that do not specifically deal with
39
+ # sharded transactions fail when run against a multi-mongos cluster
40
+ if SpecConfig.instance.addresses.length > 1
41
+ skip "Test does not specify multiple mongoses"
42
+ end
43
+ end
44
+ end
45
+
46
+ context(test.description) do
47
+
48
+ if test.skip_reason
49
+ before(:all) do
50
+ skip test.skip_reason
51
+ end
52
+ end
53
+
54
+ before(:all) do
55
+ if req.satisfied?
56
+ test.setup_test
57
+ end
58
+ end
59
+
60
+ after(:all) do
61
+ if req.satisfied?
62
+ test.teardown_test
63
+ end
64
+ end
65
+
66
+ let(:results) do
67
+ $tx_spec_results_cache ||= {}
68
+ $tx_spec_results_cache[test.object_id] ||= test.run
69
+ end
70
+
71
+ let(:verifier) { Mongo::CRUD::Verifier.new(test) }
72
+
73
+ it 'returns the correct results' do
74
+ verifier.verify_operation_result(test.expected_results, results[:results])
75
+ end
76
+
77
+ if test.outcome && test.outcome.collection_data?
78
+ it 'has the correct data in the collection' do
79
+ results
80
+ verifier.verify_collection_data(
81
+ test.outcome.collection_data,
82
+ results[:contents])
83
+ end
84
+ end
85
+
86
+ if test.expectations
87
+ it 'has the correct number of command_started events' do
88
+ verifier.verify_command_started_event_count(
89
+ test.expectations, results[:events])
90
+ end
91
+
92
+ test.expectations.each_with_index do |expectation, i|
93
+ it "has the correct command_started event #{i}" do
94
+ verifier.verify_command_started_event(
95
+ test.expectations, results[:events], i)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright (C) 2019 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Transactions
17
+ Context = Struct.new(
18
+ :session0,
19
+ :session1,
20
+ :session,
21
+ ) do
22
+ def transform_arguments(arguments)
23
+ arguments.dup.tap do |out|
24
+ [:session].each do |key|
25
+ if out[key]
26
+ out[key] = send(key)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -14,126 +14,42 @@
14
14
 
15
15
  module Mongo
16
16
  module Transactions
17
- class Operation
18
-
19
- # Map of operation names to method names.
20
- #
21
- # @since 2.6.0
22
- OPERATIONS = {
23
- 'startTransaction' => :start_transaction,
24
- 'abortTransaction' => :abort_transaction,
25
- 'commitTransaction' => :commit_transaction,
26
- 'withTransaction' => :with_transaction,
27
- 'aggregate' => :aggregate,
28
- 'deleteMany' => :delete_many,
29
- 'deleteOne' => :delete_one,
30
- 'insertMany' => :insert_many,
31
- 'insertOne' => :insert_one,
32
- 'replaceOne' => :replace_one,
33
- 'updateMany' => :update_many,
34
- 'updateOne' => :update_one,
35
- 'findOneAndDelete' => :find_one_and_delete,
36
- 'findOneAndReplace' => :find_one_and_replace,
37
- 'findOneAndUpdate' => :find_one_and_update,
38
- 'bulkWrite' => :bulk_write,
39
- 'count' => :count,
40
- 'countDocuments' => :count_documents,
41
- 'distinct' => :distinct,
42
- 'find' => :find,
43
- 'runCommand' => :run_command,
44
- }.freeze
45
-
46
- # Map of operation options to method names.
47
- #
48
- # @since 2.6.0
49
- ARGUMENT_MAP = {
50
- array_filters: 'arrayFilters',
51
- batch_size: 'batchSize',
52
- collation: 'collation',
53
- read_preference: 'readPreference',
54
- document: 'document',
55
- field_name: 'fieldName',
56
- filter: 'filter',
57
- ordered: 'ordered',
58
- pipeline: 'pipeline',
59
- projection: 'projection',
60
- replacement: 'replacement',
61
- return_document: 'returnDocument',
62
- session: 'session',
63
- sort: 'sort',
64
- update: 'update',
65
- upsert: 'upsert'
66
- }.freeze
67
-
68
- # The operation name.
69
- #
70
- # @return [ String ] name The operation name.
71
- #
72
- # @since 2.6.0
73
- attr_reader :name
74
-
75
- # Instantiate the operation.
76
- #
77
- # @return [ Hash ] spec The operation spec.
78
- #
79
- # @since 2.6.0
80
- def initialize(spec, session0, session1, transaction_session=nil)
81
- @spec = spec
82
- @name = spec['name']
83
- @session0 = session0
84
- @session1 = session1
85
- @arguments = case spec['arguments'] && spec['arguments']['session']
86
- when 'session0'
87
- spec['arguments'].merge('session' => @session0)
88
- when 'session1'
89
- spec['arguments'].merge('session' => @session1)
90
- else
91
- args = spec['arguments'] || {}
92
- if transaction_session
93
- args = args.merge('session' => transaction_session)
94
- end
95
- args
96
- end
97
- end
98
-
99
- attr_reader :arguments
17
+ class Operation < Mongo::CRUD::Operation
18
+ include RSpec::Matchers
19
+
20
+ def execute(target, session0, session1, active_session=nil)
21
+ session = case arguments && arguments['session']
22
+ when 'session0'
23
+ session0
24
+ when 'session1'
25
+ session1
26
+ else
27
+ # active session could be nil
28
+ active_session
29
+ end
100
30
 
101
- # Execute the operation.
102
- #
103
- # @example Execute the operation.
104
- # operation.execute
105
- #
106
- # @param [ Collection ] collection The collection to execute
107
- # the operation on.
108
- #
109
- # @return [ Result ] The result of executing the operation.
110
- #
111
- # @since 2.6.0
112
- def execute(collection)
113
- # Determine which object the operation method should be called on.
114
- obj = case object
115
- when 'session0'
116
- @session0
117
- when 'session1'
118
- @session1
119
- when 'database'
120
- collection.database
121
- else
122
- collection = collection.with(read: read_preference) if collection_read_preference
123
- collection = collection.with(read_concern: read_concern) if read_concern
124
- collection = collection.with(write: write_concern) if write_concern
125
- collection
126
- end
31
+ context = Context.new(
32
+ session0,
33
+ session1,
34
+ session)
127
35
 
128
- if (op_name = OPERATIONS[name]) == :with_transaction
129
- args = [collection]
36
+ op_name = Utils.underscore(name).to_sym
37
+ if op_name == :with_transaction
38
+ args = [target]
130
39
  else
131
40
  args = []
132
41
  end
133
42
  if op_name.nil?
134
43
  raise "Unknown operation #{name}"
135
44
  end
136
- send(op_name, obj, *args)
45
+ result = send(op_name, target, context, *args)
46
+ if result
47
+ if result.is_a?(Hash)
48
+ result = result.dup
49
+ result['error'] = false
50
+ end
51
+ end
52
+ result
137
53
  rescue Mongo::Error::OperationFailure => e
138
54
  err_doc = e.instance_variable_get(:@result).send(:first_document)
139
55
  error_code_name = err_doc['codeName'] || err_doc['writeConcernError'] && err_doc['writeConcernError']['codeName']
@@ -142,55 +58,81 @@ module Mongo
142
58
  # but does return the error code (or we can parse the error code
143
59
  # out of the message).
144
60
  # https://jira.mongodb.org/browse/SERVER-39706
145
- if e.code == 11000
146
- error_code_name = 'DuplicateKey'
147
- else
148
- warn "Error without error code name: #{e.code}"
149
- end
61
+ warn "Error without error code name: #{e.code}"
150
62
  end
151
63
 
152
64
  {
153
- 'errorCodeName' => error_code_name,
65
+ 'errorCode' => e.code,
66
+ 'errorCodeName' => e.code_name,
154
67
  'errorContains' => e.message,
155
68
  'errorLabels' => e.labels,
156
69
  'exception' => e,
70
+ 'error' => true,
157
71
  }
158
72
  rescue Mongo::Error => e
159
73
  {
160
74
  'errorContains' => e.message,
161
75
  'errorLabels' => e.labels,
162
76
  'exception' => e,
77
+ 'error' => true,
78
+ }
79
+ # We do not have a base class for client side BSON-related errors.
80
+ # See https://jira.mongodb.org/browse/RUBY-1806.
81
+ # Rescue this particular exception for the time being.
82
+ rescue BSON::String::IllegalKey => e
83
+ {
84
+ 'exception' => e,
85
+ 'clientError' => true,
86
+ 'error' => true,
163
87
  }
164
88
  end
165
89
 
166
90
  private
167
91
 
168
- def start_transaction(session)
169
- session.start_transaction(Utils.snakeize_hash(arguments['options'])) ; nil
92
+ # operations
93
+
94
+ def run_command(database, context)
95
+ # Convert the first key (i.e. the command name) to a symbol.
96
+ cmd = arguments['command'].dup
97
+ command_name = cmd.first.first
98
+ command_value = cmd.delete(command_name)
99
+ cmd = { command_name.to_sym => command_value }.merge(cmd)
100
+
101
+ opts = Utils.snakeize_hash(context.transform_arguments(options)).dup
102
+ opts[:read] = opts.delete(:read_preference)
103
+ database.command(cmd, opts).documents.first
104
+ end
105
+
106
+ def start_transaction(session, context)
107
+ session.start_transaction(Utils.convert_operation_options(arguments['options']))
108
+ nil
170
109
  end
171
110
 
172
- def commit_transaction(session)
173
- session.commit_transaction ; nil
111
+ def commit_transaction(session, context)
112
+ session.commit_transaction
113
+ nil
174
114
  end
175
115
 
176
- def abort_transaction(session)
177
- session.abort_transaction ; nil
116
+ def abort_transaction(session, context)
117
+ session.abort_transaction
118
+ nil
178
119
  end
179
120
 
180
- def with_transaction(session, collection)
181
- unless callback = @spec['arguments']['callback']
121
+ def with_transaction(session, context, collection)
122
+ unless callback = arguments['callback']
182
123
  raise ArgumentError, 'with_transaction requires a callback to be present'
183
124
  end
184
125
 
185
- if @spec['arguments']['options']
186
- options = Utils.snakeize_hash(@spec['arguments']['options'])
126
+ if arguments['options']
127
+ options = Utils.snakeize_hash(arguments['options'])
187
128
  else
188
129
  options = nil
189
130
  end
190
131
  session.with_transaction(options) do
191
132
  callback['operations'].each do |op_spec|
192
- op = Operation.new(op_spec, @session0, @session1, session)
193
- rv = op.execute(collection)
133
+ op = Operation.new(@crud_test, op_spec)
134
+ target = @crud_test.resolve_target(@crud_test.test_client, op)
135
+ rv = op.execute(target, context.session0, context.session1, session)
194
136
  if rv && rv['exception']
195
137
  raise rv['exception']
196
138
  end
@@ -198,237 +140,45 @@ module Mongo
198
140
  end
199
141
  end
200
142
 
201
- def run_command(database)
202
- # Convert the first key (i.e. the command name) to a symbol.
203
- cmd = command.dup
204
- command_name = cmd.first.first
205
- command_value = cmd.delete(command_name)
206
- cmd = { command_name.to_sym => command_value }.merge(cmd)
207
-
208
- opts = Utils.snakeize_hash(options)
209
- opts[:read] = opts.delete(:read_preference)
210
- database.command(cmd, opts).documents.first
211
- end
212
-
213
- def aggregate(collection)
214
- collection.aggregate(pipeline, options).to_a
215
- end
216
-
217
- def bulk_write(collection)
218
- result = collection.bulk_write(requests, options)
219
- return_doc = {}
220
- return_doc['deletedCount'] = result.deleted_count || 0
221
- return_doc['insertedIds'] = result.inserted_ids if result.inserted_ids
222
- return_doc['upsertedId'] = result.upserted_id if upsert
223
- return_doc['upsertedCount'] = result.upserted_count || 0
224
- return_doc['matchedCount'] = result.matched_count || 0
225
- return_doc['modifiedCount'] = result.modified_count || 0
226
- return_doc['upsertedIds'] = result.upserted_ids if result.upserted_ids
227
- return_doc
228
- end
229
-
230
- def count(collection)
231
- collection.count(filter, options).to_s
232
- end
233
-
234
- def count_documents(collection)
235
- collection.count_documents(filter, options)
236
- end
237
-
238
- def delete_many(collection)
239
- result = collection.delete_many(filter, options)
240
- { 'deletedCount' => result.deleted_count }
241
- end
242
-
243
- def delete_one(collection)
244
- result = collection.delete_one(filter, options)
245
- { 'deletedCount' => result.deleted_count }
246
- end
247
-
248
- def distinct(collection)
249
- collection.distinct(field_name, filter, options)
250
- end
251
-
252
- def find(collection)
253
- opts = modifiers ? options.merge(modifiers: BSON::Document.new(modifiers)) : options
254
- collection.find(filter, opts).to_a
255
- end
256
-
257
- def insert_many(collection)
258
- result = collection.insert_many(documents, options)
259
- { 'insertedIds' => result.inserted_ids }
143
+ def assert_session_transaction_state(collection, context)
144
+ session = context.send(arguments['session'])
145
+ actual_state = session.instance_variable_get('@state').to_s.sub(/^transaction_|_transaction$/, '').sub(/^no$/, 'none')
146
+ expect(actual_state).to eq(arguments['state'])
260
147
  end
261
148
 
262
- def insert_one(collection)
263
- result = collection.insert_one(document, options)
264
- { 'insertedId' => result.inserted_id }
265
- end
266
-
267
- def update_return_doc(result)
268
- return_doc = {}
269
- return_doc['upsertedId'] = result.upserted_id if upsert
270
- return_doc['upsertedCount'] = result.upserted_count
271
- return_doc['matchedCount'] = result.matched_count
272
- return_doc['modifiedCount'] = result.modified_count if result.modified_count
273
- return_doc
274
- end
275
-
276
- def replace_one(collection)
277
- result = collection.replace_one(filter, replacement, options)
278
- update_return_doc(result)
279
- end
280
-
281
- def update_many(collection)
282
- result = collection.update_many(filter, update, options)
283
- update_return_doc(result)
284
- end
285
-
286
- def update_one(collection)
287
- result = collection.update_one(filter, update, options)
288
- update_return_doc(result)
289
- end
290
-
291
- def find_one_and_delete(collection)
292
- collection.find_one_and_delete(filter, options)
293
- end
294
-
295
- def find_one_and_replace(collection)
296
- collection.find_one_and_replace(filter, replacement, options)
297
- end
298
-
299
- def find_one_and_update(collection)
300
- collection.find_one_and_update(filter, update, options)
301
- end
302
-
303
- def object
304
- @spec['object']
305
- end
306
-
307
- def options
308
- ARGUMENT_MAP.reduce({}) do |opts, (key, value)|
309
- arguments.key?(value) ? opts.merge!(key => send(key)) : opts
149
+ def targeted_fail_point(collection, context)
150
+ args = context.transform_arguments(options)
151
+ session = args[:session]
152
+ unless session.pinned_server
153
+ raise ArgumentError, 'Targeted fail point requires session to be pinned to a server'
310
154
  end
311
- end
312
-
313
- def collation
314
- arguments['collation']
315
- end
316
155
 
317
- def command
318
- arguments['command']
319
- end
320
-
321
- def replacement
322
- arguments['replacement']
323
- end
324
-
325
- def sort
326
- arguments['sort']
327
- end
328
-
329
- def projection
330
- arguments['projection']
331
- end
156
+ client = ClusterTools.instance.direct_client(session.pinned_server.address,
157
+ database: 'admin')
158
+ client.command(arguments['failPoint'])
332
159
 
333
- def documents
334
- arguments['documents']
160
+ $disable_fail_points ||= []
161
+ $disable_fail_points << [
162
+ arguments['failPoint'],
163
+ session.pinned_server.address,
164
+ ]
335
165
  end
336
166
 
337
- def document
338
- arguments['document']
339
- end
340
-
341
- def ordered
342
- arguments['ordered']
343
- end
344
-
345
- def field_name
346
- arguments['fieldName']
347
- end
348
-
349
- def filter
350
- arguments['filter']
351
- end
352
-
353
- def pipeline
354
- arguments['pipeline']
355
- end
356
-
357
- def array_filters
358
- arguments['arrayFilters']
359
- end
360
-
361
- def batch_size
362
- arguments['batchSize']
363
- end
364
-
365
- def session
366
- arguments['session']
367
- end
368
-
369
- def requests
370
- arguments['requests'].map do |request|
371
- case request.keys.first
372
- when 'insertOne' then
373
- { insert_one: request['insertOne']['document'] }
374
- when 'updateOne' then
375
- update = request['updateOne']
376
- { update_one: { filter: update['filter'], update: update['update'] } }
377
- when 'name' then
378
- bulk_request(request)
379
- end
167
+ def assert_session_pinned(collection, context)
168
+ args = context.transform_arguments(options)
169
+ session = args[:session]
170
+ unless session.pinned_server
171
+ raise ArgumentError, 'Expected session to be pinned'
380
172
  end
381
173
  end
382
174
 
383
- def bulk_request(request)
384
- op_name = OPERATIONS[request['name']]
385
- op = { op_name => {} }
386
-
387
- op[op_name][:filter] = request['arguments']['filter'] if request['arguments']['filter']
388
- op[op_name][:update] = request['arguments']['update'] if request['arguments']['update']
389
- op[op_name][:upsert] = request['arguments']['upsert'] if request['arguments']['upsert']
390
- op[op_name][:replacement] = request['arguments']['replacement'] if request['arguments']['replacement']
391
- op[op_name][:array_filters] = request['arguments']['arrayFilters'] if request['arguments']['arrayFilters']
392
- op[op_name] = request['arguments']['document'] if request['arguments']['document']
393
- op
394
- end
395
-
396
- def upsert
397
- arguments['upsert']
398
- end
399
-
400
- def return_document
401
- case arguments['returnDocument']
402
- when 'Before'
403
- :before
404
- when 'After'
405
- :after
175
+ def assert_session_unpinned(collection, context)
176
+ args = context.transform_arguments(options)
177
+ session = args[:session]
178
+ if session.pinned_server
179
+ raise ArgumentError, 'Expected session to not be pinned'
406
180
  end
407
181
  end
408
-
409
- def update
410
- arguments['update']
411
- end
412
-
413
- def modifiers
414
- arguments['modifiers']
415
- end
416
-
417
- def read_concern
418
- Utils.snakeize_hash(@spec['collectionOptions'] && @spec['collectionOptions']['readConcern'])
419
- end
420
-
421
- def write_concern
422
- Utils.snakeize_hash(@spec['collectionOptions'] && @spec['collectionOptions']['writeConcern'])
423
- end
424
-
425
- def read_preference
426
- Utils.snakeize_hash(arguments['readPreference'])
427
- end
428
-
429
- def collection_read_preference
430
- Utils.snakeize_hash(@spec['collectionOptions'] && @spec['collectionOptions']['readPreference'])
431
- end
432
182
  end
433
183
  end
434
184
  end