honeybadger 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/Appraisals +25 -1
  2. data/CHANGELOG.md +47 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +7 -8
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +117 -6
  7. data/Rakefile +15 -0
  8. data/features/rack.feature +2 -2
  9. data/features/rails.feature +79 -15
  10. data/features/step_definitions/metal_steps.rb +4 -4
  11. data/features/step_definitions/rack_steps.rb +0 -4
  12. data/features/step_definitions/rails_steps.rb +49 -32
  13. data/features/support/honeybadger_failure_shim.rb.template +5 -0
  14. data/features/support/rails.rb +17 -5
  15. data/gemfiles/rack.gemfile +8 -0
  16. data/gemfiles/rack.gemfile.lock +92 -0
  17. data/gemfiles/rails2.3.gemfile +1 -1
  18. data/gemfiles/rails2.3.gemfile.lock +6 -2
  19. data/gemfiles/rails3.0.gemfile +2 -1
  20. data/gemfiles/rails3.0.gemfile.lock +12 -3
  21. data/gemfiles/rails3.1.gemfile +2 -1
  22. data/gemfiles/rails3.1.gemfile.lock +12 -3
  23. data/gemfiles/rails3.2.gemfile +3 -2
  24. data/gemfiles/rails3.2.gemfile.lock +45 -36
  25. data/gemfiles/rails4.gemfile +9 -0
  26. data/gemfiles/rails4.gemfile.lock +174 -0
  27. data/gemfiles/rake.gemfile +8 -0
  28. data/gemfiles/rake.gemfile.lock +91 -0
  29. data/gemfiles/sinatra.gemfile +8 -0
  30. data/gemfiles/sinatra.gemfile.lock +91 -0
  31. data/generators/honeybadger/honeybadger_generator.rb +5 -5
  32. data/honeybadger.gemspec +12 -3
  33. data/lib/honeybadger.rb +7 -14
  34. data/lib/honeybadger/configuration.rb +10 -1
  35. data/lib/honeybadger/notice.rb +38 -20
  36. data/lib/honeybadger/rack.rb +0 -1
  37. data/lib/honeybadger/rails/controller_methods.rb +5 -4
  38. data/lib/honeybadger/rails3_tasks.rb +16 -4
  39. data/lib/honeybadger/sender.rb +10 -19
  40. data/lib/honeybadger/shared_tasks.rb +2 -3
  41. data/lib/honeybadger/tasks.rb +16 -9
  42. data/lib/honeybadger_tasks.rb +2 -3
  43. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +5 -5
  44. data/test/test_helper.rb +2 -7
  45. data/test/unit/backtrace_test.rb +19 -19
  46. data/test/unit/configuration_test.rb +11 -5
  47. data/test/unit/notice_test.rb +21 -3
  48. data/test/unit/rails/action_controller_catcher_test.rb +250 -241
  49. data/test/unit/sender_test.rb +22 -2
  50. metadata +13 -19
@@ -22,7 +22,6 @@ module Honeybadger
22
22
  class Rack
23
23
  def initialize(app)
24
24
  @app = app
25
- Honeybadger.configuration.logger ||= Logger.new STDOUT
26
25
  end
27
26
 
28
27
  def ignored_user_agent?(env)
@@ -37,13 +37,14 @@ module Honeybadger
37
37
  def honeybadger_filter_if_filtering(hash)
38
38
  return hash if ! hash.is_a?(Hash)
39
39
 
40
- if respond_to?(:filter_parameters) # Rails 2
40
+ # Rails 2 filters parameters in the controller
41
+ # In Rails 3+ we use request.env['action_dispatch.parameter_filter']
42
+ # to filter parameters in Honeybadger::Notice (avoids filtering twice)
43
+ if respond_to?(:filter_parameters)
41
44
  filter_parameters(hash)
42
- elsif defined?(ActionDispatch::Http::ParameterFilter) # Rails 3
43
- ActionDispatch::Http::ParameterFilter.new(::Rails.application.config.filter_parameters).filter(hash)
44
45
  else
45
46
  hash
46
- end rescue hash
47
+ end
47
48
  end
48
49
 
49
50
  def honeybadger_session_data
@@ -13,6 +13,8 @@ namespace :honeybadger do
13
13
 
14
14
  Honeybadger.configure(true) do |config|
15
15
  config.logger = Rails.logger
16
+ config.debug = true
17
+ config.development_environments = []
16
18
  end
17
19
 
18
20
  # Suppress error logging in Rails' exception handling middleware. Rails 3.0
@@ -23,7 +25,17 @@ namespace :honeybadger do
23
25
  class ActionDispatch::DebugExceptions ; def logger(*args) ; @logger ||= Logger.new('/dev/null') ; end ; end
24
26
  class ActionDispatch::ShowExceptions ; def logger(*args) ; @logger ||= Logger.new('/dev/null') ; end ; end
25
27
 
26
- require './app/controllers/application_controller'
28
+ # Detect and disable the better_errors gem
29
+ if defined? BetterErrors::Middleware
30
+ puts 'Better Errors detected: temporarily disabling middleware.'
31
+ class BetterErrors::Middleware ; def call(env) ; end ; end
32
+ end
33
+
34
+ begin
35
+ require './app/controllers/application_controller'
36
+ rescue LoadError
37
+ nil
38
+ end
27
39
 
28
40
  class HoneybadgerTestingException < RuntimeError; end
29
41
 
@@ -37,8 +49,6 @@ namespace :honeybadger do
37
49
  Honeybadger.configuration.async = nil
38
50
  end
39
51
 
40
- Honeybadger.configuration.development_environments = []
41
-
42
52
  puts "Configuration:"
43
53
  Honeybadger.configuration.to_hash.each do |key, value|
44
54
  puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
@@ -74,7 +84,9 @@ namespace :honeybadger do
74
84
  end
75
85
 
76
86
  puts 'Processing request.'
77
- env = Rack::MockRequest.env_for("/verify")
87
+
88
+ ssl = defined?(Rails.configuration.force_ssl) && Rails.configuration.force_ssl
89
+ env = Rack::MockRequest.env_for("http#{ ssl ? 's' : nil }://www.example.com/verify")
78
90
 
79
91
  Rails.application.call(env)
80
92
  end
@@ -32,14 +32,14 @@ module Honeybadger
32
32
  #
33
33
  # notice - The notice data to be sent (Hash or JSON string)
34
34
  #
35
- # Returns nothing
35
+ # Returns error id from successful response
36
36
  def send_to_honeybadger(notice)
37
37
  data = notice.is_a?(String) ? notice : notice.to_json
38
38
 
39
39
  http = setup_http_connection
40
40
  headers = HEADERS
41
41
 
42
- headers.merge!({ 'X-API-Key' => api_key}) if api_key.present?
42
+ headers.merge!({ 'X-API-Key' => api_key}) unless api_key.nil?
43
43
 
44
44
  response = begin
45
45
  http.post(url.path, data, headers)
@@ -50,17 +50,14 @@ module Honeybadger
50
50
 
51
51
  case response
52
52
  when Net::HTTPSuccess then
53
- log(:info, "Success: #{response.class}", response, data)
53
+ log(Honeybadger.configuration.debug ? :info : :debug, "Success: #{response.class}", response, data)
54
+ JSON.parse(response.body)['id']
54
55
  else
55
56
  log(:error, "Failure: #{response.class}", response, data)
56
- end
57
-
58
- if response && response.respond_to?(:body)
59
- notice = JSON.parse(response.body)
60
- error_id = notice['id']
57
+ nil
61
58
  end
62
59
  rescue => e
63
- log(:error, "[Honeybadger::Sender#send_to_honeybadger] Cannot send notification. Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
60
+ log(:error, "[Honeybadger::Sender#send_to_honeybadger] Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
64
61
  nil
65
62
  end
66
63
 
@@ -87,19 +84,13 @@ module Honeybadger
87
84
  end
88
85
 
89
86
  def log(level, message, response = nil, data = nil)
90
- if logger
91
- logger.send(level, LOG_PREFIX + message)
92
-
93
- # Log the notice payload for debugging
94
- logger.debug(LOG_PREFIX + "Notice: #{data}") if data
95
- end
87
+ # Log result:
88
+ Honeybadger.write_verbose_log(message, level)
96
89
 
90
+ # Log debug information:
97
91
  Honeybadger.report_environment_info
98
92
  Honeybadger.report_response_body(response.body) if response && response.respond_to?(:body)
99
- end
100
-
101
- def logger
102
- Honeybadger.logger
93
+ Honeybadger.write_verbose_log("Notice: #{data}", :debug) if data && Honeybadger.configuration.debug
103
94
  end
104
95
 
105
96
  def setup_http_connection
@@ -35,10 +35,9 @@ namespace :honeybadger do
35
35
  end
36
36
 
37
37
  heroku_rails_env = heroku_var('RAILS_ENV', ENV['APP'])
38
- heroku_api_key = heroku_var('HONEYBADGER_API_KEY', ENV['APP']).split.find {|x| x unless x.blank?} ||
39
- Honeybadger.configuration.api_key
38
+ heroku_api_key = heroku_var('HONEYBADGER_API_KEY', ENV['APP']).split.find(Honeybadger.configuration.api_key) {|x| x =~ /\S/ }
40
39
 
41
- if heroku_api_key.blank? || heroku_rails_env.blank?
40
+ unless heroku_api_key =~ /\S/ && heroku_rails_env =~ /\S/
42
41
  puts "WARNING: We were unable to detect the configuration from your Heroku environment."
43
42
  puts "Your Heroku application environment may not be configured correctly."
44
43
  puts "Have you configured multiple Heroku apps? Try using APP=[app name]'" unless ENV['APP']
@@ -8,7 +8,11 @@ namespace :honeybadger do
8
8
 
9
9
  require 'action_controller/test_process'
10
10
 
11
- Dir["app/controllers/application*.rb"].each { |file| require(File.expand_path(file)) }
11
+ begin
12
+ Dir["app/controllers/application*.rb"].each { |file| require(File.expand_path(file)) }
13
+ rescue LoadError
14
+ nil
15
+ end
12
16
 
13
17
  class HoneybadgerTestingException < RuntimeError; end
14
18
 
@@ -22,14 +26,9 @@ namespace :honeybadger do
22
26
  Honeybadger.configuration.async = nil
23
27
  end
24
28
 
25
- Honeybadger.configuration.development_environments = []
26
-
27
- catcher = Honeybadger::Rails::ActionControllerCatcher
28
- in_controller = ApplicationController.included_modules.include?(catcher)
29
- in_base = ActionController::Base.included_modules.include?(catcher)
30
- if !in_controller || !in_base
31
- puts "Rails initialization did not occur"
32
- exit
29
+ Honeybadger.configure(true) do |config|
30
+ config.debug = true
31
+ config.development_environments = []
33
32
  end
34
33
 
35
34
  puts "Configuration:"
@@ -42,6 +41,14 @@ namespace :honeybadger do
42
41
  exit
43
42
  end
44
43
 
44
+ catcher = Honeybadger::Rails::ActionControllerCatcher
45
+ in_controller = ApplicationController.included_modules.include?(catcher)
46
+ in_base = ActionController::Base.included_modules.include?(catcher)
47
+ if !in_controller || !in_base
48
+ puts "Rails initialization did not occur"
49
+ exit
50
+ end
51
+
45
52
  puts 'Setting up the Controller.'
46
53
  class ApplicationController
47
54
  # This is to bypass any filters that may prevent access to the action.
@@ -1,6 +1,5 @@
1
1
  require 'net/http'
2
2
  require 'uri'
3
- require 'active_support'
4
3
 
5
4
  # Capistrano tasks for notifying Honeybadger of deploys
6
5
  module HoneybadgerTasks
@@ -17,12 +16,12 @@ module HoneybadgerTasks
17
16
  # Returns true or false
18
17
  def self.deploy(opts = {})
19
18
  api_key = opts.delete(:api_key) || Honeybadger.configuration.api_key
20
- if api_key.blank?
19
+ unless api_key =~ /\S/
21
20
  puts "I don't seem to be configured with an API key. Please check your configuration."
22
21
  return false
23
22
  end
24
23
 
25
- if opts[:environment].blank?
24
+ unless opts[:environment] =~ /\S/
26
25
  puts "I don't know to which environment you are deploying (use the TO=production option)."
27
26
  return false
28
27
  end
@@ -59,15 +59,15 @@ class HoneybadgerGenerator < Rails::Generators::Base
59
59
  def determine_api_key
60
60
  puts "Attempting to determine your API Key from Heroku..."
61
61
  ENV['HONEYBADGER_API_KEY'] = heroku_api_key
62
- if ENV['HONEYBADGER_API_KEY'].blank?
62
+ if ENV['HONEYBADGER_API_KEY'] =~ /\S/
63
+ puts "... Done."
64
+ puts "Heroku's Honeybadger API Key is '#{ENV['HONEYBADGER_API_KEY']}'"
65
+ else
63
66
  puts "... Failed."
64
67
  puts "WARNING: We were unable to detect the Honeybadger API Key from your Heroku environment."
65
68
  puts "Your Heroku application environment may not be configured correctly."
66
69
  puts "Have you configured multiple Heroku apps? Try using the '--app [app name]' flag." unless options[:app]
67
70
  exit 1
68
- else
69
- puts "... Done."
70
- puts "Heroku's Honeybadger API Key is '#{ENV['HONEYBADGER_API_KEY']}'"
71
71
  end
72
72
  end
73
73
 
@@ -77,7 +77,7 @@ class HoneybadgerGenerator < Rails::Generators::Base
77
77
  end
78
78
 
79
79
  def heroku_api_key
80
- heroku_var("HONEYBADGER_API_KEY", options[:app]).split.find {|x| x unless x.blank?}
80
+ heroku_var("HONEYBADGER_API_KEY", options[:app]).split.find {|x| x =~ /\S/ }
81
81
  end
82
82
 
83
83
  def heroku?
data/test/test_helper.rb CHANGED
@@ -5,11 +5,6 @@ require 'shoulda'
5
5
  require 'bourne'
6
6
  require 'rack'
7
7
 
8
- require 'action_controller'
9
- require 'action_controller/test_process'
10
- require 'active_record'
11
- require 'active_support'
12
-
13
8
  require 'honeybadger'
14
9
 
15
10
  class BacktracedException < Exception
@@ -126,13 +121,13 @@ class Test::Unit::TestCase
126
121
 
127
122
  def assert_logged(expected)
128
123
  assert_received(Honeybadger, :write_verbose_log) do |expect|
129
- expect.with {|actual| actual =~ expected }
124
+ expect.with {|actual, level| actual =~ expected }
130
125
  end
131
126
  end
132
127
 
133
128
  def assert_not_logged(expected)
134
129
  assert_received(Honeybadger, :write_verbose_log) do |expect|
135
- expect.with {|actual| actual =~ expected }.never
130
+ expect.with {|actual, level| actual =~ expected }.never
136
131
  end
137
132
  end
138
133
 
@@ -84,23 +84,23 @@ class BacktraceTest < Test::Unit::TestCase
84
84
  end
85
85
 
86
86
  should "include a snippet from the source file for each line of the backtrace" do
87
- assert_equal 4, @backtrace.lines.first.source.keys.size
88
- assert_match /\$:<</, @backtrace.lines.first.source[1]
89
- assert_match /require/, @backtrace.lines.first.source[2]
90
- assert_match /\n/, @backtrace.lines.first.source[3]
91
- assert_match /begin/, @backtrace.lines.first.source[4]
92
-
93
- assert_equal 5, @backtrace.lines.second.source.keys.size
94
- assert_match /require/, @backtrace.lines.second.source[2]
95
- assert_match /\n/, @backtrace.lines.second.source[3]
96
- assert_match /begin/, @backtrace.lines.second.source[4]
97
- assert_match /StandardError/, @backtrace.lines.second.source[5]
98
- assert_match /rescue/, @backtrace.lines.second.source[6]
99
-
100
- assert_equal 3, @backtrace.lines.third.source.keys.size
101
- assert_match /rescue/, @backtrace.lines.third.source[6]
102
- assert_match /Honeybadger/, @backtrace.lines.third.source[7]
103
- assert_match /end/, @backtrace.lines.third.source[8]
87
+ assert_equal 4, @backtrace.lines[0].source.keys.size
88
+ assert_match /\$:<</, @backtrace.lines[0].source[1]
89
+ assert_match /require/, @backtrace.lines[0].source[2]
90
+ assert_match /\n/, @backtrace.lines[0].source[3]
91
+ assert_match /begin/, @backtrace.lines[0].source[4]
92
+
93
+ assert_equal 5, @backtrace.lines[1].source.keys.size
94
+ assert_match /require/, @backtrace.lines[1].source[2]
95
+ assert_match /\n/, @backtrace.lines[1].source[3]
96
+ assert_match /begin/, @backtrace.lines[1].source[4]
97
+ assert_match /StandardError/, @backtrace.lines[1].source[5]
98
+ assert_match /rescue/, @backtrace.lines[1].source[6]
99
+
100
+ assert_equal 3, @backtrace.lines[2].source.keys.size
101
+ assert_match /rescue/, @backtrace.lines[2].source[6]
102
+ assert_match /Honeybadger/, @backtrace.lines[2].source[7]
103
+ assert_match /end/, @backtrace.lines[2].source[8]
104
104
  end
105
105
  end
106
106
 
@@ -112,8 +112,8 @@ class BacktraceTest < Test::Unit::TestCase
112
112
 
113
113
  backtrace = Honeybadger::Backtrace.parse(array)
114
114
 
115
- assert_equal backtrace.lines.first.source, {}
116
- assert_equal backtrace.lines.second.source, {}
115
+ assert_equal backtrace.lines[0].source, {}
116
+ assert_equal backtrace.lines[1].source, {}
117
117
  end
118
118
 
119
119
  should "have an empty application trace by default" do
@@ -27,6 +27,8 @@ class ConfigurationTest < Test::Unit::TestCase
27
27
  assert_config_default :framework, 'Standalone'
28
28
  assert_config_default :source_extract_radius, 2
29
29
  assert_config_default :async, nil
30
+ assert_config_default :send_request_session, true
31
+ assert_config_default :debug, false
30
32
  end
31
33
 
32
34
  should "configure async as Proc" do
@@ -89,6 +91,10 @@ class ConfigurationTest < Test::Unit::TestCase
89
91
  assert_config_overridable :notifier_url
90
92
  assert_config_overridable :environment_name
91
93
  assert_config_overridable :logger
94
+ assert_config_overridable :source_extract_radius
95
+ assert_config_overridable :async
96
+ assert_config_overridable :send_request_session
97
+ assert_config_overridable :debug
92
98
  end
93
99
 
94
100
  should "have an api key" do
@@ -99,11 +105,11 @@ class ConfigurationTest < Test::Unit::TestCase
99
105
  config = Honeybadger::Configuration.new
100
106
  hash = config.to_hash
101
107
  [:api_key, :backtrace_filters, :development_environments,
102
- :environment_name, :host, :http_open_timeout,
103
- :http_read_timeout, :ignore, :ignore_by_filters, :ignore_user_agent,
104
- :notifier_name, :notifier_url, :notifier_version, :params_filters,
105
- :project_root, :port, :protocol, :proxy_host, :proxy_pass, :proxy_port,
106
- :proxy_user, :secure].each do |option|
108
+ :environment_name, :host, :http_open_timeout, :http_read_timeout, :ignore,
109
+ :ignore_by_filters, :ignore_user_agent, :notifier_name, :notifier_url,
110
+ :notifier_version, :params_filters, :project_root, :port, :protocol,
111
+ :proxy_host, :proxy_pass, :proxy_port, :proxy_user, :secure,
112
+ :source_extract_radius, :async, :send_request_session, :debug].each do |option|
107
113
  assert_equal config[option], hash[option], "Wrong value for #{option}"
108
114
  end
109
115
  end
@@ -163,7 +163,7 @@ class NoticeTest < Test::Unit::TestCase
163
163
  @string_io.rewind
164
164
 
165
165
  assert_not_equal notice_from_exception.source_extract, {}, 'Expected backtrace source extract to be found'
166
- assert_equal backtrace.lines.second.source, notice_from_exception.source_extract
166
+ assert_equal backtrace.lines[1].source, notice_from_exception.source_extract
167
167
  end
168
168
  end
169
169
  end
@@ -325,10 +325,23 @@ class NoticeTest < Test::Unit::TestCase
325
325
  assert notice.ignore?
326
326
  end
327
327
 
328
- should "ignore an exception with a matching error class name" do
328
+ should "ignore an exception with an equal error class name" do
329
329
  notice = build_notice(:error_class => 'ArgumentError',
330
330
  :ignore => ['ArgumentError'])
331
- assert notice.ignore?
331
+ assert notice.ignore?, "Expected ArgumentError to ignore ArgumentError"
332
+ end
333
+
334
+ should "ignore an exception matching error class name" do
335
+ notice = build_notice(:error_class => 'ArgumentError',
336
+ :ignore => [/Error$/])
337
+ assert notice.ignore?, "Expected /Error$/ to ignore ArgumentError"
338
+ end
339
+
340
+ should "ignore an exception that inherits from ignored error class" do
341
+ class ::FooError < ArgumentError ; end
342
+ notice = build_notice(:exception => FooError.new('Oh noes!'),
343
+ :ignore => [ArgumentError])
344
+ assert notice.ignore?, "Expected ArgumentError to ignore FooError"
332
345
  end
333
346
 
334
347
  should "ignore an exception with a matching filter" do
@@ -442,6 +455,11 @@ class NoticeTest < Test::Unit::TestCase
442
455
  assert_equal session_data, notice.session_data
443
456
  end
444
457
 
458
+ should "not send session data when send_request_session is false" do
459
+ notice = build_notice(:send_request_session => false, :session_data => { :foo => :bar })
460
+ assert_equal nil, notice.session_data
461
+ end
462
+
445
463
  should "not allow infinite recursion" do
446
464
  hash = {:a => :a}
447
465
  hash[:hash] = hash
@@ -1,300 +1,309 @@
1
1
  require 'test_helper'
2
- require 'honeybadger/rails'
3
2
 
4
- class ActionControllerCatcherTest < Test::Unit::TestCase
5
- include DefinesConstants
3
+ begin
4
+ require 'active_support'
5
+ require 'action_controller'
6
+ require 'action_controller/test_process'
7
+ require 'honeybadger/rails'
6
8
 
7
- def setup
8
- super
9
- reset_config
10
- Honeybadger.sender = CollectingSender.new
11
- define_constant('RAILS_ROOT', '/path/to/rails/root')
12
- end
13
-
14
- def ignore(exception_class)
15
- Honeybadger.configuration.ignore << exception_class
16
- end
9
+ class ActionControllerCatcherTest < Test::Unit::TestCase
10
+ include DefinesConstants
17
11
 
18
- def build_controller_class(&definition)
19
- Class.new(ActionController::Base).tap do |klass|
20
- klass.__send__(:include, Honeybadger::Rails::ActionControllerCatcher)
21
- klass.class_eval(&definition) if definition
22
- define_constant('HoneybadgerTestController', klass)
12
+ def setup
13
+ super
14
+ reset_config
15
+ Honeybadger.sender = CollectingSender.new
16
+ define_constant('RAILS_ROOT', '/path/to/rails/root')
23
17
  end
24
- end
25
-
26
- def assert_sent_hash(hash, &block)
27
- hash.each do |key, value|
28
- next if key.match(/^honeybadger\./) # We added this key.
29
18
 
30
- new_block = Proc.new {
31
- block.call(last_sent_notice_payload)[key]
32
- }
19
+ def ignore(exception_class)
20
+ Honeybadger.configuration.ignore << exception_class
21
+ end
33
22
 
34
- if value.respond_to?(:to_hash)
35
- assert_sent_hash(value.to_hash, &new_block)
36
- else
37
- assert_sent_element(value, &new_block)
23
+ def build_controller_class(&definition)
24
+ Class.new(ActionController::Base).tap do |klass|
25
+ klass.__send__(:include, Honeybadger::Rails::ActionControllerCatcher)
26
+ klass.class_eval(&definition) if definition
27
+ define_constant('HoneybadgerTestController', klass)
38
28
  end
39
29
  end
40
- end
41
30
 
42
- def assert_sent_element(value, &block)
43
- assert_equal yield(last_sent_notice_payload), stringify_array_elements(value)
44
- end
31
+ def assert_sent_hash(hash, &block)
32
+ hash.each do |key, value|
33
+ next if key.match(/^honeybadger\./) # We added this key.
45
34
 
46
- def stringify_array_elements(data)
47
- if data.is_a?(Array)
48
- data.collect do |value|
49
- stringify_array_elements(value)
35
+ new_block = Proc.new {
36
+ block.call(last_sent_notice_payload)[key]
37
+ }
38
+
39
+ if value.respond_to?(:to_hash)
40
+ assert_sent_hash(value.to_hash, &new_block)
41
+ else
42
+ assert_sent_element(value, &new_block)
43
+ end
50
44
  end
51
- else
52
- data.to_s
53
45
  end
54
- end
55
46
 
56
- def assert_sent_request_info_for(request)
57
- params = request.parameters.to_hash
58
- assert_sent_hash(params) { |h| h['request']['params'] }
59
- assert_sent_element(params['controller']) { |h| h['request']['component'] }
60
- assert_sent_element(params['action']) { |h| h['request']['action'] }
61
- assert_sent_element(url_from_request(request)) { |h| h['request']['url'] }
62
- assert_sent_hash(request.env) { |h| h['request']['cgi_data'] }
63
- end
47
+ def assert_sent_element(value, &block)
48
+ assert_equal yield(last_sent_notice_payload), stringify_array_elements(value)
49
+ end
64
50
 
65
- def url_from_request(request)
66
- url = "#{request.protocol}#{request.host}"
51
+ def stringify_array_elements(data)
52
+ if data.is_a?(Array)
53
+ data.collect do |value|
54
+ stringify_array_elements(value)
55
+ end
56
+ else
57
+ data.to_s
58
+ end
59
+ end
67
60
 
68
- unless [80, 443].include?(request.port)
69
- url << ":#{request.port}"
61
+ def assert_sent_request_info_for(request)
62
+ params = request.parameters.to_hash
63
+ assert_sent_hash(params) { |h| h['request']['params'] }
64
+ assert_sent_element(params['controller']) { |h| h['request']['component'] }
65
+ assert_sent_element(params['action']) { |h| h['request']['action'] }
66
+ assert_sent_element(url_from_request(request)) { |h| h['request']['url'] }
67
+ assert_sent_hash(request.env) { |h| h['request']['cgi_data'] }
70
68
  end
71
69
 
72
- url << request.request_uri
73
- url
74
- end
70
+ def url_from_request(request)
71
+ url = "#{request.protocol}#{request.host}"
75
72
 
76
- def sender
77
- Honeybadger.sender
78
- end
73
+ unless [80, 443].include?(request.port)
74
+ url << ":#{request.port}"
75
+ end
79
76
 
80
- def last_sent_notice_json
81
- sender.collected.last
82
- end
77
+ url << request.request_uri
78
+ url
79
+ end
83
80
 
84
- def last_sent_notice_payload
85
- assert_not_nil last_sent_notice_json, "No json was sent"
86
- JSON.parse(last_sent_notice_json)
87
- end
81
+ def sender
82
+ Honeybadger.sender
83
+ end
88
84
 
89
- def process_action(opts = {}, &action)
90
- opts[:request] ||= ActionController::TestRequest.new
91
- opts[:response] ||= ActionController::TestResponse.new
92
- klass = build_controller_class do
93
- cattr_accessor :local
94
- define_method(:index, &action)
95
- def local_request?
96
- local
97
- end
85
+ def last_sent_notice_json
86
+ sender.collected.last
98
87
  end
99
- if opts[:filters]
100
- klass.filter_parameter_logging *opts[:filters]
88
+
89
+ def last_sent_notice_payload
90
+ assert_not_nil last_sent_notice_json, "No json was sent"
91
+ JSON.parse(last_sent_notice_json)
101
92
  end
102
- if opts[:user_agent]
103
- if opts[:request].respond_to?(:user_agent=)
104
- opts[:request].user_agent = opts[:user_agent]
105
- else
106
- opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent]
93
+
94
+ def process_action(opts = {}, &action)
95
+ opts[:request] ||= ActionController::TestRequest.new
96
+ opts[:response] ||= ActionController::TestResponse.new
97
+ klass = build_controller_class do
98
+ cattr_accessor :local
99
+ define_method(:index, &action)
100
+ def local_request?
101
+ local
102
+ end
103
+ end
104
+ if opts[:filters]
105
+ klass.filter_parameter_logging *opts[:filters]
106
+ end
107
+ if opts[:user_agent]
108
+ if opts[:request].respond_to?(:user_agent=)
109
+ opts[:request].user_agent = opts[:user_agent]
110
+ else
111
+ opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent]
112
+ end
107
113
  end
114
+ if opts[:port]
115
+ opts[:request].port = opts[:port]
116
+ end
117
+ klass.consider_all_requests_local = opts[:all_local]
118
+ klass.local = opts[:local]
119
+ controller = klass.new
120
+ controller.stubs(:rescue_action_in_public_without_honeybadger)
121
+ opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
122
+ opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
123
+ # Prevents request.fullpath from crashing Rails in tests
124
+ opts[:request].env['REQUEST_URI'] = opts[:request].request_uri
125
+ controller.process(opts[:request], opts[:response])
126
+ controller
108
127
  end
109
- if opts[:port]
110
- opts[:request].port = opts[:port]
111
- end
112
- klass.consider_all_requests_local = opts[:all_local]
113
- klass.local = opts[:local]
114
- controller = klass.new
115
- controller.stubs(:rescue_action_in_public_without_honeybadger)
116
- opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
117
- opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
118
- # Prevents request.fullpath from crashing Rails in tests
119
- opts[:request].env['REQUEST_URI'] = opts[:request].request_uri
120
- controller.process(opts[:request], opts[:response])
121
- controller
122
- end
123
128
 
124
- def process_action_with_manual_notification(args = {})
125
- process_action(args) do
126
- notify_honeybadger(:error_message => 'fail')
127
- # Rails will raise a template error if we don't render something
128
- render :nothing => true
129
+ def process_action_with_manual_notification(args = {})
130
+ process_action(args) do
131
+ notify_honeybadger(:error_message => 'fail')
132
+ # Rails will raise a template error if we don't render something
133
+ render :nothing => true
134
+ end
129
135
  end
130
- end
131
136
 
132
- def process_action_with_automatic_notification(args = {})
133
- process_action(args) { raise "Hello" }
134
- end
137
+ def process_action_with_automatic_notification(args = {})
138
+ process_action(args) { raise "Hello" }
139
+ end
135
140
 
136
- should "deliver notices from exceptions raised in public requests" do
137
- process_action_with_automatic_notification
138
- assert_caught_and_sent
139
- end
141
+ should "deliver notices from exceptions raised in public requests" do
142
+ process_action_with_automatic_notification
143
+ assert_caught_and_sent
144
+ end
140
145
 
141
- should "not deliver notices from exceptions in local requests" do
142
- process_action_with_automatic_notification(:local => true)
143
- assert_caught_and_not_sent
144
- end
146
+ should "not deliver notices from exceptions in local requests" do
147
+ process_action_with_automatic_notification(:local => true)
148
+ assert_caught_and_not_sent
149
+ end
145
150
 
146
- should "not deliver notices from exceptions when all requests are local" do
147
- process_action_with_automatic_notification(:all_local => true)
148
- assert_caught_and_not_sent
149
- end
151
+ should "not deliver notices from exceptions when all requests are local" do
152
+ process_action_with_automatic_notification(:all_local => true)
153
+ assert_caught_and_not_sent
154
+ end
150
155
 
151
- should "not deliver notices from actions that don't raise" do
152
- controller = process_action { render :text => 'Hello' }
153
- assert_caught_and_not_sent
154
- assert_equal 'Hello', controller.response.body
155
- end
156
+ should "not deliver notices from actions that don't raise" do
157
+ controller = process_action { render :text => 'Hello' }
158
+ assert_caught_and_not_sent
159
+ assert_equal 'Hello', controller.response.body
160
+ end
156
161
 
157
- should "not deliver ignored exceptions raised by actions" do
158
- ignore(RuntimeError)
159
- process_action_with_automatic_notification
160
- assert_caught_and_not_sent
161
- end
162
+ should "not deliver ignored exceptions raised by actions" do
163
+ ignore(RuntimeError)
164
+ process_action_with_automatic_notification
165
+ assert_caught_and_not_sent
166
+ end
162
167
 
163
- should "deliver ignored exception raised manually" do
164
- ignore(RuntimeError)
165
- process_action_with_manual_notification
166
- assert_caught_and_sent
167
- end
168
+ should "deliver ignored exception raised manually" do
169
+ ignore(RuntimeError)
170
+ process_action_with_manual_notification
171
+ assert_caught_and_sent
172
+ end
168
173
 
169
- should "deliver manually sent notices in public requests" do
170
- process_action_with_manual_notification
171
- assert_caught_and_sent
172
- end
174
+ should "deliver manually sent notices in public requests" do
175
+ process_action_with_manual_notification
176
+ assert_caught_and_sent
177
+ end
173
178
 
174
- should "not deliver manually sent notices in local requests" do
175
- process_action_with_manual_notification(:local => true)
176
- assert_caught_and_not_sent
177
- end
179
+ should "not deliver manually sent notices in local requests" do
180
+ process_action_with_manual_notification(:local => true)
181
+ assert_caught_and_not_sent
182
+ end
178
183
 
179
- should "not deliver manually sent notices when all requests are local" do
180
- process_action_with_manual_notification(:all_local => true)
181
- assert_caught_and_not_sent
182
- end
184
+ should "not deliver manually sent notices when all requests are local" do
185
+ process_action_with_manual_notification(:all_local => true)
186
+ assert_caught_and_not_sent
187
+ end
183
188
 
184
- should "continue with default behavior after delivering an exception" do
185
- controller = process_action_with_automatic_notification(:public => true)
186
- # TODO: can we test this without stubbing?
187
- assert_received(controller, :rescue_action_in_public_without_honeybadger)
188
- end
189
+ should "continue with default behavior after delivering an exception" do
190
+ controller = process_action_with_automatic_notification(:public => true)
191
+ # TODO: can we test this without stubbing?
192
+ assert_received(controller, :rescue_action_in_public_without_honeybadger)
193
+ end
189
194
 
190
- should "not create actions from Honeybadger methods" do
191
- controller = build_controller_class.new
192
- assert_equal [], Honeybadger::Rails::ActionControllerCatcher.instance_methods
193
- end
195
+ should "not create actions from Honeybadger methods" do
196
+ controller = build_controller_class.new
197
+ assert_equal [], Honeybadger::Rails::ActionControllerCatcher.instance_methods
198
+ end
194
199
 
195
- should "ignore exceptions when user agent is being ignored by regular expression" do
196
- Honeybadger.configuration.ignore_user_agent_only = [/Ignored/]
197
- process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored')
198
- assert_caught_and_not_sent
199
- end
200
+ should "ignore exceptions when user agent is being ignored by regular expression" do
201
+ Honeybadger.configuration.ignore_user_agent_only = [/Ignored/]
202
+ process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored')
203
+ assert_caught_and_not_sent
204
+ end
200
205
 
201
- should "ignore exceptions when user agent is being ignored by string" do
202
- Honeybadger.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
203
- process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent')
204
- assert_caught_and_not_sent
205
- end
206
+ should "ignore exceptions when user agent is being ignored by string" do
207
+ Honeybadger.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
208
+ process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent')
209
+ assert_caught_and_not_sent
210
+ end
206
211
 
207
- should "not ignore exceptions when user agent is not being ignored" do
208
- Honeybadger.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
209
- process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent')
210
- assert_caught_and_sent
211
- end
212
+ should "not ignore exceptions when user agent is not being ignored" do
213
+ Honeybadger.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
214
+ process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent')
215
+ assert_caught_and_sent
216
+ end
212
217
 
213
- should "send session data for manual notifications" do
214
- data = { 'one' => 'two' }
215
- process_action_with_manual_notification(:session => data)
216
- assert_sent_hash(data) { |h| h['request']['session'] }
217
- end
218
+ should "send session data for manual notifications" do
219
+ data = { 'one' => 'two' }
220
+ process_action_with_manual_notification(:session => data)
221
+ assert_sent_hash(data) { |h| h['request']['session'] }
222
+ end
218
223
 
219
- should "send session data for automatic notification" do
220
- data = { 'one' => 'two' }
221
- process_action_with_automatic_notification(:session => data)
222
- assert_sent_hash(data) { |h| h['request']['session'] }
223
- end
224
+ should "send session data for automatic notification" do
225
+ data = { 'one' => 'two' }
226
+ process_action_with_automatic_notification(:session => data)
227
+ assert_sent_hash(data) { |h| h['request']['session'] }
228
+ end
224
229
 
225
- should "send request data for manual notification" do
226
- params = { 'controller' => "honeybadger_test", 'action' => "index" }
227
- controller = process_action_with_manual_notification(:params => params)
228
- assert_sent_request_info_for controller.request
229
- end
230
+ should "send request data for manual notification" do
231
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
232
+ controller = process_action_with_manual_notification(:params => params)
233
+ assert_sent_request_info_for controller.request
234
+ end
230
235
 
231
- should "send request data for manual notification with non-standard port" do
232
- params = { 'controller' => "honeybadger_test", 'action' => "index" }
233
- controller = process_action_with_manual_notification(:params => params, :port => 81)
234
- assert_sent_request_info_for controller.request
235
- end
236
+ should "send request data for manual notification with non-standard port" do
237
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
238
+ controller = process_action_with_manual_notification(:params => params, :port => 81)
239
+ assert_sent_request_info_for controller.request
240
+ end
236
241
 
237
- should "send request data for automatic notification" do
238
- params = { 'controller' => "honeybadger_test", 'action' => "index" }
239
- controller = process_action_with_automatic_notification(:params => params)
240
- assert_sent_request_info_for controller.request
241
- end
242
+ should "send request data for automatic notification" do
243
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
244
+ controller = process_action_with_automatic_notification(:params => params)
245
+ assert_sent_request_info_for controller.request
246
+ end
242
247
 
243
- should "send request data for automatic notification with non-standard port" do
244
- params = { 'controller' => "honeybadger_test", 'action' => "index" }
245
- controller = process_action_with_automatic_notification(:params => params, :port => 81)
246
- assert_sent_request_info_for controller.request
247
- end
248
+ should "send request data for automatic notification with non-standard port" do
249
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
250
+ controller = process_action_with_automatic_notification(:params => params, :port => 81)
251
+ assert_sent_request_info_for controller.request
252
+ end
248
253
 
249
- should "use standard rails logging filters on params and session and env" do
250
- filtered_params = { "abc" => "123",
251
- "def" => "456",
252
- "ghi" => "[FILTERED]" }
253
- filtered_session = { "abc" => "123",
254
- "ghi" => "[FILTERED]" }
255
- ENV['ghi'] = 'abc'
256
- filtered_env = { 'ghi' => '[FILTERED]' }
257
- filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
258
-
259
- process_action_with_automatic_notification(:filters => [:ghi, :request_method],
260
- :params => { "abc" => "123",
261
- "def" => "456",
262
- "ghi" => "789" },
263
- :session => { "abc" => "123",
264
- "ghi" => "789" })
265
- assert_sent_hash(filtered_params) { |h| h['request']['params'] }
266
- assert_sent_hash(filtered_cgi) { |h| h['request']['cgi_data'] }
267
- assert_sent_hash(filtered_session) { |h| h['request']['session'] }
268
- end
254
+ should "use standard rails logging filters on params and session and env" do
255
+ filtered_params = { "abc" => "123",
256
+ "def" => "456",
257
+ "ghi" => "[FILTERED]" }
258
+ filtered_session = { "abc" => "123",
259
+ "ghi" => "[FILTERED]" }
260
+ ENV['ghi'] = 'abc'
261
+ filtered_env = { 'ghi' => '[FILTERED]' }
262
+ filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
263
+
264
+ process_action_with_automatic_notification(:filters => [:ghi, :request_method],
265
+ :params => { "abc" => "123",
266
+ "def" => "456",
267
+ "ghi" => "789" },
268
+ :session => { "abc" => "123",
269
+ "ghi" => "789" })
270
+ assert_sent_hash(filtered_params) { |h| h['request']['params'] }
271
+ assert_sent_hash(filtered_cgi) { |h| h['request']['cgi_data'] }
272
+ assert_sent_hash(filtered_session) { |h| h['request']['session'] }
273
+ end
269
274
 
270
- should "call session.to_hash if available" do
271
- hash_data = {:key => :value}
275
+ should "call session.to_hash if available" do
276
+ hash_data = {:key => :value}
272
277
 
273
- session = ActionController::TestSession.new
274
- ActionController::TestSession.stubs(:new).returns(session)
275
- session.stubs(:to_hash).returns(hash_data)
278
+ session = ActionController::TestSession.new
279
+ ActionController::TestSession.stubs(:new).returns(session)
280
+ session.stubs(:to_hash).returns(hash_data)
276
281
 
277
- process_action_with_automatic_notification
278
- assert_received(session, :to_hash)
279
- assert_received(session, :data) { |expect| expect.never }
280
- assert_caught_and_sent
281
- end
282
+ process_action_with_automatic_notification
283
+ assert_received(session, :to_hash)
284
+ assert_received(session, :data) { |expect| expect.never }
285
+ assert_caught_and_sent
286
+ end
282
287
 
283
- should "call session.data if session.to_hash is undefined" do
284
- hash_data = {:key => :value}
288
+ should "call session.data if session.to_hash is undefined" do
289
+ hash_data = {:key => :value}
285
290
 
286
- session = ActionController::TestSession.new
287
- ActionController::TestSession.stubs(:new).returns(session)
288
- session.stubs(:data).returns(hash_data)
289
- if session.respond_to?(:to_hash)
290
- class << session
291
- undef to_hash
291
+ session = ActionController::TestSession.new
292
+ ActionController::TestSession.stubs(:new).returns(session)
293
+ session.stubs(:data).returns(hash_data)
294
+ if session.respond_to?(:to_hash)
295
+ class << session
296
+ undef to_hash
297
+ end
292
298
  end
293
- end
294
299
 
295
- process_action_with_automatic_notification
296
- assert_received(session, :to_hash) { |expect| expect.never }
297
- assert_received(session, :data) { |expect| expect.at_least_once }
298
- assert_caught_and_sent
300
+ process_action_with_automatic_notification
301
+ assert_received(session, :to_hash) { |expect| expect.never }
302
+ assert_received(session, :data) { |expect| expect.at_least_once }
303
+ assert_caught_and_sent
304
+ end
299
305
  end
306
+
307
+ rescue LoadError
308
+ nil
300
309
  end