airbrake-ruby 4.13.3-java → 5.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +23 -32
  3. data/lib/airbrake-ruby/async_sender.rb +1 -1
  4. data/lib/airbrake-ruby/backtrace.rb +6 -5
  5. data/lib/airbrake-ruby/config.rb +37 -13
  6. data/lib/airbrake-ruby/config/processor.rb +84 -0
  7. data/lib/airbrake-ruby/config/validator.rb +6 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +16 -1
  10. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  11. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -3
  13. data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
  14. data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
  15. data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
  16. data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
  17. data/lib/airbrake-ruby/filters/keys_filter.rb +26 -18
  18. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/sql_filter.rb +4 -4
  20. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  21. data/lib/airbrake-ruby/filters/thread_filter.rb +2 -0
  22. data/lib/airbrake-ruby/ignorable.rb +1 -0
  23. data/lib/airbrake-ruby/notice.rb +1 -8
  24. data/lib/airbrake-ruby/notice_notifier.rb +7 -0
  25. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  26. data/lib/airbrake-ruby/performance_notifier.rb +2 -15
  27. data/lib/airbrake-ruby/promise.rb +1 -0
  28. data/lib/airbrake-ruby/query.rb +1 -6
  29. data/lib/airbrake-ruby/queue.rb +1 -8
  30. data/lib/airbrake-ruby/remote_settings.rb +145 -0
  31. data/lib/airbrake-ruby/remote_settings/settings_data.rb +120 -0
  32. data/lib/airbrake-ruby/request.rb +1 -8
  33. data/lib/airbrake-ruby/stat.rb +1 -12
  34. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  35. data/lib/airbrake-ruby/tdigest.rb +2 -0
  36. data/lib/airbrake-ruby/thread_pool.rb +2 -0
  37. data/lib/airbrake-ruby/truncator.rb +8 -2
  38. data/lib/airbrake-ruby/version.rb +11 -1
  39. data/spec/airbrake_spec.rb +71 -36
  40. data/spec/backtrace_spec.rb +26 -26
  41. data/spec/code_hunk_spec.rb +2 -2
  42. data/spec/config/processor_spec.rb +209 -0
  43. data/spec/config/validator_spec.rb +18 -1
  44. data/spec/config_spec.rb +13 -6
  45. data/spec/filter_chain_spec.rb +27 -0
  46. data/spec/filters/gem_root_filter_spec.rb +4 -4
  47. data/spec/filters/git_last_checkout_filter_spec.rb +20 -3
  48. data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +11 -10
  49. data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +20 -10
  50. data/spec/filters/root_directory_filter_spec.rb +4 -4
  51. data/spec/filters/sql_filter_spec.rb +5 -5
  52. data/spec/notice_notifier/options_spec.rb +6 -6
  53. data/spec/notice_notifier_spec.rb +2 -2
  54. data/spec/notice_spec.rb +1 -1
  55. data/spec/performance_breakdown_spec.rb +0 -12
  56. data/spec/performance_notifier_spec.rb +2 -27
  57. data/spec/query_spec.rb +1 -11
  58. data/spec/queue_spec.rb +1 -13
  59. data/spec/remote_settings/settings_data_spec.rb +365 -0
  60. data/spec/remote_settings_spec.rb +230 -0
  61. data/spec/request_spec.rb +1 -13
  62. data/spec/spec_helper.rb +4 -4
  63. data/spec/stat_spec.rb +0 -9
  64. data/spec/sync_sender_spec.rb +3 -1
  65. data/spec/thread_pool_spec.rb +25 -5
  66. metadata +22 -14
@@ -0,0 +1,120 @@
1
+ module Airbrake
2
+ class RemoteSettings
3
+ # SettingsData is a container, which wraps JSON payload returned by the
4
+ # remote settings API. It exposes the payload via convenient methods and
5
+ # also ensures that in case some data from the payload is missing, a default
6
+ # value would be returned instead.
7
+ #
8
+ # @example
9
+ # # Create the object and pass initial data (empty hash).
10
+ # settings_data = SettingsData.new({})
11
+ #
12
+ # settings_data.interval #=> 600
13
+ #
14
+ # @since 5.0.0
15
+ # @api private
16
+ class SettingsData
17
+ # @return [Integer] how frequently we should poll the config API
18
+ DEFAULT_INTERVAL = 600
19
+
20
+ # @return [String] API version of the S3 API to poll
21
+ API_VER = '2020-06-18'.freeze
22
+
23
+ # @return [String] what path to poll
24
+ CONFIG_ROUTE_PATTERN =
25
+ "%<host>s/#{API_VER}/config/%<project_id>s/config.json".freeze
26
+
27
+ # @return [Hash{Symbol=>String}] the hash of all supported settings where
28
+ # the value is the name of the setting returned by the API
29
+ SETTINGS = {
30
+ errors: 'errors'.freeze,
31
+ apm: 'apm'.freeze,
32
+ }.freeze
33
+
34
+ # @param [Integer] project_id
35
+ # @param [Hash{String=>Object}] data
36
+ def initialize(project_id, data)
37
+ @project_id = project_id
38
+ @data = data
39
+ end
40
+
41
+ # Merges the given +hash+ with internal data.
42
+ #
43
+ # @param [Hash{String=>Object}] hash
44
+ # @return [self]
45
+ def merge!(hash)
46
+ @data.merge!(hash)
47
+
48
+ self
49
+ end
50
+
51
+ # @return [Integer] how frequently we should poll for the config
52
+ def interval
53
+ return DEFAULT_INTERVAL if !@data.key?('poll_sec') || !@data['poll_sec']
54
+
55
+ @data['poll_sec'] > 0 ? @data['poll_sec'] : DEFAULT_INTERVAL
56
+ end
57
+
58
+ # @param [String] remote_config_host
59
+ # @return [String] where the config is stored on S3.
60
+ def config_route(remote_config_host)
61
+ if !@data.key?('config_route') || !@data['config_route']
62
+ return format(
63
+ CONFIG_ROUTE_PATTERN,
64
+ host: remote_config_host.chomp('/'),
65
+ project_id: @project_id,
66
+ )
67
+ end
68
+
69
+ format(
70
+ CONFIG_ROUTE_PATTERN,
71
+ host: @data['config_route'].chomp('/'),
72
+ project_id: @project_id,
73
+ )
74
+ end
75
+
76
+ # @return [Boolean] whether error notifications are enabled
77
+ def error_notifications?
78
+ return true unless (s = find_setting(SETTINGS[:errors]))
79
+
80
+ s['enabled']
81
+ end
82
+
83
+ # @return [Boolean] whether APM is enabled
84
+ def performance_stats?
85
+ return true unless (s = find_setting(SETTINGS[:apm]))
86
+
87
+ s['enabled']
88
+ end
89
+
90
+ # @return [String, nil] the host, which provides the API endpoint to which
91
+ # exceptions should be sent
92
+ def error_host
93
+ return unless (s = find_setting(SETTINGS[:errors]))
94
+
95
+ s['endpoint']
96
+ end
97
+
98
+ # @return [String, nil] the host, which provides the API endpoint to which
99
+ # APM data should be sent
100
+ def apm_host
101
+ return unless (s = find_setting(SETTINGS[:apm]))
102
+
103
+ s['endpoint']
104
+ end
105
+
106
+ # @return [Hash{String=>Object}] raw representation of JSON payload
107
+ def to_h
108
+ @data.dup
109
+ end
110
+
111
+ private
112
+
113
+ def find_setting(name)
114
+ return unless @data.key?('settings')
115
+
116
+ @data['settings'].find { |s| s['name'] == name }
117
+ end
118
+ end
119
+ end
120
+ end
@@ -4,7 +4,6 @@ module Airbrake
4
4
  # @see Airbrake.notify_request
5
5
  # @api public
6
6
  # @since v3.2.0
7
- # rubocop:disable Metrics/ParameterLists
8
7
  class Request
9
8
  include HashKeyable
10
9
  include Ignorable
@@ -12,15 +11,12 @@ module Airbrake
12
11
  include Mergeable
13
12
  include Grouppable
14
13
 
15
- attr_accessor :method, :route, :status_code, :start_time, :end_time,
16
- :timing, :time
14
+ attr_accessor :method, :route, :status_code, :timing, :time
17
15
 
18
16
  def initialize(
19
17
  method:,
20
18
  route:,
21
19
  status_code:,
22
- start_time: Time.now,
23
- end_time: start_time + 1,
24
20
  timing: nil,
25
21
  time: Time.now
26
22
  )
@@ -28,8 +24,6 @@ module Airbrake
28
24
  @method = method
29
25
  @route = route
30
26
  @status_code = status_code
31
- @start_time = start_time
32
- @end_time = end_time
33
27
  @timing = timing
34
28
  @time = time
35
29
  end
@@ -51,5 +45,4 @@ module Airbrake
51
45
  }.delete_if { |_key, val| val.nil? }
52
46
  end
53
47
  end
54
- # rubocop:enable Metrics/ParameterLists
55
48
  end
@@ -9,7 +9,7 @@ module Airbrake
9
9
  #
10
10
  # @example
11
11
  # stat = Airbrake::Stat.new
12
- # stat.increment(Time.now - 200)
12
+ # stat.increment_ms(2000)
13
13
  # stat.to_h # Pack and serialize data so it can be transmitted.
14
14
  #
15
15
  # @since v3.2.0
@@ -41,17 +41,6 @@ module Airbrake
41
41
  end
42
42
  end
43
43
 
44
- # Increments tdigest timings and updates tdigest with the difference between
45
- # +end_time+ and +start_time+.
46
- #
47
- # @param [Date] start_time
48
- # @param [Date] end_time
49
- # @return [void]
50
- def increment(start_time, end_time = nil)
51
- end_time ||= Time.new
52
- increment_ms((end_time - start_time) * 1000)
53
- end
54
-
55
44
  # Increments tdigest timings and updates tdigest with given +ms+ value.
56
45
  #
57
46
  # @param [Float] ms
@@ -23,7 +23,7 @@ module Airbrake
23
23
  # @param [#to_json] data
24
24
  # @param [URI::HTTPS] endpoint
25
25
  # @return [Hash{String=>String}] the parsed HTTP response
26
- def send(data, promise, endpoint = @config.endpoint)
26
+ def send(data, promise, endpoint = @config.error_endpoint)
27
27
  return promise if rate_limited_ip?(promise)
28
28
 
29
29
  response = nil
@@ -47,6 +47,7 @@ module Airbrake
47
47
  end
48
48
 
49
49
  return promise.reject(parsed_resp['error']) if parsed_resp.key?('error')
50
+
50
51
  promise.resolve(parsed_resp)
51
52
  end
52
53
 
@@ -79,7 +80,7 @@ module Airbrake
79
80
  req['Authorization'] = "Bearer #{@config.project_key}"
80
81
  req['Content-Type'] = CONTENT_TYPE
81
82
  req['User-Agent'] =
82
- "#{Airbrake::Notice::NOTIFIER[:name]}/#{Airbrake::AIRBRAKE_RUBY_VERSION}" \
83
+ "#{Airbrake::NOTIFIER_INFO[:name]}/#{Airbrake::AIRBRAKE_RUBY_VERSION}" \
83
84
  " Ruby/#{RUBY_VERSION}"
84
85
 
85
86
  req
@@ -200,6 +200,7 @@ module Airbrake
200
200
  unless (0..1).cover?(item)
201
201
  raise ArgumentError, "p should be in [0,1], got #{item}"
202
202
  end
203
+
203
204
  if size == 0
204
205
  nil
205
206
  else
@@ -271,6 +272,7 @@ module Airbrake
271
272
  shift = 7
272
273
  while (v & 0x80) != 0
273
274
  raise 'Shift too large in decode' if shift > 28
275
+
274
276
  v = counts_bytes.shift || 0
275
277
  z += (v & 0x7f) << shift
276
278
  shift += 7
@@ -83,6 +83,7 @@ module Airbrake
83
83
 
84
84
  if @pid != Process.pid && @workers.list.empty?
85
85
  @pid = Process.pid
86
+ @workers = ThreadGroup.new
86
87
  spawn_workers
87
88
  end
88
89
 
@@ -128,6 +129,7 @@ module Airbrake
128
129
  Thread.new do
129
130
  while (message = @queue.pop)
130
131
  break if message == :stop
132
+
131
133
  @block.call(message)
132
134
  end
133
135
  end
@@ -12,6 +12,10 @@ module Airbrake
12
12
  # strings with +ENCODING_OPTIONS+
13
13
  TEMP_ENCODING = 'utf-16'.freeze
14
14
 
15
+ # @return [Array<Encoding>] encodings that are eligible for fixing invalid
16
+ # characters
17
+ SUPPORTED_ENCODINGS = [Encoding::UTF_8, Encoding::ASCII].freeze
18
+
15
19
  # @return [String] what to append when something is a circular reference
16
20
  CIRCULAR = '[Circular]'.freeze
17
21
 
@@ -35,6 +39,7 @@ module Airbrake
35
39
  def truncate(object, seen = Set.new)
36
40
  if seen.include?(object.object_id)
37
41
  return CIRCULAR if CIRCULAR_TYPES.any? { |t| object.is_a?(t) }
42
+
38
43
  return object
39
44
  end
40
45
  truncate_object(object, seen << object.object_id)
@@ -63,6 +68,7 @@ module Airbrake
63
68
  def truncate_string(str)
64
69
  fixed_str = replace_invalid_characters(str)
65
70
  return fixed_str if fixed_str.length <= @max_size
71
+
66
72
  (fixed_str.slice(0, @max_size) + TRUNCATED).freeze
67
73
  end
68
74
 
@@ -76,6 +82,7 @@ module Airbrake
76
82
  truncated_hash = {}
77
83
  hash.each_with_index do |(key, val), idx|
78
84
  break if idx + 1 > @max_size
85
+
79
86
  truncated_hash[key] = truncate(val, seen)
80
87
  end
81
88
 
@@ -103,8 +110,7 @@ module Airbrake
103
110
  # @return [String] a UTF-8 encoded string
104
111
  # @see https://github.com/flori/json/commit/3e158410e81f94dbbc3da6b7b35f4f64983aa4e3
105
112
  def replace_invalid_characters(str)
106
- encoding = str.encoding
107
- utf8_string = (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII)
113
+ utf8_string = SUPPORTED_ENCODINGS.include?(str.encoding)
108
114
  return str if utf8_string && str.valid_encoding?
109
115
 
110
116
  temp_str = str.dup
@@ -2,5 +2,15 @@
2
2
  # More information: http://semver.org/
3
3
  module Airbrake
4
4
  # @return [String] the library version
5
- AIRBRAKE_RUBY_VERSION = '4.13.3'.freeze
5
+ # @api public
6
+ AIRBRAKE_RUBY_VERSION = '5.0.0'.freeze
7
+
8
+ # @return [Hash{Symbol=>String}] the information about the notifier library
9
+ # @since 5.0.0
10
+ # @api public
11
+ NOTIFIER_INFO = {
12
+ name: 'airbrake-ruby'.freeze,
13
+ version: Airbrake::AIRBRAKE_RUBY_VERSION,
14
+ url: 'https://github.com/airbrake/airbrake-ruby'.freeze,
15
+ }.freeze
6
16
  end
@@ -1,4 +1,13 @@
1
1
  RSpec.describe Airbrake do
2
+ let(:remote_settings) { instance_double(Airbrake::RemoteSettings) }
3
+
4
+ before do
5
+ allow(Airbrake::RemoteSettings).to receive(:poll).and_return(remote_settings)
6
+ allow(remote_settings).to receive(:stop_polling)
7
+ end
8
+
9
+ after { described_class.instance_variable_set(:@remote_settings, nil) }
10
+
2
11
  it "gets initialized with a performance notifier" do
3
12
  expect(described_class.performance_notifier).not_to be_nil
4
13
  end
@@ -85,43 +94,55 @@ RSpec.describe Airbrake do
85
94
  expect(described_class.notice_notifier).not_to receive(:add_filter)
86
95
  10.times { described_class.configure {} }
87
96
  end
97
+
98
+ it "appends some default filters" do
99
+ allow(described_class.notice_notifier).to receive(:add_filter)
100
+ expect(described_class.notice_notifier).to receive(:add_filter).with(
101
+ an_instance_of(Airbrake::Filters::RootDirectoryFilter),
102
+ )
103
+
104
+ described_class.configure do |c|
105
+ c.project_id = 1
106
+ c.project_key = '2'
107
+ end
108
+ end
88
109
  end
89
110
 
90
- context "when blacklist_keys gets configured" do
91
- before { allow(Airbrake.notice_notifier).to receive(:add_filter) }
111
+ context "when blocklist_keys gets configured" do
112
+ before { allow(described_class.notice_notifier).to receive(:add_filter) }
92
113
 
93
- it "adds blacklist filter" do
94
- expect(Airbrake.notice_notifier).to receive(:add_filter)
95
- .with(an_instance_of(Airbrake::Filters::KeysBlacklist))
96
- described_class.configure { |c| c.blacklist_keys = %w[password] }
114
+ it "adds blocklist filter" do
115
+ expect(described_class.notice_notifier).to receive(:add_filter)
116
+ .with(an_instance_of(Airbrake::Filters::KeysBlocklist))
117
+ described_class.configure { |c| c.blocklist_keys = %w[password] }
97
118
  end
98
119
 
99
- it "initializes blacklist with specified parameters" do
100
- expect(Airbrake::Filters::KeysBlacklist).to receive(:new).with(%w[password])
101
- described_class.configure { |c| c.blacklist_keys = %w[password] }
120
+ it "initializes blocklist with specified parameters" do
121
+ expect(Airbrake::Filters::KeysBlocklist).to receive(:new).with(%w[password])
122
+ described_class.configure { |c| c.blocklist_keys = %w[password] }
102
123
  end
103
124
  end
104
125
 
105
- context "when whitelist_keys gets configured" do
106
- before { allow(Airbrake.notice_notifier).to receive(:add_filter) }
126
+ context "when allowlist_keys gets configured" do
127
+ before { allow(described_class.notice_notifier).to receive(:add_filter) }
107
128
 
108
- it "adds whitelist filter" do
109
- expect(Airbrake.notice_notifier).to receive(:add_filter)
110
- .with(an_instance_of(Airbrake::Filters::KeysWhitelist))
111
- described_class.configure { |c| c.whitelist_keys = %w[banana] }
129
+ it "adds allowlist filter" do
130
+ expect(described_class.notice_notifier).to receive(:add_filter)
131
+ .with(an_instance_of(Airbrake::Filters::KeysAllowlist))
132
+ described_class.configure { |c| c.allowlist_keys = %w[banana] }
112
133
  end
113
134
 
114
- it "initializes whitelist with specified parameters" do
115
- expect(Airbrake::Filters::KeysWhitelist).to receive(:new).with(%w[banana])
116
- described_class.configure { |c| c.whitelist_keys = %w[banana] }
135
+ it "initializes allowlist with specified parameters" do
136
+ expect(Airbrake::Filters::KeysAllowlist).to receive(:new).with(%w[banana])
137
+ described_class.configure { |c| c.allowlist_keys = %w[banana] }
117
138
  end
118
139
  end
119
140
 
120
141
  context "when root_directory gets configured" do
121
- before { allow(Airbrake.notice_notifier).to receive(:add_filter) }
142
+ before { allow(described_class.notice_notifier).to receive(:add_filter) }
122
143
 
123
144
  it "adds root directory filter" do
124
- expect(Airbrake.notice_notifier).to receive(:add_filter)
145
+ expect(described_class.notice_notifier).to receive(:add_filter)
125
146
  .with(an_instance_of(Airbrake::Filters::RootDirectoryFilter))
126
147
  described_class.configure { |c| c.root_directory = '/my/path' }
127
148
  end
@@ -133,7 +154,7 @@ RSpec.describe Airbrake do
133
154
  end
134
155
 
135
156
  it "adds git revision filter" do
136
- expect(Airbrake.notice_notifier).to receive(:add_filter)
157
+ expect(described_class.notice_notifier).to receive(:add_filter)
137
158
  .with(an_instance_of(Airbrake::Filters::GitRevisionFilter))
138
159
  described_class.configure { |c| c.root_directory = '/my/path' }
139
160
  end
@@ -145,7 +166,7 @@ RSpec.describe Airbrake do
145
166
  end
146
167
 
147
168
  it "adds git repository filter" do
148
- expect(Airbrake.notice_notifier).to receive(:add_filter)
169
+ expect(described_class.notice_notifier).to receive(:add_filter)
149
170
  .with(an_instance_of(Airbrake::Filters::GitRepositoryFilter))
150
171
  described_class.configure { |c| c.root_directory = '/my/path' }
151
172
  end
@@ -157,7 +178,7 @@ RSpec.describe Airbrake do
157
178
  end
158
179
 
159
180
  it "adds git last checkout filter" do
160
- expect(Airbrake.notice_notifier).to receive(:add_filter)
181
+ expect(described_class.notice_notifier).to receive(:add_filter)
161
182
  .with(an_instance_of(Airbrake::Filters::GitLastCheckoutFilter))
162
183
  described_class.configure { |c| c.root_directory = '/my/path' }
163
184
  end
@@ -170,7 +191,7 @@ RSpec.describe Airbrake do
170
191
  end
171
192
  end
172
193
 
173
- describe "#notify_request" do
194
+ describe ".notify_request" do
174
195
  context "when :stash key is not provided" do
175
196
  it "doesn't add anything to the stash of the request" do
176
197
  expect(described_class.performance_notifier).to receive(:notify) do |request|
@@ -205,7 +226,7 @@ RSpec.describe Airbrake do
205
226
  end
206
227
  end
207
228
 
208
- describe "#notify_request_sync" do
229
+ describe ".notify_request_sync" do
209
230
  it "notifies request synchronously" do
210
231
  expect(described_class.performance_notifier).to receive(:notify_sync)
211
232
 
@@ -221,7 +242,7 @@ RSpec.describe Airbrake do
221
242
  end
222
243
  end
223
244
 
224
- describe "#notify_query" do
245
+ describe ".notify_query" do
225
246
  context "when :stash key is not provided" do
226
247
  it "doesn't add anything to the stash of the query" do
227
248
  expect(described_class.performance_notifier).to receive(:notify) do |query|
@@ -256,7 +277,7 @@ RSpec.describe Airbrake do
256
277
  end
257
278
  end
258
279
 
259
- describe "#notify_query_sync" do
280
+ describe ".notify_query_sync" do
260
281
  it "notifies query synchronously" do
261
282
  expect(described_class.performance_notifier).to receive(:notify_sync)
262
283
 
@@ -272,7 +293,7 @@ RSpec.describe Airbrake do
272
293
  end
273
294
  end
274
295
 
275
- describe "#notify_performance_breakdown" do
296
+ describe ".notify_performance_breakdown" do
276
297
  context "when :stash key is not provided" do
277
298
  it "doesn't add anything to the stash of the performance breakdown" do
278
299
  expect(described_class.performance_notifier).to receive(:notify) do |query|
@@ -310,7 +331,7 @@ RSpec.describe Airbrake do
310
331
  end
311
332
  end
312
333
 
313
- describe "#notify_performance_breakdown_sync" do
334
+ describe ".notify_performance_breakdown_sync" do
314
335
  it "notifies performance breakdown synchronously" do
315
336
  expect(described_class.performance_notifier).to receive(:notify_sync)
316
337
 
@@ -327,7 +348,7 @@ RSpec.describe Airbrake do
327
348
  end
328
349
  end
329
350
 
330
- describe "#notify_queue" do
351
+ describe ".notify_queue" do
331
352
  context "when :stash key is not provided" do
332
353
  it "doesn't add anything to the stash of the queue" do
333
354
  expect(described_class.performance_notifier).to receive(:notify) do |queue|
@@ -358,7 +379,7 @@ RSpec.describe Airbrake do
358
379
  end
359
380
  end
360
381
 
361
- describe "#notify_queue_sync" do
382
+ describe ".notify_queue_sync" do
362
383
  it "notifies queue synchronously" do
363
384
  expect(described_class.performance_notifier).to receive(:notify_sync)
364
385
 
@@ -392,33 +413,47 @@ RSpec.describe Airbrake do
392
413
  end
393
414
 
394
415
  describe ".close" do
395
- after { Airbrake.reset }
416
+ after { described_class.reset }
396
417
 
397
418
  context "when notice_notifier is defined" do
398
419
  it "gets closed" do
399
- expect(Airbrake.notice_notifier).to receive(:close)
420
+ expect(described_class.notice_notifier).to receive(:close)
400
421
  end
401
422
  end
402
423
 
403
424
  context "when notice_notifier is undefined" do
404
425
  it "doesn't get closed (because it wasn't initialized)" do
405
- Airbrake.instance_variable_set(:@notice_notifier, nil)
426
+ described_class.instance_variable_set(:@notice_notifier, nil)
406
427
  expect_any_instance_of(Airbrake::NoticeNotifier).not_to receive(:close)
407
428
  end
408
429
  end
409
430
 
410
431
  context "when performance_notifier is defined" do
411
432
  it "gets closed" do
412
- expect(Airbrake.performance_notifier).to receive(:close)
433
+ expect(described_class.performance_notifier).to receive(:close)
413
434
  end
414
435
  end
415
436
 
416
437
  context "when perforance_notifier is undefined" do
417
438
  it "doesn't get closed (because it wasn't initialized)" do
418
- Airbrake.instance_variable_set(:@performance_notifier, nil)
439
+ described_class.instance_variable_set(:@performance_notifier, nil)
419
440
  expect_any_instance_of(Airbrake::PerformanceNotifier)
420
441
  .not_to receive(:close)
421
442
  end
422
443
  end
444
+
445
+ context "when remote settings are defined" do
446
+ it "stops polling" do
447
+ described_class.instance_variable_set(:@remote_settings, remote_settings)
448
+ expect(remote_settings).to receive(:stop_polling)
449
+ end
450
+ end
451
+
452
+ context "when remote settings are undefined" do
453
+ it "doesn't stop polling (because they weren't initialized)" do
454
+ described_class.instance_variable_set(:@remote_settings, nil)
455
+ expect(remote_settings).not_to receive(:stop_polling)
456
+ end
457
+ end
423
458
  end
424
459
  end