exception_notification 4.3.0 → 4.4.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 +4 -4
  2. data/Appraisals +2 -2
  3. data/CHANGELOG.rdoc +14 -0
  4. data/CONTRIBUTING.md +18 -0
  5. data/Gemfile +1 -1
  6. data/README.md +64 -935
  7. data/Rakefile +2 -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 +54 -0
  21. data/examples/sinatra/Gemfile +6 -6
  22. data/examples/sinatra/config.ru +1 -1
  23. data/examples/sinatra/sinatra_app.rb +14 -10
  24. data/exception_notification.gemspec +27 -22
  25. data/gemfiles/rails4_0.gemfile +3 -3
  26. data/gemfiles/rails4_1.gemfile +3 -3
  27. data/gemfiles/rails4_2.gemfile +3 -3
  28. data/gemfiles/rails5_0.gemfile +3 -3
  29. data/gemfiles/rails5_1.gemfile +3 -3
  30. data/gemfiles/rails5_2.gemfile +7 -0
  31. data/gemfiles/rails6_0.gemfile +7 -0
  32. data/lib/exception_notification.rb +1 -0
  33. data/lib/exception_notification/rack.rb +8 -21
  34. data/lib/exception_notification/resque.rb +8 -10
  35. data/lib/exception_notification/sidekiq.rb +8 -12
  36. data/lib/exception_notification/version.rb +3 -0
  37. data/lib/exception_notifier.rb +20 -3
  38. data/lib/exception_notifier/base_notifier.rb +2 -3
  39. data/lib/exception_notifier/campfire_notifier.rb +12 -13
  40. data/lib/exception_notifier/datadog_notifier.rb +153 -0
  41. data/lib/exception_notifier/email_notifier.rb +64 -87
  42. data/lib/exception_notifier/google_chat_notifier.rb +25 -119
  43. data/lib/exception_notifier/hipchat_notifier.rb +11 -12
  44. data/lib/exception_notifier/irc_notifier.rb +32 -30
  45. data/lib/exception_notifier/mattermost_notifier.rb +47 -140
  46. data/lib/exception_notifier/modules/backtrace_cleaner.rb +0 -2
  47. data/lib/exception_notifier/modules/error_grouping.rb +5 -5
  48. data/lib/exception_notifier/modules/formatter.rb +118 -0
  49. data/lib/exception_notifier/notifier.rb +5 -6
  50. data/lib/exception_notifier/slack_notifier.rb +63 -40
  51. data/lib/exception_notifier/sns_notifier.rb +17 -11
  52. data/lib/exception_notifier/teams_notifier.rb +58 -44
  53. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
  54. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
  55. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
  56. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -2
  57. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
  58. data/lib/exception_notifier/webhook_notifier.rb +14 -11
  59. data/lib/generators/exception_notification/install_generator.rb +5 -5
  60. data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
  61. data/test/exception_notification/rack_test.rb +27 -11
  62. data/test/exception_notification/resque_test.rb +52 -0
  63. data/test/exception_notifier/campfire_notifier_test.rb +42 -42
  64. data/test/exception_notifier/datadog_notifier_test.rb +151 -0
  65. data/test/exception_notifier/email_notifier_test.rb +269 -153
  66. data/test/exception_notifier/google_chat_notifier_test.rb +154 -101
  67. data/test/exception_notifier/hipchat_notifier_test.rb +78 -81
  68. data/test/exception_notifier/irc_notifier_test.rb +34 -34
  69. data/test/exception_notifier/mattermost_notifier_test.rb +164 -67
  70. data/test/exception_notifier/modules/error_grouping_test.rb +39 -40
  71. data/test/exception_notifier/modules/formatter_test.rb +150 -0
  72. data/test/exception_notifier/sidekiq_test.rb +6 -6
  73. data/test/exception_notifier/slack_notifier_test.rb +61 -60
  74. data/test/exception_notifier/sns_notifier_test.rb +27 -32
  75. data/test/exception_notifier/teams_notifier_test.rb +23 -26
  76. data/test/exception_notifier/webhook_notifier_test.rb +48 -46
  77. data/test/exception_notifier_test.rb +41 -38
  78. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
  79. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
  80. data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
  81. data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
  82. data/test/test_helper.rb +11 -14
  83. metadata +136 -166
  84. data/test/dummy/.gitignore +0 -4
  85. data/test/dummy/Rakefile +0 -7
  86. data/test/dummy/app/controllers/application_controller.rb +0 -3
  87. data/test/dummy/app/controllers/posts_controller.rb +0 -30
  88. data/test/dummy/app/helpers/application_helper.rb +0 -2
  89. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  90. data/test/dummy/app/models/post.rb +0 -2
  91. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  92. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  93. data/test/dummy/app/views/posts/new.html.erb +0 -0
  94. data/test/dummy/app/views/posts/show.html.erb +0 -0
  95. data/test/dummy/config.ru +0 -4
  96. data/test/dummy/config/application.rb +0 -42
  97. data/test/dummy/config/boot.rb +0 -6
  98. data/test/dummy/config/database.yml +0 -22
  99. data/test/dummy/config/environment.rb +0 -17
  100. data/test/dummy/config/environments/development.rb +0 -25
  101. data/test/dummy/config/environments/production.rb +0 -50
  102. data/test/dummy/config/environments/test.rb +0 -35
  103. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  104. data/test/dummy/config/initializers/inflections.rb +0 -10
  105. data/test/dummy/config/initializers/mime_types.rb +0 -5
  106. data/test/dummy/config/initializers/secret_token.rb +0 -8
  107. data/test/dummy/config/initializers/session_store.rb +0 -8
  108. data/test/dummy/config/locales/en.yml +0 -5
  109. data/test/dummy/config/routes.rb +0 -3
  110. data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
  111. data/test/dummy/db/schema.rb +0 -24
  112. data/test/dummy/db/seeds.rb +0 -7
  113. data/test/dummy/lib/tasks/.gitkeep +0 -0
  114. data/test/dummy/public/404.html +0 -26
  115. data/test/dummy/public/422.html +0 -26
  116. data/test/dummy/public/500.html +0 -26
  117. data/test/dummy/public/favicon.ico +0 -0
  118. data/test/dummy/public/images/rails.png +0 -0
  119. data/test/dummy/public/index.html +0 -239
  120. data/test/dummy/public/javascripts/application.js +0 -2
  121. data/test/dummy/public/javascripts/controls.js +0 -965
  122. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  123. data/test/dummy/public/javascripts/effects.js +0 -1123
  124. data/test/dummy/public/javascripts/prototype.js +0 -6001
  125. data/test/dummy/public/javascripts/rails.js +0 -191
  126. data/test/dummy/public/robots.txt +0 -5
  127. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  128. data/test/dummy/public/stylesheets/scaffold.css +0 -56
  129. data/test/dummy/script/rails +0 -6
  130. data/test/dummy/test/functional/posts_controller_test.rb +0 -237
  131. data/test/dummy/test/test_helper.rb +0 -7
@@ -1,3 +1,3 @@
1
1
  <pre style="font-size: 12px; padding: 10px; border: 1px solid #e1e1e8; background-color:#f5f5f5">
2
- <%= @backtrace.join("\n") %>
2
+ <%= @backtrace.join("\n") %>
3
3
  </pre>
@@ -1,5 +1,5 @@
1
1
  <% filtered_env = @request.filtered_env -%>
2
2
  <% max = filtered_env.keys.map(&:to_s).max { |a, b| a.length <=> b.length } -%>
3
3
  <% filtered_env.keys.map(&:to_s).sort.each do |key| -%>
4
- * <%= raw safe_encode("%-*s: %s" % [max.length, key, inspect_object(filtered_env[key])]) %>
4
+ * <%= raw safe_encode("%-*s: %s" % [max.length, key, inspect_object(filtered_env[key])]).strip %>
5
5
  <% end -%>
@@ -5,6 +5,6 @@
5
5
  * Timestamp : <%= raw @timestamp %>
6
6
  * Server : <%= raw Socket.gethostname %>
7
7
  <% if defined?(Rails) && Rails.respond_to?(:root) %>
8
- * Rails root : <%= raw Rails.root %>
8
+ * Rails root : <%= raw Rails.root %>
9
9
  <% end %>
10
10
  * Process: <%= raw $$ %>
@@ -11,11 +11,11 @@
11
11
  begin
12
12
  summary = render(section).strip
13
13
  unless summary.blank?
14
- title = render("title", :title => section).strip
14
+ title = render("title", title: section).strip
15
15
  [title, summary]
16
16
  end
17
17
  rescue Exception => e
18
- title = render("title", :title => section).strip
18
+ title = render("title", title: section).strip
19
19
  summary = ["ERROR: Failed to generate exception summary:", [e.class.to_s, e.message].join(": "), e.backtrace && e.backtrace.join("\n")].compact.join("\n\n")
20
20
  [title, summary]
21
21
  end
@@ -8,12 +8,12 @@
8
8
  begin
9
9
  summary = render(section).strip
10
10
  unless summary.blank?
11
- title = render("title", :title => section).strip
11
+ title = render("title", title: section).strip
12
12
  "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
13
13
  end
14
14
 
15
15
  rescue Exception => e
16
- title = render("title", :title => section).strip
16
+ title = render("title", title: section).strip
17
17
  summary = ["ERROR: Failed to generate exception summary:", [e.class.to_s, e.message].join(": "), e.backtrace && e.backtrace.join("\n")].compact.join("\n\n")
18
18
 
19
19
  [title, summary.gsub(/^/, " "), nil].join("\n\n")
@@ -3,13 +3,12 @@ require 'active_support/core_ext/time'
3
3
 
4
4
  module ExceptionNotifier
5
5
  class WebhookNotifier < BaseNotifier
6
-
7
6
  def initialize(options)
8
7
  super
9
8
  @default_options = options
10
9
  end
11
10
 
12
- def call(exception, options={})
11
+ def call(exception, options = {})
13
12
  env = options[:env]
14
13
 
15
14
  options = options.reverse_merge(@default_options)
@@ -18,23 +17,27 @@ module ExceptionNotifier
18
17
 
19
18
  options[:body] ||= {}
20
19
  options[:body][:server] = Socket.gethostname
21
- options[:body][:process] = $$
20
+ options[:body][:process] = $PROCESS_ID
22
21
  if defined?(Rails) && Rails.respond_to?(:root)
23
22
  options[:body][:rails_root] = Rails.root
24
23
  end
25
- options[:body][:exception] = {:error_class => exception.class.to_s,
26
- :message => exception.message.inspect,
27
- :backtrace => exception.backtrace}
24
+ options[:body][:exception] = {
25
+ error_class: exception.class.to_s,
26
+ message: exception.message.inspect,
27
+ backtrace: exception.backtrace
28
+ }
28
29
  options[:body][:data] = (env && env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
29
30
 
30
31
  unless env.nil?
31
32
  request = ActionDispatch::Request.new(env)
32
33
 
33
- request_items = {:url => request.original_url,
34
- :http_method => request.method,
35
- :ip_address => request.remote_ip,
36
- :parameters => request.filtered_parameters,
37
- :timestamp => Time.current }
34
+ request_items = {
35
+ url: request.original_url,
36
+ http_method: request.method,
37
+ ip_address: request.remote_ip,
38
+ parameters: request.filtered_parameters,
39
+ timestamp: Time.current
40
+ }
38
41
 
39
42
  options[:body][:request] = request_items
40
43
  options[:body][:session] = request.session
@@ -1,14 +1,14 @@
1
1
  module ExceptionNotification
2
2
  module Generators
3
3
  class InstallGenerator < Rails::Generators::Base
4
- desc "Creates a ExceptionNotification initializer."
4
+ desc 'Creates a ExceptionNotification initializer.'
5
5
 
6
- source_root File.expand_path('../templates', __FILE__)
7
- class_option :resque, :type => :boolean, :desc => 'Add support for sending notifications when errors occur in Resque jobs.'
8
- class_option :sidekiq, :type => :boolean, :desc => 'Add support for sending notifications when errors occur in Sidekiq jobs.'
6
+ source_root File.expand_path('templates', __dir__)
7
+ class_option :resque, type: :boolean, desc: 'Add support for sending notifications when errors occur in Resque jobs.'
8
+ class_option :sidekiq, type: :boolean, desc: 'Add support for sending notifications when errors occur in Sidekiq jobs.'
9
9
 
10
10
  def copy_initializer
11
- template 'exception_notification.rb', 'config/initializers/exception_notification.rb'
11
+ template 'exception_notification.rb.erb', 'config/initializers/exception_notification.rb'
12
12
  end
13
13
  end
14
14
  end
@@ -22,32 +22,34 @@ ExceptionNotification.configure do |config|
22
22
  # not Rails.env.production?
23
23
  # end
24
24
 
25
+ # Ignore exceptions generated by crawlers
26
+ # config.ignore_crawlers %w{Googlebot bingbot}
27
+
25
28
  # Notifiers =================================================================
26
29
 
27
30
  # Email notifier sends notifications by email.
28
31
  config.add_notifier :email, {
29
- :email_prefix => "[ERROR] ",
30
- :sender_address => %{"Notifier" <notifier@example.com>},
31
- :exception_recipients => %w{exceptions@example.com}
32
+ email_prefix: '[ERROR] ',
33
+ sender_address: %{"Notifier" <notifier@example.com>},
34
+ exception_recipients: %w{exceptions@example.com}
32
35
  }
33
36
 
34
37
  # Campfire notifier sends notifications to your Campfire room. Requires 'tinder' gem.
35
38
  # config.add_notifier :campfire, {
36
- # :subdomain => 'my_subdomain',
37
- # :token => 'my_token',
38
- # :room_name => 'my_room'
39
+ # subdomain: 'my_subdomain',
40
+ # token: 'my_token',
41
+ # room_name: 'my_room'
39
42
  # }
40
43
 
41
44
  # HipChat notifier sends notifications to your HipChat room. Requires 'hipchat' gem.
42
45
  # config.add_notifier :hipchat, {
43
- # :api_token => 'my_token',
44
- # :room_name => 'my_room'
46
+ # api_token: 'my_token',
47
+ # room_name: 'my_room'
45
48
  # }
46
49
 
47
50
  # Webhook notifier sends notifications over HTTP protocol. Requires 'httparty' gem.
48
51
  # config.add_notifier :webhook, {
49
- # :url => 'http://example.com:5555/hubot/path',
50
- # :http_method => :post
52
+ # url: 'http://example.com:5555/hubot/path',
53
+ # http_method: :post
51
54
  # }
52
-
53
55
  end
@@ -1,13 +1,12 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class RackTest < ActiveSupport::TestCase
4
-
5
4
  setup do
6
5
  @pass_app = Object.new
7
6
  @pass_app.stubs(:call).returns([nil, { 'X-Cascade' => 'pass' }, nil])
8
7
 
9
8
  @normal_app = Object.new
10
- @normal_app.stubs(:call).returns([nil, { }, nil])
9
+ @normal_app.stubs(:call).returns([nil, {}, nil])
11
10
  end
12
11
 
13
12
  teardown do
@@ -15,30 +14,47 @@ class RackTest < ActiveSupport::TestCase
15
14
  ExceptionNotifier.notification_trigger = nil
16
15
  end
17
16
 
18
- test "should ignore \"X-Cascade\" header by default" do
17
+ test 'should ignore "X-Cascade" header by default' do
19
18
  ExceptionNotifier.expects(:notify_exception).never
20
19
  ExceptionNotification::Rack.new(@pass_app).call({})
21
20
  end
22
21
 
23
- test "should notify on \"X-Cascade\" = \"pass\" if ignore_cascade_pass option is false" do
22
+ test 'should notify on "X-Cascade" = "pass" if ignore_cascade_pass option is false' do
24
23
  ExceptionNotifier.expects(:notify_exception).once
25
- ExceptionNotification::Rack.new(@pass_app, :ignore_cascade_pass => false).call({})
24
+ ExceptionNotification::Rack.new(@pass_app, ignore_cascade_pass: false).call({})
26
25
  end
27
26
 
28
- test "should assign error_grouping if error_grouping is specified" do
27
+ test 'should assign error_grouping if error_grouping is specified' do
29
28
  refute ExceptionNotifier.error_grouping
30
29
  ExceptionNotification::Rack.new(@normal_app, error_grouping: true).call({})
31
30
  assert ExceptionNotifier.error_grouping
32
31
  end
33
32
 
34
- test "should assign notification_trigger if notification_trigger is specified" do
33
+ test 'should assign notification_trigger if notification_trigger is specified' do
35
34
  assert_nil ExceptionNotifier.notification_trigger
36
- ExceptionNotification::Rack.new(@normal_app, notification_trigger: lambda {|i| true}).call({})
35
+ ExceptionNotification::Rack.new(@normal_app, notification_trigger: ->(_i) { true }).call({})
37
36
  assert_respond_to ExceptionNotifier.notification_trigger, :call
38
37
  end
39
38
 
40
- test "should set default cache to Rails cache" do
41
- ExceptionNotification::Rack.new(@normal_app, error_grouping: true).call({})
42
- assert_equal Rails.cache, ExceptionNotifier.error_grouping_cache
39
+ if defined?(Rails) && Rails.respond_to?(:cache)
40
+ test 'should set default cache to Rails cache' do
41
+ ExceptionNotification::Rack.new(@normal_app, error_grouping: true).call({})
42
+ assert_equal Rails.cache, ExceptionNotifier.error_grouping_cache
43
+ end
44
+ end
45
+
46
+ test 'should ignore exceptions with Usar Agent in ignore_crawlers' do
47
+ exception_app = Object.new
48
+ exception_app.stubs(:call).raises(RuntimeError)
49
+
50
+ env = { 'HTTP_USER_AGENT' => 'Mozilla/5.0 (compatible; Crawlerbot/2.1;)' }
51
+
52
+ begin
53
+ ExceptionNotification::Rack.new(exception_app, ignore_crawlers: %w[Crawlerbot]).call(env)
54
+
55
+ flunk
56
+ rescue StandardError
57
+ refute env['exception_notifier.delivered']
58
+ end
43
59
  end
44
60
  end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ require 'exception_notification/resque'
4
+ require 'resque'
5
+ require 'mock_redis'
6
+ require 'resque/failure/multiple'
7
+ require 'resque/failure/redis'
8
+
9
+ class ResqueTest < ActiveSupport::TestCase
10
+ setup do
11
+ # Resque.redis=() only supports a String or Redis instance in Resque 1.8
12
+ Resque.instance_variable_set(:@redis, MockRedis.new)
13
+
14
+ Resque::Failure::Multiple.classes = [Resque::Failure::Redis, ExceptionNotification::Resque]
15
+ Resque::Failure.backend = Resque::Failure::Multiple
16
+
17
+ @worker = Resque::Worker.new(:jobs)
18
+ # Forking causes issues with Mocha's `.expects`
19
+ @worker.cant_fork = true
20
+ end
21
+
22
+ test 'count returns the number of failures' do
23
+ Resque::Job.create(:jobs, BadJob)
24
+ @worker.work(0)
25
+ assert_equal 1, ExceptionNotification::Resque.count
26
+ end
27
+
28
+ test 'notifies exception when job fails' do
29
+ ExceptionNotifier.expects(:notify_exception).with do |ex, opts|
30
+ ex.is_a?(RuntimeError) &&
31
+ ex.message == 'Bad job!' &&
32
+ opts[:data][:resque][:error_class] == 'RuntimeError' &&
33
+ opts[:data][:resque][:error_message] == 'Bad job!' &&
34
+ opts[:data][:resque][:failed_at].present? &&
35
+ opts[:data][:resque][:payload] == {
36
+ 'class' => 'ResqueTest::BadJob',
37
+ 'args' => []
38
+ } &&
39
+ opts[:data][:resque][:queue] == :jobs &&
40
+ opts[:data][:resque][:worker].present?
41
+ end
42
+
43
+ Resque::Job.create(:jobs, BadJob)
44
+ @worker.work(0)
45
+ end
46
+
47
+ class BadJob
48
+ def self.perform
49
+ raise 'Bad job!'
50
+ end
51
+ end
52
+ end
@@ -8,43 +8,42 @@ silence_warnings do
8
8
  end
9
9
 
10
10
  class CampfireNotifierTest < ActiveSupport::TestCase
11
-
12
- test "should send campfire notification if properly configured" do
11
+ test 'should send campfire notification if properly configured' do
13
12
  ExceptionNotifier::CampfireNotifier.stubs(:new).returns(Object.new)
14
- campfire = ExceptionNotifier::CampfireNotifier.new({:subdomain => 'test', :token => 'test_token', :room_name => 'test_room'})
13
+ campfire = ExceptionNotifier::CampfireNotifier.new(subdomain: 'test', token: 'test_token', room_name: 'test_room')
15
14
  campfire.stubs(:call).returns(fake_notification)
16
15
  notif = campfire.call(fake_exception)
17
16
 
18
17
  assert !notif[:message].empty?
19
18
  assert_equal notif[:message][:type], 'PasteMessage'
20
- assert_includes notif[:message][:body], "A new exception occurred:"
21
- assert_includes notif[:message][:body], "divided by 0"
22
- assert_includes notif[:message][:body], "/exception_notification/test/campfire_test.rb:45"
19
+ assert_includes notif[:message][:body], 'A new exception occurred:'
20
+ assert_includes notif[:message][:body], 'divided by 0'
21
+ assert_includes notif[:message][:body], '/exception_notification/test/campfire_test.rb:45'
23
22
  end
24
23
 
25
- test "should send campfire notification without backtrace info if properly configured" do
24
+ test 'should send campfire notification without backtrace info if properly configured' do
26
25
  ExceptionNotifier::CampfireNotifier.stubs(:new).returns(Object.new)
27
- campfire = ExceptionNotifier::CampfireNotifier.new({:subdomain => 'test', :token => 'test_token', :room_name => 'test_room'})
26
+ campfire = ExceptionNotifier::CampfireNotifier.new(subdomain: 'test', token: 'test_token', room_name: 'test_room')
28
27
  campfire.stubs(:call).returns(fake_notification_without_backtrace)
29
28
  notif = campfire.call(fake_exception_without_backtrace)
30
29
 
31
30
  assert !notif[:message].empty?
32
31
  assert_equal notif[:message][:type], 'PasteMessage'
33
- assert_includes notif[:message][:body], "A new exception occurred:"
34
- assert_includes notif[:message][:body], "my custom error"
32
+ assert_includes notif[:message][:body], 'A new exception occurred:'
33
+ assert_includes notif[:message][:body], 'my custom error'
35
34
  end
36
35
 
37
- test "should not send campfire notification if badly configured" do
38
- wrong_params = {:subdomain => 'test', :token => 'bad_token', :room_name => 'test_room'}
39
- Tinder::Campfire.stubs(:new).with('test', {:token => 'bad_token'}).returns(nil)
36
+ test 'should not send campfire notification if badly configured' do
37
+ wrong_params = { subdomain: 'test', token: 'bad_token', room_name: 'test_room' }
38
+ Tinder::Campfire.stubs(:new).with('test', token: 'bad_token').returns(nil)
40
39
  campfire = ExceptionNotifier::CampfireNotifier.new(wrong_params)
41
40
 
42
41
  assert_nil campfire.room
43
42
  assert_nil campfire.call(fake_exception)
44
43
  end
45
44
 
46
- test "should not send campfire notification if config attr missing" do
47
- wrong_params = {:subdomain => 'test', :room_name => 'test_room'}
45
+ test 'should not send campfire notification if config attr missing' do
46
+ wrong_params = { subdomain: 'test', room_name: 'test_room' }
48
47
  Tinder::Campfire.stubs(:new).with('test', {}).returns(nil)
49
48
  campfire = ExceptionNotifier::CampfireNotifier.new(wrong_params)
50
49
 
@@ -52,35 +51,34 @@ class CampfireNotifierTest < ActiveSupport::TestCase
52
51
  assert_nil campfire.call(fake_exception)
53
52
  end
54
53
 
55
- test "should send the new exception message if no :accumulated_errors_count option" do
54
+ test 'should send the new exception message if no :accumulated_errors_count option' do
56
55
  campfire = ExceptionNotifier::CampfireNotifier.new({})
57
56
  campfire.stubs(:active?).returns(true)
58
- campfire.expects(:send_notice).with{ |_, _, message| message.start_with?("A new exception occurred") }.once
57
+ campfire.expects(:send_notice).with { |_, _, message| message.start_with?('A new exception occurred') }.once
59
58
  campfire.call(fake_exception)
60
59
  end
61
60
 
62
- test "shoud send the exception message if :accumulated_errors_count option greater than 1" do
61
+ test 'shoud send the exception message if :accumulated_errors_count option greater than 1' do
63
62
  campfire = ExceptionNotifier::CampfireNotifier.new({})
64
63
  campfire.stubs(:active?).returns(true)
65
- campfire.expects(:send_notice).with{ |_, _, message| message.start_with?("The exception occurred 3 times:") }.once
64
+ campfire.expects(:send_notice).with { |_, _, message| message.start_with?('The exception occurred 3 times:') }.once
66
65
  campfire.call(fake_exception, accumulated_errors_count: 3)
67
66
  end
68
67
 
69
- test "should call pre/post_callback if specified" do
70
- pre_callback_called, post_callback_called = 0,0
68
+ test 'should call pre/post_callback if specified' do
69
+ pre_callback_called = 0
70
+ post_callback_called = 0
71
71
  Tinder::Campfire.stubs(:new).returns(Object.new)
72
72
 
73
73
  campfire = ExceptionNotifier::CampfireNotifier.new(
74
- {
75
- :subdomain => 'test',
76
- :token => 'test_token',
77
- :room_name => 'test_room',
78
- :pre_callback => proc { |opts, notifier, backtrace, message, message_opts|
79
- pre_callback_called += 1
80
- },
81
- :post_callback => proc { |opts, notifier, backtrace, message, message_opts|
82
- post_callback_called += 1
83
- }
74
+ subdomain: 'test',
75
+ token: 'test_token',
76
+ room_name: 'test_room',
77
+ pre_callback: proc { |_opts, _notifier, _backtrace, _message, _message_opts|
78
+ pre_callback_called += 1
79
+ },
80
+ post_callback: proc { |_opts, _notifier, _backtrace, _message, _message_opts|
81
+ post_callback_called += 1
84
82
  }
85
83
  )
86
84
  campfire.room = Object.new
@@ -93,24 +91,26 @@ class CampfireNotifierTest < ActiveSupport::TestCase
93
91
  private
94
92
 
95
93
  def fake_notification
96
- {:message => {:type => 'PasteMessage',
97
- :body => "A new exception occurred: 'divided by 0' on '/Users/sebastian/exception_notification/test/campfire_test.rb:45:in `/'"
98
- }
94
+ {
95
+ message: {
96
+ type: 'PasteMessage',
97
+ body: "A new exception occurred: 'divided by 0' on '/Users/sebastian/exception_notification/test/campfire_test.rb:45:in `/'"
98
+ }
99
99
  }
100
100
  end
101
101
 
102
102
  def fake_exception
103
- begin
104
- 5/0
105
- rescue Exception => e
106
- e
107
- end
103
+ 5 / 0
104
+ rescue StandardError => e
105
+ e
108
106
  end
109
107
 
110
108
  def fake_notification_without_backtrace
111
- {:message => {:type => 'PasteMessage',
112
- :body => "A new exception occurred: 'my custom error'"
113
- }
109
+ {
110
+ message: {
111
+ type: 'PasteMessage',
112
+ body: "A new exception occurred: 'my custom error'"
113
+ }
114
114
  }
115
115
  end
116
116
 
@@ -0,0 +1,151 @@
1
+ require 'test_helper'
2
+ require 'dogapi/common'
3
+ require 'dogapi/event'
4
+
5
+ class DatadogNotifierTest < ActiveSupport::TestCase
6
+ def setup
7
+ @client = FakeDatadogClient.new
8
+ @options = {
9
+ client: @client
10
+ }
11
+ @notifier = ExceptionNotifier::DatadogNotifier.new(@options)
12
+ @exception = FakeException.new
13
+ @controller = FakeController.new
14
+ @request = FakeRequest.new
15
+ end
16
+
17
+ test 'should send an event to datadog' do
18
+ fake_event = Dogapi::Event.any_instance
19
+ @client.expects(:emit_event).with(fake_event)
20
+
21
+ @notifier.stubs(:datadog_event).returns(fake_event)
22
+ @notifier.call(@exception)
23
+ end
24
+
25
+ test 'should include exception class in event title' do
26
+ event = @notifier.datadog_event(@exception)
27
+ assert_includes event.msg_title, 'FakeException'
28
+ end
29
+
30
+ test 'should include prefix in event title and not append previous events' do
31
+ options = {
32
+ client: @client,
33
+ title_prefix: 'prefix'
34
+ }
35
+
36
+ notifier = ExceptionNotifier::DatadogNotifier.new(options)
37
+ event = notifier.datadog_event(@exception)
38
+ assert_equal event.msg_title, 'prefix (DatadogNotifierTest::FakeException) "Fake exception message"'
39
+
40
+ event2 = notifier.datadog_event(@exception)
41
+ assert_equal event2.msg_title, 'prefix (DatadogNotifierTest::FakeException) "Fake exception message"'
42
+ end
43
+
44
+ test 'should include exception message in event title' do
45
+ event = @notifier.datadog_event(@exception)
46
+ assert_includes event.msg_title, 'Fake exception message'
47
+ end
48
+
49
+ test 'should include controller info in event title if controller information is available' do
50
+ event = @notifier.datadog_event(@exception,
51
+ env: {
52
+ 'action_controller.instance' => @controller,
53
+ 'REQUEST_METHOD' => 'GET',
54
+ 'rack.input' => ''
55
+ })
56
+ assert_includes event.msg_title, 'Fake controller'
57
+ assert_includes event.msg_title, 'Fake action'
58
+ end
59
+
60
+ test 'should include backtrace info in event body' do
61
+ event = @notifier.datadog_event(@exception)
62
+ assert_includes event.msg_text, "backtrace line 1\nbacktrace line 2\nbacktrace line 3"
63
+ end
64
+
65
+ test 'should include request info in event body' do
66
+ ActionDispatch::Request.stubs(:new).returns(@request)
67
+
68
+ event = @notifier.datadog_event(@exception,
69
+ env: {
70
+ 'action_controller.instance' => @controller,
71
+ 'REQUEST_METHOD' => 'GET',
72
+ 'rack.input' => ''
73
+ })
74
+ assert_includes event.msg_text, 'http://localhost:8080'
75
+ assert_includes event.msg_text, 'GET'
76
+ assert_includes event.msg_text, '127.0.0.1'
77
+ assert_includes event.msg_text, '{"param 1"=>"value 1", "param 2"=>"value 2"}'
78
+ end
79
+
80
+ test 'should include tags in event' do
81
+ options = {
82
+ client: @client,
83
+ tags: %w[error production]
84
+ }
85
+ notifier = ExceptionNotifier::DatadogNotifier.new(options)
86
+ event = notifier.datadog_event(@exception)
87
+ assert_equal event.tags, %w[error production]
88
+ end
89
+
90
+ test 'should include event title in event aggregation key' do
91
+ event = @notifier.datadog_event(@exception)
92
+ assert_equal event.aggregation_key, [event.msg_title]
93
+ end
94
+
95
+ class FakeDatadogClient
96
+ def emit_event(event); end
97
+ end
98
+
99
+ class FakeController
100
+ def controller_name
101
+ 'Fake controller'
102
+ end
103
+
104
+ def action_name
105
+ 'Fake action'
106
+ end
107
+ end
108
+
109
+ class FakeException
110
+ def backtrace
111
+ [
112
+ 'backtrace line 1',
113
+ 'backtrace line 2',
114
+ 'backtrace line 3',
115
+ 'backtrace line 4',
116
+ 'backtrace line 5'
117
+ ]
118
+ end
119
+
120
+ def message
121
+ 'Fake exception message'
122
+ end
123
+ end
124
+
125
+ class FakeRequest
126
+ def url
127
+ 'http://localhost:8080'
128
+ end
129
+
130
+ def request_method
131
+ 'GET'
132
+ end
133
+
134
+ def remote_ip
135
+ '127.0.0.1'
136
+ end
137
+
138
+ def filtered_parameters
139
+ {
140
+ 'param 1' => 'value 1',
141
+ 'param 2' => 'value 2'
142
+ }
143
+ end
144
+
145
+ def session
146
+ {
147
+ 'session_id' => '1234'
148
+ }
149
+ end
150
+ end
151
+ end