mongoid 7.2.0 → 7.2.1

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  5. data/lib/mongoid/document.rb +3 -2
  6. data/lib/mongoid/interceptable.rb +3 -1
  7. data/lib/mongoid/matcher/field_operator.rb +7 -11
  8. data/lib/mongoid/version.rb +1 -1
  9. data/spec/integration/app_spec.rb +35 -2
  10. data/spec/integration/callbacks_models.rb +49 -0
  11. data/spec/integration/callbacks_spec.rb +216 -0
  12. data/spec/integration/matcher_operator_data/gt_types.yml +63 -0
  13. data/spec/integration/matcher_operator_data/gte_types.yml +15 -0
  14. data/spec/integration/matcher_operator_data/lt_types.yml +15 -0
  15. data/spec/integration/matcher_operator_data/lte_types.yml +15 -0
  16. data/spec/integration/matcher_operator_data/ne_types.yml +15 -0
  17. data/spec/lite_spec_helper.rb +1 -1
  18. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  19. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  20. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +36 -0
  21. data/spec/shared/lib/mrss/cluster_config.rb +211 -0
  22. data/spec/shared/lib/mrss/constraints.rb +27 -0
  23. data/spec/shared/lib/mrss/docker_runner.rb +262 -0
  24. data/spec/shared/lib/mrss/server_version_registry.rb +69 -0
  25. data/spec/shared/share/Dockerfile.erb +229 -0
  26. data/spec/shared/shlib/distro.sh +73 -0
  27. data/spec/shared/shlib/server.sh +270 -0
  28. data/spec/shared/shlib/set_env.sh +128 -0
  29. data/spec/support/models/customer.rb +11 -0
  30. data/spec/support/models/customer_address.rb +12 -0
  31. data/spec/support/models/dictionary.rb +6 -0
  32. metadata +44 -12
  33. metadata.gz.sig +0 -0
@@ -0,0 +1,15 @@
1
+ - name: Date field - matches
2
+ document:
3
+ date_field: 2020-12-22
4
+ query:
5
+ date_field:
6
+ $gte: 2020-12-21
7
+ matches: true
8
+
9
+ - name: Date field - does not match
10
+ document:
11
+ date_field: 2020-12-20
12
+ query:
13
+ date_field:
14
+ $gte: 2020-12-21
15
+ matches: false
@@ -0,0 +1,15 @@
1
+ - name: Date field - matches
2
+ document:
3
+ date_field: 2020-12-19
4
+ query:
5
+ date_field:
6
+ $lt: 2020-12-21
7
+ matches: true
8
+
9
+ - name: Date field - does not match
10
+ document:
11
+ date_field: 2020-12-21
12
+ query:
13
+ date_field:
14
+ $lt: 2020-12-21
15
+ matches: false
@@ -0,0 +1,15 @@
1
+ - name: Date field - matches
2
+ document:
3
+ date_field: 2020-12-19
4
+ query:
5
+ date_field:
6
+ $lte: 2020-12-21
7
+ matches: true
8
+
9
+ - name: Date field - does not match
10
+ document:
11
+ date_field: 2020-12-22
12
+ query:
13
+ date_field:
14
+ $lte: 2020-12-21
15
+ matches: false
@@ -0,0 +1,15 @@
1
+ - name: Date field - matches
2
+ document:
3
+ date_field: 2020-12-22
4
+ query:
5
+ date_field:
6
+ $ne: 2020-12-21
7
+ matches: true
8
+
9
+ - name: Date field - does not match
10
+ document:
11
+ date_field: 2020-12-21
12
+ query:
13
+ date_field:
14
+ $ne: 2020-12-21
15
+ matches: false
@@ -51,7 +51,7 @@ RSpec.configure do |config|
51
51
  config.add_formatter(RSpec::Core::Formatters::JsonFormatter, File.join(File.dirname(__FILE__), '../tmp/rspec.json'))
52
52
  end
53
53
 
54
- if SpecConfig.instance.ci?
54
+ if SpecConfig.instance.ci? && !%w(1 true yes).include?(ENV['INTERACTIVE']&.downcase)
55
55
  timeout = if SpecConfig.instance.app_tests?
56
56
  # Allow 5 minutes per test for the app tests, since they install
57
57
  # gems for Rails applications which can take a long time.
@@ -507,4 +507,54 @@ describe Mongoid::Association::Embedded::EmbeddedIn::Proxy do
507
507
  end
508
508
  end
509
509
  end
510
+
511
+ context "when the same class is embedded multiple times" do
512
+
513
+ let(:customer) do
514
+ Customer.new
515
+ end
516
+
517
+ context "assignment after saving" do
518
+
519
+ it "correctly sets the association for the embedded class" do
520
+ pending 'MONGOID-5039'
521
+
522
+ customer.home_address = CustomerAddress.new
523
+ customer.work_address = CustomerAddress.new
524
+
525
+ expect(customer.home_address._association.store_as).to eq("home_address")
526
+ expect(customer.work_address._association.store_as).to eq("work_address")
527
+
528
+ expect(customer.home_address.instance_eval { _association.store_as }).to eq("home_address")
529
+ expect(customer.work_address.instance_eval { _association.store_as }).to eq("work_address")
530
+
531
+ customer.save!
532
+
533
+ customer.home_address = CustomerAddress.new
534
+ customer.work_address = CustomerAddress.new
535
+
536
+ expect(customer.home_address._association.store_as).to eq("home_address")
537
+ expect(customer.work_address._association.store_as).to eq("work_address")
538
+
539
+ expect(customer.home_address.instance_eval { _association.store_as }).to eq("home_address")
540
+ expect(customer.work_address.instance_eval { _association.store_as }).to eq("work_address")
541
+ end
542
+ end
543
+
544
+ context "inverse assignment" do
545
+
546
+ it "correctly sets the association for the embedded class" do
547
+ pending 'MONGOID-5039'
548
+
549
+ customer.work_address = CustomerAddress.new
550
+ customer.work_address.addressable = customer
551
+
552
+ expect(customer.home_address._association.store_as).to eq("home_address")
553
+ expect(customer.work_address._association.store_as).to eq("work_address")
554
+
555
+ expect(customer.home_address.instance_eval { _association.store_as }).to eq("home_address")
556
+ expect(customer.work_address.instance_eval { _association.store_as }).to eq("work_address")
557
+ end
558
+ end
559
+ end
510
560
  end
@@ -268,5 +268,46 @@ describe Mongoid::Atomic::Paths do
268
268
  end
269
269
  end
270
270
  end
271
+
272
+ context "when the same class is embedded in multiple associations" do
273
+
274
+ let(:customer) do
275
+ Customer.new
276
+ end
277
+
278
+ context "assignment after saving" do
279
+
280
+ it "correctly sets the association for the embedded class" do
281
+ pending 'MONGOID-5039'
282
+
283
+ customer.home_address = CustomerAddress.new
284
+ customer.work_address = CustomerAddress.new
285
+
286
+ expect(customer.home_address.atomic_path).to eq("home_address")
287
+ expect(customer.work_address.atomic_path).to eq("work_address")
288
+
289
+ customer.save!
290
+
291
+ customer.home_address = CustomerAddress.new
292
+ customer.work_address = CustomerAddress.new
293
+
294
+ expect(customer.home_address.atomic_path).to eq("home_address")
295
+ expect(customer.work_address.atomic_path).to eq("work_address")
296
+ end
297
+ end
298
+
299
+ context "inverse assignment" do
300
+
301
+ it "correctly returns the path for each embedded class" do
302
+ pending 'MONGOID-5039'
303
+
304
+ customer.work_address = CustomerAddress.new
305
+ customer.work_address.addressable = customer
306
+
307
+ expect(customer.home_address.atomic_path).to eq("home_address")
308
+ expect(customer.work_address.atomic_path).to eq("work_address")
309
+ end
310
+ end
311
+ end
271
312
  end
272
313
  end
@@ -1447,6 +1447,42 @@ describe Mongoid::Criteria::Queryable::Selectable do
1447
1447
  end
1448
1448
  end
1449
1449
  end
1450
+
1451
+ context 'when using multiple criteria and symbol operators' do
1452
+ context 'when using fields that meaningfully evolve values' do
1453
+
1454
+ let(:query) do
1455
+ Dictionary.any_of({a: 1}, :published.gt => Date.new(2020, 2, 3))
1456
+ end
1457
+
1458
+ it 'generates the expected query' do
1459
+ query.selector.should == {'$or' => [
1460
+ {'a' => 1},
1461
+ # Date instance is converted to a Time instance in local time,
1462
+ # because we are querying on a Time field and dates are interpreted
1463
+ # in local time when assigning to Time fields
1464
+ {'published' => {'$gt' => Time.local(2020, 2, 3)}},
1465
+ ]}
1466
+ end
1467
+ end
1468
+
1469
+ context 'when using fields that do not meaningfully evolve values' do
1470
+
1471
+ let(:query) do
1472
+ Dictionary.any_of({a: 1}, :submitted_on.gt => Date.new(2020, 2, 3))
1473
+ end
1474
+
1475
+ it 'generates the expected query' do
1476
+ query.selector.should == {'$or' => [
1477
+ {'a' => 1},
1478
+ # Date instance is converted to a Time instance in UTC,
1479
+ # because we are querying on a Date field and dates are interpreted
1480
+ # in UTC when persisted as dates by Mongoid
1481
+ {'submitted_on' => {'$gt' => Time.utc(2020, 2, 3)}},
1482
+ ]}
1483
+ end
1484
+ end
1485
+ end
1450
1486
  end
1451
1487
 
1452
1488
  describe "#not" do
@@ -0,0 +1,211 @@
1
+ # ClusterConfig requires ClientRegistry class provided by the host project.
2
+
3
+ require 'singleton'
4
+
5
+ module Mrss
6
+ class ClusterConfig
7
+ include Singleton
8
+ include RSpec::Core::Pending
9
+
10
+ def single_server?
11
+ determine_cluster_config
12
+ @single_server
13
+ end
14
+
15
+ def replica_set_name
16
+ determine_cluster_config
17
+ @replica_set_name
18
+ end
19
+
20
+ def server_version
21
+ determine_cluster_config
22
+ @server_version
23
+ end
24
+
25
+ def enterprise?
26
+ determine_cluster_config
27
+ @enterprise
28
+ end
29
+
30
+ def short_server_version
31
+ server_version.split('.')[0..1].join('.')
32
+ end
33
+
34
+ def fcv
35
+ determine_cluster_config
36
+ @fcv
37
+ end
38
+
39
+ # Per https://jira.mongodb.org/browse/SERVER-39052, working with FCV
40
+ # in sharded topologies is annoying. Also, FCV doesn't exist in servers
41
+ # less than 3.4. This method returns FCV on 3.4+ servers when in single
42
+ # or RS topologies, and otherwise returns the major.minor server version.
43
+ def fcv_ish
44
+ if server_version.nil?
45
+ raise "Deployment server version not known - check that connection to deployment succeeded"
46
+ end
47
+
48
+ if server_version >= '3.4' && topology != :sharded
49
+ fcv
50
+ else
51
+ if short_server_version == '4.1'
52
+ '4.2'
53
+ else
54
+ short_server_version
55
+ end
56
+ end
57
+ end
58
+
59
+ # @return [ Mongo::Address ] The address of the primary in the deployment.
60
+ def primary_address
61
+ determine_cluster_config
62
+ @primary_address
63
+ end
64
+
65
+ def primary_address_str
66
+ determine_cluster_config
67
+ @primary_address.seed
68
+ end
69
+
70
+ def primary_address_host
71
+ both = primary_address_str
72
+ both.split(':').first
73
+ end
74
+
75
+ def primary_address_port
76
+ both = primary_address_str
77
+ both.split(':')[1] || 27017
78
+ end
79
+
80
+ def primary_description
81
+ determine_cluster_config
82
+ @primary_description
83
+ end
84
+
85
+ # Try running a command on the admin database to see if the mongod was
86
+ # started with auth.
87
+ def auth_enabled?
88
+ if @auth_enabled.nil?
89
+ @auth_enabled = begin
90
+ basic_client.use(:admin).command(getCmdLineOpts: 1).first["argv"].include?("--auth")
91
+ rescue => e
92
+ e.message =~ /(not authorized)|(unauthorized)|(no users authenticated)|(requires authentication)/
93
+ end
94
+ end
95
+ @auth_enabled
96
+ end
97
+
98
+ def topology
99
+ determine_cluster_config
100
+ @topology
101
+ end
102
+
103
+ def storage_engine
104
+ @storage_engine ||= begin
105
+ # 2.6 does not have wired tiger
106
+ if short_server_version == '2.6'
107
+ :mmapv1
108
+ else
109
+ client = ClientRegistry.instance.global_client('root_authorized')
110
+ if topology == :sharded
111
+ shards = client.use(:admin).command(listShards: 1).first
112
+ if shards['shards'].empty?
113
+ raise 'Shards are empty'
114
+ end
115
+ shard = shards['shards'].first
116
+ address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
117
+ client = ClusterTools.instance.direct_client(address_str,
118
+ SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
119
+ end
120
+ rv = client.use(:admin).command(serverStatus: 1).first
121
+ rv = rv['storageEngine']['name']
122
+ rv_map = {
123
+ 'wiredTiger' => :wired_tiger,
124
+ 'mmapv1' => :mmapv1,
125
+ }
126
+ rv_map[rv] || rv
127
+ end
128
+ end
129
+ end
130
+
131
+ # This method returns an alternate address for connecting to the configured
132
+ # deployment. For example, if the replica set is configured with nodes at
133
+ # of localhost:27017 and so on, this method will return 127.0.0.:27017.
134
+ #
135
+ # Note that the "alternate" refers to replica set configuration, not the
136
+ # addresses specified in test suite configuration. If the deployment topology
137
+ # is not a replica set, "alternate" refers to test suite configuration as
138
+ # this is the only configuration available.
139
+ def alternate_address
140
+ @alternate_address ||= begin
141
+ address = primary_address_host
142
+ str = case address
143
+ when '127.0.0.1'
144
+ 'localhost'
145
+ when /^(\d+\.){3}\d+$/
146
+ skip 'This test requires a hostname or 127.0.0.1 as address'
147
+ else
148
+ # We don't know if mongod is listening on ipv4 or ipv6, in principle.
149
+ # Our tests use ipv4, so hardcode that for now.
150
+ # To support both we need to try both addresses which will make this
151
+ # test more complicated.
152
+ #
153
+ # JRuby chokes on primary_address_port as the port (e.g. 27017).
154
+ # Since the port does not actually matter, use a common port like 80.
155
+ resolved_address = Addrinfo.getaddrinfo(address, 80, Socket::PF_INET).first.ip_address
156
+ if resolved_address.include?(':')
157
+ "[#{resolved_address}]"
158
+ else
159
+ resolved_address
160
+ end
161
+ end + ":#{primary_address_port}"
162
+ Mongo::Address.new(str)
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def determine_cluster_config
169
+ return if @primary_address
170
+
171
+ # Run all commands to figure out the cluster configuration from the same
172
+ # client. This is somewhat wasteful when running a single test, but reduces
173
+ # test runtime for the suite overall because all commands are sent on the
174
+ # same connection rather than each command connecting to the cluster by
175
+ # itself.
176
+ client = ClientRegistry.instance.global_client('root_authorized')
177
+
178
+ primary = client.cluster.next_primary
179
+ @primary_address = primary.address
180
+ @primary_description = primary.description
181
+ @replica_set_name = client.cluster.topology.replica_set_name
182
+
183
+ @topology ||= begin
184
+ topology = client.cluster.topology.class.name.sub(/.*::/, '')
185
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
186
+ if topology =~ /^replica_set/
187
+ topology = 'replica_set'
188
+ end
189
+ topology.to_sym
190
+ end
191
+
192
+ @single_server = client.cluster.servers_list.length == 1
193
+
194
+ build_info = client.database.command(buildInfo: 1).first
195
+
196
+ @server_version = build_info['version']
197
+ @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
198
+
199
+ if @topology != :sharded && short_server_version >= '3.4'
200
+ rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
201
+ @fcv = rv['version'] || rv
202
+ end
203
+ end
204
+
205
+ def basic_client
206
+ # Do not cache the result here so that if the client gets closed,
207
+ # client registry reconnects it in subsequent tests
208
+ ClientRegistry.instance.global_client('basic')
209
+ end
210
+ end
211
+ end
@@ -147,6 +147,33 @@ module Mrss
147
147
  end
148
148
  end
149
149
 
150
+ def require_no_snappy_compression
151
+ before(:all) do
152
+ compressors = SpecConfig.instance.compressors
153
+ if compressors && compressors.include?('snappy')
154
+ skip "Snappy compression is enabled"
155
+ end
156
+ end
157
+ end
158
+
159
+ def require_zstd_compression
160
+ before(:all) do
161
+ compressors = SpecConfig.instance.compressors
162
+ unless compressors && compressors.include?('zstd')
163
+ skip "Zstd compression is not enabled"
164
+ end
165
+ end
166
+ end
167
+
168
+ def require_no_zstd_compression
169
+ before(:all) do
170
+ compressors = SpecConfig.instance.compressors
171
+ if compressors && compressors.include?('zstd')
172
+ skip "Zstd compression is enabled"
173
+ end
174
+ end
175
+ end
176
+
150
177
  def require_no_compression
151
178
  before(:all) do
152
179
  if SpecConfig.instance.compressors