honeybadger 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Appraisals +25 -1
- data/CHANGELOG.md +47 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -8
- data/MIT-LICENSE +1 -1
- data/README.md +117 -6
- data/Rakefile +15 -0
- data/features/rack.feature +2 -2
- data/features/rails.feature +79 -15
- data/features/step_definitions/metal_steps.rb +4 -4
- data/features/step_definitions/rack_steps.rb +0 -4
- data/features/step_definitions/rails_steps.rb +49 -32
- data/features/support/honeybadger_failure_shim.rb.template +5 -0
- data/features/support/rails.rb +17 -5
- data/gemfiles/rack.gemfile +8 -0
- data/gemfiles/rack.gemfile.lock +92 -0
- data/gemfiles/rails2.3.gemfile +1 -1
- data/gemfiles/rails2.3.gemfile.lock +6 -2
- data/gemfiles/rails3.0.gemfile +2 -1
- data/gemfiles/rails3.0.gemfile.lock +12 -3
- data/gemfiles/rails3.1.gemfile +2 -1
- data/gemfiles/rails3.1.gemfile.lock +12 -3
- data/gemfiles/rails3.2.gemfile +3 -2
- data/gemfiles/rails3.2.gemfile.lock +45 -36
- data/gemfiles/rails4.gemfile +9 -0
- data/gemfiles/rails4.gemfile.lock +174 -0
- data/gemfiles/rake.gemfile +8 -0
- data/gemfiles/rake.gemfile.lock +91 -0
- data/gemfiles/sinatra.gemfile +8 -0
- data/gemfiles/sinatra.gemfile.lock +91 -0
- data/generators/honeybadger/honeybadger_generator.rb +5 -5
- data/honeybadger.gemspec +12 -3
- data/lib/honeybadger.rb +7 -14
- data/lib/honeybadger/configuration.rb +10 -1
- data/lib/honeybadger/notice.rb +38 -20
- data/lib/honeybadger/rack.rb +0 -1
- data/lib/honeybadger/rails/controller_methods.rb +5 -4
- data/lib/honeybadger/rails3_tasks.rb +16 -4
- data/lib/honeybadger/sender.rb +10 -19
- data/lib/honeybadger/shared_tasks.rb +2 -3
- data/lib/honeybadger/tasks.rb +16 -9
- data/lib/honeybadger_tasks.rb +2 -3
- data/lib/rails/generators/honeybadger/honeybadger_generator.rb +5 -5
- data/test/test_helper.rb +2 -7
- data/test/unit/backtrace_test.rb +19 -19
- data/test/unit/configuration_test.rb +11 -5
- data/test/unit/notice_test.rb +21 -3
- data/test/unit/rails/action_controller_catcher_test.rb +250 -241
- data/test/unit/sender_test.rb +22 -2
- metadata +13 -19
data/lib/honeybadger/rack.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
data/lib/honeybadger/sender.rb
CHANGED
@@ -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
|
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})
|
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
|
-
|
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]
|
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
|
-
|
91
|
-
|
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
|
-
|
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
|
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
|
-
|
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']
|
data/lib/honeybadger/tasks.rb
CHANGED
@@ -8,7 +8,11 @@ namespace :honeybadger do
|
|
8
8
|
|
9
9
|
require 'action_controller/test_process'
|
10
10
|
|
11
|
-
|
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.
|
26
|
-
|
27
|
-
|
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.
|
data/lib/honeybadger_tasks.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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']
|
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
|
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
|
|
data/test/unit/backtrace_test.rb
CHANGED
@@ -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.
|
88
|
-
assert_match /\$:<</, @backtrace.lines.
|
89
|
-
assert_match /require/, @backtrace.lines.
|
90
|
-
assert_match /\n/, @backtrace.lines.
|
91
|
-
assert_match /begin/, @backtrace.lines.
|
92
|
-
|
93
|
-
assert_equal 5, @backtrace.lines.
|
94
|
-
assert_match /require/, @backtrace.lines.
|
95
|
-
assert_match /\n/, @backtrace.lines.
|
96
|
-
assert_match /begin/, @backtrace.lines.
|
97
|
-
assert_match /StandardError/, @backtrace.lines.
|
98
|
-
assert_match /rescue/, @backtrace.lines.
|
99
|
-
|
100
|
-
assert_equal 3, @backtrace.lines.
|
101
|
-
assert_match /rescue/, @backtrace.lines.
|
102
|
-
assert_match /Honeybadger/, @backtrace.lines.
|
103
|
-
assert_match /end/, @backtrace.lines.
|
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.
|
116
|
-
assert_equal backtrace.lines.
|
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
|
-
:
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
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
|
data/test/unit/notice_test.rb
CHANGED
@@ -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.
|
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
|
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
|
-
|
5
|
-
|
3
|
+
begin
|
4
|
+
require 'active_support'
|
5
|
+
require 'action_controller'
|
6
|
+
require 'action_controller/test_process'
|
7
|
+
require 'honeybadger/rails'
|
6
8
|
|
7
|
-
|
8
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
define_constant('
|
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
|
-
|
31
|
-
|
32
|
-
|
19
|
+
def ignore(exception_class)
|
20
|
+
Honeybadger.configuration.ignore << exception_class
|
21
|
+
end
|
33
22
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
73
|
-
|
74
|
-
end
|
70
|
+
def url_from_request(request)
|
71
|
+
url = "#{request.protocol}#{request.host}"
|
75
72
|
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
unless [80, 443].include?(request.port)
|
74
|
+
url << ":#{request.port}"
|
75
|
+
end
|
79
76
|
|
80
|
-
|
81
|
-
|
82
|
-
|
77
|
+
url << request.request_uri
|
78
|
+
url
|
79
|
+
end
|
83
80
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
81
|
+
def sender
|
82
|
+
Honeybadger.sender
|
83
|
+
end
|
88
84
|
|
89
|
-
|
90
|
-
|
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
|
-
|
100
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
137
|
+
def process_action_with_automatic_notification(args = {})
|
138
|
+
process_action(args) { raise "Hello" }
|
139
|
+
end
|
135
140
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
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
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
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
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
271
|
-
|
275
|
+
should "call session.to_hash if available" do
|
276
|
+
hash_data = {:key => :value}
|
272
277
|
|
273
|
-
|
274
|
-
|
275
|
-
|
278
|
+
session = ActionController::TestSession.new
|
279
|
+
ActionController::TestSession.stubs(:new).returns(session)
|
280
|
+
session.stubs(:to_hash).returns(hash_data)
|
276
281
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
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
|
-
|
284
|
-
|
288
|
+
should "call session.data if session.to_hash is undefined" do
|
289
|
+
hash_data = {:key => :value}
|
285
290
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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
|