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.
Files changed (45) hide show
  1. data/CHANGELOG +161 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +384 -0
  5. data/Rakefile +217 -0
  6. data/SUPPORTED_RAILS_VERSIONS +9 -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 +232 -0
  18. data/lib/hoptoad_notifier/notice.rb +318 -0
  19. data/lib/hoptoad_notifier/rack.rb +40 -0
  20. data/lib/hoptoad_notifier/rails.rb +37 -0
  21. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +29 -0
  22. data/lib/hoptoad_notifier/rails/controller_methods.rb +63 -0
  23. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  24. data/lib/hoptoad_notifier/rails3_tasks.rb +90 -0
  25. data/lib/hoptoad_notifier/railtie.rb +23 -0
  26. data/lib/hoptoad_notifier/sender.rb +63 -0
  27. data/lib/hoptoad_notifier/tasks.rb +97 -0
  28. data/lib/hoptoad_notifier/version.rb +3 -0
  29. data/lib/hoptoad_tasks.rb +44 -0
  30. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +69 -0
  31. data/lib/templates/rescue.erb +91 -0
  32. data/rails/init.rb +1 -0
  33. data/script/integration_test.rb +38 -0
  34. data/test/backtrace_test.rb +118 -0
  35. data/test/catcher_test.rb +324 -0
  36. data/test/configuration_test.rb +208 -0
  37. data/test/helper.rb +239 -0
  38. data/test/hoptoad_tasks_test.rb +152 -0
  39. data/test/logger_test.rb +85 -0
  40. data/test/notice_test.rb +443 -0
  41. data/test/notifier_test.rb +222 -0
  42. data/test/rack_test.rb +58 -0
  43. data/test/rails_initializer_test.rb +36 -0
  44. data/test/sender_test.rb +123 -0
  45. metadata +205 -0
@@ -0,0 +1,3 @@
1
+ module HoptoadNotifier
2
+ VERSION = "2.3.0".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 '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>
@@ -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