mongoid 7.3.2 → 7.3.3

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 (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/config/locales/en.yml +13 -0
  4. data/lib/mongoid/association/referenced/has_many/enumerable.rb +3 -7
  5. data/lib/mongoid/association/relatable.rb +2 -0
  6. data/lib/mongoid/atomic.rb +26 -2
  7. data/lib/mongoid/config/environment.rb +9 -1
  8. data/lib/mongoid/contextual/atomic.rb +7 -2
  9. data/lib/mongoid/contextual/none.rb +3 -0
  10. data/lib/mongoid/criteria/queryable/selectable.rb +2 -2
  11. data/lib/mongoid/criteria/queryable/storable.rb +4 -4
  12. data/lib/mongoid/document.rb +3 -2
  13. data/lib/mongoid/errors/empty_config_file.rb +26 -0
  14. data/lib/mongoid/errors/invalid_config_file.rb +26 -0
  15. data/lib/mongoid/errors.rb +2 -0
  16. data/lib/mongoid/persistence_context.rb +3 -1
  17. data/lib/mongoid/query_cache.rb +11 -1
  18. data/lib/mongoid/tasks/database.rb +1 -1
  19. data/lib/mongoid/touchable.rb +10 -0
  20. data/lib/mongoid/version.rb +1 -1
  21. data/spec/integration/contextual/empty_spec.rb +142 -0
  22. data/spec/integration/stringified_symbol_field_spec.rb +2 -2
  23. data/spec/mongoid/association/referenced/belongs_to_query_spec.rb +20 -0
  24. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +244 -92
  25. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +6 -6
  26. data/spec/mongoid/association/referenced/has_many_models.rb +17 -0
  27. data/spec/mongoid/clients/factory_spec.rb +9 -3
  28. data/spec/mongoid/clients/options_spec.rb +11 -5
  29. data/spec/mongoid/config/environment_spec.rb +86 -8
  30. data/spec/mongoid/config_spec.rb +89 -16
  31. data/spec/mongoid/contextual/atomic_spec.rb +64 -25
  32. data/spec/mongoid/contextual/geo_near_spec.rb +1 -1
  33. data/spec/mongoid/document_spec.rb +21 -1
  34. data/spec/mongoid/errors/invalid_config_file_spec.rb +32 -0
  35. data/spec/mongoid/persistable/updatable_spec.rb +2 -0
  36. data/spec/mongoid/query_cache_spec.rb +24 -0
  37. data/spec/mongoid/touchable_spec.rb +18 -0
  38. data/spec/mongoid/touchable_spec_models.rb +2 -0
  39. data/spec/shared/lib/mrss/constraints.rb +21 -4
  40. data/spec/shared/lib/mrss/event_subscriber.rb +200 -0
  41. data/spec/shared/lib/mrss/server_version_registry.rb +17 -12
  42. data/spec/shared/share/Dockerfile.erb +5 -4
  43. data/spec/shared/shlib/server.sh +71 -21
  44. data.tar.gz.sig +0 -0
  45. metadata +581 -573
  46. metadata.gz.sig +0 -0
@@ -59,7 +59,7 @@ describe Mongoid::Contextual::GeoNear do
59
59
  end
60
60
 
61
61
  it "is nil except for 4.0 sharded when it is 0" do
62
- expect(geo_near.average_distance).to be expected_value
62
+ expect(geo_near.average_distance).to eq expected_value
63
63
  end
64
64
  end
65
65
  end
@@ -501,7 +501,7 @@ describe Mongoid::Document do
501
501
  end
502
502
  end
503
503
 
504
- context ':compact option' do
504
+ context 'deprecated :compact option' do
505
505
  # Since rails 6 differs in how it treats id fields,
506
506
  # run this test on one version of rails. Currently rails 6 is in beta,
507
507
  # when it is released this version should be changed to 6.
@@ -513,6 +513,26 @@ describe Mongoid::Document do
513
513
  expect(church.as_json.keys.sort).to eq(%w(_id location name))
514
514
  end
515
515
 
516
+ context 'deprecation' do
517
+ let(:church) do
518
+ Church.create!(name: 'St. Basil')
519
+ end
520
+
521
+ let(:message) do
522
+ '#as_json :compact option is deprecated. Please call #compact on the returned Hash object instead.'
523
+ end
524
+
525
+ it 'logs a deprecation warning when :compact is given' do
526
+ expect_any_instance_of(Logger).to receive(:warn).with(message)
527
+ church.as_json(compact: true)
528
+ end
529
+
530
+ it 'does not log a deprecation warning when :compact is not given' do
531
+ expect_any_instance_of(Logger).to_not receive(:warn).with(message)
532
+ church.as_json
533
+ end
534
+ end
535
+
516
536
  context 'there is a nil valued attribute' do
517
537
  let(:church) do
518
538
  Church.create!(name: 'St. Basil')
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require "spec_helper"
5
+
6
+ describe Mongoid::Errors::InvalidConfigFile do
7
+
8
+ describe "#message" do
9
+
10
+ let(:error) do
11
+ described_class.new('/my/path')
12
+ end
13
+
14
+ it "contains the problem in the message" do
15
+ expect(error.message).to include(
16
+ "Invalid configuration file: /my/path."
17
+ )
18
+ end
19
+
20
+ it "contains the summary in the message" do
21
+ expect(error.message).to include(
22
+ "Your mongoid.yml configuration file does not contain the"
23
+ )
24
+ end
25
+
26
+ it "contains the resolution in the message" do
27
+ expect(error.message).to include(
28
+ "Ensure your configuration file contains the correct contents."
29
+ )
30
+ end
31
+ end
32
+ end
@@ -458,6 +458,7 @@ describe Mongoid::Persistable::Updatable do
458
458
  describe "##{method}" do
459
459
 
460
460
  context "when saving with a hash field with invalid keys" do
461
+ max_server_version '4.9'
461
462
 
462
463
  let(:person) do
463
464
  Person.create
@@ -494,6 +495,7 @@ describe Mongoid::Persistable::Updatable do
494
495
  end
495
496
 
496
497
  context "when the document has been destroyed" do
498
+ max_server_version '4.9'
497
499
 
498
500
  let(:person) do
499
501
  Person.create
@@ -24,10 +24,27 @@ describe Mongoid::QueryCache do
24
24
  SessionRegistry.instance.verify_sessions_ended!
25
25
  end
26
26
 
27
+ let(:reset_legacy_qc_warning) do
28
+ begin
29
+ Mongoid::QueryCache.remove_instance_variable('@legacy_query_cache_warned')
30
+ rescue NameError
31
+ # raised if the instance variable wasn't set
32
+ end
33
+ end
34
+
27
35
  describe '#cache' do
28
36
  context 'with driver query cache' do
29
37
  min_driver_version '2.14'
30
38
 
39
+ it 'does not log a deprecation warning' do
40
+ reset_legacy_qc_warning
41
+
42
+ expect_any_instance_of(Logger).to_not receive(:warn).with(
43
+ described_class::LEGACY_WARNING
44
+ )
45
+ described_class.cache { }
46
+ end
47
+
31
48
  context 'when query cache is not enabled' do
32
49
  before do
33
50
  Mongoid::QueryCache.enabled = false
@@ -180,6 +197,13 @@ describe Mongoid::QueryCache do
180
197
  context 'with mongoid query cache' do
181
198
  max_driver_version '2.13'
182
199
 
200
+ it 'logs a deprecation warning' do
201
+ reset_legacy_qc_warning
202
+
203
+ expect_any_instance_of(Logger).to receive(:warn).with(described_class::LEGACY_WARNING)
204
+ described_class.cache { }
205
+ end
206
+
183
207
  context 'when query cache is not enabled' do
184
208
  before do
185
209
  Mongoid::QueryCache.enabled = false
@@ -132,6 +132,24 @@ describe Mongoid::Touchable do
132
132
  include_examples 'updates the child'
133
133
  include_examples 'updates the parent when :touch is true'
134
134
  include_examples 'updates the parent when :touch is not set'
135
+
136
+ context 'when also updating an additional field' do
137
+ it 'persists the update to the additional field' do
138
+ entrance
139
+ update_time
140
+ entrance.touch(:last_used_at)
141
+
142
+ entrance.reload
143
+ building.reload
144
+
145
+ # This is the assertion we want.
146
+ entrance.last_used_at.should == update_time
147
+
148
+ # Check other timestamps for good measure.
149
+ entrance.updated_at.should == update_time
150
+ building.updated_at.should == update_time
151
+ end
152
+ end
135
153
  end
136
154
 
137
155
  context "when the document is referenced" do
@@ -16,6 +16,8 @@ module TouchableSpec
16
16
  include Mongoid::Timestamps
17
17
 
18
18
  embedded_in :building
19
+
20
+ field :last_used_at, type: Time
19
21
  end
20
22
 
21
23
  class Floor
@@ -257,22 +257,39 @@ module Mrss
257
257
  end
258
258
  end
259
259
 
260
- def require_multi_shard
260
+ def require_multi_mongos
261
261
  before(:all) do
262
262
  if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length == 1
263
- skip 'Test requires a minimum of two shards if run in sharded topology'
263
+ skip 'Test requires a minimum of two mongoses if run in sharded topology'
264
+ end
265
+
266
+ if ClusterConfig.instance.topology == :load_balanced && SpecConfig.instance.single_mongos?
267
+ skip 'Test requires a minimum of two mongoses if run in load-balanced topology'
264
268
  end
265
269
  end
266
270
  end
267
271
 
268
- def require_no_multi_shard
272
+ # In sharded topology operations are distributed to the mongoses.
273
+ # When we set fail points, the fail point may be set on one mongos and
274
+ # operation may be executed on another mongos, causing failures.
275
+ # Tests that are not setting targeted fail points should utilize this
276
+ # method to restrict themselves to single mongos.
277
+ #
278
+ # In load-balanced topology, the same problem can happen when there is
279
+ # more than one mongos behind the load balancer.
280
+ def require_no_multi_mongos
269
281
  before(:all) do
270
282
  if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length > 1
271
- skip 'Test requires a single shard if run in sharded topology'
283
+ skip 'Test requires a single mongos if run in sharded topology'
284
+ end
285
+ if ClusterConfig.instance.topology == :load_balanced && !SpecConfig.instance.single_mongos?
286
+ skip 'Test requires a single mongos, as indicated by SINGLE_MONGOS=1 environment variable, if run in load-balanced topology'
272
287
  end
273
288
  end
274
289
  end
275
290
 
291
+ alias :require_no_multi_shard :require_no_multi_mongos
292
+
276
293
  def require_wired_tiger
277
294
  before(:all) do
278
295
  # Storage detection fails for serverless instances. However, it is safe to
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mrss
4
+ # Test event subscriber.
5
+ class EventSubscriber
6
+
7
+ # The mappings of event names to types.
8
+ MAPPINGS = {
9
+ 'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening,
10
+ 'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged,
11
+ 'topology_closed_event' => Mongo::Monitoring::Event::TopologyClosed,
12
+ 'server_opening_event' => Mongo::Monitoring::Event::ServerOpening,
13
+ 'server_description_changed_event' => Mongo::Monitoring::Event::ServerDescriptionChanged,
14
+ 'server_closed_event' => Mongo::Monitoring::Event::ServerClosed
15
+ }.freeze
16
+
17
+ attr_reader :all_events
18
+
19
+ attr_reader :started_events
20
+
21
+ attr_reader :succeeded_events
22
+
23
+ attr_reader :failed_events
24
+
25
+ attr_reader :published_events
26
+
27
+ # @param [ String ] name Optional name for the event subscriber.
28
+ def initialize(name: nil)
29
+ @mutex = Mutex.new
30
+ clear_events!
31
+ @name = name
32
+ end
33
+
34
+ def to_s
35
+ %Q`#<EventSubscriber:#{@name ? "\"#{@name}\"" : '%x' % object_id} \
36
+ started=#{started_events.length} \
37
+ succeeded=#{succeeded_events.length} \
38
+ failed=#{failed_events.length} \
39
+ published=#{published_events.length}>`
40
+ end
41
+
42
+ alias :inspect :to_s
43
+
44
+ # Event retrieval
45
+
46
+ def select_started_events(cls)
47
+ started_events.select do |event|
48
+ event.is_a?(cls)
49
+ end
50
+ end
51
+
52
+ def select_succeeded_events(cls)
53
+ succeeded_events.select do |event|
54
+ event.is_a?(cls)
55
+ end
56
+ end
57
+
58
+ def select_completed_events(*classes)
59
+ (succeeded_events + failed_events).select do |event|
60
+ classes.any? { |c| c === event }
61
+ end
62
+ end
63
+
64
+ def select_published_events(cls)
65
+ published_events.select do |event|
66
+ event.is_a?(cls)
67
+ end
68
+ end
69
+
70
+ # Filters command started events for the specified command name.
71
+ def command_started_events(command_name)
72
+ started_events.select do |event|
73
+ event.command[command_name]
74
+ end
75
+ end
76
+
77
+ def non_auth_command_started_events
78
+ started_events.reject do |event|
79
+ %w(authenticate getnonce saslSstart saslContinue).any? do |cmd|
80
+ event.command[cmd]
81
+ end
82
+ end
83
+ end
84
+
85
+ # Locates command stated events for the specified command name,
86
+ # asserts that there is exactly one such event, and returns it.
87
+ def single_command_started_event(command_name, include_auth: false)
88
+ events = if include_auth
89
+ started_events
90
+ else
91
+ non_auth_command_started_events
92
+ end
93
+ events.select! do |event|
94
+ event.command[command_name]
95
+ end
96
+ if events.length != 1
97
+ raise "Expected a single #{command_name} event but we have #{events.length}"
98
+ end
99
+ events.first
100
+ end
101
+
102
+
103
+ # Get the first succeeded event published for the name, and then delete it.
104
+ #
105
+ # @param [ String ] name The event name.
106
+ #
107
+ # @return [ Event ] The matching event.
108
+ def first_event(name)
109
+ cls = MAPPINGS[name]
110
+ if cls.nil?
111
+ raise ArgumentError, "Bogus event name #{name}"
112
+ end
113
+ matching = succeeded_events.find do |event|
114
+ cls === event
115
+ end
116
+ succeeded_events.delete(matching)
117
+ matching
118
+ end
119
+
120
+ # Event recording
121
+
122
+ # Cache the started event.
123
+ #
124
+ # @param [ Event ] event The event.
125
+ def started(event)
126
+ @mutex.synchronize do
127
+ started_events << event
128
+ all_events << event
129
+ end
130
+ end
131
+
132
+ # Cache the succeeded event.
133
+ #
134
+ # @param [ Event ] event The event.
135
+ def succeeded(event)
136
+ @mutex.synchronize do
137
+ succeeded_events << event
138
+ all_events << event
139
+ end
140
+ end
141
+
142
+ # Cache the failed event.
143
+ #
144
+ # @param [ Event ] event The event.
145
+ def failed(event)
146
+ @mutex.synchronize do
147
+ failed_events << event
148
+ all_events << event
149
+ end
150
+ end
151
+
152
+ def published(event)
153
+ @mutex.synchronize do
154
+ published_events << event
155
+ all_events << event
156
+ end
157
+ end
158
+
159
+ # Clear all cached events.
160
+ def clear_events!
161
+ @all_events = []
162
+ @started_events = []
163
+ @succeeded_events = []
164
+ @failed_events = []
165
+ @published_events = []
166
+ self
167
+ end
168
+ end
169
+ # Only handles succeeded events correctly.
170
+ class PhasedEventSubscriber < EventSubscriber
171
+ def initialize
172
+ super
173
+ @phase_events = {}
174
+ end
175
+
176
+ def phase_finished(phase_index)
177
+ @phase_events[phase_index] = succeeded_events
178
+ @succeeded_events = []
179
+ end
180
+
181
+ def phase_events(phase_index)
182
+ @phase_events[phase_index]
183
+ end
184
+
185
+ def event_count
186
+ @phase_events.inject(0) do |sum, event|
187
+ sum + event.length
188
+ end
189
+ end
190
+ end
191
+
192
+ class VerboseEventSubscriber < EventSubscriber
193
+ %w(started succeeded failed published).each do |meth|
194
+ define_method(meth) do |event|
195
+ puts event.summary
196
+ super(event)
197
+ end
198
+ end
199
+ end
200
+ end
@@ -48,19 +48,24 @@ module Mrss
48
48
  url = dl['archive']['url']
49
49
  end
50
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
51
+ if %w(2.6 3.0).include?(desired_version) && arch == 'ubuntu1604'
52
+ # 2.6 and 3.0 are only available for ubuntu1204 and ubuntu1404.
53
+ # Those ubuntus have ancient Pythons that don't work due to not
54
+ # implementing recent TLS protocols.
55
+ # Because of this we test on ubuntu1604 which has a newer Python.
56
+ # But we still need to retrieve ubuntu1404-targeting builds.
57
+ url = self.class.new('3.2', arch).download_url
58
+ unless url.include?('3.2.')
59
+ raise 'URL not in expected format'
63
60
  end
61
+ url = case desired_version
62
+ when '2.6'
63
+ url.sub(/\b3\.2\.\d+/, '2.6.12')
64
+ when '3.0'
65
+ url.sub(/\b3\.2\.\d+/, '3.0.15')
66
+ else
67
+ raise NotImplementedError
68
+ end.sub('ubuntu1604', 'ubuntu1404')
64
69
  else
65
70
  raise
66
71
  end