mongo 2.14.1 → 2.15.0.alpha
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/README.md +4 -1
- data/Rakefile +8 -15
- data/lib/mongo/auth/aws/conversation.rb +1 -4
- data/lib/mongo/auth/base.rb +13 -7
- data/lib/mongo/auth/conversation_base.rb +32 -0
- data/lib/mongo/auth/cr/conversation.rb +6 -29
- data/lib/mongo/auth/gssapi/conversation.rb +4 -15
- data/lib/mongo/auth/ldap/conversation.rb +3 -14
- data/lib/mongo/auth/sasl_conversation_base.rb +1 -13
- data/lib/mongo/auth/scram_conversation_base.rb +7 -34
- data/lib/mongo/auth/user/view.rb +16 -9
- data/lib/mongo/auth/x509/conversation.rb +4 -25
- data/lib/mongo/bulk_write.rb +21 -18
- data/lib/mongo/client.rb +82 -6
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
- data/lib/mongo/cluster.rb +19 -2
- data/lib/mongo/collection/view/aggregation.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +1 -1
- data/lib/mongo/collection/view/iterable.rb +7 -17
- data/lib/mongo/collection/view/map_reduce.rb +2 -2
- data/lib/mongo/collection/view/readable.rb +42 -20
- data/lib/mongo/collection/view/writable.rb +14 -14
- data/lib/mongo/collection.rb +6 -6
- data/lib/mongo/cursor.rb +2 -12
- data/lib/mongo/database/view.rb +1 -1
- data/lib/mongo/database.rb +8 -3
- data/lib/mongo/error/bulk_write_error.rb +17 -3
- data/lib/mongo/error/internal_driver_error.rb +22 -0
- data/lib/mongo/error/operation_failure.rb +21 -2
- data/lib/mongo/error/parser.rb +65 -12
- data/lib/mongo/error/server_api_conflict.rb +23 -0
- data/lib/mongo/error/server_api_not_supported.rb +24 -0
- data/lib/mongo/error/unmet_dependency.rb +21 -0
- data/lib/mongo/error.rb +9 -1
- data/lib/mongo/index/view.rb +21 -11
- data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +27 -16
- data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +26 -15
- data/lib/mongo/monitoring.rb +13 -4
- data/lib/mongo/operation/collections_info/command.rb +2 -2
- data/lib/mongo/operation/collections_info.rb +18 -1
- data/lib/mongo/operation/context.rb +99 -0
- data/lib/mongo/operation/indexes.rb +15 -1
- data/lib/mongo/operation/insert/command.rb +2 -2
- data/lib/mongo/operation/insert/legacy.rb +2 -2
- data/lib/mongo/operation/insert/op_msg.rb +2 -2
- data/lib/mongo/operation/list_collections/result.rb +4 -1
- data/lib/mongo/operation/parallel_scan/command.rb +2 -1
- data/lib/mongo/operation/result.rb +2 -0
- data/lib/mongo/operation/shared/executable.rb +24 -14
- data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
- data/lib/mongo/operation/shared/op_msg_or_command.rb +1 -7
- data/lib/mongo/operation/shared/op_msg_or_find_command.rb +1 -7
- data/lib/mongo/operation/shared/polymorphic_operation.rb +39 -0
- data/lib/mongo/operation/shared/read_preference_supported.rb +36 -38
- data/lib/mongo/operation/shared/response_handling.rb +23 -23
- data/lib/mongo/operation/shared/sessions_supported.rb +15 -5
- data/lib/mongo/operation/shared/write.rb +8 -18
- data/lib/mongo/operation.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +51 -5
- data/lib/mongo/protocol/message.rb +20 -2
- data/lib/mongo/protocol/msg.rb +38 -13
- data/lib/mongo/protocol/query.rb +11 -11
- data/lib/mongo/query_cache.rb +30 -0
- data/lib/mongo/retryable.rb +1 -1
- data/lib/mongo/server/app_metadata.rb +52 -18
- data/lib/mongo/server/connection.rb +5 -0
- data/lib/mongo/server/connection_base.rb +13 -10
- data/lib/mongo/server/connection_pool.rb +6 -2
- data/lib/mongo/server/description/features.rb +9 -8
- data/lib/mongo/server/description.rb +4 -0
- data/lib/mongo/server/monitor/app_metadata.rb +1 -1
- data/lib/mongo/server/monitor/connection.rb +9 -10
- data/lib/mongo/server/monitor.rb +20 -1
- data/lib/mongo/server/pending_connection.rb +24 -6
- data/lib/mongo/server/push_monitor.rb +11 -1
- data/lib/mongo/server.rb +7 -1
- data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
- data/lib/mongo/session/session_pool.rb +4 -2
- data/lib/mongo/session.rb +2 -2
- data/lib/mongo/socket/ssl.rb +8 -0
- data/lib/mongo/socket.rb +29 -4
- data/lib/mongo/uri/options_mapper.rb +38 -0
- data/lib/mongo/utils.rb +15 -0
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +23 -0
- data/spec/README.md +24 -1
- data/spec/integration/auth_spec.rb +25 -15
- data/spec/integration/bulk_write_error_message_spec.rb +41 -0
- data/spec/integration/change_stream_spec.rb +4 -4
- data/spec/integration/command_monitoring_spec.rb +2 -2
- data/spec/integration/connection_spec.rb +2 -0
- data/spec/integration/docs_examples_spec.rb +8 -1
- data/spec/integration/fork_reconnect_spec.rb +4 -1
- data/spec/integration/ocsp_verifier_spec.rb +13 -7
- data/spec/integration/operation_failure_code_spec.rb +1 -1
- data/spec/integration/operation_failure_message_spec.rb +90 -0
- data/spec/integration/query_cache_spec.rb +0 -45
- data/spec/integration/reconnect_spec.rb +1 -1
- data/spec/integration/snappy_compression_spec.rb +25 -0
- data/spec/integration/srv_monitoring_spec.rb +1 -1
- data/spec/integration/transactions_examples_spec.rb +6 -0
- data/spec/integration/zlib_compression_spec.rb +1 -1
- data/spec/integration/zstd_compression_spec.rb +26 -0
- data/spec/lite_spec_helper.rb +7 -1
- data/spec/mongo/address_spec.rb +15 -11
- data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
- data/spec/mongo/auth/ldap_spec.rb +5 -1
- data/spec/mongo/auth/scram_negotiation_spec.rb +1 -1
- data/spec/mongo/auth/scram_spec.rb +1 -1
- data/spec/mongo/auth/x509/conversation_spec.rb +3 -3
- data/spec/mongo/client_construction_spec.rb +207 -33
- data/spec/mongo/client_spec.rb +17 -0
- data/spec/mongo/cluster_spec.rb +1 -0
- data/spec/mongo/collection/view/explainable_spec.rb +1 -1
- data/spec/mongo/collection/view/readable_spec.rb +33 -19
- data/spec/mongo/collection_crud_spec.rb +4357 -0
- data/spec/mongo/collection_ddl_spec.rb +534 -0
- data/spec/mongo/collection_spec.rb +5 -4859
- data/spec/mongo/database_spec.rb +66 -4
- data/spec/mongo/error/bulk_write_error_spec.rb +3 -3
- data/spec/mongo/error/parser_spec.rb +37 -6
- data/spec/mongo/index/view_spec.rb +4 -0
- data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +1 -1
- data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +1 -1
- data/spec/mongo/operation/aggregate_spec.rb +2 -1
- data/spec/mongo/operation/collections_info_spec.rb +4 -1
- data/spec/mongo/operation/command_spec.rb +6 -3
- data/spec/mongo/operation/create_index_spec.rb +6 -3
- data/spec/mongo/operation/create_user_spec.rb +6 -3
- data/spec/mongo/operation/delete/bulk_spec.rb +9 -6
- data/spec/mongo/operation/delete_spec.rb +11 -7
- data/spec/mongo/operation/drop_index_spec.rb +6 -2
- data/spec/mongo/operation/find/legacy_spec.rb +3 -1
- data/spec/mongo/operation/get_more_spec.rb +3 -1
- data/spec/mongo/operation/indexes_spec.rb +5 -1
- data/spec/mongo/operation/insert/bulk_spec.rb +10 -7
- data/spec/mongo/operation/insert_spec.rb +15 -12
- data/spec/mongo/operation/map_reduce_spec.rb +5 -2
- data/spec/mongo/operation/read_preference_legacy_spec.rb +19 -9
- data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
- data/spec/mongo/operation/remove_user_spec.rb +6 -3
- data/spec/mongo/operation/result_spec.rb +1 -1
- data/spec/mongo/operation/update/bulk_spec.rb +9 -6
- data/spec/mongo/operation/update_spec.rb +10 -7
- data/spec/mongo/operation/update_user_spec.rb +4 -1
- data/spec/mongo/protocol/compressed_spec.rb +26 -12
- data/spec/mongo/query_cache_middleware_spec.rb +55 -0
- data/spec/mongo/retryable_spec.rb +3 -2
- data/spec/mongo/server/app_metadata_shared.rb +7 -33
- data/spec/mongo/server/app_metadata_spec.rb +2 -0
- data/spec/mongo/server/connection_pool/populator_spec.rb +3 -1
- data/spec/mongo/server/connection_pool_spec.rb +1 -1
- data/spec/mongo/server/connection_spec.rb +24 -17
- data/spec/mongo/server/monitor/connection_spec.rb +17 -7
- data/spec/mongo/server/monitor_spec.rb +9 -1
- data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
- data/spec/mongo/server_spec.rb +15 -2
- data/spec/mongo/socket/ssl_spec.rb +40 -0
- data/spec/mongo/socket_spec.rb +2 -2
- data/spec/mongo/tls_context_hooks_spec.rb +37 -0
- data/spec/runners/connection_string.rb +0 -4
- data/spec/runners/crud/requirement.rb +40 -3
- data/spec/runners/crud/verifier.rb +8 -0
- data/spec/runners/transactions/operation.rb +1 -1
- data/spec/runners/transactions/test.rb +1 -0
- data/spec/runners/unified/assertions.rb +249 -0
- data/spec/runners/unified/change_stream_operations.rb +26 -0
- data/spec/runners/unified/crud_operations.rb +199 -0
- data/spec/runners/unified/ddl_operations.rb +96 -0
- data/spec/runners/unified/entity_map.rb +39 -0
- data/spec/runners/unified/error.rb +25 -0
- data/spec/runners/unified/event_subscriber.rb +91 -0
- data/spec/runners/unified/exceptions.rb +21 -0
- data/spec/runners/unified/grid_fs_operations.rb +55 -0
- data/spec/runners/unified/support_operations.rb +250 -0
- data/spec/runners/unified/test.rb +393 -0
- data/spec/runners/unified/test_group.rb +28 -0
- data/spec/runners/unified/using_hash.rb +31 -0
- data/spec/runners/unified.rb +96 -0
- data/spec/shared/lib/mrss/cluster_config.rb +0 -3
- data/spec/shared/lib/mrss/docker_runner.rb +0 -3
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -16
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -3
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -3
- data/spec/shared/shlib/server.sh +1 -1
- data/spec/spec_helper.rb +4 -1
- data/spec/spec_tests/crud_unified_spec.rb +10 -0
- data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
- data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
- data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
- data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
- data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
- data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +2 -0
- data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
- data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
- data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
- data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +416 -0
- data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +409 -0
- data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +67 -0
- data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
- data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +44 -0
- data/spec/spec_tests/data/versioned_api/transaction-handling.yml +180 -0
- data/spec/spec_tests/unified_spec.rb +15 -0
- data/spec/spec_tests/uri_options_spec.rb +16 -0
- data/spec/spec_tests/versioned_api_spec.rb +10 -0
- data/spec/support/client_registry.rb +4 -8
- data/spec/support/client_registry_macros.rb +4 -4
- data/spec/support/common_shortcuts.rb +15 -1
- data/spec/support/shared/session.rb +2 -2
- data/spec/support/spec_config.rb +42 -11
- data/spec/support/utils.rb +64 -3
- data.tar.gz.sig +0 -0
- metadata +1005 -915
- metadata.gz.sig +0 -0
- data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
- data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
- data/spec/integration/secondary_reads_spec.rb +0 -102
- data/spec/support/cluster_config.rb +0 -207
@@ -0,0 +1,393 @@
|
|
1
|
+
require 'runners/crud/requirement'
|
2
|
+
require 'runners/unified/crud_operations'
|
3
|
+
require 'runners/unified/grid_fs_operations'
|
4
|
+
require 'runners/unified/ddl_operations'
|
5
|
+
require 'runners/unified/change_stream_operations'
|
6
|
+
require 'runners/unified/support_operations'
|
7
|
+
require 'runners/unified/assertions'
|
8
|
+
require 'support/utils'
|
9
|
+
|
10
|
+
module Unified
|
11
|
+
|
12
|
+
class Test
|
13
|
+
include CrudOperations
|
14
|
+
include GridFsOperations
|
15
|
+
include DdlOperations
|
16
|
+
include ChangeStreamOperations
|
17
|
+
include SupportOperations
|
18
|
+
include Assertions
|
19
|
+
|
20
|
+
def initialize(spec, **opts)
|
21
|
+
@spec = spec
|
22
|
+
@entities = EntityMap.new
|
23
|
+
@test_spec = UsingHash[@spec.fetch('test')]
|
24
|
+
@description = @test_spec.use('description')
|
25
|
+
@outcome = @test_spec.use('outcome')
|
26
|
+
@expected_events = @test_spec.use('expectEvents')
|
27
|
+
@skip_reason = @test_spec.use('skipReason')
|
28
|
+
if req = @test_spec.use('runOnRequirements')
|
29
|
+
@reqs = req.map { |r| Mongo::CRUD::Requirement.new(r) }
|
30
|
+
end
|
31
|
+
if req = @spec['group_runOnRequirements']
|
32
|
+
@group_reqs = req.map { |r| Mongo::CRUD::Requirement.new(r) }
|
33
|
+
end
|
34
|
+
mongoses = @spec['createEntities'].select do |spec|
|
35
|
+
spec['client']
|
36
|
+
end.map do |spec|
|
37
|
+
spec['client']['useMultipleMongoses']
|
38
|
+
end.compact.uniq
|
39
|
+
if mongoses.length > 1
|
40
|
+
raise Error::InvalidTest, "Conflicting useMultipleMongoses values"
|
41
|
+
end
|
42
|
+
@multiple_mongoses = mongoses.first
|
43
|
+
@test_spec.freeze
|
44
|
+
@subscribers = {}
|
45
|
+
@options = opts
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :test_spec
|
49
|
+
attr_reader :description
|
50
|
+
attr_reader :outcome
|
51
|
+
attr_reader :skip_reason
|
52
|
+
attr_reader :reqs, :group_reqs
|
53
|
+
attr_reader :options
|
54
|
+
|
55
|
+
def skip?
|
56
|
+
!!@skip_reason
|
57
|
+
end
|
58
|
+
|
59
|
+
def require_multiple_mongoses?
|
60
|
+
@multiple_mongoses == true
|
61
|
+
end
|
62
|
+
|
63
|
+
def require_single_mongos?
|
64
|
+
@multiple_mongoses == false
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_reader :entities
|
68
|
+
|
69
|
+
def create_entities
|
70
|
+
@spec['createEntities'].each do |entity_spec|
|
71
|
+
unless entity_spec.keys.length == 1
|
72
|
+
raise NotImplementedError, "Entity must have exactly one key"
|
73
|
+
end
|
74
|
+
|
75
|
+
type, spec = entity_spec.first
|
76
|
+
spec = UsingHash[spec]
|
77
|
+
id = spec.use!('id')
|
78
|
+
|
79
|
+
entity = case type
|
80
|
+
when 'client'
|
81
|
+
# Handled earlier
|
82
|
+
spec.delete('useMultipleMongoses')
|
83
|
+
|
84
|
+
if smc_opts = spec.use('uriOptions')
|
85
|
+
opts = Mongo::URI::OptionsMapper.new.smc_to_ruby(smc_opts)
|
86
|
+
else
|
87
|
+
opts = {}
|
88
|
+
end
|
89
|
+
|
90
|
+
if store_events = spec.use('storeEventsAsEntities')
|
91
|
+
store_event_names = {}
|
92
|
+
store_events.each do |spec|
|
93
|
+
entity_name = spec['id']
|
94
|
+
event_names = spec['events']
|
95
|
+
#event_name = event_name.gsub(/Event$/, '').gsub(/[A-Z]/) { |m| "_#{m}" }.upcase
|
96
|
+
#event_name = event_name.gsub(/Event$/, '').sub(/./) { |m| m.upcase }
|
97
|
+
event_names.each do |event_name|
|
98
|
+
store_event_names[event_name] = entity_name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
store_event_names.values.uniq.each do |entity_name|
|
102
|
+
entities.set(:event_list, entity_name, [])
|
103
|
+
end
|
104
|
+
subscriber = StoringEventSubscriber.new do |payload|
|
105
|
+
if entity_name = store_event_names[payload['name']]
|
106
|
+
entities.get(:event_list, entity_name) << payload
|
107
|
+
end
|
108
|
+
end
|
109
|
+
opts[:sdam_proc] = lambda do |client|
|
110
|
+
client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
|
111
|
+
client.subscribe(Mongo::Monitoring::CONNECTION_POOL, subscriber)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
if server_api = spec.use('serverApi')
|
116
|
+
server_api = ::Utils.underscore_hash(server_api)
|
117
|
+
opts[:server_api] = server_api
|
118
|
+
end
|
119
|
+
|
120
|
+
create_client(**opts).tap do |client|
|
121
|
+
if oe = spec.use('observeEvents')
|
122
|
+
oe.each do |event|
|
123
|
+
case event
|
124
|
+
when 'commandStartedEvent', 'commandSucceededEvent', 'commandFailedEvent'
|
125
|
+
subscriber = (@subscribers[client] ||= EventSubscriber.new)
|
126
|
+
unless client.send(:monitoring).subscribers[Mongo::Monitoring::COMMAND].include?(subscriber)
|
127
|
+
client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
|
128
|
+
end
|
129
|
+
kind = event.sub('command', '').sub('Event', '').downcase.to_sym
|
130
|
+
subscriber.add_wanted_events(kind)
|
131
|
+
if ignore_events = spec.use('ignoreCommandMonitoringEvents')
|
132
|
+
subscriber.ignore_commands(ignore_events)
|
133
|
+
end
|
134
|
+
else
|
135
|
+
raise NotImplementedError, "Unknown event #{event}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
when 'database'
|
141
|
+
client = entities.get(:client, spec.use!('client'))
|
142
|
+
client.use(spec.use!('databaseName')).database
|
143
|
+
when 'collection'
|
144
|
+
database = entities.get(:database, spec.use!('database'))
|
145
|
+
# TODO verify
|
146
|
+
opts = Utils.snakeize_hash(spec.use('collectionOptions') || {})
|
147
|
+
database[spec.use!('collectionName'), opts]
|
148
|
+
when 'bucket'
|
149
|
+
database = entities.get(:database, spec.use!('database'))
|
150
|
+
database.fs
|
151
|
+
when 'session'
|
152
|
+
client = entities.get(:client, spec.use!('client'))
|
153
|
+
|
154
|
+
if smc_opts = spec.use('sessionOptions')
|
155
|
+
opts = ::Utils.underscore_hash(smc_opts)
|
156
|
+
else
|
157
|
+
opts = {}
|
158
|
+
end
|
159
|
+
|
160
|
+
client.start_session(**opts)
|
161
|
+
else
|
162
|
+
raise NotImplementedError, "Unknown type #{type}"
|
163
|
+
end
|
164
|
+
unless spec.empty?
|
165
|
+
raise NotImplementedError, "Unhandled spec keys: #{spec}"
|
166
|
+
end
|
167
|
+
entities.set(type.to_sym, id, entity)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def set_initial_data
|
172
|
+
@spec['initialData']&.each do |entity_spec|
|
173
|
+
spec = UsingHash[entity_spec]
|
174
|
+
collection = root_authorized_client.use(spec.use!('databaseName'))[spec.use!('collectionName')]
|
175
|
+
collection.drop
|
176
|
+
docs = spec.use!('documents')
|
177
|
+
if docs.any?
|
178
|
+
collection.insert_many(docs)
|
179
|
+
else
|
180
|
+
begin
|
181
|
+
collection.create
|
182
|
+
rescue Mongo::Error => e
|
183
|
+
if Mongo::Error::OperationFailure === e && (
|
184
|
+
e.code == 48 || e.message =~ /collection already exists/
|
185
|
+
)
|
186
|
+
# Already exists
|
187
|
+
else
|
188
|
+
raise
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
unless spec.empty?
|
193
|
+
raise NotImplementedError, "Unhandled spec keys: #{spec}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def run
|
199
|
+
kill_sessions
|
200
|
+
|
201
|
+
test_spec = UsingHash[self.test_spec]
|
202
|
+
ops = test_spec.use!('operations')
|
203
|
+
execute_operations(ops)
|
204
|
+
unless test_spec.empty?
|
205
|
+
raise NotImplementedError, "Unhandled spec keys: #{test_spec}"
|
206
|
+
end
|
207
|
+
ensure
|
208
|
+
disable_fail_points
|
209
|
+
end
|
210
|
+
|
211
|
+
def stop!
|
212
|
+
@stop = true
|
213
|
+
end
|
214
|
+
|
215
|
+
def stop?
|
216
|
+
!!@stop
|
217
|
+
end
|
218
|
+
|
219
|
+
def cleanup
|
220
|
+
if $kill_transactions || true
|
221
|
+
kill_sessions
|
222
|
+
$kill_transactions = nil
|
223
|
+
end
|
224
|
+
|
225
|
+
entities[:client]&.each do |id, client|
|
226
|
+
client.close
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def execute_operations(ops)
|
233
|
+
ops.each do |op|
|
234
|
+
execute_operation(op)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def execute_operation(op)
|
239
|
+
use_all(op, 'operation', op) do |op|
|
240
|
+
name = Utils.underscore(op.use!('name'))
|
241
|
+
method_name = name
|
242
|
+
if name.to_s == 'loop'
|
243
|
+
method_name = "_#{name}"
|
244
|
+
end
|
245
|
+
if expected_error = op.use('expectError')
|
246
|
+
begin
|
247
|
+
send(method_name, op)
|
248
|
+
rescue Mongo::Error, BSON::String::IllegalKey => e
|
249
|
+
if expected_error.use('isClientError')
|
250
|
+
unless BSON::String::IllegalKey === e
|
251
|
+
raise Error::ErrorMismatch, "Expected client error but got #{e}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
if code = expected_error.use('errorCode')
|
255
|
+
unless e.code == code
|
256
|
+
raise Error::ErrorMismatch, "Expected #{code} code but had #{e.code}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
if code_name = expected_error.use('errorCodeName')
|
260
|
+
unless e.code_name == code_name
|
261
|
+
raise Error::ErrorMismatch, "Expected #{code_name} code name but had #{e.code_name}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
if text = expected_error.use('errorContains')
|
265
|
+
unless e.to_s.include?(text)
|
266
|
+
raise Error::ErrorMismatch, "Expected #{text} in the message but had #{e}"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
if labels = expected_error.use('errorLabelsContain')
|
270
|
+
labels.each do |label|
|
271
|
+
unless e.label?(label)
|
272
|
+
raise Error::ErrorMismatch, "Expected error to contain label #{label} but it did not"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
if omit_labels = expected_error.use('errorLabelsOmit')
|
277
|
+
omit_labels.each do |label|
|
278
|
+
if e.label?(label)
|
279
|
+
raise Error::ErrorMismatch, "Expected error to not contain label #{label} but it did"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
if expected_result = expected_error.use('expectResult')
|
284
|
+
assert_result_matches(e.result, expected_result)
|
285
|
+
#expected_result.clear
|
286
|
+
# Important: this must be the last branch.
|
287
|
+
elsif expected_error.use('isError')
|
288
|
+
# Nothing but we consume the key.
|
289
|
+
end
|
290
|
+
unless expected_error.empty?
|
291
|
+
raise NotImplementedError, "Unhandled keys: #{expected_error}"
|
292
|
+
end
|
293
|
+
else
|
294
|
+
raise Error::ErrorMismatch, "Expected exception but none was raised"
|
295
|
+
end
|
296
|
+
else
|
297
|
+
result = send(method_name, op)
|
298
|
+
if expected_result = op.use('expectResult')
|
299
|
+
if result.nil? && !expected_result.empty?
|
300
|
+
raise Error::ResultMismatch, "Actual result nil but expected result #{expected_result}"
|
301
|
+
elsif Array === expected_result
|
302
|
+
assert_documents_match(result, expected_result)
|
303
|
+
else
|
304
|
+
assert_result_matches(result, expected_result)
|
305
|
+
end
|
306
|
+
#expected_result.clear
|
307
|
+
end
|
308
|
+
if save_entity = op.use('saveResultAsEntity')
|
309
|
+
entities.set(:result, save_entity, result)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def use_sub(hash, key, &block)
|
316
|
+
v = hash.use!(key)
|
317
|
+
use_all(hash, key, v, &block)
|
318
|
+
end
|
319
|
+
|
320
|
+
def use_all(hash, key, v)
|
321
|
+
orig_v = v.dup
|
322
|
+
(yield v).tap do
|
323
|
+
unless v.empty?
|
324
|
+
raise NotImplementedError, "Unconsumed items for #{key}: #{v}\nOriginal hash: #{orig_v}"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def use_arguments(op, &block)
|
330
|
+
if op.key?('arguments')
|
331
|
+
use_sub(op, 'arguments', &block)
|
332
|
+
else
|
333
|
+
yield UsingHash.new
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def disable_fail_points
|
338
|
+
if $disable_fail_points
|
339
|
+
$disable_fail_points.each do |(fail_point_command, address)|
|
340
|
+
client = ClusterTools.instance.direct_client(address,
|
341
|
+
database: 'admin')
|
342
|
+
client.command(configureFailPoint: fail_point_command['configureFailPoint'],
|
343
|
+
mode: 'off')
|
344
|
+
end
|
345
|
+
$disable_fail_points = nil
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def kill_sessions
|
350
|
+
if options[:kill_sessions] != false
|
351
|
+
begin
|
352
|
+
root_authorized_client.command(
|
353
|
+
killAllSessions: [],
|
354
|
+
)
|
355
|
+
rescue Mongo::Error::OperationFailure => e
|
356
|
+
if e.code == 11601
|
357
|
+
# operation was interrupted, ignore
|
358
|
+
elsif e.code == 59
|
359
|
+
# no such command (old server), ignore
|
360
|
+
else
|
361
|
+
raise
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def root_authorized_client
|
368
|
+
@root_authorized_client ||= ClientRegistry.instance.global_client('root_authorized')
|
369
|
+
end
|
370
|
+
|
371
|
+
def create_client(**opts)
|
372
|
+
args = case v = options[:client_args]
|
373
|
+
when Array
|
374
|
+
unless v.length == 2
|
375
|
+
raise NotImplementedError, 'Client args array must have two elements'
|
376
|
+
end
|
377
|
+
[v.first, v.last.dup]
|
378
|
+
when String
|
379
|
+
[v, {}]
|
380
|
+
else
|
381
|
+
[
|
382
|
+
SpecConfig.instance.addresses,
|
383
|
+
SpecConfig.instance.all_test_options,
|
384
|
+
]
|
385
|
+
end
|
386
|
+
args.last.update(
|
387
|
+
max_read_retries: 0,
|
388
|
+
max_write_retries: 0,
|
389
|
+
).update(opts)
|
390
|
+
Mongo::Client.new(*args)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Unified
|
2
|
+
|
3
|
+
class TestGroup
|
4
|
+
def initialize(path, **opts)
|
5
|
+
if String === path
|
6
|
+
data = YAML.load(File.read(path))
|
7
|
+
else
|
8
|
+
data = path
|
9
|
+
end
|
10
|
+
@spec = BSON::ExtJSON.parse_obj(data)
|
11
|
+
@options = opts
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
def tests
|
17
|
+
reqs = @spec['runOnRequirements']
|
18
|
+
|
19
|
+
@spec.fetch('tests').map do |test|
|
20
|
+
sub = @spec.dup
|
21
|
+
sub.delete('tests')
|
22
|
+
sub['test'] = test
|
23
|
+
sub['group_runOnRequirements'] = reqs
|
24
|
+
Test.new(sub, **options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Unified
|
2
|
+
|
3
|
+
class UsingHash < Hash
|
4
|
+
def use(key)
|
5
|
+
wrap(self[key]).tap do
|
6
|
+
delete(key)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def use!(key)
|
11
|
+
wrap(fetch(key)).tap do
|
12
|
+
delete(key)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def wrap(v)
|
19
|
+
case v
|
20
|
+
when Hash
|
21
|
+
self.class[v]
|
22
|
+
when Array
|
23
|
+
v.map do |subv|
|
24
|
+
wrap(subv)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'runners/unified/error'
|
2
|
+
require 'runners/unified/entity_map'
|
3
|
+
require 'runners/unified/event_subscriber'
|
4
|
+
require 'runners/unified/test'
|
5
|
+
require 'runners/unified/test_group'
|
6
|
+
require 'runners/unified/using_hash'
|
7
|
+
|
8
|
+
def define_unified_spec_tests(base_path, paths, expect_failure: false)
|
9
|
+
paths.each do |path|
|
10
|
+
basename = path[base_path.length+1...path.length]
|
11
|
+
context basename do
|
12
|
+
group = Unified::TestGroup.new(path)
|
13
|
+
|
14
|
+
if basename =~ /retryable|transaction/
|
15
|
+
require_wired_tiger
|
16
|
+
end
|
17
|
+
|
18
|
+
group.tests.each do |test|
|
19
|
+
context test.description do
|
20
|
+
|
21
|
+
if test.skip?
|
22
|
+
before do
|
23
|
+
skip test.skip_reason
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
before(:all) do
|
28
|
+
if SpecConfig.instance.retry_reads == false
|
29
|
+
skip "Tests are not applicable when legacy read retries are used"
|
30
|
+
end
|
31
|
+
if SpecConfig.instance.retry_writes == false
|
32
|
+
skip "Tests are not applicable when legacy write retries are used"
|
33
|
+
end
|
34
|
+
|
35
|
+
if ClusterConfig.instance.topology == :sharded
|
36
|
+
if test.require_multiple_mongoses? && SpecConfig.instance.addresses.length == 1
|
37
|
+
skip "Test requires multiple mongoses"
|
38
|
+
elsif test.require_single_mongos? && SpecConfig.instance.addresses.length > 1
|
39
|
+
# Many transaction spec tests that do not specifically deal with
|
40
|
+
# sharded transactions fail when run against a multi-mongos cluster
|
41
|
+
skip "Test requires single mongos"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if expect_failure
|
47
|
+
it 'fails as expected' do
|
48
|
+
if test.group_reqs
|
49
|
+
unless test.group_reqs.any? { |r| r.satisfied? }
|
50
|
+
skip "Group requirements not satisfied"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
if test.reqs
|
54
|
+
unless test.reqs.any? { |r| r.satisfied? }
|
55
|
+
skip "Requirements not satisfied"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
begin
|
59
|
+
test.create_entities
|
60
|
+
test.set_initial_data
|
61
|
+
lambda do
|
62
|
+
test.run
|
63
|
+
test.assert_outcome
|
64
|
+
test.assert_events
|
65
|
+
# HACK: other errors are possible and likely will need to
|
66
|
+
# be added here later as the tests evolve.
|
67
|
+
end.should raise_error(Mongo::Error::OperationFailure)
|
68
|
+
ensure
|
69
|
+
test.cleanup
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
it 'passes' do
|
74
|
+
if test.group_reqs
|
75
|
+
unless test.group_reqs.any? { |r| r.satisfied? }
|
76
|
+
skip "Group requirements not satisfied"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if test.reqs
|
80
|
+
unless test.reqs.any? { |r| r.satisfied? }
|
81
|
+
skip "Requirements not satisfied"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
test.create_entities
|
85
|
+
test.set_initial_data
|
86
|
+
test.run
|
87
|
+
test.assert_outcome
|
88
|
+
test.assert_events
|
89
|
+
test.cleanup
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -171,21 +171,5 @@ module Mrss
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
end
|
174
|
-
|
175
|
-
def require_active_support
|
176
|
-
before(:all) do
|
177
|
-
if !SpecConfig.instance.active_support?
|
178
|
-
skip 'This test requires ActiveSupport; set WITH_ACTIVE_SUPPORT=1 in environment'
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def no_active_support
|
184
|
-
before(:all) do
|
185
|
-
if SpecConfig.instance.active_support?
|
186
|
-
skip 'This test requires no ActiveSupport; unset WITH_ACTIVE_SUPPORT in environment'
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
174
|
end
|
191
175
|
end
|
data/spec/shared/shlib/server.sh
CHANGED
@@ -163,7 +163,7 @@ calculate_server_args() {
|
|
163
163
|
args="$args --replicaset --name ruby-driver-rs --nodes 2 --arbiter"
|
164
164
|
export HAVE_ARBITER=1
|
165
165
|
elif test "$TOPOLOGY" = sharded-cluster; then
|
166
|
-
args="$args --replicaset --nodes
|
166
|
+
args="$args --replicaset --nodes 1 --sharded 1 --name ruby-driver-rs"
|
167
167
|
if test -z "$SINGLE_MONGOS"; then
|
168
168
|
args="$args --mongos 2"
|
169
169
|
fi
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'lite_spec_helper'
|
2
2
|
|
3
3
|
require 'mrss/constraints'
|
4
|
+
require 'mrss/cluster_config'
|
5
|
+
|
6
|
+
ClusterConfig = Mrss::ClusterConfig
|
7
|
+
|
4
8
|
require 'support/constraints'
|
5
9
|
require 'support/authorization'
|
6
10
|
require 'support/primary_socket'
|
7
|
-
require 'support/cluster_config'
|
8
11
|
require 'support/cluster_tools'
|
9
12
|
require 'rspec/retry'
|
10
13
|
require 'support/monitoring_ext'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'runners/unified'
|
4
|
+
|
5
|
+
base = "#{CURRENT_PATH}/spec_tests/data/crud_unified"
|
6
|
+
CRUD_UNIFIED_TESTS = Dir.glob("#{base}/**/*.yml").sort
|
7
|
+
|
8
|
+
describe 'CRUD unified spec tests' do
|
9
|
+
define_unified_spec_tests(base, CRUD_UNIFIED_TESTS)
|
10
|
+
end
|