airbrake-ruby 4.13.3-java → 5.0.0-java

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 (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