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,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'dogapi/common'
5
+ require 'dogapi/event'
6
+
7
+ class DatadogNotifierTest < ActiveSupport::TestCase
8
+ def setup
9
+ @client = FakeDatadogClient.new
10
+ @options = {
11
+ client: @client
12
+ }
13
+ @notifier = ExceptionNotifier::DatadogNotifier.new(@options)
14
+ @exception = FakeException.new
15
+ @controller = FakeController.new
16
+ @request = FakeRequest.new
17
+ end
18
+
19
+ test 'should send an event to datadog' do
20
+ fake_event = Dogapi::Event.any_instance
21
+ @client.expects(:emit_event).with(fake_event)
22
+
23
+ @notifier.stubs(:datadog_event).returns(fake_event)
24
+ @notifier.call(@exception)
25
+ end
26
+
27
+ test 'should include exception class in event title' do
28
+ event = @notifier.datadog_event(@exception)
29
+ assert_includes event.msg_title, 'FakeException'
30
+ end
31
+
32
+ test 'should include prefix in event title and not append previous events' do
33
+ options = {
34
+ client: @client,
35
+ title_prefix: 'prefix'
36
+ }
37
+
38
+ notifier = ExceptionNotifier::DatadogNotifier.new(options)
39
+ event = notifier.datadog_event(@exception)
40
+ assert_equal event.msg_title, 'prefix (DatadogNotifierTest::FakeException) "Fake exception message"'
41
+
42
+ event2 = notifier.datadog_event(@exception)
43
+ assert_equal event2.msg_title, 'prefix (DatadogNotifierTest::FakeException) "Fake exception message"'
44
+ end
45
+
46
+ test 'should include exception message in event title' do
47
+ event = @notifier.datadog_event(@exception)
48
+ assert_includes event.msg_title, 'Fake exception message'
49
+ end
50
+
51
+ test 'should include controller info in event title if controller information is available' do
52
+ event = @notifier.datadog_event(@exception,
53
+ env: {
54
+ 'action_controller.instance' => @controller,
55
+ 'REQUEST_METHOD' => 'GET',
56
+ 'rack.input' => ''
57
+ })
58
+ assert_includes event.msg_title, 'Fake controller'
59
+ assert_includes event.msg_title, 'Fake action'
60
+ end
61
+
62
+ test 'should include backtrace info in event body' do
63
+ event = @notifier.datadog_event(@exception)
64
+ assert_includes event.msg_text, "backtrace line 1\nbacktrace line 2\nbacktrace line 3"
65
+ end
66
+
67
+ test 'should include request info in event body' do
68
+ ActionDispatch::Request.stubs(:new).returns(@request)
69
+
70
+ event = @notifier.datadog_event(@exception,
71
+ env: {
72
+ 'action_controller.instance' => @controller,
73
+ 'REQUEST_METHOD' => 'GET',
74
+ 'rack.input' => ''
75
+ })
76
+ assert_includes event.msg_text, 'http://localhost:8080'
77
+ assert_includes event.msg_text, 'GET'
78
+ assert_includes event.msg_text, '127.0.0.1'
79
+ assert_includes event.msg_text, '{"param 1"=>"value 1", "param 2"=>"value 2"}'
80
+ end
81
+
82
+ test 'should include tags in event' do
83
+ options = {
84
+ client: @client,
85
+ tags: %w[error production]
86
+ }
87
+ notifier = ExceptionNotifier::DatadogNotifier.new(options)
88
+ event = notifier.datadog_event(@exception)
89
+ assert_equal event.tags, %w[error production]
90
+ end
91
+
92
+ test 'should include event title in event aggregation key' do
93
+ event = @notifier.datadog_event(@exception)
94
+ assert_equal event.aggregation_key, [event.msg_title]
95
+ end
96
+
97
+ class FakeDatadogClient
98
+ def emit_event(event); end
99
+ end
100
+
101
+ class FakeController
102
+ def controller_name
103
+ 'Fake controller'
104
+ end
105
+
106
+ def action_name
107
+ 'Fake action'
108
+ end
109
+ end
110
+
111
+ class FakeException
112
+ def backtrace
113
+ [
114
+ 'backtrace line 1',
115
+ 'backtrace line 2',
116
+ 'backtrace line 3',
117
+ 'backtrace line 4',
118
+ 'backtrace line 5'
119
+ ]
120
+ end
121
+
122
+ def message
123
+ 'Fake exception message'
124
+ end
125
+ end
126
+
127
+ class FakeRequest
128
+ def url
129
+ 'http://localhost:8080'
130
+ end
131
+
132
+ def request_method
133
+ 'GET'
134
+ end
135
+
136
+ def remote_ip
137
+ '127.0.0.1'
138
+ end
139
+
140
+ def filtered_parameters
141
+ {
142
+ 'param 1' => 'value 1',
143
+ 'param 2' => 'value 2'
144
+ }
145
+ end
146
+
147
+ def session
148
+ {
149
+ 'session_id' => '1234'
150
+ }
151
+ end
152
+ end
153
+ end
@@ -1,181 +1,150 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'action_mailer'
5
+ require 'action_controller'
3
6
 
4
7
  class EmailNotifierTest < ActiveSupport::TestCase
5
8
  setup do
6
9
  Time.stubs(:current).returns('Sat, 20 Apr 2013 20:58:55 UTC +00:00')
7
- @email_notifier = ExceptionNotifier.registered_exception_notifier(:email)
8
- begin
9
- 1/0
10
- rescue => e
11
- @exception = e
12
- @mail = @email_notifier.create_email(@exception,
13
- :data => {:job => 'DivideWorkerJob', :payload => '1/0', :message => 'My Custom Message'})
14
- end
15
- end
16
-
17
- test "should call pre/post_callback if specified" do
18
- assert_equal @email_notifier.options[:pre_callback_called], 1
19
- assert_equal @email_notifier.options[:post_callback_called], 1
20
- end
21
-
22
- test "should have default sender address overridden" do
23
- assert_equal @email_notifier.sender_address, %("Dummy Notifier" <dummynotifier@example.com>)
24
- end
25
-
26
- test "should have default exception recipients overridden" do
27
- assert_equal @email_notifier.exception_recipients, %w(dummyexceptions@example.com)
28
- end
29
-
30
- test "should have default email prefix overridden" do
31
- assert_equal @email_notifier.email_prefix, "[Dummy ERROR] "
32
- end
33
-
34
- test "should have default email headers overridden" do
35
- assert_equal @email_notifier.email_headers, { "X-Custom-Header" => "foobar"}
36
- end
37
-
38
- test "should have default sections overridden" do
39
- for section in %w(new_section request session environment backtrace)
40
- assert_includes @email_notifier.sections, section
41
- end
42
- end
43
10
 
44
- test "should have default background sections" do
45
- for section in %w(new_bkg_section backtrace data)
46
- assert_includes @email_notifier.background_sections, section
47
- end
48
- end
11
+ @exception = ZeroDivisionError.new('divided by 0')
12
+ @exception.set_backtrace(['test/exception_notifier/email_notifier_test.rb:20'])
13
+
14
+ @email_notifier = ExceptionNotifier::EmailNotifier.new(
15
+ email_prefix: '[Dummy ERROR] ',
16
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
17
+ exception_recipients: %w[dummyexceptions@example.com],
18
+ email_headers: { 'X-Custom-Header' => 'foobar' },
19
+ sections: %w[new_section request session environment backtrace],
20
+ background_sections: %w[new_bkg_section backtrace data],
21
+ pre_callback: proc { |_opts, _notifier, _backtrace, _message, _message_opts| @pre_callback_called = true },
22
+ post_callback: proc { |_opts, _notifier, _backtrace, _message, _message_opts| @post_callback_called = true },
23
+ smtp_settings: {
24
+ user_name: 'Dummy user_name',
25
+ password: 'Dummy password'
26
+ }
27
+ )
49
28
 
50
- test "should have email format by default" do
51
- assert_equal @email_notifier.email_format, :text
29
+ @mail = @email_notifier.call(
30
+ @exception,
31
+ data: { job: 'DivideWorkerJob', payload: '1/0', message: 'My Custom Message' }
32
+ )
52
33
  end
53
34
 
54
- test "should have verbose subject by default" do
55
- assert @email_notifier.verbose_subject
35
+ test 'should call pre/post_callback if specified' do
36
+ assert @pre_callback_called
37
+ assert @post_callback_called
56
38
  end
57
39
 
58
- test "should have normalize_subject false by default" do
59
- refute @email_notifier.normalize_subject
60
- end
40
+ test 'sends mail with correct content' do
41
+ assert_equal %("Dummy Notifier" <dummynotifier@example.com>), @mail[:from].value
42
+ assert_equal %w[dummyexceptions@example.com], @mail.to
43
+ assert_equal '[Dummy ERROR] (ZeroDivisionError) "divided by 0"', @mail.subject
44
+ assert_equal 'foobar', @mail['X-Custom-Header'].value
45
+ assert_equal 'text/plain; charset=UTF-8', @mail.content_type
46
+ assert_equal [], @mail.attachments
47
+ assert_equal 'Dummy user_name', @mail.delivery_method.settings[:user_name]
48
+ assert_equal 'Dummy password', @mail.delivery_method.settings[:password]
61
49
 
62
- test "should have delivery_method nil by default" do
63
- assert_nil @email_notifier.delivery_method
64
- end
50
+ body = <<-BODY.gsub(/^ /, '')
51
+ A ZeroDivisionError occurred in background at Sat, 20 Apr 2013 20:58:55 UTC +00:00 :
65
52
 
66
- test "should have mailer_settings nil by default" do
67
- assert_nil @email_notifier.mailer_settings
68
- end
53
+ divided by 0
54
+ test/exception_notifier/email_notifier_test.rb:20
69
55
 
70
- test "should have mailer_parent by default" do
71
- assert_equal @email_notifier.mailer_parent, 'ActionMailer::Base'
72
- end
56
+ -------------------------------
57
+ New bkg section:
58
+ -------------------------------
73
59
 
74
- test "should have template_path by default" do
75
- assert_equal @email_notifier.template_path, 'exception_notifier'
76
- end
60
+ * New background section for testing
77
61
 
78
- test "should normalize multiple digits into one N" do
79
- assert_equal 'N foo N bar N baz N',
80
- ExceptionNotifier::EmailNotifier.normalize_digits('1 foo 12 bar 123 baz 1234')
81
- end
62
+ -------------------------------
63
+ Backtrace:
64
+ -------------------------------
82
65
 
83
- test "mail should be plain text and UTF-8 enconded by default" do
84
- assert_equal @mail.content_type, "text/plain; charset=UTF-8"
85
- end
66
+ test/exception_notifier/email_notifier_test.rb:20
86
67
 
87
- test "should have raised an exception" do
88
- refute_nil @exception
89
- end
68
+ -------------------------------
69
+ Data:
70
+ -------------------------------
90
71
 
91
- test "should have generated a notification email" do
92
- refute_nil @mail
93
- end
72
+ * data: {:job=>"DivideWorkerJob", :payload=>"1/0", :message=>"My Custom Message"}
94
73
 
95
- test "mail should have a from address set" do
96
- assert_equal @mail.from, ["dummynotifier@example.com"]
97
- end
98
74
 
99
- test "mail should have a to address set" do
100
- assert_equal @mail.to, ["dummyexceptions@example.com"]
101
- end
75
+ BODY
102
76
 
103
- test "mail should have a descriptive subject" do
104
- assert_match(/^\[Dummy ERROR\]\s+\(ZeroDivisionError\) "divided by 0"$/, @mail.subject)
77
+ assert_equal body, @mail.decode_body
105
78
  end
106
79
 
107
- test "mail should say exception was raised in background at show timestamp" do
108
- assert_includes @mail.encoded, "A ZeroDivisionError occurred in background at #{Time.current}"
80
+ test 'should normalize multiple digits into one N' do
81
+ assert_equal 'N foo N bar N baz N',
82
+ ExceptionNotifier::EmailNotifier.normalize_digits('1 foo 12 bar 123 baz 1234')
109
83
  end
110
84
 
111
85
  test "mail should prefix exception class with 'an' instead of 'a' when it starts with a vowel" do
112
86
  begin
113
- raise ActiveRecord::RecordNotFound
114
- rescue => e
87
+ raise ArgumentError
88
+ rescue StandardError => e
115
89
  @vowel_exception = e
116
- @vowel_mail = @email_notifier.create_email(@vowel_exception)
90
+ @vowel_mail = @email_notifier.call(@vowel_exception)
117
91
  end
118
92
 
119
- assert_includes @vowel_mail.encoded, "An ActiveRecord::RecordNotFound occurred in background at #{Time.current}"
120
- end
121
-
122
- test "mail should contain backtrace in body" do
123
- assert @mail.encoded.include?("test/exception_notifier/email_notifier_test.rb:9"), "\n#{@mail.inspect}"
124
- end
125
-
126
- test "mail should contain data in body" do
127
- assert_includes @mail.encoded, '* data:'
128
- assert_includes @mail.encoded, ':payload=>"1/0"'
129
- assert_includes @mail.encoded, ':job=>"DivideWorkerJob"'
130
- assert_includes @mail.encoded, "My Custom Message"
93
+ assert_includes @vowel_mail.encoded, "An ArgumentError occurred in background at #{Time.current}"
131
94
  end
132
95
 
133
- test "mail should not contain any attachments" do
134
- assert_equal @mail.attachments, []
135
- end
136
-
137
- test "should not send notification if one of ignored exceptions" do
96
+ test 'should not send notification if one of ignored exceptions' do
138
97
  begin
139
- raise ActiveRecord::RecordNotFound
140
- rescue => e
98
+ raise AbstractController::ActionNotFound
99
+ rescue StandardError => e
141
100
  @ignored_exception = e
142
101
  unless ExceptionNotifier.ignored_exceptions.include?(@ignored_exception.class.name)
143
- ignored_mail = @email_notifier.create_email(@ignored_exception)
102
+ ignored_mail = @email_notifier.call(@ignored_exception)
144
103
  end
145
104
  end
146
105
 
147
- assert_equal @ignored_exception.class.inspect, "ActiveRecord::RecordNotFound"
106
+ assert_equal @ignored_exception.class.inspect, 'AbstractController::ActionNotFound'
148
107
  assert_nil ignored_mail
149
108
  end
150
109
 
151
- test "should encode environment strings" do
110
+ test 'should encode environment strings' do
152
111
  email_notifier = ExceptionNotifier::EmailNotifier.new(
153
- :sender_address => "<dummynotifier@example.com>",
154
- :exception_recipients => %w{dummyexceptions@example.com},
155
- :deliver_with => :deliver_now
112
+ sender_address: '<dummynotifier@example.com>',
113
+ exception_recipients: %w[dummyexceptions@example.com]
156
114
  )
157
115
 
158
- mail = email_notifier.create_email(
116
+ mail = email_notifier.call(
159
117
  @exception,
160
- :env => {
161
- "REQUEST_METHOD" => "GET",
162
- "rack.input" => "",
163
- "invalid_encoding" => "R\xC3\xA9sum\xC3\xA9".force_encoding(Encoding::ASCII),
164
- },
165
- :email_format => :text
118
+ env: {
119
+ 'REQUEST_METHOD' => 'GET',
120
+ 'rack.input' => '',
121
+ 'invalid_encoding' => "R\xC3\xA9sum\xC3\xA9".dup.force_encoding(Encoding::ASCII)
122
+ }
166
123
  )
167
124
 
168
125
  assert_match(/invalid_encoding\s+: R__sum__/, mail.encoded)
169
126
  end
170
127
 
171
- test "should send email using ActionMailer" do
128
+ test 'should send email using ActionMailer' do
172
129
  ActionMailer::Base.deliveries.clear
130
+ @email_notifier.call(@exception)
131
+ assert_equal 1, ActionMailer::Base.deliveries.count
132
+ end
133
+
134
+ test 'should be able to specify ActionMailer::MessageDelivery method' do
135
+ ActionMailer::Base.deliveries.clear
136
+
137
+ deliver_with = if ActionMailer.version < Gem::Version.new('4.2')
138
+ :deliver
139
+ else
140
+ :deliver_now
141
+ end
173
142
 
174
143
  email_notifier = ExceptionNotifier::EmailNotifier.new(
175
- :email_prefix => '[Dummy ERROR] ',
176
- :sender_address => %{"Dummy Notifier" <dummynotifier@example.com>},
177
- :exception_recipients => %w{dummyexceptions@example.com},
178
- :delivery_method => :test
144
+ email_prefix: '[Dummy ERROR] ',
145
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
146
+ exception_recipients: %w[dummyexceptions@example.com],
147
+ deliver_with: deliver_with
179
148
  )
180
149
 
181
150
  email_notifier.call(@exception)
@@ -183,39 +152,204 @@ class EmailNotifierTest < ActiveSupport::TestCase
183
152
  assert_equal 1, ActionMailer::Base.deliveries.count
184
153
  end
185
154
 
186
- test "should be able to specify ActionMailer::MessageDelivery method" do
187
- ActionMailer::Base.deliveries.clear
155
+ test 'should lazily evaluate exception_recipients' do
156
+ exception_recipients = %w[first@example.com second@example.com]
157
+ email_notifier = ExceptionNotifier::EmailNotifier.new(
158
+ email_prefix: '[Dummy ERROR] ',
159
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
160
+ exception_recipients: -> { [exception_recipients.shift] },
161
+ delivery_method: :test
162
+ )
188
163
 
189
- if ActionMailer.version < Gem::Version.new("4.2")
190
- deliver_with = :deliver
191
- else
192
- deliver_with = :deliver_now
193
- end
164
+ mail = email_notifier.call(@exception)
165
+ assert_equal %w[first@example.com], mail.to
166
+ mail = email_notifier.call(@exception)
167
+ assert_equal %w[second@example.com], mail.to
168
+ end
194
169
 
170
+ test 'should prepend accumulated_errors_count in email subject if accumulated_errors_count larger than 1' do
195
171
  email_notifier = ExceptionNotifier::EmailNotifier.new(
196
- :email_prefix => '[Dummy ERROR] ',
197
- :sender_address => %{"Dummy Notifier" <dummynotifier@example.com>},
198
- :exception_recipients => %w{dummyexceptions@example.com},
199
- :deliver_with => deliver_with
172
+ email_prefix: '[Dummy ERROR] ',
173
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
174
+ exception_recipients: %w[dummyexceptions@example.com],
175
+ delivery_method: :test
200
176
  )
201
177
 
202
- email_notifier.call(@exception)
203
-
204
- assert_equal 1, ActionMailer::Base.deliveries.count
178
+ mail = email_notifier.call(@exception, accumulated_errors_count: 3)
179
+ assert mail.subject.start_with?('[Dummy ERROR] (3 times) (ZeroDivisionError)')
205
180
  end
206
181
 
207
- test "should lazily evaluate exception_recipients" do
208
- exception_recipients = %w{first@example.com second@example.com}
182
+ test 'should not include exception message in subject when verbose_subject: false' do
209
183
  email_notifier = ExceptionNotifier::EmailNotifier.new(
210
- :email_prefix => '[Dummy ERROR] ',
211
- :sender_address => %{"Dummy Notifier" <dummynotifier@example.com>},
212
- :exception_recipients => -> { [ exception_recipients.shift ] },
213
- :delivery_method => :test
184
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
185
+ exception_recipients: %w[dummyexceptions@example.com],
186
+ verbose_subject: false
214
187
  )
215
188
 
216
189
  mail = email_notifier.call(@exception)
217
- assert_equal %w{first@example.com}, mail.to
190
+
191
+ assert_equal '[ERROR] (ZeroDivisionError)', mail.subject
192
+ end
193
+
194
+ test 'should send html email when selected html format' do
195
+ email_notifier = ExceptionNotifier::EmailNotifier.new(
196
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
197
+ exception_recipients: %w[dummyexceptions@example.com],
198
+ email_format: :html
199
+ )
200
+
218
201
  mail = email_notifier.call(@exception)
219
- assert_equal %w{second@example.com}, mail.to
202
+
203
+ assert mail.multipart?
204
+ end
205
+ end
206
+
207
+ class EmailNotifierWithEnvTest < ActiveSupport::TestCase
208
+ class HomeController < ActionController::Metal
209
+ def index; end
210
+ end
211
+
212
+ setup do
213
+ Time.stubs(:current).returns('Sat, 20 Apr 2013 20:58:55 UTC +00:00')
214
+
215
+ @exception = ZeroDivisionError.new('divided by 0')
216
+ @exception.set_backtrace(['test/exception_notifier/email_notifier_test.rb:20'])
217
+
218
+ @email_notifier = ExceptionNotifier::EmailNotifier.new(
219
+ email_prefix: '[Dummy ERROR] ',
220
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
221
+ exception_recipients: %w[dummyexceptions@example.com],
222
+ email_headers: { 'X-Custom-Header' => 'foobar' },
223
+ sections: %w[new_section request session environment backtrace],
224
+ background_sections: %w[new_bkg_section backtrace data],
225
+ pre_callback:
226
+ proc { |_opts, _notifier, _backtrace, _message, message_opts| message_opts[:pre_callback_called] = 1 },
227
+ post_callback:
228
+ proc { |_opts, _notifier, _backtrace, _message, message_opts| message_opts[:post_callback_called] = 1 }
229
+ )
230
+
231
+ @controller = HomeController.new
232
+ @controller.process(:index)
233
+
234
+ @test_env = Rack::MockRequest.env_for(
235
+ '/',
236
+ 'HTTP_HOST' => 'test.address',
237
+ 'REMOTE_ADDR' => '127.0.0.1',
238
+ 'HTTP_USER_AGENT' => 'Rails Testing',
239
+ 'action_dispatch.parameter_filter' => ['secret'],
240
+ 'HTTPS' => 'on',
241
+ 'action_controller.instance' => @controller,
242
+ params: { id: 'foo', secret: 'secret' }
243
+ )
244
+
245
+ @mail = @email_notifier.call(@exception, env: @test_env, data: { message: 'My Custom Message' })
246
+ end
247
+
248
+ test 'sends mail with correct content' do
249
+ assert_equal %("Dummy Notifier" <dummynotifier@example.com>), @mail[:from].value
250
+ assert_equal %w[dummyexceptions@example.com], @mail.to
251
+ assert_equal '[Dummy ERROR] home index (ZeroDivisionError) "divided by 0"', @mail.subject
252
+ assert_equal 'foobar', @mail['X-Custom-Header'].value
253
+ assert_equal 'text/plain; charset=UTF-8', @mail.content_type
254
+ assert_equal [], @mail.attachments
255
+
256
+ body = <<-BODY.gsub(/^ /, '')
257
+ A ZeroDivisionError occurred in home#index:
258
+
259
+ divided by 0
260
+ test/exception_notifier/email_notifier_test.rb:20
261
+
262
+
263
+ -------------------------------
264
+ New section:
265
+ -------------------------------
266
+
267
+ * New text section for testing
268
+
269
+ -------------------------------
270
+ Request:
271
+ -------------------------------
272
+
273
+ * URL : https://test.address/?id=foo&secret=secret
274
+ * HTTP Method: GET
275
+ * IP address : 127.0.0.1
276
+ * Parameters : {\"id\"=>\"foo\", \"secret\"=>\"[FILTERED]\"}
277
+ * Timestamp : Sat, 20 Apr 2013 20:58:55 UTC +00:00
278
+ * Server : #{Socket.gethostname}
279
+ BODY
280
+
281
+ body << " * Rails root : #{Rails.root}\n" if defined?(Rails) && Rails.respond_to?(:root)
282
+
283
+ body << <<-BODY.gsub(/^ /, '')
284
+ * Process: #{Process.pid}
285
+
286
+ -------------------------------
287
+ Session:
288
+ -------------------------------
289
+
290
+ * session id: [FILTERED]
291
+ * data: {}
292
+
293
+ -------------------------------
294
+ Environment:
295
+ -------------------------------
296
+
297
+ * CONTENT_LENGTH : 0
298
+ * HTTPS : on
299
+ * HTTP_HOST : test.address
300
+ * HTTP_USER_AGENT : Rails Testing
301
+ * PATH_INFO : /
302
+ * QUERY_STRING : id=foo&secret=secret
303
+ * REMOTE_ADDR : 127.0.0.1
304
+ * REQUEST_METHOD : GET
305
+ * SCRIPT_NAME :
306
+ * SERVER_NAME : example.org
307
+ * SERVER_PORT : 80
308
+ * action_controller.instance : #{@controller}
309
+ * action_dispatch.parameter_filter : [\"secret\"]
310
+ * action_dispatch.request.content_type :
311
+ * action_dispatch.request.parameters : {"id"=>"foo", "secret"=>"[FILTERED]"}
312
+ * action_dispatch.request.path_parameters : {}
313
+ * action_dispatch.request.query_parameters : {"id"=>"foo", "secret"=>"[FILTERED]"}
314
+ * action_dispatch.request.request_parameters: {}
315
+ * rack.errors : #{@test_env['rack.errors']}
316
+ * rack.input : #{@test_env['rack.input']}
317
+ * rack.multiprocess : true
318
+ * rack.multithread : true
319
+ * rack.request.query_hash : {"id"=>"foo", "secret"=>"[FILTERED]"}
320
+ * rack.request.query_string : id=foo&secret=secret
321
+ * rack.run_once : false
322
+ * rack.session : {}
323
+ * rack.url_scheme : http
324
+ * rack.version : #{Rack::VERSION}
325
+
326
+ -------------------------------
327
+ Backtrace:
328
+ -------------------------------
329
+
330
+ test/exception_notifier/email_notifier_test.rb:20
331
+
332
+ -------------------------------
333
+ Data:
334
+ -------------------------------
335
+
336
+ * data: {:message=>\"My Custom Message\"}
337
+
338
+
339
+ BODY
340
+
341
+ assert_equal body, @mail.decode_body
342
+ end
343
+
344
+ test 'should not include controller and action names in subject' do
345
+ email_notifier = ExceptionNotifier::EmailNotifier.new(
346
+ sender_address: %("Dummy Notifier" <dummynotifier@example.com>),
347
+ exception_recipients: %w[dummyexceptions@example.com],
348
+ include_controller_and_action_names_in_subject: false
349
+ )
350
+
351
+ mail = email_notifier.call(@exception, env: @test_env)
352
+
353
+ assert_equal '[ERROR] (ZeroDivisionError) "divided by 0"', mail.subject
220
354
  end
221
355
  end