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.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +7 -0
  3. data/CHANGELOG.rdoc +129 -1
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +29 -1
  6. data/Gemfile +1 -1
  7. data/MIT-LICENSE +23 -0
  8. data/README.md +168 -222
  9. data/Rakefile +5 -11
  10. data/docs/notifiers/campfire.md +50 -0
  11. data/docs/notifiers/custom.md +42 -0
  12. data/docs/notifiers/datadog.md +51 -0
  13. data/docs/notifiers/email.md +195 -0
  14. data/docs/notifiers/google_chat.md +31 -0
  15. data/docs/notifiers/hipchat.md +66 -0
  16. data/docs/notifiers/irc.md +97 -0
  17. data/docs/notifiers/mattermost.md +115 -0
  18. data/docs/notifiers/slack.md +161 -0
  19. data/docs/notifiers/sns.md +37 -0
  20. data/docs/notifiers/teams.md +54 -0
  21. data/docs/notifiers/webhook.md +60 -0
  22. data/examples/sample_app.rb +54 -0
  23. data/examples/sinatra/Gemfile +8 -0
  24. data/examples/sinatra/Gemfile.lock +95 -0
  25. data/examples/sinatra/Procfile +2 -0
  26. data/examples/sinatra/README.md +11 -0
  27. data/examples/sinatra/config.ru +3 -0
  28. data/examples/sinatra/sinatra_app.rb +36 -0
  29. data/exception_notification.gemspec +32 -11
  30. data/gemfiles/rails4_0.gemfile +7 -0
  31. data/gemfiles/rails4_1.gemfile +7 -0
  32. data/gemfiles/rails4_2.gemfile +7 -0
  33. data/gemfiles/rails5_0.gemfile +7 -0
  34. data/gemfiles/rails5_1.gemfile +7 -0
  35. data/gemfiles/rails5_2.gemfile +7 -0
  36. data/gemfiles/rails6_0.gemfile +7 -0
  37. data/lib/exception_notification.rb +11 -0
  38. data/lib/exception_notification/rack.rb +55 -0
  39. data/lib/exception_notification/rails.rb +9 -0
  40. data/lib/exception_notification/resque.rb +22 -0
  41. data/lib/exception_notification/sidekiq.rb +27 -0
  42. data/lib/exception_notification/version.rb +3 -0
  43. data/lib/exception_notifier.rb +137 -61
  44. data/lib/exception_notifier/base_notifier.rb +24 -0
  45. data/lib/exception_notifier/campfire_notifier.rb +16 -11
  46. data/lib/exception_notifier/datadog_notifier.rb +153 -0
  47. data/lib/exception_notifier/email_notifier.rb +196 -0
  48. data/lib/exception_notifier/google_chat_notifier.rb +42 -0
  49. data/lib/exception_notifier/hipchat_notifier.rb +49 -0
  50. data/lib/exception_notifier/irc_notifier.rb +57 -0
  51. data/lib/exception_notifier/mattermost_notifier.rb +72 -0
  52. data/lib/exception_notifier/modules/backtrace_cleaner.rb +11 -0
  53. data/lib/exception_notifier/modules/error_grouping.rb +77 -0
  54. data/lib/exception_notifier/modules/formatter.rb +118 -0
  55. data/lib/exception_notifier/notifier.rb +9 -179
  56. data/lib/exception_notifier/slack_notifier.rb +111 -0
  57. data/lib/exception_notifier/sns_notifier.rb +85 -0
  58. data/lib/exception_notifier/teams_notifier.rb +193 -0
  59. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +3 -1
  60. data/lib/exception_notifier/views/exception_notifier/_data.html.erb +6 -1
  61. data/lib/exception_notifier/views/exception_notifier/_environment.html.erb +8 -6
  62. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -4
  63. data/lib/exception_notifier/views/exception_notifier/_request.html.erb +36 -5
  64. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +10 -5
  65. data/lib/exception_notifier/views/exception_notifier/_session.html.erb +10 -2
  66. data/lib/exception_notifier/views/exception_notifier/_session.text.erb +2 -2
  67. data/lib/exception_notifier/views/exception_notifier/_title.html.erb +3 -3
  68. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.html.erb +38 -11
  69. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.text.erb +10 -11
  70. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +38 -22
  71. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -3
  72. data/lib/exception_notifier/webhook_notifier.rb +51 -0
  73. data/lib/generators/exception_notification/install_generator.rb +15 -0
  74. data/lib/generators/exception_notification/templates/exception_notification.rb.erb +55 -0
  75. data/test/exception_notification/rack_test.rb +60 -0
  76. data/test/exception_notification/resque_test.rb +52 -0
  77. data/test/exception_notifier/campfire_notifier_test.rb +120 -0
  78. data/test/exception_notifier/datadog_notifier_test.rb +151 -0
  79. data/test/exception_notifier/email_notifier_test.rb +351 -0
  80. data/test/exception_notifier/google_chat_notifier_test.rb +181 -0
  81. data/test/exception_notifier/hipchat_notifier_test.rb +218 -0
  82. data/test/exception_notifier/irc_notifier_test.rb +137 -0
  83. data/test/exception_notifier/mattermost_notifier_test.rb +202 -0
  84. data/test/exception_notifier/modules/error_grouping_test.rb +165 -0
  85. data/test/exception_notifier/modules/formatter_test.rb +150 -0
  86. data/test/exception_notifier/sidekiq_test.rb +38 -0
  87. data/test/exception_notifier/slack_notifier_test.rb +227 -0
  88. data/test/exception_notifier/sns_notifier_test.rb +121 -0
  89. data/test/exception_notifier/teams_notifier_test.rb +90 -0
  90. data/test/exception_notifier/webhook_notifier_test.rb +96 -0
  91. data/test/exception_notifier_test.rb +182 -0
  92. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
  93. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
  94. data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
  95. data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
  96. data/test/test_helper.rb +12 -8
  97. metadata +333 -164
  98. data/.gemtest +0 -0
  99. data/Gemfile.lock +0 -122
  100. data/test/background_exception_notification_test.rb +0 -82
  101. data/test/campfire_test.rb +0 -53
  102. data/test/dummy/.gitignore +0 -4
  103. data/test/dummy/Gemfile +0 -33
  104. data/test/dummy/Gemfile.lock +0 -118
  105. data/test/dummy/Rakefile +0 -7
  106. data/test/dummy/app/controllers/application_controller.rb +0 -3
  107. data/test/dummy/app/controllers/posts_controller.rb +0 -30
  108. data/test/dummy/app/helpers/application_helper.rb +0 -2
  109. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  110. data/test/dummy/app/models/post.rb +0 -2
  111. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  112. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  113. data/test/dummy/app/views/posts/new.html.erb +0 -0
  114. data/test/dummy/app/views/posts/show.html.erb +0 -0
  115. data/test/dummy/config.ru +0 -4
  116. data/test/dummy/config/application.rb +0 -42
  117. data/test/dummy/config/boot.rb +0 -6
  118. data/test/dummy/config/database.yml +0 -22
  119. data/test/dummy/config/environment.rb +0 -13
  120. data/test/dummy/config/environments/development.rb +0 -24
  121. data/test/dummy/config/environments/production.rb +0 -49
  122. data/test/dummy/config/environments/test.rb +0 -35
  123. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  124. data/test/dummy/config/initializers/inflections.rb +0 -10
  125. data/test/dummy/config/initializers/mime_types.rb +0 -5
  126. data/test/dummy/config/initializers/secret_token.rb +0 -7
  127. data/test/dummy/config/initializers/session_store.rb +0 -8
  128. data/test/dummy/config/locales/en.yml +0 -5
  129. data/test/dummy/config/routes.rb +0 -3
  130. data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
  131. data/test/dummy/db/schema.rb +0 -24
  132. data/test/dummy/db/seeds.rb +0 -7
  133. data/test/dummy/lib/tasks/.gitkeep +0 -0
  134. data/test/dummy/public/404.html +0 -26
  135. data/test/dummy/public/422.html +0 -26
  136. data/test/dummy/public/500.html +0 -26
  137. data/test/dummy/public/favicon.ico +0 -0
  138. data/test/dummy/public/images/rails.png +0 -0
  139. data/test/dummy/public/index.html +0 -239
  140. data/test/dummy/public/javascripts/application.js +0 -2
  141. data/test/dummy/public/javascripts/controls.js +0 -965
  142. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  143. data/test/dummy/public/javascripts/effects.js +0 -1123
  144. data/test/dummy/public/javascripts/prototype.js +0 -6001
  145. data/test/dummy/public/javascripts/rails.js +0 -191
  146. data/test/dummy/public/robots.txt +0 -5
  147. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  148. data/test/dummy/public/stylesheets/scaffold.css +0 -56
  149. data/test/dummy/script/rails +0 -6
  150. data/test/dummy/test/fixtures/posts.yml +0 -11
  151. data/test/dummy/test/functional/posts_controller_test.rb +0 -239
  152. data/test/dummy/test/test_helper.rb +0 -13
  153. 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