airbrake-ruby 4.6.0 → 6.2.2

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 (107) hide show
  1. checksums.yaml +5 -5
  2. data/lib/airbrake-ruby/async_sender.rb +24 -109
  3. data/lib/airbrake-ruby/backlog.rb +123 -0
  4. data/lib/airbrake-ruby/backtrace.rb +8 -7
  5. data/lib/airbrake-ruby/benchmark.rb +1 -1
  6. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  7. data/lib/airbrake-ruby/config/processor.rb +77 -0
  8. data/lib/airbrake-ruby/config/validator.rb +9 -3
  9. data/lib/airbrake-ruby/config.rb +78 -19
  10. data/lib/airbrake-ruby/context.rb +51 -0
  11. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  12. data/lib/airbrake-ruby/file_cache.rb +6 -0
  13. data/lib/airbrake-ruby/filter_chain.rb +18 -1
  14. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  15. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  16. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +3 -3
  17. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  18. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +7 -7
  19. data/lib/airbrake-ruby/filters/git_repository_filter.rb +11 -2
  20. data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
  21. data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
  22. data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
  23. data/lib/airbrake-ruby/filters/keys_filter.rb +41 -22
  24. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  25. data/lib/airbrake-ruby/filters/sql_filter.rb +35 -11
  26. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  27. data/lib/airbrake-ruby/filters/thread_filter.rb +6 -5
  28. data/lib/airbrake-ruby/grouppable.rb +12 -0
  29. data/lib/airbrake-ruby/ignorable.rb +1 -2
  30. data/lib/airbrake-ruby/inspectable.rb +2 -2
  31. data/lib/airbrake-ruby/loggable.rb +1 -1
  32. data/lib/airbrake-ruby/mergeable.rb +12 -0
  33. data/lib/airbrake-ruby/monotonic_time.rb +6 -1
  34. data/lib/airbrake-ruby/nested_exception.rb +22 -1
  35. data/lib/airbrake-ruby/notice.rb +12 -17
  36. data/lib/airbrake-ruby/notice_notifier.rb +15 -7
  37. data/lib/airbrake-ruby/performance_breakdown.rb +16 -9
  38. data/lib/airbrake-ruby/performance_notifier.rb +106 -54
  39. data/lib/airbrake-ruby/promise.rb +1 -0
  40. data/lib/airbrake-ruby/query.rb +20 -14
  41. data/lib/airbrake-ruby/queue.rb +65 -0
  42. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  43. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  44. data/lib/airbrake-ruby/remote_settings.rb +128 -0
  45. data/lib/airbrake-ruby/request.rb +14 -11
  46. data/lib/airbrake-ruby/response.rb +56 -5
  47. data/lib/airbrake-ruby/stat.rb +27 -34
  48. data/lib/airbrake-ruby/sync_sender.rb +43 -11
  49. data/lib/airbrake-ruby/tdigest.rb +52 -66
  50. data/lib/airbrake-ruby/thread_pool.rb +139 -0
  51. data/lib/airbrake-ruby/time_truncate.rb +2 -2
  52. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  53. data/lib/airbrake-ruby/truncator.rb +10 -4
  54. data/lib/airbrake-ruby/version.rb +11 -1
  55. data/lib/airbrake-ruby.rb +157 -78
  56. metadata +122 -116
  57. data/spec/airbrake_spec.rb +0 -324
  58. data/spec/async_sender_spec.rb +0 -155
  59. data/spec/backtrace_spec.rb +0 -427
  60. data/spec/benchmark_spec.rb +0 -33
  61. data/spec/code_hunk_spec.rb +0 -115
  62. data/spec/config/validator_spec.rb +0 -184
  63. data/spec/config_spec.rb +0 -154
  64. data/spec/deploy_notifier_spec.rb +0 -48
  65. data/spec/file_cache.rb +0 -36
  66. data/spec/filter_chain_spec.rb +0 -92
  67. data/spec/filters/context_filter_spec.rb +0 -23
  68. data/spec/filters/dependency_filter_spec.rb +0 -12
  69. data/spec/filters/exception_attributes_filter_spec.rb +0 -50
  70. data/spec/filters/gem_root_filter_spec.rb +0 -41
  71. data/spec/filters/git_last_checkout_filter_spec.rb +0 -46
  72. data/spec/filters/git_repository_filter.rb +0 -61
  73. data/spec/filters/git_revision_filter_spec.rb +0 -126
  74. data/spec/filters/keys_blacklist_spec.rb +0 -225
  75. data/spec/filters/keys_whitelist_spec.rb +0 -194
  76. data/spec/filters/root_directory_filter_spec.rb +0 -39
  77. data/spec/filters/sql_filter_spec.rb +0 -219
  78. data/spec/filters/system_exit_filter_spec.rb +0 -14
  79. data/spec/filters/thread_filter_spec.rb +0 -277
  80. data/spec/fixtures/notroot.txt +0 -7
  81. data/spec/fixtures/project_root/code.rb +0 -221
  82. data/spec/fixtures/project_root/empty_file.rb +0 -0
  83. data/spec/fixtures/project_root/long_line.txt +0 -1
  84. data/spec/fixtures/project_root/short_file.rb +0 -3
  85. data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +0 -5
  86. data/spec/helpers.rb +0 -9
  87. data/spec/ignorable_spec.rb +0 -14
  88. data/spec/inspectable_spec.rb +0 -45
  89. data/spec/monotonic_time_spec.rb +0 -12
  90. data/spec/nested_exception_spec.rb +0 -73
  91. data/spec/notice_notifier_spec/options_spec.rb +0 -259
  92. data/spec/notice_notifier_spec.rb +0 -356
  93. data/spec/notice_spec.rb +0 -296
  94. data/spec/performance_breakdown_spec.rb +0 -12
  95. data/spec/performance_notifier_spec.rb +0 -435
  96. data/spec/promise_spec.rb +0 -197
  97. data/spec/query_spec.rb +0 -11
  98. data/spec/request_spec.rb +0 -11
  99. data/spec/response_spec.rb +0 -88
  100. data/spec/spec_helper.rb +0 -100
  101. data/spec/stashable_spec.rb +0 -23
  102. data/spec/stat_spec.rb +0 -47
  103. data/spec/sync_sender_spec.rb +0 -133
  104. data/spec/tdigest_spec.rb +0 -230
  105. data/spec/time_truncate_spec.rb +0 -13
  106. data/spec/timed_trace_spec.rb +0 -125
  107. data/spec/truncator_spec.rb +0 -238
@@ -1,259 +0,0 @@
1
- RSpec.describe Airbrake::NoticeNotifier do
2
- let(:project_id) { 105138 }
3
- let(:project_key) { 'fd04e13d806a90f96614ad8e529b2822' }
4
- let(:localhost) { 'http://localhost:8080' }
5
-
6
- let(:endpoint) do
7
- "https://api.airbrake.io/api/v3/projects/#{project_id}/notices"
8
- end
9
-
10
- let(:params) { {} }
11
- let(:ex) { AirbrakeTestError.new }
12
-
13
- before do
14
- stub_request(:post, endpoint).to_return(status: 201, body: '{}')
15
-
16
- Airbrake::Config.instance = Airbrake::Config.new(
17
- project_id: project_id,
18
- project_key: project_key
19
- )
20
- end
21
-
22
- describe "options" do
23
- describe ":host" do
24
- context "when custom" do
25
- shared_examples 'endpoint' do |host, endpoint, title|
26
- before { Airbrake::Config.instance.merge(host: host) }
27
-
28
- example(title) do
29
- stub_request(:post, endpoint).to_return(status: 201, body: '{}')
30
- subject.notify_sync(ex)
31
-
32
- expect(a_request(:post, endpoint)).to have_been_made.once
33
- end
34
- end
35
-
36
- path = '/api/v3/projects/105138/notices'
37
-
38
- context "given a full host" do
39
- include_examples('endpoint', localhost = 'http://localhost:8080',
40
- URI.join(localhost, path),
41
- "sends notices to the specified host's endpoint")
42
- end
43
-
44
- context "given a full host" do
45
- include_examples('endpoint', localhost = 'http://localhost',
46
- URI.join(localhost, path),
47
- "assumes port 80 by default")
48
- end
49
-
50
- context "given a host without scheme" do
51
- include_examples 'endpoint', localhost = 'localhost:8080',
52
- URI.join("https://#{localhost}", path),
53
- "assumes https by default"
54
- end
55
-
56
- context "given only hostname" do
57
- include_examples 'endpoint', localhost = 'localhost',
58
- URI.join("https://#{localhost}", path),
59
- "assumes https and port 80 by default"
60
- end
61
- end
62
- end
63
-
64
- describe ":root_directory" do
65
- before do
66
- subject.add_filter(
67
- Airbrake::Filters::RootDirectoryFilter.new('/home/kyrylo/code')
68
- )
69
- end
70
-
71
- it "filters out frames" do
72
- subject.notify_sync(ex)
73
-
74
- expect(
75
- a_request(:post, endpoint)
76
- .with(body: %r|{"file":"/PROJECT_ROOT/airbrake/ruby/spec/airbrake_spec.+|)
77
- ).to have_been_made.once
78
- end
79
-
80
- context "when present and is a" do
81
- shared_examples 'root directory' do |dir|
82
- before { Airbrake::Config.instance.merge(root_directory: dir) }
83
-
84
- it "being included into the notice's payload" do
85
- subject.notify_sync(ex)
86
- expect(
87
- a_request(:post, endpoint)
88
- .with(body: %r{"rootDirectory":"/bingo/bango"})
89
- ).to have_been_made.once
90
- end
91
- end
92
-
93
- context "String" do
94
- include_examples 'root directory', '/bingo/bango'
95
- end
96
-
97
- context "Pathname" do
98
- include_examples 'root directory', Pathname.new('/bingo/bango')
99
- end
100
- end
101
- end
102
-
103
- describe ":proxy" do
104
- let(:proxy) do
105
- WEBrick::HTTPServer.new(
106
- Port: 0,
107
- Logger: WEBrick::Log.new('/dev/null'),
108
- AccessLog: []
109
- )
110
- end
111
-
112
- let(:requests) { Queue.new }
113
-
114
- let(:proxy_params) do
115
- { host: 'localhost',
116
- port: proxy.config[:Port],
117
- user: 'user',
118
- password: 'password' }
119
- end
120
-
121
- before do
122
- Airbrake::Config.instance.merge(
123
- proxy: proxy_params,
124
- host: "http://localhost:#{proxy.config[:Port]}"
125
- )
126
-
127
- proxy.mount_proc '/' do |req, res|
128
- requests << req
129
- res.status = 201
130
- res.body = "OK\n"
131
- end
132
-
133
- Thread.new { proxy.start }
134
- end
135
-
136
- after { proxy.stop }
137
-
138
- it "is being used if configured" do
139
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
140
- skip(
141
- "We use Webmock 2, which doesn't support Ruby 2.6+. It's " \
142
- "safe to run this test on 2.6+ once we upgrade to Webmock 3.5+"
143
- )
144
- end
145
- subject.notify_sync(ex)
146
-
147
- proxied_request = requests.pop(true)
148
-
149
- expect(proxied_request.header['proxy-authorization'].first)
150
- .to eq('Basic dXNlcjpwYXNzd29yZA==')
151
-
152
- # rubocop:disable Metrics/LineLength
153
- expect(proxied_request.request_line)
154
- .to eq("POST http://localhost:#{proxy.config[:Port]}/api/v3/projects/105138/notices HTTP/1.1\r\n")
155
- # rubocop:enable Metrics/LineLength
156
- end
157
- end
158
-
159
- describe ":environment" do
160
- context "when present" do
161
- before { Airbrake::Config.instance.merge(environment: :production) }
162
-
163
- it "being included into the notice's payload" do
164
- subject.notify_sync(ex)
165
- expect(
166
- a_request(:post, endpoint)
167
- .with(body: /"context":{.*"environment":"production".*}/)
168
- ).to have_been_made.once
169
- end
170
- end
171
- end
172
-
173
- describe ":ignore_environments" do
174
- shared_examples 'sent notice' do |params|
175
- before { Airbrake::Config.instance.merge(params) }
176
-
177
- it "sends a notice" do
178
- subject.notify_sync(ex)
179
- expect(a_request(:post, endpoint)).to have_been_made
180
- end
181
- end
182
-
183
- shared_examples 'ignored notice' do |params|
184
- before { Airbrake::Config.instance.merge(params) }
185
-
186
- it "ignores exceptions occurring in envs that were not configured" do
187
- subject.notify_sync(ex)
188
- expect(a_request(:post, endpoint)).not_to have_been_made
189
- end
190
- end
191
-
192
- context "when env is set and ignore_environments doesn't mention it" do
193
- params = {
194
- environment: :development,
195
- ignore_environments: [:production]
196
- }
197
-
198
- include_examples 'sent notice', params
199
- end
200
-
201
- context "when the current env and notify envs are the same" do
202
- params = {
203
- environment: :development,
204
- ignore_environments: %i[production development]
205
- }
206
-
207
- include_examples 'ignored notice', params
208
-
209
- it "returns early and doesn't try to parse the given exception" do
210
- expect(Airbrake::Notice).not_to receive(:new)
211
- expect(subject.notify_sync(ex))
212
- .to eq('error' => "current environment 'development' is ignored")
213
- expect(a_request(:post, endpoint)).not_to have_been_made
214
- end
215
- end
216
-
217
- context "when the current env is not set and notify envs are present" do
218
- params = { ignore_environments: %i[production development] }
219
-
220
- include_examples 'sent notice', params
221
- end
222
-
223
- context "when the current env is set and notify envs aren't" do
224
- include_examples 'sent notice', environment: :development
225
- end
226
-
227
- context "when ignore_environments specifies a Regexp pattern" do
228
- params = {
229
- environment: :testing,
230
- ignore_environments: ['staging', /test.+/]
231
- }
232
-
233
- include_examples 'ignored notice', params
234
- end
235
- end
236
-
237
- describe ":blacklist_keys" do
238
- # Fixes https://github.com/airbrake/airbrake-ruby/issues/276
239
- context "when specified along with :whitelist_keys" do
240
- context "and when context payload is present" do
241
- before do
242
- Airbrake::Config.instance.merge(
243
- blacklist_keys: %i[password password_confirmation],
244
- whitelist_keys: [:email, /user/i, 'account_id']
245
- )
246
- end
247
-
248
- it "sends a notice" do
249
- notice = subject.build_notice(ex)
250
- notice[:context][:headers] = 'banana'
251
- subject.notify_sync(notice)
252
-
253
- expect(a_request(:post, endpoint)).to have_been_made
254
- end
255
- end
256
- end
257
- end
258
- end
259
- end
@@ -1,356 +0,0 @@
1
- RSpec.describe Airbrake::NoticeNotifier do
2
- before do
3
- Airbrake::Config.instance = Airbrake::Config.new(
4
- project_id: 1,
5
- project_key: 'abc',
6
- logger: Logger.new('/dev/null'),
7
- performance_stats: true
8
- )
9
- end
10
-
11
- describe "#new" do
12
- describe "default filter addition" do
13
- before { allow_any_instance_of(Airbrake::FilterChain).to receive(:add_filter) }
14
-
15
- it "appends the context filter" do
16
- expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
17
- .with(instance_of(Airbrake::Filters::ContextFilter))
18
- subject
19
- end
20
-
21
- it "appends the exception attributes filter" do
22
- expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
23
- .with(instance_of(Airbrake::Filters::ExceptionAttributesFilter))
24
- subject
25
- end
26
- end
27
- end
28
-
29
- describe "#notify" do
30
- let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
31
-
32
- let(:body) do
33
- {
34
- 'id' => '00054414-b147-6ffa-85d6-1524d83362a6',
35
- 'url' => 'http://localhost/locate/00054414-b147-6ffa-85d6-1524d83362a6'
36
- }.to_json
37
- end
38
-
39
- before { stub_request(:post, endpoint).to_return(status: 201, body: body) }
40
-
41
- it "returns a promise" do
42
- expect(subject.notify('ex')).to be_an(Airbrake::Promise)
43
- sleep 1
44
- end
45
-
46
- it "refines the notice object" do
47
- subject.add_filter { |n| n[:params] = { foo: 'bar' } }
48
- notice = subject.build_notice('ex')
49
- subject.notify(notice)
50
- expect(notice[:params]).to eq(foo: 'bar')
51
- sleep 1
52
- end
53
-
54
- context "when config is invalid" do
55
- before { Airbrake::Config.instance.merge(project_id: nil) }
56
-
57
- it "returns a rejected promise" do
58
- promise = subject.notify({})
59
- expect(promise).to be_rejected
60
- end
61
- end
62
-
63
- context "when a notice is not ignored" do
64
- it "yields the notice" do
65
- expect { |b| subject.notify('ex', &b) }
66
- .to yield_with_args(Airbrake::Notice)
67
- sleep 1
68
- end
69
- end
70
-
71
- context "when a notice is ignored via a filter" do
72
- before { subject.add_filter(&:ignore!) }
73
-
74
- it "yields the notice" do
75
- expect { |b| subject.notify('ex', &b) }
76
- .to yield_with_args(Airbrake::Notice)
77
- end
78
-
79
- it "returns a rejected promise" do
80
- value = subject.notify('ex').value
81
- expect(value['error']).to match(/was marked as ignored/)
82
- end
83
- end
84
-
85
- context "when a notice is ignored via an inline filter" do
86
- before { subject.add_filter { raise AirbrakeTestError } }
87
-
88
- it "doesn't invoke regular filters" do
89
- expect { subject.notify('ex', &:ignore!) }.not_to raise_error
90
- end
91
- end
92
-
93
- context "when async sender has workers" do
94
- it "sends an exception asynchronously" do
95
- expect_any_instance_of(Airbrake::AsyncSender).to receive(:send)
96
- subject.notify('foo', bingo: 'bango')
97
- end
98
- end
99
-
100
- context "when async sender doesn't have workers" do
101
- it "sends an exception synchronously" do
102
- expect_any_instance_of(Airbrake::AsyncSender)
103
- .to receive(:has_workers?).and_return(false)
104
- expect_any_instance_of(Airbrake::SyncSender).to receive(:send)
105
- subject.notify('foo', bingo: 'bango')
106
- end
107
- end
108
-
109
- context "when the provided environment is ignored" do
110
- before do
111
- Airbrake::Config.instance.merge(
112
- environment: 'test',
113
- ignore_environments: %w[test]
114
- )
115
- end
116
-
117
- it "doesn't send an notice" do
118
- expect_any_instance_of(Airbrake::AsyncSender).not_to receive(:send)
119
- subject.notify('foo', bingo: 'bango')
120
- end
121
-
122
- it "returns a rejected promise" do
123
- promise = subject.notify('foo', bingo: 'bango')
124
- expect(promise.value).to eq('error' => "current environment 'test' is ignored")
125
- end
126
- end
127
- end
128
-
129
- describe "#notify_sync" do
130
- let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
131
-
132
- let(:body) do
133
- {
134
- 'id' => '00054414-b147-6ffa-85d6-1524d83362a6',
135
- 'url' => 'http://localhost/locate/00054414-b147-6ffa-85d6-1524d83362a6'
136
- }
137
- end
138
-
139
- before { stub_request(:post, endpoint).to_return(status: 201, body: body.to_json) }
140
-
141
- it "returns a reponse hash" do
142
- expect(subject.notify_sync('ex')).to eq(body)
143
- end
144
-
145
- it "refines the notice object" do
146
- subject.add_filter { |n| n[:params] = { foo: 'bar' } }
147
- notice = subject.build_notice('ex')
148
- subject.notify_sync(notice)
149
- expect(notice[:params]).to eq(foo: 'bar')
150
- end
151
-
152
- it "sends an exception synchronously" do
153
- subject.notify_sync('foo', bingo: 'bango')
154
- expect(
155
- a_request(:post, endpoint).with(
156
- body: /"params":{.*"bingo":"bango".*}/
157
- )
158
- ).to have_been_made.once
159
- end
160
-
161
- context "when a notice is not ignored" do
162
- it "yields the notice" do
163
- expect { |b| subject.notify_sync('ex', &b) }
164
- .to yield_with_args(Airbrake::Notice)
165
- end
166
- end
167
-
168
- context "when a notice is ignored via a filter" do
169
- before { subject.add_filter(&:ignore!) }
170
-
171
- it "yields the notice" do
172
- expect { |b| subject.notify_sync('ex', &b) }
173
- .to yield_with_args(Airbrake::Notice)
174
- end
175
-
176
- it "returns an error hash" do
177
- response = subject.notify_sync('ex')
178
- expect(response['error']).to match(/was marked as ignored/)
179
- end
180
- end
181
-
182
- context "when a notice is ignored via an inline filter" do
183
- before { subject.add_filter { raise AirbrakeTestError } }
184
-
185
- it "doesn't invoke regular filters" do
186
- expect { subject.notify('ex', &:ignore!) }.not_to raise_error
187
- end
188
- end
189
-
190
- context "when the provided environment is ignored" do
191
- before do
192
- Airbrake::Config.instance.merge(
193
- environment: 'test', ignore_environments: %w[test]
194
- )
195
- end
196
-
197
- it "doesn't send an notice" do
198
- expect_any_instance_of(Airbrake::SyncSender).not_to receive(:send)
199
- subject.notify_sync('foo', bingo: 'bango')
200
- end
201
-
202
- it "returns an error hash" do
203
- expect(subject.notify_sync('foo'))
204
- .to eq('error' => "current environment 'test' is ignored")
205
- end
206
- end
207
- end
208
-
209
- describe "#add_filter" do
210
- context "given a block" do
211
- it "appends a new filter to the filter chain" do
212
- notifier = subject
213
- b = proc {}
214
- expect_any_instance_of(Airbrake::FilterChain)
215
- .to receive(:add_filter) { |*args| expect(args.last).to be(b) }
216
- notifier.add_filter(&b)
217
- end
218
- end
219
-
220
- context "given a class" do
221
- it "appends a new filter to the filter chain" do
222
- notifier = subject
223
- klass = Class.new
224
- expect_any_instance_of(Airbrake::FilterChain)
225
- .to receive(:add_filter).with(klass)
226
- notifier.add_filter(klass)
227
- end
228
- end
229
- end
230
-
231
- describe "#build_notice" do
232
- context "when given exception is another notice" do
233
- it "merges params with the notice" do
234
- notice = subject.build_notice('ex')
235
- other = subject.build_notice(notice, foo: 'bar')
236
- expect(other[:params]).to eq(foo: 'bar')
237
- end
238
-
239
- it "it returns the provided notice" do
240
- notice = subject.build_notice('ex')
241
- other = subject.build_notice(notice, foo: 'bar')
242
- expect(other).to eq(notice)
243
- end
244
- end
245
-
246
- context "when given exception is an Exception" do
247
- it "prevents mutation of passed-in params hash" do
248
- params = { immutable: true }
249
- notice = subject.build_notice('ex', params)
250
- notice[:params][:mutable] = true
251
- expect(params).to eq(immutable: true)
252
- end
253
-
254
- context "and also when it doesn't have own backtrace" do
255
- context "and when the generated backtrace consists only of library frames" do
256
- it "returns the full generated backtrace" do
257
- backtrace = [
258
- "/lib/airbrake-ruby/a.rb:84:in `build_notice'",
259
- "/lib/airbrake-ruby/b.rb:124:in `send_notice'"
260
- ]
261
- allow(Kernel).to receive(:caller).and_return(backtrace)
262
-
263
- notice = subject.build_notice(Exception.new)
264
-
265
- expect(notice[:errors].first[:backtrace]).to eq(
266
- [
267
- { file: '/lib/airbrake-ruby/a.rb', line: 84, function: 'build_notice' },
268
- { file: '/lib/airbrake-ruby/b.rb', line: 124, function: 'send_notice' }
269
- ]
270
- )
271
- end
272
- end
273
-
274
- context "and when the generated backtrace consists of mixed frames" do
275
- it "returns the filtered backtrace" do
276
- backtrace = [
277
- "/airbrake-ruby/lib/airbrake-ruby/a.rb:84:in `b'",
278
- "/airbrake-ruby/lib/foo/b.rb:84:in `build'",
279
- "/airbrake-ruby/lib/bar/c.rb:124:in `send'"
280
- ]
281
- allow(Kernel).to receive(:caller).and_return(backtrace)
282
-
283
- notice = subject.build_notice(Exception.new)
284
-
285
- expect(notice[:errors].first[:backtrace]).to eq(
286
- [
287
- { file: '/airbrake-ruby/lib/foo/b.rb', line: 84, function: 'build' },
288
- { file: '/airbrake-ruby/lib/bar/c.rb', line: 124, function: 'send' }
289
- ]
290
- )
291
- end
292
- end
293
- end
294
- end
295
-
296
- # TODO: this seems to be bugged. Fix later.
297
- context "when given exception is a Java exception", skip: true do
298
- before do
299
- expect(Airbrake::Backtrace).to receive(:java_exception?).and_return(true)
300
- end
301
-
302
- it "automatically generates the backtrace" do
303
- backtrace = [
304
- "org/jruby/RubyKernel.java:998:in `eval'",
305
- "/ruby/stdlib/irb/workspace.rb:87:in `evaluate'",
306
- "/ruby/stdlib/irb.rb:489:in `block in eval_input'"
307
- ]
308
- allow(Kernel).to receive(:caller).and_return(backtrace)
309
-
310
- notice = subject.build_notice(Exception.new)
311
-
312
- # rubocop:disable Metrics/LineLength
313
- expect(notice[:errors].first[:backtrace]).to eq(
314
- [
315
- { file: 'org/jruby/RubyKernel.java', line: 998, function: 'eval' },
316
- { file: '/ruby/stdlib/irb/workspace.rb', line: 87, function: 'evaluate' },
317
- { file: '/ruby/stdlib/irb.rb:489', line: 489, function: 'block in eval_input' }
318
- ]
319
- )
320
- # rubocop:enable Metrics/LineLength
321
- end
322
- end
323
-
324
- context "when async sender is closed" do
325
- before do
326
- expect_any_instance_of(Airbrake::AsyncSender)
327
- .to receive(:closed?).and_return(true)
328
- end
329
-
330
- it "raises error" do
331
- expect { subject.build_notice(Exception.new) }.to raise_error(
332
- Airbrake::Error,
333
- 'attempted to build Exception with closed Airbrake instance'
334
- )
335
- end
336
- end
337
- end
338
-
339
- describe "#close" do
340
- it "sends the close message to async sender" do
341
- expect_any_instance_of(Airbrake::AsyncSender).to receive(:close)
342
- subject.close
343
- end
344
- end
345
-
346
- describe "#configured?" do
347
- it { is_expected.to be_configured }
348
- end
349
-
350
- describe "#merge_context" do
351
- it "merges the provided context with the notice object" do
352
- expect_any_instance_of(Hash).to receive(:merge!).with(apples: 'oranges')
353
- subject.merge_context(apples: 'oranges')
354
- end
355
- end
356
- end