jordan-brough-hoptoad_notifier 2.3.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/CHANGELOG +161 -0
- data/INSTALL +25 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +384 -0
- data/Rakefile +217 -0
- data/SUPPORTED_RAILS_VERSIONS +9 -0
- data/TESTING.rdoc +8 -0
- data/generators/hoptoad/hoptoad_generator.rb +63 -0
- data/generators/hoptoad/lib/insert_commands.rb +34 -0
- data/generators/hoptoad/lib/rake_commands.rb +24 -0
- data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
- data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
- data/generators/hoptoad/templates/initializer.rb +6 -0
- data/lib/hoptoad_notifier.rb +148 -0
- data/lib/hoptoad_notifier/backtrace.rb +99 -0
- data/lib/hoptoad_notifier/capistrano.rb +20 -0
- data/lib/hoptoad_notifier/configuration.rb +232 -0
- data/lib/hoptoad_notifier/notice.rb +318 -0
- data/lib/hoptoad_notifier/rack.rb +40 -0
- data/lib/hoptoad_notifier/rails.rb +37 -0
- data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +29 -0
- data/lib/hoptoad_notifier/rails/controller_methods.rb +63 -0
- data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
- data/lib/hoptoad_notifier/rails3_tasks.rb +90 -0
- data/lib/hoptoad_notifier/railtie.rb +23 -0
- data/lib/hoptoad_notifier/sender.rb +63 -0
- data/lib/hoptoad_notifier/tasks.rb +97 -0
- data/lib/hoptoad_notifier/version.rb +3 -0
- data/lib/hoptoad_tasks.rb +44 -0
- data/lib/rails/generators/hoptoad/hoptoad_generator.rb +69 -0
- data/lib/templates/rescue.erb +91 -0
- data/rails/init.rb +1 -0
- data/script/integration_test.rb +38 -0
- data/test/backtrace_test.rb +118 -0
- data/test/catcher_test.rb +324 -0
- data/test/configuration_test.rb +208 -0
- data/test/helper.rb +239 -0
- data/test/hoptoad_tasks_test.rb +152 -0
- data/test/logger_test.rb +85 -0
- data/test/notice_test.rb +443 -0
- data/test/notifier_test.rb +222 -0
- data/test/rack_test.rb +58 -0
- data/test/rails_initializer_test.rb +36 -0
- data/test/sender_test.rb +123 -0
- metadata +205 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'active_support'
|
4
|
+
|
5
|
+
# Capistrano tasks for notifying Hoptoad of deploys
|
6
|
+
module HoptoadTasks
|
7
|
+
|
8
|
+
# Alerts Hoptoad of a deploy.
|
9
|
+
#
|
10
|
+
# @param [Hash] opts Data about the deploy that is set to Hoptoad
|
11
|
+
#
|
12
|
+
# @option opts [String] :rails_env Environment of the deploy (production, staging)
|
13
|
+
# @option opts [String] :scm_revision The given revision/sha that is being deployed
|
14
|
+
# @option opts [String] :scm_repository Address of your repository to help with code lookups
|
15
|
+
# @option opts [String] :local_username Who is deploying
|
16
|
+
def self.deploy(opts = {})
|
17
|
+
if HoptoadNotifier.configuration.api_key.blank?
|
18
|
+
puts "I don't seem to be configured with an API key. Please check your configuration."
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
|
22
|
+
if opts[:rails_env].blank?
|
23
|
+
puts "I don't know to which Rails environment you are deploying (use the TO=production option)."
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
|
27
|
+
params = {'api_key' => opts.delete(:api_key) ||
|
28
|
+
HoptoadNotifier.configuration.api_key}
|
29
|
+
opts.each {|k,v| params["deploy[#{k}]"] = v }
|
30
|
+
|
31
|
+
url = URI.parse("http://#{HoptoadNotifier.configuration.host || 'hoptoadapp.com'}/deploys.txt")
|
32
|
+
|
33
|
+
proxy = Net::HTTP.Proxy(HoptoadNotifier.configuration.proxy_host,
|
34
|
+
HoptoadNotifier.configuration.proxy_port,
|
35
|
+
HoptoadNotifier.configuration.proxy_user,
|
36
|
+
HoptoadNotifier.configuration.proxy_pass)
|
37
|
+
|
38
|
+
response = proxy.post_form(url, params)
|
39
|
+
|
40
|
+
puts response.body
|
41
|
+
return Net::HTTPSuccess === response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class HoptoadGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
class_option :api_key, :aliases => "-k", :type => :string, :desc => "Your Hoptoad API key"
|
6
|
+
class_option :heroku, :type => :boolean, :desc => "Use the Heroku addon to provide your Hoptoad API key"
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
@_hoptoad_source_root ||= File.expand_path("../../../../../generators/hoptoad/templates", __FILE__)
|
10
|
+
end
|
11
|
+
|
12
|
+
def install
|
13
|
+
ensure_api_key_was_configured
|
14
|
+
ensure_plugin_is_not_present
|
15
|
+
append_capistrano_hook
|
16
|
+
generate_initializer unless api_key_configured?
|
17
|
+
test_hoptoad
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def ensure_api_key_was_configured
|
23
|
+
if !options[:api_key] && !options[:heroku] && !api_key_configured?
|
24
|
+
puts "Must pass --api-key or --heroku or create config/initializers/hoptoad.rb"
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ensure_plugin_is_not_present
|
30
|
+
if plugin_is_present?
|
31
|
+
puts "You must first remove the hoptoad_notifier plugin. Please run: script/plugin remove hoptoad_notifier"
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def append_capistrano_hook
|
37
|
+
if File.exists?('config/deploy.rb') && File.exists?('Capfile')
|
38
|
+
append_file('config/deploy.rb', <<-HOOK)
|
39
|
+
|
40
|
+
require 'config/boot'
|
41
|
+
require 'hoptoad_notifier/capistrano'
|
42
|
+
HOOK
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def api_key_expression
|
47
|
+
s = if options[:api_key]
|
48
|
+
"'#{options[:api_key]}'"
|
49
|
+
elsif options[:heroku]
|
50
|
+
"ENV['HOPTOAD_API_KEY']"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_initializer
|
55
|
+
template 'initializer.rb', 'config/initializers/hoptoad.rb'
|
56
|
+
end
|
57
|
+
|
58
|
+
def api_key_configured?
|
59
|
+
File.exists?('config/initializers/hoptoad.rb')
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_hoptoad
|
63
|
+
puts run("rake hoptoad:test --trace")
|
64
|
+
end
|
65
|
+
|
66
|
+
def plugin_is_present?
|
67
|
+
File.exists?('vendor/plugins/hoptoad_notifier')
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
var Hoptoad = {
|
3
|
+
host : <%= host.to_json %>,
|
4
|
+
api_key : <%= api_key.to_json %>,
|
5
|
+
notice : <%= notice.to_json %>,
|
6
|
+
message : 'This error exists in production!',
|
7
|
+
|
8
|
+
initialize: function() {
|
9
|
+
if (this.initialized) {
|
10
|
+
return;
|
11
|
+
} else {
|
12
|
+
this.initialized = true;
|
13
|
+
}
|
14
|
+
|
15
|
+
var data = [];
|
16
|
+
|
17
|
+
for (var key in this.notice) {
|
18
|
+
data[data.length] = 'notice[' + key + ']=' + this.notice[key];
|
19
|
+
}
|
20
|
+
|
21
|
+
data[data.length] = 'notice[api_key]=' + this.api_key;
|
22
|
+
data[data.length] = 'callback=Hoptoad.onSuccess';
|
23
|
+
data[data.length] = '_=' + (new Date()).getTime();
|
24
|
+
|
25
|
+
var head = document.getElementsByTagName('head')[0];
|
26
|
+
var done = false;
|
27
|
+
|
28
|
+
var
|
29
|
+
script = document.createElement('script');
|
30
|
+
script.src = 'http://' + this.host + '/notices_api/v1/notices/exist?' +
|
31
|
+
data.join('&');
|
32
|
+
script.type = 'text/javascript';
|
33
|
+
script.onload = script.onreadystatechange = function(){
|
34
|
+
if (!done && (!this.readyState ||
|
35
|
+
this.readyState == 'loaded' || this.readyState == 'complete')) {
|
36
|
+
|
37
|
+
done = true;
|
38
|
+
|
39
|
+
// Handle memory leak in IE. (via jQuery)
|
40
|
+
script.onload = script.onreadystatechange = null;
|
41
|
+
head.removeChild(script);
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
45
|
+
head.appendChild(script);
|
46
|
+
},
|
47
|
+
|
48
|
+
onSuccess: function(error) {
|
49
|
+
var body = document.getElementsByTagName('body')[0];
|
50
|
+
var text = document.createTextNode(this.message);
|
51
|
+
var element = document.createElement('a');
|
52
|
+
|
53
|
+
element.id = 'hoptoad';
|
54
|
+
element.href = 'http://' + error.subdomain + '.' + this.host +
|
55
|
+
'/projects/' + error.project_id + '/errors/' + error.id;
|
56
|
+
element.appendChild(text);
|
57
|
+
|
58
|
+
body.insertBefore(element, body.firstChild);
|
59
|
+
|
60
|
+
var h1 = document.getElementsByTagName('h1')[0];
|
61
|
+
var pre = document.getElementsByTagName('pre')[0];
|
62
|
+
var wrapper = document.createElement('div');
|
63
|
+
|
64
|
+
wrapper.id = 'wrapper';
|
65
|
+
wrapper.appendChild(h1);
|
66
|
+
wrapper.appendChild(pre);
|
67
|
+
|
68
|
+
body.insertBefore(wrapper, body.children[1]);
|
69
|
+
}
|
70
|
+
};
|
71
|
+
|
72
|
+
window.onload = function() {
|
73
|
+
Hoptoad.initialize.apply(Hoptoad);
|
74
|
+
};
|
75
|
+
</script>
|
76
|
+
|
77
|
+
<style type="text/css">
|
78
|
+
#hoptoad {
|
79
|
+
background: #FFF url(http://hoptoadapp.com/images/fell-off-the-toad.gif) no-repeat top right;
|
80
|
+
color: #F00;
|
81
|
+
padding: 45px 101px 45px 12px;
|
82
|
+
font-size: 14px;
|
83
|
+
font-weight: bold;
|
84
|
+
display: block;
|
85
|
+
float: right;
|
86
|
+
}
|
87
|
+
|
88
|
+
#wrapper {
|
89
|
+
padding-right: 360px;
|
90
|
+
}
|
91
|
+
</style>
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'hoptoad_notifier/rails'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
RAILS_ENV = "production"
|
7
|
+
RAILS_ROOT = FileUtils.pwd
|
8
|
+
RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
|
9
|
+
|
10
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
|
+
require 'hoptoad_notifier'
|
12
|
+
require 'rails/init'
|
13
|
+
|
14
|
+
fail "Please supply an API Key as the first argument" if ARGV.empty?
|
15
|
+
|
16
|
+
host = ARGV[1]
|
17
|
+
host ||= "hoptoadapp.com"
|
18
|
+
|
19
|
+
secure = (ARGV[2] == "secure")
|
20
|
+
|
21
|
+
exception = begin
|
22
|
+
raise "Testing hoptoad notifier with secure = #{secure}. If you can see this, it works."
|
23
|
+
rescue => foo
|
24
|
+
foo
|
25
|
+
end
|
26
|
+
|
27
|
+
HoptoadNotifier.configure do |config|
|
28
|
+
config.secure = secure
|
29
|
+
config.host = host
|
30
|
+
config.api_key = ARGV.first
|
31
|
+
end
|
32
|
+
puts "Configuration:"
|
33
|
+
HoptoadNotifier.configuration.to_hash.each do |key, value|
|
34
|
+
puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
|
35
|
+
end
|
36
|
+
puts "Sending #{secure ? "" : "in"}secure notification to project with key #{ARGV.first}"
|
37
|
+
HoptoadNotifier.notify(exception)
|
38
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class BacktraceTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "parse a backtrace into lines" do
|
6
|
+
array = [
|
7
|
+
"app/models/user.rb:13:in `magic'",
|
8
|
+
"app/controllers/users_controller.rb:8:in `index'"
|
9
|
+
]
|
10
|
+
|
11
|
+
backtrace = HoptoadNotifier::Backtrace.parse(array)
|
12
|
+
|
13
|
+
line = backtrace.lines.first
|
14
|
+
assert_equal '13', line.number
|
15
|
+
assert_equal 'app/models/user.rb', line.file
|
16
|
+
assert_equal 'magic', line.method
|
17
|
+
|
18
|
+
line = backtrace.lines.last
|
19
|
+
assert_equal '8', line.number
|
20
|
+
assert_equal 'app/controllers/users_controller.rb', line.file
|
21
|
+
assert_equal 'index', line.method
|
22
|
+
end
|
23
|
+
|
24
|
+
should "be equal with equal lines" do
|
25
|
+
one = build_backtrace_array
|
26
|
+
two = one.dup
|
27
|
+
assert_equal one, two
|
28
|
+
|
29
|
+
assert_equal HoptoadNotifier::Backtrace.parse(one), HoptoadNotifier::Backtrace.parse(two)
|
30
|
+
end
|
31
|
+
|
32
|
+
should "parse massive one-line exceptions into multiple lines" do
|
33
|
+
original_backtrace = HoptoadNotifier::Backtrace.
|
34
|
+
parse(["one:1:in `one'\n two:2:in `two'\n three:3:in `three`"])
|
35
|
+
expected_backtrace = HoptoadNotifier::Backtrace.
|
36
|
+
parse(["one:1:in `one'", "two:2:in `two'", "three:3:in `three`"])
|
37
|
+
|
38
|
+
assert_equal expected_backtrace, original_backtrace
|
39
|
+
end
|
40
|
+
|
41
|
+
context "with a project root" do
|
42
|
+
setup do
|
43
|
+
@project_root = '/some/path'
|
44
|
+
HoptoadNotifier.configure {|config| config.project_root = @project_root }
|
45
|
+
end
|
46
|
+
|
47
|
+
teardown do
|
48
|
+
reset_config
|
49
|
+
end
|
50
|
+
|
51
|
+
should "filter out the project root" do
|
52
|
+
backtrace_with_root = HoptoadNotifier::Backtrace.parse(
|
53
|
+
["#{@project_root}/app/models/user.rb:7:in `latest'",
|
54
|
+
"#{@project_root}/app/controllers/users_controller.rb:13:in `index'",
|
55
|
+
"/lib/something.rb:41:in `open'"],
|
56
|
+
:filters => default_filters)
|
57
|
+
backtrace_without_root = HoptoadNotifier::Backtrace.parse(
|
58
|
+
["[PROJECT_ROOT]/app/models/user.rb:7:in `latest'",
|
59
|
+
"[PROJECT_ROOT]/app/controllers/users_controller.rb:13:in `index'",
|
60
|
+
"/lib/something.rb:41:in `open'"])
|
61
|
+
|
62
|
+
assert_equal backtrace_without_root, backtrace_with_root
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with a blank project root" do
|
67
|
+
setup do
|
68
|
+
HoptoadNotifier.configure {|config| config.project_root = '' }
|
69
|
+
end
|
70
|
+
|
71
|
+
teardown do
|
72
|
+
reset_config
|
73
|
+
end
|
74
|
+
|
75
|
+
should "not filter line numbers with respect to any project root" do
|
76
|
+
backtrace = ["/app/models/user.rb:7:in `latest'",
|
77
|
+
"/app/controllers/users_controller.rb:13:in `index'",
|
78
|
+
"/lib/something.rb:41:in `open'"]
|
79
|
+
|
80
|
+
backtrace_with_root =
|
81
|
+
HoptoadNotifier::Backtrace.parse(backtrace, :filters => default_filters)
|
82
|
+
|
83
|
+
backtrace_without_root =
|
84
|
+
HoptoadNotifier::Backtrace.parse(backtrace)
|
85
|
+
|
86
|
+
assert_equal backtrace_without_root, backtrace_with_root
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
should "remove notifier trace" do
|
91
|
+
inside_notifier = ['lib/hoptoad_notifier.rb:13:in `voodoo`']
|
92
|
+
outside_notifier = ['users_controller:8:in `index`']
|
93
|
+
|
94
|
+
without_inside = HoptoadNotifier::Backtrace.parse(outside_notifier)
|
95
|
+
with_inside = HoptoadNotifier::Backtrace.parse(inside_notifier + outside_notifier,
|
96
|
+
:filters => default_filters)
|
97
|
+
|
98
|
+
assert_equal without_inside, with_inside
|
99
|
+
end
|
100
|
+
|
101
|
+
should "run filters on the backtrace" do
|
102
|
+
filters = [lambda { |line| line.sub('foo', 'bar') }]
|
103
|
+
input = HoptoadNotifier::Backtrace.parse(["foo:13:in `one'", "baz:14:in `two'"],
|
104
|
+
:filters => filters)
|
105
|
+
expected = HoptoadNotifier::Backtrace.parse(["bar:13:in `one'", "baz:14:in `two'"])
|
106
|
+
assert_equal expected, input
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_backtrace_array
|
110
|
+
["app/models/user.rb:13:in `magic'",
|
111
|
+
"app/controllers/users_controller.rb:8:in `index'"]
|
112
|
+
end
|
113
|
+
|
114
|
+
def default_filters
|
115
|
+
HoptoadNotifier::Configuration::DEFAULT_BACKTRACE_FILTERS
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class ActionControllerCatcherTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include DefinesConstants
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
reset_config
|
10
|
+
HoptoadNotifier.sender = CollectingSender.new
|
11
|
+
define_constant('RAILS_ROOT', '/path/to/rails/root')
|
12
|
+
end
|
13
|
+
|
14
|
+
def ignore(exception_class)
|
15
|
+
HoptoadNotifier.configuration.ignore << exception_class
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_controller_class(&definition)
|
19
|
+
returning Class.new(ActionController::Base) do |klass|
|
20
|
+
klass.__send__(:include, HoptoadNotifier::Rails::ActionControllerCatcher)
|
21
|
+
klass.class_eval(&definition) if definition
|
22
|
+
define_constant('HoptoadTestController', klass)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def assert_sent_hash(hash, xpath)
|
27
|
+
hash.each do |key, value|
|
28
|
+
element_xpath = "#{xpath}/var[@key = '#{key}']"
|
29
|
+
if value.respond_to?(:to_hash)
|
30
|
+
assert_sent_hash value.to_hash, element_xpath
|
31
|
+
else
|
32
|
+
assert_sent_element value, element_xpath
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_sent_element(value, xpath)
|
38
|
+
assert_valid_node last_sent_notice_document, xpath, stringify_array_elements(value).to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def stringify_array_elements(data)
|
42
|
+
if data.respond_to?(:to_ary)
|
43
|
+
data.collect do |value|
|
44
|
+
stringify_array_elements(value)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
data.to_s
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def assert_sent_request_info_for(request)
|
52
|
+
params = request.parameters.to_hash
|
53
|
+
assert_sent_hash params, '/notice/request/params'
|
54
|
+
assert_sent_element params['controller'], '/notice/request/component'
|
55
|
+
assert_sent_element params['action'], '/notice/request/action'
|
56
|
+
assert_sent_element url_from_request(request), '/notice/request/url'
|
57
|
+
assert_sent_hash request.env, '/notice/request/cgi-data'
|
58
|
+
end
|
59
|
+
|
60
|
+
def url_from_request(request)
|
61
|
+
url = "#{request.protocol}#{request.host}"
|
62
|
+
|
63
|
+
unless [80, 443].include?(request.port)
|
64
|
+
url << ":#{request.port}"
|
65
|
+
end
|
66
|
+
|
67
|
+
url << request.request_uri
|
68
|
+
url
|
69
|
+
end
|
70
|
+
|
71
|
+
def sender
|
72
|
+
HoptoadNotifier.sender
|
73
|
+
end
|
74
|
+
|
75
|
+
def last_sent_notice_xml
|
76
|
+
sender.collected.last
|
77
|
+
end
|
78
|
+
|
79
|
+
def last_sent_notice_document
|
80
|
+
assert_not_nil xml = last_sent_notice_xml, "No xml was sent"
|
81
|
+
Nokogiri::XML.parse(xml)
|
82
|
+
end
|
83
|
+
|
84
|
+
def process_action(opts = {}, &action)
|
85
|
+
opts[:request] ||= ActionController::TestRequest.new
|
86
|
+
opts[:response] ||= ActionController::TestResponse.new
|
87
|
+
klass = build_controller_class do
|
88
|
+
cattr_accessor :local
|
89
|
+
define_method(:index, &action)
|
90
|
+
def local_request?
|
91
|
+
local
|
92
|
+
end
|
93
|
+
end
|
94
|
+
if opts[:filters]
|
95
|
+
klass.filter_parameter_logging *opts[:filters]
|
96
|
+
end
|
97
|
+
if opts[:user_agent]
|
98
|
+
if opts[:request].respond_to?(:user_agent=)
|
99
|
+
opts[:request].user_agent = opts[:user_agent]
|
100
|
+
else
|
101
|
+
opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
if opts[:port]
|
105
|
+
opts[:request].port = opts[:port]
|
106
|
+
end
|
107
|
+
klass.consider_all_requests_local = opts[:all_local]
|
108
|
+
klass.local = opts[:local]
|
109
|
+
controller = klass.new
|
110
|
+
controller.stubs(:rescue_action_in_public_without_hoptoad)
|
111
|
+
opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
|
112
|
+
opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
|
113
|
+
controller.process(opts[:request], opts[:response])
|
114
|
+
controller
|
115
|
+
end
|
116
|
+
|
117
|
+
def process_action_with_manual_notification(args = {})
|
118
|
+
process_action(args) do
|
119
|
+
notify_hoptoad(:error_message => 'fail')
|
120
|
+
# Rails will raise a template error if we don't render something
|
121
|
+
render :nothing => true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def process_action_with_automatic_notification(args = {})
|
126
|
+
process_action(args) { raise "Hello" }
|
127
|
+
end
|
128
|
+
|
129
|
+
should "deliver notices from exceptions raised in public requests" do
|
130
|
+
process_action_with_automatic_notification
|
131
|
+
assert_caught_and_sent
|
132
|
+
end
|
133
|
+
|
134
|
+
should "not deliver notices from exceptions in local requests" do
|
135
|
+
process_action_with_automatic_notification(:local => true)
|
136
|
+
assert_caught_and_not_sent
|
137
|
+
end
|
138
|
+
|
139
|
+
should "not deliver notices from exceptions when all requests are local" do
|
140
|
+
process_action_with_automatic_notification(:all_local => true)
|
141
|
+
assert_caught_and_not_sent
|
142
|
+
end
|
143
|
+
|
144
|
+
should "not deliver notices from actions that don't raise" do
|
145
|
+
controller = process_action { render :text => 'Hello' }
|
146
|
+
assert_caught_and_not_sent
|
147
|
+
assert_equal 'Hello', controller.response.body
|
148
|
+
end
|
149
|
+
|
150
|
+
should "not deliver ignored exceptions raised by actions" do
|
151
|
+
ignore(RuntimeError)
|
152
|
+
process_action_with_automatic_notification
|
153
|
+
assert_caught_and_not_sent
|
154
|
+
end
|
155
|
+
|
156
|
+
should "deliver ignored exception raised manually" do
|
157
|
+
ignore(RuntimeError)
|
158
|
+
process_action_with_manual_notification
|
159
|
+
assert_caught_and_sent
|
160
|
+
end
|
161
|
+
|
162
|
+
should "deliver manually sent notices in public requests" do
|
163
|
+
process_action_with_manual_notification
|
164
|
+
assert_caught_and_sent
|
165
|
+
end
|
166
|
+
|
167
|
+
should "not deliver manually sent notices in local requests" do
|
168
|
+
process_action_with_manual_notification(:local => true)
|
169
|
+
assert_caught_and_not_sent
|
170
|
+
end
|
171
|
+
|
172
|
+
should "not deliver manually sent notices when all requests are local" do
|
173
|
+
process_action_with_manual_notification(:all_local => true)
|
174
|
+
assert_caught_and_not_sent
|
175
|
+
end
|
176
|
+
|
177
|
+
should "continue with default behavior after delivering an exception" do
|
178
|
+
controller = process_action_with_automatic_notification(:public => true)
|
179
|
+
# TODO: can we test this without stubbing?
|
180
|
+
assert_received(controller, :rescue_action_in_public_without_hoptoad)
|
181
|
+
end
|
182
|
+
|
183
|
+
should "not create actions from Hoptoad methods" do
|
184
|
+
controller = build_controller_class.new
|
185
|
+
assert_equal [], HoptoadNotifier::Rails::ActionControllerCatcher.instance_methods
|
186
|
+
end
|
187
|
+
|
188
|
+
should "ignore exceptions when user agent is being ignored by regular expression" do
|
189
|
+
HoptoadNotifier.configuration.ignore_user_agent_only = [/Ignored/]
|
190
|
+
process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored')
|
191
|
+
assert_caught_and_not_sent
|
192
|
+
end
|
193
|
+
|
194
|
+
should "ignore exceptions when user agent is being ignored by string" do
|
195
|
+
HoptoadNotifier.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
|
196
|
+
process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent')
|
197
|
+
assert_caught_and_not_sent
|
198
|
+
end
|
199
|
+
|
200
|
+
should "not ignore exceptions when user agent is not being ignored" do
|
201
|
+
HoptoadNotifier.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
|
202
|
+
process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent')
|
203
|
+
assert_caught_and_sent
|
204
|
+
end
|
205
|
+
|
206
|
+
should "send session data for manual notifications" do
|
207
|
+
data = { 'one' => 'two' }
|
208
|
+
process_action_with_manual_notification(:session => data)
|
209
|
+
assert_sent_hash data, "/notice/request/session"
|
210
|
+
end
|
211
|
+
|
212
|
+
should "send session data for automatic notification" do
|
213
|
+
data = { 'one' => 'two' }
|
214
|
+
process_action_with_automatic_notification(:session => data)
|
215
|
+
assert_sent_hash data, "/notice/request/session"
|
216
|
+
end
|
217
|
+
|
218
|
+
should "send request data for manual notification" do
|
219
|
+
params = { 'controller' => "hoptoad_test", 'action' => "index" }
|
220
|
+
controller = process_action_with_manual_notification(:params => params)
|
221
|
+
assert_sent_request_info_for controller.request
|
222
|
+
end
|
223
|
+
|
224
|
+
should "send request data for manual notification with non-standard port" do
|
225
|
+
params = { 'controller' => "hoptoad_test", 'action' => "index" }
|
226
|
+
controller = process_action_with_manual_notification(:params => params, :port => 81)
|
227
|
+
assert_sent_request_info_for controller.request
|
228
|
+
end
|
229
|
+
|
230
|
+
should "send request data for automatic notification" do
|
231
|
+
params = { 'controller' => "hoptoad_test", 'action' => "index" }
|
232
|
+
controller = process_action_with_automatic_notification(:params => params)
|
233
|
+
assert_sent_request_info_for controller.request
|
234
|
+
end
|
235
|
+
|
236
|
+
should "send request data for automatic notification with non-standard port" do
|
237
|
+
params = { 'controller' => "hoptoad_test", 'action' => "index" }
|
238
|
+
controller = process_action_with_automatic_notification(:params => params, :port => 81)
|
239
|
+
assert_sent_request_info_for controller.request
|
240
|
+
end
|
241
|
+
|
242
|
+
should "use standard rails logging filters on params and env" do
|
243
|
+
filtered_params = { "abc" => "123",
|
244
|
+
"def" => "456",
|
245
|
+
"ghi" => "[FILTERED]" }
|
246
|
+
ENV['ghi'] = 'abc'
|
247
|
+
filtered_env = { 'ghi' => '[FILTERED]' }
|
248
|
+
filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
|
249
|
+
|
250
|
+
process_action_with_automatic_notification(:filters => [:ghi, :request_method],
|
251
|
+
:params => { "abc" => "123",
|
252
|
+
"def" => "456",
|
253
|
+
"ghi" => "789" })
|
254
|
+
assert_sent_hash filtered_params, '/notice/request/params'
|
255
|
+
assert_sent_hash filtered_cgi, '/notice/request/cgi-data'
|
256
|
+
end
|
257
|
+
|
258
|
+
context "for a local error with development lookup enabled" do
|
259
|
+
setup do
|
260
|
+
HoptoadNotifier.configuration.development_lookup = true
|
261
|
+
HoptoadNotifier.stubs(:build_lookup_hash_for).returns({ :awesome => 2 })
|
262
|
+
|
263
|
+
@controller = process_action_with_automatic_notification(:local => true)
|
264
|
+
@response = @controller.response
|
265
|
+
end
|
266
|
+
|
267
|
+
should "append custom CSS and JS to response body for a local error" do
|
268
|
+
assert_match /text\/css/, @response.body
|
269
|
+
assert_match /text\/javascript/, @response.body
|
270
|
+
end
|
271
|
+
|
272
|
+
should "contain host, API key and notice JSON" do
|
273
|
+
assert_match HoptoadNotifier.configuration.host.to_json, @response.body
|
274
|
+
assert_match HoptoadNotifier.configuration.api_key.to_json, @response.body
|
275
|
+
assert_match ({ :awesome => 2 }).to_json, @response.body
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context "for a local error with development lookup disabled" do
|
280
|
+
setup do
|
281
|
+
HoptoadNotifier.configuration.development_lookup = false
|
282
|
+
|
283
|
+
@controller = process_action_with_automatic_notification(:local => true)
|
284
|
+
@response = @controller.response
|
285
|
+
end
|
286
|
+
|
287
|
+
should "not append custom CSS and JS to response for a local error" do
|
288
|
+
assert_no_match /text\/css/, @response.body
|
289
|
+
assert_no_match /text\/javascript/, @response.body
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
should "call session.to_hash if available" do
|
294
|
+
hash_data = {:key => :value}
|
295
|
+
|
296
|
+
session = ActionController::TestSession.new
|
297
|
+
ActionController::TestSession.stubs(:new).returns(session)
|
298
|
+
session.stubs(:to_hash).returns(hash_data)
|
299
|
+
|
300
|
+
process_action_with_automatic_notification
|
301
|
+
assert_received(session, :to_hash)
|
302
|
+
assert_received(session, :data) { |expect| expect.never }
|
303
|
+
assert_caught_and_sent
|
304
|
+
end
|
305
|
+
|
306
|
+
should "call session.data if session.to_hash is undefined" do
|
307
|
+
hash_data = {:key => :value}
|
308
|
+
|
309
|
+
session = ActionController::TestSession.new
|
310
|
+
ActionController::TestSession.stubs(:new).returns(session)
|
311
|
+
session.stubs(:data).returns(hash_data)
|
312
|
+
if session.respond_to?(:to_hash)
|
313
|
+
class << session
|
314
|
+
undef to_hash
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
process_action_with_automatic_notification
|
319
|
+
assert_received(session, :to_hash) { |expect| expect.never }
|
320
|
+
assert_received(session, :data) { |expect| expect.at_least_once }
|
321
|
+
assert_caught_and_sent
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|