mongoid 7.0.6 → 7.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) 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 +14 -5
  6. data/lib/mongoid.rb +1 -1
  7. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  8. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  9. data/lib/mongoid/association/proxy.rb +1 -1
  10. data/lib/mongoid/atomic.rb +13 -3
  11. data/lib/mongoid/clients/sessions.rb +20 -4
  12. data/lib/mongoid/criteria.rb +7 -1
  13. data/lib/mongoid/criteria/modifiable.rb +2 -1
  14. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  15. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  16. data/lib/mongoid/criteria/queryable/extensions/time.rb +1 -1
  17. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  18. data/lib/mongoid/document.rb +3 -2
  19. data/lib/mongoid/extensions/hash.rb +4 -2
  20. data/lib/mongoid/extensions/regexp.rb +1 -1
  21. data/lib/mongoid/fields.rb +2 -1
  22. data/lib/mongoid/interceptable.rb +3 -1
  23. data/lib/mongoid/matchable/regexp.rb +2 -2
  24. data/lib/mongoid/persistable/pushable.rb +11 -2
  25. data/lib/mongoid/persistence_context.rb +6 -6
  26. data/lib/mongoid/query_cache.rb +61 -18
  27. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  28. data/lib/mongoid/version.rb +1 -1
  29. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  30. data/spec/app/models/customer.rb +11 -0
  31. data/spec/app/models/customer_address.rb +12 -0
  32. data/spec/app/models/delegating_patient.rb +16 -0
  33. data/spec/integration/app_spec.rb +192 -0
  34. data/spec/integration/associations/embedded_spec.rb +62 -0
  35. data/spec/integration/callbacks_models.rb +49 -0
  36. data/spec/integration/callbacks_spec.rb +216 -0
  37. data/spec/integration/criteria/date_field_spec.rb +41 -0
  38. data/spec/integration/document_spec.rb +22 -0
  39. data/spec/lite_spec_helper.rb +12 -4
  40. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  41. data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
  42. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  43. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  44. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  45. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +105 -0
  46. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  47. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  48. data/spec/mongoid/clients/options_spec.rb +4 -4
  49. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  50. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  51. data/spec/mongoid/clients_spec.rb +2 -2
  52. data/spec/mongoid/contextual/atomic_spec.rb +20 -10
  53. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  54. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  55. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  56. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  57. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  58. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  59. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  60. data/spec/mongoid/criteria_spec.rb +4 -2
  61. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  62. data/spec/mongoid/indexable_spec.rb +6 -4
  63. data/spec/mongoid/matchable/default_spec.rb +1 -1
  64. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  65. data/spec/mongoid/matchable_spec.rb +2 -2
  66. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  67. data/spec/mongoid/query_cache_spec.rb +77 -9
  68. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  69. data/spec/mongoid/scopable_spec.rb +2 -1
  70. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  71. data/spec/mongoid/tasks/database_spec.rb +1 -1
  72. data/spec/shared/LICENSE +20 -0
  73. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  74. data/spec/shared/lib/mrss/cluster_config.rb +211 -0
  75. data/spec/shared/lib/mrss/constraints.rb +330 -0
  76. data/spec/shared/lib/mrss/docker_runner.rb +262 -0
  77. data/spec/shared/lib/mrss/lite_constraints.rb +175 -0
  78. data/spec/shared/lib/mrss/server_version_registry.rb +69 -0
  79. data/spec/shared/lib/mrss/spec_organizer.rb +149 -0
  80. data/spec/shared/share/Dockerfile.erb +229 -0
  81. data/spec/shared/shlib/distro.sh +73 -0
  82. data/spec/shared/shlib/server.sh +270 -0
  83. data/spec/shared/shlib/set_env.sh +128 -0
  84. data/spec/spec_helper.rb +0 -31
  85. data/spec/support/child_process_helper.rb +76 -0
  86. data/spec/support/cluster_config.rb +3 -3
  87. data/spec/support/constraints.rb +201 -30
  88. data/spec/support/session_registry.rb +50 -0
  89. data/spec/support/spec_config.rb +12 -4
  90. metadata +510 -461
  91. metadata.gz.sig +2 -2
data/spec/spec_helper.rb CHANGED
@@ -75,37 +75,6 @@ CONFIG = {
75
75
  }
76
76
  }
77
77
 
78
- def non_legacy_server?
79
- Mongoid::Clients.default.cluster.servers.first.features.write_command_enabled?
80
- end
81
-
82
- def testing_replica_set?
83
- Mongoid::Clients.default.cluster.replica_set?
84
- end
85
-
86
- def collation_supported?
87
- Mongoid::Clients.default.cluster.next_primary.features.collation_enabled?
88
- end
89
- alias :decimal128_supported? :collation_supported?
90
-
91
- def array_filters_supported?
92
- Mongoid::Clients.default.cluster.next_primary.features.array_filters_enabled?
93
- end
94
- alias :sessions_supported? :array_filters_supported?
95
-
96
- def transactions_supported?
97
- features = Mongoid::Clients.default.cluster.next_primary.features
98
- features.respond_to?(:transactions_enabled?) && features.transactions_enabled?
99
- end
100
-
101
- def testing_transactions?
102
- transactions_supported? && testing_replica_set?
103
- end
104
-
105
- def testing_locally?
106
- !(ENV['CI'] == 'travis')
107
- end
108
-
109
78
  # Set the database that the spec suite connects to.
110
79
  Mongoid.configure do |config|
111
80
  config.load_configuration(CONFIG)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :ChildProcess, 'childprocess'
5
+ autoload :Tempfile, 'tempfile'
6
+
7
+ module ChildProcessHelper
8
+ module_function def call(cmd, env: nil, cwd: nil)
9
+ process = ChildProcess.new(*cmd)
10
+ process.io.inherit!
11
+ if cwd
12
+ process.cwd = cwd
13
+ end
14
+ if env
15
+ env.each do |k, v|
16
+ process.environment[k.to_s] = v
17
+ end
18
+ end
19
+ process.start
20
+ process.wait
21
+ process
22
+ end
23
+
24
+ module_function def check_call(cmd, env: nil, cwd: nil)
25
+ process = call(cmd, env: env, cwd: cwd)
26
+ unless process.exit_code == 0
27
+ raise "Failed to execute: #{cmd}"
28
+ end
29
+ end
30
+
31
+ module_function def get_output(cmd, env: nil, cwd: nil)
32
+ process = ChildProcess.new(*cmd)
33
+ process.io.inherit!
34
+ if cwd
35
+ process.cwd = cwd
36
+ end
37
+ if env
38
+ env.each do |k, v|
39
+ process.environment[k.to_s] = v
40
+ end
41
+ end
42
+
43
+ output = ''
44
+ r, w = IO.pipe
45
+
46
+ begin
47
+ process.io.stdout = w
48
+ process.start
49
+ w.close
50
+
51
+ thread = Thread.new do
52
+ begin
53
+ loop do
54
+ output << r.readpartial(16384)
55
+ end
56
+ rescue EOFError
57
+ end
58
+ end
59
+
60
+ process.wait
61
+ thread.join
62
+ ensure
63
+ r.close
64
+ end
65
+
66
+ [process, output]
67
+ end
68
+
69
+ module_function def check_output(*args)
70
+ process, output = get_output(*args)
71
+ unless process.exit_code == 0
72
+ raise "Failed to execute: #{args}"
73
+ end
74
+ output
75
+ end
76
+ end
@@ -99,7 +99,7 @@ class ClusterConfig
99
99
  if topology == :sharded
100
100
  shards = client.use(:admin).command(listShards: 1).first
101
101
  shard = shards['shards'].first
102
- address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
102
+ address_str = shard['host'].sub(/\A.*\//, '').sub(/,.*/, '')
103
103
  client = ClusterTools.instance.direct_client(address_str,
104
104
  SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
105
105
  end
@@ -133,8 +133,8 @@ class ClusterConfig
133
133
 
134
134
  @topology ||= begin
135
135
  topology = client.cluster.topology.class.name.sub(/.*::/, '')
136
- topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
137
- if topology =~ /^replica_set/
136
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/\A_/, '')
137
+ if topology =~ /\Areplica_set/
138
138
  topology = 'replica_set'
139
139
  end
140
140
  topology.to_sym
@@ -1,12 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  module Constraints
2
5
  RAILS_VERSION = ActiveSupport.version.to_s.split('.')[0..1].join('.').freeze
3
6
 
7
+ def require_driver_query_cache
8
+ before(:all) do
9
+ if !defined?(Mongo::QueryCache)
10
+ skip "Driver version #{Mongo::VERSION} does not support query cache"
11
+ end
12
+ end
13
+ end
14
+
15
+ def require_mongoid_query_cache
16
+ before (:all) do
17
+ if defined?(Mongo::QueryCache)
18
+ skip "Mongoid uses the driver query cache in driver versions that support it"
19
+ end
20
+ end
21
+ end
22
+
4
23
  def min_rails_version(version)
5
- unless version =~ /^\d+\.\d+$/
24
+ unless version =~ /\A\d+\.\d+\z/
6
25
  raise ArgumentError, "Version can only be major.minor: #{version}"
7
26
  end
8
27
 
9
- before do
28
+ before(:all) do
10
29
  if version > RAILS_VERSION
11
30
  skip "Rails version #{version} or higher required, we have #{RAILS_VERSION}"
12
31
  end
@@ -14,11 +33,11 @@ module Constraints
14
33
  end
15
34
 
16
35
  def max_rails_version(version)
17
- unless version =~ /^\d+\.\d+$/
36
+ unless version =~ /\A\d+\.\d+\z/
18
37
  raise ArgumentError, "Version can only be major.minor: #{version}"
19
38
  end
20
39
 
21
- before do
40
+ before(:all) do
22
41
  if version < RAILS_VERSION
23
42
  skip "Rails version #{version} or lower required, we have #{RAILS_VERSION}"
24
43
  end
@@ -26,11 +45,11 @@ module Constraints
26
45
  end
27
46
 
28
47
  def min_server_version(version)
29
- unless version =~ /^\d+\.\d+$/
48
+ unless version =~ /\A\d+\.\d+\z/
30
49
  raise ArgumentError, "Version can only be major.minor: #{version}"
31
50
  end
32
51
 
33
- before do
52
+ before(:all) do
34
53
  if version > ClusterConfig.instance.server_version
35
54
  skip "Server version #{version} or higher required, we have #{ClusterConfig.instance.server_version}"
36
55
  end
@@ -38,11 +57,11 @@ module Constraints
38
57
  end
39
58
 
40
59
  def max_server_version(version)
41
- unless version =~ /^\d+\.\d+$/
60
+ unless version =~ /\A\d+\.\d+\z/
42
61
  raise ArgumentError, "Version can only be major.minor: #{version}"
43
62
  end
44
63
 
45
- before do
64
+ before(:all) do
46
65
  if version < ClusterConfig.instance.short_server_version
47
66
  skip "Server version #{version} or lower required, we have #{ClusterConfig.instance.server_version}"
48
67
  end
@@ -50,18 +69,14 @@ module Constraints
50
69
  end
51
70
 
52
71
  def require_topology(*topologies)
53
- topologies = topologies.map { |t| t.to_s }
54
- invalid_topologies = topologies - %w(single replica_set sharded)
72
+ invalid_topologies = topologies - [:single, :replica_set, :sharded]
73
+
55
74
  unless invalid_topologies.empty?
56
75
  raise ArgumentError, "Invalid topologies requested: #{invalid_topologies.join(', ')}"
57
76
  end
58
- before do
59
- topology = Mongoid.default_client.cluster.topology.class.name.sub(/.*::/, '')
60
- topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
61
- if topology =~ /^replica_set/
62
- topology = 'replica_set'
63
- end
64
- unless topologies.include?(topology)
77
+
78
+ before(:all) do
79
+ unless topologies.include?(topology = ClusterConfig.instance.topology)
65
80
  skip "Topology #{topologies.join(' or ')} required, we have #{topology}"
66
81
  end
67
82
  end
@@ -69,32 +84,188 @@ module Constraints
69
84
 
70
85
  def max_example_run_time(timeout)
71
86
  around do |example|
72
- TimeoutInterrupt.timeout(timeout) do
87
+ TimeoutInterrupt.timeout(timeout, TimeoutInterrupt::Error.new("Test execution terminated after #{timeout} seconds")) do
73
88
  example.run
74
89
  end
75
90
  end
76
91
  end
77
92
 
78
93
  def require_transaction_support
79
- min_server_version '4.0'
80
- require_topology :replica_set
94
+ before(:all) do
95
+ case ClusterConfig.instance.topology
96
+ when :single
97
+ skip 'Transactions tests require a replica set (4.0+) or a sharded cluster (4.2+)'
98
+ when :replica_set
99
+ unless ClusterConfig.instance.server_version >= '4.0'
100
+ skip 'Transactions tests in a replica set topology require server 4.0+'
101
+ end
102
+ when :sharded
103
+ unless ClusterConfig.instance.server_version >= '4.2'
104
+ skip 'Transactions tests in a sharded cluster topology require server 4.2+'
105
+ end
106
+ else
107
+ raise NotImplementedError
108
+ end
109
+ end
81
110
  end
82
111
 
83
- def require_scram_sha_256_support
84
- before do
85
- $mongo_server_features ||= begin
86
- scanned_client_server!.features
112
+ def require_tls
113
+ before(:all) do
114
+ unless SpecConfig.instance.ssl?
115
+ skip "SSL not enabled"
87
116
  end
88
- unless $mongo_server_features.scram_sha_256_enabled?
89
- skip "SCRAM SHA 256 is not enabled on the server"
117
+ end
118
+ end
119
+
120
+ def require_no_tls
121
+ before(:all) do
122
+ if SpecConfig.instance.ssl?
123
+ skip "SSL enabled"
90
124
  end
91
125
  end
92
126
  end
93
127
 
94
- def require_ssl
95
- before do
96
- unless SpecConfig.instance.ssl?
97
- skip "SSL not enabled"
128
+ def require_local_tls
129
+ require_tls
130
+ end
131
+
132
+ def require_no_retry_writes
133
+ before(:all) do
134
+ if SpecConfig.instance.retry_writes?
135
+ skip "Retry writes is enabled"
136
+ end
137
+ end
138
+ end
139
+
140
+ def require_compression
141
+ before(:all) do
142
+ if SpecConfig.instance.compressors.nil?
143
+ skip "Compression is not enabled"
144
+ end
145
+ end
146
+ end
147
+
148
+ def require_no_compression
149
+ before(:all) do
150
+ if SpecConfig.instance.compressors
151
+ skip "Compression is enabled"
152
+ end
153
+ end
154
+ end
155
+
156
+ def ruby_version_gte(version)
157
+ before(:all) do
158
+ if RUBY_VERSION < version
159
+ skip "Ruby version #{version} or higher required"
160
+ end
161
+ end
162
+ end
163
+
164
+ def ruby_version_lt(version)
165
+ before(:all) do
166
+ if RUBY_VERSION >= version
167
+ skip "Ruby version less than #{version} required"
168
+ end
169
+ end
170
+ end
171
+
172
+ def require_auth
173
+ before(:all) do
174
+ unless ENV['AUTH'] == 'auth' || SpecConfig.instance.user || ClusterConfig.instance.auth_enabled?
175
+ skip "Auth required"
176
+ end
177
+ end
178
+ end
179
+
180
+ def require_no_auth
181
+ before(:all) do
182
+ if ENV['AUTH'] == 'auth' || SpecConfig.instance.user || ClusterConfig.instance.auth_enabled?
183
+ skip "Auth not allowed"
184
+ end
185
+ end
186
+ end
187
+
188
+ def require_no_x509_auth
189
+ before(:all) do
190
+ if SpecConfig.instance.x509_auth?
191
+ skip "X.509 auth not allowed"
192
+ end
193
+ end
194
+ end
195
+
196
+ # Can the driver specify a write concern that won't be overridden?
197
+ # (mongos 4.0+ overrides the write concern)
198
+ def require_set_write_concern
199
+ before(:all) do
200
+ if ClusterConfig.instance.topology == :sharded && ClusterConfig.instance.short_server_version >= '4.0'
201
+ skip "mongos 4.0+ overrides write concern"
202
+ end
203
+ end
204
+ end
205
+
206
+ def require_multi_shard
207
+ before(:all) do
208
+ if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length == 1
209
+ skip 'Test requires a minimum of two shards if run in sharded topology'
210
+ end
211
+ end
212
+ end
213
+
214
+ def require_no_multi_shard
215
+ before(:all) do
216
+ if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length > 1
217
+ skip 'Test requires a single shard if run in sharded topology'
218
+ end
219
+ end
220
+ end
221
+
222
+ def require_wired_tiger
223
+ before(:all) do
224
+ if ClusterConfig.instance.storage_engine != :wired_tiger
225
+ skip 'Test requires WiredTiger storage engine'
226
+ end
227
+ end
228
+ end
229
+
230
+ def require_wired_tiger_on_36
231
+ before(:all) do
232
+ if ClusterConfig.instance.short_server_version >= '3.6'
233
+ if ClusterConfig.instance.storage_engine != :wired_tiger
234
+ skip 'Test requires WiredTiger storage engine on 3.6+ servers'
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ def require_mmapv1
241
+ before(:all) do
242
+ if ClusterConfig.instance.storage_engine != :mmapv1
243
+ skip 'Test requires MMAPv1 storage engine'
244
+ end
245
+ end
246
+ end
247
+
248
+ def require_enterprise
249
+ before(:all) do
250
+ unless ClusterConfig.instance.enterprise?
251
+ skip 'Test requires enterprise build of MongoDB'
252
+ end
253
+ end
254
+ end
255
+
256
+ # Integration tests for SRV polling require internet connectivity to
257
+ # look up SRV records and a sharded cluster configured on default port on
258
+ # localhost (localhost:27017, localhost:27018).
259
+ def require_default_port_deployment
260
+ # Because the DNS records at test1.test.build.10gen.cc point to
261
+ # localhost:27017 & localhost:27018, the test suite must have been
262
+ # configured to use these addresses
263
+ before(:all) do
264
+ have_default_port = SpecConfig.instance.addresses.any? do |address|
265
+ %w(127.0.0.1 127.0.0.1:27017 localhost localhost:27017).include?(address)
266
+ end
267
+ unless have_default_port
268
+ skip 'This test requires the test suite to be configured for localhost:27017'
98
269
  end
99
270
  end
100
271
  end
@@ -0,0 +1,50 @@
1
+ require 'singleton'
2
+
3
+ module Mongo
4
+ class Client
5
+ alias :get_session_without_tracking :get_session
6
+
7
+ def get_session(options = {})
8
+ get_session_without_tracking(options).tap do |session|
9
+ SessionRegistry.instance.register(session)
10
+ end
11
+ end
12
+ end
13
+
14
+ class Session
15
+ alias :end_session_without_tracking :end_session
16
+
17
+ def end_session
18
+ SessionRegistry.instance.unregister(self)
19
+ end_session_without_tracking
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ class SessionRegistry
26
+ include Singleton
27
+
28
+ def initialize
29
+ @registry = {}
30
+ end
31
+
32
+ def register(session)
33
+ @registry[session.session_id] = session if session
34
+ end
35
+
36
+ def unregister(session)
37
+ @registry.delete(session.session_id)
38
+ end
39
+
40
+ def verify_sessions_ended!
41
+ unless @registry.empty?
42
+ sessions = @registry.map { |_, session| session }
43
+ raise "Session registry contains live sessions: #{sessions.join(', ')}"
44
+ end
45
+ end
46
+
47
+ def clear_registry
48
+ @registry = {}
49
+ end
50
+ end