exception_notification 3.0.1 → 4.4.0
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.
- checksums.yaml +7 -0
- data/Appraisals +7 -0
- data/CHANGELOG.rdoc +129 -1
- data/CODE_OF_CONDUCT.md +22 -0
- data/CONTRIBUTING.md +29 -1
- data/Gemfile +1 -1
- data/MIT-LICENSE +23 -0
- data/README.md +168 -222
- data/Rakefile +5 -11
- data/docs/notifiers/campfire.md +50 -0
- data/docs/notifiers/custom.md +42 -0
- data/docs/notifiers/datadog.md +51 -0
- data/docs/notifiers/email.md +195 -0
- data/docs/notifiers/google_chat.md +31 -0
- data/docs/notifiers/hipchat.md +66 -0
- data/docs/notifiers/irc.md +97 -0
- data/docs/notifiers/mattermost.md +115 -0
- data/docs/notifiers/slack.md +161 -0
- data/docs/notifiers/sns.md +37 -0
- data/docs/notifiers/teams.md +54 -0
- data/docs/notifiers/webhook.md +60 -0
- data/examples/sample_app.rb +54 -0
- data/examples/sinatra/Gemfile +8 -0
- data/examples/sinatra/Gemfile.lock +95 -0
- data/examples/sinatra/Procfile +2 -0
- data/examples/sinatra/README.md +11 -0
- data/examples/sinatra/config.ru +3 -0
- data/examples/sinatra/sinatra_app.rb +36 -0
- data/exception_notification.gemspec +32 -11
- data/gemfiles/rails4_0.gemfile +7 -0
- data/gemfiles/rails4_1.gemfile +7 -0
- data/gemfiles/rails4_2.gemfile +7 -0
- data/gemfiles/rails5_0.gemfile +7 -0
- data/gemfiles/rails5_1.gemfile +7 -0
- data/gemfiles/rails5_2.gemfile +7 -0
- data/gemfiles/rails6_0.gemfile +7 -0
- data/lib/exception_notification.rb +11 -0
- data/lib/exception_notification/rack.rb +55 -0
- data/lib/exception_notification/rails.rb +9 -0
- data/lib/exception_notification/resque.rb +22 -0
- data/lib/exception_notification/sidekiq.rb +27 -0
- data/lib/exception_notification/version.rb +3 -0
- data/lib/exception_notifier.rb +137 -61
- data/lib/exception_notifier/base_notifier.rb +24 -0
- data/lib/exception_notifier/campfire_notifier.rb +16 -11
- data/lib/exception_notifier/datadog_notifier.rb +153 -0
- data/lib/exception_notifier/email_notifier.rb +196 -0
- data/lib/exception_notifier/google_chat_notifier.rb +42 -0
- data/lib/exception_notifier/hipchat_notifier.rb +49 -0
- data/lib/exception_notifier/irc_notifier.rb +57 -0
- data/lib/exception_notifier/mattermost_notifier.rb +72 -0
- data/lib/exception_notifier/modules/backtrace_cleaner.rb +11 -0
- data/lib/exception_notifier/modules/error_grouping.rb +77 -0
- data/lib/exception_notifier/modules/formatter.rb +118 -0
- data/lib/exception_notifier/notifier.rb +9 -179
- data/lib/exception_notifier/slack_notifier.rb +111 -0
- data/lib/exception_notifier/sns_notifier.rb +85 -0
- data/lib/exception_notifier/teams_notifier.rb +193 -0
- data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +3 -1
- data/lib/exception_notifier/views/exception_notifier/_data.html.erb +6 -1
- data/lib/exception_notifier/views/exception_notifier/_environment.html.erb +8 -6
- data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -4
- data/lib/exception_notifier/views/exception_notifier/_request.html.erb +36 -5
- data/lib/exception_notifier/views/exception_notifier/_request.text.erb +10 -5
- data/lib/exception_notifier/views/exception_notifier/_session.html.erb +10 -2
- data/lib/exception_notifier/views/exception_notifier/_session.text.erb +2 -2
- data/lib/exception_notifier/views/exception_notifier/_title.html.erb +3 -3
- data/lib/exception_notifier/views/exception_notifier/background_exception_notification.html.erb +38 -11
- data/lib/exception_notifier/views/exception_notifier/background_exception_notification.text.erb +10 -11
- data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +38 -22
- data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -3
- data/lib/exception_notifier/webhook_notifier.rb +51 -0
- data/lib/generators/exception_notification/install_generator.rb +15 -0
- data/lib/generators/exception_notification/templates/exception_notification.rb.erb +55 -0
- data/test/exception_notification/rack_test.rb +60 -0
- data/test/exception_notification/resque_test.rb +52 -0
- data/test/exception_notifier/campfire_notifier_test.rb +120 -0
- data/test/exception_notifier/datadog_notifier_test.rb +151 -0
- data/test/exception_notifier/email_notifier_test.rb +351 -0
- data/test/exception_notifier/google_chat_notifier_test.rb +181 -0
- data/test/exception_notifier/hipchat_notifier_test.rb +218 -0
- data/test/exception_notifier/irc_notifier_test.rb +137 -0
- data/test/exception_notifier/mattermost_notifier_test.rb +202 -0
- data/test/exception_notifier/modules/error_grouping_test.rb +165 -0
- data/test/exception_notifier/modules/formatter_test.rb +150 -0
- data/test/exception_notifier/sidekiq_test.rb +38 -0
- data/test/exception_notifier/slack_notifier_test.rb +227 -0
- data/test/exception_notifier/sns_notifier_test.rb +121 -0
- data/test/exception_notifier/teams_notifier_test.rb +90 -0
- data/test/exception_notifier/webhook_notifier_test.rb +96 -0
- data/test/exception_notifier_test.rb +182 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
- data/test/test_helper.rb +12 -8
- metadata +333 -164
- data/.gemtest +0 -0
- data/Gemfile.lock +0 -122
- data/test/background_exception_notification_test.rb +0 -82
- data/test/campfire_test.rb +0 -53
- data/test/dummy/.gitignore +0 -4
- data/test/dummy/Gemfile +0 -33
- data/test/dummy/Gemfile.lock +0 -118
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/controllers/posts_controller.rb +0 -30
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/helpers/posts_helper.rb +0 -2
- data/test/dummy/app/models/post.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/app/views/posts/_form.html.erb +0 -0
- data/test/dummy/app/views/posts/new.html.erb +0 -0
- data/test/dummy/app/views/posts/show.html.erb +0 -0
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -42
- data/test/dummy/config/boot.rb +0 -6
- data/test/dummy/config/database.yml +0 -22
- data/test/dummy/config/environment.rb +0 -13
- data/test/dummy/config/environments/development.rb +0 -24
- data/test/dummy/config/environments/production.rb +0 -49
- data/test/dummy/config/environments/test.rb +0 -35
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -10
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -7
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -3
- data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
- data/test/dummy/db/schema.rb +0 -24
- data/test/dummy/db/seeds.rb +0 -7
- data/test/dummy/lib/tasks/.gitkeep +0 -0
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/images/rails.png +0 -0
- data/test/dummy/public/index.html +0 -239
- data/test/dummy/public/javascripts/application.js +0 -2
- data/test/dummy/public/javascripts/controls.js +0 -965
- data/test/dummy/public/javascripts/dragdrop.js +0 -974
- data/test/dummy/public/javascripts/effects.js +0 -1123
- data/test/dummy/public/javascripts/prototype.js +0 -6001
- data/test/dummy/public/javascripts/rails.js +0 -191
- data/test/dummy/public/robots.txt +0 -5
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/dummy/public/stylesheets/scaffold.css +0 -56
- data/test/dummy/script/rails +0 -6
- data/test/dummy/test/fixtures/posts.yml +0 -11
- data/test/dummy/test/functional/posts_controller_test.rb +0 -239
- data/test/dummy/test/test_helper.rb +0 -13
- data/test/exception_notification_test.rb +0 -73
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'rack'
|
|
3
|
+
|
|
4
|
+
# silence_warnings trick around require can be removed once
|
|
5
|
+
# https://github.com/hipchat/hipchat-rb/pull/174
|
|
6
|
+
# gets merged and released
|
|
7
|
+
silence_warnings do
|
|
8
|
+
require 'hipchat'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class HipchatNotifierTest < ActiveSupport::TestCase
|
|
12
|
+
test 'should send hipchat notification if properly configured' do
|
|
13
|
+
options = {
|
|
14
|
+
api_token: 'good_token',
|
|
15
|
+
room_name: 'room_name',
|
|
16
|
+
color: 'yellow'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
HipChat::Room.any_instance.expects(:send).with('Exception', fake_body, color: 'yellow')
|
|
20
|
+
|
|
21
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
22
|
+
hipchat.call(fake_exception)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
test 'should call pre/post_callback if specified' do
|
|
26
|
+
pre_callback_called = 0
|
|
27
|
+
post_callback_called = 0
|
|
28
|
+
options = {
|
|
29
|
+
api_token: 'good_token',
|
|
30
|
+
room_name: 'room_name',
|
|
31
|
+
color: 'yellow',
|
|
32
|
+
pre_callback: proc { |*| pre_callback_called += 1 },
|
|
33
|
+
post_callback: proc { |*| post_callback_called += 1 }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
HipChat::Room.any_instance.expects(:send).with('Exception', fake_body, { color: 'yellow' }.merge(options.except(:api_token, :room_name)))
|
|
37
|
+
|
|
38
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
39
|
+
hipchat.call(fake_exception)
|
|
40
|
+
assert_equal(1, pre_callback_called)
|
|
41
|
+
assert_equal(1, post_callback_called)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
test 'should send hipchat notification without backtrace info if properly configured' do
|
|
45
|
+
options = {
|
|
46
|
+
api_token: 'good_token',
|
|
47
|
+
room_name: 'room_name',
|
|
48
|
+
color: 'yellow'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
HipChat::Room.any_instance.expects(:send).with('Exception', fake_body_without_backtrace, color: 'yellow')
|
|
52
|
+
|
|
53
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
54
|
+
hipchat.call(fake_exception_without_backtrace)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test 'should allow custom from value if set' do
|
|
58
|
+
options = {
|
|
59
|
+
api_token: 'good_token',
|
|
60
|
+
room_name: 'room_name',
|
|
61
|
+
from: 'TrollFace'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
HipChat::Room.any_instance.expects(:send).with('TrollFace', fake_body, color: 'red')
|
|
65
|
+
|
|
66
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
67
|
+
hipchat.call(fake_exception)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
test 'should not send hipchat notification if badly configured' do
|
|
71
|
+
wrong_params = {
|
|
72
|
+
api_token: 'bad_token',
|
|
73
|
+
room_name: 'test_room'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
HipChat::Client.stubs(:new).with('bad_token', api_version: 'v1').returns(nil)
|
|
77
|
+
|
|
78
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(wrong_params)
|
|
79
|
+
assert_nil hipchat.room
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
test 'should not send hipchat notification if api_key is missing' do
|
|
83
|
+
wrong_params = { room_name: 'test_room' }
|
|
84
|
+
|
|
85
|
+
HipChat::Client.stubs(:new).with(nil, api_version: 'v1').returns(nil)
|
|
86
|
+
|
|
87
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(wrong_params)
|
|
88
|
+
assert_nil hipchat.room
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test 'should not send hipchat notification if room_name is missing' do
|
|
92
|
+
wrong_params = { api_token: 'good_token' }
|
|
93
|
+
|
|
94
|
+
HipChat::Client.stubs(:new).with('good_token', api_version: 'v1').returns({})
|
|
95
|
+
|
|
96
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(wrong_params)
|
|
97
|
+
assert_nil hipchat.room
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
test 'should send hipchat notification with message_template' do
|
|
101
|
+
options = {
|
|
102
|
+
api_token: 'good_token',
|
|
103
|
+
room_name: 'room_name',
|
|
104
|
+
color: 'yellow',
|
|
105
|
+
message_template: ->(exception, _) { "This is custom message: '#{exception.message}'" }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
HipChat::Room.any_instance.expects(:send).with('Exception', "This is custom message: '#{fake_exception.message}'", color: 'yellow')
|
|
109
|
+
|
|
110
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
111
|
+
hipchat.call(fake_exception)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
test 'should send hipchat notification exclude accumulated errors count' do
|
|
115
|
+
options = {
|
|
116
|
+
api_token: 'good_token',
|
|
117
|
+
room_name: 'room_name',
|
|
118
|
+
color: 'yellow'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
HipChat::Room.any_instance.expects(:send).with { |_, msg, _| msg.start_with?('A new exception occurred:') }
|
|
122
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
123
|
+
hipchat.call(fake_exception)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
test 'should send hipchat notification include accumulated errors count' do
|
|
127
|
+
options = {
|
|
128
|
+
api_token: 'good_token',
|
|
129
|
+
room_name: 'room_name',
|
|
130
|
+
color: 'yellow'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
HipChat::Room.any_instance.expects(:send).with { |_, msg, _| msg.start_with?('The exception occurred 3 times:') }
|
|
134
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
135
|
+
hipchat.call(fake_exception, accumulated_errors_count: 3)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
test 'should send hipchat notification with HTML-escaped meessage if using default message_template' do
|
|
139
|
+
options = {
|
|
140
|
+
api_token: 'good_token',
|
|
141
|
+
room_name: 'room_name',
|
|
142
|
+
color: 'yellow'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
exception = fake_exception_with_html_characters
|
|
146
|
+
body = "A new exception occurred: '#{Rack::Utils.escape_html(exception.message)}' on '#{exception.backtrace.first}'"
|
|
147
|
+
|
|
148
|
+
HipChat::Room.any_instance.expects(:send).with('Exception', body, color: 'yellow')
|
|
149
|
+
|
|
150
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
151
|
+
hipchat.call(exception)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
test 'should use APIv1 if api_version is not specified' do
|
|
155
|
+
options = {
|
|
156
|
+
api_token: 'good_token',
|
|
157
|
+
room_name: 'room_name'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
HipChat::Client.stubs(:new).with('good_token', api_version: 'v1').returns({})
|
|
161
|
+
|
|
162
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
163
|
+
hipchat.call(fake_exception)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
test 'should use APIv2 when specified' do
|
|
167
|
+
options = {
|
|
168
|
+
api_token: 'good_token',
|
|
169
|
+
room_name: 'room_name',
|
|
170
|
+
api_version: 'v2'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
HipChat::Client.stubs(:new).with('good_token', api_version: 'v2').returns({})
|
|
174
|
+
|
|
175
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
176
|
+
hipchat.call(fake_exception)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
test 'should allow server_url value (for a self-hosted HipChat Server) if set' do
|
|
180
|
+
options = {
|
|
181
|
+
api_token: 'good_token',
|
|
182
|
+
room_name: 'room_name',
|
|
183
|
+
api_version: 'v2',
|
|
184
|
+
server_url: 'https://domain.com'
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
HipChat::Client.stubs(:new).with('good_token', api_version: 'v2', server_url: 'https://domain.com').returns({})
|
|
188
|
+
|
|
189
|
+
hipchat = ExceptionNotifier::HipchatNotifier.new(options)
|
|
190
|
+
hipchat.call(fake_exception)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
def fake_body
|
|
196
|
+
"A new exception occurred: '#{fake_exception.message}' on '#{fake_exception.backtrace.first}'"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def fake_exception
|
|
200
|
+
5 / 0
|
|
201
|
+
rescue StandardError => e
|
|
202
|
+
e
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def fake_exception_with_html_characters
|
|
206
|
+
raise StandardError, 'an error with <html> characters'
|
|
207
|
+
rescue StandardError => e
|
|
208
|
+
e
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def fake_body_without_backtrace
|
|
212
|
+
"A new exception occurred: '#{fake_exception_without_backtrace.message}'"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def fake_exception_without_backtrace
|
|
216
|
+
StandardError.new('my custom error')
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'carrier-pigeon'
|
|
3
|
+
|
|
4
|
+
class IrcNotifierTest < ActiveSupport::TestCase
|
|
5
|
+
test 'should send irc notification if properly configured' do
|
|
6
|
+
options = {
|
|
7
|
+
domain: 'irc.example.com'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
11
|
+
/divided by 0/.match(v[:message])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
15
|
+
irc.call(fake_exception)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test 'should exclude errors count in message if :accumulated_errors_count nil' do
|
|
19
|
+
irc = ExceptionNotifier::IrcNotifier.new({})
|
|
20
|
+
irc.stubs(:active?).returns(true)
|
|
21
|
+
|
|
22
|
+
irc.expects(:send_message).with { |message| message.include?('divided by 0') }.once
|
|
23
|
+
irc.call(fake_exception)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test 'should include errors count in message if :accumulated_errors_count is 3' do
|
|
27
|
+
irc = ExceptionNotifier::IrcNotifier.new({})
|
|
28
|
+
irc.stubs(:active?).returns(true)
|
|
29
|
+
|
|
30
|
+
irc.expects(:send_message).with { |message| message.include?("(3 times)'divided by 0'") }.once
|
|
31
|
+
irc.call(fake_exception, accumulated_errors_count: 3)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test 'should call pre/post_callback if specified' do
|
|
35
|
+
pre_callback_called = 0
|
|
36
|
+
post_callback_called = 0
|
|
37
|
+
|
|
38
|
+
options = {
|
|
39
|
+
domain: 'irc.example.com',
|
|
40
|
+
pre_callback: proc { |*| pre_callback_called += 1 },
|
|
41
|
+
post_callback: proc { |*| post_callback_called += 1 }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
45
|
+
/divided by 0/.match(v[:message])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
49
|
+
irc.call(fake_exception)
|
|
50
|
+
assert_equal(1, pre_callback_called)
|
|
51
|
+
assert_equal(1, post_callback_called)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test 'should send irc notification without backtrace info if properly configured' do
|
|
55
|
+
options = {
|
|
56
|
+
domain: 'irc.example.com'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
60
|
+
/my custom error/.match(v[:message])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
64
|
+
irc.call(fake_exception_without_backtrace)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
test 'should properly construct URI from constituent parts' do
|
|
68
|
+
options = {
|
|
69
|
+
nick: 'BadNewsBot',
|
|
70
|
+
password: 'secret',
|
|
71
|
+
domain: 'irc.example.com',
|
|
72
|
+
port: 9999,
|
|
73
|
+
channel: '#exceptions'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
CarrierPigeon.expects(:send).with(has_entry(uri: 'irc://BadNewsBot:secret@irc.example.com:9999/#exceptions'))
|
|
77
|
+
|
|
78
|
+
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
79
|
+
irc.call(fake_exception)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
test 'should properly add recipients if specified' do
|
|
83
|
+
options = {
|
|
84
|
+
domain: 'irc.example.com',
|
|
85
|
+
recipients: %w[peter michael samir]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
89
|
+
/peter, michael, samir/.match(v[:message])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
93
|
+
irc.call(fake_exception)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
test 'should properly set miscellaneous options' do
|
|
97
|
+
options = {
|
|
98
|
+
domain: 'irc.example.com',
|
|
99
|
+
ssl: true,
|
|
100
|
+
join: true,
|
|
101
|
+
notice: true,
|
|
102
|
+
prefix: '[test notification]'
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
entries = {
|
|
106
|
+
ssl: true,
|
|
107
|
+
join: true,
|
|
108
|
+
notice: true
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
CarrierPigeon.expects(:send).with(has_entries(entries)) do |v|
|
|
112
|
+
/\[test notification\]/.match(v[:message])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
116
|
+
irc.call(fake_exception)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
test 'should not send irc notification if badly configured' do
|
|
120
|
+
wrong_params = { domain: '##scriptkiddie.com###' }
|
|
121
|
+
irc = ExceptionNotifier::IrcNotifier.new(wrong_params)
|
|
122
|
+
|
|
123
|
+
assert_nil irc.call(fake_exception)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def fake_exception
|
|
129
|
+
5 / 0
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
e
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def fake_exception_without_backtrace
|
|
135
|
+
StandardError.new('my custom error')
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'httparty'
|
|
3
|
+
require 'timecop'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
class MattermostNotifierTest < ActiveSupport::TestCase
|
|
7
|
+
URL = 'http://localhost:8000'.freeze
|
|
8
|
+
|
|
9
|
+
def setup
|
|
10
|
+
Timecop.freeze('2018-12-09 12:07:16 UTC')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def teardown
|
|
14
|
+
Timecop.return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test 'should send notification if properly configured' do
|
|
18
|
+
opts = {
|
|
19
|
+
body: default_body.to_json,
|
|
20
|
+
headers: default_headers
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
24
|
+
notifier.call ArgumentError.new('foo')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
test 'should send notification with create issue link if specified' do
|
|
28
|
+
body = default_body.merge(
|
|
29
|
+
text: [
|
|
30
|
+
'@channel',
|
|
31
|
+
error_occurred_in,
|
|
32
|
+
'An *ArgumentError* occurred.',
|
|
33
|
+
'*foo*',
|
|
34
|
+
github_link
|
|
35
|
+
].join("\n")
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
opts = {
|
|
39
|
+
body: body.to_json,
|
|
40
|
+
headers: default_headers
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
44
|
+
notifier.call ArgumentError.new('foo'), git_url: 'github.com/aschen'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test 'should add username and icon_url params to the notification if specified' do
|
|
48
|
+
body = default_body.merge(
|
|
49
|
+
username: 'Test Bot',
|
|
50
|
+
icon_url: 'http://site.com/icon.png'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
opts = {
|
|
54
|
+
body: body.to_json,
|
|
55
|
+
headers: default_headers
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
59
|
+
notifier.call(
|
|
60
|
+
ArgumentError.new('foo'),
|
|
61
|
+
username: 'Test Bot',
|
|
62
|
+
avatar: 'http://site.com/icon.png'
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test 'should add other HTTParty options to params' do
|
|
67
|
+
opts = {
|
|
68
|
+
basic_auth: {
|
|
69
|
+
username: 'clara',
|
|
70
|
+
password: 'password'
|
|
71
|
+
},
|
|
72
|
+
body: default_body.to_json,
|
|
73
|
+
headers: default_headers
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
77
|
+
notifier.call(
|
|
78
|
+
ArgumentError.new('foo'),
|
|
79
|
+
basic_auth: {
|
|
80
|
+
username: 'clara',
|
|
81
|
+
password: 'password'
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
test "should use 'An' for exceptions count if :accumulated_errors_count option is nil" do
|
|
87
|
+
opts = {
|
|
88
|
+
body: default_body.to_json,
|
|
89
|
+
headers: default_headers
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
93
|
+
notifier.call(ArgumentError.new('foo'))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
test 'shoud use direct errors count if :accumulated_errors_count option is 5' do
|
|
97
|
+
body = default_body.merge(
|
|
98
|
+
text: [
|
|
99
|
+
'@channel',
|
|
100
|
+
error_occurred_in,
|
|
101
|
+
'5 *ArgumentError* occurred.',
|
|
102
|
+
'*foo*'
|
|
103
|
+
].join("\n")
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
opts = {
|
|
107
|
+
body: body.to_json,
|
|
108
|
+
headers: default_headers
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
112
|
+
notifier.call(ArgumentError.new('foo'), accumulated_errors_count: 5)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
test 'should include backtrace and request info' do
|
|
116
|
+
body = default_body.merge(
|
|
117
|
+
text: [
|
|
118
|
+
'@channel',
|
|
119
|
+
error_occurred_in,
|
|
120
|
+
'An *ArgumentError* occurred.',
|
|
121
|
+
'*foo*',
|
|
122
|
+
'### Request',
|
|
123
|
+
'```',
|
|
124
|
+
'* url : http://test.address/?id=foo',
|
|
125
|
+
'* http_method : GET',
|
|
126
|
+
'* ip_address : 127.0.0.1',
|
|
127
|
+
'* parameters : {"id"=>"foo"}',
|
|
128
|
+
'* timestamp : 2018-12-09 12:07:16 UTC',
|
|
129
|
+
'```',
|
|
130
|
+
'### Backtrace',
|
|
131
|
+
'```',
|
|
132
|
+
"* app/controllers/my_controller.rb:53:in `my_controller_params'",
|
|
133
|
+
"* app/controllers/my_controller.rb:34:in `update'",
|
|
134
|
+
'```'
|
|
135
|
+
].join("\n")
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
opts = {
|
|
139
|
+
body: body.to_json,
|
|
140
|
+
headers: default_headers
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
144
|
+
|
|
145
|
+
exception = ArgumentError.new('foo')
|
|
146
|
+
exception.set_backtrace([
|
|
147
|
+
"app/controllers/my_controller.rb:53:in `my_controller_params'",
|
|
148
|
+
"app/controllers/my_controller.rb:34:in `update'"
|
|
149
|
+
])
|
|
150
|
+
|
|
151
|
+
notifier.call(exception, env: test_env)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def notifier
|
|
157
|
+
ExceptionNotifier::MattermostNotifier.new(webhook_url: URL)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def default_body
|
|
161
|
+
{
|
|
162
|
+
text: [
|
|
163
|
+
'@channel',
|
|
164
|
+
error_occurred_in,
|
|
165
|
+
'An *ArgumentError* occurred.',
|
|
166
|
+
'*foo*'
|
|
167
|
+
].join("\n"),
|
|
168
|
+
username: 'Exception Notifier'
|
|
169
|
+
}
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def default_headers
|
|
173
|
+
{ 'Content-Type' => 'application/json' }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def test_env
|
|
177
|
+
Rack::MockRequest.env_for(
|
|
178
|
+
'/',
|
|
179
|
+
'HTTP_HOST' => 'test.address',
|
|
180
|
+
'REMOTE_ADDR' => '127.0.0.1',
|
|
181
|
+
'HTTP_USER_AGENT' => 'Rails Testing',
|
|
182
|
+
params: { id: 'foo' }
|
|
183
|
+
)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def error_occurred_in
|
|
187
|
+
if defined?(::Rails) && ::Rails.respond_to?(:env)
|
|
188
|
+
'### ⚠️ Error occurred in test ⚠️'
|
|
189
|
+
else
|
|
190
|
+
'### ⚠️ Error occurred ⚠️'
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def github_link
|
|
195
|
+
if defined?(::Rails) && ::Rails.respond_to?(:application)
|
|
196
|
+
'[Create an issue](github.com/aschen/dummy/issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)'
|
|
197
|
+
else
|
|
198
|
+
# TODO: fix missing app name
|
|
199
|
+
'[Create an issue](github.com/aschen//issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)'
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|