airbrake-ruby 3.2.6 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +31 -138
  3. data/lib/airbrake-ruby/async_sender.rb +20 -8
  4. data/lib/airbrake-ruby/backtrace.rb +15 -13
  5. data/lib/airbrake-ruby/code_hunk.rb +2 -4
  6. data/lib/airbrake-ruby/config.rb +8 -38
  7. data/lib/airbrake-ruby/deploy_notifier.rb +4 -17
  8. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +5 -4
  9. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +6 -4
  10. data/lib/airbrake-ruby/filters/keys_blacklist.rb +0 -1
  11. data/lib/airbrake-ruby/filters/keys_filter.rb +4 -4
  12. data/lib/airbrake-ruby/filters/keys_whitelist.rb +0 -1
  13. data/lib/airbrake-ruby/loggable.rb +31 -0
  14. data/lib/airbrake-ruby/nested_exception.rb +2 -3
  15. data/lib/airbrake-ruby/notice.rb +6 -6
  16. data/lib/airbrake-ruby/notice_notifier.rb +11 -47
  17. data/lib/airbrake-ruby/performance_notifier.rb +6 -18
  18. data/lib/airbrake-ruby/response.rb +5 -2
  19. data/lib/airbrake-ruby/sync_sender.rb +8 -6
  20. data/lib/airbrake-ruby/version.rb +1 -1
  21. data/spec/airbrake_spec.rb +1 -143
  22. data/spec/async_sender_spec.rb +83 -90
  23. data/spec/backtrace_spec.rb +36 -47
  24. data/spec/code_hunk_spec.rb +12 -15
  25. data/spec/config_spec.rb +79 -96
  26. data/spec/deploy_notifier_spec.rb +3 -7
  27. data/spec/filter_chain_spec.rb +1 -3
  28. data/spec/filters/context_filter_spec.rb +1 -3
  29. data/spec/filters/dependency_filter_spec.rb +1 -3
  30. data/spec/filters/exception_attributes_filter_spec.rb +1 -14
  31. data/spec/filters/gem_root_filter_spec.rb +1 -4
  32. data/spec/filters/git_last_checkout_filter_spec.rb +3 -5
  33. data/spec/filters/git_revision_filter_spec.rb +1 -3
  34. data/spec/filters/keys_blacklist_spec.rb +14 -25
  35. data/spec/filters/keys_whitelist_spec.rb +14 -25
  36. data/spec/filters/root_directory_filter_spec.rb +1 -4
  37. data/spec/filters/system_exit_filter_spec.rb +2 -2
  38. data/spec/filters/thread_filter_spec.rb +1 -3
  39. data/spec/nested_exception_spec.rb +3 -5
  40. data/spec/notice_notifier_spec.rb +23 -20
  41. data/spec/notice_notifier_spec/options_spec.rb +20 -25
  42. data/spec/notice_spec.rb +13 -12
  43. data/spec/performance_notifier_spec.rb +19 -31
  44. data/spec/response_spec.rb +23 -17
  45. data/spec/sync_sender_spec.rb +26 -33
  46. metadata +2 -1
@@ -1,9 +1,5 @@
1
1
  RSpec.describe Airbrake::DeployNotifier do
2
- let(:user_params) { { project_id: 1, project_key: 'banana' } }
3
- let(:params) { {} }
4
- let(:config) { Airbrake::Config.new(user_params.merge(params)) }
5
-
6
- subject { described_class.new(config) }
2
+ before { Airbrake::Config.instance = Airbrake::Config.new(project_id: 1) }
7
3
 
8
4
  describe "#notify" do
9
5
  it "returns a promise" do
@@ -13,7 +9,7 @@ RSpec.describe Airbrake::DeployNotifier do
13
9
  end
14
10
 
15
11
  context "when environment is configured" do
16
- let(:params) { { environment: 'fooenv' } }
12
+ before { Airbrake::Config.instance.merge(environment: 'fooenv') }
17
13
 
18
14
  it "prefers the passed environment to the config env" do
19
15
  expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
@@ -26,7 +22,7 @@ RSpec.describe Airbrake::DeployNotifier do
26
22
  end
27
23
 
28
24
  context "when environment is not configured" do
29
- let(:params) { { environment: 'fooenv' } }
25
+ before { Airbrake::Config.instance.merge(environment: 'fooenv') }
30
26
 
31
27
  it "sets the environment from the config" do
32
28
  expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
@@ -1,7 +1,5 @@
1
1
  RSpec.describe Airbrake::FilterChain do
2
- let(:notice) do
3
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
- end
2
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
5
3
 
6
4
  describe "#refine" do
7
5
  let(:filter) do
@@ -1,7 +1,5 @@
1
1
  RSpec.describe Airbrake::Filters::ContextFilter do
2
- let(:notice) do
3
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
- end
2
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
5
3
 
6
4
  context "when the current context is empty" do
7
5
  it "doesn't merge anything with params" do
@@ -1,7 +1,5 @@
1
1
  RSpec.describe Airbrake::Filters::DependencyFilter do
2
- let(:notice) do
3
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
- end
2
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
5
3
 
6
4
  describe "#call" do
7
5
  it "attaches loaded dependencies to context/versions/dependencies" do
@@ -1,9 +1,6 @@
1
1
  RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
2
2
  describe "#call" do
3
- let(:out) { StringIO.new }
4
- let(:notice) { Airbrake::Notice.new(Airbrake::Config.new, ex) }
5
-
6
- subject { described_class.new(Logger.new(out)) }
3
+ let(:notice) { Airbrake::Notice.new(ex) }
7
4
 
8
5
  context "when #to_airbrake returns a non-Hash object" do
9
6
  let(:ex) do
@@ -18,11 +15,6 @@ RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
18
15
  expect { subject.call(notice) }.not_to raise_error
19
16
  expect(notice[:params]).to be_empty
20
17
  end
21
-
22
- it "logs the error" do
23
- expect { subject.call(notice) }.not_to raise_error
24
- expect(out.string).to match(/wanted Hash, got Object/)
25
- end
26
18
  end
27
19
 
28
20
  context "when #to_airbrake errors out" do
@@ -38,11 +30,6 @@ RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
38
30
  expect { subject.call(notice) }.not_to raise_error
39
31
  expect(notice[:params]).to be_empty
40
32
  end
41
-
42
- it "logs the error" do
43
- expect { subject.call(notice) }.not_to raise_error
44
- expect(out.string).to match(/#to_airbrake failed.+ZeroDivisionError/)
45
- end
46
33
  end
47
34
 
48
35
  context "when #to_airbrake returns a hash" do
@@ -1,8 +1,5 @@
1
1
  RSpec.describe Airbrake::Filters::GemRootFilter do
2
- let(:notice) do
3
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
- end
5
-
2
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
6
3
  let(:root1) { '/my/gem/root' }
7
4
  let(:root2) { '/my/other/gem/root' }
8
5
 
@@ -1,9 +1,7 @@
1
1
  RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
2
- subject { described_class.new(Logger.new(STDOUT), '.') }
2
+ subject { described_class.new('.') }
3
3
 
4
- let(:notice) do
5
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
- end
4
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
7
5
 
8
6
  context "when context/lastCheckout is defined" do
9
7
  it "doesn't attach anything to context/lastCheckout" do
@@ -14,7 +12,7 @@ RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
14
12
  end
15
13
 
16
14
  context "when .git directory doesn't exist" do
17
- subject { described_class.new(Logger.new(STDOUT), 'root/dir') }
15
+ subject { described_class.new('root/dir') }
18
16
 
19
17
  it "doesn't attach anything to context/lastCheckout" do
20
18
  subject.call(notice)
@@ -1,9 +1,7 @@
1
1
  RSpec.describe Airbrake::Filters::GitRevisionFilter do
2
2
  subject { described_class.new('root/dir') }
3
3
 
4
- let(:notice) do
5
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
- end
4
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
7
5
 
8
6
  context "when context/revision is defined" do
9
7
  it "doesn't attach anything to context/revision" do
@@ -1,9 +1,7 @@
1
1
  RSpec.describe Airbrake::Filters::KeysBlacklist do
2
- subject { described_class.new(Logger.new('/dev/null'), patterns) }
2
+ subject { described_class.new(patterns) }
3
3
 
4
- let(:notice) do
5
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
- end
4
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
7
5
 
8
6
  shared_examples 'pattern matching' do |patts, params|
9
7
  let(:patterns) { patts }
@@ -92,14 +90,11 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
92
90
  )
93
91
 
94
92
  it "logs an error" do
95
- out = StringIO.new
96
- logger = Logger.new(out)
97
- keys_blacklist = described_class.new(logger, patterns)
98
- keys_blacklist.call(notice)
99
-
100
- expect(out.string).to(
101
- match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/)
93
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
94
+ /KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/
102
95
  )
96
+ keys_blacklist = described_class.new(patterns)
97
+ keys_blacklist.call(notice)
103
98
  end
104
99
  end
105
100
 
@@ -108,14 +103,11 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
108
103
 
109
104
  context "and when the filter is called once" do
110
105
  it "logs an error" do
111
- out = StringIO.new
112
- logger = Logger.new(out)
113
- keys_blacklist = described_class.new(logger, patterns)
114
- keys_blacklist.call(notice)
115
-
116
- expect(out.string).to(
117
- match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Proc:.+>\]/)
106
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
107
+ /KeysBlacklist is invalid.+patterns: \[#<Proc:.+>\]/
118
108
  )
109
+ keys_blacklist = described_class.new(patterns)
110
+ keys_blacklist.call(notice)
119
111
  end
120
112
  end
121
113
 
@@ -140,14 +132,11 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
140
132
  )
141
133
 
142
134
  it "logs an error" do
143
- out = StringIO.new
144
- logger = Logger.new(out)
145
- keys_blacklist = described_class.new(logger, patterns)
146
- keys_blacklist.call(notice)
147
-
148
- expect(out.string).to(
149
- match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/)
135
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
136
+ /KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/
150
137
  )
138
+ keys_blacklist = described_class.new(patterns)
139
+ keys_blacklist.call(notice)
151
140
  end
152
141
  end
153
142
 
@@ -1,9 +1,7 @@
1
1
  RSpec.describe Airbrake::Filters::KeysWhitelist do
2
- subject { described_class.new(Logger.new('/dev/null'), patterns) }
2
+ subject { described_class.new(patterns) }
3
3
 
4
- let(:notice) do
5
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
- end
4
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
7
5
 
8
6
  shared_examples 'pattern matching' do |patts, params|
9
7
  let(:patterns) { patts }
@@ -71,14 +69,11 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
71
69
  )
72
70
 
73
71
  it "logs an error" do
74
- out = StringIO.new
75
- logger = Logger.new(out)
76
- keys_whitelist = described_class.new(logger, patterns)
77
- keys_whitelist.call(notice)
78
-
79
- expect(out.string).to(
80
- match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/)
72
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
73
+ /KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/
81
74
  )
75
+ keys_whitelist = described_class.new(patterns)
76
+ keys_whitelist.call(notice)
82
77
  end
83
78
  end
84
79
 
@@ -87,14 +82,11 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
87
82
 
88
83
  context "and when the filter is called once" do
89
84
  it "logs an error" do
90
- out = StringIO.new
91
- logger = Logger.new(out)
92
- keys_whitelist = described_class.new(logger, patterns)
93
- keys_whitelist.call(notice)
94
-
95
- expect(out.string).to(
96
- match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Proc:.+>\]/)
85
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
86
+ /KeysWhitelist is invalid.+patterns: \[#<Proc:.+>\]/
97
87
  )
88
+ keys_whitelist = described_class.new(patterns)
89
+ keys_whitelist.call(notice)
98
90
  end
99
91
 
100
92
  include_examples(
@@ -120,14 +112,11 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
120
112
  )
121
113
 
122
114
  it "logs an error" do
123
- out = StringIO.new
124
- logger = Logger.new(out)
125
- keys_whitelist = described_class.new(logger, patterns)
126
- keys_whitelist.call(notice)
127
-
128
- expect(out.string).to(
129
- match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/)
115
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
116
+ /KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/
130
117
  )
118
+ keys_whitelist = described_class.new(patterns)
119
+ keys_whitelist.call(notice)
131
120
  end
132
121
  end
133
122
 
@@ -2,10 +2,7 @@ RSpec.describe Airbrake::Filters::RootDirectoryFilter do
2
2
  subject { described_class.new(root_directory) }
3
3
 
4
4
  let(:root_directory) { '/var/www/project' }
5
-
6
- let(:notice) do
7
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
8
- end
5
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
9
6
 
10
7
  it "replaces root directory in the backtrace with a label" do
11
8
  # rubocop:disable Metrics/LineLength
@@ -1,13 +1,13 @@
1
1
  RSpec.describe Airbrake::Filters::SystemExitFilter do
2
2
  it "marks SystemExit exceptions as ignored" do
3
- notice = Airbrake::Notice.new(Airbrake::Config.new, SystemExit.new)
3
+ notice = Airbrake::Notice.new(SystemExit.new)
4
4
  expect { subject.call(notice) }.to(
5
5
  change { notice.ignored? }.from(false).to(true)
6
6
  )
7
7
  end
8
8
 
9
9
  it "doesn't mark non SystemExit exceptions as ignored" do
10
- notice = Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
10
+ notice = Airbrake::Notice.new(AirbrakeTestError.new)
11
11
  expect(notice).not_to be_ignored
12
12
  expect { subject.call(notice) }.not_to(change { notice.ignored? })
13
13
  end
@@ -1,7 +1,5 @@
1
1
  RSpec.describe Airbrake::Filters::ThreadFilter do
2
- let(:notice) do
3
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
- end
2
+ let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
5
3
 
6
4
  def new_thread
7
5
  Thread.new do
@@ -1,6 +1,4 @@
1
1
  RSpec.describe Airbrake::NestedException do
2
- let(:config) { Airbrake::Config.new }
3
-
4
2
  describe "#as_json" do
5
3
  context "given exceptions with backtraces" do
6
4
  it "unwinds nested exceptions" do
@@ -11,7 +9,7 @@ RSpec.describe Airbrake::NestedException do
11
9
  Ruby21Error.raise_error('bingo')
12
10
  end
13
11
  rescue Ruby21Error => ex
14
- nested_exception = described_class.new(config, ex)
12
+ nested_exception = described_class.new(ex)
15
13
  exceptions = nested_exception.as_json
16
14
 
17
15
  expect(exceptions.size).to eq(2)
@@ -38,7 +36,7 @@ RSpec.describe Airbrake::NestedException do
38
36
  end
39
37
  end
40
38
  rescue Ruby21Error => ex
41
- nested_exception = described_class.new(config, ex)
39
+ nested_exception = described_class.new(ex)
42
40
  exceptions = nested_exception.as_json
43
41
 
44
42
  expect(exceptions.size).to eq(3)
@@ -62,7 +60,7 @@ RSpec.describe Airbrake::NestedException do
62
60
  end
63
61
  rescue Ruby21Error => ex1
64
62
  ex1.set_backtrace([])
65
- nested_exception = described_class.new(config, ex1)
63
+ nested_exception = described_class.new(ex1)
66
64
  exceptions = nested_exception.as_json
67
65
 
68
66
  expect(exceptions.size).to eq(2)
@@ -1,18 +1,14 @@
1
1
  # rubocop:disable Layout/DotPosition
2
2
  RSpec.describe Airbrake::NoticeNotifier do
3
- let(:user_params) do
4
- {
3
+ before do
4
+ Airbrake::Config.instance = Airbrake::Config.new(
5
5
  project_id: 1,
6
6
  project_key: 'abc',
7
7
  logger: Logger.new('/dev/null'),
8
8
  performance_stats: true
9
- }
9
+ )
10
10
  end
11
11
 
12
- let(:params) { {} }
13
- let(:config) { Airbrake::Config.new(user_params.merge(params)) }
14
- subject { described_class.new(config) }
15
-
16
12
  describe "#new" do
17
13
  describe "default filter addition" do
18
14
  before { allow_any_instance_of(Airbrake::FilterChain).to receive(:add_filter) }
@@ -30,12 +26,12 @@ RSpec.describe Airbrake::NoticeNotifier do
30
26
  end
31
27
 
32
28
  context "when user config has some whitelist keys" do
33
- let(:params) { { whitelist_keys: %w[foo] } }
29
+ before { Airbrake::Config.instance.merge(whitelist_keys: %w[foo]) }
34
30
 
35
31
  it "appends the whitelist filter" do
36
32
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
37
33
  .with(instance_of(Airbrake::Filters::KeysWhitelist))
38
- described_class.new(config)
34
+ subject
39
35
  end
40
36
  end
41
37
 
@@ -43,17 +39,17 @@ RSpec.describe Airbrake::NoticeNotifier do
43
39
  it "doesn't append the whitelist filter" do
44
40
  expect_any_instance_of(Airbrake::FilterChain).not_to receive(:add_filter)
45
41
  .with(instance_of(Airbrake::Filters::KeysWhitelist))
46
- described_class.new(config)
42
+ subject
47
43
  end
48
44
  end
49
45
 
50
46
  context "when user config has some blacklist keys" do
51
- let(:params) { { blacklist_keys: %w[bar] } }
47
+ before { Airbrake::Config.instance.merge(blacklist_keys: %w[bar]) }
52
48
 
53
49
  it "appends the blacklist filter" do
54
50
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
55
51
  .with(instance_of(Airbrake::Filters::KeysBlacklist))
56
- described_class.new(config)
52
+ subject
57
53
  end
58
54
  end
59
55
 
@@ -61,17 +57,17 @@ RSpec.describe Airbrake::NoticeNotifier do
61
57
  it "doesn't append the blacklist filter" do
62
58
  expect_any_instance_of(Airbrake::FilterChain).not_to receive(:add_filter)
63
59
  .with(instance_of(Airbrake::Filters::KeysBlacklist))
64
- described_class.new(config)
60
+ subject
65
61
  end
66
62
  end
67
63
 
68
64
  context "when user config specifies a root directory" do
69
- let(:params) { { root_directory: '/foo' } }
65
+ before { Airbrake::Config.instance.merge(root_directory: '/foo') }
70
66
 
71
67
  it "appends the root directory filter" do
72
68
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
73
69
  .with(instance_of(Airbrake::Filters::RootDirectoryFilter))
74
- described_class.new(config)
70
+ subject
75
71
  end
76
72
  end
77
73
 
@@ -81,7 +77,7 @@ RSpec.describe Airbrake::NoticeNotifier do
81
77
  .and_return(nil)
82
78
  expect_any_instance_of(Airbrake::FilterChain).not_to receive(:add_filter)
83
79
  .with(instance_of(Airbrake::Filters::RootDirectoryFilter))
84
- described_class.new(config)
80
+ subject
85
81
  end
86
82
  end
87
83
  end
@@ -90,8 +86,6 @@ RSpec.describe Airbrake::NoticeNotifier do
90
86
  describe "#notify" do
91
87
  let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
92
88
 
93
- subject { described_class.new(Airbrake::Config.new(user_params)) }
94
-
95
89
  let(:body) do
96
90
  {
97
91
  'id' => '00054414-b147-6ffa-85d6-1524d83362a6',
@@ -161,7 +155,12 @@ RSpec.describe Airbrake::NoticeNotifier do
161
155
  end
162
156
 
163
157
  context "when the provided environment is ignored" do
164
- let(:user_params) { { environment: 'test', ignore_environments: %w[test] } }
158
+ before do
159
+ Airbrake::Config.instance.merge(
160
+ environment: 'test',
161
+ ignore_environments: %w[test]
162
+ )
163
+ end
165
164
 
166
165
  it "doesn't send an notice" do
167
166
  expect_any_instance_of(Airbrake::AsyncSender).not_to receive(:send)
@@ -241,7 +240,11 @@ RSpec.describe Airbrake::NoticeNotifier do
241
240
  end
242
241
 
243
242
  context "when the provided environment is ignored" do
244
- let(:params) { { environment: 'test', ignore_environments: %w[test] } }
243
+ before do
244
+ Airbrake::Config.instance.merge(
245
+ environment: 'test', ignore_environments: %w[test]
246
+ )
247
+ end
245
248
 
246
249
  it "doesn't send an notice" do
247
250
  expect_any_instance_of(Airbrake::SyncSender).not_to receive(:send)