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,121 @@
1
+ require 'test_helper'
2
+ require 'aws-sdk-sns'
3
+
4
+ class SnsNotifierTest < ActiveSupport::TestCase
5
+ def setup
6
+ @exception = fake_exception
7
+ @exception.stubs(:class).returns('MyException')
8
+ @exception.stubs(:backtrace).returns(fake_backtrace)
9
+ @exception.stubs(:message).returns("undefined method 'method=' for Empty")
10
+ @options = {
11
+ access_key_id: 'my-access_key_id',
12
+ secret_access_key: 'my-secret_access_key',
13
+ region: 'us-east',
14
+ topic_arn: 'topicARN',
15
+ sns_prefix: '[App Exception]'
16
+ }
17
+ Socket.stubs(:gethostname).returns('example.com')
18
+ end
19
+
20
+ # initialize
21
+
22
+ test 'should initialize aws notifier with received params' do
23
+ Aws::SNS::Client.expects(:new).with(
24
+ region: 'us-east',
25
+ access_key_id: 'my-access_key_id',
26
+ secret_access_key: 'my-secret_access_key'
27
+ )
28
+
29
+ ExceptionNotifier::SnsNotifier.new(@options)
30
+ end
31
+
32
+ test 'should raise an exception if region is not received' do
33
+ @options[:region] = nil
34
+
35
+ error = assert_raises ArgumentError do
36
+ ExceptionNotifier::SnsNotifier.new(@options)
37
+ end
38
+ assert_equal "You must provide 'region' option", error.message
39
+ end
40
+
41
+ test 'should raise an exception on publish if access_key_id is not received' do
42
+ @options[:access_key_id] = nil
43
+ error = assert_raises ArgumentError do
44
+ ExceptionNotifier::SnsNotifier.new(@options)
45
+ end
46
+
47
+ assert_equal "You must provide 'access_key_id' option", error.message
48
+ end
49
+
50
+ test 'should raise an exception on publish if secret_access_key is not received' do
51
+ @options[:secret_access_key] = nil
52
+ error = assert_raises ArgumentError do
53
+ ExceptionNotifier::SnsNotifier.new(@options)
54
+ end
55
+
56
+ assert_equal "You must provide 'secret_access_key' option", error.message
57
+ end
58
+
59
+ # call
60
+
61
+ test 'should send a sns notification in background' do
62
+ Aws::SNS::Client.any_instance.expects(:publish).with(
63
+ topic_arn: 'topicARN',
64
+ message: "3 MyException occured in background\n"\
65
+ "Exception: undefined method 'method=' for Empty\n"\
66
+ "Hostname: example.com\n"\
67
+ "Backtrace:\n#{fake_backtrace.join("\n")}\n",
68
+ subject: '[App Exception] - 3 MyException occurred'
69
+ )
70
+
71
+ sns_notifier = ExceptionNotifier::SnsNotifier.new(@options)
72
+ sns_notifier.call(@exception, accumulated_errors_count: 3)
73
+ end
74
+
75
+ test 'should send a sns notification with controller#action information' do
76
+ controller = mock('controller')
77
+ controller.stubs(:action_name).returns('index')
78
+ controller.stubs(:controller_name).returns('examples')
79
+
80
+ Aws::SNS::Client.any_instance.expects(:publish).with(
81
+ 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"\
86
+ "Backtrace:\n#{fake_backtrace.join("\n")}\n",
87
+ subject: '[App Exception] - A MyException occurred'
88
+ )
89
+
90
+ sns_notifier = ExceptionNotifier::SnsNotifier.new(@options)
91
+ sns_notifier.call(@exception,
92
+ env: {
93
+ 'REQUEST_METHOD' => 'GET',
94
+ 'REQUEST_URI' => '/examples',
95
+ 'action_controller.instance' => controller
96
+ })
97
+ end
98
+
99
+ private
100
+
101
+ def fake_exception
102
+ 1 / 0
103
+ rescue StandardError => e
104
+ e
105
+ end
106
+
107
+ def fake_exception_without_backtrace
108
+ StandardError.new('my custom error')
109
+ end
110
+
111
+ def fake_backtrace
112
+ [
113
+ 'backtrace line 1',
114
+ 'backtrace line 2',
115
+ 'backtrace line 3',
116
+ 'backtrace line 4',
117
+ 'backtrace line 5',
118
+ 'backtrace line 6'
119
+ ]
120
+ end
121
+ end
@@ -0,0 +1,90 @@
1
+ require 'test_helper'
2
+ require 'httparty'
3
+
4
+ class TeamsNotifierTest < ActiveSupport::TestCase
5
+ test 'should send notification if properly configured' do
6
+ options = {
7
+ webhook_url: 'http://localhost:8000'
8
+ }
9
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
10
+ teams_notifier.httparty = FakeHTTParty.new
11
+
12
+ options = teams_notifier.call ArgumentError.new('foo'), options
13
+
14
+ body = ActiveSupport::JSON.decode options[:body]
15
+ assert body.key? 'title'
16
+ assert body.key? 'sections'
17
+
18
+ sections = body['sections']
19
+ header = sections[0]
20
+
21
+ assert_equal 2, sections.size
22
+ assert_equal 'A *ArgumentError* occurred.', header['activityTitle']
23
+ assert_equal 'foo', header['activitySubtitle']
24
+ end
25
+
26
+ test 'should send notification with create gitlab issue link if specified' do
27
+ options = {
28
+ webhook_url: 'http://localhost:8000',
29
+ git_url: 'github.com/aschen'
30
+ }
31
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
32
+ teams_notifier.httparty = FakeHTTParty.new
33
+
34
+ options = teams_notifier.call ArgumentError.new('foo'), options
35
+
36
+ body = ActiveSupport::JSON.decode options[:body]
37
+
38
+ potential_action = body['potentialAction']
39
+ assert_equal 2, potential_action.size
40
+ assert_equal '🦊 View in GitLab', potential_action[0]['name']
41
+ assert_equal '🦊 Create Issue in GitLab', potential_action[1]['name']
42
+ end
43
+
44
+ test 'should add other HTTParty options to params' do
45
+ options = {
46
+ webhook_url: 'http://localhost:8000',
47
+ username: 'Test Bot',
48
+ avatar: 'http://site.com/icon.png',
49
+ basic_auth: {
50
+ username: 'clara',
51
+ password: 'password'
52
+ }
53
+ }
54
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
55
+ teams_notifier.httparty = FakeHTTParty.new
56
+
57
+ options = teams_notifier.call ArgumentError.new('foo'), options
58
+
59
+ assert options.key? :basic_auth
60
+ assert 'clara', options[:basic_auth][:username]
61
+ assert 'password', options[:basic_auth][:password]
62
+ end
63
+
64
+ test "should use 'A' for exceptions count if :accumulated_errors_count option is nil" do
65
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
66
+ exception = ArgumentError.new('foo')
67
+ teams_notifier.instance_variable_set(:@exception, exception)
68
+ teams_notifier.instance_variable_set(:@options, {})
69
+
70
+ message_text = teams_notifier.send(:message_text)
71
+ header = message_text['sections'][0]
72
+ assert_equal 'A *ArgumentError* occurred.', header['activityTitle']
73
+ end
74
+
75
+ test 'should use direct errors count if :accumulated_errors_count option is 5' do
76
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
77
+ exception = ArgumentError.new('foo')
78
+ teams_notifier.instance_variable_set(:@exception, exception)
79
+ teams_notifier.instance_variable_set(:@options, accumulated_errors_count: 5)
80
+ message_text = teams_notifier.send(:message_text)
81
+ header = message_text['sections'][0]
82
+ assert_equal '5 *ArgumentError* occurred.', header['activityTitle']
83
+ end
84
+ end
85
+
86
+ class FakeHTTParty
87
+ def post(_url, options)
88
+ options
89
+ end
90
+ end
@@ -0,0 +1,96 @@
1
+ require 'test_helper'
2
+ require 'httparty'
3
+
4
+ class WebhookNotifierTest < ActiveSupport::TestCase
5
+ test 'should send webhook notification if properly configured' do
6
+ ExceptionNotifier::WebhookNotifier.stubs(:new).returns(Object.new)
7
+ webhook = ExceptionNotifier::WebhookNotifier.new(url: 'http://localhost:8000')
8
+ webhook.stubs(:call).returns(fake_response)
9
+ response = webhook.call(fake_exception)
10
+
11
+ refute_nil response
12
+ assert_equal response[:status], 200
13
+ assert_equal response[:body][:exception][:error_class], 'ZeroDivisionError'
14
+ assert_includes response[:body][:exception][:message], 'divided by 0'
15
+ assert_includes response[:body][:exception][:backtrace], '/exception_notification/test/webhook_notifier_test.rb:48'
16
+
17
+ assert response[:body][:request][:cookies].key?(:cookie_item1)
18
+ assert_equal response[:body][:request][:url], 'http://example.com/example'
19
+ assert_equal response[:body][:request][:ip_address], '192.168.1.1'
20
+ assert response[:body][:request][:environment].key?(:env_item1)
21
+ assert_equal response[:body][:request][:controller], '#<ControllerName:0x007f9642a04d00>'
22
+ assert response[:body][:request][:session].key?(:session_item1)
23
+ assert response[:body][:request][:parameters].key?(:controller)
24
+ assert response[:body][:data][:extra_data].key?(:data_item1)
25
+ end
26
+
27
+ test 'should send webhook notification with correct params data' do
28
+ url = 'http://localhost:8000'
29
+ fake_exception.stubs(:backtrace).returns('the backtrace')
30
+ webhook = ExceptionNotifier::WebhookNotifier.new(url: url)
31
+
32
+ HTTParty.expects(:send).with(:post, url, fake_params)
33
+
34
+ webhook.call(fake_exception)
35
+ end
36
+
37
+ test 'should call pre/post_callback if specified' do
38
+ HTTParty.stubs(:send).returns(fake_response)
39
+ webhook = ExceptionNotifier::WebhookNotifier.new(url: 'http://localhost:8000')
40
+ webhook.call(fake_exception)
41
+ end
42
+
43
+ private
44
+
45
+ def fake_response
46
+ {
47
+ status: 200,
48
+ body: {
49
+ exception: {
50
+ error_class: 'ZeroDivisionError',
51
+ message: 'divided by 0',
52
+ backtrace: '/exception_notification/test/webhook_notifier_test.rb:48:in `/'
53
+ },
54
+ data: {
55
+ extra_data: { data_item1: 'datavalue1', data_item2: 'datavalue2' }
56
+ },
57
+ request: {
58
+ cookies: { cookie_item1: 'cookieitemvalue1', cookie_item2: 'cookieitemvalue2' },
59
+ url: 'http://example.com/example',
60
+ ip_address: '192.168.1.1',
61
+ environment: { env_item1: 'envitem1', env_item2: 'envitem2' },
62
+ controller: '#<ControllerName:0x007f9642a04d00>',
63
+ session: { session_item1: 'sessionitem1', session_item2: 'sessionitem2' },
64
+ parameters: { action: 'index', controller: 'projects' }
65
+ }
66
+ }
67
+ }
68
+ end
69
+
70
+ def fake_params
71
+ params = {
72
+ body: {
73
+ server: Socket.gethostname,
74
+ process: $PROCESS_ID,
75
+ exception: {
76
+ error_class: 'ZeroDivisionError',
77
+ message: 'divided by 0'.inspect,
78
+ backtrace: 'the backtrace'
79
+ },
80
+ data: {}
81
+ }
82
+ }
83
+
84
+ params[:body][:rails_root] = Rails.root if defined?(::Rails) && Rails.respond_to?(:root)
85
+
86
+ params
87
+ end
88
+
89
+ def fake_exception
90
+ @fake_exception ||= begin
91
+ 5 / 0
92
+ rescue StandardError => e
93
+ e
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,182 @@
1
+ require 'test_helper'
2
+
3
+ class ExceptionOne < StandardError; end
4
+ class ExceptionTwo < StandardError; end
5
+
6
+ class ExceptionNotifierTest < ActiveSupport::TestCase
7
+ setup do
8
+ ExceptionNotifier.register_exception_notifier(:email, exception_recipients: %w[dummyexceptions@example.com])
9
+
10
+ @notifier_calls = 0
11
+ @test_notifier = ->(_exception, _options) { @notifier_calls += 1 }
12
+ end
13
+
14
+ teardown do
15
+ ExceptionNotifier.error_grouping = false
16
+ ExceptionNotifier.notification_trigger = nil
17
+ ExceptionNotifier.class_eval('@@notifiers.delete_if { |k, _| k.to_s != "email"}') # 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
23
+ assert_equal ExceptionNotifier.ignored_exceptions,
24
+ ['ActiveRecord::RecordNotFound', 'Mongoid::Errors::DocumentNotFound', 'AbstractController::ActionNotFound',
25
+ 'ActionController::RoutingError', 'ActionController::UnknownFormat', 'ActionController::UrlGenerationError']
26
+ end
27
+
28
+ test 'should have email notifier registered' do
29
+ assert_equal ExceptionNotifier.notifiers, [:email]
30
+ end
31
+
32
+ test 'should have a valid email notifier' do
33
+ @email_notifier = ExceptionNotifier.registered_exception_notifier(:email)
34
+ refute_nil @email_notifier
35
+ assert_equal @email_notifier.class, ExceptionNotifier::EmailNotifier
36
+ assert_respond_to @email_notifier, :call
37
+ end
38
+
39
+ test 'should allow register/unregister another notifier' do
40
+ called = false
41
+ proc_notifier = ->(_exception, _options) { called = true }
42
+ ExceptionNotifier.register_exception_notifier(:proc, proc_notifier)
43
+
44
+ assert_equal ExceptionNotifier.notifiers.sort, %i[email proc]
45
+
46
+ exception = StandardError.new
47
+
48
+ ExceptionNotifier.notify_exception(exception)
49
+ assert called
50
+
51
+ ExceptionNotifier.unregister_exception_notifier(:proc)
52
+ assert_equal ExceptionNotifier.notifiers, [:email]
53
+ end
54
+
55
+ test 'should allow select notifiers to send error to' do
56
+ notifier1_calls = 0
57
+ notifier1 = ->(_exception, _options) { notifier1_calls += 1 }
58
+ ExceptionNotifier.register_exception_notifier(:notifier1, notifier1)
59
+
60
+ notifier2_calls = 0
61
+ notifier2 = ->(_exception, _options) { notifier2_calls += 1 }
62
+ ExceptionNotifier.register_exception_notifier(:notifier2, notifier2)
63
+
64
+ assert_equal ExceptionNotifier.notifiers.sort, %i[email notifier1 notifier2]
65
+
66
+ exception = StandardError.new
67
+ ExceptionNotifier.notify_exception(exception)
68
+ assert_equal notifier1_calls, 1
69
+ assert_equal notifier2_calls, 1
70
+
71
+ ExceptionNotifier.notify_exception(exception, notifiers: :notifier1)
72
+ assert_equal notifier1_calls, 2
73
+ assert_equal notifier2_calls, 1
74
+
75
+ ExceptionNotifier.notify_exception(exception, notifiers: :notifier2)
76
+ assert_equal notifier1_calls, 2
77
+ assert_equal notifier2_calls, 2
78
+
79
+ ExceptionNotifier.unregister_exception_notifier(:notifier1)
80
+ ExceptionNotifier.unregister_exception_notifier(:notifier2)
81
+ assert_equal ExceptionNotifier.notifiers, [:email]
82
+ end
83
+
84
+ test 'should ignore exception if satisfies conditional ignore' do
85
+ env = 'production'
86
+ ExceptionNotifier.ignore_if do |_exception, _options|
87
+ env != 'production'
88
+ end
89
+
90
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
91
+
92
+ exception = StandardError.new
93
+
94
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
95
+ assert_equal @notifier_calls, 1
96
+
97
+ env = 'development'
98
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
99
+ assert_equal @notifier_calls, 1
100
+
101
+ ExceptionNotifier.clear_ignore_conditions!
102
+ end
103
+
104
+ test 'should not send notification if one of ignored exceptions' do
105
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
106
+
107
+ exception = StandardError.new
108
+
109
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
110
+ assert_equal @notifier_calls, 1
111
+
112
+ ExceptionNotifier.notify_exception(exception, notifiers: :test, ignore_exceptions: 'StandardError')
113
+ assert_equal @notifier_calls, 1
114
+ end
115
+
116
+ test 'should not send notification if subclass of one of ignored exceptions' do
117
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
118
+
119
+ class StandardErrorSubclass < StandardError
120
+ end
121
+
122
+ exception = StandardErrorSubclass.new
123
+
124
+ ExceptionNotifier.notify_exception(exception, notifiers: :test)
125
+ assert_equal @notifier_calls, 1
126
+
127
+ ExceptionNotifier.notify_exception(exception, notifiers: :test, ignore_exceptions: 'StandardError')
128
+ assert_equal @notifier_calls, 1
129
+ end
130
+
131
+ test 'should call received block' do
132
+ @block_called = false
133
+ notifier = ->(_exception, _options, &block) { block.call }
134
+ ExceptionNotifier.register_exception_notifier(:test, notifier)
135
+
136
+ exception = ExceptionOne.new
137
+
138
+ ExceptionNotifier.notify_exception(exception) do
139
+ @block_called = true
140
+ end
141
+
142
+ assert @block_called
143
+ end
144
+
145
+ test 'should not call group_error! or send_notification? if error_grouping false' do
146
+ exception = StandardError.new
147
+ ExceptionNotifier.expects(:group_error!).never
148
+ ExceptionNotifier.expects(:send_notification?).never
149
+
150
+ ExceptionNotifier.notify_exception(exception)
151
+ end
152
+
153
+ test 'should call group_error! and send_notification? if error_grouping true' do
154
+ ExceptionNotifier.error_grouping = true
155
+
156
+ exception = StandardError.new
157
+ ExceptionNotifier.expects(:group_error!).once
158
+ ExceptionNotifier.expects(:send_notification?).once
159
+
160
+ ExceptionNotifier.notify_exception(exception)
161
+ end
162
+
163
+ test 'should skip notification if send_notification? is false' do
164
+ ExceptionNotifier.error_grouping = true
165
+
166
+ exception = StandardError.new
167
+ ExceptionNotifier.expects(:group_error!).once.returns(1)
168
+ ExceptionNotifier.expects(:send_notification?).with(exception, 1).once.returns(false)
169
+
170
+ refute ExceptionNotifier.notify_exception(exception)
171
+ end
172
+
173
+ test 'should send notification if send_notification? is true' do
174
+ ExceptionNotifier.error_grouping = true
175
+
176
+ exception = StandardError.new
177
+ ExceptionNotifier.expects(:group_error!).once.returns(1)
178
+ ExceptionNotifier.expects(:send_notification?).with(exception, 1).once.returns(true)
179
+
180
+ assert ExceptionNotifier.notify_exception(exception)
181
+ end
182
+ end