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
@@ -1,173 +1,98 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe "Rake integration" do
4
- let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/113743/notices' }
4
+ let(:task) { Rake::Task['bingo:bango'] }
5
5
 
6
- let(:routes_endpoint) do
7
- 'https://api.airbrake.io/api/v5/projects/113743/routes-stats'
8
- end
6
+ before { Rails.application.load_tasks }
9
7
 
10
- let(:queries_endpoint) do
11
- 'https://api.airbrake.io/api/v5/projects/113743/queries-stats'
12
- end
8
+ after do
9
+ expect { task.invoke }.to raise_error(AirbrakeTestError)
13
10
 
14
- def wait_for_a_request_with_body(body)
15
- wait_for(a_request(:post, endpoint).with(body: body)).to have_been_made.once
11
+ # Rake ensures that each task is executed only once per session. For testing
12
+ # purposes, we run the task multiple times.
13
+ task.reenable
16
14
  end
17
15
 
18
- def expect_no_requests_with_body(body)
19
- sleep 1
20
- expect(a_request(:post, endpoint).with(body: body)).not_to have_been_made
16
+ it "sends the exception to Airbrake" do
17
+ expect(Airbrake).to receive(:notify_sync)
18
+ .with(an_instance_of(Airbrake::Notice))
21
19
  end
22
20
 
23
- before do
24
- stub_request(:post, endpoint).to_return(status: 201, body: '{}')
25
-
26
- # Airbrake Ruby has a background thread that sends performance requests
27
- # periodically. We don't want this to get in the way.
28
- [routes_endpoint, queries_endpoint].each do |e|
29
- stub_request(:put, e).to_return(status: 200, body: '')
21
+ describe "contains the context payload, which" do
22
+ it "includes correct component" do
23
+ expect(Airbrake).to receive(:notify_sync)
24
+ .with(a_notice_with(%i[context component], 'rake'))
30
25
  end
31
26
 
32
- Rails.application.load_tasks
33
-
34
- expect { faulty_task.invoke }.to raise_error(AirbrakeTestError)
35
- end
36
-
37
- after do
38
- # Rake ensures that each task is executed only once per session. For testing
39
- # purposes, we run the task multiple times.
40
- faulty_task.reenable
27
+ it "includes correct action" do
28
+ expect(Airbrake).to receive(:notify_sync)
29
+ .with(a_notice_with(%i[context action], 'bingo:bango'))
30
+ end
41
31
  end
42
32
 
43
- describe "a task with maximum information, which raises an exception" do
44
- let(:faulty_task) { Rake::Task['bingo:bango'] }
45
-
46
- it "sends the exception to Airbrake" do
47
- wait_for_a_request_with_body(/"errors":\[{"type":"AirbrakeTestError"/)
33
+ describe "contains the params payload, which" do
34
+ it "includes a task name" do
35
+ expect(Airbrake).to receive(:notify_sync)
36
+ .with(a_notice_with(%i[params rake_task name], 'bingo:bango'))
48
37
  end
49
38
 
50
- describe "contains the context payload, which" do
51
- it "includes correct component" do
52
- wait_for_a_request_with_body(/"context":{.*"component":"rake".*}/)
53
- end
54
-
55
- it "includes correct action" do
56
- wait_for_a_request_with_body(
57
- /"context":{.*"action":"bingo:bango".*/
58
- )
59
- end
39
+ it "includes a timestamp" do
40
+ expected_notice = a_notice_with(
41
+ %i[params rake_task timestamp], /20\d\d\-\d\d-\d\d.+/
42
+ )
43
+ expect(Airbrake).to receive(:notify_sync).with(expected_notice)
60
44
  end
61
45
 
62
- describe "contains the params payload, which" do
63
- it "includes a task name" do
64
- wait_for_a_request_with_body(
65
- /"params":{.*"rake_task":{.*"name":"bingo:bango".*}.*}/
66
- )
67
- end
68
-
69
- it "includes a timestamp" do
70
- wait_for_a_request_with_body(
71
- /"params":{.*"rake_task":{.*"timestamp":"201\d\-\d\d-\d\d.+".*}.*}/
72
- )
73
- end
74
-
75
- it "includes investigation" do
76
- # rubocop:disable Metrics/LineLength
77
- wait_for_a_request_with_body(
78
- /"params":{.*"rake_task":{.*"investigation":".+Investigating bingo:bango.+".*}.*}/
79
- )
80
- # rubocop:enable Metrics/LineLength
81
- end
82
-
83
- it "includes full comment" do
84
- wait_for_a_request_with_body(
85
- /"params":{.*"rake_task":{.*"full_comment":"Dummy description".*}.*}/
86
- )
87
- end
88
-
89
- it "includes arg names" do
90
- wait_for_a_request_with_body(
91
- /"params":{.*"rake_task":{.*"arg_names":\["dummy_arg"\].*}.*}/
92
- )
93
- end
94
-
95
- it "includes arg description" do
96
- wait_for_a_request_with_body(
97
- /"params":{.*"rake_task":{.*"arg_description":"\[dummy_arg\]".*}.*}/
98
- )
99
- end
100
-
101
- it "includes locations" do
102
- # rubocop:disable Metrics/LineLength
103
- wait_for_a_request_with_body(
104
- %r("params":{.*"rake_task":{.*"locations":\[".+spec/apps/rails/dummy_task.rake:\d+:in.+"\].*}.*})
105
- )
106
- # rubocop:enable Metrics/LineLength
107
- end
108
-
109
- it "includes sources" do
110
- wait_for_a_request_with_body(
111
- /"params":{.*"rake_task":{.*"sources":\["environment"\].*}.*}/
112
- )
113
- end
114
-
115
- it "includes prerequisite tasks" do
116
- # rubocop:disable Metrics/LineLength
117
- wait_for_a_request_with_body(
118
- /"params":{.*"rake_task":{.*"prerequisite_tasks":\[{"name":"bingo:environment".+\].*}.*}/
119
- )
120
- # rubocop:enable Metrics/LineLength
121
- end
46
+ it "includes investigation" do
47
+ expected_notice = a_notice_with(
48
+ %i[params rake_task investigation], /Investigating bingo:bango/
49
+ )
50
+ expect(Airbrake).to receive(:notify_sync).with(expected_notice)
51
+ end
122
52
 
123
- it "includes argv info" do
124
- wait_for_a_request_with_body(
125
- %r("params":{.*"argv":".*spec/integration/rails/.+_spec.rb".*})
126
- )
127
- end
53
+ it "includes full comment" do
54
+ expect(Airbrake).to receive(:notify_sync)
55
+ .with(a_notice_with(%i[params rake_task full_comment], 'Dummy description'))
56
+ end
128
57
 
129
- it "includes #execute args" do
130
- wait_for_a_request_with_body(
131
- /"params":{.*"execute_args":\[\].*}/
132
- )
133
- end
58
+ it "includes arg names" do
59
+ expect(Airbrake).to receive(:notify_sync)
60
+ .with(a_notice_with(%i[params rake_task arg_names], [:dummy_arg]))
134
61
  end
135
- end
136
62
 
137
- describe "a task with minimum information, which raises an exception" do
138
- let(:faulty_task) { Rake::Task['bingo:bongo'] }
63
+ it "includes arg description" do
64
+ expect(Airbrake).to receive(:notify_sync)
65
+ .with(a_notice_with(%i[params rake_task arg_description], '[dummy_arg]'))
66
+ end
139
67
 
140
- describe "doesn't contain in the params payload" do
141
- it "full comment" do
142
- expect_no_requests_with_body(
143
- /"params":{.*"rake_task":{.*"full_comment":"Dummy description".*}.*}/
144
- )
68
+ it "includes locations" do
69
+ expect(Airbrake).to receive(:notify_sync) do |notice|
70
+ expect(notice[:params][:rake_task][:locations])
71
+ .to match(array_including(%r{spec/apps/rails/dummy_task.rake:\d+:in}))
145
72
  end
73
+ end
146
74
 
147
- it "arg names" do
148
- expect_no_requests_with_body(
149
- /"params":{.*"rake_task":{.*"arg_names":\["dummy_arg"\].*}.*}/
150
- )
151
- end
75
+ it "includes sources" do
76
+ expect(Airbrake).to receive(:notify_sync)
77
+ .with(a_notice_with(%i[params rake_task sources], ['environment']))
78
+ end
152
79
 
153
- it "arg description" do
154
- expect_no_requests_with_body(
155
- /"params":{.*"rake_task":{.*"arg_description":"\[dummy_arg\]".*}.*}/
156
- )
80
+ it "includes prerequisite tasks" do
81
+ expect(Airbrake).to receive(:notify_sync) do |notice|
82
+ expect(notice[:params][:rake_task][:prerequisite_tasks])
83
+ .to match(array_including(hash_including(name: 'bingo:environment')))
157
84
  end
85
+ end
158
86
 
159
- it "sources" do
160
- expect_no_requests_with_body(
161
- /"params":{.*"rake_task":{.*"sources":\["environment"\].*}.*}/
162
- )
163
- end
87
+ it "includes argv info" do
88
+ expect(Airbrake).to receive(:notify_sync)
89
+ .with(a_notice_with(%i[params argv], %r{spec/integration/rails/.+_spec.rb}))
90
+ end
164
91
 
165
- it "prerequisite tasks" do
166
- # rubocop:disable Metrics/LineLength
167
- expect_no_requests_with_body(
168
- /"params":{.*"rake_task":{.*"prerequisite_tasks":\[{"name":"bingo:environment".+\].*}.*}/
169
- )
170
- # rubocop:enable Metrics/LineLength
92
+ it "includes execute args" do
93
+ expect(Airbrake).to receive(:notify_sync) do |notice|
94
+ expect(notice[:params][:execute_args])
95
+ .to be_an_instance_of(Rake::TaskArguments)
171
96
  end
172
97
  end
173
98
  end
@@ -1,139 +1,110 @@
1
1
  RSpec.shared_examples 'rack examples' do
2
2
  include Warden::Test::Helpers
3
3
 
4
- after { Warden.test_reset! }
5
-
6
4
  let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/113743/notices' }
7
5
 
8
- let(:routes_endpoint) do
9
- 'https://api.airbrake.io/api/v5/projects/113743/routes-stats'
10
- end
11
-
12
- let(:queries_endpoint) do
13
- 'https://api.airbrake.io/api/v5/projects/113743/queries-stats'
14
- end
15
-
16
- def wait_for_a_request_with_body(body)
17
- allow(Airbrake.notifiers[:performance][:default]).
18
- to receive(:notify).and_return(nil)
19
- wait_for(a_request(:post, endpoint).with(body: body)).
20
- to have_been_made.at_least_once
21
- end
22
-
23
6
  before do
24
- # Make sure the Logger integration doesn't get in the way.
25
- allow_any_instance_of(Logger).to receive(:airbrake_notifier).and_return(nil)
26
-
27
- allow(Airbrake.notifiers[:performance][:default]).
28
- to receive(:notify).and_return(nil)
29
7
  stub_request(:post, endpoint).to_return(status: 200, body: '')
30
- [routes_endpoint, queries_endpoint].each do |endpoint|
31
- stub_request(:put, endpoint).to_return(status: 200, body: '')
32
- end
8
+ Airbrake::Config.instance.merge(performance_stats: false)
33
9
  end
34
10
 
11
+ after { Warden.test_reset! }
12
+
35
13
  describe "application routes" do
36
14
  describe "/index" do
37
15
  it "successfully returns 200 and body" do
16
+ expect(Airbrake).not_to receive(:notify)
17
+
38
18
  get '/'
39
19
 
40
20
  expect(last_response.status).to eq(200)
41
21
  expect(last_response.body).to eq('Hello from index')
42
-
43
- wait_for(a_request(:post, endpoint)).not_to have_been_made
44
22
  end
45
23
  end
46
24
 
47
25
  describe "/crash" do
48
26
  it "returns 500 and sends a notice to Airbrake" do
49
- get '/crash'
27
+ expect(Airbrake).to receive(:notify).with(
28
+ an_instance_of(Airbrake::Notice)
29
+ ) do |notice|
30
+ expect(notice[:errors].first[:type]).to eq('AirbrakeTestError')
31
+ end
50
32
 
51
- expect(last_response.status).to eq(500)
52
- wait_for_a_request_with_body(/"errors":\[{"type":"AirbrakeTestError"/)
33
+ get '/crash'
53
34
  end
54
35
  end
55
36
  end
56
37
 
57
- describe "context payload" do
58
- context "when the user is present" do
59
- let(:common_user_params) do
60
- { id: 1, email: 'qa@example.com', username: 'qa-dept' }
61
- end
62
-
63
- before do
64
- login_as(OpenStruct.new(user_params))
65
- get '/crash'
66
- sleep 2
67
- end
68
-
69
- context "when the user has first and last names" do
70
- let(:user_params) do
71
- common_user_params.merge(first_name: 'Bingo', last_name: 'Bongo')
72
- end
73
-
74
- it "reports the user's first and last names" do
75
- wait_for_a_request_with_body(/
76
- "context":{.*
77
- "user":{
78
- "id":"1",
79
- "name":"Bingo\sBongo",
80
- "username":"qa-dept",
81
- "email":"qa@example.com"}
82
- /x)
83
- end
84
- end
85
-
86
- context "when the user has only name" do
87
- let(:user_params) do
88
- common_user_params.merge(name: 'Bingo')
89
- end
38
+ describe "user payload" do
39
+ let(:user) do
40
+ OpenStruct.new(
41
+ id: 1,
42
+ email: 'qa@example.com',
43
+ username: 'qa-dept',
44
+ first_name: 'John',
45
+ last_name: 'Doe'
46
+ )
47
+ end
90
48
 
91
- it "reports the user's name" do
92
- wait_for_a_request_with_body(/
93
- "context":{.*
94
- "user":{
95
- "id":"1",
96
- "name":"Bingo",
97
- "username":"qa-dept",
98
- "email":"qa@example.com"}
99
- /x)
100
- end
101
- end
49
+ before { login_as(user) }
50
+
51
+ it "reports user info" do
52
+ get '/crash'
53
+ sleep 2
54
+
55
+ body = /
56
+ "context":{.*
57
+ "user":{
58
+ "id":"1",
59
+ "name":"John\sDoe",
60
+ "username":"qa-dept",
61
+ "email":"qa@example.com"}
62
+ /x
63
+ expect(a_request(:post, endpoint).with(body: body))
64
+ .to have_been_made.at_least_once
102
65
  end
66
+ end
103
67
 
104
- context "when additional parameters present" do
105
- before do
106
- get '/crash', nil, 'HTTP_USER_AGENT' => 'Bot', 'HTTP_REFERER' => 'bingo.com'
107
- sleep 2
108
- end
68
+ context "when additional parameters are present" do
69
+ before do
70
+ get '/crash', nil, 'HTTP_USER_AGENT' => 'Bot', 'HTTP_REFERER' => 'bingo.com'
71
+ sleep 2
72
+ end
109
73
 
110
- it "contains url" do
111
- wait_for_a_request_with_body(
112
- %r("context":{.*"url":"http://example\.org/crash".*})
113
- )
114
- end
74
+ it "contains url" do
75
+ body = %r("context":{.*"url":"http://example\.org/crash".*})
76
+ expect(a_request(:post, endpoint).with(body: body))
77
+ .to have_been_made.at_least_once
78
+ end
115
79
 
116
- it "contains hostname" do
117
- wait_for_a_request_with_body(/"context":{.*"hostname":".+".*}/)
118
- end
80
+ it "contains hostname" do
81
+ body = /"context":{.*"hostname":".+".*}/
82
+ expect(a_request(:post, endpoint).with(body: body))
83
+ .to have_been_made.at_least_once
84
+ end
119
85
 
120
- it "contains userAgent" do
121
- wait_for_a_request_with_body(/"context":{.*"userAgent":"Bot".*}/)
122
- end
86
+ it "contains userAgent" do
87
+ body = /"context":{.*"userAgent":"Bot".*}/
88
+ expect(a_request(:post, endpoint).with(body: body))
89
+ .to have_been_made.at_least_once
90
+ end
123
91
 
124
- it "contains referer" do
125
- wait_for_a_request_with_body(/"context":{.*"referer":"bingo.com".*}/)
126
- end
92
+ it "contains referer" do
93
+ body = /"context":{.*"referer":"bingo.com".*}/
94
+ expect(a_request(:post, endpoint).with(body: body))
95
+ .to have_been_made.at_least_once
96
+ end
127
97
 
128
- it "contains HTTP headers" do
129
- wait_for_a_request_with_body(
130
- /"context":{.*"headers":{.*"CONTENT_LENGTH":"0".*}/
131
- )
132
- end
98
+ it "contains HTTP headers" do
99
+ body = /"context":{.*"headers":{.*"CONTENT_LENGTH":"0".*}/
100
+ expect(a_request(:post, endpoint).with(body: body))
101
+ .to have_been_made.at_least_once
102
+ end
133
103
 
134
- it "contains HTTP method" do
135
- wait_for_a_request_with_body(/"context":{.*"httpMethod":"GET".*}/)
136
- end
104
+ it "contains HTTP method" do
105
+ body = /"context":{.*"httpMethod":"GET".*}/
106
+ expect(a_request(:post, endpoint).with(body: body))
107
+ .to have_been_made.at_least_once
137
108
  end
138
109
  end
139
110
  end