mongoid 7.1.5 → 7.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +1 -1
  5. data/Rakefile +33 -7
  6. data/lib/mongoid/association/referenced/has_one/proxy.rb +6 -1
  7. data/lib/mongoid/attributes.rb +8 -1
  8. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  9. data/lib/mongoid/document.rb +3 -2
  10. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  11. data/lib/mongoid/interceptable.rb +3 -1
  12. data/lib/mongoid/reloadable.rb +5 -0
  13. data/lib/mongoid/validatable/associated.rb +1 -1
  14. data/lib/mongoid/validatable/presence.rb +3 -3
  15. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  16. data/lib/mongoid/version.rb +1 -1
  17. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  18. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  19. data/spec/app/models/customer.rb +11 -0
  20. data/spec/app/models/customer_address.rb +12 -0
  21. data/spec/app/models/dictionary.rb +6 -0
  22. data/spec/app/models/person.rb +2 -0
  23. data/spec/app/models/series.rb +1 -0
  24. data/spec/app/models/wiki_page.rb +1 -0
  25. data/spec/integration/app_spec.rb +178 -88
  26. data/spec/integration/associations/embeds_many_spec.rb +24 -0
  27. data/spec/integration/associations/embeds_one_spec.rb +24 -0
  28. data/spec/integration/associations/has_many_spec.rb +42 -0
  29. data/spec/integration/associations/has_one_spec.rb +42 -0
  30. data/spec/integration/callbacks_models.rb +49 -0
  31. data/spec/integration/callbacks_spec.rb +216 -0
  32. data/spec/integration/document_spec.rb +21 -0
  33. data/spec/lite_spec_helper.rb +6 -6
  34. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  35. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  36. data/spec/mongoid/attributes_spec.rb +241 -0
  37. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  38. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +36 -0
  39. data/spec/mongoid/document_query_spec.rb +51 -0
  40. data/spec/mongoid/errors/mongoid_error_spec.rb +20 -8
  41. data/spec/mongoid/factory_spec.rb +2 -2
  42. data/spec/mongoid/persistable/savable_spec.rb +4 -4
  43. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  44. data/spec/mongoid/persistable_spec.rb +2 -2
  45. data/spec/shared/LICENSE +20 -0
  46. data/spec/shared/bin/get-mongodb-download-url +17 -0
  47. data/spec/shared/bin/s3-copy +45 -0
  48. data/spec/shared/bin/s3-upload +69 -0
  49. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  50. data/spec/shared/lib/mrss/cluster_config.rb +226 -0
  51. data/spec/shared/lib/mrss/constraints.rb +368 -0
  52. data/spec/shared/lib/mrss/docker_runner.rb +271 -0
  53. data/spec/shared/lib/mrss/lite_constraints.rb +191 -0
  54. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  55. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  56. data/spec/shared/lib/mrss/utils.rb +15 -0
  57. data/spec/shared/share/Dockerfile.erb +322 -0
  58. data/spec/shared/share/haproxy-1.conf +16 -0
  59. data/spec/shared/share/haproxy-2.conf +17 -0
  60. data/spec/shared/shlib/distro.sh +73 -0
  61. data/spec/shared/shlib/server.sh +317 -0
  62. data/spec/shared/shlib/set_env.sh +131 -0
  63. data/spec/spec_helper.rb +3 -1
  64. data/spec/support/constraints.rb +0 -226
  65. data/spec/support/spec_config.rb +8 -0
  66. metadata +538 -493
  67. metadata.gz.sig +0 -0
  68. data/spec/support/child_process_helper.rb +0 -76
  69. data/spec/support/lite_constraints.rb +0 -22
@@ -0,0 +1,191 @@
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
+ unless ENV['LIBMONGOCRYPT_PATH']
91
+ skip 'Test requires path to libmongocrypt to be specified in LIBMONGOCRYPT_PATH env variable'
92
+ end
93
+ end
94
+ end
95
+
96
+ def require_no_libmongocrypt
97
+ before(:all) do
98
+ if ENV['LIBMONGOCRYPT_PATH']
99
+ skip 'Test requires libmongocrypt to not be configured'
100
+ end
101
+ end
102
+ end
103
+
104
+ def require_aws_auth
105
+ before(:all) do
106
+ unless (ENV['AUTH'] || '') =~ /^aws/
107
+ skip 'This test requires AUTH=aws* and an appropriately configured runtime environment'
108
+ end
109
+ end
110
+ end
111
+
112
+ def require_ec2_host
113
+ before(:all) do
114
+ if $have_aws.nil?
115
+ $have_aws = begin
116
+ require 'open-uri'
117
+ begin
118
+ Timeout.timeout(3.81) do
119
+ URI.parse('http://169.254.169.254/latest/meta-data/profile').open.read
120
+ end
121
+ true
122
+ # When trying to use the EC2 metadata endpoint on ECS:
123
+ # Errno::EINVAL: Failed to open TCP connection to 169.254.169.254:80 (Invalid argument - connect(2) for "169.254.169.254" port 80)
124
+ rescue Timeout::Error, Errno::ETIMEDOUT, Errno::EINVAL, OpenURI::HTTPError => $aws_error
125
+ false
126
+ end
127
+ end
128
+ end
129
+ unless $have_aws
130
+ skip "EC2 instance metadata is not available - assuming not running on an EC2 instance: #{$aws_error.class}: #{$aws_error}"
131
+ end
132
+ end
133
+ end
134
+
135
+ def require_stress
136
+ before(:all) do
137
+ if !SpecConfig.instance.stress?
138
+ skip 'Set STRESS=1 in environment to run stress tests'
139
+ end
140
+ end
141
+ end
142
+
143
+ def require_fork
144
+ before(:all) do
145
+ if !SpecConfig.instance.fork?
146
+ skip 'Set FORK=1 in environment to run fork tests'
147
+ end
148
+ end
149
+ end
150
+
151
+ def require_ocsp
152
+ before(:all) do
153
+ if !SpecConfig.instance.ocsp?
154
+ skip 'Set OCSP=1 in environment to run OCSP tests'
155
+ end
156
+ end
157
+ end
158
+
159
+ def require_ocsp_verifier
160
+ before(:all) do
161
+ if !SpecConfig.instance.ocsp_verifier?
162
+ skip 'Set OCSP_VERIFIER=1 in environment to run OCSP verifier tests'
163
+ end
164
+ end
165
+ end
166
+
167
+ def require_ocsp_connectivity
168
+ before(:all) do
169
+ if !SpecConfig.instance.ocsp_connectivity?
170
+ skip 'Set OCSP_CONNECTIVITY=pass or OCSP_CONNECTIVITY=fail in environment to run OCSP connectivity tests'
171
+ end
172
+ end
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
+ end
191
+ end
@@ -0,0 +1,115 @@
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 download_url
28
+ @download_url ||= begin
29
+ version, version_ok = detect_version(current_catalog)
30
+ if version.nil?
31
+ version, full_version_ok = detect_version(full_catalog)
32
+ version_ok ||= full_version_ok
33
+ end
34
+ if version.nil?
35
+ if version_ok
36
+ raise MissingDownloadUrl, "No downloads for version #{desired_version}"
37
+ else
38
+ raise UnknownVersion, "No version #{desired_version}"
39
+ end
40
+ end
41
+ dl = version['downloads'].detect do |dl|
42
+ dl['archive']['url'].index("enterprise-#{arch}") &&
43
+ dl['arch'] == 'x86_64'
44
+ end
45
+ unless dl
46
+ raise MissingDownloadUrl, "No download for #{arch} for #{version['version']}"
47
+ end
48
+ url = dl['archive']['url']
49
+ end
50
+ rescue MissingDownloadUrl
51
+ if %w(4.7 4.7.0).include?(desired_version)
52
+ # 4.7.0 has no advertised downloads but it is downloadable and
53
+ # we do need it. Dirty hack below.
54
+ registry = self.class.new('4.4.3', arch)
55
+ registry.download_url.sub('4.4.3', '4.7.0').tap do |url|
56
+ # Sanity check - ensure the URL we hacked up is a valid one
57
+ io = uri_open(url)
58
+ begin
59
+ io.read(1)
60
+ ensure
61
+ io.close
62
+ end
63
+ end
64
+ else
65
+ raise
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def uri_open(*args)
72
+ if RUBY_VERSION < '2.5'
73
+ open(*args)
74
+ else
75
+ URI.open(*args)
76
+ end
77
+ end
78
+
79
+ def detect_version(catalog)
80
+ candidate_versions = catalog['versions'].select do |version|
81
+ version['version'].start_with?(desired_version) &&
82
+ !version['version'].include?('-')
83
+ end
84
+ version_ok = !candidate_versions.empty?
85
+ # Sometimes the download situation is borked and there is a release
86
+ # with no downloads... skip those.
87
+ version = candidate_versions.detect do |version|
88
+ !version['downloads'].empty?
89
+ end
90
+ # Allow RC releases if there isn't a GA release.
91
+ if version.nil?
92
+ candidate_versions = catalog['versions'].select do |version|
93
+ version['version'].start_with?(desired_version)
94
+ end
95
+ version_ok ||= !candidate_versions.empty?
96
+ version = candidate_versions.detect do |version|
97
+ !version['downloads'].empty?
98
+ end
99
+ end
100
+ [version, version_ok]
101
+ end
102
+
103
+ def current_catalog
104
+ @current_catalog ||= begin
105
+ JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
106
+ end
107
+ end
108
+
109
+ def full_catalog
110
+ @full_catalog ||= begin
111
+ JSON.load(uri_open('http://downloads.mongodb.org/full.json').read)
112
+ end
113
+ end
114
+ end
115
+ 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