riscfuture-hoptoad_notifier 2.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/CHANGELOG +218 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +394 -0
  5. data/Rakefile +217 -0
  6. data/SUPPORTED_RAILS_VERSIONS +10 -0
  7. data/TESTING.rdoc +8 -0
  8. data/generators/hoptoad/hoptoad_generator.rb +63 -0
  9. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  10. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  11. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  12. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  13. data/generators/hoptoad/templates/initializer.rb +6 -0
  14. data/lib/hoptoad_notifier.rb +148 -0
  15. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  16. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  17. data/lib/hoptoad_notifier/configuration.rb +236 -0
  18. data/lib/hoptoad_notifier/notice.rb +334 -0
  19. data/lib/hoptoad_notifier/rack.rb +40 -0
  20. data/lib/hoptoad_notifier/rails.rb +39 -0
  21. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +29 -0
  22. data/lib/hoptoad_notifier/rails/controller_methods.rb +60 -0
  23. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  24. data/lib/hoptoad_notifier/rails/javascript_notifier.rb +43 -0
  25. data/lib/hoptoad_notifier/rails3_tasks.rb +91 -0
  26. data/lib/hoptoad_notifier/railtie.rb +29 -0
  27. data/lib/hoptoad_notifier/sender.rb +63 -0
  28. data/lib/hoptoad_notifier/tasks.rb +97 -0
  29. data/lib/hoptoad_notifier/version.rb +3 -0
  30. data/lib/hoptoad_tasks.rb +44 -0
  31. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +69 -0
  32. data/lib/templates/javascript_notifier.erb +6 -0
  33. data/lib/templates/rescue.erb +91 -0
  34. data/rails/init.rb +1 -0
  35. data/script/integration_test.rb +38 -0
  36. data/test/backtrace_test.rb +118 -0
  37. data/test/catcher_test.rb +329 -0
  38. data/test/configuration_test.rb +209 -0
  39. data/test/helper.rb +239 -0
  40. data/test/hoptoad_tasks_test.rb +152 -0
  41. data/test/logger_test.rb +85 -0
  42. data/test/notice_test.rb +457 -0
  43. data/test/notifier_test.rb +222 -0
  44. data/test/rack_test.rb +58 -0
  45. data/test/rails_initializer_test.rb +36 -0
  46. data/test/sender_test.rb +123 -0
  47. metadata +197 -0
@@ -0,0 +1,3 @@
1
+ module HoptoadNotifier
2
+ VERSION = "2.3.6".freeze
3
+ end
@@ -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 File.dirname(__FILE__) + '/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,6 @@
1
+ <script type="text/javascript" src="http<%= 's' if secure %>://<%= host %>/javascripts/notifier.js"></script>
2
+ <script type="text/javascript">
3
+ Hoptoad.setKey('<%= api_key %>');
4
+ Hoptoad.setHost('<%= host %>');
5
+ Hoptoad.setEnvironment('<%= environment %>');
6
+ </script>
@@ -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,329 @@
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 session and env" do
243
+ filtered_params = { "abc" => "123",
244
+ "def" => "456",
245
+ "ghi" => "[FILTERED]" }
246
+ filtered_session = { "abc" => "123",
247
+ "ghi" => "[FILTERED]" }
248
+ ENV['ghi'] = 'abc'
249
+ filtered_env = { 'ghi' => '[FILTERED]' }
250
+ filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
251
+
252
+ process_action_with_automatic_notification(:filters => [:ghi, :request_method],
253
+ :params => { "abc" => "123",
254
+ "def" => "456",
255
+ "ghi" => "789" },
256
+ :session => { "abc" => "123",
257
+ "ghi" => "789" })
258
+ assert_sent_hash filtered_params, '/notice/request/params'
259
+ assert_sent_hash filtered_cgi, '/notice/request/cgi-data'
260
+ assert_sent_hash filtered_session, '/notice/request/session'
261
+ end
262
+
263
+ context "for a local error with development lookup enabled" do
264
+ setup do
265
+ HoptoadNotifier.configuration.development_lookup = true
266
+ HoptoadNotifier.stubs(:build_lookup_hash_for).returns({ :awesome => 2 })
267
+
268
+ @controller = process_action_with_automatic_notification(:local => true)
269
+ @response = @controller.response
270
+ end
271
+
272
+ should "append custom CSS and JS to response body for a local error" do
273
+ assert_match /text\/css/, @response.body
274
+ assert_match /text\/javascript/, @response.body
275
+ end
276
+
277
+ should "contain host, API key and notice JSON" do
278
+ assert_match HoptoadNotifier.configuration.host.to_json, @response.body
279
+ assert_match HoptoadNotifier.configuration.api_key.to_json, @response.body
280
+ assert_match ({ :awesome => 2 }).to_json, @response.body
281
+ end
282
+ end
283
+
284
+ context "for a local error with development lookup disabled" do
285
+ setup do
286
+ HoptoadNotifier.configuration.development_lookup = false
287
+
288
+ @controller = process_action_with_automatic_notification(:local => true)
289
+ @response = @controller.response
290
+ end
291
+
292
+ should "not append custom CSS and JS to response for a local error" do
293
+ assert_no_match /text\/css/, @response.body
294
+ assert_no_match /text\/javascript/, @response.body
295
+ end
296
+ end
297
+
298
+ should "call session.to_hash if available" do
299
+ hash_data = {:key => :value}
300
+
301
+ session = ActionController::TestSession.new
302
+ ActionController::TestSession.stubs(:new).returns(session)
303
+ session.stubs(:to_hash).returns(hash_data)
304
+
305
+ process_action_with_automatic_notification
306
+ assert_received(session, :to_hash)
307
+ assert_received(session, :data) { |expect| expect.never }
308
+ assert_caught_and_sent
309
+ end
310
+
311
+ should "call session.data if session.to_hash is undefined" do
312
+ hash_data = {:key => :value}
313
+
314
+ session = ActionController::TestSession.new
315
+ ActionController::TestSession.stubs(:new).returns(session)
316
+ session.stubs(:data).returns(hash_data)
317
+ if session.respond_to?(:to_hash)
318
+ class << session
319
+ undef to_hash
320
+ end
321
+ end
322
+
323
+ process_action_with_automatic_notification
324
+ assert_received(session, :to_hash) { |expect| expect.never }
325
+ assert_received(session, :data) { |expect| expect.at_least_once }
326
+ assert_caught_and_sent
327
+ end
328
+
329
+ end