dfg-airbrake 5.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake.rb +57 -0
  3. data/lib/airbrake/capistrano/tasks.rb +65 -0
  4. data/lib/airbrake/delayed_job/plugin1.rb +52 -0
  5. data/lib/airbrake/rack/middleware.rb +59 -0
  6. data/lib/airbrake/rack/notice_builder.rb +125 -0
  7. data/lib/airbrake/rack/user.rb +61 -0
  8. data/lib/airbrake/rails/action_controller.rb +37 -0
  9. data/lib/airbrake/rails/active_job.rb +35 -0
  10. data/lib/airbrake/rails/active_record.rb +36 -0
  11. data/lib/airbrake/rails/railtie.rb +79 -0
  12. data/lib/airbrake/rake/task_ext.rb +66 -0
  13. data/lib/airbrake/rake/tasks.rb +118 -0
  14. data/lib/airbrake/resque/failure.rb +19 -0
  15. data/lib/airbrake/sidekiq/error_handler.rb +35 -0
  16. data/lib/airbrake/version.rb +6 -0
  17. data/lib/generators/airbrake_generator.rb +25 -0
  18. data/lib/generators/airbrake_initializer.rb.erb +68 -0
  19. data/spec/airbrake_spec.rb +17 -0
  20. data/spec/apps/rack/dummy_app.rb +17 -0
  21. data/spec/apps/rails/dummy_app.rb +156 -0
  22. data/spec/apps/rails/dummy_task.rake +20 -0
  23. data/spec/apps/sinatra/composite_app/sinatra_app1.rb +11 -0
  24. data/spec/apps/sinatra/composite_app/sinatra_app2.rb +11 -0
  25. data/spec/apps/sinatra/dummy_app.rb +12 -0
  26. data/spec/integration/rack/rack_spec.rb +17 -0
  27. data/spec/integration/rails/rails_spec.rb +216 -0
  28. data/spec/integration/rails/rake_spec.rb +160 -0
  29. data/spec/integration/shared_examples/rack_examples.rb +126 -0
  30. data/spec/integration/sinatra/sinatra_spec.rb +77 -0
  31. data/spec/spec_helper.rb +116 -0
  32. data/spec/unit/rack/middleware_spec.rb +136 -0
  33. data/spec/unit/rack/notice_builder_spec.rb +157 -0
  34. data/spec/unit/rack/user_spec.rb +172 -0
  35. data/spec/unit/rake/tasks_spec.rb +67 -0
  36. data/spec/unit/sidekiq/error_handler_spec.rb +33 -0
  37. metadata +247 -0
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake do
4
+ describe ".add_rack_builder" do
5
+ let :builder do
6
+ proc { |_, _| nil }
7
+ end
8
+
9
+ after { Airbrake::Rack::NoticeBuilder.builders.delete(builder) }
10
+
11
+ it "adds new builder to the chain" do
12
+ expect { Airbrake.add_rack_builder(&builder) }.to change {
13
+ Airbrake::Rack::NoticeBuilder.builders.count
14
+ }.by(1)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ DummyApp = Rack::Builder.new do
2
+ use Rack::ShowExceptions
3
+ use Airbrake::Rack::Middleware
4
+ use Warden::Manager
5
+
6
+ map '/' do
7
+ run(
8
+ proc do |_env|
9
+ [200, { 'Content-Type' => 'text/plain' }, ['Hello from index']]
10
+ end
11
+ )
12
+ end
13
+
14
+ map '/crash' do
15
+ run proc { |_env| raise AirbrakeTestError }
16
+ end
17
+ end
@@ -0,0 +1,156 @@
1
+ class DummyApp < Rails::Application
2
+ # Rails requires these two keys.
3
+ config.session_store :cookie_store, key: 'jiez4Mielu1AiHugog3shiiPhe3lai3faer'
4
+ config.secret_token = 'ni6aeph6aeriBiphesh8omahv6cohpue5Quah5ceiMohtuvei8'
5
+
6
+ if Gem::Version.new(Rails.version) > Gem::Version.new('3.2.0')
7
+ config.secret_key_base = '62773890cad9d9d584b57320f8612f8f7378a90aadcabc6ee'
8
+ end
9
+
10
+ # Configure a logger, without it the tests can't run.
11
+ vsn = Rails.version.split('').values_at(0, 2).join('')
12
+ log_path = File.join(File.dirname(__FILE__), 'logs', "#{vsn}.log")
13
+ config.logger = Logger.new(log_path)
14
+ Rails.logger = config.logger
15
+
16
+ config.active_support.deprecation = :stderr
17
+
18
+ config.middleware.use Warden::Manager
19
+
20
+ # In Rails 4.2.x Active Record suppresses errors raised within
21
+ # 'after_rollback' & 'after_commit' callbacks and only print them to the
22
+ # logs. In the next version, these errors will no longer be suppressed.
23
+ # Instead, the errors will propagate normally just like in other Active Record
24
+ # callbacks.
25
+ config.active_record.raise_in_transactional_callbacks = true if vsn == '42'
26
+
27
+ # Silences the warning, which says 'config.eager_load is set to nil'.
28
+ config.eager_load = false
29
+
30
+ routes.append do
31
+ get '/' => 'dummy#index'
32
+ get '/crash' => 'dummy#crash'
33
+ get '/notify_airbrake_helper' => 'dummy#notify_airbrake_helper'
34
+ get '/notify_airbrake_sync_helper' => 'dummy#notify_airbrake_sync_helper'
35
+ get '/active_record_after_commit' => 'dummy#active_record_after_commit'
36
+ get '/active_record_after_rollback' => 'dummy#active_record_after_rollback'
37
+ get '/active_job' => 'dummy#active_job'
38
+ get '/resque' => 'dummy#resque'
39
+ get '/delayed_job' => 'dummy#delayed_job'
40
+ end
41
+ end
42
+
43
+ class Book < ActiveRecord::Base
44
+ after_commit :raise_error_after_commit
45
+ after_rollback :raise_error_after_rollback
46
+
47
+ def raise_error_after_commit
48
+ raise AirbrakeTestError, 'after_commit'
49
+ end
50
+
51
+ def raise_error_after_rollback
52
+ raise AirbrakeTestError, 'after_rollback'
53
+ end
54
+ end
55
+
56
+ # ActiveJob.
57
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('4.2')
58
+ class BingoJob < ActiveJob::Base
59
+ queue_as :bingo
60
+
61
+ class BingoWrapper
62
+ def initialize(bingo)
63
+ @bingo = bingo
64
+ end
65
+ end
66
+
67
+ def perform(*_args)
68
+ @wrapper = BingoWrapper.new(self)
69
+ raise AirbrakeTestError, 'active_job error'
70
+ end
71
+ end
72
+ end
73
+
74
+ # Resque.
75
+ class BingoWorker
76
+ @queue = :bingo_workers_queue
77
+
78
+ def self.perform(_bango, _bongo)
79
+ raise AirbrakeTestError, 'resque error'
80
+ end
81
+ end
82
+
83
+ # DelayedJob.
84
+ BangoJob = Struct.new(:bingo, :bongo) do
85
+ def perform
86
+ raise AirbrakeTestError, 'delayed_job error'
87
+ end
88
+ end
89
+
90
+ class DummyController < ActionController::Base
91
+ layout 'application'
92
+
93
+ self.view_paths = [
94
+ ActionView::FixtureResolver.new(
95
+ 'layouts/application.html.erb' => '<%= yield %>',
96
+ 'dummy/index.html.erb' => 'Hello from index',
97
+ 'dummy/notify_airbrake_helper.html.erb' => 'notify_airbrake_helper',
98
+ 'dummy/notify_airbrake_sync_helper.html.erb' => 'notify_airbrake_helper_sync',
99
+ 'dummy/active_record_after_commit.html.erb' => 'active_record_after_commit',
100
+ 'dummy/active_record_after_rollback.html.erb' => 'active_record_after_rollback',
101
+ 'dummy/active_job.html.erb' => 'active_job',
102
+ 'dummy/resque.html.erb' => 'resque',
103
+ 'dummy/delayed_job.html.erb' => 'delayed_job'
104
+ )
105
+ ]
106
+
107
+ def index; end
108
+
109
+ def crash
110
+ raise AirbrakeTestError
111
+ end
112
+
113
+ def notify_airbrake_helper
114
+ notify_airbrake(AirbrakeTestError.new)
115
+ end
116
+
117
+ def notify_airbrake_sync_helper
118
+ notify_airbrake_sync(AirbrakeTestError.new)
119
+ end
120
+
121
+ def active_record_after_commit
122
+ Book.create(title: 'Bingo')
123
+ end
124
+
125
+ def active_record_after_rollback
126
+ Book.transaction do
127
+ Book.create(title: 'Bango')
128
+ raise ActiveRecord::Rollback
129
+ end
130
+ end
131
+
132
+ def active_job
133
+ BingoJob.perform_later('bango', 'bongo')
134
+ end
135
+
136
+ def resque
137
+ Resque.enqueue(BingoWorker, 'bango', 'bongo')
138
+ end
139
+
140
+ def delayed_job
141
+ Delayed::Job.enqueue(BangoJob.new('bingo', 'bongo'))
142
+ end
143
+ end
144
+
145
+ # Initializes middlewares and such.
146
+ DummyApp.initialize!
147
+
148
+ ActiveRecord::Base.connection.create_table(:books) do |t|
149
+ t.string(:title)
150
+ end
151
+
152
+ ActiveRecord::Migration.verbose = false
153
+ require 'generators/delayed_job/templates/migration'
154
+ ActiveRecord::Schema.define do
155
+ CreateDelayedJobs.up
156
+ end
@@ -0,0 +1,20 @@
1
+ # Keep this before any task definitions to collect extra info about tasks.
2
+ # Without this line the tests will fail.
3
+ Rake::TaskManager.record_task_metadata = true
4
+
5
+ namespace :bingo do
6
+ # This task contains *maximum* amount of information.
7
+ desc 'Dummy description'
8
+ task :bango, [:dummy_arg] => [:environment] do |_t, _args|
9
+ raise AirbrakeTestError
10
+ end
11
+
12
+ # This task contains *minimum* amount of information.
13
+ task :bongo do
14
+ raise AirbrakeTestError
15
+ end
16
+
17
+ task :environment do
18
+ # No-op.
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ require 'sinatra/base'
2
+
3
+ class SinatraApp1 < Sinatra::Base
4
+ get('/') { raise AirbrakeTestError }
5
+ end
6
+
7
+ Airbrake.configure(SinatraApp1) do |c|
8
+ c.project_id = 113743
9
+ c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
10
+ c.logger = Logger.new('/dev/null')
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'sinatra/base'
2
+
3
+ class SinatraApp2 < Sinatra::Base
4
+ get('/') { raise AirbrakeTestError }
5
+ end
6
+
7
+ Airbrake.configure(SinatraApp2) do |c|
8
+ c.project_id = 99123
9
+ c.project_key = 'ad04e13d806a90f96614ad8e529b2821'
10
+ c.logger = Logger.new('/dev/null')
11
+ end
@@ -0,0 +1,12 @@
1
+ class DummyApp < Sinatra::Base
2
+ use Airbrake::Rack::Middleware
3
+ use Warden::Manager
4
+
5
+ get '/' do
6
+ 'Hello from index'
7
+ end
8
+
9
+ get '/crash' do
10
+ raise AirbrakeTestError
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'integration/shared_examples/rack_examples'
3
+
4
+ RSpec.describe "Rack integration specs" do
5
+ let(:app) { DummyApp }
6
+
7
+ include_examples 'rack examples'
8
+
9
+ describe "context payload" do
10
+ it "includes version" do
11
+ get '/crash'
12
+ wait_for_a_request_with_body(
13
+ /"context":{.*"version":"1.2.3 Rack\.version.+Rack\.release/
14
+ )
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,216 @@
1
+ require 'spec_helper'
2
+ require 'integration/shared_examples/rack_examples'
3
+
4
+ RSpec.describe "Rails integration specs" do
5
+ include Warden::Test::Helpers
6
+
7
+ let(:app) { Rails.application }
8
+
9
+ include_examples 'rack examples'
10
+
11
+ if ::Rails.version.start_with?('5.')
12
+ it "inserts the Airbrake Rack middleware after DebugExceptions" do
13
+ middlewares = Rails.configuration.middleware.middlewares.map(&:inspect)
14
+ own_idx = middlewares.index('Airbrake::Rack::Middleware')
15
+
16
+ expect(middlewares[own_idx - 1]).to eq('ActionDispatch::DebugExceptions')
17
+ end
18
+ else
19
+ it "inserts the Airbrake Rack middleware after ConnectionManagement" do
20
+ middlewares = Rails.configuration.middleware.middlewares.map(&:inspect)
21
+ own_idx = middlewares.index('Airbrake::Rack::Middleware')
22
+
23
+ expect(middlewares[own_idx - 1]).
24
+ to eq('ActiveRecord::ConnectionAdapters::ConnectionManagement')
25
+ end
26
+ end
27
+
28
+ shared_examples 'context payload content' do |route|
29
+ before do
30
+ login_as(OpenStruct.new(id: 1, email: 'qa@example.com', username: 'qa-dept'))
31
+ get(route, foo: :bar)
32
+ end
33
+
34
+ it "includes component information" do
35
+ wait_for_a_request_with_body(/"context":{.*"component":"dummy".*}/)
36
+ end
37
+
38
+ it "includes action information" do
39
+ case route
40
+ when '/crash'
41
+ wait_for_a_request_with_body(/"context":{.*"action":"crash".*}/)
42
+ when '/notify_airbrake_helper'
43
+ wait_for_a_request_with_body(
44
+ /"context":{.*"action":"notify_airbrake_helper".*}/
45
+ )
46
+ when '/notify_airbrake_sync_helper'
47
+ wait_for_a_request_with_body(
48
+ /"context":{.*"action":"notify_airbrake_sync_helper".*}/
49
+ )
50
+ else
51
+ raise 'Unknown route'
52
+ end
53
+ end
54
+
55
+ it "includes version" do
56
+ wait_for_a_request_with_body(/"context":{.*"version":"1.2.3 Rails/)
57
+ end
58
+
59
+ it "includes session" do
60
+ wait_for_a_request_with_body(
61
+ /"context":{.*"session":{.*"session_id":"\w+".*}/
62
+ )
63
+ end
64
+
65
+ it "includes params" do
66
+ action = route[1..-1]
67
+ wait_for_a_request_with_body(
68
+ /"context":{.*"params":{.*"controller":"dummy","action":"#{action}".*}/
69
+ )
70
+ end
71
+ end
72
+
73
+ describe "context payload" do
74
+ context "when exception reported through middleware" do
75
+ include_examples('context payload content', '/crash')
76
+ end
77
+
78
+ context "when exception reported through the notify_airbrake helper" do
79
+ include_examples('context payload content', '/notify_airbrake_helper')
80
+ end
81
+
82
+ context "when exception reported through the notify_airbrake_sync helper" do
83
+ include_examples('context payload content', '/notify_airbrake_sync_helper')
84
+ end
85
+ end
86
+
87
+ describe "Active Record callbacks" do
88
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('5.1.0.alpha')
89
+ it "reports exceptions in after_commit callbacks" do
90
+ get '/active_record_after_commit'
91
+ wait_for(
92
+ a_request(:post, endpoint).
93
+ with(body: /"type":"AirbrakeTestError","message":"after_commit"/)
94
+ ).to have_been_made.twice
95
+ end
96
+
97
+ it "reports exceptions in after_rollback callbacks" do
98
+ get '/active_record_after_rollback'
99
+ wait_for(
100
+ a_request(:post, endpoint).
101
+ with(body: /"type":"AirbrakeTestError","message":"after_rollback"/)
102
+ ).to have_been_made.twice
103
+ end
104
+ else
105
+ it "reports exceptions in after_commit callbacks" do
106
+ get '/active_record_after_commit'
107
+ wait_for_a_request_with_body(
108
+ /"type":"AirbrakeTestError","message":"after_commit"/
109
+ )
110
+ end
111
+
112
+ it "reports exceptions in after_rollback callbacks" do
113
+ get '/active_record_after_rollback'
114
+ wait_for_a_request_with_body(
115
+ /"type":"AirbrakeTestError","message":"after_rollback"/
116
+ )
117
+ end
118
+ end
119
+ end
120
+
121
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('4.2')
122
+ describe "ActiveJob jobs" do
123
+ it "reports exceptions occurring in ActiveJob workers" do
124
+ get '/active_job'
125
+ sleep 2
126
+
127
+ wait_for(
128
+ a_request(:post, endpoint).
129
+ with(body: /"message":"active_job error"/)
130
+ ).to have_been_made.at_least_once
131
+ end
132
+
133
+ it "does not raise SystemStackError" do
134
+ get '/active_job'
135
+ sleep 2
136
+
137
+ wait_for(
138
+ a_request(:post, endpoint).
139
+ with(body: /"type":"SystemStackError"/)
140
+ ).not_to have_been_made
141
+ end
142
+
143
+ context "when Airbrake is not configured" do
144
+ it "doesn't report errors" do
145
+ allow(Airbrake).to receive(:build_notice).and_return(nil)
146
+ allow(Airbrake).to receive(:notify)
147
+
148
+ # Make sure we don't call `build_notice` more than 1 time. Rack
149
+ # integration will try to handle error 500 and we want to prevent
150
+ # that: https://github.com/airbrake/airbrake/pull/583
151
+ allow_any_instance_of(Airbrake::Rack::Middleware).to(
152
+ receive(:notify_airbrake).
153
+ and_return(nil)
154
+ )
155
+
156
+ get '/active_job'
157
+ sleep 2
158
+
159
+ wait_for(
160
+ a_request(:post, endpoint).
161
+ with(body: /"message":"active_job error"/)
162
+ ).not_to have_been_made
163
+
164
+ expect(Airbrake).to have_received(:build_notice)
165
+ expect(Airbrake).not_to have_received(:notify)
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ describe "Resque workers" do
172
+ it "reports exceptions occurring in Resque workers" do
173
+ with_resque { get '/resque' }
174
+
175
+ wait_for_a_request_with_body(
176
+ /"message":"resque\serror".*"params":{.*
177
+ "class":"BingoWorker","args":\["bango","bongo"\].*}/x
178
+ )
179
+ end
180
+ end
181
+
182
+ # Delayed Job doesn't support Ruby 1.9.2
183
+ if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('1.9.2')
184
+ describe "DelayedJob jobs" do
185
+ it "reports exceptions occurring in DelayedJob jobs" do
186
+ get '/delayed_job'
187
+ sleep 2
188
+
189
+ wait_for_a_request_with_body(
190
+ %r("message":"delayed_job\serror".*"params":{.*
191
+ "handler":"---\s!ruby/struct:BangoJob\\nbingo:\s
192
+ bingo\\nbongo:\sbongo\\n".*})x
193
+ )
194
+
195
+ # Two requests are performed during this example. We care only about one.
196
+ # Sleep guarantees that we let the unimportant request occur here and not
197
+ # elsewhere.
198
+ sleep 2
199
+ end
200
+ end
201
+ end
202
+
203
+ describe "notice payload when a user is authenticated without Warden" do
204
+ context "when the current_user method is defined" do
205
+ it "contains the user information" do
206
+ user = OpenStruct.new(id: 1, email: 'qa@example.com', username: 'qa-dept')
207
+ allow_any_instance_of(DummyController).to receive(:current_user) { user }
208
+
209
+ get '/crash'
210
+ wait_for_a_request_with_body(
211
+ /"user":{"id":"1","username":"qa-dept","email":"qa@example.com"}/
212
+ )
213
+ end
214
+ end
215
+ end
216
+ end