exception_notification 4.3.0 → 4.5.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 (131) hide show
  1. checksums.yaml +5 -5
  2. data/Appraisals +4 -2
  3. data/CHANGELOG.rdoc +47 -0
  4. data/CONTRIBUTING.md +18 -0
  5. data/Gemfile +3 -1
  6. data/README.md +97 -945
  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 -24
  25. data/gemfiles/{rails4_0.gemfile → rails5_2.gemfile} +2 -2
  26. data/gemfiles/{rails4_1.gemfile → rails6_0.gemfile} +2 -2
  27. data/gemfiles/{rails4_2.gemfile → rails6_1.gemfile} +2 -2
  28. data/gemfiles/{rails5_0.gemfile → rails7_0.gemfile} +2 -2
  29. data/lib/exception_notification/rack.rb +28 -30
  30. data/lib/exception_notification/rails.rb +2 -0
  31. data/lib/exception_notification/resque.rb +10 -10
  32. data/lib/exception_notification/sidekiq.rb +10 -12
  33. data/lib/exception_notification/version.rb +5 -0
  34. data/lib/exception_notification.rb +3 -0
  35. data/lib/exception_notifier/base_notifier.rb +10 -5
  36. data/lib/exception_notifier/datadog_notifier.rb +156 -0
  37. data/lib/exception_notifier/email_notifier.rb +73 -88
  38. data/lib/exception_notifier/google_chat_notifier.rb +27 -119
  39. data/lib/exception_notifier/hipchat_notifier.rb +13 -12
  40. data/lib/exception_notifier/irc_notifier.rb +36 -33
  41. data/lib/exception_notifier/mattermost_notifier.rb +54 -137
  42. data/lib/exception_notifier/modules/backtrace_cleaner.rb +2 -2
  43. data/lib/exception_notifier/modules/error_grouping.rb +24 -13
  44. data/lib/exception_notifier/modules/formatter.rb +125 -0
  45. data/lib/exception_notifier/notifier.rb +9 -6
  46. data/lib/exception_notifier/slack_notifier.rb +65 -40
  47. data/lib/exception_notifier/sns_notifier.rb +23 -13
  48. data/lib/exception_notifier/teams_notifier.rb +67 -46
  49. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
  50. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
  51. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
  52. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -2
  53. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
  54. data/lib/exception_notifier/webhook_notifier.rb +17 -14
  55. data/lib/exception_notifier.rb +65 -10
  56. data/lib/generators/exception_notification/install_generator.rb +11 -5
  57. data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
  58. data/test/exception_notification/rack_test.rb +75 -13
  59. data/test/exception_notification/resque_test.rb +54 -0
  60. data/test/exception_notifier/datadog_notifier_test.rb +153 -0
  61. data/test/exception_notifier/email_notifier_test.rb +275 -153
  62. data/test/exception_notifier/google_chat_notifier_test.rb +158 -101
  63. data/test/exception_notifier/hipchat_notifier_test.rb +84 -81
  64. data/test/exception_notifier/irc_notifier_test.rb +36 -34
  65. data/test/exception_notifier/mattermost_notifier_test.rb +213 -67
  66. data/test/exception_notifier/modules/error_grouping_test.rb +41 -40
  67. data/test/exception_notifier/modules/formatter_test.rb +152 -0
  68. data/test/exception_notifier/sidekiq_test.rb +9 -17
  69. data/test/exception_notifier/slack_notifier_test.rb +66 -63
  70. data/test/exception_notifier/sns_notifier_test.rb +84 -32
  71. data/test/exception_notifier/teams_notifier_test.rb +25 -26
  72. data/test/exception_notifier/webhook_notifier_test.rb +52 -48
  73. data/test/exception_notifier_test.rb +150 -41
  74. data/test/support/exception_notifier_helper.rb +14 -0
  75. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
  76. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
  77. data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
  78. data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
  79. data/test/test_helper.rb +14 -13
  80. metadata +134 -175
  81. data/gemfiles/rails5_1.gemfile +0 -7
  82. data/lib/exception_notifier/campfire_notifier.rb +0 -40
  83. data/test/dummy/.gitignore +0 -4
  84. data/test/dummy/Rakefile +0 -7
  85. data/test/dummy/app/controllers/application_controller.rb +0 -3
  86. data/test/dummy/app/controllers/posts_controller.rb +0 -30
  87. data/test/dummy/app/helpers/application_helper.rb +0 -2
  88. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  89. data/test/dummy/app/models/post.rb +0 -2
  90. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  91. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  92. data/test/dummy/app/views/posts/new.html.erb +0 -0
  93. data/test/dummy/app/views/posts/show.html.erb +0 -0
  94. data/test/dummy/config/application.rb +0 -42
  95. data/test/dummy/config/boot.rb +0 -6
  96. data/test/dummy/config/database.yml +0 -22
  97. data/test/dummy/config/environment.rb +0 -17
  98. data/test/dummy/config/environments/development.rb +0 -25
  99. data/test/dummy/config/environments/production.rb +0 -50
  100. data/test/dummy/config/environments/test.rb +0 -35
  101. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  102. data/test/dummy/config/initializers/inflections.rb +0 -10
  103. data/test/dummy/config/initializers/mime_types.rb +0 -5
  104. data/test/dummy/config/initializers/secret_token.rb +0 -8
  105. data/test/dummy/config/initializers/session_store.rb +0 -8
  106. data/test/dummy/config/locales/en.yml +0 -5
  107. data/test/dummy/config/routes.rb +0 -3
  108. data/test/dummy/config.ru +0 -4
  109. data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
  110. data/test/dummy/db/schema.rb +0 -24
  111. data/test/dummy/db/seeds.rb +0 -7
  112. data/test/dummy/lib/tasks/.gitkeep +0 -0
  113. data/test/dummy/public/404.html +0 -26
  114. data/test/dummy/public/422.html +0 -26
  115. data/test/dummy/public/500.html +0 -26
  116. data/test/dummy/public/favicon.ico +0 -0
  117. data/test/dummy/public/images/rails.png +0 -0
  118. data/test/dummy/public/index.html +0 -239
  119. data/test/dummy/public/javascripts/application.js +0 -2
  120. data/test/dummy/public/javascripts/controls.js +0 -965
  121. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  122. data/test/dummy/public/javascripts/effects.js +0 -1123
  123. data/test/dummy/public/javascripts/prototype.js +0 -6001
  124. data/test/dummy/public/javascripts/rails.js +0 -191
  125. data/test/dummy/public/robots.txt +0 -5
  126. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  127. data/test/dummy/public/stylesheets/scaffold.css +0 -56
  128. data/test/dummy/script/rails +0 -6
  129. data/test/dummy/test/functional/posts_controller_test.rb +0 -237
  130. data/test/dummy/test/test_helper.rb +0 -7
  131. data/test/exception_notifier/campfire_notifier_test.rb +0 -120
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'carrier-pigeon'
3
5
 
4
6
  class IrcNotifierTest < ActiveSupport::TestCase
5
-
6
- test "should send irc notification if properly configured" do
7
+ test 'should send irc notification if properly configured' do
7
8
  options = {
8
- :domain => 'irc.example.com'
9
+ domain: 'irc.example.com'
9
10
  }
10
11
 
11
12
  CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
@@ -16,29 +17,30 @@ class IrcNotifierTest < ActiveSupport::TestCase
16
17
  irc.call(fake_exception)
17
18
  end
18
19
 
19
- test "should exclude errors count in message if :accumulated_errors_count nil" do
20
+ test 'should exclude errors count in message if :accumulated_errors_count nil' do
20
21
  irc = ExceptionNotifier::IrcNotifier.new({})
21
22
  irc.stubs(:active?).returns(true)
22
23
 
23
- irc.expects(:send_message).with{ |message| message.include?("divided by 0") }.once
24
+ irc.expects(:send_message).with { |message| message.include?('divided by 0') }.once
24
25
  irc.call(fake_exception)
25
26
  end
26
27
 
27
- test "should include errors count in message if :accumulated_errors_count is 3" do
28
+ test 'should include errors count in message if :accumulated_errors_count is 3' do
28
29
  irc = ExceptionNotifier::IrcNotifier.new({})
29
30
  irc.stubs(:active?).returns(true)
30
31
 
31
- irc.expects(:send_message).with{ |message| message.include?("(3 times)'divided by 0'") }.once
32
+ irc.expects(:send_message).with { |message| message.include?("(3 times)'divided by 0'") }.once
32
33
  irc.call(fake_exception, accumulated_errors_count: 3)
33
34
  end
34
35
 
35
- test "should call pre/post_callback if specified" do
36
- pre_callback_called, post_callback_called = 0,0
36
+ test 'should call pre/post_callback if specified' do
37
+ pre_callback_called = 0
38
+ post_callback_called = 0
37
39
 
38
40
  options = {
39
- :domain => 'irc.example.com',
40
- :pre_callback => proc { |*| pre_callback_called += 1},
41
- :post_callback => proc { |*| post_callback_called += 1}
41
+ domain: 'irc.example.com',
42
+ pre_callback: proc { |*| pre_callback_called += 1 },
43
+ post_callback: proc { |*| post_callback_called += 1 }
42
44
  }
43
45
 
44
46
  CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
@@ -51,9 +53,9 @@ class IrcNotifierTest < ActiveSupport::TestCase
51
53
  assert_equal(1, post_callback_called)
52
54
  end
53
55
 
54
- test "should send irc notification without backtrace info if properly configured" do
56
+ test 'should send irc notification without backtrace info if properly configured' do
55
57
  options = {
56
- :domain => 'irc.example.com'
58
+ domain: 'irc.example.com'
57
59
  }
58
60
 
59
61
  CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
@@ -64,25 +66,25 @@ class IrcNotifierTest < ActiveSupport::TestCase
64
66
  irc.call(fake_exception_without_backtrace)
65
67
  end
66
68
 
67
- test "should properly construct URI from constituent parts" do
69
+ test 'should properly construct URI from constituent parts' do
68
70
  options = {
69
- :nick => 'BadNewsBot',
70
- :password => 'secret',
71
- :domain => 'irc.example.com',
72
- :port => 9999,
73
- :channel => '#exceptions'
71
+ nick: 'BadNewsBot',
72
+ password: 'secret',
73
+ domain: 'irc.example.com',
74
+ port: 9999,
75
+ channel: '#exceptions'
74
76
  }
75
77
 
76
- CarrierPigeon.expects(:send).with(has_entry(uri: "irc://BadNewsBot:secret@irc.example.com:9999/#exceptions"))
78
+ CarrierPigeon.expects(:send).with(has_entry(uri: 'irc://BadNewsBot:secret@irc.example.com:9999/#exceptions'))
77
79
 
78
80
  irc = ExceptionNotifier::IrcNotifier.new(options)
79
81
  irc.call(fake_exception)
80
82
  end
81
83
 
82
- test "should properly add recipients if specified" do
84
+ test 'should properly add recipients if specified' do
83
85
  options = {
84
86
  domain: 'irc.example.com',
85
- recipients: ['peter', 'michael', 'samir']
87
+ recipients: %w[peter michael samir]
86
88
  }
87
89
 
88
90
  CarrierPigeon.expects(:send).with(has_key(:uri)) do |v|
@@ -93,7 +95,7 @@ class IrcNotifierTest < ActiveSupport::TestCase
93
95
  irc.call(fake_exception)
94
96
  end
95
97
 
96
- test "should properly set miscellaneous options" do
98
+ test 'should properly set miscellaneous options' do
97
99
  options = {
98
100
  domain: 'irc.example.com',
99
101
  ssl: true,
@@ -102,11 +104,13 @@ class IrcNotifierTest < ActiveSupport::TestCase
102
104
  prefix: '[test notification]'
103
105
  }
104
106
 
105
- CarrierPigeon.expects(:send).with(has_entries(
107
+ entries = {
106
108
  ssl: true,
107
109
  join: true,
108
- notice: true,
109
- )) do |v|
110
+ notice: true
111
+ }
112
+
113
+ CarrierPigeon.expects(:send).with(has_entries(entries)) do |v|
110
114
  /\[test notification\]/.match(v[:message])
111
115
  end
112
116
 
@@ -114,8 +118,8 @@ class IrcNotifierTest < ActiveSupport::TestCase
114
118
  irc.call(fake_exception)
115
119
  end
116
120
 
117
- test "should not send irc notification if badly configured" do
118
- wrong_params = { domain: '##scriptkiddie.com###'}
121
+ test 'should not send irc notification if badly configured' do
122
+ wrong_params = { domain: '##scriptkiddie.com###' }
119
123
  irc = ExceptionNotifier::IrcNotifier.new(wrong_params)
120
124
 
121
125
  assert_nil irc.call(fake_exception)
@@ -124,11 +128,9 @@ class IrcNotifierTest < ActiveSupport::TestCase
124
128
  private
125
129
 
126
130
  def fake_exception
127
- begin
128
- 5/0
129
- rescue Exception => e
130
- e
131
- end
131
+ 5 / 0
132
+ rescue StandardError => e
133
+ e
132
134
  end
133
135
 
134
136
  def fake_exception_without_backtrace
@@ -1,105 +1,251 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'httparty'
5
+ require 'timecop'
6
+ require 'json'
3
7
 
4
8
  class MattermostNotifierTest < ActiveSupport::TestCase
9
+ URL = 'http://localhost:8000'
10
+
11
+ def setup
12
+ Timecop.freeze('2018-12-09 12:07:16 UTC')
13
+ end
14
+
15
+ def teardown
16
+ Timecop.return
17
+ end
5
18
 
6
- test "should send notification if properly configured" do
7
- options = {
8
- :webhook_url => 'http://localhost:8000'
19
+ test 'should send notification if properly configured' do
20
+ opts = {
21
+ body: default_body.to_json,
22
+ headers: default_headers
9
23
  }
10
- mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
11
- mattermost_notifier.httparty = FakeHTTParty.new
12
24
 
13
- options = mattermost_notifier.call ArgumentError.new("foo"), options
25
+ HTTParty.expects(:post).with(URL, opts)
26
+ notifier.call ArgumentError.new('foo')
27
+ end
14
28
 
15
- body = ActiveSupport::JSON.decode options[:body]
16
- assert body.has_key? 'text'
17
- assert body.has_key? 'username'
29
+ test 'should send notification with create issue link if specified' do
30
+ body = default_body.merge(
31
+ text: [
32
+ '@channel',
33
+ error_occurred_in,
34
+ 'An *ArgumentError* occurred.',
35
+ '*foo*',
36
+ github_link
37
+ ].join("\n")
38
+ )
39
+
40
+ opts = {
41
+ body: body.to_json,
42
+ headers: default_headers
43
+ }
18
44
 
19
- text = body['text'].split("\n")
20
- assert_equal 4, text.size
21
- assert_equal '@channel', text[0]
22
- assert_equal 'An *ArgumentError* occured.', text[2]
23
- assert_equal '*foo*', text[3]
45
+ HTTParty.expects(:post).with(URL, opts)
46
+ notifier.call ArgumentError.new('foo'), git_url: 'github.com/aschen'
24
47
  end
25
48
 
26
- test "should send notification with create issue link if specified" do
27
- options = {
28
- :webhook_url => 'http://localhost:8000',
29
- :git_url => 'github.com/aschen'
49
+ test 'should add username and icon_url params to the notification if specified' do
50
+ body = default_body.merge(
51
+ username: 'Test Bot',
52
+ icon_url: 'http://site.com/icon.png'
53
+ )
54
+
55
+ opts = {
56
+ body: body.to_json,
57
+ headers: default_headers
30
58
  }
31
- mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
32
- mattermost_notifier.httparty = FakeHTTParty.new
33
59
 
34
- options = mattermost_notifier.call ArgumentError.new("foo"), options
60
+ HTTParty.expects(:post).with(URL, opts)
61
+ notifier.call(
62
+ ArgumentError.new('foo'),
63
+ username: 'Test Bot',
64
+ avatar: 'http://site.com/icon.png'
65
+ )
66
+ end
35
67
 
36
- body = ActiveSupport::JSON.decode options[:body]
68
+ test 'should add other HTTParty options to params' do
69
+ opts = {
70
+ basic_auth: {
71
+ username: 'clara',
72
+ password: 'password'
73
+ },
74
+ body: default_body.to_json,
75
+ headers: default_headers
76
+ }
37
77
 
38
- text = body['text'].split("\n")
39
- assert_equal 5, text.size
40
- assert_equal '[Create an issue](github.com/aschen/dummy/issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)', text[4]
78
+ HTTParty.expects(:post).with(URL, opts)
79
+ notifier.call(
80
+ ArgumentError.new('foo'),
81
+ basic_auth: {
82
+ username: 'clara',
83
+ password: 'password'
84
+ }
85
+ )
41
86
  end
42
87
 
43
- test 'should add username and icon_url params to the notification if specified' do
44
- options = {
45
- :webhook_url => 'http://localhost:8000',
46
- :username => "Test Bot",
47
- :avatar => 'http://site.com/icon.png'
88
+ test "should use 'An' for exceptions count if :accumulated_errors_count option is nil" do
89
+ opts = {
90
+ body: default_body.to_json,
91
+ headers: default_headers
48
92
  }
49
- mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
50
- mattermost_notifier.httparty = FakeHTTParty.new
51
93
 
52
- options = mattermost_notifier.call ArgumentError.new("foo"), options
94
+ HTTParty.expects(:post).with(URL, opts)
95
+ notifier.call(ArgumentError.new('foo'))
96
+ end
53
97
 
54
- body = ActiveSupport::JSON.decode options[:body]
98
+ test 'shoud use direct errors count if :accumulated_errors_count option is 5' do
99
+ body = default_body.merge(
100
+ text: [
101
+ '@channel',
102
+ error_occurred_in,
103
+ '5 *ArgumentError* occurred.',
104
+ '*foo*'
105
+ ].join("\n")
106
+ )
107
+
108
+ opts = {
109
+ body: body.to_json,
110
+ headers: default_headers
111
+ }
55
112
 
56
- assert_equal 'Test Bot', body['username']
57
- assert_equal 'http://site.com/icon.png', body['icon_url']
113
+ HTTParty.expects(:post).with(URL, opts)
114
+ notifier.call(ArgumentError.new('foo'), accumulated_errors_count: 5)
58
115
  end
59
116
 
60
- test 'should add other HTTParty options to params' do
61
- options = {
62
- :webhook_url => 'http://localhost:8000',
63
- :username => "Test Bot",
64
- :avatar => 'http://site.com/icon.png',
65
- :basic_auth => {
66
- :username => 'clara',
67
- :password => 'password'
68
- }
117
+ test 'should include backtrace and request info' do
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"))
126
+
127
+ opts = {
128
+ body: body.to_json,
129
+ headers: default_headers
69
130
  }
70
- mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
71
- mattermost_notifier.httparty = FakeHTTParty.new
72
131
 
73
- options = mattermost_notifier.call ArgumentError.new("foo"), options
132
+ HTTParty.expects(:post).with(URL, opts)
74
133
 
75
- assert options.has_key? :basic_auth
76
- assert 'clara', options[:basic_auth][:username]
77
- assert 'password', options[:basic_auth][:password]
134
+ exception = ArgumentError.new('foo')
135
+ exception.set_backtrace([
136
+ "app/controllers/my_controller.rb:53:in `my_controller_params'",
137
+ "app/controllers/my_controller.rb:34:in `update'"
138
+ ])
139
+
140
+ notifier.call(exception, env: test_env)
78
141
  end
79
142
 
80
- test "should use 'An' for exceptions count if :accumulated_errors_count option is nil" do
81
- mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
82
- exception = ArgumentError.new("foo")
83
- mattermost_notifier.instance_variable_set(:@exception, exception)
84
- mattermost_notifier.instance_variable_set(:@options, {})
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
+ }
85
159
 
86
- assert_includes mattermost_notifier.send(:message_header), "An *ArgumentError* occured."
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)
87
166
  end
88
167
 
89
- test "shoud use direct errors count if :accumulated_errors_count option is 5" do
90
- mattermost_notifier = ExceptionNotifier::MattermostNotifier.new
91
- exception = ArgumentError.new("foo")
92
- mattermost_notifier.instance_variable_set(:@exception, exception)
93
- mattermost_notifier.instance_variable_set(:@options, { accumulated_errors_count: 5 })
168
+ private
94
169
 
95
- assert_includes mattermost_notifier.send(:message_header), "5 *ArgumentError* occured."
170
+ def notifier
171
+ ExceptionNotifier::MattermostNotifier.new(webhook_url: URL)
172
+ end
173
+
174
+ def default_body
175
+ {
176
+ text: [
177
+ '@channel',
178
+ error_occurred_in,
179
+ 'An *ArgumentError* occurred.',
180
+ '*foo*'
181
+ ].join("\n"),
182
+ username: 'Exception Notifier'
183
+ }
184
+ end
185
+
186
+ def default_headers
187
+ { 'Content-Type' => 'application/json' }
96
188
  end
97
- end
98
189
 
99
- class FakeHTTParty
190
+ def test_env
191
+ Rack::MockRequest.env_for(
192
+ '/',
193
+ 'HTTP_HOST' => 'test.address',
194
+ 'REMOTE_ADDR' => '127.0.0.1',
195
+ 'HTTP_USER_AGENT' => 'Rails Testing',
196
+ params: { id: 'foo' }
197
+ )
198
+ end
100
199
 
101
- def post(url, options)
102
- return options
200
+ def error_occurred_in
201
+ if defined?(::Rails) && ::Rails.respond_to?(:env)
202
+ '### ⚠️ Error occurred in test ⚠️'
203
+ else
204
+ '### ⚠️ Error occurred ⚠️'
205
+ end
103
206
  end
104
207
 
208
+ def github_link
209
+ if defined?(::Rails) && ::Rails.respond_to?(:application)
210
+ '[Create an issue]' \
211
+ '(github.com/aschen/dummy/issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)'
212
+ else
213
+ # TODO: fix missing app name
214
+ '[Create an issue]' \
215
+ '(github.com/aschen//issues/new/?issue%5Btitle%5D=%5BBUG%5D+Error+500+%3A++%28ArgumentError%29+foo)'
216
+ end
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
105
251
  end
@@ -1,18 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ErrorGroupTest < ActiveSupport::TestCase
4
-
5
6
  setup do
6
7
  module TestModule
7
8
  include ExceptionNotifier::ErrorGrouping
8
- @@error_grouping_cache = ActiveSupport::Cache::FileStore.new("test/dummy/tmp/non_default_location")
9
+ @@error_grouping_cache = ActiveSupport::Cache::FileStore.new('test/dummy/tmp/non_default_location')
9
10
  end
10
11
 
11
- @exception = RuntimeError.new("ERROR")
12
- @exception.stubs(:backtrace).returns(["/path/where/error/raised:1"])
12
+ @exception = RuntimeError.new('ERROR')
13
+ @exception.stubs(:backtrace).returns(['/path/where/error/raised:1'])
13
14
 
14
- @exception2 = RuntimeError.new("ERROR2")
15
- @exception2.stubs(:backtrace).returns(["/path/where/error/found:2"])
15
+ @exception2 = RuntimeError.new('ERROR2')
16
+ @exception2.stubs(:backtrace).returns(['/path/where/error/found:2'])
16
17
  end
17
18
 
18
19
  teardown do
@@ -20,88 +21,88 @@ class ErrorGroupTest < ActiveSupport::TestCase
20
21
  TestModule.fallback_cache_store.clear
21
22
  end
22
23
 
23
- test "should add additional option: error_grouping" do
24
+ test 'should add additional option: error_grouping' do
24
25
  assert_respond_to TestModule, :error_grouping
25
26
  assert_respond_to TestModule, :error_grouping=
26
27
  end
27
28
 
28
- test "should set error_grouping to false default" do
29
+ test 'should set error_grouping to false default' do
29
30
  assert_equal false, TestModule.error_grouping
30
31
  end
31
32
 
32
- test "should add additional option: error_grouping_cache" do
33
+ test 'should add additional option: error_grouping_cache' do
33
34
  assert_respond_to TestModule, :error_grouping_cache
34
35
  assert_respond_to TestModule, :error_grouping_cache=
35
36
  end
36
37
 
37
- test "should add additional option: error_grouping_period" do
38
+ test 'should add additional option: error_grouping_period' do
38
39
  assert_respond_to TestModule, :error_grouping_period
39
40
  assert_respond_to TestModule, :error_grouping_period=
40
41
  end
41
42
 
42
- test "shoud set error_grouping_period to 5.minutes default" do
43
+ test 'shoud set error_grouping_period to 5.minutes default' do
43
44
  assert_equal 300, TestModule.error_grouping_period
44
45
  end
45
46
 
46
- test "should add additional option: notification_trigger" do
47
+ test 'should add additional option: notification_trigger' do
47
48
  assert_respond_to TestModule, :notification_trigger
48
49
  assert_respond_to TestModule, :notification_trigger=
49
50
  end
50
51
 
51
- test "should return errors count nil when not same error for .error_count" do
52
- assert_nil TestModule.error_count("something")
52
+ test 'should return errors count nil when not same error for .error_count' do
53
+ assert_nil TestModule.error_count('something')
53
54
  end
54
55
 
55
- test "should return errors count when same error for .error_count" do
56
- TestModule.error_grouping_cache.write("error_key", 13)
57
- assert_equal 13, TestModule.error_count("error_key")
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')
58
59
  end
59
60
 
60
- test "should fallback to memory store cache if specified cache store failed to read" do
61
- TestModule.error_grouping_cache.stubs(:read).raises(RuntimeError.new "Failed to read")
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'))
62
63
  original_fallback = TestModule.fallback_cache_store
63
64
  TestModule.expects(:fallback_cache_store).returns(original_fallback).at_least_once
64
65
 
65
- assert_nil TestModule.error_count("something_to_read")
66
+ assert_nil TestModule.error_count('something_to_read')
66
67
  end
67
68
 
68
- test "should save error with count for .save_error_count" do
69
+ test 'should save error with count for .save_error_count' do
69
70
  count = rand(1..10)
70
71
 
71
- TestModule.save_error_count("error_key", count)
72
- assert_equal count, TestModule.error_grouping_cache.read("error_key")
72
+ TestModule.save_error_count('error_key', count)
73
+ assert_equal count, TestModule.error_grouping_cache.read('error_key')
73
74
  end
74
75
 
75
- test "should fallback to memory store cache if specified cache store failed to write" do
76
- TestModule.error_grouping_cache.stubs(:write).raises(RuntimeError.new "Failed to write")
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'))
77
78
  original_fallback = TestModule.fallback_cache_store
78
79
  TestModule.expects(:fallback_cache_store).returns(original_fallback).at_least_once
79
80
 
80
- assert TestModule.save_error_count("something_to_cache", rand(1..10))
81
+ assert TestModule.save_error_count('something_to_cache', rand(1..10))
81
82
  end
82
83
 
83
- test "should save accumulated_errors_count into options" do
84
+ test 'should save accumulated_errors_count into options' do
84
85
  options = {}
85
86
  TestModule.group_error!(@exception, options)
86
87
 
87
88
  assert_equal 1, options[:accumulated_errors_count]
88
89
  end
89
90
 
90
- test "should not group error if different exception in .group_error!" do
91
+ test 'should not group error if different exception in .group_error!' do
91
92
  options1 = {}
92
- TestModule.expects(:save_error_count).with{|key, count| key.is_a?(String) && count == 1}.times(4).returns(true)
93
+ TestModule.expects(:save_error_count).with { |key, count| key.is_a?(String) && count == 1 }.times(4).returns(true)
93
94
  TestModule.group_error!(@exception, options1)
94
95
 
95
96
  options2 = {}
96
- TestModule.group_error!(NoMethodError.new("method not found"), options2)
97
+ TestModule.group_error!(NoMethodError.new('method not found'), options2)
97
98
 
98
99
  assert_equal 1, options1[:accumulated_errors_count]
99
100
  assert_equal 1, options2[:accumulated_errors_count]
100
101
  end
101
102
 
102
- test "should not group error is same exception but different message or backtrace" do
103
+ test 'should not group error is same exception but different message or backtrace' do
103
104
  options1 = {}
104
- TestModule.expects(:save_error_count).with{|key, count| key.is_a?(String) && count == 1}.times(4).returns(true)
105
+ TestModule.expects(:save_error_count).with { |key, count| key.is_a?(String) && count == 1 }.times(4).returns(true)
105
106
  TestModule.group_error!(@exception, options1)
106
107
 
107
108
  options2 = {}
@@ -111,7 +112,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
111
112
  assert_equal 1, options2[:accumulated_errors_count]
112
113
  end
113
114
 
114
- test "should group error if same exception and message" do
115
+ test 'should group error if same exception and message' do
115
116
  options = {}
116
117
 
117
118
  10.times do |i|
@@ -122,7 +123,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
122
123
  assert_equal 10, options[:accumulated_errors_count]
123
124
  end
124
125
 
125
- test "should group error if same exception and backtrace" do
126
+ test 'should group error if same exception and backtrace' do
126
127
  options = {}
127
128
 
128
129
  10.times do |i|
@@ -133,9 +134,9 @@ class ErrorGroupTest < ActiveSupport::TestCase
133
134
  assert_equal 10, options[:accumulated_errors_count]
134
135
  end
135
136
 
136
- test "should group error by that message have high priority" do
137
+ test 'should group error by that message have high priority' do
137
138
  message_based_key = "exception:#{Zlib.crc32("RuntimeError\nmessage:ERROR")}"
138
- backtrace_based_key = "exception:#{Zlib.crc32("RuntimeError\n/path/where/error/raised:1")}"
139
+ backtrace_based_key = "exception:#{Zlib.crc32("RuntimeError\npath:/path/where/error/raised:1")}"
139
140
 
140
141
  TestModule.save_error_count(message_based_key, 1)
141
142
  TestModule.save_error_count(backtrace_based_key, 1)
@@ -146,7 +147,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
146
147
  TestModule.group_error!(@exception, {})
147
148
  end
148
149
 
149
- test "use default formula if not specify notification_trigger in .send_notification?" do
150
+ test 'use default formula if not specify notification_trigger in .send_notification?' do
150
151
  TestModule.stubs(:notification_trigger).returns(nil)
151
152
 
152
153
  count = 16
@@ -155,12 +156,12 @@ class ErrorGroupTest < ActiveSupport::TestCase
155
156
  assert TestModule.send_notification?(@exception, count)
156
157
  end
157
158
 
158
- test "use specified trigger in .send_notification?" do
159
- trigger = Proc.new { |exception, count| count % 4 == 0 }
159
+ test 'use specified trigger in .send_notification?' do
160
+ trigger = proc { |_exception, count| (count % 4).zero? }
160
161
  TestModule.stubs(:notification_trigger).returns(trigger)
161
162
 
162
163
  count = 16
163
164
  trigger.expects(:call).with(@exception, count).returns(true)
164
165
  assert TestModule.send_notification?(@exception, count)
165
166
  end
166
- end
167
+ end