airbrake 8.1.4 → 8.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake/logger.rb +3 -3
  3. data/lib/airbrake/rack/context_filter.rb +16 -13
  4. data/lib/airbrake/rack/middleware.rb +19 -63
  5. data/lib/airbrake/rails.rb +8 -0
  6. data/lib/airbrake/rails/action_controller.rb +8 -8
  7. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +11 -12
  8. data/lib/airbrake/rails/action_controller_route_subscriber.rb +5 -0
  9. data/lib/airbrake/rails/active_job.rb +6 -5
  10. data/lib/airbrake/rails/active_record_subscriber.rb +30 -12
  11. data/lib/airbrake/rake.rb +10 -9
  12. data/lib/airbrake/rake/tasks.rb +1 -1
  13. data/lib/airbrake/version.rb +1 -1
  14. data/spec/apps/rails/dummy_task.rake +0 -5
  15. data/spec/apps/rails/logs/32.log +652 -27267
  16. data/spec/apps/rails/logs/42.log +597 -1717
  17. data/spec/apps/rails/logs/52.log +5237 -1
  18. data/spec/integration/rack/rack_spec.rb +5 -26
  19. data/spec/integration/rails/rails_spec.rb +135 -200
  20. data/spec/integration/rails/rake_spec.rb +66 -141
  21. data/spec/integration/shared_examples/rack_examples.rb +72 -101
  22. data/spec/integration/sinatra/sinatra_spec.rb +9 -55
  23. data/spec/spec_helper.rb +8 -3
  24. data/spec/support/matchers/a_notice_with.rb +29 -0
  25. data/spec/unit/logger_spec.rb +3 -13
  26. data/spec/unit/rack/middleware_spec.rb +16 -61
  27. data/spec/unit/rake/tasks_spec.rb +2 -2
  28. data/spec/unit/shoryuken_spec.rb +0 -17
  29. data/spec/unit/sidekiq/retryable_jobs_filter_spec.rb +1 -1
  30. data/spec/unit/sidekiq_spec.rb +0 -15
  31. metadata +20 -10
  32. data/spec/apps/rails/logs/51.log +0 -3166
  33. data/spec/apps/sinatra/composite_app/sinatra_app1.rb +0 -11
  34. data/spec/apps/sinatra/composite_app/sinatra_app2.rb +0 -11
@@ -7,35 +7,14 @@ RSpec.describe "Rack integration specs" do
7
7
  include_examples 'rack examples'
8
8
 
9
9
  describe "context payload" do
10
- let(:endpoint) do
11
- 'https://api.airbrake.io/api/v3/projects/113743/notices'
12
- end
13
-
14
- let(:routes_endpoint) do
15
- 'https://api.airbrake.io/api/v5/projects/113743/routes-stats'
16
- end
17
-
18
- let(:queries_endpoint) do
19
- 'https://api.airbrake.io/api/v5/projects/113743/queries-stats'
20
- end
21
-
22
- # Airbrake Ruby has a background thread that sends performance requests
23
- # periodically. We don't want this to get in the way.
24
- before do
25
- allow(Airbrake.notifiers[:performance][:default]).
26
- to receive(:notify).and_return(nil)
27
-
28
- stub_request(:post, endpoint).to_return(status: 200, body: '')
29
- [routes_endpoint, queries_endpoint].each do |endpoint|
30
- stub_request(:put, endpoint).to_return(status: 200, body: '')
31
- end
32
- end
10
+ before { stub_request(:post, endpoint).to_return(status: 200, body: '') }
33
11
 
34
12
  it "includes version" do
35
13
  get '/crash'
36
- wait_for_a_request_with_body(
37
- /"context":{.*"versions":{"rack_version":"\d\..+","rack_release":"\d\..+"}/
38
- )
14
+ sleep 2
15
+
16
+ body = /"context":{.*"versions":{"rack_version":"\d\..+","rack_release":"\d\..+"}/
17
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
39
18
  end
40
19
  end
41
20
  end
@@ -8,13 +8,6 @@ RSpec.describe "Rails integration specs" do
8
8
 
9
9
  include_examples 'rack examples'
10
10
 
11
- # Airbrake Ruby has a background thread that sends performance requests
12
- # periodically. We don't want this to get in the way.
13
- before do
14
- allow(Airbrake.notifiers[:performance][:default]).
15
- to receive(:notify).and_return(nil)
16
- end
17
-
18
11
  if ::Rails.version.start_with?('5.')
19
12
  it "inserts the Airbrake Rack middleware after DebugExceptions" do
20
13
  middlewares = Rails.configuration.middleware.middlewares.map(&:inspect)
@@ -27,58 +20,65 @@ RSpec.describe "Rails integration specs" do
27
20
  middlewares = Rails.configuration.middleware.middlewares.map(&:inspect)
28
21
  own_idx = middlewares.index('Airbrake::Rack::Middleware')
29
22
 
30
- expect(middlewares[own_idx - 1]).
31
- to eq('ActiveRecord::ConnectionAdapters::ConnectionManagement')
23
+ expect(middlewares[own_idx - 1])
24
+ .to eq('ActiveRecord::ConnectionAdapters::ConnectionManagement')
32
25
  end
33
26
  end
34
27
 
35
- shared_examples 'context payload content' do |route|
28
+ shared_examples "context payload content" do |route|
29
+ let(:user) do
30
+ OpenStruct.new(
31
+ id: 1,
32
+ email: 'qa@example.com',
33
+ username: 'qa-dept'
34
+ )
35
+ end
36
+
36
37
  before do
37
- login_as(OpenStruct.new(id: 1, email: 'qa@example.com', username: 'qa-dept'))
38
+ login_as(user)
38
39
  get(route, foo: :bar)
39
40
  sleep 2
40
41
  end
41
42
 
42
43
  it "includes component information" do
43
- wait_for_a_request_with_body(/"context":{.*"component":"dummy".*}/)
44
+ body = /"context":{.*"component":"dummy".*}/
45
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
44
46
  end
45
47
 
46
48
  it "includes action information" do
47
- case route
48
- when '/crash'
49
- wait_for_a_request_with_body(/"context":{.*"action":"crash".*}/)
50
- when '/notify_airbrake_helper'
51
- wait_for_a_request_with_body(
49
+ body =
50
+ case route
51
+ when '/crash'
52
+ /"context":{.*"action":"crash".*}/
53
+ when '/notify_airbrake_helper'
52
54
  /"context":{.*"action":"notify_airbrake_helper".*}/
53
- )
54
- when '/notify_airbrake_sync_helper'
55
- wait_for_a_request_with_body(
55
+ when '/notify_airbrake_sync_helper'
56
56
  /"context":{.*"action":"notify_airbrake_sync_helper".*}/
57
- )
58
- else
59
- raise 'Unknown route'
60
- end
57
+ else
58
+ raise 'Unknown route'
59
+ end
60
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
61
61
  end
62
62
 
63
63
  it "includes version" do
64
- wait_for_a_request_with_body(/"context":{.*"versions":{"rails":"\d\./)
64
+ body = /"context":{.*"versions":{"rails":"\d\./
65
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
65
66
  end
66
67
 
67
68
  it "includes session" do
68
- wait_for_a_request_with_body(
69
- /"context":{.*"session":{.*"session_id":"\w+".*}/
70
- )
69
+ body = /"context":{.*"session":{.*"session_id":"\w+".*}/
70
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
71
71
  end
72
72
 
73
73
  it "includes params" do
74
74
  action = route[1..-1]
75
- wait_for_a_request_with_body(
76
- /"context":{.*"params":{.*"controller":"dummy","action":"#{action}".*}/
77
- )
75
+ body = /"context":{.*"params":{.*"controller":"dummy","action":"#{action}".*}/
76
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
78
77
  end
79
78
 
80
79
  it "includes route" do
81
- wait_for_a_request_with_body(/"context":{.*"route":"#{route}\(\.:format\)".*}/)
80
+ body = /"context":{.*"route":"#{route}\(\.:format\)".*}/
81
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made
82
82
  end
83
83
  end
84
84
 
@@ -96,245 +96,180 @@ RSpec.describe "Rails integration specs" do
96
96
  end
97
97
  end
98
98
 
99
- describe "Active Record callbacks" do
100
- if Gem::Version.new(Rails.version) >= Gem::Version.new('5.1.0.alpha')
101
- it "reports exceptions in after_commit callbacks" do
102
- get '/active_record_after_commit'
103
- sleep 2
104
-
105
- wait_for(
106
- a_request(:post, endpoint).
107
- with(body: /"type":"AirbrakeTestError","message":"after_commit"/)
108
- ).to have_been_made.twice
99
+ describe(
100
+ "Active Record callbacks",
101
+ skip: Gem::Version.new(Rails.version) < Gem::Version.new('5.1.0')
102
+ ) do
103
+ it "reports exceptions in after_commit callbacks" do
104
+ expect(Airbrake).to receive(:notify).with(
105
+ an_instance_of(AirbrakeTestError)
106
+ ) do |exception|
107
+ expect(exception.message).to eq('after_commit')
109
108
  end
110
109
 
111
- it "reports exceptions in after_rollback callbacks" do
112
- get '/active_record_after_rollback'
113
- sleep 2
110
+ get '/active_record_after_commit'
111
+ end
114
112
 
115
- wait_for(
116
- a_request(:post, endpoint).
117
- with(body: /"type":"AirbrakeTestError","message":"after_rollback"/)
118
- ).to have_been_made.twice
119
- end
120
- else
121
- it "reports exceptions in after_commit callbacks" do
122
- get '/active_record_after_commit'
123
- wait_for_a_request_with_body(
124
- /"type":"AirbrakeTestError","message":"after_commit"/
125
- )
113
+ it "reports exceptions in after_rollback callbacks" do
114
+ expect(Airbrake).to receive(:notify).with(
115
+ an_instance_of(AirbrakeTestError)
116
+ ) do |exception|
117
+ expect(exception.message).to eq('after_rollback')
126
118
  end
127
119
 
128
- it "reports exceptions in after_rollback callbacks" do
129
- get '/active_record_after_rollback'
130
- wait_for_a_request_with_body(
131
- /"type":"AirbrakeTestError","message":"after_rollback"/
132
- )
133
- end
120
+ get '/active_record_after_rollback'
134
121
  end
135
122
  end
136
123
 
137
124
  if Gem::Version.new(Rails.version) >= Gem::Version.new('4.2')
138
125
  describe "ActiveJob jobs" do
139
126
  it "reports exceptions occurring in ActiveJob workers" do
140
- get '/active_job'
141
- sleep 2
142
-
143
- wait_for(
144
- a_request(:post, endpoint).
145
- with(body: /"message":"active_job error"/)
146
- ).to have_been_made.at_least_once
147
- end
127
+ expect(Airbrake).to receive(:notify)
128
+ .with(an_instance_of(Airbrake::Notice)).at_least(:once)
148
129
 
149
- it "does not raise SystemStackError" do
150
130
  get '/active_job'
151
- sleep 2
152
-
153
- wait_for(
154
- a_request(:post, endpoint).
155
- with(body: /"type":"SystemStackError"/)
156
- ).not_to have_been_made
131
+ sleep 2 # Wait for ActiveJob job exiting
157
132
  end
158
133
 
159
134
  context "when Airbrake is not configured" do
160
135
  before do
161
- # Make sure the Logger intergration doesn't get in the way.
162
- allow_any_instance_of(Logger).to receive(:airbrake_notifier).and_return(nil)
163
- end
164
-
165
- it "doesn't report errors" do
166
- allow(Airbrake).to receive(:notify)
167
-
168
136
  # Make sure we don't call `build_notice` more than 1 time. Rack
169
137
  # integration will try to handle error 500 and we want to prevent
170
138
  # that: https://github.com/airbrake/airbrake/pull/583
171
139
  allow_any_instance_of(Airbrake::Rack::Middleware).to(
172
- receive(:notify_airbrake).
173
- and_return(nil)
140
+ receive(:notify_airbrake).and_return(nil)
174
141
  )
142
+ end
143
+
144
+ it "doesn't report errors" do
145
+ expect(Airbrake).to receive(:notify).with(
146
+ an_instance_of(Airbrake::Notice)
147
+ ) { |notice|
148
+ # TODO: this doesn't actually fail but prints a failure. Figure
149
+ # out how to test properly.
150
+ expect(notice[:errors].first[:message]).to eq('active_job error')
151
+ }.at_least(:once)
175
152
 
176
153
  get '/active_job'
177
154
  sleep 2
178
-
179
- wait_for(
180
- a_request(:post, endpoint).
181
- with(body: /"message":"active_job error"/)
182
- ).not_to have_been_made
183
155
  end
184
156
  end
185
157
  end
186
158
  end
187
159
 
188
160
  describe "Resque workers" do
189
- context "when Airbrake is configured" do
190
- it "reports exceptions occurring in Resque workers" do
191
- with_resque { get '/resque' }
192
-
193
- wait_for_a_request_with_body(
194
- /"message":"resque\serror".*"params":{.*
195
- "class":"BingoWorker","args":\["bango","bongo"\].*}/x
161
+ it "reports exceptions occurring in Resque workers" do
162
+ expect(Airbrake).to receive(:notify_sync).with(
163
+ anything,
164
+ hash_including(
165
+ 'class' => 'BingoWorker',
166
+ 'args' => %w[bango bongo]
196
167
  )
197
- end
198
- end
199
-
200
- context "when Airbrake is not configured" do
201
- before do
202
- @notifiers = Airbrake.notifiers[:notice]
203
- @default_notifier = @notifiers.delete(:default)
204
- end
205
-
206
- after do
207
- @notifiers[:default] = @default_notifier
208
- end
209
-
210
- it "doesn't report errors" do
211
- with_resque { get '/resque' }
212
-
213
- wait_for(
214
- a_request(:post, endpoint).
215
- with(body: /"message":"resque error"/)
216
- ).not_to have_been_made
217
- end
168
+ )
169
+ with_resque { get '/resque' }
218
170
  end
219
171
  end
220
172
 
221
173
  describe "DelayedJob jobs" do
222
174
  it "reports exceptions occurring in DelayedJob jobs" do
223
- get '/delayed_job'
224
- sleep 2
175
+ skip if Gem::Version.new(Rails.version) > Gem::Version.new('3.2.22.5')
225
176
 
226
- wait_for_a_request_with_body(
227
- %r("message":"delayed_job\serror".*"params":{.*
228
- "handler":"---\s!ruby/struct:BangoJob\\nbingo:\s
229
- bingo\\nbongo:\sbongo\\n".*})x
177
+ expect(Airbrake).to receive(:notify).with(
178
+ anything,
179
+ 'job' => hash_including(
180
+ 'handler' => "--- !ruby/struct:BangoJob\nbingo: bingo\nbongo: bongo\n"
181
+ )
230
182
  )
231
183
 
232
- # Two requests are performed during this example. We care only about one.
233
- # Sleep guarantees that we let the unimportant request occur here and not
234
- # elsewhere.
235
- sleep 2
184
+ get '/delayed_job'
236
185
  end
237
186
 
238
- context "when Airbrake is not configured" do
239
- before do
240
- # Make sure the Logger intergration doesn't get in the way.
241
- allow_any_instance_of(Logger).to receive(:airbrake_notifier).and_return(nil)
242
-
243
- @notifiers = Airbrake.notifiers[:notice]
244
- @default_notifier = @notifiers.delete(:default)
245
- end
246
-
247
- after do
248
- @notifiers[:default] = @default_notifier
249
- end
187
+ it "reports exceptions occurring in DelayedJob jobs on Rails 4.2" do
188
+ skip if Gem::Version.new(Rails.version) == Gem::Version.new('3.2.22.5')
250
189
 
251
- it "doesn't report errors" do
252
- # Make sure we don't call `build_notice` more than 1 time. Rack
253
- # integration will try to handle error 500 and we want to prevent
254
- # that: https://github.com/airbrake/airbrake/pull/583
255
- allow_any_instance_of(Airbrake::Rack::Middleware).to(
256
- receive(:notify_airbrake).
257
- and_return(nil)
190
+ expect(Airbrake).to receive(:notify).with(
191
+ anything,
192
+ hash_including(
193
+ 'handler' => "--- !ruby/struct:BangoJob\nbingo: bingo\nbongo: bongo\n"
258
194
  )
195
+ )
259
196
 
260
- get '/delayed_job'
261
- sleep 2
262
-
263
- wait_for(
264
- a_request(:post, endpoint).
265
- with(body: /"message":"delayed_job error"/)
266
- ).not_to have_been_made
267
- end
197
+ get '/delayed_job'
268
198
  end
269
199
  end
270
200
 
271
- describe "notice payload when a user is authenticated without Warden" do
272
- context "when the current_user method is defined" do
201
+ describe "user extraction" do
202
+ context "when Warden is not available but 'current_user' is defined" do
203
+ let(:user) do
204
+ OpenStruct.new(
205
+ id: 1,
206
+ email: 'qa@example.com',
207
+ username: 'qa-dept'
208
+ )
209
+ end
210
+
273
211
  before do
274
212
  allow_message_expectations_on_nil
275
213
  allow(Warden::Proxy).to receive(:new).and_return(nil)
276
214
  # Mock on_request to make the test pass. Started failing in warden 1.2.8
277
215
  # due to: https://github.com/wardencommunity/warden/pull/162
278
216
  allow(nil).to receive(:on_request).and_return(nil)
279
- end
280
-
281
- it "contains the user information" do
282
- user = OpenStruct.new(id: 1, email: 'qa@example.com', username: 'qa-dept')
283
217
  allow_any_instance_of(DummyController).to receive(:current_user) { user }
218
+ end
284
219
 
220
+ it "sends user info" do
285
221
  get '/crash'
286
222
  sleep 2
287
- wait_for_a_request_with_body(
288
- /"user":{"id":"1","username":"qa-dept","email":"qa@example.com"}/
289
- )
223
+
224
+ body = /"user":{"id":"1","username":"qa-dept","email":"qa@example.com"}/
225
+ wait_for(a_request(:post, endpoint).with(body: body)).to have_been_made
290
226
  end
291
227
  end
292
228
  end
293
229
 
294
- describe "the performance hook" do
295
- before do
296
- expect(Airbrake.notifiers[:performance][:default]).
297
- to receive(:notify).at_least(:once).and_call_original
298
- end
230
+ describe "request performance hook" do
231
+ before { allow(Airbrake).to receive(:notify).and_return(nil) }
299
232
 
300
233
  it "notifies request" do
234
+ expect(Airbrake).to receive(:notify_request).with(
235
+ hash_including(
236
+ route: '/crash(.:format)',
237
+ method: 'GET'
238
+ )
239
+ )
301
240
  get '/crash'
302
- sleep 2
303
-
304
- wait_for_a_request_with_body(/"errors"/)
305
-
306
- body = %r|
307
- {"routes":\[.*{"method":"GET","route":"\/crash\(\.:format\)","statusCode":500
308
- |x
309
- wait_for(a_request(:put, routes_endpoint).with(body: body)).
310
- to have_been_made.once
311
- end
312
-
313
- it "notifies query" do
314
- get '/crash'
315
- sleep 2
316
-
317
- body = %r|
318
- {"queries":.*"method":"GET","route":"/crash\(\.:format\)","query":
319
- |x
320
- wait_for(a_request(:put, queries_endpoint).with(body: body)).
321
- to have_been_made.once
322
241
  end
323
242
 
324
243
  it "defaults to 500 when status code for exception returns 0" do
325
- allow(ActionDispatch::ExceptionWrapper).
326
- to receive(:status_code_for_exception).and_return(0)
327
-
244
+ allow(ActionDispatch::ExceptionWrapper)
245
+ .to receive(:status_code_for_exception).and_return(0)
246
+
247
+ expect(Airbrake).to receive(:notify_request).with(
248
+ hash_including(
249
+ route: '/crash(.:format)',
250
+ method: 'HEAD',
251
+ status_code: 500
252
+ )
253
+ )
328
254
  head '/crash'
329
- sleep 2
255
+ end
256
+ end
330
257
 
331
- wait_for_a_request_with_body(/"errors"/)
258
+ describe "query performance hook" do
259
+ before { allow(Airbrake).to receive(:notify).and_return(nil) }
260
+
261
+ it "sends queries to Airbrake" do
262
+ expect(Airbrake).to receive(:notify_query).with(
263
+ hash_including(
264
+ route: '/crash(.:format)',
265
+ method: 'GET',
266
+ func: 'tap',
267
+ file: 'lib/airbrake/rails/active_record_subscriber.rb',
268
+ line: anything
269
+ )
270
+ ).at_least(:once)
332
271
 
333
- body = %r|
334
- {"routes":\[.*{"method":"HEAD","route":"\/crash\(\.:format\)","statusCode":500
335
- |x
336
- wait_for(a_request(:put, routes_endpoint).with(body: body)).
337
- to have_been_made.once
272
+ get '/crash'
338
273
  end
339
274
  end
340
275
  end