mongo 2.9.2 → 2.10.0.rc0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|