exception_notification 4.4.0 → 4.4.1

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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/Appraisals +3 -1
  3. data/CHANGELOG.rdoc +10 -0
  4. data/Gemfile +2 -0
  5. data/README.md +34 -8
  6. data/Rakefile +2 -0
  7. data/examples/sample_app.rb +2 -0
  8. data/examples/sinatra/Gemfile +2 -0
  9. data/examples/sinatra/config.ru +2 -0
  10. data/examples/sinatra/sinatra_app.rb +6 -2
  11. data/exception_notification.gemspec +9 -8
  12. data/gemfiles/rails4_0.gemfile +3 -3
  13. data/gemfiles/rails4_1.gemfile +3 -3
  14. data/gemfiles/rails4_2.gemfile +3 -3
  15. data/gemfiles/rails5_0.gemfile +3 -3
  16. data/gemfiles/rails5_1.gemfile +3 -3
  17. data/gemfiles/rails5_2.gemfile +3 -3
  18. data/lib/exception_notification.rb +2 -0
  19. data/lib/exception_notification/rack.rb +24 -13
  20. data/lib/exception_notification/rails.rb +2 -0
  21. data/lib/exception_notification/resque.rb +2 -0
  22. data/lib/exception_notification/sidekiq.rb +5 -3
  23. data/lib/exception_notification/version.rb +3 -1
  24. data/lib/exception_notifier.rb +46 -7
  25. data/lib/exception_notifier/base_notifier.rb +8 -2
  26. data/lib/exception_notifier/campfire_notifier.rb +2 -0
  27. data/lib/exception_notifier/datadog_notifier.rb +12 -9
  28. data/lib/exception_notifier/email_notifier.rb +11 -3
  29. data/lib/exception_notifier/google_chat_notifier.rb +2 -0
  30. data/lib/exception_notifier/hipchat_notifier.rb +2 -0
  31. data/lib/exception_notifier/irc_notifier.rb +4 -3
  32. data/lib/exception_notifier/mattermost_notifier.rb +10 -0
  33. data/lib/exception_notifier/modules/backtrace_cleaner.rb +2 -0
  34. data/lib/exception_notifier/modules/error_grouping.rb +19 -9
  35. data/lib/exception_notifier/modules/formatter.rb +3 -0
  36. data/lib/exception_notifier/notifier.rb +5 -1
  37. data/lib/exception_notifier/slack_notifier.rb +2 -0
  38. data/lib/exception_notifier/sns_notifier.rb +4 -3
  39. data/lib/exception_notifier/teams_notifier.rb +10 -3
  40. data/lib/exception_notifier/webhook_notifier.rb +3 -3
  41. data/lib/generators/exception_notification/install_generator.rb +8 -2
  42. data/test/exception_notification/rack_test.rb +48 -2
  43. data/test/exception_notification/resque_test.rb +2 -0
  44. data/test/exception_notifier/campfire_notifier_test.rb +8 -1
  45. data/test/exception_notifier/datadog_notifier_test.rb +2 -0
  46. data/test/exception_notifier/email_notifier_test.rb +29 -3
  47. data/test/exception_notifier/google_chat_notifier_test.rb +15 -11
  48. data/test/exception_notifier/hipchat_notifier_test.rb +8 -2
  49. data/test/exception_notifier/irc_notifier_test.rb +2 -0
  50. data/test/exception_notifier/mattermost_notifier_test.rb +73 -24
  51. data/test/exception_notifier/modules/error_grouping_test.rb +2 -0
  52. data/test/exception_notifier/modules/formatter_test.rb +2 -0
  53. data/test/exception_notifier/sidekiq_test.rb +3 -11
  54. data/test/exception_notifier/slack_notifier_test.rb +12 -10
  55. data/test/exception_notifier/sns_notifier_test.rb +9 -7
  56. data/test/exception_notifier/teams_notifier_test.rb +2 -0
  57. data/test/exception_notifier/webhook_notifier_test.rb +6 -4
  58. data/test/exception_notifier_test.rb +112 -6
  59. data/test/support/exception_notifier_helper.rb +14 -0
  60. data/test/test_helper.rb +5 -1
  61. metadata +22 -27
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  require 'exception_notification/resque'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  # silence_warnings trick around require can be removed once
@@ -94,11 +96,16 @@ class CampfireNotifierTest < ActiveSupport::TestCase
94
96
  {
95
97
  message: {
96
98
  type: 'PasteMessage',
97
- body: "A new exception occurred: 'divided by 0' on '/Users/sebastian/exception_notification/test/campfire_test.rb:45:in `/'"
99
+ body: fake_notification_body
98
100
  }
99
101
  }
100
102
  end
101
103
 
104
+ def fake_notification_body
105
+ "A new exception occurred: 'divided by 0' on " \
106
+ "/Users/sebastian/exception_notification/test/campfire_test.rb:45:in `/'"
107
+ end
108
+
102
109
  def fake_exception
103
110
  5 / 0
104
111
  rescue StandardError => e
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'dogapi/common'
3
5
  require 'dogapi/event'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'action_mailer'
3
5
  require 'action_controller'
@@ -116,7 +118,7 @@ class EmailNotifierTest < ActiveSupport::TestCase
116
118
  env: {
117
119
  'REQUEST_METHOD' => 'GET',
118
120
  'rack.input' => '',
119
- 'invalid_encoding' => "R\xC3\xA9sum\xC3\xA9".force_encoding(Encoding::ASCII)
121
+ 'invalid_encoding' => "R\xC3\xA9sum\xC3\xA9".dup.force_encoding(Encoding::ASCII)
120
122
  }
121
123
  )
122
124
 
@@ -220,8 +222,10 @@ class EmailNotifierWithEnvTest < ActiveSupport::TestCase
220
222
  email_headers: { 'X-Custom-Header' => 'foobar' },
221
223
  sections: %w[new_section request session environment backtrace],
222
224
  background_sections: %w[new_bkg_section backtrace data],
223
- pre_callback: proc { |_opts, _notifier, _backtrace, _message, message_opts| message_opts[:pre_callback_called] = 1 },
224
- post_callback: proc { |_opts, _notifier, _backtrace, _message, message_opts| message_opts[:post_callback_called] = 1 }
225
+ pre_callback:
226
+ proc { |_opts, _notifier, _backtrace, _message, message_opts| message_opts[:pre_callback_called] = 1 },
227
+ post_callback:
228
+ proc { |_opts, _notifier, _backtrace, _message, message_opts| message_opts[:post_callback_called] = 1 }
225
229
  )
226
230
 
227
231
  @controller = HomeController.new
@@ -349,3 +353,25 @@ class EmailNotifierWithEnvTest < ActiveSupport::TestCase
349
353
  assert_equal '[ERROR] (ZeroDivisionError) "divided by 0"', mail.subject
350
354
  end
351
355
  end
356
+
357
+ class EmailNotifierWithCustomParentClassTest < ActiveSupport::TestCase
358
+ class ApplicationMailer < ActionMailer::Base
359
+ default from: 'infrastructure@example.com'
360
+ end
361
+
362
+ setup do
363
+ @exception = ZeroDivisionError.new('divided by 0')
364
+ @exception.set_backtrace(["#{__FILE__}:#{__LINE__}"])
365
+ end
366
+
367
+ test 'uses default from configured parent class' do
368
+ email_notifier = ExceptionNotifier::EmailNotifier.new(
369
+ mailer_parent: 'EmailNotifierWithCustomParentClassTest::ApplicationMailer',
370
+ exception_recipients: %w[dummyexceptions@example.com]
371
+ )
372
+ mail = email_notifier.call(@exception)
373
+
374
+ assert email_notifier.__send__(:mailer) < ApplicationMailer
375
+ assert_equal ['infrastructure@example.com'], mail.from
376
+ end
377
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'rack'
3
5
  require 'httparty'
@@ -5,7 +7,7 @@ require 'timecop'
5
7
  require 'json'
6
8
 
7
9
  class GoogleChatNotifierTest < ActiveSupport::TestCase
8
- URL = 'http://localhost:8000'.freeze
10
+ URL = 'http://localhost:8000'
9
11
 
10
12
  def setup
11
13
  Timecop.freeze('2018-12-09 12:07:16 UTC')
@@ -59,11 +61,7 @@ class GoogleChatNotifierTest < ActiveSupport::TestCase
59
61
  header,
60
62
  body,
61
63
  '',
62
- '*Backtrace:*',
63
- '```',
64
- "* app/controllers/my_controller.rb:53:in `my_controller_params'",
65
- "* app/controllers/my_controller.rb:34:in `update'",
66
- '```'
64
+ backtrace
67
65
  ].join("\n")
68
66
 
69
67
  HTTParty.expects(:post).with(URL, post_opts(text))
@@ -117,11 +115,7 @@ class GoogleChatNotifierTest < ActiveSupport::TestCase
117
115
  '* timestamp : 2018-12-09 12:07:16 UTC',
118
116
  '```',
119
117
  '',
120
- '*Backtrace:*',
121
- '```',
122
- "* app/controllers/my_controller.rb:53:in `my_controller_params'",
123
- "* app/controllers/my_controller.rb:34:in `update'",
124
- '```'
118
+ backtrace
125
119
  ].join("\n")
126
120
 
127
121
  HTTParty.expects(:post).with(URL, post_opts(text))
@@ -178,4 +172,14 @@ class GoogleChatNotifierTest < ActiveSupport::TestCase
178
172
  def app_name
179
173
  'dummy' if defined?(::Rails) && ::Rails.respond_to?(:application)
180
174
  end
175
+
176
+ def backtrace
177
+ [
178
+ '*Backtrace:*',
179
+ '```',
180
+ "* app/controllers/my_controller.rb:53:in `my_controller_params'",
181
+ "* app/controllers/my_controller.rb:34:in `update'",
182
+ '```'
183
+ ]
184
+ end
181
185
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'rack'
3
5
 
@@ -33,7 +35,9 @@ class HipchatNotifierTest < ActiveSupport::TestCase
33
35
  post_callback: proc { |*| post_callback_called += 1 }
34
36
  }
35
37
 
36
- HipChat::Room.any_instance.expects(:send).with('Exception', fake_body, { color: 'yellow' }.merge(options.except(:api_token, :room_name)))
38
+ HipChat::Room.any_instance
39
+ .expects(:send)
40
+ .with('Exception', fake_body, { color: 'yellow' }.merge(options.except(:api_token, :room_name)))
37
41
 
38
42
  hipchat = ExceptionNotifier::HipchatNotifier.new(options)
39
43
  hipchat.call(fake_exception)
@@ -105,7 +109,9 @@ class HipchatNotifierTest < ActiveSupport::TestCase
105
109
  message_template: ->(exception, _) { "This is custom message: '#{exception.message}'" }
106
110
  }
107
111
 
108
- HipChat::Room.any_instance.expects(:send).with('Exception', "This is custom message: '#{fake_exception.message}'", color: 'yellow')
112
+ HipChat::Room.any_instance
113
+ .expects(:send)
114
+ .with('Exception', "This is custom message: '#{fake_exception.message}'", color: 'yellow')
109
115
 
110
116
  hipchat = ExceptionNotifier::HipchatNotifier.new(options)
111
117
  hipchat.call(fake_exception)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'carrier-pigeon'
3
5
 
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'httparty'
3
5
  require 'timecop'
4
6
  require 'json'
5
7
 
6
8
  class MattermostNotifierTest < ActiveSupport::TestCase
7
- URL = 'http://localhost:8000'.freeze
9
+ URL = 'http://localhost:8000'
8
10
 
9
11
  def setup
10
12
  Timecop.freeze('2018-12-09 12:07:16 UTC')
@@ -113,27 +115,14 @@ class MattermostNotifierTest < ActiveSupport::TestCase
113
115
  end
114
116
 
115
117
  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
- )
118
+ body = default_body.merge(text: [
119
+ '@channel',
120
+ error_occurred_in,
121
+ 'An *ArgumentError* occurred.',
122
+ '*foo*',
123
+ request_info,
124
+ backtrace_info
125
+ ].join("\n"))
137
126
 
138
127
  opts = {
139
128
  body: body.to_json,
@@ -151,6 +140,31 @@ class MattermostNotifierTest < ActiveSupport::TestCase
151
140
  notifier.call(exception, env: test_env)
152
141
  end
153
142
 
143
+ test 'should include exception_data_info' do
144
+ body = default_body.merge(
145
+ text: [
146
+ '@channel',
147
+ error_occurred_in,
148
+ 'An *ArgumentError* occurred.',
149
+ '*foo*',
150
+ request_info,
151
+ exception_data_info
152
+ ].join("\n")
153
+ )
154
+
155
+ opts = {
156
+ body: body.to_json,
157
+ headers: default_headers
158
+ }
159
+
160
+ env = test_env.merge(
161
+ 'exception_notifier.exception_data' => { foo: 'bar', john: 'doe' }
162
+ )
163
+
164
+ HTTParty.expects(:post).with(URL, opts)
165
+ notifier.call(ArgumentError.new('foo'), env: env)
166
+ end
167
+
154
168
  private
155
169
 
156
170
  def notifier
@@ -193,10 +207,45 @@ class MattermostNotifierTest < ActiveSupport::TestCase
193
207
 
194
208
  def github_link
195
209
  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)'
210
+ '[Create an issue]' \
211
+ '(github.com/aschen/dummy/issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)'
197
212
  else
198
213
  # 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)'
214
+ '[Create an issue]' \
215
+ '(github.com/aschen//issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)'
200
216
  end
201
217
  end
218
+
219
+ def request_info
220
+ [
221
+ '### Request',
222
+ '```',
223
+ '* url : http://test.address/?id=foo',
224
+ '* http_method : GET',
225
+ '* ip_address : 127.0.0.1',
226
+ '* parameters : {"id"=>"foo"}',
227
+ '* timestamp : 2018-12-09 12:07:16 UTC',
228
+ '```'
229
+ ]
230
+ end
231
+
232
+ def backtrace_info
233
+ [
234
+ '### Backtrace',
235
+ '```',
236
+ "* app/controllers/my_controller.rb:53:in `my_controller_params'",
237
+ "* app/controllers/my_controller.rb:34:in `update'",
238
+ '```'
239
+ ]
240
+ end
241
+
242
+ def exception_data_info
243
+ [
244
+ '### Data',
245
+ '```',
246
+ '* foo : bar',
247
+ '* john : doe',
248
+ '```'
249
+ ]
250
+ end
202
251
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ErrorGroupTest < ActiveSupport::TestCase
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'timecop'
3
5
 
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  # To allow sidekiq error handlers to be registered, sidekiq must be in
4
6
  # "server mode". This mode is triggered by loading sidekiq/cli. Note this
5
7
  # has to be loaded before exception_notification/sidekiq.
6
8
  require 'sidekiq/cli'
9
+ require 'sidekiq/testing'
7
10
 
8
11
  require 'exception_notification/sidekiq'
9
12
 
@@ -12,13 +15,6 @@ class MockSidekiqServer
12
15
  end
13
16
 
14
17
  class SidekiqTest < ActiveSupport::TestCase
15
- setup do
16
- @_original_sidekiq_logger = Sidekiq::Logging.logger
17
-
18
- # Silence sidekiq warning to stdout
19
- Sidekiq::Logging.logger = nil
20
- end
21
-
22
18
  test 'should call notify_exception when sidekiq raises an error' do
23
19
  server = MockSidekiqServer.new
24
20
  message = {}
@@ -31,8 +27,4 @@ class SidekiqTest < ActiveSupport::TestCase
31
27
 
32
28
  server.handle_exception(exception, message)
33
29
  end
34
-
35
- teardown do
36
- Sidekiq::Logging.logger = @_original_sidekiq_logger
37
- end
38
30
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'slack-notifier'
3
5
 
@@ -128,15 +130,17 @@ class SlackNotifierTest < ActiveSupport::TestCase
128
130
  'exception_notifier.exception_data' => { foo: 'bar', john: 'doe' }
129
131
  },
130
132
  data: {
131
- 'user_id' => 5,
133
+ 'user_id' => 5,
132
134
  'key_to_be_ignored' => 'whatever',
133
- 'ignore_as_well' => { what: 'ever' }
135
+ 'ignore_as_well' => { what: 'ever' }
134
136
  }
135
137
  }
136
138
 
137
139
  expected_data_string = "foo: bar\njohn: doe\nuser_id: 5"
138
140
 
139
- Slack::Notifier.any_instance.expects(:ping).with('', fake_notification(@exception, notification_options, expected_data_string))
141
+ Slack::Notifier.any_instance
142
+ .expects(:ping)
143
+ .with('', fake_notification(@exception, notification_options, expected_data_string))
140
144
  slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
141
145
  slack_notifier.call(@exception, notification_options)
142
146
  end
@@ -184,12 +188,8 @@ class SlackNotifierTest < ActiveSupport::TestCase
184
188
 
185
189
  def fake_backtrace
186
190
  [
187
- 'backtrace line 1',
188
- 'backtrace line 2',
189
- 'backtrace line 3',
190
- 'backtrace line 4',
191
- 'backtrace line 5',
192
- 'backtrace line 6'
191
+ 'backtrace line 1', 'backtrace line 2', 'backtrace line 3',
192
+ 'backtrace line 4', 'backtrace line 5', 'backtrace line 6'
193
193
  ]
194
194
  end
195
195
 
@@ -197,7 +197,9 @@ class SlackNotifierTest < ActiveSupport::TestCase
197
197
  fake_backtrace[2..-1]
198
198
  end
199
199
 
200
- def fake_notification(exception = @exception, notification_options = {}, data_string = nil, expected_backtrace_lines = 10, additional_fields = [])
200
+ def fake_notification(exception = @exception, notification_options = {},
201
+ data_string = nil, expected_backtrace_lines = 10, additional_fields = [])
202
+
201
203
  exception_name = "*#{exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A'}* `#{exception.class}`"
202
204
  if notification_options[:env].nil?
203
205
  text = "#{exception_name} *occured in background*"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'aws-sdk-sns'
3
5
 
@@ -61,9 +63,9 @@ class SnsNotifierTest < ActiveSupport::TestCase
61
63
  test 'should send a sns notification in background' do
62
64
  Aws::SNS::Client.any_instance.expects(:publish).with(
63
65
  topic_arn: 'topicARN',
64
- message: "3 MyException occured in background\n"\
65
- "Exception: undefined method 'method=' for Empty\n"\
66
- "Hostname: example.com\n"\
66
+ message: "3 MyException occured in background\n" \
67
+ "Exception: undefined method 'method=' for Empty\n" \
68
+ "Hostname: example.com\n" \
67
69
  "Backtrace:\n#{fake_backtrace.join("\n")}\n",
68
70
  subject: '[App Exception] - 3 MyException occurred'
69
71
  )
@@ -79,10 +81,10 @@ class SnsNotifierTest < ActiveSupport::TestCase
79
81
 
80
82
  Aws::SNS::Client.any_instance.expects(:publish).with(
81
83
  topic_arn: 'topicARN',
82
- message: 'A MyException occurred while GET </examples> '\
83
- "was processed by examples#index\n"\
84
- "Exception: undefined method 'method=' for Empty\n"\
85
- "Hostname: example.com\n"\
84
+ message: 'A MyException occurred while GET </examples> ' \
85
+ "was processed by examples#index\n" \
86
+ "Exception: undefined method 'method=' for Empty\n" \
87
+ "Hostname: example.com\n" \
86
88
  "Backtrace:\n#{fake_backtrace.join("\n")}\n",
87
89
  subject: '[App Exception] - A MyException occurred'
88
90
  )
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'httparty'
3
5