exception_notification 4.3.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
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