mongo 2.14.0 → 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.tar.gz.sig +0 -0
- data/README.md +4 -1
- data/Rakefile +8 -15
- data/lib/mongo.rb +23 -0
- 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/background_thread.rb +11 -0
- data/lib/mongo/bulk_write.rb +21 -18
- data/lib/mongo/client.rb +82 -6
- data/lib/mongo/cluster.rb +19 -28
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
- data/lib/mongo/cluster/sdam_flow.rb +14 -0
- data/lib/mongo/collection.rb +8 -6
- 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 +1 -1
- 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/cursor.rb +2 -2
- data/lib/mongo/database.rb +22 -5
- data/lib/mongo/database/view.rb +1 -1
- data/lib/mongo/error.rb +9 -1
- 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/grid/fs_bucket.rb +37 -37
- data/lib/mongo/index/view.rb +21 -11
- data/lib/mongo/monitoring.rb +13 -4
- 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/operation.rb +2 -2
- data/lib/mongo/operation/collections_info.rb +18 -1
- data/lib/mongo/operation/collections_info/command.rb +2 -2
- 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/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/response_handling.rb +23 -23
- data/lib/mongo/operation/shared/sessions_supported.rb +13 -2
- data/lib/mongo/operation/shared/write.rb +8 -18
- data/lib/mongo/protocol/compressed.rb +51 -5
- data/lib/mongo/protocol/message.rb +20 -2
- data/lib/mongo/protocol/msg.rb +36 -11
- data/lib/mongo/query_cache.rb +30 -0
- data/lib/mongo/retryable.rb +1 -1
- data/lib/mongo/server.rb +7 -15
- 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 -4
- data/lib/mongo/server/description.rb +4 -0
- data/lib/mongo/server/description/features.rb +9 -8
- data/lib/mongo/server/monitor.rb +20 -1
- data/lib/mongo/server/monitor/app_metadata.rb +1 -1
- data/lib/mongo/server/monitor/connection.rb +9 -10
- data/lib/mongo/server/pending_connection.rb +24 -6
- data/lib/mongo/server/push_monitor.rb +11 -1
- data/lib/mongo/session.rb +2 -2
- data/lib/mongo/session/session_pool.rb +4 -2
- data/lib/mongo/socket.rb +29 -4
- data/lib/mongo/socket/ssl.rb +8 -0
- data/lib/mongo/srv/monitor.rb +0 -11
- data/lib/mongo/uri/options_mapper.rb +38 -0
- data/lib/mongo/utils.rb +15 -0
- data/lib/mongo/version.rb +1 -1
- 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/reconnect_spec.rb +1 -1
- data/spec/integration/sdam_error_handling_spec.rb +1 -1
- data/spec/integration/sdam_events_spec.rb +3 -5
- 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 +3 -18
- 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 +8 -2
- 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/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_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_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 +13 -2
- data/spec/runners/transactions/test.rb +1 -0
- data/spec/runners/unified.rb +96 -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/shared/bin/get-mongodb-download-url +17 -0
- data/spec/shared/lib/mrss/cluster_config.rb +218 -0
- data/spec/shared/lib/mrss/constraints.rb +43 -0
- data/spec/shared/lib/mrss/docker_runner.rb +262 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +112 -0
- data/spec/shared/lib/mrss/utils.rb +15 -0
- data/spec/shared/share/Dockerfile.erb +231 -0
- data/spec/shared/shlib/distro.sh +73 -0
- data/spec/shared/shlib/server.sh +290 -0
- data/spec/shared/shlib/set_env.sh +128 -0
- data/spec/solo/clean_exit_spec.rb +21 -0
- 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/common_shortcuts.rb +15 -1
- data/spec/support/shared/session.rb +2 -2
- data/spec/support/spec_config.rb +46 -3
- data/spec/support/spec_setup.rb +48 -38
- data/spec/support/utils.rb +64 -3
- metadata +1104 -992
- 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/support/cluster_config.rb +0 -207
@@ -17,6 +17,12 @@ describe Mongo::Server::Monitor do
|
|
17
17
|
{}
|
18
18
|
end
|
19
19
|
|
20
|
+
let(:monitor_app_metadata) do
|
21
|
+
Mongo::Server::Monitor::AppMetadata.new(
|
22
|
+
server_api: SpecConfig.instance.ruby_options[:server_api],
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
20
26
|
let(:cluster) do
|
21
27
|
double('cluster').tap do |cluster|
|
22
28
|
allow(cluster).to receive(:run_sdam_flow)
|
@@ -32,7 +38,9 @@ describe Mongo::Server::Monitor do
|
|
32
38
|
let(:monitor) do
|
33
39
|
register_background_thread_object(
|
34
40
|
described_class.new(server, listeners, Mongo::Monitoring.new,
|
35
|
-
SpecConfig.instance.test_options.merge(cluster: cluster).merge(monitor_options)
|
41
|
+
SpecConfig.instance.test_options.merge(cluster: cluster).merge(monitor_options).update(
|
42
|
+
app_metadata: monitor_app_metadata,
|
43
|
+
push_monitor_app_metadata: monitor_app_metadata))
|
36
44
|
)
|
37
45
|
end
|
38
46
|
|
data/spec/mongo/server_spec.rb
CHANGED
@@ -40,12 +40,20 @@ describe Mongo::Server do
|
|
40
40
|
)
|
41
41
|
end
|
42
42
|
|
43
|
+
let(:monitor_app_metadata) do
|
44
|
+
Mongo::Server::Monitor::AppMetadata.new(
|
45
|
+
server_api: SpecConfig.instance.ruby_options[:server_api],
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
43
49
|
shared_context 'with monitoring io' do
|
44
50
|
let(:server_options) do
|
45
51
|
{monitoring_io: true}
|
46
52
|
end
|
47
53
|
|
48
54
|
before do
|
55
|
+
allow(cluster).to receive(:monitor_app_metadata).and_return(monitor_app_metadata)
|
56
|
+
allow(cluster).to receive(:push_monitor_app_metadata).and_return(monitor_app_metadata)
|
49
57
|
allow(cluster).to receive(:heartbeat_interval).and_return(1000)
|
50
58
|
end
|
51
59
|
end
|
@@ -144,8 +152,13 @@ describe Mongo::Server do
|
|
144
152
|
expect(server.options[:monitoring_io]).to be true
|
145
153
|
end
|
146
154
|
|
147
|
-
|
148
|
-
|
155
|
+
context 'with monitoring app metadata option' do
|
156
|
+
require_no_required_api_version
|
157
|
+
|
158
|
+
it 'creates monitor with monitoring app metadata' do
|
159
|
+
server.monitor.scan!
|
160
|
+
expect(server.monitor.connection.options[:app_metadata]).to be monitor_app_metadata
|
161
|
+
end
|
149
162
|
end
|
150
163
|
|
151
164
|
context 'monitoring_io: false' do
|
@@ -50,6 +50,46 @@ describe Mongo::Socket::SSL, retry: 3 do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
describe '#connect!' do
|
53
|
+
context 'when TLS context hooks are provided' do
|
54
|
+
# https://github.com/jruby/jruby-openssl/issues/221
|
55
|
+
fails_on_jruby
|
56
|
+
|
57
|
+
let(:proc) do
|
58
|
+
Proc.new do |context|
|
59
|
+
if BSON::Environment.jruby?
|
60
|
+
context.ciphers = ["AES256-SHA256"]
|
61
|
+
else
|
62
|
+
context.ciphers = ["AES256-SHA"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
before do
|
68
|
+
Mongo.tls_context_hooks = [ proc ]
|
69
|
+
end
|
70
|
+
|
71
|
+
after do
|
72
|
+
Mongo.tls_context_hooks.clear
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'runs the TLS context hook before connecting' do
|
76
|
+
if ENV['OCSP_ALGORITHM']
|
77
|
+
skip "OCSP configurations use different certificates which this test does not handle"
|
78
|
+
end
|
79
|
+
|
80
|
+
expect(proc).to receive(:call).and_call_original
|
81
|
+
socket
|
82
|
+
# Even though we are requesting a single cipher in the hook,
|
83
|
+
# there may be multiple ciphers available in the context.
|
84
|
+
# All of the ciphers should match the requested one (using
|
85
|
+
# OpenSSL's idea of what "match" means).
|
86
|
+
socket.context.ciphers.each do |cipher|
|
87
|
+
unless cipher.first =~ /SHA256/ || cipher.last == 256
|
88
|
+
raise "Unexpected cipher #{cipher} after requesting SHA-256"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
53
93
|
|
54
94
|
context 'when a certificate is provided' do
|
55
95
|
|
data/spec/mongo/socket_spec.rb
CHANGED
@@ -48,7 +48,7 @@ describe Mongo::Socket do
|
|
48
48
|
socket.send(:map_exceptions) do
|
49
49
|
raise OpenSSL::SSL::SSLError.new('Test error')
|
50
50
|
end
|
51
|
-
end.to raise_error(Mongo::Error::SocketError, 'OpenSSL::SSL::SSLError: Test error (for fake-address)
|
51
|
+
end.to raise_error(Mongo::Error::SocketError, 'OpenSSL::SSL::SSLError: Test error (for fake-address)')
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -86,7 +86,7 @@ describe Mongo::Socket do
|
|
86
86
|
|
87
87
|
expect do
|
88
88
|
socket.read(10)
|
89
|
-
end.to raise_error(Mongo::Error::SocketTimeoutError, /Took more than .* seconds to receive data
|
89
|
+
end.to raise_error(Mongo::Error::SocketTimeoutError, /Took more than .* seconds to receive data.*\(for /)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongo do
|
4
|
+
before do
|
5
|
+
Mongo.tls_context_hooks.clear
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#tls_context_hooks' do
|
9
|
+
it 'returns an array' do
|
10
|
+
expect(Mongo.tls_context_hooks).to eq([])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#tls_context_hooks=' do
|
15
|
+
context 'when argument is not an array' do
|
16
|
+
it 'raises an ArgumentError' do
|
17
|
+
expect do
|
18
|
+
Mongo.tls_context_hooks = "Hello"
|
19
|
+
end.to raise_error(ArgumentError, /TLS context hooks must be an array of Procs/)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when argument is an array not containing procs' do
|
24
|
+
it 'raises an ArgumentError' do
|
25
|
+
expect do
|
26
|
+
Mongo.tls_context_hooks = [1, 2, 3]
|
27
|
+
end.to raise_error(ArgumentError, /TLS context hooks must be an array of Procs/)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'saves the provided hooks' do
|
32
|
+
Mongo.tls_context_hooks = [ Proc.new { |x| x ** 2 } ]
|
33
|
+
expect(Mongo.tls_context_hooks.length).to eq(1)
|
34
|
+
expect(Mongo.tls_context_hooks.first).to be_a(Proc)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -235,10 +235,6 @@ module Mongo
|
|
235
235
|
if k.downcase == 'authmechanismproperties'
|
236
236
|
expected[k] = ::Utils.downcase_keys(v)
|
237
237
|
end
|
238
|
-
# Ruby driver does not support snappy.
|
239
|
-
if k == 'compressors'
|
240
|
-
expected[k] = v.reject { |sub_v| sub_v == 'snappy' }
|
241
|
-
end
|
242
238
|
end
|
243
239
|
# We omit retryReads/retryWrites=true because some tests do not
|
244
240
|
# provide those.
|
@@ -1,18 +1,39 @@
|
|
1
1
|
module Mongo
|
2
2
|
module CRUD
|
3
3
|
class Requirement
|
4
|
-
YAML_KEYS = %w(minServerVersion maxServerVersion topology).freeze
|
4
|
+
YAML_KEYS = %w(minServerVersion maxServerVersion topology topologies serverParameters).freeze
|
5
5
|
|
6
6
|
def initialize(spec)
|
7
|
+
spec = spec.dup
|
8
|
+
# Legacy tests have the requirements mixed with other test fields
|
9
|
+
spec.delete('data')
|
10
|
+
spec.delete('tests')
|
11
|
+
|
12
|
+
unless (unhandled_keys = spec.keys - YAML_KEYS).empty?
|
13
|
+
raise "Unhandled requirement specification keys: #{unhandled_keys}"
|
14
|
+
end
|
15
|
+
|
7
16
|
@min_server_version = spec['minServerVersion']
|
8
17
|
@max_server_version = spec['maxServerVersion']
|
9
|
-
|
18
|
+
# topologies is for unified test format.
|
19
|
+
# topology is for legacy tests.
|
20
|
+
@topologies = if topologies = spec['topology'] || spec['topologies']
|
10
21
|
topologies.map do |topology|
|
11
|
-
{
|
22
|
+
{
|
23
|
+
'replicaset' => :replica_set,
|
24
|
+
'single' => :single,
|
25
|
+
'sharded' => :sharded,
|
26
|
+
'sharded-replicaset' => :sharded,
|
27
|
+
}[topology].tap do |v|
|
28
|
+
unless v
|
29
|
+
raise "Unknown topology #{topology}"
|
30
|
+
end
|
31
|
+
end
|
12
32
|
end
|
13
33
|
else
|
14
34
|
nil
|
15
35
|
end
|
36
|
+
@server_parameters = spec['serverParameters']
|
16
37
|
end
|
17
38
|
|
18
39
|
attr_reader :min_server_version
|
@@ -47,6 +68,22 @@ module Mongo
|
|
47
68
|
if topologies
|
48
69
|
ok &&= topologies.include?(cc.topology)
|
49
70
|
end
|
71
|
+
if @server_parameters
|
72
|
+
@server_parameters.each do |k, required_v|
|
73
|
+
actual_v = cc.server_parameters[k]
|
74
|
+
if actual_v.nil? && !required_v.nil?
|
75
|
+
ok = false
|
76
|
+
elsif actual_v != required_v
|
77
|
+
if Numeric === actual_v && Numeric === required_v
|
78
|
+
if actual_v.to_f != required_v.to_f
|
79
|
+
ok = false
|
80
|
+
end
|
81
|
+
else
|
82
|
+
ok = false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
50
87
|
ok
|
51
88
|
end
|
52
89
|
|
@@ -193,6 +193,14 @@ EOT
|
|
193
193
|
return
|
194
194
|
end
|
195
195
|
|
196
|
+
if k == 'updateDescription'
|
197
|
+
# Change stream result - verify subset, not exact match
|
198
|
+
expected.fetch(k).each do |sub_k, sub_v|
|
199
|
+
{sub_k => sub_v}.should == {sub_k => actual.fetch(k).fetch(sub_k)}
|
200
|
+
end
|
201
|
+
return
|
202
|
+
end
|
203
|
+
|
196
204
|
if expected[k].is_a?(Time)
|
197
205
|
expect(k => actual[k].utc.to_s).to eq(k => expected[k].utc.to_s)
|
198
206
|
else
|
@@ -191,8 +191,19 @@ module Mongo
|
|
191
191
|
|
192
192
|
def assert_event_count(client, context)
|
193
193
|
events = _select_events(context)
|
194
|
-
|
195
|
-
|
194
|
+
if %w(ServerMarkedUnknownEvent PoolClearedEvent).include?(arguments['event'])
|
195
|
+
# We publish SDAM events from both regular and push monitors.
|
196
|
+
# This means sometimes there are two ServerMarkedUnknownEvent
|
197
|
+
# events published for the same server transition.
|
198
|
+
# Allow actual event count to be at least the expected event count
|
199
|
+
# in case there are multiple transitions in a single test.
|
200
|
+
unless events.length >= arguments['count']
|
201
|
+
raise "Expected #{arguments['count']} #{arguments['event']} events, but have #{events.length}"
|
202
|
+
end
|
203
|
+
else
|
204
|
+
unless events.length == arguments['count']
|
205
|
+
raise "Expected #{arguments['count']} #{arguments['event']} events, but have #{events.length}"
|
206
|
+
end
|
196
207
|
end
|
197
208
|
end
|
198
209
|
|
@@ -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
|
@@ -0,0 +1,249 @@
|
|
1
|
+
module Unified
|
2
|
+
|
3
|
+
module Assertions
|
4
|
+
|
5
|
+
def assert_result_matches(actual, expected)
|
6
|
+
if Hash === expected
|
7
|
+
use_all(expected, 'expected result', expected) do |expected|
|
8
|
+
%w(deleted inserted matched modified upserted).each do |k|
|
9
|
+
if count = expected.use("#{k}Count")
|
10
|
+
if Hash === count || count > 0
|
11
|
+
actual_count = case actual
|
12
|
+
when Mongo::BulkWrite::Result, Mongo::Operation::Delete::Result
|
13
|
+
actual.send("#{k}_count")
|
14
|
+
else
|
15
|
+
actual["n_#{k}"]
|
16
|
+
end
|
17
|
+
assert_value_matches(actual_count, count, "#{k} count")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
%w(inserted upserted).each do |k|
|
22
|
+
expected_v = expected.use("#{k}Ids")
|
23
|
+
next unless expected_v
|
24
|
+
actual_v = case actual
|
25
|
+
when Mongo::BulkWrite::Result, Mongo::Operation::Update::Result
|
26
|
+
# Ruby driver returns inserted ids as an array of ids.
|
27
|
+
# The yaml file specifies them as a map from operation.
|
28
|
+
if Hash === expected_v && expected_v.keys == %w($$unsetOrMatches)
|
29
|
+
expected_v = expected_v.values.first.values
|
30
|
+
elsif Hash === expected_v
|
31
|
+
expected_v = expected_v.values
|
32
|
+
end
|
33
|
+
actual.send("#{k}_ids")
|
34
|
+
else
|
35
|
+
actual["#{k}_ids"]
|
36
|
+
end
|
37
|
+
if expected_v
|
38
|
+
if expected_v.empty?
|
39
|
+
if actual_v && !actual_v.empty?
|
40
|
+
raise Error::ResultMismatch, "Actual not empty"
|
41
|
+
end
|
42
|
+
else
|
43
|
+
if actual_v != expected_v
|
44
|
+
raise Error::ResultMismatch, "Mismatch: actual #{actual_v}, expected #{expected_v}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_matches(actual, expected, 'result')
|
51
|
+
expected.clear
|
52
|
+
end
|
53
|
+
else
|
54
|
+
assert_matches(actual, expected, 'result')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def assert_outcome
|
59
|
+
return unless outcome
|
60
|
+
|
61
|
+
client = ClientRegistry.instance.global_client('authorized')
|
62
|
+
outcome.each do |spec|
|
63
|
+
spec = UsingHash[spec]
|
64
|
+
collection = client.use(spec.use!('databaseName'))[spec.use!('collectionName')]
|
65
|
+
expected_docs = spec.use!('documents')
|
66
|
+
actual_docs = collection.find({}, order: :_id).to_a
|
67
|
+
assert_documents_match(actual_docs, expected_docs)
|
68
|
+
unless spec.empty?
|
69
|
+
raise NotImplementedError, "Unhandled keys: #{spec}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def assert_documents_match(actual, expected)
|
75
|
+
unless actual.length == expected.length
|
76
|
+
raise Error::ResultMismatch, "Unexpected number of documents: expected #{expected.length}, actual #{actual.length}"
|
77
|
+
end
|
78
|
+
|
79
|
+
actual.each_with_index do |document, index|
|
80
|
+
assert_matches(document, expected[index], "document ##{index}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def assert_document_matches(actual, expected, msg)
|
85
|
+
unless actual == expected
|
86
|
+
p actual
|
87
|
+
p expected
|
88
|
+
raise Error::ResultMismatch, "#{msg} does not match"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def assert_events
|
93
|
+
return unless @expected_events
|
94
|
+
@expected_events.each do |spec|
|
95
|
+
spec = UsingHash[spec]
|
96
|
+
client_id = spec.use!('client')
|
97
|
+
client = entities.get(:client, client_id)
|
98
|
+
subscriber = @subscribers.fetch(client)
|
99
|
+
expected_events = spec.use!('events')
|
100
|
+
actual_events = subscriber.wanted_events
|
101
|
+
unless actual_events.length == expected_events.length
|
102
|
+
raise Error::ResultMismatch, "Event count mismatch: expected #{expected_events.length}, actual #{actual_events.length}\nExpected: #{expected_events}\nActual: #{actual_events}"
|
103
|
+
end
|
104
|
+
expected_events.each_with_index do |event, i|
|
105
|
+
assert_event_matches(actual_events[i], event)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def assert_event_matches(actual, expected)
|
111
|
+
assert_eq(expected.keys.length, 1, "Expected event must have one key: #{expected}")
|
112
|
+
expected_name, spec = expected.first
|
113
|
+
spec = UsingHash[spec]
|
114
|
+
expected_name = expected_name.sub(/Event$/, '').sub(/^(.)/) { $1.upcase }
|
115
|
+
assert_eq(actual.class.name.sub(/.*::/, ''), expected_name, 'Event name does not match')
|
116
|
+
if db_name = spec.use('databaseName')
|
117
|
+
assert_eq(actual.database_name, db_name, 'Database names differ')
|
118
|
+
end
|
119
|
+
if command_name = spec.use('commandName')
|
120
|
+
assert_eq(actual.command_name, command_name, 'Command names differ')
|
121
|
+
end
|
122
|
+
if command = spec.use('command')
|
123
|
+
assert_matches(actual.command, command, 'Commands differ')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def assert_eq(actual, expected, msg)
|
128
|
+
unless expected == actual
|
129
|
+
raise Error::ResultMismatch, "#{msg}: expected #{expected}, actual #{actual}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def assert_matches(actual, expected, msg)
|
134
|
+
if actual.nil? && !expected.nil?
|
135
|
+
raise Error::ResultMismatch, "#{msg}: expected #{expected} but got nil"
|
136
|
+
end
|
137
|
+
|
138
|
+
case expected
|
139
|
+
when Array
|
140
|
+
unless Array === actual
|
141
|
+
raise Error::ResultMismatch, "Expected an array, found #{actual}"
|
142
|
+
end
|
143
|
+
unless actual.length == expected.length
|
144
|
+
raise Error::ResultMismatch, "Expected array of length #{expected.length}, found array of length #{actual.length}: #{actual}"
|
145
|
+
end
|
146
|
+
expected.each_with_index do |v, i|
|
147
|
+
assert_matches(actual[i], v, "#{msg}: index #{i}")
|
148
|
+
end
|
149
|
+
when Hash
|
150
|
+
if expected.keys == %w($$unsetOrMatches) && expected.values.first.keys == %w(insertedId)
|
151
|
+
actual_v = actual.inserted_id
|
152
|
+
expected_v = expected.values.first.values.first
|
153
|
+
assert_value_matches(actual_v, expected_v, 'inserted_id')
|
154
|
+
else
|
155
|
+
expected.each do |k, expected_v|
|
156
|
+
if k.start_with?('$$')
|
157
|
+
assert_value_matches(actual, expected, k)
|
158
|
+
else
|
159
|
+
actual_v = actual[k]
|
160
|
+
if Hash === expected_v && expected_v.length == 1 && expected_v.keys.first.start_with?('$$')
|
161
|
+
assert_value_matches(actual_v, expected_v, k)
|
162
|
+
else
|
163
|
+
assert_matches(actual_v, expected_v, "#{msg}: key #{k}")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
else
|
169
|
+
if Integer === expected && BSON::Int64 === actual
|
170
|
+
actual = actual.value
|
171
|
+
end
|
172
|
+
unless actual == expected
|
173
|
+
raise Error::ResultMismatch, "#{msg}: expected #{expected}, actual #{actual}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def assert_type(object, type)
|
179
|
+
ok = case type
|
180
|
+
when 'object'
|
181
|
+
Hash === object
|
182
|
+
when %w(int long)
|
183
|
+
Integer === object || BSON::Int32 === object || BSON::Int64 === object
|
184
|
+
when 'objectId'
|
185
|
+
BSON::ObjectId === object
|
186
|
+
when 'date'
|
187
|
+
Time === object
|
188
|
+
else
|
189
|
+
raise NotImplementedError, "Unhandled type #{type}"
|
190
|
+
end
|
191
|
+
unless ok
|
192
|
+
raise Error::ResultMismatch, "Object #{object} is not of type #{type}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def assert_value_matches(actual, expected, msg)
|
197
|
+
if Hash === expected && expected.keys.length == 1 &&
|
198
|
+
(operator = expected.keys.first).start_with?('$$')
|
199
|
+
then
|
200
|
+
expected_v = expected.values.first
|
201
|
+
case operator
|
202
|
+
when '$$unsetOrMatches'
|
203
|
+
if actual
|
204
|
+
unless actual == expected_v
|
205
|
+
raise Error::ResultMismatch, "Mismatch for #{msg}: expected #{expected}, have #{actual}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
when '$$matchesHexBytes'
|
209
|
+
expected_data = decode_hex_bytes(expected_v)
|
210
|
+
unless actual == expected_data
|
211
|
+
raise Error::ResultMismatch, "Hex bytes do not match"
|
212
|
+
end
|
213
|
+
when '$$exists'
|
214
|
+
case expected_v
|
215
|
+
when true
|
216
|
+
if actual.nil?
|
217
|
+
raise Error::ResultMismatch, "#{msg}: wanted value to exist, but it did not"
|
218
|
+
end
|
219
|
+
when false
|
220
|
+
if actual
|
221
|
+
raise Error::ResultMismatch, "#{msg}: wanted value to not exist, but it did"
|
222
|
+
end
|
223
|
+
else
|
224
|
+
raise NotImplementedError, "Bogus value #{expected_v}"
|
225
|
+
end
|
226
|
+
when '$$sessionLsid'
|
227
|
+
expected_session = entities.get(:session, expected_v)
|
228
|
+
# TODO - sessions do not expose server sessions after being ended
|
229
|
+
#unless actual_v == {'id' => expected_session.server_session.session_id.to_bson}
|
230
|
+
# raise Error::ResultMismatch, "Session does not match: wanted #{expected_session}, have #{actual_v}"
|
231
|
+
#end
|
232
|
+
when '$$type'
|
233
|
+
assert_type(actual, expected_v)
|
234
|
+
when '$$matchesEntity'
|
235
|
+
result = entities.get(:result, expected_v)
|
236
|
+
unless actual == result
|
237
|
+
raise Error::ResultMismatch, "Actual value #{actual} does not match entity #{expected_v} with value #{result}"
|
238
|
+
end
|
239
|
+
else
|
240
|
+
raise NotImplementedError, "Unknown operator #{operator}"
|
241
|
+
end
|
242
|
+
else
|
243
|
+
if actual != expected
|
244
|
+
raise Error::ResultMismatch, "Mismatch for #{msg}: expected #{expected}, have #{actual}"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|