exception_notification 4.2.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +5 -5
  2. data/Appraisals +4 -3
  3. data/CHANGELOG.rdoc +57 -1
  4. data/CONTRIBUTING.md +21 -2
  5. data/Gemfile +3 -1
  6. data/README.md +106 -789
  7. data/Rakefile +4 -2
  8. data/docs/notifiers/campfire.md +50 -0
  9. data/docs/notifiers/custom.md +42 -0
  10. data/docs/notifiers/datadog.md +51 -0
  11. data/docs/notifiers/email.md +195 -0
  12. data/docs/notifiers/google_chat.md +31 -0
  13. data/docs/notifiers/hipchat.md +66 -0
  14. data/docs/notifiers/irc.md +97 -0
  15. data/docs/notifiers/mattermost.md +115 -0
  16. data/docs/notifiers/slack.md +161 -0
  17. data/docs/notifiers/sns.md +37 -0
  18. data/docs/notifiers/teams.md +54 -0
  19. data/docs/notifiers/webhook.md +60 -0
  20. data/examples/sample_app.rb +56 -0
  21. data/examples/sinatra/Gemfile +8 -6
  22. data/examples/sinatra/config.ru +3 -1
  23. data/examples/sinatra/sinatra_app.rb +19 -11
  24. data/exception_notification.gemspec +30 -23
  25. data/gemfiles/rails4_0.gemfile +1 -2
  26. data/gemfiles/rails4_1.gemfile +1 -2
  27. data/gemfiles/rails4_2.gemfile +1 -2
  28. data/gemfiles/rails5_0.gemfile +1 -2
  29. data/gemfiles/rails5_1.gemfile +7 -0
  30. data/gemfiles/rails5_2.gemfile +7 -0
  31. data/gemfiles/rails6_0.gemfile +7 -0
  32. data/lib/exception_notification.rb +3 -0
  33. data/lib/exception_notification/rack.rb +34 -27
  34. data/lib/exception_notification/rails.rb +3 -0
  35. data/lib/exception_notification/resque.rb +10 -10
  36. data/lib/exception_notification/sidekiq.rb +10 -12
  37. data/lib/exception_notification/version.rb +5 -0
  38. data/lib/exception_notifier.rb +79 -11
  39. data/lib/exception_notifier/base_notifier.rb +10 -5
  40. data/lib/exception_notifier/campfire_notifier.rb +14 -9
  41. data/lib/exception_notifier/datadog_notifier.rb +156 -0
  42. data/lib/exception_notifier/email_notifier.rb +78 -87
  43. data/lib/exception_notifier/google_chat_notifier.rb +44 -0
  44. data/lib/exception_notifier/hipchat_notifier.rb +16 -10
  45. data/lib/exception_notifier/irc_notifier.rb +38 -31
  46. data/lib/exception_notifier/mattermost_notifier.rb +54 -131
  47. data/lib/exception_notifier/modules/backtrace_cleaner.rb +2 -2
  48. data/lib/exception_notifier/modules/error_grouping.rb +87 -0
  49. data/lib/exception_notifier/modules/formatter.rb +121 -0
  50. data/lib/exception_notifier/notifier.rb +9 -6
  51. data/lib/exception_notifier/slack_notifier.rb +75 -32
  52. data/lib/exception_notifier/sns_notifier.rb +86 -0
  53. data/lib/exception_notifier/teams_notifier.rb +200 -0
  54. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
  55. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
  56. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
  57. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.text.erb +9 -9
  58. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -4
  59. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
  60. data/lib/exception_notifier/webhook_notifier.rb +19 -16
  61. data/lib/generators/exception_notification/install_generator.rb +11 -5
  62. data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +14 -12
  63. data/test/exception_notification/rack_test.rb +90 -4
  64. data/test/exception_notification/resque_test.rb +54 -0
  65. data/test/exception_notifier/campfire_notifier_test.rb +66 -39
  66. data/test/exception_notifier/datadog_notifier_test.rb +153 -0
  67. data/test/exception_notifier/email_notifier_test.rb +301 -145
  68. data/test/exception_notifier/google_chat_notifier_test.rb +185 -0
  69. data/test/exception_notifier/hipchat_notifier_test.rb +112 -65
  70. data/test/exception_notifier/irc_notifier_test.rb +48 -30
  71. data/test/exception_notifier/mattermost_notifier_test.rb +218 -55
  72. data/test/exception_notifier/modules/error_grouping_test.rb +167 -0
  73. data/test/exception_notifier/modules/formatter_test.rb +152 -0
  74. data/test/exception_notifier/sidekiq_test.rb +9 -6
  75. data/test/exception_notifier/slack_notifier_test.rb +109 -59
  76. data/test/exception_notifier/sns_notifier_test.rb +123 -0
  77. data/test/exception_notifier/teams_notifier_test.rb +92 -0
  78. data/test/exception_notifier/webhook_notifier_test.rb +68 -38
  79. data/test/exception_notifier_test.rb +220 -37
  80. data/test/support/exception_notifier_helper.rb +14 -0
  81. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
  82. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
  83. data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
  84. data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
  85. data/test/test_helper.rb +14 -13
  86. metadata +154 -162
  87. data/test/dummy/.gitignore +0 -4
  88. data/test/dummy/Rakefile +0 -7
  89. data/test/dummy/app/controllers/application_controller.rb +0 -3
  90. data/test/dummy/app/controllers/posts_controller.rb +0 -30
  91. data/test/dummy/app/helpers/application_helper.rb +0 -2
  92. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  93. data/test/dummy/app/models/post.rb +0 -2
  94. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  95. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  96. data/test/dummy/app/views/posts/new.html.erb +0 -0
  97. data/test/dummy/app/views/posts/show.html.erb +0 -0
  98. data/test/dummy/config.ru +0 -4
  99. data/test/dummy/config/application.rb +0 -42
  100. data/test/dummy/config/boot.rb +0 -6
  101. data/test/dummy/config/database.yml +0 -22
  102. data/test/dummy/config/environment.rb +0 -17
  103. data/test/dummy/config/environments/development.rb +0 -25
  104. data/test/dummy/config/environments/production.rb +0 -50
  105. data/test/dummy/config/environments/test.rb +0 -38
  106. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  107. data/test/dummy/config/initializers/inflections.rb +0 -10
  108. data/test/dummy/config/initializers/mime_types.rb +0 -5
  109. data/test/dummy/config/initializers/secret_token.rb +0 -8
  110. data/test/dummy/config/initializers/session_store.rb +0 -8
  111. data/test/dummy/config/locales/en.yml +0 -5
  112. data/test/dummy/config/routes.rb +0 -3
  113. data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
  114. data/test/dummy/db/schema.rb +0 -24
  115. data/test/dummy/db/seeds.rb +0 -7
  116. data/test/dummy/lib/tasks/.gitkeep +0 -0
  117. data/test/dummy/public/404.html +0 -26
  118. data/test/dummy/public/422.html +0 -26
  119. data/test/dummy/public/500.html +0 -26
  120. data/test/dummy/public/favicon.ico +0 -0
  121. data/test/dummy/public/images/rails.png +0 -0
  122. data/test/dummy/public/index.html +0 -239
  123. data/test/dummy/public/javascripts/application.js +0 -2
  124. data/test/dummy/public/javascripts/controls.js +0 -965
  125. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  126. data/test/dummy/public/javascripts/effects.js +0 -1123
  127. data/test/dummy/public/javascripts/prototype.js +0 -6001
  128. data/test/dummy/public/javascripts/rails.js +0 -191
  129. data/test/dummy/public/robots.txt +0 -5
  130. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  131. data/test/dummy/public/stylesheets/scaffold.css +0 -56
  132. data/test/dummy/script/rails +0 -6
  133. data/test/dummy/test/functional/posts_controller_test.rb +0 -218
  134. data/test/dummy/test/test_helper.rb +0 -7
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'aws-sdk-sns'
5
+
6
+ class SnsNotifierTest < ActiveSupport::TestCase
7
+ def setup
8
+ @exception = fake_exception
9
+ @exception.stubs(:class).returns('MyException')
10
+ @exception.stubs(:backtrace).returns(fake_backtrace)
11
+ @exception.stubs(:message).returns("undefined method 'method=' for Empty")
12
+ @options = {
13
+ access_key_id: 'my-access_key_id',
14
+ secret_access_key: 'my-secret_access_key',
15
+ region: 'us-east',
16
+ topic_arn: 'topicARN',
17
+ sns_prefix: '[App Exception]'
18
+ }
19
+ Socket.stubs(:gethostname).returns('example.com')
20
+ end
21
+
22
+ # initialize
23
+
24
+ test 'should initialize aws notifier with received params' do
25
+ Aws::SNS::Client.expects(:new).with(
26
+ region: 'us-east',
27
+ access_key_id: 'my-access_key_id',
28
+ secret_access_key: 'my-secret_access_key'
29
+ )
30
+
31
+ ExceptionNotifier::SnsNotifier.new(@options)
32
+ end
33
+
34
+ test 'should raise an exception if region is not received' do
35
+ @options[:region] = nil
36
+
37
+ error = assert_raises ArgumentError do
38
+ ExceptionNotifier::SnsNotifier.new(@options)
39
+ end
40
+ assert_equal "You must provide 'region' option", error.message
41
+ end
42
+
43
+ test 'should raise an exception on publish if access_key_id is not received' do
44
+ @options[:access_key_id] = nil
45
+ error = assert_raises ArgumentError do
46
+ ExceptionNotifier::SnsNotifier.new(@options)
47
+ end
48
+
49
+ assert_equal "You must provide 'access_key_id' option", error.message
50
+ end
51
+
52
+ test 'should raise an exception on publish if secret_access_key is not received' do
53
+ @options[:secret_access_key] = nil
54
+ error = assert_raises ArgumentError do
55
+ ExceptionNotifier::SnsNotifier.new(@options)
56
+ end
57
+
58
+ assert_equal "You must provide 'secret_access_key' option", error.message
59
+ end
60
+
61
+ # call
62
+
63
+ test 'should send a sns notification in background' do
64
+ Aws::SNS::Client.any_instance.expects(:publish).with(
65
+ topic_arn: 'topicARN',
66
+ message: "3 MyException occured in background\n" \
67
+ "Exception: undefined method 'method=' for Empty\n" \
68
+ "Hostname: example.com\n" \
69
+ "Backtrace:\n#{fake_backtrace.join("\n")}\n",
70
+ subject: '[App Exception] - 3 MyException occurred'
71
+ )
72
+
73
+ sns_notifier = ExceptionNotifier::SnsNotifier.new(@options)
74
+ sns_notifier.call(@exception, accumulated_errors_count: 3)
75
+ end
76
+
77
+ test 'should send a sns notification with controller#action information' do
78
+ controller = mock('controller')
79
+ controller.stubs(:action_name).returns('index')
80
+ controller.stubs(:controller_name).returns('examples')
81
+
82
+ Aws::SNS::Client.any_instance.expects(:publish).with(
83
+ topic_arn: 'topicARN',
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" \
88
+ "Backtrace:\n#{fake_backtrace.join("\n")}\n",
89
+ subject: '[App Exception] - A MyException occurred'
90
+ )
91
+
92
+ sns_notifier = ExceptionNotifier::SnsNotifier.new(@options)
93
+ sns_notifier.call(@exception,
94
+ env: {
95
+ 'REQUEST_METHOD' => 'GET',
96
+ 'REQUEST_URI' => '/examples',
97
+ 'action_controller.instance' => controller
98
+ })
99
+ end
100
+
101
+ private
102
+
103
+ def fake_exception
104
+ 1 / 0
105
+ rescue StandardError => e
106
+ e
107
+ end
108
+
109
+ def fake_exception_without_backtrace
110
+ StandardError.new('my custom error')
111
+ end
112
+
113
+ def fake_backtrace
114
+ [
115
+ 'backtrace line 1',
116
+ 'backtrace line 2',
117
+ 'backtrace line 3',
118
+ 'backtrace line 4',
119
+ 'backtrace line 5',
120
+ 'backtrace line 6'
121
+ ]
122
+ end
123
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'httparty'
5
+
6
+ class TeamsNotifierTest < ActiveSupport::TestCase
7
+ test 'should send notification if properly configured' do
8
+ options = {
9
+ webhook_url: 'http://localhost:8000'
10
+ }
11
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
12
+ teams_notifier.httparty = FakeHTTParty.new
13
+
14
+ options = teams_notifier.call ArgumentError.new('foo'), options
15
+
16
+ body = ActiveSupport::JSON.decode options[:body]
17
+ assert body.key? 'title'
18
+ assert body.key? 'sections'
19
+
20
+ sections = body['sections']
21
+ header = sections[0]
22
+
23
+ assert_equal 2, sections.size
24
+ assert_equal 'A *ArgumentError* occurred.', header['activityTitle']
25
+ assert_equal 'foo', header['activitySubtitle']
26
+ end
27
+
28
+ test 'should send notification with create gitlab issue link if specified' do
29
+ options = {
30
+ webhook_url: 'http://localhost:8000',
31
+ git_url: 'github.com/aschen'
32
+ }
33
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
34
+ teams_notifier.httparty = FakeHTTParty.new
35
+
36
+ options = teams_notifier.call ArgumentError.new('foo'), options
37
+
38
+ body = ActiveSupport::JSON.decode options[:body]
39
+
40
+ potential_action = body['potentialAction']
41
+ assert_equal 2, potential_action.size
42
+ assert_equal '🦊 View in GitLab', potential_action[0]['name']
43
+ assert_equal '🦊 Create Issue in GitLab', potential_action[1]['name']
44
+ end
45
+
46
+ test 'should add other HTTParty options to params' do
47
+ options = {
48
+ webhook_url: 'http://localhost:8000',
49
+ username: 'Test Bot',
50
+ avatar: 'http://site.com/icon.png',
51
+ basic_auth: {
52
+ username: 'clara',
53
+ password: 'password'
54
+ }
55
+ }
56
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
57
+ teams_notifier.httparty = FakeHTTParty.new
58
+
59
+ options = teams_notifier.call ArgumentError.new('foo'), options
60
+
61
+ assert options.key? :basic_auth
62
+ assert 'clara', options[:basic_auth][:username]
63
+ assert 'password', options[:basic_auth][:password]
64
+ end
65
+
66
+ test "should use 'A' for exceptions count if :accumulated_errors_count option is nil" do
67
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
68
+ exception = ArgumentError.new('foo')
69
+ teams_notifier.instance_variable_set(:@exception, exception)
70
+ teams_notifier.instance_variable_set(:@options, {})
71
+
72
+ message_text = teams_notifier.send(:message_text)
73
+ header = message_text['sections'][0]
74
+ assert_equal 'A *ArgumentError* occurred.', header['activityTitle']
75
+ end
76
+
77
+ test 'should use direct errors count if :accumulated_errors_count option is 5' do
78
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
79
+ exception = ArgumentError.new('foo')
80
+ teams_notifier.instance_variable_set(:@exception, exception)
81
+ teams_notifier.instance_variable_set(:@options, accumulated_errors_count: 5)
82
+ message_text = teams_notifier.send(:message_text)
83
+ header = message_text['sections'][0]
84
+ assert_equal '5 *ArgumentError* occurred.', header['activityTitle']
85
+ end
86
+ end
87
+
88
+ class FakeHTTParty
89
+ def post(_url, options)
90
+ options
91
+ end
92
+ end
@@ -1,33 +1,44 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'httparty'
3
5
 
4
6
  class WebhookNotifierTest < ActiveSupport::TestCase
5
-
6
- test "should send webhook notification if properly configured" do
7
+ test 'should send webhook notification if properly configured' do
7
8
  ExceptionNotifier::WebhookNotifier.stubs(:new).returns(Object.new)
8
- webhook = ExceptionNotifier::WebhookNotifier.new({:url => 'http://localhost:8000'})
9
+ webhook = ExceptionNotifier::WebhookNotifier.new(url: 'http://localhost:8000')
9
10
  webhook.stubs(:call).returns(fake_response)
10
11
  response = webhook.call(fake_exception)
11
12
 
12
13
  refute_nil response
13
14
  assert_equal response[:status], 200
14
- assert_equal response[:body][:exception][:error_class], "ZeroDivisionError"
15
- assert_includes response[:body][:exception][:message], "divided by 0"
16
- assert_includes response[:body][:exception][:backtrace], "/exception_notification/test/webhook_notifier_test.rb:48"
17
-
18
- assert response[:body][:request][:cookies].has_key?(:cookie_item1)
19
- assert_equal response[:body][:request][:url], "http://example.com/example"
20
- assert_equal response[:body][:request][:ip_address], "192.168.1.1"
21
- assert response[:body][:request][:environment].has_key?(:env_item1)
22
- assert_equal response[:body][:request][:controller], "#<ControllerName:0x007f9642a04d00>"
23
- assert response[:body][:request][:session].has_key?(:session_item1)
24
- assert response[:body][:request][:parameters].has_key?(:controller)
25
- assert response[:body][:data][:extra_data].has_key?(:data_item1)
15
+ assert_equal response[:body][:exception][:error_class], 'ZeroDivisionError'
16
+ assert_includes response[:body][:exception][:message], 'divided by 0'
17
+ assert_includes response[:body][:exception][:backtrace], '/exception_notification/test/webhook_notifier_test.rb:48'
18
+
19
+ assert response[:body][:request][:cookies].key?(:cookie_item1)
20
+ assert_equal response[:body][:request][:url], 'http://example.com/example'
21
+ assert_equal response[:body][:request][:ip_address], '192.168.1.1'
22
+ assert response[:body][:request][:environment].key?(:env_item1)
23
+ assert_equal response[:body][:request][:controller], '#<ControllerName:0x007f9642a04d00>'
24
+ assert response[:body][:request][:session].key?(:session_item1)
25
+ assert response[:body][:request][:parameters].key?(:controller)
26
+ assert response[:body][:data][:extra_data].key?(:data_item1)
27
+ end
28
+
29
+ test 'should send webhook notification with correct params data' do
30
+ url = 'http://localhost:8000'
31
+ fake_exception.stubs(:backtrace).returns('the backtrace')
32
+ webhook = ExceptionNotifier::WebhookNotifier.new(url: url)
33
+
34
+ HTTParty.expects(:send).with(:post, url, fake_params)
35
+
36
+ webhook.call(fake_exception)
26
37
  end
27
38
 
28
- test "should call pre/post_callback if specified" do
39
+ test 'should call pre/post_callback if specified' do
29
40
  HTTParty.stubs(:send).returns(fake_response)
30
- webhook = ExceptionNotifier::WebhookNotifier.new({:url => 'http://localhost:8000'})
41
+ webhook = ExceptionNotifier::WebhookNotifier.new(url: 'http://localhost:8000')
31
42
  webhook.call(fake_exception)
32
43
  end
33
44
 
@@ -35,34 +46,53 @@ class WebhookNotifierTest < ActiveSupport::TestCase
35
46
 
36
47
  def fake_response
37
48
  {
38
- :status => 200,
39
- :body => {
40
- :exception => {
41
- :error_class => 'ZeroDivisionError',
42
- :message => 'divided by 0',
43
- :backtrace => '/exception_notification/test/webhook_notifier_test.rb:48:in `/'
49
+ status: 200,
50
+ body: {
51
+ exception: {
52
+ error_class: 'ZeroDivisionError',
53
+ message: 'divided by 0',
54
+ backtrace: '/exception_notification/test/webhook_notifier_test.rb:48:in `/'
44
55
  },
45
- :data => {
46
- :extra_data => {:data_item1 => "datavalue1", :data_item2 => "datavalue2"}
56
+ data: {
57
+ extra_data: { data_item1: 'datavalue1', data_item2: 'datavalue2' }
47
58
  },
48
- :request => {
49
- :cookies => {:cookie_item1 => 'cookieitemvalue1', :cookie_item2 => 'cookieitemvalue2'},
50
- :url => 'http://example.com/example',
51
- :ip_address => '192.168.1.1',
52
- :environment => {:env_item1 => "envitem1", :env_item2 => "envitem2"},
53
- :controller => '#<ControllerName:0x007f9642a04d00>',
54
- :session => {:session_item1 => "sessionitem1", :session_item2 => "sessionitem2"},
55
- :parameters => {:action =>"index", :controller =>"projects"}
59
+ request: {
60
+ cookies: { cookie_item1: 'cookieitemvalue1', cookie_item2: 'cookieitemvalue2' },
61
+ url: 'http://example.com/example',
62
+ ip_address: '192.168.1.1',
63
+ environment: { env_item1: 'envitem1', env_item2: 'envitem2' },
64
+ controller: '#<ControllerName:0x007f9642a04d00>',
65
+ session: { session_item1: 'sessionitem1', session_item2: 'sessionitem2' },
66
+ parameters: { action: 'index', controller: 'projects' }
56
67
  }
57
68
  }
58
69
  }
59
70
  end
60
71
 
72
+ def fake_params
73
+ params = {
74
+ body: {
75
+ server: Socket.gethostname,
76
+ process: $PROCESS_ID,
77
+ exception: {
78
+ error_class: 'ZeroDivisionError',
79
+ message: 'divided by 0'.inspect,
80
+ backtrace: 'the backtrace'
81
+ },
82
+ data: {}
83
+ }
84
+ }
85
+
86
+ params[:body][:rails_root] = Rails.root if defined?(::Rails) && Rails.respond_to?(:root)
87
+
88
+ params
89
+ end
90
+
61
91
  def fake_exception
62
- exception = begin
63
- 5/0
64
- rescue Exception => e
65
- e
66
- end
92
+ @fake_exception ||= begin
93
+ 5 / 0
94
+ rescue StandardError => e
95
+ e
96
+ end
67
97
  end
68
98
  end
@@ -1,29 +1,48 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
5
+ class ExceptionOne < StandardError; end
6
+ class ExceptionTwo < StandardError; end
7
+
3
8
  class ExceptionNotifierTest < ActiveSupport::TestCase
4
- test "should have default ignored exceptions" do
9
+ setup do
10
+ ExceptionNotifier.register_exception_notifier(:email, exception_recipients: %w[dummyexceptions@example.com])
11
+
12
+ @notifier_calls = 0
13
+ @test_notifier = ->(_exception, _options) { @notifier_calls += 1 }
14
+ end
15
+
16
+ teardown do
17
+ ExceptionNotifier.reset_notifiers!
18
+
19
+ Rails.cache.clear if defined?(Rails) && Rails.respond_to?(:cache)
20
+ end
21
+
22
+ test 'should have default ignored exceptions' do
5
23
  assert_equal ExceptionNotifier.ignored_exceptions,
6
- ['ActiveRecord::RecordNotFound', 'AbstractController::ActionNotFound', 'ActionController::RoutingError',
7
- 'ActionController::UnknownFormat', 'ActionController::UrlGenerationError']
24
+ ['ActiveRecord::RecordNotFound', 'Mongoid::Errors::DocumentNotFound',
25
+ 'AbstractController::ActionNotFound', 'ActionController::RoutingError',
26
+ 'ActionController::UnknownFormat', 'ActionController::UrlGenerationError']
8
27
  end
9
28
 
10
- test "should have email notifier registered" do
29
+ test 'should have email notifier registered' do
11
30
  assert_equal ExceptionNotifier.notifiers, [:email]
12
31
  end
13
32
 
14
- test "should have a valid email notifier" do
33
+ test 'should have a valid email notifier' do
15
34
  @email_notifier = ExceptionNotifier.registered_exception_notifier(:email)
16
35
  refute_nil @email_notifier
17
36
  assert_equal @email_notifier.class, ExceptionNotifier::EmailNotifier
18
37
  assert_respond_to @email_notifier, :call
19
38
  end
20
39
 
21
- test "should allow register/unregister another notifier" do
40
+ test 'should allow register/unregister another notifier' do
22
41
  called = false
23
- proc_notifier = lambda { |exception, options| called = true }
42
+ proc_notifier = ->(_exception, _options) { called = true }
24
43
  ExceptionNotifier.register_exception_notifier(:proc, proc_notifier)
25
44
 
26
- assert_equal ExceptionNotifier.notifiers.sort, [:email, :proc]
45
+ assert_equal ExceptionNotifier.notifiers.sort, %i[email proc]
27
46
 
28
47
  exception = StandardError.new
29
48
 
@@ -34,27 +53,27 @@ class ExceptionNotifierTest < ActiveSupport::TestCase
34
53
  assert_equal ExceptionNotifier.notifiers, [:email]
35
54
  end
36
55
 
37
- test "should allow select notifiers to send error to" do
56
+ test 'should allow select notifiers to send error to' do
38
57
  notifier1_calls = 0
39
- notifier1 = lambda { |exception, options| notifier1_calls += 1 }
58
+ notifier1 = ->(_exception, _options) { notifier1_calls += 1 }
40
59
  ExceptionNotifier.register_exception_notifier(:notifier1, notifier1)
41
60
 
42
61
  notifier2_calls = 0
43
- notifier2 = lambda { |exception, options| notifier2_calls += 1 }
62
+ notifier2 = ->(_exception, _options) { notifier2_calls += 1 }
44
63
  ExceptionNotifier.register_exception_notifier(:notifier2, notifier2)
45
64
 
46
- assert_equal ExceptionNotifier.notifiers.sort, [:email, :notifier1, :notifier2]
65
+ assert_equal ExceptionNotifier.notifiers.sort, %i[email notifier1 notifier2]
47
66
 
48
67
  exception = StandardError.new
49
68
  ExceptionNotifier.notify_exception(exception)
50
69
  assert_equal notifier1_calls, 1
51
70
  assert_equal notifier2_calls, 1
52
71
 
53
- ExceptionNotifier.notify_exception(exception, {:notifiers => :notifier1})
72
+ ExceptionNotifier.notify_exception(exception, notifiers: :notifier1)
54
73
  assert_equal notifier1_calls, 2
55
74
  assert_equal notifier2_calls, 1
56
75
 
57
- ExceptionNotifier.notify_exception(exception, {:notifiers => :notifier2})
76
+ ExceptionNotifier.notify_exception(exception, notifiers: :notifier2)
58
77
  assert_equal notifier1_calls, 2
59
78
  assert_equal notifier2_calls, 2
60
79
 
@@ -63,43 +82,207 @@ class ExceptionNotifierTest < ActiveSupport::TestCase
63
82
  assert_equal ExceptionNotifier.notifiers, [:email]
64
83
  end
65
84
 
66
- test "should ignore exception if satisfies conditional ignore" do
67
- env = "production"
68
- ExceptionNotifier.ignore_if do |exception, options|
69
- env != "production"
85
+ test 'should ignore exception if satisfies conditional ignore' do
86
+ env = 'production'
87
+ ExceptionNotifier.ignore_if do |_exception, _options|
88
+ env != 'production'
70
89
  end
71
90
 
72
- notifier_calls = 0
73
- test_notifier = lambda { |exception, options| notifier_calls += 1 }
74
- ExceptionNotifier.register_exception_notifier(:test, test_notifier)
91
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
92
+
93
+ exception = StandardError.new
94
+
95
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
96
+ assert_equal @notifier_calls, 1
97
+
98
+ env = 'development'
99
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
100
+ assert_equal @notifier_calls, 1
101
+ end
102
+
103
+ test 'should ignore exception if satisfies by-notifier conditional ignore' do
104
+ notifier1_calls = 0
105
+ notifier1 = ->(_exception, _options) { notifier1_calls += 1 }
106
+ ExceptionNotifier.register_exception_notifier(:notifier1, notifier1)
107
+
108
+ notifier2_calls = 0
109
+ notifier2 = ->(_exception, _options) { notifier2_calls += 1 }
110
+ ExceptionNotifier.register_exception_notifier(:notifier2, notifier2)
111
+
112
+ env = 'production'
113
+ ExceptionNotifier.ignore_notifier_if(:notifier1) do |_exception, _options|
114
+ env == 'development'
115
+ end
116
+ ExceptionNotifier.ignore_notifier_if(:notifier2) do |_exception, _options|
117
+ env == 'production'
118
+ end
75
119
 
76
120
  exception = StandardError.new
77
121
 
78
- ExceptionNotifier.notify_exception(exception, {:notifiers => :test})
79
- assert_equal notifier_calls, 1
122
+ ExceptionNotifier.notify_exception(exception)
123
+ assert_equal notifier1_calls, 1
124
+ assert_equal notifier2_calls, 0
125
+
126
+ env = 'development'
127
+
128
+ ExceptionNotifier.notify_exception(exception)
129
+ assert_equal notifier1_calls, 1
130
+ assert_equal notifier2_calls, 1
131
+
132
+ env = 'test'
133
+
134
+ ExceptionNotifier.notify_exception(exception)
135
+ assert_equal notifier1_calls, 2
136
+ assert_equal notifier2_calls, 2
137
+ end
138
+
139
+ test 'should return false if all the registered notifiers are ignored' do
140
+ ExceptionNotifier.notifiers.each do |notifier|
141
+ # make sure to register no other notifiers but the tested ones
142
+ ExceptionNotifier.unregister_exception_notifier(notifier)
143
+ end
144
+
145
+ ExceptionNotifier.register_exception_notifier(:notifier1, ->(_, _) {})
146
+ ExceptionNotifier.register_exception_notifier(:notifier2, ->(_, _) {})
147
+
148
+ ExceptionNotifier.ignore_notifier_if(:notifier1) do |exception, _options|
149
+ exception.message =~ /non_critical_error/
150
+ end
151
+ ExceptionNotifier.ignore_notifier_if(:notifier2) do |exception, _options|
152
+ exception.message =~ /non_critical_error/
153
+ end
80
154
 
81
- env = "development"
82
- ExceptionNotifier.notify_exception(exception, {:notifiers => :test})
83
- assert_equal notifier_calls, 1
155
+ exception = StandardError.new('a non_critical_error occured.')
84
156
 
85
- ExceptionNotifier.clear_ignore_conditions!
86
- ExceptionNotifier.unregister_exception_notifier(:test)
157
+ refute ExceptionNotifier.notify_exception(exception)
87
158
  end
88
159
 
89
- test "should not send notification if one of ignored exceptions" do
90
- notifier_calls = 0
91
- test_notifier = lambda { |exception, options| notifier_calls += 1 }
92
- ExceptionNotifier.register_exception_notifier(:test, test_notifier)
160
+ test 'should return true if one of the notifiers fires' do
161
+ ExceptionNotifier.notifiers.each do |notifier|
162
+ # make sure to register no other notifiers but the tested ones
163
+ ExceptionNotifier.unregister_exception_notifier(notifier)
164
+ end
165
+
166
+ ExceptionNotifier.register_exception_notifier(:notifier1, ->(_, _) {})
167
+ ExceptionNotifier.register_exception_notifier(:notifier2, ->(_, _) {})
168
+
169
+ ExceptionNotifier.ignore_notifier_if(:notifier1) do |exception, _options|
170
+ exception.message =~ /non-critical\serror/
171
+ end
172
+
173
+ exception = StandardError.new('a non-critical error occured')
174
+
175
+ assert ExceptionNotifier.notify_exception(exception)
176
+ end
177
+
178
+ test 'should not send notification if one of ignored exceptions' do
179
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
180
+
181
+ exception = StandardError.new
182
+
183
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
184
+ assert_equal @notifier_calls, 1
185
+
186
+ ExceptionNotifier.notify_exception(exception, notifiers: :test, ignore_exceptions: 'StandardError')
187
+ assert_equal @notifier_calls, 1
188
+ end
189
+
190
+ test 'should not send notification if subclass of one of ignored exceptions' do
191
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
192
+
193
+ class StandardErrorSubclass < StandardError
194
+ end
195
+
196
+ exception = StandardErrorSubclass.new
197
+
198
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
199
+ assert_equal @notifier_calls, 1
200
+
201
+ ExceptionNotifier.notify_exception(exception, notifiers: :test, ignore_exceptions: 'StandardError')
202
+ assert_equal @notifier_calls, 1
203
+ end
204
+
205
+ test 'should not send notification if extended module one of ignored exceptions' do
206
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
207
+
208
+ module StandardErrorModule; end
93
209
 
94
210
  exception = StandardError.new
211
+ exception.extend StandardErrorModule
212
+
213
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
214
+ assert_equal @notifier_calls, 1
215
+
216
+ ignore_exceptions = 'ExceptionNotifierTest::StandardErrorModule'
217
+ ExceptionNotifier.notify_exception(exception, notifiers: :test, ignore_exceptions: ignore_exceptions)
218
+ assert_equal @notifier_calls, 1
219
+ end
95
220
 
96
- ExceptionNotifier.notify_exception(exception, {:notifiers => :test})
97
- assert_equal notifier_calls, 1
221
+ test 'should not send notification if prepended module at singleton class one of ignored exceptions' do
222
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
98
223
 
99
- ExceptionNotifier.notify_exception(exception, {:notifiers => :test, :ignore_exceptions => 'StandardError' })
100
- assert_equal notifier_calls, 1
224
+ module StandardErrorModule; end
101
225
 
102
- ExceptionNotifier.unregister_exception_notifier(:test)
226
+ exception = StandardError.new
227
+ exception.singleton_class.prepend StandardErrorModule
228
+
229
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
230
+ assert_equal @notifier_calls, 1
231
+
232
+ ignore_exceptions = 'ExceptionNotifierTest::StandardErrorModule'
233
+ ExceptionNotifier.notify_exception(exception, notifiers: :test, ignore_exceptions: ignore_exceptions)
234
+ assert_equal @notifier_calls, 1
103
235
  end
104
236
 
237
+ test 'should call received block' do
238
+ @block_called = false
239
+ notifier = ->(_exception, _options, &block) { block.call }
240
+ ExceptionNotifier.register_exception_notifier(:test, notifier)
241
+
242
+ exception = ExceptionOne.new
243
+
244
+ ExceptionNotifier.notify_exception(exception) do
245
+ @block_called = true
246
+ end
247
+
248
+ assert @block_called
249
+ end
250
+
251
+ test 'should not call group_error! or send_notification? if error_grouping false' do
252
+ exception = StandardError.new
253
+ ExceptionNotifier.expects(:group_error!).never
254
+ ExceptionNotifier.expects(:send_notification?).never
255
+
256
+ ExceptionNotifier.notify_exception(exception)
257
+ end
258
+
259
+ test 'should call group_error! and send_notification? if error_grouping true' do
260
+ ExceptionNotifier.error_grouping = true
261
+
262
+ exception = StandardError.new
263
+ ExceptionNotifier.expects(:group_error!).once
264
+ ExceptionNotifier.expects(:send_notification?).once
265
+
266
+ ExceptionNotifier.notify_exception(exception)
267
+ end
268
+
269
+ test 'should skip notification if send_notification? is false' do
270
+ ExceptionNotifier.error_grouping = true
271
+
272
+ exception = StandardError.new
273
+ ExceptionNotifier.expects(:group_error!).once.returns(1)
274
+ ExceptionNotifier.expects(:send_notification?).with(exception, 1).once.returns(false)
275
+
276
+ refute ExceptionNotifier.notify_exception(exception)
277
+ end
278
+
279
+ test 'should send notification if send_notification? is true' do
280
+ ExceptionNotifier.error_grouping = true
281
+
282
+ exception = StandardError.new
283
+ ExceptionNotifier.expects(:group_error!).once.returns(1)
284
+ ExceptionNotifier.expects(:send_notification?).with(exception, 1).once.returns(true)
285
+
286
+ assert ExceptionNotifier.notify_exception(exception)
287
+ end
105
288
  end