mongoid 8.0.10 → 8.1.0
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/CHANGELOG.md +3 -3
- data/README.md +3 -3
- data/Rakefile +18 -67
- data/lib/config/locales/en.yml +46 -14
- data/lib/mongoid/association/accessors.rb +3 -7
- data/lib/mongoid/association/builders.rb +1 -1
- data/lib/mongoid/association/eager_loadable.rb +0 -3
- data/lib/mongoid/association/embedded/batchable.rb +2 -2
- data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
- data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
- data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
- data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
- data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
- data/lib/mongoid/association/macros.rb +0 -6
- data/lib/mongoid/association/nested/one.rb +40 -2
- data/lib/mongoid/association/proxy.rb +1 -1
- data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
- data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
- data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
- data/lib/mongoid/association/reflections.rb +2 -2
- data/lib/mongoid/atomic.rb +7 -16
- data/lib/mongoid/attributes/dynamic.rb +1 -1
- data/lib/mongoid/attributes/nested.rb +2 -2
- data/lib/mongoid/attributes/processing.rb +5 -29
- data/lib/mongoid/attributes/projector.rb +1 -1
- data/lib/mongoid/attributes/readonly.rb +1 -1
- data/lib/mongoid/attributes.rb +8 -2
- data/lib/mongoid/changeable.rb +107 -5
- data/lib/mongoid/clients/storage_options.rb +2 -5
- data/lib/mongoid/clients/validators/storage.rb +1 -13
- data/lib/mongoid/collection_configurable.rb +58 -0
- data/lib/mongoid/composable.rb +2 -0
- data/lib/mongoid/config/defaults.rb +60 -0
- data/lib/mongoid/config/options.rb +0 -3
- data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
- data/lib/mongoid/config/validators.rb +1 -0
- data/lib/mongoid/config.rb +88 -27
- data/lib/mongoid/contextual/atomic.rb +1 -1
- data/lib/mongoid/contextual/memory.rb +233 -33
- data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
- data/lib/mongoid/contextual/mongo.rb +370 -133
- data/lib/mongoid/contextual/none.rb +162 -7
- data/lib/mongoid/contextual.rb +12 -0
- data/lib/mongoid/criteria/findable.rb +2 -2
- data/lib/mongoid/criteria/includable.rb +4 -3
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
- data/lib/mongoid/criteria/queryable/key.rb +1 -1
- data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
- data/lib/mongoid/criteria/queryable/optional.rb +8 -8
- data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
- data/lib/mongoid/criteria/queryable/selector.rb +1 -1
- data/lib/mongoid/criteria/queryable/storable.rb +1 -1
- data/lib/mongoid/criteria.rb +6 -5
- data/lib/mongoid/deprecable.rb +1 -2
- data/lib/mongoid/deprecation.rb +3 -3
- data/lib/mongoid/document.rb +1 -8
- data/lib/mongoid/errors/create_collection_failure.rb +33 -0
- data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
- data/lib/mongoid/errors/immutable_attribute.rb +26 -0
- data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
- data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
- data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
- data/lib/mongoid/errors.rb +4 -1
- data/lib/mongoid/extensions/hash.rb +2 -24
- data/lib/mongoid/extensions/object.rb +2 -2
- data/lib/mongoid/extensions/time.rb +2 -0
- data/lib/mongoid/fields/localized.rb +10 -0
- data/lib/mongoid/fields/standard.rb +10 -0
- data/lib/mongoid/fields.rb +59 -35
- data/lib/mongoid/findable.rb +27 -3
- data/lib/mongoid/interceptable.rb +6 -116
- data/lib/mongoid/matcher/eq_impl.rb +1 -1
- data/lib/mongoid/matcher/type.rb +1 -1
- data/lib/mongoid/persistable/creatable.rb +1 -0
- data/lib/mongoid/persistable/deletable.rb +1 -1
- data/lib/mongoid/persistable/savable.rb +13 -1
- data/lib/mongoid/persistable/unsettable.rb +2 -2
- data/lib/mongoid/persistable/updatable.rb +51 -1
- data/lib/mongoid/persistable/upsertable.rb +20 -1
- data/lib/mongoid/persistable.rb +3 -0
- data/lib/mongoid/query_cache.rb +5 -1
- data/lib/mongoid/railties/database.rake +7 -2
- data/lib/mongoid/reloadable.rb +5 -3
- data/lib/mongoid/stateful.rb +22 -1
- data/lib/mongoid/tasks/database.rake +12 -0
- data/lib/mongoid/tasks/database.rb +20 -0
- data/lib/mongoid/timestamps/created.rb +1 -8
- data/lib/mongoid/traversable.rb +0 -12
- data/lib/mongoid/utils.rb +22 -0
- data/lib/mongoid/validatable/associated.rb +17 -98
- data/lib/mongoid/validatable/macros.rb +5 -5
- data/lib/mongoid/validatable.rb +4 -9
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/warnings.rb +17 -1
- data/lib/mongoid.rb +16 -3
- data/spec/integration/app_spec.rb +2 -6
- data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
- data/spec/integration/callbacks_spec.rb +99 -12
- data/spec/integration/discriminator_key_spec.rb +4 -5
- data/spec/integration/i18n_fallbacks_spec.rb +3 -2
- data/spec/mongoid/association/eager_spec.rb +2 -24
- data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
- data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
- data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
- data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -55
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
- data/spec/mongoid/association/syncable_spec.rb +1 -1
- data/spec/mongoid/association_spec.rb +0 -60
- data/spec/mongoid/attributes_spec.rb +3 -33
- data/spec/mongoid/changeable_spec.rb +299 -24
- data/spec/mongoid/clients_spec.rb +122 -13
- data/spec/mongoid/collection_configurable_spec.rb +158 -0
- data/spec/mongoid/config/defaults_spec.rb +160 -0
- data/spec/mongoid/config_spec.rb +154 -27
- data/spec/mongoid/contextual/memory_spec.rb +332 -76
- data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
- data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
- data/spec/mongoid/contextual/none_spec.rb +49 -2
- data/spec/mongoid/copyable_spec.rb +2 -10
- data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
- data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
- data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
- data/spec/mongoid/criteria_projection_spec.rb +1 -4
- data/spec/mongoid/criteria_spec.rb +5 -9
- data/spec/mongoid/document_spec.rb +0 -27
- data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
- data/spec/mongoid/extensions/hash_spec.rb +3 -3
- data/spec/mongoid/extensions/time_spec.rb +8 -43
- data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
- data/spec/mongoid/fields/localized_spec.rb +46 -28
- data/spec/mongoid/fields_spec.rb +136 -77
- data/spec/mongoid/findable_spec.rb +391 -34
- data/spec/mongoid/indexable_spec.rb +16 -10
- data/spec/mongoid/interceptable_spec.rb +153 -442
- data/spec/mongoid/interceptable_spec_models.rb +111 -51
- data/spec/mongoid/persistable/deletable_spec.rb +26 -6
- data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
- data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
- data/spec/mongoid/persistable/logical_spec.rb +37 -0
- data/spec/mongoid/persistable/poppable_spec.rb +36 -0
- data/spec/mongoid/persistable/pullable_spec.rb +72 -0
- data/spec/mongoid/persistable/pushable_spec.rb +72 -0
- data/spec/mongoid/persistable/renamable_spec.rb +36 -0
- data/spec/mongoid/persistable/savable_spec.rb +96 -0
- data/spec/mongoid/persistable/settable_spec.rb +37 -0
- data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
- data/spec/mongoid/persistable/updatable_spec.rb +20 -28
- data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
- data/spec/mongoid/persistence_context_spec.rb +7 -57
- data/spec/mongoid/query_cache_spec.rb +56 -61
- data/spec/mongoid/reloadable_spec.rb +24 -28
- data/spec/mongoid/scopable_spec.rb +70 -0
- data/spec/mongoid/serializable_spec.rb +23 -44
- data/spec/mongoid/stateful_spec.rb +122 -8
- data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
- data/spec/mongoid/tasks/database_spec.rb +127 -0
- data/spec/mongoid/timestamps/created_spec.rb +0 -23
- data/spec/mongoid/timestamps_spec.rb +9 -11
- data/spec/mongoid/touchable_spec.rb +277 -5
- data/spec/mongoid/touchable_spec_models.rb +3 -1
- data/spec/mongoid/traversable_spec.rb +9 -24
- data/spec/mongoid/validatable/associated_spec.rb +34 -27
- data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
- data/spec/mongoid_spec.rb +36 -10
- data/spec/shared/LICENSE +20 -0
- data/spec/shared/bin/get-mongodb-download-url +17 -0
- data/spec/shared/bin/s3-copy +45 -0
- data/spec/shared/bin/s3-upload +69 -0
- data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
- data/spec/shared/lib/mrss/cluster_config.rb +231 -0
- data/spec/shared/lib/mrss/constraints.rb +378 -0
- data/spec/shared/lib/mrss/docker_runner.rb +298 -0
- data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
- data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
- data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
- data/spec/shared/lib/mrss/session_registry.rb +69 -0
- data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
- data/spec/shared/lib/mrss/utils.rb +37 -0
- data/spec/shared/share/Dockerfile.erb +321 -0
- data/spec/shared/share/haproxy-1.conf +16 -0
- data/spec/shared/share/haproxy-2.conf +17 -0
- data/spec/shared/shlib/config.sh +27 -0
- data/spec/shared/shlib/distro.sh +74 -0
- data/spec/shared/shlib/server.sh +416 -0
- data/spec/shared/shlib/set_env.sh +169 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/immutable_ids.rb +118 -0
- data/spec/support/macros.rb +47 -15
- data/spec/support/models/artist.rb +0 -1
- data/spec/support/models/band.rb +1 -0
- data/spec/support/models/building.rb +2 -0
- data/spec/support/models/name.rb +0 -10
- data/spec/support/models/person.rb +0 -1
- data/spec/support/models/product.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +745 -637
- metadata.gz.sig +2 -0
- data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
- data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
- data/spec/support/models/purse.rb +0 -9
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
module Mrss
|
5
|
+
module LiteConstraints
|
6
|
+
|
7
|
+
# Constrain tests that use TimeoutInterrupt to MRI (and Unix).
|
8
|
+
def require_mri
|
9
|
+
before(:all) do
|
10
|
+
unless SpecConfig.instance.mri?
|
11
|
+
skip "MRI required, we have #{SpecConfig.instance.platform}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def require_jruby
|
17
|
+
before(:all) do
|
18
|
+
unless BSON::Environment.jruby?
|
19
|
+
skip "JRuby required, we have #{SpecConfig.instance.platform}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is for marking tests that fail on JRuby that should
|
25
|
+
# in principle work (as opposed to being fundamentally incompatible
|
26
|
+
# with JRuby).
|
27
|
+
# Often times these failures happen only in Evergreen.
|
28
|
+
def fails_on_jruby(version=nil)
|
29
|
+
before(:all) do
|
30
|
+
if BSON::Environment.jruby?
|
31
|
+
if version
|
32
|
+
min_parts = version.split('.').map(&:to_i)
|
33
|
+
actual_parts = JRUBY_VERSION.split('.').map(&:to_i)[0...min_parts.length]
|
34
|
+
actual = actual_parts.join('.')
|
35
|
+
if actual <= version
|
36
|
+
skip "Fails on jruby through #{version}"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
skip "Fails on jruby"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Indicates that the respective test uses the internet in some capacity,
|
46
|
+
# for example the test resolves SRV DNS records.
|
47
|
+
def require_external_connectivity
|
48
|
+
before(:all) do
|
49
|
+
if ENV['EXTERNAL_DISABLED']
|
50
|
+
skip "Test requires external connectivity"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def require_mongo_kerberos
|
56
|
+
before(:all) do
|
57
|
+
# TODO Use a more generic environment variable name if/when
|
58
|
+
# Mongoid tests get Kerberos configurations.
|
59
|
+
unless %w(1 yes true).include?(ENV['MONGO_RUBY_DRIVER_KERBEROS']&.downcase)
|
60
|
+
skip 'Set MONGO_RUBY_DRIVER_KERBEROS=1 in environment to run Kerberos unit tests'
|
61
|
+
end
|
62
|
+
require 'mongo_kerberos'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def require_linting
|
67
|
+
before(:all) do
|
68
|
+
unless Mongo::Lint.enabled?
|
69
|
+
skip "Linting is not enabled"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Some tests will fail if linting is enabled:
|
75
|
+
# 1. Tests that pass invalid options to client, etc. which the linter
|
76
|
+
# rejects.
|
77
|
+
# 2. Tests that set expectations on topologies, server descriptions, etc.
|
78
|
+
# (since setting expectations requires mutating said objects, and when
|
79
|
+
# linting is on those objects are frozen).
|
80
|
+
def require_no_linting
|
81
|
+
before(:all) do
|
82
|
+
if Mongo::Lint.enabled?
|
83
|
+
skip "Linting is enabled"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def require_libmongocrypt
|
89
|
+
before(:all) do
|
90
|
+
# If FLE is set in environment, the entire test run is supposed to
|
91
|
+
# include FLE therefore run the FLE tests.
|
92
|
+
if (ENV['LIBMONGOCRYPT_PATH'] || '').empty? && (ENV['FLE'] || '').empty?
|
93
|
+
skip 'Test requires path to libmongocrypt to be specified in LIBMONGOCRYPT_PATH env variable'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def min_libmongocrypt_version(version)
|
99
|
+
require_libmongocrypt
|
100
|
+
before(:all) do
|
101
|
+
actual_version = Utils.parse_version(Mongo::Crypt::Binding.mongocrypt_version(nil))
|
102
|
+
min_version = Utils.parse_version(version)
|
103
|
+
unless actual_version >= min_version
|
104
|
+
skip "libmongocrypt version #{min_version} required, but version #{actual_version} is available"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def require_no_libmongocrypt
|
110
|
+
before(:all) do
|
111
|
+
if ENV['LIBMONGOCRYPT_PATH']
|
112
|
+
skip 'Test requires libmongocrypt to not be configured'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def require_aws_auth
|
118
|
+
before(:all) do
|
119
|
+
unless (ENV['AUTH'] || '') =~ /^aws/
|
120
|
+
skip 'This test requires AUTH=aws* and an appropriately configured runtime environment'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def require_ec2_host
|
126
|
+
before(:all) do
|
127
|
+
if $have_aws.nil?
|
128
|
+
$have_aws = begin
|
129
|
+
require 'open-uri'
|
130
|
+
begin
|
131
|
+
Timeout.timeout(3.81) do
|
132
|
+
URI.parse('http://169.254.169.254/latest/meta-data/profile').open.read
|
133
|
+
end
|
134
|
+
true
|
135
|
+
# When trying to use the EC2 metadata endpoint on ECS:
|
136
|
+
# Errno::EINVAL: Failed to open TCP connection to 169.254.169.254:80 (Invalid argument - connect(2) for "169.254.169.254" port 80)
|
137
|
+
rescue Timeout::Error, Errno::ETIMEDOUT, Errno::EINVAL, OpenURI::HTTPError => $aws_error
|
138
|
+
false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
unless $have_aws
|
143
|
+
skip "EC2 instance metadata is not available - assuming not running on an EC2 instance: #{$aws_error.class}: #{$aws_error}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def require_stress
|
149
|
+
before(:all) do
|
150
|
+
if !SpecConfig.instance.stress?
|
151
|
+
skip 'Set STRESS=1 in environment to run stress tests'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def require_fork
|
157
|
+
before(:all) do
|
158
|
+
if !SpecConfig.instance.fork?
|
159
|
+
skip 'Set FORK=1 in environment to run fork tests'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def require_ocsp
|
165
|
+
before(:all) do
|
166
|
+
if !SpecConfig.instance.ocsp?
|
167
|
+
skip 'Set OCSP=1 in environment to run OCSP tests'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def require_ocsp_verifier
|
173
|
+
before(:all) do
|
174
|
+
if !SpecConfig.instance.ocsp_verifier?
|
175
|
+
skip 'Set OCSP_VERIFIER=1 in environment to run OCSP verifier tests'
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def require_ocsp_connectivity
|
181
|
+
before(:all) do
|
182
|
+
if !SpecConfig.instance.ocsp_connectivity?
|
183
|
+
skip 'Set OCSP_CONNECTIVITY=pass or OCSP_CONNECTIVITY=fail in environment to run OCSP connectivity tests'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def require_active_support
|
189
|
+
before(:all) do
|
190
|
+
if !SpecConfig.instance.active_support?
|
191
|
+
skip 'This test requires ActiveSupport; set WITH_ACTIVE_SUPPORT=1 in environment'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def no_active_support
|
197
|
+
before(:all) do
|
198
|
+
if SpecConfig.instance.active_support?
|
199
|
+
skip 'This test requires no ActiveSupport; unset WITH_ACTIVE_SUPPORT in environment'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def require_fallbacks
|
205
|
+
before(:all) do
|
206
|
+
unless %w(yes true 1).include?((ENV['TEST_I18N_FALLBACKS'] || '').downcase)
|
207
|
+
skip 'Set TEST_I18N_FALLBACKS=1 environment variable to run these tests'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def require_no_fallbacks
|
213
|
+
before(:all) do
|
214
|
+
if %w(yes true 1).include?((ENV['TEST_I18N_FALLBACKS'] || '').downcase)
|
215
|
+
skip 'Set TEST_I18N_FALLBACKS=0 environment variable to run these tests'
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# This is a macro for retrying flaky tests on CI that occasionally fail.
|
221
|
+
# Note that the tests will only be retried on CI.
|
222
|
+
#
|
223
|
+
# @param [ Integer ] :tries The number of times to retry.
|
224
|
+
# @param [ Integer ] :sleep The number of seconds to sleep in between retries.
|
225
|
+
# If nothing, or nil, is passed, we won't wait in between retries.
|
226
|
+
def retry_test(tries: 3, sleep: nil)
|
227
|
+
if %w(1 yes true).include?(ENV['CI'])
|
228
|
+
around do |example|
|
229
|
+
if sleep
|
230
|
+
example.run_with_retry retry: tries, retry_wait: sleep
|
231
|
+
else
|
232
|
+
example.run_with_retry retry: tries
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
autoload :JSON, 'json'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module Mrss
|
8
|
+
class ServerVersionRegistry
|
9
|
+
class Error < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class UnknownVersion < Error
|
13
|
+
end
|
14
|
+
|
15
|
+
class MissingDownloadUrl < Error
|
16
|
+
end
|
17
|
+
|
18
|
+
class BrokenDownloadUrl < Error
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(desired_version, arch)
|
22
|
+
@desired_version, @arch = desired_version, arch
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :desired_version, :arch
|
26
|
+
|
27
|
+
def target_arch
|
28
|
+
# can't use RbConfig::CONFIG["arch"] because JRuby doesn't
|
29
|
+
# return anything meaningful there.
|
30
|
+
#
|
31
|
+
# also, need to use `uname -a` instead of (e.g.) `uname -p`
|
32
|
+
# because debian (at least) does not return anything meaningful
|
33
|
+
# for `uname -p`.
|
34
|
+
uname = `uname -a`.strip
|
35
|
+
@target_arch ||= case uname
|
36
|
+
when /aarch/ then "aarch64"
|
37
|
+
when /x86/ then "x86_64"
|
38
|
+
else raise "unsupported architecture #{uname.inspect}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def download_url
|
43
|
+
@download_url ||= begin
|
44
|
+
version, version_ok = detect_version(current_catalog)
|
45
|
+
if version.nil?
|
46
|
+
version, full_version_ok = detect_version(full_catalog)
|
47
|
+
version_ok ||= full_version_ok
|
48
|
+
end
|
49
|
+
if version.nil?
|
50
|
+
if version_ok
|
51
|
+
raise MissingDownloadUrl, "No downloads for version #{desired_version}"
|
52
|
+
else
|
53
|
+
raise UnknownVersion, "No version #{desired_version}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
dl = version['downloads'].detect do |dl|
|
57
|
+
dl['archive']['url'].index("enterprise-#{arch}") &&
|
58
|
+
dl['arch'] == target_arch
|
59
|
+
end
|
60
|
+
unless dl
|
61
|
+
raise MissingDownloadUrl, "No download for #{arch} for #{version['version']}"
|
62
|
+
end
|
63
|
+
url = dl['archive']['url']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def uri_open(*args)
|
70
|
+
if RUBY_VERSION < '2.5'
|
71
|
+
open(*args)
|
72
|
+
else
|
73
|
+
URI.open(*args)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def detect_version(catalog)
|
78
|
+
candidate_versions = catalog['versions'].select do |version|
|
79
|
+
version['version'].start_with?(desired_version) &&
|
80
|
+
!version['version'].include?('-')
|
81
|
+
end
|
82
|
+
version_ok = !candidate_versions.empty?
|
83
|
+
# Sometimes the download situation is borked and there is a release
|
84
|
+
# with no downloads... skip those.
|
85
|
+
version = candidate_versions.detect do |version|
|
86
|
+
!version['downloads'].empty?
|
87
|
+
end
|
88
|
+
# Allow RC releases if there isn't a GA release.
|
89
|
+
if version.nil?
|
90
|
+
candidate_versions = catalog['versions'].select do |version|
|
91
|
+
version['version'].start_with?(desired_version)
|
92
|
+
end
|
93
|
+
version_ok ||= !candidate_versions.empty?
|
94
|
+
version = candidate_versions.detect do |version|
|
95
|
+
!version['downloads'].empty?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
[version, version_ok]
|
99
|
+
end
|
100
|
+
|
101
|
+
def current_catalog
|
102
|
+
@current_catalog ||= begin
|
103
|
+
JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def full_catalog
|
108
|
+
@full_catalog ||= begin
|
109
|
+
JSON.load(uri_open('http://downloads.mongodb.org/full.json').read)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Mrss
|
7
|
+
|
8
|
+
def self.patch_mongo_for_session_registry
|
9
|
+
|
10
|
+
Mongo::Client.class_eval do
|
11
|
+
alias :get_session_without_tracking :get_session
|
12
|
+
|
13
|
+
def get_session(options = {})
|
14
|
+
get_session_without_tracking(options).tap do |session|
|
15
|
+
SessionRegistry.instance.register(session) if session&.materialized?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Mongo::Session.class_eval do
|
21
|
+
alias :end_session_without_tracking :end_session
|
22
|
+
|
23
|
+
def end_session
|
24
|
+
SessionRegistry.instance.unregister(self)
|
25
|
+
end_session_without_tracking
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :materialize_if_needed_without_tracking :materialize_if_needed
|
29
|
+
|
30
|
+
def materialize_if_needed
|
31
|
+
materialize_if_needed_without_tracking.tap do
|
32
|
+
SessionRegistry.instance.register(self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Mrss
|
40
|
+
class SessionRegistry
|
41
|
+
include Singleton
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@registry = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def register(session)
|
48
|
+
@registry[session.session_id] = session if session
|
49
|
+
end
|
50
|
+
|
51
|
+
def unregister(session)
|
52
|
+
return if session.ended? || !session.materialized?
|
53
|
+
@registry.delete(session.session_id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def verify_sessions_ended!
|
57
|
+
@registry.delete_if { |_, session| session.ended? }
|
58
|
+
|
59
|
+
unless @registry.empty?
|
60
|
+
sessions = @registry.map { |_, session| session }
|
61
|
+
raise "Session registry contains live sessions: #{sessions.join(', ')}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def clear_registry
|
66
|
+
@registry = {}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Mrss
|
7
|
+
|
8
|
+
def self.patch_mongo_for_session_registry
|
9
|
+
|
10
|
+
Mongo::Client.class_eval do
|
11
|
+
alias :get_session_without_tracking :get_session
|
12
|
+
|
13
|
+
def get_session(options = {})
|
14
|
+
get_session_without_tracking(options).tap do |session|
|
15
|
+
SessionRegistry.instance.register(session)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Mongo::Session.class_eval do
|
21
|
+
alias :end_session_without_tracking :end_session
|
22
|
+
|
23
|
+
def end_session
|
24
|
+
SessionRegistry.instance.unregister(self)
|
25
|
+
end_session_without_tracking
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Mrss
|
32
|
+
class SessionRegistry
|
33
|
+
include Singleton
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@registry = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def register(session)
|
40
|
+
@registry[session.session_id] = session if session
|
41
|
+
end
|
42
|
+
|
43
|
+
def unregister(session)
|
44
|
+
@registry.delete(session.session_id) unless session.ended?
|
45
|
+
end
|
46
|
+
|
47
|
+
def verify_sessions_ended!
|
48
|
+
@registry.delete_if { |_, session| session.ended? }
|
49
|
+
|
50
|
+
unless @registry.empty?
|
51
|
+
sessions = @registry.map { |_, session| session }
|
52
|
+
raise "Session registry contains live sessions: #{sessions.join(', ')}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear_registry
|
57
|
+
@registry = {}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
autoload :JSON, 'json'
|
5
|
+
autoload :FileUtils, 'fileutils'
|
6
|
+
autoload :Find, 'find'
|
7
|
+
|
8
|
+
module Mrss
|
9
|
+
|
10
|
+
autoload :ChildProcessHelper, 'mrss/child_process_helper'
|
11
|
+
|
12
|
+
# Organizes and runs all of the tests in the test suite in batches.
|
13
|
+
#
|
14
|
+
# Organizing the tests in batches serves two purposes:
|
15
|
+
#
|
16
|
+
# 1. This allows running unit tests before integration tests, therefore
|
17
|
+
# in theory revealing failures quicker on average.
|
18
|
+
# 2. This allows running some tests that have high intermittent failure rate
|
19
|
+
# in their own test process.
|
20
|
+
#
|
21
|
+
# This class aggregates RSpec results after the test runs.
|
22
|
+
class SpecOrganizer
|
23
|
+
|
24
|
+
class BucketsNotPrioritized < StandardError
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(root: nil, classifiers:, priority_order:,
|
28
|
+
spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil,
|
29
|
+
randomize: false
|
30
|
+
)
|
31
|
+
@spec_root = spec_root || File.join(root, 'spec')
|
32
|
+
@classifiers = classifiers
|
33
|
+
@priority_order = priority_order
|
34
|
+
@rspec_json_path = rspec_json_path || File.join(root, 'tmp/rspec.json')
|
35
|
+
@rspec_all_json_path = rspec_all_json_path || File.join(root, 'tmp/rspec-all.json')
|
36
|
+
@randomize = !!randomize
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :spec_root, :classifiers, :priority_order
|
40
|
+
attr_reader :rspec_json_path, :rspec_all_json_path
|
41
|
+
|
42
|
+
def randomize?
|
43
|
+
@randomize
|
44
|
+
end
|
45
|
+
|
46
|
+
def seed
|
47
|
+
@seed ||= (rand * 100_000).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def buckets
|
51
|
+
@buckets ||= {}.tap do |buckets|
|
52
|
+
Find.find(spec_root) do |path|
|
53
|
+
next unless File.file?(path)
|
54
|
+
next unless path =~ /_spec\.rb\z/
|
55
|
+
rel_path = path[(spec_root.length + 1)..path.length]
|
56
|
+
|
57
|
+
found = false
|
58
|
+
classifiers.each do |(regexp, category)|
|
59
|
+
if regexp =~ rel_path
|
60
|
+
buckets[category] ||= []
|
61
|
+
buckets[category] << File.join('spec', rel_path)
|
62
|
+
found = true
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
unless found
|
68
|
+
buckets[nil] ||= []
|
69
|
+
buckets[nil] << File.join('spec', rel_path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end.freeze
|
73
|
+
end
|
74
|
+
|
75
|
+
def ordered_buckets
|
76
|
+
@ordered_buckets ||= {}.tap do |ordered_buckets|
|
77
|
+
buckets = self.buckets.dup
|
78
|
+
priority_order.each do |category|
|
79
|
+
files = buckets.delete(category)
|
80
|
+
ordered_buckets[category] = files
|
81
|
+
end
|
82
|
+
|
83
|
+
if files = buckets.delete(nil)
|
84
|
+
ordered_buckets[nil] = files
|
85
|
+
end
|
86
|
+
|
87
|
+
unless buckets.empty?
|
88
|
+
raise BucketsNotPrioritized, "Some buckets were not prioritized: #{buckets.keys.map(&:to_s).join(', ')}"
|
89
|
+
end
|
90
|
+
end.freeze
|
91
|
+
end
|
92
|
+
|
93
|
+
def run
|
94
|
+
run_buckets(*buckets.keys)
|
95
|
+
end
|
96
|
+
|
97
|
+
def run_buckets(*buckets)
|
98
|
+
FileUtils.rm_f(rspec_all_json_path)
|
99
|
+
|
100
|
+
buckets.each do |bucket|
|
101
|
+
if bucket && !self.buckets[bucket]
|
102
|
+
raise "Unknown bucket #{bucket}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
buckets = Hash[self.buckets.select { |k, v| buckets.include?(k) }]
|
106
|
+
|
107
|
+
failed = []
|
108
|
+
|
109
|
+
priority_order.each do |category|
|
110
|
+
if files = buckets.delete(category)
|
111
|
+
unless run_files(category, files)
|
112
|
+
failed << category
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
if files = buckets.delete(nil)
|
117
|
+
unless run_files('remaining', files)
|
118
|
+
failed << 'remaining'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
unless buckets.empty?
|
123
|
+
raise "Some buckets were not executed: #{buckets.keys.map(&:to_s).join(', ')}"
|
124
|
+
end
|
125
|
+
|
126
|
+
if failed.any?
|
127
|
+
raise "The following buckets failed: #{failed.map(&:to_s).join(', ')}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def run_files(category, paths)
|
132
|
+
puts "Running #{category.to_s.gsub('_', ' ')} tests"
|
133
|
+
FileUtils.rm_f(rspec_json_path)
|
134
|
+
cmd = %w(rspec) + paths
|
135
|
+
if randomize?
|
136
|
+
cmd += %W(--order rand:#{seed})
|
137
|
+
end
|
138
|
+
|
139
|
+
begin
|
140
|
+
puts "Running #{cmd.join(' ')}"
|
141
|
+
ChildProcessHelper.check_call(cmd)
|
142
|
+
ensure
|
143
|
+
if File.exist?(rspec_json_path)
|
144
|
+
if File.exist?(rspec_all_json_path)
|
145
|
+
merge_rspec_results
|
146
|
+
else
|
147
|
+
FileUtils.cp(rspec_json_path, rspec_all_json_path)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
true
|
153
|
+
rescue ChildProcessHelper::SpawnError
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
def merge_rspec_results
|
158
|
+
all = JSON.parse(File.read(rspec_all_json_path))
|
159
|
+
new = JSON.parse(File.read(rspec_json_path))
|
160
|
+
all['examples'] += new.delete('examples')
|
161
|
+
new.delete('summary').each do |k, v|
|
162
|
+
all['summary'][k] += v
|
163
|
+
end
|
164
|
+
new.delete('version')
|
165
|
+
new.delete('summary_line')
|
166
|
+
# The spec organizer runs all buckets with the same seed, hence
|
167
|
+
# we can drop the seed from new results.
|
168
|
+
new.delete('seed')
|
169
|
+
unless new.empty?
|
170
|
+
raise "Unhandled rspec results keys: #{new.keys.join(', ')}"
|
171
|
+
end
|
172
|
+
# We do not merge summary lines, delete them from aggregated results
|
173
|
+
all.delete('summary_line')
|
174
|
+
File.open(rspec_all_json_path, 'w') do |f|
|
175
|
+
f << JSON.dump(all)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
module Mrss
|
5
|
+
module Utils
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def print_backtrace(dest=STDERR)
|
9
|
+
raise
|
10
|
+
rescue => e
|
11
|
+
dest.puts e.backtrace.join("\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Parses the given version string, accounting for suffix information that
|
15
|
+
# Gem::Version cannot successfully parse.
|
16
|
+
#
|
17
|
+
# @param [ String ] version the version to parse
|
18
|
+
#
|
19
|
+
# @return [ Gem::Version ] the parsed version
|
20
|
+
#
|
21
|
+
# @raise [ ArgumentError ] if the string cannot be parsed.
|
22
|
+
def parse_version(version)
|
23
|
+
Gem::Version.new(version)
|
24
|
+
rescue ArgumentError
|
25
|
+
match = version.match(/\A(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?(-[A-Za-z\+\d]+)?\z/)
|
26
|
+
raise ArgumentError.new("Malformed version number string #{version}") if match.nil?
|
27
|
+
|
28
|
+
Gem::Version.new(
|
29
|
+
[
|
30
|
+
match[:major],
|
31
|
+
match[:minor],
|
32
|
+
match[:patch]
|
33
|
+
].join('.')
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|