exception_notification 4.2.1 → 4.4.3

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 (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 +105 -780
  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 +30 -23
  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 +71 -40
  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 +17 -14
  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} +13 -11
  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 +59 -38
  66. data/test/exception_notifier/datadog_notifier_test.rb +153 -0
  67. data/test/exception_notifier/email_notifier_test.rb +279 -145
  68. data/test/exception_notifier/google_chat_notifier_test.rb +185 -0
  69. data/test/exception_notifier/hipchat_notifier_test.rb +105 -64
  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 -17
  75. data/test/exception_notifier/slack_notifier_test.rb +84 -62
  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 +52 -48
  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 -35
  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,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ErrorGroupTest < ActiveSupport::TestCase
6
+ setup do
7
+ module TestModule
8
+ include ExceptionNotifier::ErrorGrouping
9
+ @@error_grouping_cache = ActiveSupport::Cache::FileStore.new('test/dummy/tmp/non_default_location')
10
+ end
11
+
12
+ @exception = RuntimeError.new('ERROR')
13
+ @exception.stubs(:backtrace).returns(['/path/where/error/raised:1'])
14
+
15
+ @exception2 = RuntimeError.new('ERROR2')
16
+ @exception2.stubs(:backtrace).returns(['/path/where/error/found:2'])
17
+ end
18
+
19
+ teardown do
20
+ TestModule.error_grouping_cache.clear
21
+ TestModule.fallback_cache_store.clear
22
+ end
23
+
24
+ test 'should add additional option: error_grouping' do
25
+ assert_respond_to TestModule, :error_grouping
26
+ assert_respond_to TestModule, :error_grouping=
27
+ end
28
+
29
+ test 'should set error_grouping to false default' do
30
+ assert_equal false, TestModule.error_grouping
31
+ end
32
+
33
+ test 'should add additional option: error_grouping_cache' do
34
+ assert_respond_to TestModule, :error_grouping_cache
35
+ assert_respond_to TestModule, :error_grouping_cache=
36
+ end
37
+
38
+ test 'should add additional option: error_grouping_period' do
39
+ assert_respond_to TestModule, :error_grouping_period
40
+ assert_respond_to TestModule, :error_grouping_period=
41
+ end
42
+
43
+ test 'shoud set error_grouping_period to 5.minutes default' do
44
+ assert_equal 300, TestModule.error_grouping_period
45
+ end
46
+
47
+ test 'should add additional option: notification_trigger' do
48
+ assert_respond_to TestModule, :notification_trigger
49
+ assert_respond_to TestModule, :notification_trigger=
50
+ end
51
+
52
+ test 'should return errors count nil when not same error for .error_count' do
53
+ assert_nil TestModule.error_count('something')
54
+ end
55
+
56
+ test 'should return errors count when same error for .error_count' do
57
+ TestModule.error_grouping_cache.write('error_key', 13)
58
+ assert_equal 13, TestModule.error_count('error_key')
59
+ end
60
+
61
+ test 'should fallback to memory store cache if specified cache store failed to read' do
62
+ TestModule.error_grouping_cache.stubs(:read).raises(RuntimeError.new('Failed to read'))
63
+ original_fallback = TestModule.fallback_cache_store
64
+ TestModule.expects(:fallback_cache_store).returns(original_fallback).at_least_once
65
+
66
+ assert_nil TestModule.error_count('something_to_read')
67
+ end
68
+
69
+ test 'should save error with count for .save_error_count' do
70
+ count = rand(1..10)
71
+
72
+ TestModule.save_error_count('error_key', count)
73
+ assert_equal count, TestModule.error_grouping_cache.read('error_key')
74
+ end
75
+
76
+ test 'should fallback to memory store cache if specified cache store failed to write' do
77
+ TestModule.error_grouping_cache.stubs(:write).raises(RuntimeError.new('Failed to write'))
78
+ original_fallback = TestModule.fallback_cache_store
79
+ TestModule.expects(:fallback_cache_store).returns(original_fallback).at_least_once
80
+
81
+ assert TestModule.save_error_count('something_to_cache', rand(1..10))
82
+ end
83
+
84
+ test 'should save accumulated_errors_count into options' do
85
+ options = {}
86
+ TestModule.group_error!(@exception, options)
87
+
88
+ assert_equal 1, options[:accumulated_errors_count]
89
+ end
90
+
91
+ test 'should not group error if different exception in .group_error!' do
92
+ options1 = {}
93
+ TestModule.expects(:save_error_count).with { |key, count| key.is_a?(String) && count == 1 }.times(4).returns(true)
94
+ TestModule.group_error!(@exception, options1)
95
+
96
+ options2 = {}
97
+ TestModule.group_error!(NoMethodError.new('method not found'), options2)
98
+
99
+ assert_equal 1, options1[:accumulated_errors_count]
100
+ assert_equal 1, options2[:accumulated_errors_count]
101
+ end
102
+
103
+ test 'should not group error is same exception but different message or backtrace' do
104
+ options1 = {}
105
+ TestModule.expects(:save_error_count).with { |key, count| key.is_a?(String) && count == 1 }.times(4).returns(true)
106
+ TestModule.group_error!(@exception, options1)
107
+
108
+ options2 = {}
109
+ TestModule.group_error!(@exception2, options2)
110
+
111
+ assert_equal 1, options1[:accumulated_errors_count]
112
+ assert_equal 1, options2[:accumulated_errors_count]
113
+ end
114
+
115
+ test 'should group error if same exception and message' do
116
+ options = {}
117
+
118
+ 10.times do |i|
119
+ @exception2.stubs(:backtrace).returns(["/path:#{i}"])
120
+ TestModule.group_error!(@exception2, options)
121
+ end
122
+
123
+ assert_equal 10, options[:accumulated_errors_count]
124
+ end
125
+
126
+ test 'should group error if same exception and backtrace' do
127
+ options = {}
128
+
129
+ 10.times do |i|
130
+ @exception2.stubs(:message).returns("ERRORS#{i}")
131
+ TestModule.group_error!(@exception2, options)
132
+ end
133
+
134
+ assert_equal 10, options[:accumulated_errors_count]
135
+ end
136
+
137
+ test 'should group error by that message have high priority' do
138
+ message_based_key = "exception:#{Zlib.crc32("RuntimeError\nmessage:ERROR")}"
139
+ backtrace_based_key = "exception:#{Zlib.crc32("RuntimeError\npath:/path/where/error/raised:1")}"
140
+
141
+ TestModule.save_error_count(message_based_key, 1)
142
+ TestModule.save_error_count(backtrace_based_key, 1)
143
+
144
+ TestModule.expects(:save_error_count).with(message_based_key, 2).once
145
+ TestModule.expects(:save_error_count).with(backtrace_based_key, 2).never
146
+
147
+ TestModule.group_error!(@exception, {})
148
+ end
149
+
150
+ test 'use default formula if not specify notification_trigger in .send_notification?' do
151
+ TestModule.stubs(:notification_trigger).returns(nil)
152
+
153
+ count = 16
154
+ Math.expects(:log2).with(count).returns(4)
155
+
156
+ assert TestModule.send_notification?(@exception, count)
157
+ end
158
+
159
+ test 'use specified trigger in .send_notification?' do
160
+ trigger = proc { |_exception, count| (count % 4).zero? }
161
+ TestModule.stubs(:notification_trigger).returns(trigger)
162
+
163
+ count = 16
164
+ trigger.expects(:call).with(@exception, count).returns(true)
165
+ assert TestModule.send_notification?(@exception, count)
166
+ end
167
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'timecop'
5
+
6
+ class FormatterTest < ActiveSupport::TestCase
7
+ setup do
8
+ @exception = RuntimeError.new('test')
9
+ Timecop.freeze('2018-12-09 12:07:16 UTC')
10
+ end
11
+
12
+ teardown do
13
+ Timecop.return
14
+ end
15
+
16
+ #
17
+ # #title
18
+ #
19
+ test 'title returns correct content' do
20
+ formatter = ExceptionNotifier::Formatter.new(@exception)
21
+
22
+ title = if defined?(::Rails) && ::Rails.respond_to?(:env)
23
+ '⚠️ Error occurred in test ⚠️'
24
+ else
25
+ '⚠️ Error occurred ⚠️'
26
+ end
27
+
28
+ assert_equal title, formatter.title
29
+ end
30
+
31
+ #
32
+ # #subtitle
33
+ #
34
+ test 'subtitle without accumulated error' do
35
+ formatter = ExceptionNotifier::Formatter.new(@exception)
36
+ assert_equal 'A *RuntimeError* occurred.', formatter.subtitle
37
+ end
38
+
39
+ test 'subtitle with accumulated error' do
40
+ formatter = ExceptionNotifier::Formatter.new(@exception, accumulated_errors_count: 3)
41
+ assert_equal '3 *RuntimeError* occurred.', formatter.subtitle
42
+ end
43
+
44
+ test 'subtitle with controller' do
45
+ env = Rack::MockRequest.env_for(
46
+ '/', 'action_controller.instance' => test_controller
47
+ )
48
+
49
+ formatter = ExceptionNotifier::Formatter.new(@exception, env: env)
50
+ assert_equal 'A *RuntimeError* occurred in *home#index*.', formatter.subtitle
51
+ end
52
+
53
+ #
54
+ # #app_name
55
+ #
56
+ test 'app_name defaults to Rails app name' do
57
+ formatter = ExceptionNotifier::Formatter.new(@exception)
58
+
59
+ if defined?(::Rails) && ::Rails.respond_to?(:application)
60
+ assert_equal 'dummy', formatter.app_name
61
+ else
62
+ assert_nil formatter.app_name
63
+ end
64
+ end
65
+
66
+ test 'app_name can be overwritten using options' do
67
+ formatter = ExceptionNotifier::Formatter.new(@exception, app_name: 'test')
68
+ assert_equal 'test', formatter.app_name
69
+ end
70
+
71
+ #
72
+ # #request_message
73
+ #
74
+ test 'request_message when env set' do
75
+ text = [
76
+ '```',
77
+ '* url : http://test.address/?id=foo',
78
+ '* http_method : GET',
79
+ '* ip_address : 127.0.0.1',
80
+ '* parameters : {"id"=>"foo"}',
81
+ '* timestamp : 2018-12-09 12:07:16 UTC',
82
+ '```'
83
+ ].join("\n")
84
+
85
+ env = Rack::MockRequest.env_for(
86
+ '/',
87
+ 'HTTP_HOST' => 'test.address',
88
+ 'REMOTE_ADDR' => '127.0.0.1',
89
+ params: { id: 'foo' }
90
+ )
91
+
92
+ formatter = ExceptionNotifier::Formatter.new(@exception, env: env)
93
+ assert_equal text, formatter.request_message
94
+ end
95
+
96
+ test 'request_message when env not set' do
97
+ formatter = ExceptionNotifier::Formatter.new(@exception)
98
+ assert_nil formatter.request_message
99
+ end
100
+
101
+ #
102
+ # #backtrace_message
103
+ #
104
+ test 'backtrace_message when backtrace set' do
105
+ text = [
106
+ '```',
107
+ "* app/controllers/my_controller.rb:53:in `my_controller_params'",
108
+ "* app/controllers/my_controller.rb:34:in `update'",
109
+ '```'
110
+ ].join("\n")
111
+
112
+ @exception.set_backtrace([
113
+ "app/controllers/my_controller.rb:53:in `my_controller_params'",
114
+ "app/controllers/my_controller.rb:34:in `update'"
115
+ ])
116
+
117
+ formatter = ExceptionNotifier::Formatter.new(@exception)
118
+ assert_equal text, formatter.backtrace_message
119
+ end
120
+
121
+ test 'backtrace_message when no backtrace' do
122
+ formatter = ExceptionNotifier::Formatter.new(@exception)
123
+ assert_nil formatter.backtrace_message
124
+ end
125
+
126
+ #
127
+ # #controller_and_action
128
+ #
129
+ test 'correct controller_and_action if controller is present' do
130
+ env = Rack::MockRequest.env_for(
131
+ '/', 'action_controller.instance' => test_controller
132
+ )
133
+
134
+ formatter = ExceptionNotifier::Formatter.new(@exception, env: env)
135
+ assert_equal 'home#index', formatter.controller_and_action
136
+ end
137
+
138
+ test 'controller_and_action is nil if no controller' do
139
+ env = Rack::MockRequest.env_for('/')
140
+
141
+ formatter = ExceptionNotifier::Formatter.new(@exception, env: env)
142
+ assert_nil formatter.controller_and_action
143
+ end
144
+
145
+ def test_controller
146
+ controller = mock('controller')
147
+ controller.stubs(:action_name).returns('index')
148
+ controller.stubs(:controller_name).returns('home')
149
+
150
+ controller
151
+ end
152
+ end
@@ -1,38 +1,30 @@
1
- require "test_helper"
1
+ # frozen_string_literal: true
2
+
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
- require "sidekiq/cli"
8
+ require 'sidekiq/cli'
9
+ require 'sidekiq/testing'
7
10
 
8
- require "exception_notification/sidekiq"
11
+ require 'exception_notification/sidekiq'
9
12
 
10
13
  class MockSidekiqServer
11
14
  include ::Sidekiq::ExceptionHandler
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
- test "should call notify_exception when sidekiq raises an error" do
18
+ test 'should call notify_exception when sidekiq raises an error' do
23
19
  server = MockSidekiqServer.new
24
- message = Hash.new
20
+ message = {}
25
21
  exception = RuntimeError.new
26
22
 
27
23
  ExceptionNotifier.expects(:notify_exception).with(
28
24
  exception,
29
- :data => { :sidekiq => message }
25
+ data: { sidekiq: message }
30
26
  )
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,18 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'slack-notifier'
3
5
 
4
6
  class SlackNotifierTest < ActiveSupport::TestCase
5
-
6
7
  def setup
7
8
  @exception = fake_exception
8
9
  @exception.stubs(:backtrace).returns(fake_backtrace)
9
10
  @exception.stubs(:message).returns('exception message')
11
+ ExceptionNotifier::SlackNotifier.any_instance.stubs(:clean_backtrace).returns(fake_cleaned_backtrace)
10
12
  Socket.stubs(:gethostname).returns('example.com')
11
13
  end
12
14
 
13
- test "should send a slack notification if properly configured" do
15
+ test 'should send a slack notification if properly configured' do
14
16
  options = {
15
- webhook_url: "http://slack.webhook.url"
17
+ webhook_url: 'http://slack.webhook.url'
16
18
  }
17
19
 
18
20
  Slack::Notifier.any_instance.expects(:ping).with('', fake_notification)
@@ -21,9 +23,9 @@ class SlackNotifierTest < ActiveSupport::TestCase
21
23
  slack_notifier.call(@exception)
22
24
  end
23
25
 
24
- test "should send a slack notification without backtrace info if properly configured" do
26
+ test 'should send a slack notification without backtrace info if properly configured' do
25
27
  options = {
26
- webhook_url: "http://slack.webhook.url"
28
+ webhook_url: 'http://slack.webhook.url'
27
29
  }
28
30
 
29
31
  Slack::Notifier.any_instance.expects(:ping).with('', fake_notification(fake_exception_without_backtrace))
@@ -32,10 +34,10 @@ class SlackNotifierTest < ActiveSupport::TestCase
32
34
  slack_notifier.call(fake_exception_without_backtrace)
33
35
  end
34
36
 
35
- test "should send the notification to the specified channel" do
37
+ test 'should send the notification to the specified channel' do
36
38
  options = {
37
- webhook_url: "http://slack.webhook.url",
38
- channel: "channel"
39
+ webhook_url: 'http://slack.webhook.url',
40
+ channel: 'channel'
39
41
  }
40
42
 
41
43
  Slack::Notifier.any_instance.expects(:ping).with('', fake_notification)
@@ -43,13 +45,14 @@ class SlackNotifierTest < ActiveSupport::TestCase
43
45
  slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
44
46
  slack_notifier.call(@exception)
45
47
 
46
- assert_equal slack_notifier.notifier.channel, options[:channel]
48
+ channel = slack_notifier.notifier.config.defaults[:channel]
49
+ assert_equal channel, options[:channel]
47
50
  end
48
51
 
49
- test "should send the notification to the specified username" do
52
+ test 'should send the notification to the specified username' do
50
53
  options = {
51
- webhook_url: "http://slack.webhook.url",
52
- username: "username"
54
+ webhook_url: 'http://slack.webhook.url',
55
+ username: 'username'
53
56
  }
54
57
 
55
58
  Slack::Notifier.any_instance.expects(:ping).with('', fake_notification)
@@ -57,12 +60,13 @@ class SlackNotifierTest < ActiveSupport::TestCase
57
60
  slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
58
61
  slack_notifier.call(@exception)
59
62
 
60
- assert_equal slack_notifier.notifier.username, options[:username]
63
+ username = slack_notifier.notifier.config.defaults[:username]
64
+ assert_equal username, options[:username]
61
65
  end
62
66
 
63
- test "should send the notification with specific backtrace lines" do
67
+ test 'should send the notification with specific backtrace lines' do
64
68
  options = {
65
- webhook_url: "http://slack.webhook.url",
69
+ webhook_url: 'http://slack.webhook.url',
66
70
  backtrace_lines: 1
67
71
  }
68
72
 
@@ -72,17 +76,33 @@ class SlackNotifierTest < ActiveSupport::TestCase
72
76
  slack_notifier.call(@exception)
73
77
  end
74
78
 
75
- test "should pass the additional parameters to Slack::Notifier.ping" do
79
+ test 'should send the notification with additional fields' do
80
+ field = { title: 'Branch', value: 'master', short: true }
81
+ options = {
82
+ webhook_url: 'http://slack.webhook.url',
83
+ additional_fields: [field]
84
+ }
85
+
86
+ Slack::Notifier.any_instance.expects(:ping).with('', fake_notification(@exception, {}, nil, 10, [field]))
87
+
88
+ slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
89
+ slack_notifier.call(@exception)
90
+
91
+ additional_fields = slack_notifier.notifier.config.defaults[:additional_fields]
92
+ assert_equal additional_fields, options[:additional_fields]
93
+ end
94
+
95
+ test 'should pass the additional parameters to Slack::Notifier.ping' do
76
96
  options = {
77
- webhook_url: "http://slack.webhook.url",
78
- username: "test",
79
- custom_hook: "hook",
97
+ webhook_url: 'http://slack.webhook.url',
98
+ username: 'test',
99
+ custom_hook: 'hook',
80
100
  additional_parameters: {
81
- icon_url: "icon",
101
+ icon_url: 'icon'
82
102
  }
83
103
  }
84
104
 
85
- Slack::Notifier.any_instance.expects(:ping).with('', options[:additional_parameters].merge(fake_notification) )
105
+ Slack::Notifier.any_instance.expects(:ping).with('', options[:additional_parameters].merge(fake_notification))
86
106
 
87
107
  slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
88
108
  slack_notifier.call(@exception)
@@ -97,55 +117,57 @@ class SlackNotifierTest < ActiveSupport::TestCase
97
117
  assert_nil slack_notifier.call(@exception)
98
118
  end
99
119
 
100
- test "should pass along environment data" do
120
+ test 'should pass along environment data' do
101
121
  options = {
102
- webhook_url: "http://slack.webhook.url",
103
- ignore_data_if: lambda {|k,v|
104
- "#{k}" == 'key_to_be_ignored' || v.is_a?(Hash)
122
+ webhook_url: 'http://slack.webhook.url',
123
+ ignore_data_if: lambda { |k, v|
124
+ k.to_s == 'key_to_be_ignored' || v.is_a?(Hash)
105
125
  }
106
126
  }
107
127
 
108
128
  notification_options = {
109
129
  env: {
110
- 'exception_notifier.exception_data' => {foo: 'bar', john: 'doe'}
130
+ 'exception_notifier.exception_data' => { foo: 'bar', john: 'doe' }
111
131
  },
112
132
  data: {
113
- 'user_id' => 5,
133
+ 'user_id' => 5,
114
134
  'key_to_be_ignored' => 'whatever',
115
- 'ignore_as_well' => {what: 'ever'}
135
+ 'ignore_as_well' => { what: 'ever' }
116
136
  }
117
137
  }
118
138
 
119
139
  expected_data_string = "foo: bar\njohn: doe\nuser_id: 5"
120
140
 
121
- 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))
122
144
  slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
123
145
  slack_notifier.call(@exception, notification_options)
124
146
  end
125
147
 
126
- test "should call pre/post_callback proc if specified" do
148
+ test 'should call pre/post_callback proc if specified' do
127
149
  post_callback_called = 0
128
150
  options = {
129
- webhook_url: "http://slack.webhook.url",
130
- username: "test",
131
- custom_hook: "hook",
132
- :pre_callback => proc { |opts, notifier, backtrace, message, message_opts|
133
- (message_opts[:attachments] = []) << { text: "#{backtrace.join("\n")}", color: 'danger' }
151
+ webhook_url: 'http://slack.webhook.url',
152
+ username: 'test',
153
+ custom_hook: 'hook',
154
+ pre_callback: proc { |_opts, _notifier, backtrace, _message, message_opts|
155
+ (message_opts[:attachments] = []) << { text: backtrace.join("\n").to_s, color: 'danger' }
134
156
  },
135
- :post_callback => proc { |opts, notifier, backtrace, message, message_opts|
157
+ post_callback: proc { |_opts, _notifier, _backtrace, _message, _message_opts|
136
158
  post_callback_called = 1
137
159
  },
138
160
  additional_parameters: {
139
- icon_url: "icon",
161
+ icon_url: 'icon'
140
162
  }
141
163
  }
142
164
 
143
165
  Slack::Notifier.any_instance.expects(:ping).with('',
144
- {:icon_url => 'icon',
145
- :attachments => [
146
- {:text => fake_backtrace.join("\n"),
147
- :color => 'danger'}
148
- ]})
166
+ icon_url: 'icon',
167
+ attachments: [{
168
+ text: fake_backtrace.join("\n"),
169
+ color: 'danger'
170
+ }])
149
171
 
150
172
  slack_notifier = ExceptionNotifier::SlackNotifier.new(options)
151
173
  slack_notifier.call(@exception)
@@ -155,11 +177,9 @@ class SlackNotifierTest < ActiveSupport::TestCase
155
177
  private
156
178
 
157
179
  def fake_exception
158
- begin
159
- 5/0
160
- rescue Exception => e
161
- e
162
- end
180
+ 5 / 0
181
+ rescue StandardError => e
182
+ e
163
183
  end
164
184
 
165
185
  def fake_exception_without_backtrace
@@ -168,17 +188,19 @@ class SlackNotifierTest < ActiveSupport::TestCase
168
188
 
169
189
  def fake_backtrace
170
190
  [
171
- "backtrace line 1",
172
- "backtrace line 2",
173
- "backtrace line 3",
174
- "backtrace line 4",
175
- "backtrace line 5",
176
- "backtrace line 6",
191
+ 'backtrace line 1', 'backtrace line 2', 'backtrace line 3',
192
+ 'backtrace line 4', 'backtrace line 5', 'backtrace line 6'
177
193
  ]
178
194
  end
179
195
 
180
- def fake_notification(exception = @exception, notification_options = {}, data_string = nil, expected_backtrace_lines = nil)
181
- exception_name = "*#{exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A'}* `#{exception.class.to_s}`"
196
+ def fake_cleaned_backtrace
197
+ fake_backtrace[2..-1]
198
+ end
199
+
200
+ def fake_notification(exception = @exception, notification_options = {},
201
+ data_string = nil, expected_backtrace_lines = 10, additional_fields = [])
202
+
203
+ exception_name = "*#{exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A'}* `#{exception.class}`"
182
204
  if notification_options[:env].nil?
183
205
  text = "#{exception_name} *occured in background*"
184
206
  else
@@ -193,15 +215,15 @@ class SlackNotifierTest < ActiveSupport::TestCase
193
215
 
194
216
  text += "\n"
195
217
 
196
- fields = [ { title: 'Exception', value: exception.message} ]
197
- fields.push({ title: 'Hostname', value: 'example.com' })
218
+ fields = [{ title: 'Exception', value: exception.message }]
219
+ fields.push(title: 'Hostname', value: 'example.com')
198
220
  if exception.backtrace
199
- formatted_backtrace = expected_backtrace_lines ? "```#{exception.backtrace.first(expected_backtrace_lines).join("\n")}```" : "```#{exception.backtrace.join("\n")}```"
200
- fields.push({ title: 'Backtrace', value: formatted_backtrace })
221
+ formatted_backtrace = "```#{fake_cleaned_backtrace.first(expected_backtrace_lines).join("\n")}```"
222
+ fields.push(title: 'Backtrace', value: formatted_backtrace)
201
223
  end
202
- fields.push({ title: 'Data', value: "```#{data_string}```" }) if data_string
224
+ fields.push(title: 'Data', value: "```#{data_string}```") if data_string
225
+ additional_fields.each { |f| fields.push(f) }
203
226
 
204
- { attachments: [ color: 'danger', text: text, fields: fields, mrkdwn_in: %w(text fields) ] }
227
+ { attachments: [color: 'danger', text: text, fields: fields, mrkdwn_in: %w[text fields]] }
205
228
  end
206
-
207
229
  end