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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongo.rb +1 -0
- data/lib/mongo/auth/user/view.rb +4 -4
- data/lib/mongo/bulk_write.rb +14 -8
- data/lib/mongo/bulk_write/result.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +2 -2
- data/lib/mongo/bulk_write/transformable.rb +17 -9
- data/lib/mongo/client.rb +107 -16
- data/lib/mongo/cluster.rb +47 -25
- data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
- data/lib/mongo/cluster_time.rb +139 -0
- data/lib/mongo/collection.rb +84 -25
- data/lib/mongo/collection/view.rb +7 -3
- data/lib/mongo/collection/view/aggregation.rb +4 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
- data/lib/mongo/collection/view/builder/find_command.rb +4 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
- data/lib/mongo/collection/view/change_stream.rb +54 -66
- data/lib/mongo/collection/view/iterable.rb +2 -2
- data/lib/mongo/collection/view/map_reduce.rb +6 -4
- data/lib/mongo/collection/view/readable.rb +36 -16
- data/lib/mongo/collection/view/writable.rb +68 -22
- data/lib/mongo/cursor.rb +87 -20
- data/lib/mongo/database.rb +47 -43
- data/lib/mongo/database/view.rb +54 -11
- data/lib/mongo/error.rb +13 -4
- data/lib/mongo/error/invalid_write_concern.rb +2 -2
- data/lib/mongo/error/operation_failure.rb +65 -11
- data/lib/mongo/error/parser.rb +41 -8
- data/lib/mongo/grid/fs_bucket.rb +26 -6
- data/lib/mongo/grid/stream/read.rb +9 -2
- data/lib/mongo/grid/stream/write.rb +21 -5
- data/lib/mongo/index/view.rb +3 -3
- data/lib/mongo/lint.rb +10 -3
- data/lib/mongo/operation.rb +2 -0
- data/lib/mongo/operation/aggregate/result.rb +19 -6
- data/lib/mongo/operation/collections_info.rb +1 -1
- data/lib/mongo/operation/get_more/result.rb +9 -0
- data/lib/mongo/operation/list_collections/command.rb +1 -3
- data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
- data/lib/mongo/operation/parallel_scan/command.rb +4 -1
- data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
- data/lib/mongo/operation/result.rb +27 -4
- data/lib/mongo/operation/shared/executable.rb +19 -5
- data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
- data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
- data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
- data/lib/mongo/operation/shared/specifiable.rb +40 -0
- data/lib/mongo/operation/shared/unpinnable.rb +39 -0
- data/lib/mongo/operation/shared/write.rb +1 -1
- data/lib/mongo/protocol/update.rb +6 -2
- data/lib/mongo/retryable.rb +79 -39
- data/lib/mongo/server/connection.rb +10 -3
- data/lib/mongo/server/description.rb +25 -1
- data/lib/mongo/server/monitor/connection.rb +1 -1
- data/lib/mongo/server_selector.rb +10 -0
- data/lib/mongo/server_selector/selectable.rb +172 -32
- data/lib/mongo/session.rb +654 -581
- data/lib/mongo/session/session_pool.rb +1 -1
- data/lib/mongo/socket.rb +7 -28
- data/lib/mongo/socket/ssl.rb +26 -1
- data/lib/mongo/socket/tcp.rb +3 -0
- data/lib/mongo/socket/unix.rb +3 -0
- data/lib/mongo/uri.rb +112 -265
- data/lib/mongo/uri/srv_protocol.rb +4 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo/write_concern.rb +10 -29
- data/lib/mongo/write_concern/acknowledged.rb +12 -0
- data/lib/mongo/write_concern/base.rb +17 -13
- data/lib/mongo/write_concern/unacknowledged.rb +12 -0
- data/spec/atlas/atlas_connectivity_spec.rb +7 -37
- data/spec/atlas/operations_spec.rb +25 -0
- data/spec/integration/change_stream_examples_spec.rb +45 -31
- data/spec/integration/change_stream_spec.rb +305 -5
- data/spec/integration/client_spec.rb +44 -0
- data/spec/integration/command_monitoring_spec.rb +1 -0
- data/spec/integration/command_spec.rb +7 -1
- data/spec/integration/mmapv1_spec.rb +28 -0
- data/spec/integration/mongos_pinning_spec.rb +34 -0
- data/spec/integration/operation_failure_code_spec.rb +2 -2
- data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
- data/spec/integration/read_preference_spec.rb +485 -0
- data/spec/integration/retryable_writes_spec.rb +8 -19
- data/spec/integration/sdam_error_handling_spec.rb +1 -1
- data/spec/integration/sdam_events_spec.rb +2 -2
- data/spec/integration/server_description_spec.rb +14 -17
- data/spec/integration/server_selector_spec.rb +7 -3
- data/spec/integration/server_spec.rb +48 -0
- data/spec/integration/ssl_uri_options_spec.rb +1 -1
- data/spec/integration/step_down_spec.rb +10 -4
- data/spec/integration/transactions_examples_spec.rb +11 -10
- data/spec/lite_spec_helper.rb +19 -16
- data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
- data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
- data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
- data/spec/mongo/bulk_write_spec.rb +12 -2
- data/spec/mongo/client_construction_spec.rb +160 -8
- data/spec/mongo/client_spec.rb +5 -4
- data/spec/mongo/cluster_spec.rb +6 -6
- data/spec/mongo/cluster_time_spec.rb +148 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
- data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
- data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
- data/spec/mongo/collection/view/readable_spec.rb +4 -4
- data/spec/mongo/collection_spec.rb +331 -14
- data/spec/mongo/cursor_spec.rb +117 -5
- data/spec/mongo/database_spec.rb +240 -8
- data/spec/mongo/error/operation_failure_spec.rb +47 -1
- data/spec/mongo/error/parser_spec.rb +160 -23
- data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
- data/spec/mongo/operation/result_spec.rb +27 -0
- data/spec/mongo/operation/update/bulk_spec.rb +1 -0
- data/spec/mongo/retryable_spec.rb +2 -0
- data/spec/mongo/server/app_metadata_spec.rb +2 -2
- data/spec/mongo/server/connection_spec.rb +13 -17
- data/spec/mongo/server/monitor/connection_spec.rb +13 -10
- data/spec/mongo/server_selector_spec.rb +34 -2
- data/spec/mongo/session/session_pool_spec.rb +14 -3
- data/spec/mongo/session_spec.rb +3 -3
- data/spec/mongo/session_transaction_spec.rb +4 -3
- data/spec/mongo/socket/ssl_spec.rb +19 -5
- data/spec/mongo/socket_spec.rb +1 -62
- data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
- data/spec/mongo/uri_option_parsing_spec.rb +94 -8
- data/spec/mongo/uri_spec.rb +23 -10
- data/spec/mongo/write_concern_spec.rb +56 -3
- data/spec/spec_tests/change_streams_spec.rb +2 -1
- data/spec/spec_tests/cmap_spec.rb +1 -1
- data/spec/spec_tests/crud_spec.rb +12 -2
- data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
- data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
- data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
- data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
- data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
- data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
- data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
- data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
- data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
- data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
- data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
- data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
- data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
- data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
- data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
- data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
- data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
- data/spec/spec_tests/data/transactions/abort.yml +3 -0
- data/spec/spec_tests/data/transactions/bulk.yml +3 -8
- data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
- data/spec/spec_tests/data/transactions/commit.yml +3 -1
- data/spec/spec_tests/data/transactions/count.yml +3 -0
- data/spec/spec_tests/data/transactions/delete.yml +3 -0
- data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
- data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
- data/spec/spec_tests/data/transactions/errors.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
- data/spec/spec_tests/data/transactions/insert.yml +3 -0
- data/spec/spec_tests/data/transactions/isolation.yml +3 -0
- data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
- data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
- data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
- data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
- data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
- data/spec/spec_tests/data/transactions/reads.yml +3 -0
- data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
- data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
- data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
- data/spec/spec_tests/data/transactions/run-command.yml +3 -0
- data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
- data/spec/spec_tests/data/transactions/update.yml +3 -8
- data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
- data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
- data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
- data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
- data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
- data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
- data/spec/spec_tests/retryable_reads_spec.rb +5 -2
- data/spec/spec_tests/retryable_writes_spec.rb +5 -2
- data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
- data/spec/spec_tests/sdam_spec.rb +2 -2
- data/spec/spec_tests/transactions_api_spec.rb +1 -67
- data/spec/spec_tests/transactions_spec.rb +2 -66
- data/spec/support/authorization.rb +4 -0
- data/spec/support/change_streams.rb +30 -10
- data/spec/support/change_streams/operation.rb +27 -0
- data/spec/support/client_registry.rb +44 -25
- data/spec/support/cluster_config.rb +25 -14
- data/spec/support/cluster_tools.rb +32 -10
- data/spec/support/command_monitoring.rb +1 -1
- data/spec/support/common_shortcuts.rb +30 -0
- data/spec/support/connection_string.rb +8 -3
- data/spec/support/constraints.rb +34 -0
- data/spec/support/crud.rb +31 -16
- data/spec/support/crud/context.rb +23 -0
- data/spec/support/crud/operation.rb +311 -14
- data/spec/support/crud/spec.rb +2 -1
- data/spec/support/crud/test.rb +24 -27
- data/spec/support/crud/test_base.rb +22 -0
- data/spec/support/crud/verifier.rb +15 -1
- data/spec/support/event_subscriber.rb +12 -0
- data/spec/support/sdam_formatter_integration.rb +12 -6
- data/spec/support/shared/server_selector.rb +10 -0
- data/spec/support/shared/session.rb +13 -12
- data/spec/support/spec_config.rb +32 -22
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/transactions.rb +87 -0
- data/spec/support/transactions/context.rb +33 -0
- data/spec/support/transactions/operation.rb +99 -349
- data/spec/support/transactions/spec.rb +1 -3
- data/spec/support/transactions/test.rb +110 -49
- data/spec/support/utils.rb +74 -1
- metadata +52 -10
- metadata.gz.sig +0 -0
- data/spec/support/crud/read.rb +0 -265
- data/spec/support/crud/write.rb +0 -284
@@ -74,20 +74,6 @@ class ClusterConfig
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
def primary_address_str
|
78
|
-
primary_address
|
79
|
-
end
|
80
|
-
|
81
|
-
def primary_address_host
|
82
|
-
both = primary_address_str
|
83
|
-
both.split(':').first
|
84
|
-
end
|
85
|
-
|
86
|
-
def primary_address_port
|
87
|
-
both = primary_address_str
|
88
|
-
both.split(':')[1] || 27017
|
89
|
-
end
|
90
|
-
|
91
77
|
# Try running a command on the admin database to see if the mongod was
|
92
78
|
# started with auth.
|
93
79
|
def auth_enabled?
|
@@ -111,4 +97,29 @@ class ClusterConfig
|
|
111
97
|
topology.to_sym
|
112
98
|
end
|
113
99
|
end
|
100
|
+
|
101
|
+
def storage_engine
|
102
|
+
@storage_engine ||= begin
|
103
|
+
# 2.6 does not have wired tiger
|
104
|
+
if short_server_version == '2.6'
|
105
|
+
:mmapv1
|
106
|
+
else
|
107
|
+
client = ClientRegistry.instance.global_client('root_authorized')
|
108
|
+
if topology == :sharded
|
109
|
+
shards = client.use(:admin).command(listShards: 1).first
|
110
|
+
shard = shards['shards'].first
|
111
|
+
address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
|
112
|
+
client = ClusterTools.instance.direct_client(address_str,
|
113
|
+
SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
|
114
|
+
end
|
115
|
+
rv = client.use(:admin).command(serverStatus: 1).first
|
116
|
+
rv = rv['storageEngine']['name']
|
117
|
+
rv_map = {
|
118
|
+
'wiredTiger' => :wired_tiger,
|
119
|
+
'mmapv1' => :mmapv1,
|
120
|
+
}
|
121
|
+
rv_map[rv] || rv
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
114
125
|
end
|
@@ -309,25 +309,47 @@ class ClusterTools
|
|
309
309
|
end
|
310
310
|
end
|
311
311
|
|
312
|
-
private
|
313
|
-
|
314
312
|
def admin_client
|
315
313
|
# Since we are triggering elections, we need to have a higher server
|
316
314
|
# selection timeout applied. The default timeout for tests assumes a
|
317
315
|
# stable deployment.
|
318
|
-
|
319
|
-
|
316
|
+
(
|
317
|
+
@admin_client ||= ClientRegistry.instance.global_client('root_authorized').
|
318
|
+
with(server_selection_timeout: 15).use(:admin)
|
319
|
+
).tap do |client|
|
320
|
+
ClientRegistry.reconnect_client_if_perished(client)
|
321
|
+
end
|
320
322
|
end
|
321
323
|
|
322
|
-
def direct_client(address)
|
324
|
+
def direct_client(address, options = {})
|
323
325
|
@direct_clients ||= {}
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
326
|
+
cache_key = {address: address}.update(options)
|
327
|
+
(
|
328
|
+
@direct_clients[cache_key] ||= ClientRegistry.instance.new_local_client(
|
329
|
+
[address.to_s],
|
330
|
+
SpecConfig.instance.test_options.merge(
|
331
|
+
SpecConfig.instance.auth_options).merge(
|
332
|
+
connect: :direct, server_selection_timeout: 10).merge(options))
|
333
|
+
).tap do |client|
|
334
|
+
ClientRegistry.reconnect_client_if_perished(client)
|
335
|
+
end
|
329
336
|
end
|
330
337
|
|
338
|
+
def close_clients
|
339
|
+
if @admin_client
|
340
|
+
@admin_client.close
|
341
|
+
@admin_client = nil
|
342
|
+
end
|
343
|
+
if @direct_clients
|
344
|
+
@direct_clients.each do |cache_key, client|
|
345
|
+
client.close
|
346
|
+
end
|
347
|
+
@direct_clients = nil
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
private
|
352
|
+
|
331
353
|
def each_server(&block)
|
332
354
|
admin_client.cluster.servers_list.each(&block)
|
333
355
|
end
|
@@ -248,7 +248,7 @@ module Mongo
|
|
248
248
|
@description = test['description']
|
249
249
|
@max_server_version = test['ignore_if_server_version_greater_than']
|
250
250
|
@min_server_fcv = test['ignore_if_server_version_less_than']
|
251
|
-
@operation = Mongo::CRUD::Operation.
|
251
|
+
@operation = Mongo::CRUD::Operation.new(self, test['operation'])
|
252
252
|
@expectations = test['expectations'].map{ |e| Expectation.new(e) }
|
253
253
|
end
|
254
254
|
|
@@ -9,6 +9,36 @@ module CommonShortcuts
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
# For tests which require clients to connect, clean slate asks all
|
14
|
+
# existing clients to be closed prior to the test execution.
|
15
|
+
# Note that clean_slate closes all clients for each test in the scope.
|
16
|
+
def clean_slate
|
17
|
+
before do
|
18
|
+
ClientRegistry.instance.close_all_clients
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Similar to clean slate but closes clients once before all tests in
|
23
|
+
# the scope. Use when the tests do not create new clients but do not
|
24
|
+
# want any background output from previously existing clients.
|
25
|
+
def clean_slate_for_all
|
26
|
+
before(:all) do
|
27
|
+
ClientRegistry.instance.close_all_clients
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# For some reason, there are tests which fail on evergreen either
|
32
|
+
# intermittently or reliably that always succeed locally.
|
33
|
+
# Debugging of tests in evergreen is difficult/impossible,
|
34
|
+
# thus this workaround.
|
35
|
+
def clean_slate_on_evergreen
|
36
|
+
before(:all) do
|
37
|
+
if SpecConfig.instance.ci?
|
38
|
+
ClientRegistry.instance.close_all_clients
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
12
42
|
end
|
13
43
|
|
14
44
|
module InstanceMethods
|
@@ -123,6 +123,7 @@ module Mongo
|
|
123
123
|
end
|
124
124
|
|
125
125
|
class Test
|
126
|
+
include RSpec::Core::Pending
|
126
127
|
|
127
128
|
attr_reader :description
|
128
129
|
attr_reader :uri_string
|
@@ -153,6 +154,10 @@ module Mongo
|
|
153
154
|
|
154
155
|
def client
|
155
156
|
@client ||= ClientRegistry.instance.new_local_client(@spec['uri'], monitoring_io: false)
|
157
|
+
rescue Mongo::Error::LintError => e
|
158
|
+
if e.message =~ /arbitraryButStillValid/
|
159
|
+
skip 'Test uses a read concern that fails linter'
|
160
|
+
end
|
156
161
|
end
|
157
162
|
|
158
163
|
def uri
|
@@ -223,9 +228,9 @@ module Mongo
|
|
223
228
|
'maxidletimems' => :max_idle_time,
|
224
229
|
|
225
230
|
# Write Options
|
226
|
-
'journal' => [:
|
227
|
-
'w' => [:
|
228
|
-
'wtimeoutms' => [:
|
231
|
+
'journal' => [:write_concern, 'j'],
|
232
|
+
'w' => [:write_concern, 'w'],
|
233
|
+
'wtimeoutms' => [:write_concern, 'wtimeout'],
|
229
234
|
|
230
235
|
# Read Options
|
231
236
|
'readpreference' => ['read', 'mode'],
|
data/spec/support/constraints.rb
CHANGED
@@ -157,4 +157,38 @@ module Constraints
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
end
|
160
|
+
|
161
|
+
def require_no_multi_shard
|
162
|
+
before do
|
163
|
+
if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length > 1
|
164
|
+
skip 'Test requires a single shard if run in sharded topology'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def require_wired_tiger
|
170
|
+
before(:all) do
|
171
|
+
if ClusterConfig.instance.storage_engine != :wired_tiger
|
172
|
+
skip 'Test requires WiredTiger storage engine'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def require_wired_tiger_on_36
|
178
|
+
before(:all) do
|
179
|
+
if ClusterConfig.instance.short_server_version >= '3.6'
|
180
|
+
if ClusterConfig.instance.storage_engine != :wired_tiger
|
181
|
+
skip 'Test requires WiredTiger storage engine on 3.6+ servers'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def require_mmapv1
|
188
|
+
before do
|
189
|
+
if ClusterConfig.instance.storage_engine != :mmapv1
|
190
|
+
skip 'Test requires MMAPv1 storage engine'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
160
194
|
end
|
data/spec/support/crud.rb
CHANGED
@@ -15,13 +15,17 @@
|
|
15
15
|
require 'support/gridfs'
|
16
16
|
require 'support/crud/requirement'
|
17
17
|
require 'support/crud/spec'
|
18
|
+
require 'support/crud/test_base'
|
18
19
|
require 'support/crud/test'
|
19
20
|
require 'support/crud/outcome'
|
21
|
+
require 'support/crud/context'
|
20
22
|
require 'support/crud/operation'
|
21
|
-
require 'support/crud/read'
|
22
|
-
require 'support/crud/write'
|
23
23
|
require 'support/crud/verifier'
|
24
24
|
|
25
|
+
def collection_data(collection)
|
26
|
+
collection.find.to_a
|
27
|
+
end
|
28
|
+
|
25
29
|
def crud_execute_operations(spec, test, num_ops, event_subscriber, expect_error,
|
26
30
|
client
|
27
31
|
)
|
@@ -42,7 +46,7 @@ def crud_execute_operations(spec, test, num_ops, event_subscriber, expect_error,
|
|
42
46
|
result = if expect_error.nil?
|
43
47
|
res = nil
|
44
48
|
begin
|
45
|
-
res = test.run(
|
49
|
+
res = test.run(client, num_ops)
|
46
50
|
rescue => e
|
47
51
|
res = e
|
48
52
|
end
|
@@ -50,13 +54,13 @@ def crud_execute_operations(spec, test, num_ops, event_subscriber, expect_error,
|
|
50
54
|
elsif expect_error
|
51
55
|
error = nil
|
52
56
|
begin
|
53
|
-
test.run(
|
57
|
+
test.run(client, num_ops)
|
54
58
|
rescue => e
|
55
59
|
error = e
|
56
60
|
end
|
57
61
|
error
|
58
62
|
else
|
59
|
-
test.run(
|
63
|
+
test.run(client, num_ops)
|
60
64
|
end
|
61
65
|
|
62
66
|
$crud_event_cache ||= {}
|
@@ -68,7 +72,7 @@ def crud_execute_operations(spec, test, num_ops, event_subscriber, expect_error,
|
|
68
72
|
if last_op.outcome && last_op.outcome.collection_data?
|
69
73
|
verify_collection = client[last_op.verify_collection_name]
|
70
74
|
$crud_collection_data_cache ||= {}
|
71
|
-
$crud_collection_data_cache[cache_key] = verify_collection
|
75
|
+
$crud_collection_data_cache[cache_key] = collection_data(verify_collection)
|
72
76
|
end
|
73
77
|
|
74
78
|
result
|
@@ -136,7 +140,7 @@ def define_crud_spec_test_examples(spec, req = nil, &block)
|
|
136
140
|
result
|
137
141
|
verifier.verify_collection_data(
|
138
142
|
operation.outcome.collection_data,
|
139
|
-
verify_collection
|
143
|
+
collection_data(verify_collection))
|
140
144
|
end
|
141
145
|
end
|
142
146
|
|
@@ -173,6 +177,20 @@ def define_crud_spec_test_examples(spec, req = nil, &block)
|
|
173
177
|
end
|
174
178
|
end
|
175
179
|
end
|
180
|
+
|
181
|
+
if test.outcome && test.outcome.collection_data?
|
182
|
+
let(:result) do
|
183
|
+
crud_execute_operations(spec, test, test.operations.length,
|
184
|
+
event_subscriber, nil, client)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'has the correct data in the collection' do
|
188
|
+
result
|
189
|
+
verifier.verify_collection_data(
|
190
|
+
test.outcome.collection_data,
|
191
|
+
collection_data(client[test.outcome.collection_name || spec.collection_name]))
|
192
|
+
end
|
193
|
+
end
|
176
194
|
end
|
177
195
|
end
|
178
196
|
end
|
@@ -203,17 +221,14 @@ def define_spec_tests_with_requirements(spec, &block)
|
|
203
221
|
end
|
204
222
|
end
|
205
223
|
|
206
|
-
def define_crud_spec_tests(
|
207
|
-
|
208
|
-
|
209
|
-
test_paths.each do |path|
|
224
|
+
def define_crud_spec_tests(test_paths, spec_cls = Mongo::CRUD::Spec, &block)
|
225
|
+
test_paths.each do |path|
|
210
226
|
|
211
|
-
|
227
|
+
spec = spec_cls.new(path)
|
212
228
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
end
|
229
|
+
context(spec.description) do
|
230
|
+
define_spec_tests_with_requirements(spec) do |req|
|
231
|
+
define_crud_spec_test_examples(spec, req, &block)
|
217
232
|
end
|
218
233
|
end
|
219
234
|
end
|
@@ -0,0 +1,23 @@
|
|
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 CRUD
|
17
|
+
class Context
|
18
|
+
def transform_arguments(arguments)
|
19
|
+
arguments
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -15,29 +15,326 @@
|
|
15
15
|
module Mongo
|
16
16
|
module CRUD
|
17
17
|
|
18
|
-
|
19
|
-
#
|
20
|
-
# @since 2.0.0
|
21
|
-
module Operation
|
22
|
-
extend self
|
18
|
+
class Operation
|
23
19
|
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# @example Get the operation.
|
27
|
-
# Operation.get(spec)
|
20
|
+
# Instantiate the operation.
|
28
21
|
#
|
29
22
|
# @param [ Hash ] spec The operation specification.
|
23
|
+
# @param [ Hash ] outcome_spec The outcome specification.
|
24
|
+
# If not provided, outcome is taken out of operation specification.
|
25
|
+
#
|
26
|
+
# @since 2.0.0
|
27
|
+
def initialize(crud_test, spec, outcome_spec = nil)
|
28
|
+
@crud_test = crud_test
|
29
|
+
@spec = IceNine.deep_freeze(spec)
|
30
|
+
@name = spec['name']
|
31
|
+
@arguments = spec['arguments'] || {}
|
32
|
+
@outcome = Outcome.new(outcome_spec || spec)
|
33
|
+
end
|
34
|
+
|
35
|
+
# The operation name.
|
30
36
|
#
|
31
|
-
# @return [
|
37
|
+
# @return [ String ] name The operation name.
|
32
38
|
#
|
33
39
|
# @since 2.0.0
|
34
|
-
|
35
|
-
|
36
|
-
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
attr_reader :arguments
|
43
|
+
|
44
|
+
attr_reader :outcome
|
45
|
+
|
46
|
+
def object
|
47
|
+
@spec['object'] || 'collection'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Which collection to verify results from.
|
51
|
+
# Returns the collection name specified on the operation, or
|
52
|
+
# the collection name for the entire spec file.
|
53
|
+
def verify_collection_name
|
54
|
+
if outcome && outcome.collection_name
|
55
|
+
outcome.collection_name
|
37
56
|
else
|
38
|
-
|
57
|
+
@spec['collection_name'] || 'crud_spec_test'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Whether the operation is expected to have results.
|
62
|
+
#
|
63
|
+
# @example Whether the operation is expected to have results.
|
64
|
+
# operation.has_results?
|
65
|
+
#
|
66
|
+
# @return [ true, false ] If the operation is expected to have results.
|
67
|
+
#
|
68
|
+
# @since 2.0.0
|
69
|
+
def has_results?
|
70
|
+
!(name == 'aggregate' &&
|
71
|
+
pipeline.find {|op| op.keys.include?('$out') })
|
72
|
+
end
|
73
|
+
|
74
|
+
# Execute the operation.
|
75
|
+
#
|
76
|
+
# @example Execute the operation.
|
77
|
+
# operation.execute
|
78
|
+
#
|
79
|
+
# @param [ Collection ] collection The collection to execute the operation on.
|
80
|
+
#
|
81
|
+
# @return [ Result, Array<Hash> ] The result of executing the operation.
|
82
|
+
#
|
83
|
+
# @since 2.0.0
|
84
|
+
def execute(target)
|
85
|
+
op_name = Utils.underscore(name)
|
86
|
+
if target.is_a?(Mongo::Database)
|
87
|
+
op_name = "db_#{op_name}"
|
88
|
+
elsif target.is_a?(Mongo::Client)
|
89
|
+
op_name= "client_#{op_name}"
|
90
|
+
end
|
91
|
+
send(op_name, target, Context.new)
|
92
|
+
end
|
93
|
+
|
94
|
+
def collection_options
|
95
|
+
Utils.convert_operation_options(@spec['collectionOptions'])
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# read operations
|
101
|
+
|
102
|
+
def aggregate(collection, context)
|
103
|
+
collection.aggregate(arguments['pipeline'], context.transform_arguments(options)).to_a
|
104
|
+
end
|
105
|
+
|
106
|
+
def db_aggregate(database, context)
|
107
|
+
database.aggregate(arguments['pipeline'], context.transform_arguments(options)).to_a
|
108
|
+
end
|
109
|
+
|
110
|
+
def count(collection, context)
|
111
|
+
collection.count(arguments['filter'], context.transform_arguments(options))
|
112
|
+
end
|
113
|
+
|
114
|
+
def count_documents(collection, context)
|
115
|
+
collection.count_documents(arguments['filter'], context.transform_arguments(options))
|
116
|
+
end
|
117
|
+
|
118
|
+
def distinct(collection, context)
|
119
|
+
collection.distinct(arguments['fieldName'], arguments['filter'], context.transform_arguments(options))
|
120
|
+
end
|
121
|
+
|
122
|
+
def estimated_document_count(collection, context)
|
123
|
+
collection.estimated_document_count(context.transform_arguments(options))
|
124
|
+
end
|
125
|
+
|
126
|
+
def find(collection, context)
|
127
|
+
opts = context.transform_arguments(options)
|
128
|
+
if arguments['modifiers']
|
129
|
+
opts = opts.merge(modifiers: BSON::Document.new(arguments['modifiers']))
|
130
|
+
end
|
131
|
+
if read_preference
|
132
|
+
collection = collection.with(read: read_preference)
|
133
|
+
end
|
134
|
+
collection.find(arguments['filter'], opts).to_a
|
135
|
+
end
|
136
|
+
|
137
|
+
def find_one(collection, context)
|
138
|
+
find(collection, context).first
|
139
|
+
end
|
140
|
+
|
141
|
+
def client_list_databases(client, context)
|
142
|
+
client.list_databases
|
143
|
+
end
|
144
|
+
|
145
|
+
def client_list_database_names(client, context)
|
146
|
+
client.list_databases({}, true)
|
147
|
+
end
|
148
|
+
|
149
|
+
def client_list_database_objects(client, context)
|
150
|
+
client.list_mongo_databases
|
151
|
+
end
|
152
|
+
|
153
|
+
def db_list_collections(database, context)
|
154
|
+
database.list_collections
|
155
|
+
end
|
156
|
+
|
157
|
+
def db_list_collection_names(database, context)
|
158
|
+
database.collection_names
|
159
|
+
end
|
160
|
+
|
161
|
+
def db_list_collection_objects(database, context)
|
162
|
+
database.collections
|
163
|
+
end
|
164
|
+
|
165
|
+
def list_indexes(collection, context)
|
166
|
+
collection.indexes.to_a
|
167
|
+
end
|
168
|
+
|
169
|
+
def watch(collection, context)
|
170
|
+
collection.watch
|
171
|
+
end
|
172
|
+
|
173
|
+
def db_watch(database, context)
|
174
|
+
database.watch
|
175
|
+
end
|
176
|
+
|
177
|
+
def client_watch(client, context)
|
178
|
+
client.watch
|
179
|
+
end
|
180
|
+
|
181
|
+
def download(fs_bucket, context)
|
182
|
+
stream = fs_bucket.open_download_stream(BSON::ObjectId.from_string(arguments['id']['$oid']))
|
183
|
+
stream.read
|
184
|
+
end
|
185
|
+
|
186
|
+
def download_by_name(fs_bucket, context)
|
187
|
+
stream = fs_bucket.open_download_stream_by_name(arguments['filename'])
|
188
|
+
stream.read
|
189
|
+
end
|
190
|
+
|
191
|
+
def map_reduce(collection, context)
|
192
|
+
view = Mongo::Collection::View.new(collection)
|
193
|
+
mr = Mongo::Collection::View::MapReduce.new(view, arguments['map']['$code'], arguments['reduce']['$code'])
|
194
|
+
mr.to_a
|
195
|
+
end
|
196
|
+
|
197
|
+
# write operations
|
198
|
+
|
199
|
+
def bulk_write(collection, context)
|
200
|
+
result = collection.bulk_write(requests, context.transform_arguments(options))
|
201
|
+
return_doc = {}
|
202
|
+
return_doc['deletedCount'] = result.deleted_count || 0
|
203
|
+
return_doc['insertedIds'] = result.inserted_ids if result.inserted_ids
|
204
|
+
return_doc['insertedCount'] = result.inserted_count || 0
|
205
|
+
return_doc['upsertedId'] = result.upserted_id if arguments['upsert']
|
206
|
+
return_doc['upsertedIds'] = result.upserted_ids if result.upserted_ids
|
207
|
+
return_doc['upsertedCount'] = result.upserted_count || 0
|
208
|
+
return_doc['matchedCount'] = result.matched_count || 0
|
209
|
+
return_doc['modifiedCount'] = result.modified_count || 0
|
210
|
+
return_doc
|
211
|
+
end
|
212
|
+
|
213
|
+
def delete_many(collection, context)
|
214
|
+
result = collection.delete_many(arguments['filter'], context.transform_arguments(options))
|
215
|
+
{ 'deletedCount' => result.deleted_count }
|
216
|
+
end
|
217
|
+
|
218
|
+
def delete_one(collection, context)
|
219
|
+
result = collection.delete_one(arguments['filter'], context.transform_arguments(options))
|
220
|
+
{ 'deletedCount' => result.deleted_count }
|
221
|
+
end
|
222
|
+
|
223
|
+
def insert_many(collection, context)
|
224
|
+
result = collection.insert_many(arguments['documents'], context.transform_arguments(options))
|
225
|
+
{ 'insertedIds' => result.inserted_ids }
|
226
|
+
end
|
227
|
+
|
228
|
+
def insert_one(collection, context)
|
229
|
+
result = collection.insert_one(arguments['document'], context.transform_arguments(options))
|
230
|
+
{ 'insertedId' => result.inserted_id }
|
231
|
+
end
|
232
|
+
|
233
|
+
def replace_one(collection, context)
|
234
|
+
result = collection.replace_one(arguments['filter'], arguments['replacement'], context.transform_arguments(options))
|
235
|
+
update_return_doc(result)
|
236
|
+
end
|
237
|
+
|
238
|
+
def update_many(collection, context)
|
239
|
+
result = collection.update_many(arguments['filter'], arguments['update'], context.transform_arguments(options))
|
240
|
+
update_return_doc(result)
|
241
|
+
end
|
242
|
+
|
243
|
+
def update_one(collection, context)
|
244
|
+
result = collection.update_one(arguments['filter'], arguments['update'], context.transform_arguments(options))
|
245
|
+
update_return_doc(result)
|
246
|
+
end
|
247
|
+
|
248
|
+
def find_one_and_delete(collection, context)
|
249
|
+
collection.find_one_and_delete(arguments['filter'], context.transform_arguments(options))
|
250
|
+
end
|
251
|
+
|
252
|
+
def find_one_and_replace(collection, context)
|
253
|
+
collection.find_one_and_replace(arguments['filter'], arguments['replacement'], context.transform_arguments(options))
|
254
|
+
end
|
255
|
+
|
256
|
+
def find_one_and_update(collection, context)
|
257
|
+
collection.find_one_and_update(arguments['filter'], arguments['update'], context.transform_arguments(options))
|
258
|
+
end
|
259
|
+
|
260
|
+
# options & arguments
|
261
|
+
|
262
|
+
def options
|
263
|
+
out = {}
|
264
|
+
# Most tests have an "arguments" key which is a hash of options to
|
265
|
+
# be provided to the operation. The command monitoring unacknowledged
|
266
|
+
# bulk write test is an exception in that it has an "options" key
|
267
|
+
# with the options.
|
268
|
+
arguments.merge(arguments['options'] || {}).each do |spec_k, v|
|
269
|
+
ruby_k = Utils.underscore(spec_k).to_sym
|
270
|
+
|
271
|
+
if v.is_a?(Hash) && v['$numberLong']
|
272
|
+
v = v['$numberLong'].to_i
|
273
|
+
end
|
274
|
+
|
275
|
+
if respond_to?("transform_#{ruby_k}", true)
|
276
|
+
v = send("transform_#{ruby_k}", v)
|
277
|
+
end
|
278
|
+
|
279
|
+
out[ruby_k] = v
|
280
|
+
end
|
281
|
+
out
|
282
|
+
end
|
283
|
+
|
284
|
+
def requests
|
285
|
+
arguments['requests'].map do |request|
|
286
|
+
case request.keys.first
|
287
|
+
when 'insertOne' then
|
288
|
+
{ insert_one: request['insertOne']['document'] }
|
289
|
+
when 'updateOne' then
|
290
|
+
update = request['updateOne']
|
291
|
+
{ update_one: { filter: update['filter'], update: update['update'] } }
|
292
|
+
when 'name' then
|
293
|
+
bulk_request(request)
|
294
|
+
end
|
39
295
|
end
|
40
296
|
end
|
297
|
+
|
298
|
+
def bulk_request(request)
|
299
|
+
op_name = Utils.underscore(request['name'])
|
300
|
+
args = Utils.shallow_snakeize_hash(request['arguments'])
|
301
|
+
if args[:document]
|
302
|
+
unless args.keys == [:document]
|
303
|
+
raise "If :document is given, it must be the only key"
|
304
|
+
end
|
305
|
+
args = args[:document]
|
306
|
+
end
|
307
|
+
{ op_name => args }
|
308
|
+
end
|
309
|
+
|
310
|
+
def upsert
|
311
|
+
arguments['upsert']
|
312
|
+
end
|
313
|
+
|
314
|
+
def transform_return_document(v)
|
315
|
+
Utils.underscore(v).to_sym
|
316
|
+
end
|
317
|
+
|
318
|
+
def update
|
319
|
+
arguments['update']
|
320
|
+
end
|
321
|
+
|
322
|
+
def transform_read_preference(v)
|
323
|
+
Utils.snakeize_hash(v)
|
324
|
+
end
|
325
|
+
|
326
|
+
def read_preference
|
327
|
+
transform_read_preference(@spec['read_preference'])
|
328
|
+
end
|
329
|
+
|
330
|
+
def update_return_doc(result)
|
331
|
+
return_doc = {}
|
332
|
+
return_doc['upsertedId'] = result.upserted_id if arguments['upsert']
|
333
|
+
return_doc['upsertedCount'] = result.upserted_count
|
334
|
+
return_doc['matchedCount'] = result.matched_count
|
335
|
+
return_doc['modifiedCount'] = result.modified_count if result.modified_count
|
336
|
+
return_doc
|
337
|
+
end
|
41
338
|
end
|
42
339
|
end
|
43
340
|
end
|