airbrake-ruby 2.2.3 → 2.2.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 03b38987d8ca77dcd6c8302246192bd1a9184854
4
- data.tar.gz: 93f4e52998430af6c6bcce0bca5e1b6dae3202ae
3
+ metadata.gz: 076ef6e377b4e2fbc3a5cce600393a13390da43c
4
+ data.tar.gz: 8b31cc49491997971856b5c2d1480c00881f5058
5
5
  SHA512:
6
- metadata.gz: 9f2ae08f59cb532ca0313d7d16eebab0ea9e54c5796048c57fc6418d2c7b18d37744450651f4743d4147b7288aba6f25ae0d8db88d0135104a50d35444d59be1
7
- data.tar.gz: 0a327f231fea3629de145c84570c17f48a5f0e9c37050ed0a3f53ab8cea9d59d83c17c03066c4e025051d6f0870b9b0e5591345e1d76f554a42744b0a55949b0
6
+ metadata.gz: b65105fb50ef5098b1bbfa489387e48f785fcdd8ba25dc7ac306c817385719b8fb9af684b11c447a98740dcc1a2cee17fc65376bc6934150f7f26e268c8ea88d
7
+ data.tar.gz: 8ef30b2ef9aa0a652de4c9ef1a3a0a9f8b7d60018f9441bae0e7ebe16c79fe0a793290b666132b4adf447bfcdfc4ec9c6610459471f02d9ec0d1cd5b0cabc1ad
data/lib/airbrake-ruby.rb CHANGED
@@ -15,8 +15,7 @@ require 'airbrake-ruby/response'
15
15
  require 'airbrake-ruby/nested_exception'
16
16
  require 'airbrake-ruby/notice'
17
17
  require 'airbrake-ruby/backtrace'
18
- require 'airbrake-ruby/payload_truncator'
19
- require 'airbrake-ruby/filters'
18
+ require 'airbrake-ruby/truncator'
20
19
  require 'airbrake-ruby/filters/keys_filter'
21
20
  require 'airbrake-ruby/filters/keys_whitelist'
22
21
  require 'airbrake-ruby/filters/keys_blacklist'
@@ -19,33 +19,9 @@ module Airbrake
19
19
  # @return [Integer]
20
20
  DEFAULT_WEIGHT = 0
21
21
 
22
- ##
23
- # @param [Airbrake::Config] config
24
- def initialize(config)
22
+ def initialize
25
23
  @filters = []
26
-
27
24
  DEFAULT_FILTERS.each { |f| add_filter(f.new) }
28
-
29
- if config.whitelist_keys.any?
30
- add_filter(
31
- Airbrake::Filters::KeysWhitelist.new(
32
- config.logger,
33
- config.whitelist_keys
34
- )
35
- )
36
- end
37
-
38
- if config.blacklist_keys.any?
39
- add_filter(
40
- Airbrake::Filters::KeysBlacklist.new(
41
- config.logger,
42
- config.blacklist_keys
43
- )
44
- )
45
- end
46
-
47
- return unless (root_directory = config.root_directory)
48
- add_filter(Airbrake::Filters::RootDirectoryFilter.new(root_directory))
49
25
  end
50
26
 
51
27
  ##
@@ -18,6 +18,11 @@ module Airbrake
18
18
  # which can compared with payload keys
19
19
  VALID_PATTERN_CLASSES = [String, Symbol, Regexp].freeze
20
20
 
21
+ ##
22
+ # @return [Array<Symbol>] parts of a Notice's payload that can be modified
23
+ # by blacklist/whitelist filters
24
+ FILTERABLE_KEYS = %i[environment session params].freeze
25
+
21
26
  ##
22
27
  # @return [Integer]
23
28
  attr_reader :weight
@@ -9,13 +9,16 @@ module Airbrake
9
9
  attr_reader :weight
10
10
 
11
11
  ##
12
- # @return [Array<Symbol>] the list of ignored fiber variables
13
- IGNORED_FIBER_VARIABLES = [
14
- # https://github.com/airbrake/airbrake-ruby/issues/204
15
- :__recursive_key__,
16
-
17
- # https://github.com/rails/rails/issues/28996
18
- :__rspec
12
+ # @return [Array<Class>] the list of classes that can be safely converted
13
+ # to JSON
14
+ SAFE_CLASSES = [
15
+ NilClass,
16
+ TrueClass,
17
+ FalseClass,
18
+ String,
19
+ Symbol,
20
+ Regexp,
21
+ Numeric
19
22
  ].freeze
20
23
 
21
24
  def initialize
@@ -48,16 +51,13 @@ module Airbrake
48
51
 
49
52
  def thread_variables(th)
50
53
  th.thread_variables.map.with_object({}) do |var, h|
51
- next if (value = th.thread_variable_get(var)).is_a?(IO)
52
- h[var] = value
54
+ h[var] = sanitize_value(th.thread_variable_get(var))
53
55
  end
54
56
  end
55
57
 
56
58
  def fiber_variables(th)
57
59
  th.keys.map.with_object({}) do |key, h|
58
- next if IGNORED_FIBER_VARIABLES.any? { |v| v == key }
59
- next if (value = th[key]).is_a?(IO)
60
- h[key] = value
60
+ h[key] = sanitize_value(th[key])
61
61
  end
62
62
  end
63
63
 
@@ -68,6 +68,19 @@ module Airbrake
68
68
 
69
69
  thread_info[:safe_level] = th.safe_level unless Airbrake::JRUBY
70
70
  end
71
+
72
+ def sanitize_value(value)
73
+ return value if SAFE_CLASSES.any? { |klass| value.is_a?(klass) }
74
+
75
+ case value
76
+ when Array
77
+ value = value.map { |elem| sanitize_value(elem) }
78
+ when Hash
79
+ Hash[value.map { |k, v| [k, sanitize_value(v)] }]
80
+ else
81
+ value.to_s
82
+ end
83
+ end
71
84
  end
72
85
  end
73
86
  end
@@ -4,7 +4,6 @@ module Airbrake
4
4
  # Airbrake or ignored completely.
5
5
  #
6
6
  # @since v1.0.0
7
- # rubocop:disable Metrics/ClassLength
8
7
  class Notice
9
8
  ##
10
9
  # @return [Hash{Symbol=>String}] the information about the notifier library
@@ -44,13 +43,12 @@ module Airbrake
44
43
 
45
44
  # @return [Array<Symbol>] the list of keys that can be be overwritten with
46
45
  # {Airbrake::Notice#[]=}
47
- WRITABLE_KEYS = %i[
48
- notifier
49
- context
50
- environment
51
- session
52
- params
53
- ].freeze
46
+ WRITABLE_KEYS = %i[notifier context environment session params].freeze
47
+
48
+ ##
49
+ # @return [Array<Symbol>] parts of a Notice's payload that can be modified
50
+ # by the truncator
51
+ TRUNCATABLE_KEYS = %i[errors environment session params].freeze
54
52
 
55
53
  ##
56
54
  # @return [String] the name of the host machine
@@ -77,10 +75,9 @@ module Airbrake
77
75
  params: params
78
76
  }
79
77
  @stash = {}
78
+ @truncator = Airbrake::Truncator.new(PAYLOAD_MAX_SIZE)
80
79
 
81
80
  extract_custom_attributes(exception)
82
-
83
- @truncator = PayloadTruncator.new(PAYLOAD_MAX_SIZE, @config.logger)
84
81
  end
85
82
 
86
83
  ##
@@ -99,7 +96,7 @@ module Airbrake
99
96
  return json if json && json.bytesize <= MAX_NOTICE_SIZE
100
97
  end
101
98
 
102
- break if truncate_payload.zero?
99
+ break if truncate == 0
103
100
  end
104
101
  end
105
102
 
@@ -184,17 +181,11 @@ module Airbrake
184
181
  raise Airbrake::Error, "Got #{value.class} value, wanted a Hash"
185
182
  end
186
183
 
187
- def truncate_payload
188
- @payload[:errors].each do |error|
189
- @truncator.truncate_error(error)
190
- end
191
-
192
- Filters::FILTERABLE_KEYS.each do |key|
193
- @truncator.truncate_object(@payload[key])
194
- end
184
+ def truncate
185
+ TRUNCATABLE_KEYS.each { |key| @truncator.truncate_object(self[key]) }
195
186
 
196
187
  new_max_size = @truncator.reduce_max_size
197
- if new_max_size.zero?
188
+ if new_max_size == 0
198
189
  @config.logger.error(
199
190
  "#{LOG_LABEL} truncation failed. File an issue at " \
200
191
  "https://github.com/airbrake/airbrake-ruby " \
@@ -229,5 +220,4 @@ module Airbrake
229
220
  end
230
221
  end
231
222
  end
232
- # rubocop:enable Metrics/ClassLength
233
223
  end
@@ -33,7 +33,8 @@ module Airbrake
33
33
  raise Airbrake::Error, @config.validation_error_message
34
34
  end
35
35
 
36
- @filter_chain = FilterChain.new(@config)
36
+ @filter_chain = FilterChain.new
37
+ add_default_filters
37
38
 
38
39
  @async_sender = AsyncSender.new(@config)
39
40
  @sync_sender = SyncSender.new(@config)
@@ -145,6 +146,25 @@ module Airbrake
145
146
  return caller_copy if clean_bt.empty?
146
147
  clean_bt
147
148
  end
149
+
150
+ def add_default_filters
151
+ if (whitelist_keys = @config.whitelist_keys).any?
152
+ @filter_chain.add_filter(
153
+ Airbrake::Filters::KeysWhitelist.new(@config.logger, whitelist_keys)
154
+ )
155
+ end
156
+
157
+ if (blacklist_keys = @config.blacklist_keys).any?
158
+ @filter_chain.add_filter(
159
+ Airbrake::Filters::KeysBlacklist.new(@config.logger, blacklist_keys)
160
+ )
161
+ end
162
+
163
+ return unless (root_directory = @config.root_directory)
164
+ @filter_chain.add_filter(
165
+ Airbrake::Filters::RootDirectoryFilter.new(root_directory)
166
+ )
167
+ end
148
168
  end
149
169
 
150
170
  ##
@@ -153,13 +173,13 @@ module Airbrake
153
173
  #
154
174
  # @since 2.1.0
155
175
  class NilNotifier
156
- def notify(_exception, _params = {}); end
176
+ def notify(_exception, _params = {}, &block); end
157
177
 
158
- def notify_sync(_exception, _params); end
178
+ def notify_sync(_exception, _params = {}, &block); end
159
179
 
160
180
  def add_filter(_filter = nil, &_block); end
161
181
 
162
- def build_notice(_exception, _params); end
182
+ def build_notice(_exception, _params = {}); end
163
183
 
164
184
  def close; end
165
185
 
@@ -5,7 +5,7 @@ module Airbrake
5
5
  #
6
6
  # @api private
7
7
  # @since v1.0.0
8
- class PayloadTruncator
8
+ class Truncator
9
9
  ##
10
10
  # @return [Hash] the options for +String#encode+
11
11
  ENCODING_OPTIONS = { invalid: :replace, undef: :replace }.freeze
@@ -17,29 +17,8 @@ module Airbrake
17
17
 
18
18
  ##
19
19
  # @param [Integer] max_size maximum size of hashes, arrays and strings
20
- # @param [Logger] logger the logger object
21
- def initialize(max_size, logger)
20
+ def initialize(max_size)
22
21
  @max_size = max_size
23
- @logger = logger
24
- end
25
-
26
- ##
27
- # Truncates errors (not exceptions) to fit the limit.
28
- #
29
- # @param [Hash] error
30
- # @option error [Symbol] :message
31
- # @option error [Array<String>] :backtrace
32
- # @return [void]
33
- def truncate_error(error)
34
- if error[:message].length > @max_size
35
- error[:message] = truncate_string(error[:message])
36
- @logger.info("#{LOG_LABEL} truncated the message of #{error[:type]}")
37
- end
38
-
39
- return if (dropped_frames = error[:backtrace].size - @max_size) < 0
40
-
41
- error[:backtrace] = error[:backtrace].slice(0, @max_size)
42
- @logger.info("#{LOG_LABEL} dropped #{dropped_frames} frame(s) from #{error[:type]}")
43
22
  end
44
23
 
45
24
  ##
@@ -49,26 +28,29 @@ module Airbrake
49
28
  # @param [Hash,Array] object The object to truncate
50
29
  # @param [Hash] seen The hash that helps to detect recursion
51
30
  # @return [void]
31
+ # @note This method is public to simplify testing. You probably want to use
32
+ # {truncate_notice} instead
52
33
  def truncate_object(object, seen = {})
53
34
  return seen[object] if seen[object]
54
35
 
55
36
  seen[object] = '[Circular]'.freeze
56
- truncated = if object.is_a?(Hash)
57
- truncate_hash(object, seen)
58
- elsif object.is_a?(Array)
59
- truncate_array(object, seen)
60
- elsif object.is_a?(Set)
61
- truncate_set(object, seen)
62
- else
63
- raise Airbrake::Error,
64
- "cannot truncate object: #{object} (#{object.class})"
65
- end
37
+ truncated =
38
+ if object.is_a?(Hash)
39
+ truncate_hash(object, seen)
40
+ elsif object.is_a?(Array)
41
+ truncate_array(object, seen)
42
+ elsif object.is_a?(Set)
43
+ truncate_set(object, seen)
44
+ else
45
+ raise Airbrake::Error,
46
+ "cannot truncate object: #{object} (#{object.class})"
47
+ end
66
48
  seen[object] = truncated
67
49
  end
68
50
 
69
51
  ##
70
52
  # Reduces maximum allowed size of the truncated object.
71
- # @return [void]
53
+ # @return [Integer] current +max_size+ value
72
54
  def reduce_max_size
73
55
  @max_size /= 2
74
56
  end
@@ -84,11 +66,12 @@ module Airbrake
84
66
  when Numeric, TrueClass, FalseClass, Symbol, NilClass
85
67
  val
86
68
  else
87
- stringified_val = begin
88
- val.to_json
89
- rescue *Notice::JSON_EXCEPTIONS
90
- val.to_s
91
- end
69
+ stringified_val =
70
+ begin
71
+ val.to_json
72
+ rescue *Notice::JSON_EXCEPTIONS
73
+ val.to_s
74
+ end
92
75
  truncate_string(stringified_val)
93
76
  end
94
77
  end
@@ -4,5 +4,5 @@
4
4
  module Airbrake
5
5
  ##
6
6
  # @return [String] the library version
7
- AIRBRAKE_RUBY_VERSION = '2.2.3'.freeze
7
+ AIRBRAKE_RUBY_VERSION = '2.2.4'.freeze
8
8
  end
@@ -1,222 +1,46 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Airbrake::FilterChain do
4
- before do
5
- @chain = described_class.new(config)
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
6
  end
7
7
 
8
- let(:config) { Airbrake::Config.new }
9
-
10
8
  describe "#refine" do
11
- describe "execution order" do
12
- let(:notice) do
13
- Airbrake::Notice.new(config, AirbrakeTestError.new)
14
- end
15
-
16
- it "executes keys filters last" do
17
- notice[:params] = { bingo: 'bango' }
18
- config.blacklist_keys = [:bingo]
19
- @chain = described_class.new(config)
20
-
21
- @chain.add_filter(
22
- proc do |notice|
23
- expect(notice[:params][:bingo]).to eq('bango')
24
- end
25
- )
26
-
27
- @chain.refine(notice)
28
- expect(notice[:params][:bingo]).to eq('[Filtered]')
29
- end
30
-
31
- describe "filter weight" do
32
- let(:filter) do
33
- Class.new do
34
- attr_reader :weight
9
+ let(:filter) do
10
+ Class.new do
11
+ attr_reader :weight
35
12
 
36
- def initialize(weight)
37
- @weight = weight
38
- end
39
-
40
- def call(notice)
41
- notice[:params][:bingo] << @weight
42
- end
43
- end
13
+ def initialize(weight)
14
+ @weight = weight
44
15
  end
45
16
 
46
- it "executes filters from heaviest to lightest" do
47
- notice[:params][:bingo] = []
48
-
49
- (0...3).reverse_each do |i|
50
- @chain.add_filter(filter.new(i))
51
- end
52
- @chain.refine(notice)
53
-
54
- expect(notice[:params][:bingo]).to eq([2, 1, 0])
55
- end
56
-
57
- it "stops execution once a notice was ignored" do
58
- f2 = filter.new(2)
59
- expect(f2).to receive(:call)
60
-
61
- f1 = proc { |notice| notice.ignore! }
62
-
63
- f0 = filter.new(-1)
64
- expect(f0).not_to receive(:call)
65
-
66
- [f2, f1, f0].each { |f| @chain.add_filter(f) }
67
-
68
- @chain.refine(notice)
17
+ def call(notice)
18
+ notice[:params][:bingo] << @weight
69
19
  end
70
20
  end
71
21
  end
72
22
 
73
- describe "default backtrace filters" do
74
- let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(backtrace) } }
75
- let(:notice) { Airbrake::Notice.new(config, ex) }
76
-
77
- before do
78
- Gem.path << '/my/gem/root' << '/my/other/gem/root'
79
- @chain.refine(notice)
80
- @bt = notice[:errors].first[:backtrace].map { |frame| frame[:file] }
81
- end
82
-
83
- shared_examples 'root directories' do |root_directory, bt, expected_bt|
84
- let(:backtrace) { bt }
85
-
86
- before do
87
- config = Airbrake::Config.new(root_directory: root_directory)
88
- chain = described_class.new(config)
89
- chain.refine(notice)
90
- @bt = notice[:errors].first[:backtrace].map { |frame| frame[:file] }
91
- end
92
-
93
- it "filters it out" do
94
- expect(@bt).to eq(expected_bt)
95
- end
96
- end
97
-
98
- # rubocop:disable Metrics/LineLength
99
- context "gem root" do
100
- bt = [
101
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
102
- "/my/gem/root/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
103
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
104
- "/my/other/gem/root/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
105
- ]
23
+ it "executes filters from heaviest to lightest" do
24
+ notice[:params][:bingo] = []
106
25
 
107
- expected_bt = [
108
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
109
- "[GEM_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
110
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
111
- "[GEM_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
112
- ]
26
+ (0...3).reverse_each { |i| subject.add_filter(filter.new(i)) }
27
+ subject.refine(notice)
113
28
 
114
- include_examples 'root directories', nil, bt, expected_bt
115
- end
116
-
117
- context "root directory" do
118
- context "when normal string path" do
119
- bt = [
120
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
121
- "/var/www/project/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
122
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
123
- "/var/www/project/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
124
- ]
125
-
126
- expected_bt = [
127
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
128
- "[PROJECT_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
129
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
130
- "[PROJECT_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
131
- ]
132
-
133
- include_examples 'root directories', '/var/www/project', bt, expected_bt
134
- end
135
-
136
- context "when equals to a part of filename" do
137
- bt = [
138
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
139
- "/var/www/gems/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
140
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
141
- "/var/www/gems/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
142
- ]
143
-
144
- expected_bt = [
145
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
146
- "[PROJECT_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
147
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
148
- "[PROJECT_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
149
- ]
150
-
151
- include_examples 'root directories', '/var/www/gems', bt, expected_bt
152
- end
153
-
154
- context "when normal pathname path" do
155
- bt = [
156
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
157
- "/var/www/project/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
158
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
159
- "/var/www/project/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
160
- ]
161
-
162
- expected_bt = [
163
- "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
164
- "[PROJECT_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
165
- "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
166
- "[PROJECT_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
167
- ]
168
-
169
- include_examples 'root directories',
170
- Pathname.new('/var/www/project'), bt, expected_bt
171
- end
172
- end
173
- # rubocop:enable Metrics/LineLength
29
+ expect(notice[:params][:bingo]).to eq([2, 1, 0])
174
30
  end
175
31
 
176
- describe "default ignore filters" do
177
- context "system exit filter" do
178
- it "marks SystemExit exceptions as ignored" do
179
- notice = Airbrake::Notice.new(config, SystemExit.new)
180
- expect { @chain.refine(notice) }.
181
- to(change { notice.ignored? }.from(false).to(true))
182
- end
183
- end
184
-
185
- context "gem root filter" do
186
- let(:ex) do
187
- AirbrakeTestError.new.tap do |error|
188
- error.set_backtrace(['(unparseable/frame.rb:23)'])
189
- end
190
- end
191
-
192
- it "does not filter file if it is nil" do
193
- config.logger = Logger.new('/dev/null')
194
- notice = Airbrake::Notice.new(config, ex)
32
+ it "stops execution once a notice was ignored" do
33
+ f2 = filter.new(2)
34
+ expect(f2).to receive(:call)
195
35
 
196
- expect(notice[:errors].first[:file]).to be_nil
197
- expect { @chain.refine(notice) }.
198
- not_to(change { notice[:errors].first[:file] })
199
- end
200
- end
36
+ f1 = proc { |notice| notice.ignore! }
201
37
 
202
- context "root directory filter" do
203
- let(:ex) do
204
- AirbrakeTestError.new.tap do |error|
205
- error.set_backtrace(['(unparseable/frame.rb:23)'])
206
- end
207
- end
38
+ f0 = filter.new(-1)
39
+ expect(f0).not_to receive(:call)
208
40
 
209
- it "does not filter file if it is nil" do
210
- config.logger = Logger.new('/dev/null')
211
- config.root_directory = '/bingo/bango'
212
- notice = Airbrake::Notice.new(config, ex)
213
- filter_chain = described_class.new(config)
41
+ [f2, f1, f0].each { |f| subject.add_filter(f) }
214
42
 
215
- expect(notice[:errors].first[:file]).to be_nil
216
- expect { filter_chain.refine(notice) }.
217
- not_to(change { notice[:errors].first[:file] })
218
- end
219
- end
43
+ subject.refine(notice)
220
44
  end
221
45
  end
222
46
  end