airbrake-ruby 4.14.1 → 5.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +23 -35
- data/lib/airbrake-ruby/async_sender.rb +1 -1
- data/lib/airbrake-ruby/backtrace.rb +6 -5
- data/lib/airbrake-ruby/config.rb +37 -13
- data/lib/airbrake-ruby/config/processor.rb +84 -0
- data/lib/airbrake-ruby/config/validator.rb +6 -0
- data/lib/airbrake-ruby/file_cache.rb +1 -1
- data/lib/airbrake-ruby/filter_chain.rb +1 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +1 -2
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
- data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
- data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
- data/lib/airbrake-ruby/filters/keys_filter.rb +26 -18
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +4 -4
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +2 -0
- data/lib/airbrake-ruby/ignorable.rb +1 -0
- data/lib/airbrake-ruby/notice.rb +1 -8
- data/lib/airbrake-ruby/notice_notifier.rb +1 -0
- data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
- data/lib/airbrake-ruby/performance_notifier.rb +2 -15
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +1 -6
- data/lib/airbrake-ruby/queue.rb +1 -8
- data/lib/airbrake-ruby/remote_settings.rb +145 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +121 -0
- data/lib/airbrake-ruby/request.rb +1 -8
- data/lib/airbrake-ruby/stat.rb +1 -12
- data/lib/airbrake-ruby/sync_sender.rb +3 -2
- data/lib/airbrake-ruby/tdigest.rb +2 -0
- data/lib/airbrake-ruby/thread_pool.rb +1 -0
- data/lib/airbrake-ruby/truncator.rb +8 -2
- data/lib/airbrake-ruby/version.rb +11 -1
- data/spec/airbrake_spec.rb +59 -36
- data/spec/backtrace_spec.rb +26 -26
- data/spec/code_hunk_spec.rb +2 -2
- data/spec/config/processor_spec.rb +209 -0
- data/spec/config/validator_spec.rb +18 -1
- data/spec/config_spec.rb +13 -6
- data/spec/filters/gem_root_filter_spec.rb +4 -4
- data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +11 -10
- data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +20 -10
- data/spec/filters/root_directory_filter_spec.rb +4 -4
- data/spec/filters/sql_filter_spec.rb +5 -5
- data/spec/notice_notifier/options_spec.rb +6 -6
- data/spec/notice_notifier_spec.rb +2 -2
- data/spec/notice_spec.rb +1 -1
- data/spec/performance_breakdown_spec.rb +0 -12
- data/spec/performance_notifier_spec.rb +0 -25
- data/spec/query_spec.rb +1 -11
- data/spec/queue_spec.rb +1 -13
- data/spec/remote_settings/settings_data_spec.rb +378 -0
- data/spec/remote_settings_spec.rb +230 -0
- data/spec/request_spec.rb +1 -13
- data/spec/spec_helper.rb +4 -4
- data/spec/stat_spec.rb +0 -9
- data/spec/sync_sender_spec.rb +3 -1
- metadata +21 -12
@@ -0,0 +1,121 @@
|
|
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') &&
|
62
|
+
@data['config_route'] && !@data['config_route'].empty?
|
63
|
+
return format(
|
64
|
+
CONFIG_ROUTE_PATTERN,
|
65
|
+
host: @data['config_route'].chomp('/'),
|
66
|
+
project_id: @project_id,
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
format(
|
71
|
+
CONFIG_ROUTE_PATTERN,
|
72
|
+
host: remote_config_host.chomp('/'),
|
73
|
+
project_id: @project_id,
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Boolean] whether error notifications are enabled
|
78
|
+
def error_notifications?
|
79
|
+
return true unless (s = find_setting(SETTINGS[:errors]))
|
80
|
+
|
81
|
+
s['enabled']
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Boolean] whether APM is enabled
|
85
|
+
def performance_stats?
|
86
|
+
return true unless (s = find_setting(SETTINGS[:apm]))
|
87
|
+
|
88
|
+
s['enabled']
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [String, nil] the host, which provides the API endpoint to which
|
92
|
+
# exceptions should be sent
|
93
|
+
def error_host
|
94
|
+
return unless (s = find_setting(SETTINGS[:errors]))
|
95
|
+
|
96
|
+
s['endpoint']
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [String, nil] the host, which provides the API endpoint to which
|
100
|
+
# APM data should be sent
|
101
|
+
def apm_host
|
102
|
+
return unless (s = find_setting(SETTINGS[:apm]))
|
103
|
+
|
104
|
+
s['endpoint']
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [Hash{String=>Object}] raw representation of JSON payload
|
108
|
+
def to_h
|
109
|
+
@data.dup
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def find_setting(name)
|
115
|
+
return unless @data.key?('settings')
|
116
|
+
|
117
|
+
@data['settings'].find { |s| s['name'] == name }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
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, :
|
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
|
data/lib/airbrake-ruby/stat.rb
CHANGED
@@ -9,7 +9,7 @@ module Airbrake
|
|
9
9
|
#
|
10
10
|
# @example
|
11
11
|
# stat = Airbrake::Stat.new
|
12
|
-
# stat.
|
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.
|
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::
|
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
|
@@ -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
|
-
|
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
|
-
|
5
|
+
# @api public
|
6
|
+
AIRBRAKE_RUBY_VERSION = '5.0.1'.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
|
data/spec/airbrake_spec.rb
CHANGED
@@ -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
|
@@ -99,41 +108,41 @@ RSpec.describe Airbrake do
|
|
99
108
|
end
|
100
109
|
end
|
101
110
|
|
102
|
-
context "when
|
103
|
-
before { allow(
|
111
|
+
context "when blocklist_keys gets configured" do
|
112
|
+
before { allow(described_class.notice_notifier).to receive(:add_filter) }
|
104
113
|
|
105
|
-
it "adds
|
106
|
-
expect(
|
107
|
-
.with(an_instance_of(Airbrake::Filters::
|
108
|
-
described_class.configure { |c| c.
|
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] }
|
109
118
|
end
|
110
119
|
|
111
|
-
it "initializes
|
112
|
-
expect(Airbrake::Filters::
|
113
|
-
described_class.configure { |c| c.
|
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] }
|
114
123
|
end
|
115
124
|
end
|
116
125
|
|
117
|
-
context "when
|
118
|
-
before { allow(
|
126
|
+
context "when allowlist_keys gets configured" do
|
127
|
+
before { allow(described_class.notice_notifier).to receive(:add_filter) }
|
119
128
|
|
120
|
-
it "adds
|
121
|
-
expect(
|
122
|
-
.with(an_instance_of(Airbrake::Filters::
|
123
|
-
described_class.configure { |c| c.
|
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] }
|
124
133
|
end
|
125
134
|
|
126
|
-
it "initializes
|
127
|
-
expect(Airbrake::Filters::
|
128
|
-
described_class.configure { |c| c.
|
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] }
|
129
138
|
end
|
130
139
|
end
|
131
140
|
|
132
141
|
context "when root_directory gets configured" do
|
133
|
-
before { allow(
|
142
|
+
before { allow(described_class.notice_notifier).to receive(:add_filter) }
|
134
143
|
|
135
144
|
it "adds root directory filter" do
|
136
|
-
expect(
|
145
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
137
146
|
.with(an_instance_of(Airbrake::Filters::RootDirectoryFilter))
|
138
147
|
described_class.configure { |c| c.root_directory = '/my/path' }
|
139
148
|
end
|
@@ -145,7 +154,7 @@ RSpec.describe Airbrake do
|
|
145
154
|
end
|
146
155
|
|
147
156
|
it "adds git revision filter" do
|
148
|
-
expect(
|
157
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
149
158
|
.with(an_instance_of(Airbrake::Filters::GitRevisionFilter))
|
150
159
|
described_class.configure { |c| c.root_directory = '/my/path' }
|
151
160
|
end
|
@@ -157,7 +166,7 @@ RSpec.describe Airbrake do
|
|
157
166
|
end
|
158
167
|
|
159
168
|
it "adds git repository filter" do
|
160
|
-
expect(
|
169
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
161
170
|
.with(an_instance_of(Airbrake::Filters::GitRepositoryFilter))
|
162
171
|
described_class.configure { |c| c.root_directory = '/my/path' }
|
163
172
|
end
|
@@ -169,7 +178,7 @@ RSpec.describe Airbrake do
|
|
169
178
|
end
|
170
179
|
|
171
180
|
it "adds git last checkout filter" do
|
172
|
-
expect(
|
181
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
173
182
|
.with(an_instance_of(Airbrake::Filters::GitLastCheckoutFilter))
|
174
183
|
described_class.configure { |c| c.root_directory = '/my/path' }
|
175
184
|
end
|
@@ -182,7 +191,7 @@ RSpec.describe Airbrake do
|
|
182
191
|
end
|
183
192
|
end
|
184
193
|
|
185
|
-
describe "
|
194
|
+
describe ".notify_request" do
|
186
195
|
context "when :stash key is not provided" do
|
187
196
|
it "doesn't add anything to the stash of the request" do
|
188
197
|
expect(described_class.performance_notifier).to receive(:notify) do |request|
|
@@ -217,7 +226,7 @@ RSpec.describe Airbrake do
|
|
217
226
|
end
|
218
227
|
end
|
219
228
|
|
220
|
-
describe "
|
229
|
+
describe ".notify_request_sync" do
|
221
230
|
it "notifies request synchronously" do
|
222
231
|
expect(described_class.performance_notifier).to receive(:notify_sync)
|
223
232
|
|
@@ -233,7 +242,7 @@ RSpec.describe Airbrake do
|
|
233
242
|
end
|
234
243
|
end
|
235
244
|
|
236
|
-
describe "
|
245
|
+
describe ".notify_query" do
|
237
246
|
context "when :stash key is not provided" do
|
238
247
|
it "doesn't add anything to the stash of the query" do
|
239
248
|
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
@@ -268,7 +277,7 @@ RSpec.describe Airbrake do
|
|
268
277
|
end
|
269
278
|
end
|
270
279
|
|
271
|
-
describe "
|
280
|
+
describe ".notify_query_sync" do
|
272
281
|
it "notifies query synchronously" do
|
273
282
|
expect(described_class.performance_notifier).to receive(:notify_sync)
|
274
283
|
|
@@ -284,7 +293,7 @@ RSpec.describe Airbrake do
|
|
284
293
|
end
|
285
294
|
end
|
286
295
|
|
287
|
-
describe "
|
296
|
+
describe ".notify_performance_breakdown" do
|
288
297
|
context "when :stash key is not provided" do
|
289
298
|
it "doesn't add anything to the stash of the performance breakdown" do
|
290
299
|
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
@@ -322,7 +331,7 @@ RSpec.describe Airbrake do
|
|
322
331
|
end
|
323
332
|
end
|
324
333
|
|
325
|
-
describe "
|
334
|
+
describe ".notify_performance_breakdown_sync" do
|
326
335
|
it "notifies performance breakdown synchronously" do
|
327
336
|
expect(described_class.performance_notifier).to receive(:notify_sync)
|
328
337
|
|
@@ -339,7 +348,7 @@ RSpec.describe Airbrake do
|
|
339
348
|
end
|
340
349
|
end
|
341
350
|
|
342
|
-
describe "
|
351
|
+
describe ".notify_queue" do
|
343
352
|
context "when :stash key is not provided" do
|
344
353
|
it "doesn't add anything to the stash of the queue" do
|
345
354
|
expect(described_class.performance_notifier).to receive(:notify) do |queue|
|
@@ -370,7 +379,7 @@ RSpec.describe Airbrake do
|
|
370
379
|
end
|
371
380
|
end
|
372
381
|
|
373
|
-
describe "
|
382
|
+
describe ".notify_queue_sync" do
|
374
383
|
it "notifies queue synchronously" do
|
375
384
|
expect(described_class.performance_notifier).to receive(:notify_sync)
|
376
385
|
|
@@ -404,33 +413,47 @@ RSpec.describe Airbrake do
|
|
404
413
|
end
|
405
414
|
|
406
415
|
describe ".close" do
|
407
|
-
after {
|
416
|
+
after { described_class.reset }
|
408
417
|
|
409
418
|
context "when notice_notifier is defined" do
|
410
419
|
it "gets closed" do
|
411
|
-
expect(
|
420
|
+
expect(described_class.notice_notifier).to receive(:close)
|
412
421
|
end
|
413
422
|
end
|
414
423
|
|
415
424
|
context "when notice_notifier is undefined" do
|
416
425
|
it "doesn't get closed (because it wasn't initialized)" do
|
417
|
-
|
426
|
+
described_class.instance_variable_set(:@notice_notifier, nil)
|
418
427
|
expect_any_instance_of(Airbrake::NoticeNotifier).not_to receive(:close)
|
419
428
|
end
|
420
429
|
end
|
421
430
|
|
422
431
|
context "when performance_notifier is defined" do
|
423
432
|
it "gets closed" do
|
424
|
-
expect(
|
433
|
+
expect(described_class.performance_notifier).to receive(:close)
|
425
434
|
end
|
426
435
|
end
|
427
436
|
|
428
437
|
context "when perforance_notifier is undefined" do
|
429
438
|
it "doesn't get closed (because it wasn't initialized)" do
|
430
|
-
|
439
|
+
described_class.instance_variable_set(:@performance_notifier, nil)
|
431
440
|
expect_any_instance_of(Airbrake::PerformanceNotifier)
|
432
441
|
.not_to receive(:close)
|
433
442
|
end
|
434
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
|
435
458
|
end
|
436
459
|
end
|