exception_notification 4.3.0 → 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 +4 -4
- data/Appraisals +2 -2
- data/CHANGELOG.rdoc +14 -0
- data/CONTRIBUTING.md +18 -0
- data/Gemfile +1 -1
- data/README.md +64 -935
- data/Rakefile +2 -2
- 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 +6 -6
- data/examples/sinatra/config.ru +1 -1
- data/examples/sinatra/sinatra_app.rb +14 -10
- data/exception_notification.gemspec +27 -22
- data/gemfiles/rails4_0.gemfile +3 -3
- data/gemfiles/rails4_1.gemfile +3 -3
- data/gemfiles/rails4_2.gemfile +3 -3
- data/gemfiles/rails5_0.gemfile +3 -3
- data/gemfiles/rails5_1.gemfile +3 -3
- data/gemfiles/rails5_2.gemfile +7 -0
- data/gemfiles/rails6_0.gemfile +7 -0
- data/lib/exception_notification.rb +1 -0
- data/lib/exception_notification/rack.rb +8 -21
- data/lib/exception_notification/resque.rb +8 -10
- data/lib/exception_notification/sidekiq.rb +8 -12
- data/lib/exception_notification/version.rb +3 -0
- data/lib/exception_notifier.rb +20 -3
- data/lib/exception_notifier/base_notifier.rb +2 -3
- data/lib/exception_notifier/campfire_notifier.rb +12 -13
- data/lib/exception_notifier/datadog_notifier.rb +153 -0
- data/lib/exception_notifier/email_notifier.rb +64 -87
- data/lib/exception_notifier/google_chat_notifier.rb +25 -119
- data/lib/exception_notifier/hipchat_notifier.rb +11 -12
- data/lib/exception_notifier/irc_notifier.rb +32 -30
- data/lib/exception_notifier/mattermost_notifier.rb +47 -140
- data/lib/exception_notifier/modules/backtrace_cleaner.rb +0 -2
- data/lib/exception_notifier/modules/error_grouping.rb +5 -5
- data/lib/exception_notifier/modules/formatter.rb +118 -0
- data/lib/exception_notifier/notifier.rb +5 -6
- data/lib/exception_notifier/slack_notifier.rb +63 -40
- data/lib/exception_notifier/sns_notifier.rb +17 -11
- data/lib/exception_notifier/teams_notifier.rb +58 -44
- data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
- data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
- data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
- data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -2
- data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
- data/lib/exception_notifier/webhook_notifier.rb +14 -11
- data/lib/generators/exception_notification/install_generator.rb +5 -5
- data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
- data/test/exception_notification/rack_test.rb +27 -11
- data/test/exception_notification/resque_test.rb +52 -0
- data/test/exception_notifier/campfire_notifier_test.rb +42 -42
- data/test/exception_notifier/datadog_notifier_test.rb +151 -0
- data/test/exception_notifier/email_notifier_test.rb +269 -153
- data/test/exception_notifier/google_chat_notifier_test.rb +154 -101
- data/test/exception_notifier/hipchat_notifier_test.rb +78 -81
- data/test/exception_notifier/irc_notifier_test.rb +34 -34
- data/test/exception_notifier/mattermost_notifier_test.rb +164 -67
- data/test/exception_notifier/modules/error_grouping_test.rb +39 -40
- data/test/exception_notifier/modules/formatter_test.rb +150 -0
- data/test/exception_notifier/sidekiq_test.rb +6 -6
- data/test/exception_notifier/slack_notifier_test.rb +61 -60
- data/test/exception_notifier/sns_notifier_test.rb +27 -32
- data/test/exception_notifier/teams_notifier_test.rb +23 -26
- data/test/exception_notifier/webhook_notifier_test.rb +48 -46
- data/test/exception_notifier_test.rb +41 -38
- 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 +11 -14
- metadata +136 -166
- data/test/dummy/.gitignore +0 -4
- 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 -17
- data/test/dummy/config/environments/development.rb +0 -25
- data/test/dummy/config/environments/production.rb +0 -50
- 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 -8
- 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/functional/posts_controller_test.rb +0 -237
- data/test/dummy/test/test_helper.rb +0 -7
|
@@ -2,10 +2,9 @@ require 'test_helper'
|
|
|
2
2
|
require 'carrier-pigeon'
|
|
3
3
|
|
|
4
4
|
class IrcNotifierTest < ActiveSupport::TestCase
|
|
5
|
-
|
|
6
|
-
test "should send irc notification if properly configured" do
|
|
5
|
+
test 'should send irc notification if properly configured' do
|
|
7
6
|
options = {
|
|
8
|
-
:
|
|
7
|
+
domain: 'irc.example.com'
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
@@ -16,29 +15,30 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
16
15
|
irc.call(fake_exception)
|
|
17
16
|
end
|
|
18
17
|
|
|
19
|
-
test
|
|
18
|
+
test 'should exclude errors count in message if :accumulated_errors_count nil' do
|
|
20
19
|
irc = ExceptionNotifier::IrcNotifier.new({})
|
|
21
20
|
irc.stubs(:active?).returns(true)
|
|
22
21
|
|
|
23
|
-
irc.expects(:send_message).with{ |message| message.include?(
|
|
22
|
+
irc.expects(:send_message).with { |message| message.include?('divided by 0') }.once
|
|
24
23
|
irc.call(fake_exception)
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
test
|
|
26
|
+
test 'should include errors count in message if :accumulated_errors_count is 3' do
|
|
28
27
|
irc = ExceptionNotifier::IrcNotifier.new({})
|
|
29
28
|
irc.stubs(:active?).returns(true)
|
|
30
29
|
|
|
31
|
-
irc.expects(:send_message).with{ |message| message.include?("(3 times)'divided by 0'") }.once
|
|
30
|
+
irc.expects(:send_message).with { |message| message.include?("(3 times)'divided by 0'") }.once
|
|
32
31
|
irc.call(fake_exception, accumulated_errors_count: 3)
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
test
|
|
36
|
-
pre_callback_called
|
|
34
|
+
test 'should call pre/post_callback if specified' do
|
|
35
|
+
pre_callback_called = 0
|
|
36
|
+
post_callback_called = 0
|
|
37
37
|
|
|
38
38
|
options = {
|
|
39
|
-
:
|
|
40
|
-
:
|
|
41
|
-
:
|
|
39
|
+
domain: 'irc.example.com',
|
|
40
|
+
pre_callback: proc { |*| pre_callback_called += 1 },
|
|
41
|
+
post_callback: proc { |*| post_callback_called += 1 }
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
@@ -51,9 +51,9 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
51
51
|
assert_equal(1, post_callback_called)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
test
|
|
54
|
+
test 'should send irc notification without backtrace info if properly configured' do
|
|
55
55
|
options = {
|
|
56
|
-
:
|
|
56
|
+
domain: 'irc.example.com'
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
@@ -64,25 +64,25 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
64
64
|
irc.call(fake_exception_without_backtrace)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
test
|
|
67
|
+
test 'should properly construct URI from constituent parts' do
|
|
68
68
|
options = {
|
|
69
|
-
:
|
|
70
|
-
:
|
|
71
|
-
:
|
|
72
|
-
:
|
|
73
|
-
:
|
|
69
|
+
nick: 'BadNewsBot',
|
|
70
|
+
password: 'secret',
|
|
71
|
+
domain: 'irc.example.com',
|
|
72
|
+
port: 9999,
|
|
73
|
+
channel: '#exceptions'
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
CarrierPigeon.expects(:send).with(has_entry(uri:
|
|
76
|
+
CarrierPigeon.expects(:send).with(has_entry(uri: 'irc://BadNewsBot:secret@irc.example.com:9999/#exceptions'))
|
|
77
77
|
|
|
78
78
|
irc = ExceptionNotifier::IrcNotifier.new(options)
|
|
79
79
|
irc.call(fake_exception)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
test
|
|
82
|
+
test 'should properly add recipients if specified' do
|
|
83
83
|
options = {
|
|
84
84
|
domain: 'irc.example.com',
|
|
85
|
-
recipients: [
|
|
85
|
+
recipients: %w[peter michael samir]
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
|
|
@@ -93,7 +93,7 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
93
93
|
irc.call(fake_exception)
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
test
|
|
96
|
+
test 'should properly set miscellaneous options' do
|
|
97
97
|
options = {
|
|
98
98
|
domain: 'irc.example.com',
|
|
99
99
|
ssl: true,
|
|
@@ -102,11 +102,13 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
102
102
|
prefix: '[test notification]'
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
entries = {
|
|
106
106
|
ssl: true,
|
|
107
107
|
join: true,
|
|
108
|
-
notice: true
|
|
109
|
-
|
|
108
|
+
notice: true
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
CarrierPigeon.expects(:send).with(has_entries(entries)) do |v|
|
|
110
112
|
/\[test notification\]/.match(v[:message])
|
|
111
113
|
end
|
|
112
114
|
|
|
@@ -114,8 +116,8 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
114
116
|
irc.call(fake_exception)
|
|
115
117
|
end
|
|
116
118
|
|
|
117
|
-
test
|
|
118
|
-
wrong_params = { domain: '##scriptkiddie.com###'}
|
|
119
|
+
test 'should not send irc notification if badly configured' do
|
|
120
|
+
wrong_params = { domain: '##scriptkiddie.com###' }
|
|
119
121
|
irc = ExceptionNotifier::IrcNotifier.new(wrong_params)
|
|
120
122
|
|
|
121
123
|
assert_nil irc.call(fake_exception)
|
|
@@ -124,11 +126,9 @@ class IrcNotifierTest < ActiveSupport::TestCase
|
|
|
124
126
|
private
|
|
125
127
|
|
|
126
128
|
def fake_exception
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
e
|
|
131
|
-
end
|
|
129
|
+
5 / 0
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
e
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def fake_exception_without_backtrace
|
|
@@ -1,105 +1,202 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
require 'httparty'
|
|
3
|
+
require 'timecop'
|
|
4
|
+
require 'json'
|
|
3
5
|
|
|
4
6
|
class MattermostNotifierTest < ActiveSupport::TestCase
|
|
7
|
+
URL = 'http://localhost:8000'.freeze
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
9
21
|
}
|
|
10
|
-
mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
|
|
11
|
-
mattermost_notifier.httparty = FakeHTTParty.new
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
24
|
+
notifier.call ArgumentError.new('foo')
|
|
25
|
+
end
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
}
|
|
18
42
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
assert_equal '@channel', text[0]
|
|
22
|
-
assert_equal 'An *ArgumentError* occured.', text[2]
|
|
23
|
-
assert_equal '*foo*', text[3]
|
|
43
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
44
|
+
notifier.call ArgumentError.new('foo'), git_url: 'github.com/aschen'
|
|
24
45
|
end
|
|
25
46
|
|
|
26
|
-
test
|
|
27
|
-
|
|
28
|
-
:
|
|
29
|
-
:
|
|
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
|
|
30
56
|
}
|
|
31
|
-
mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
|
|
32
|
-
mattermost_notifier.httparty = FakeHTTParty.new
|
|
33
57
|
|
|
34
|
-
|
|
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
|
|
35
65
|
|
|
36
|
-
|
|
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
|
+
}
|
|
37
75
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
)
|
|
41
84
|
end
|
|
42
85
|
|
|
43
|
-
test
|
|
44
|
-
|
|
45
|
-
:
|
|
46
|
-
:
|
|
47
|
-
:avatar => 'http://site.com/icon.png'
|
|
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
|
|
48
90
|
}
|
|
49
|
-
mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
|
|
50
|
-
mattermost_notifier.httparty = FakeHTTParty.new
|
|
51
91
|
|
|
52
|
-
|
|
92
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
93
|
+
notifier.call(ArgumentError.new('foo'))
|
|
94
|
+
end
|
|
53
95
|
|
|
54
|
-
|
|
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
|
+
}
|
|
55
110
|
|
|
56
|
-
|
|
57
|
-
|
|
111
|
+
HTTParty.expects(:post).with(URL, opts)
|
|
112
|
+
notifier.call(ArgumentError.new('foo'), accumulated_errors_count: 5)
|
|
58
113
|
end
|
|
59
114
|
|
|
60
|
-
test 'should
|
|
61
|
-
|
|
62
|
-
:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
69
141
|
}
|
|
70
|
-
mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
|
|
71
|
-
mattermost_notifier.httparty = FakeHTTParty.new
|
|
72
142
|
|
|
73
|
-
|
|
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
|
+
])
|
|
74
150
|
|
|
75
|
-
|
|
76
|
-
assert 'clara', options[:basic_auth][:username]
|
|
77
|
-
assert 'password', options[:basic_auth][:password]
|
|
151
|
+
notifier.call(exception, env: test_env)
|
|
78
152
|
end
|
|
79
153
|
|
|
80
|
-
|
|
81
|
-
mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
|
|
82
|
-
exception = ArgumentError.new("foo")
|
|
83
|
-
mattermost_notifier.instance_variable_set(:@exception, exception)
|
|
84
|
-
mattermost_notifier.instance_variable_set(:@options, {})
|
|
154
|
+
private
|
|
85
155
|
|
|
86
|
-
|
|
156
|
+
def notifier
|
|
157
|
+
ExceptionNotifier::MattermostNotifier.new(webhook_url: URL)
|
|
87
158
|
end
|
|
88
159
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
94
171
|
|
|
95
|
-
|
|
172
|
+
def default_headers
|
|
173
|
+
{ 'Content-Type' => 'application/json' }
|
|
96
174
|
end
|
|
97
|
-
end
|
|
98
175
|
|
|
99
|
-
|
|
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
|
|
100
185
|
|
|
101
|
-
def
|
|
102
|
-
|
|
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
|
|
103
192
|
end
|
|
104
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
|
|
105
202
|
end
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
3
|
class ErrorGroupTest < ActiveSupport::TestCase
|
|
4
|
-
|
|
5
4
|
setup do
|
|
6
5
|
module TestModule
|
|
7
6
|
include ExceptionNotifier::ErrorGrouping
|
|
8
|
-
@@error_grouping_cache = ActiveSupport::Cache::FileStore.new(
|
|
7
|
+
@@error_grouping_cache = ActiveSupport::Cache::FileStore.new('test/dummy/tmp/non_default_location')
|
|
9
8
|
end
|
|
10
9
|
|
|
11
|
-
@exception = RuntimeError.new(
|
|
12
|
-
@exception.stubs(:backtrace).returns([
|
|
10
|
+
@exception = RuntimeError.new('ERROR')
|
|
11
|
+
@exception.stubs(:backtrace).returns(['/path/where/error/raised:1'])
|
|
13
12
|
|
|
14
|
-
@exception2 = RuntimeError.new(
|
|
15
|
-
@exception2.stubs(:backtrace).returns([
|
|
13
|
+
@exception2 = RuntimeError.new('ERROR2')
|
|
14
|
+
@exception2.stubs(:backtrace).returns(['/path/where/error/found:2'])
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
teardown do
|
|
@@ -20,88 +19,88 @@ class ErrorGroupTest < ActiveSupport::TestCase
|
|
|
20
19
|
TestModule.fallback_cache_store.clear
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
test
|
|
22
|
+
test 'should add additional option: error_grouping' do
|
|
24
23
|
assert_respond_to TestModule, :error_grouping
|
|
25
24
|
assert_respond_to TestModule, :error_grouping=
|
|
26
25
|
end
|
|
27
26
|
|
|
28
|
-
test
|
|
27
|
+
test 'should set error_grouping to false default' do
|
|
29
28
|
assert_equal false, TestModule.error_grouping
|
|
30
29
|
end
|
|
31
30
|
|
|
32
|
-
test
|
|
31
|
+
test 'should add additional option: error_grouping_cache' do
|
|
33
32
|
assert_respond_to TestModule, :error_grouping_cache
|
|
34
33
|
assert_respond_to TestModule, :error_grouping_cache=
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
test
|
|
36
|
+
test 'should add additional option: error_grouping_period' do
|
|
38
37
|
assert_respond_to TestModule, :error_grouping_period
|
|
39
38
|
assert_respond_to TestModule, :error_grouping_period=
|
|
40
39
|
end
|
|
41
40
|
|
|
42
|
-
test
|
|
41
|
+
test 'shoud set error_grouping_period to 5.minutes default' do
|
|
43
42
|
assert_equal 300, TestModule.error_grouping_period
|
|
44
43
|
end
|
|
45
44
|
|
|
46
|
-
test
|
|
45
|
+
test 'should add additional option: notification_trigger' do
|
|
47
46
|
assert_respond_to TestModule, :notification_trigger
|
|
48
47
|
assert_respond_to TestModule, :notification_trigger=
|
|
49
48
|
end
|
|
50
49
|
|
|
51
|
-
test
|
|
52
|
-
assert_nil TestModule.error_count(
|
|
50
|
+
test 'should return errors count nil when not same error for .error_count' do
|
|
51
|
+
assert_nil TestModule.error_count('something')
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
test
|
|
56
|
-
TestModule.error_grouping_cache.write(
|
|
57
|
-
assert_equal 13, TestModule.error_count(
|
|
54
|
+
test 'should return errors count when same error for .error_count' do
|
|
55
|
+
TestModule.error_grouping_cache.write('error_key', 13)
|
|
56
|
+
assert_equal 13, TestModule.error_count('error_key')
|
|
58
57
|
end
|
|
59
58
|
|
|
60
|
-
test
|
|
61
|
-
TestModule.error_grouping_cache.stubs(:read).raises(RuntimeError.new
|
|
59
|
+
test 'should fallback to memory store cache if specified cache store failed to read' do
|
|
60
|
+
TestModule.error_grouping_cache.stubs(:read).raises(RuntimeError.new('Failed to read'))
|
|
62
61
|
original_fallback = TestModule.fallback_cache_store
|
|
63
62
|
TestModule.expects(:fallback_cache_store).returns(original_fallback).at_least_once
|
|
64
63
|
|
|
65
|
-
assert_nil TestModule.error_count(
|
|
64
|
+
assert_nil TestModule.error_count('something_to_read')
|
|
66
65
|
end
|
|
67
66
|
|
|
68
|
-
test
|
|
67
|
+
test 'should save error with count for .save_error_count' do
|
|
69
68
|
count = rand(1..10)
|
|
70
69
|
|
|
71
|
-
TestModule.save_error_count(
|
|
72
|
-
assert_equal count, TestModule.error_grouping_cache.read(
|
|
70
|
+
TestModule.save_error_count('error_key', count)
|
|
71
|
+
assert_equal count, TestModule.error_grouping_cache.read('error_key')
|
|
73
72
|
end
|
|
74
73
|
|
|
75
|
-
test
|
|
76
|
-
TestModule.error_grouping_cache.stubs(:write).raises(RuntimeError.new
|
|
74
|
+
test 'should fallback to memory store cache if specified cache store failed to write' do
|
|
75
|
+
TestModule.error_grouping_cache.stubs(:write).raises(RuntimeError.new('Failed to write'))
|
|
77
76
|
original_fallback = TestModule.fallback_cache_store
|
|
78
77
|
TestModule.expects(:fallback_cache_store).returns(original_fallback).at_least_once
|
|
79
78
|
|
|
80
|
-
assert TestModule.save_error_count(
|
|
79
|
+
assert TestModule.save_error_count('something_to_cache', rand(1..10))
|
|
81
80
|
end
|
|
82
81
|
|
|
83
|
-
test
|
|
82
|
+
test 'should save accumulated_errors_count into options' do
|
|
84
83
|
options = {}
|
|
85
84
|
TestModule.group_error!(@exception, options)
|
|
86
85
|
|
|
87
86
|
assert_equal 1, options[:accumulated_errors_count]
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
test
|
|
89
|
+
test 'should not group error if different exception in .group_error!' do
|
|
91
90
|
options1 = {}
|
|
92
|
-
TestModule.expects(:save_error_count).with{|key, count| key.is_a?(String) && count == 1}.times(4).returns(true)
|
|
91
|
+
TestModule.expects(:save_error_count).with { |key, count| key.is_a?(String) && count == 1 }.times(4).returns(true)
|
|
93
92
|
TestModule.group_error!(@exception, options1)
|
|
94
93
|
|
|
95
94
|
options2 = {}
|
|
96
|
-
TestModule.group_error!(NoMethodError.new(
|
|
95
|
+
TestModule.group_error!(NoMethodError.new('method not found'), options2)
|
|
97
96
|
|
|
98
97
|
assert_equal 1, options1[:accumulated_errors_count]
|
|
99
98
|
assert_equal 1, options2[:accumulated_errors_count]
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
test
|
|
101
|
+
test 'should not group error is same exception but different message or backtrace' do
|
|
103
102
|
options1 = {}
|
|
104
|
-
TestModule.expects(:save_error_count).with{|key, count| key.is_a?(String) && count == 1}.times(4).returns(true)
|
|
103
|
+
TestModule.expects(:save_error_count).with { |key, count| key.is_a?(String) && count == 1 }.times(4).returns(true)
|
|
105
104
|
TestModule.group_error!(@exception, options1)
|
|
106
105
|
|
|
107
106
|
options2 = {}
|
|
@@ -111,7 +110,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
|
|
|
111
110
|
assert_equal 1, options2[:accumulated_errors_count]
|
|
112
111
|
end
|
|
113
112
|
|
|
114
|
-
test
|
|
113
|
+
test 'should group error if same exception and message' do
|
|
115
114
|
options = {}
|
|
116
115
|
|
|
117
116
|
10.times do |i|
|
|
@@ -122,7 +121,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
|
|
|
122
121
|
assert_equal 10, options[:accumulated_errors_count]
|
|
123
122
|
end
|
|
124
123
|
|
|
125
|
-
test
|
|
124
|
+
test 'should group error if same exception and backtrace' do
|
|
126
125
|
options = {}
|
|
127
126
|
|
|
128
127
|
10.times do |i|
|
|
@@ -133,9 +132,9 @@ class ErrorGroupTest < ActiveSupport::TestCase
|
|
|
133
132
|
assert_equal 10, options[:accumulated_errors_count]
|
|
134
133
|
end
|
|
135
134
|
|
|
136
|
-
test
|
|
135
|
+
test 'should group error by that message have high priority' do
|
|
137
136
|
message_based_key = "exception:#{Zlib.crc32("RuntimeError\nmessage:ERROR")}"
|
|
138
|
-
backtrace_based_key = "exception:#{Zlib.crc32("RuntimeError\
|
|
137
|
+
backtrace_based_key = "exception:#{Zlib.crc32("RuntimeError\npath:/path/where/error/raised:1")}"
|
|
139
138
|
|
|
140
139
|
TestModule.save_error_count(message_based_key, 1)
|
|
141
140
|
TestModule.save_error_count(backtrace_based_key, 1)
|
|
@@ -146,7 +145,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
|
|
|
146
145
|
TestModule.group_error!(@exception, {})
|
|
147
146
|
end
|
|
148
147
|
|
|
149
|
-
test
|
|
148
|
+
test 'use default formula if not specify notification_trigger in .send_notification?' do
|
|
150
149
|
TestModule.stubs(:notification_trigger).returns(nil)
|
|
151
150
|
|
|
152
151
|
count = 16
|
|
@@ -155,12 +154,12 @@ class ErrorGroupTest < ActiveSupport::TestCase
|
|
|
155
154
|
assert TestModule.send_notification?(@exception, count)
|
|
156
155
|
end
|
|
157
156
|
|
|
158
|
-
test
|
|
159
|
-
trigger =
|
|
157
|
+
test 'use specified trigger in .send_notification?' do
|
|
158
|
+
trigger = proc { |_exception, count| (count % 4).zero? }
|
|
160
159
|
TestModule.stubs(:notification_trigger).returns(trigger)
|
|
161
160
|
|
|
162
161
|
count = 16
|
|
163
162
|
trigger.expects(:call).with(@exception, count).returns(true)
|
|
164
163
|
assert TestModule.send_notification?(@exception, count)
|
|
165
164
|
end
|
|
166
|
-
end
|
|
165
|
+
end
|