square-hoptoad_notifier 2.4.8

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 (52) hide show
  1. data/CHANGELOG +427 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.md +435 -0
  5. data/README_FOR_HEROKU_ADDON.md +93 -0
  6. data/Rakefile +227 -0
  7. data/SUPPORTED_RAILS_VERSIONS +10 -0
  8. data/TESTING.rdoc +8 -0
  9. data/generators/hoptoad/hoptoad_generator.rb +88 -0
  10. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  11. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  12. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  13. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  14. data/generators/hoptoad/templates/initializer.rb +6 -0
  15. data/lib/hoptoad_notifier.rb +153 -0
  16. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  17. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  18. data/lib/hoptoad_notifier/configuration.rb +242 -0
  19. data/lib/hoptoad_notifier/notice.rb +337 -0
  20. data/lib/hoptoad_notifier/rack.rb +42 -0
  21. data/lib/hoptoad_notifier/rails.rb +41 -0
  22. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +30 -0
  23. data/lib/hoptoad_notifier/rails/controller_methods.rb +68 -0
  24. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  25. data/lib/hoptoad_notifier/rails/javascript_notifier.rb +42 -0
  26. data/lib/hoptoad_notifier/rails3_tasks.rb +82 -0
  27. data/lib/hoptoad_notifier/railtie.rb +32 -0
  28. data/lib/hoptoad_notifier/sender.rb +83 -0
  29. data/lib/hoptoad_notifier/shared_tasks.rb +29 -0
  30. data/lib/hoptoad_notifier/tasks.rb +83 -0
  31. data/lib/hoptoad_notifier/user_informer.rb +23 -0
  32. data/lib/hoptoad_notifier/version.rb +3 -0
  33. data/lib/hoptoad_tasks.rb +44 -0
  34. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +94 -0
  35. data/lib/templates/javascript_notifier.erb +13 -0
  36. data/lib/templates/rescue.erb +91 -0
  37. data/rails/init.rb +1 -0
  38. data/script/integration_test.rb +38 -0
  39. data/test/backtrace_test.rb +118 -0
  40. data/test/catcher_test.rb +331 -0
  41. data/test/configuration_test.rb +216 -0
  42. data/test/helper.rb +248 -0
  43. data/test/hoptoad_tasks_test.rb +152 -0
  44. data/test/javascript_notifier_test.rb +52 -0
  45. data/test/logger_test.rb +85 -0
  46. data/test/notice_test.rb +448 -0
  47. data/test/notifier_test.rb +222 -0
  48. data/test/rack_test.rb +58 -0
  49. data/test/rails_initializer_test.rb +36 -0
  50. data/test/sender_test.rb +161 -0
  51. data/test/user_informer_test.rb +29 -0
  52. metadata +225 -0
@@ -0,0 +1,152 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'rubygems'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/hoptoad_tasks'
5
+ require 'fakeweb'
6
+
7
+ FakeWeb.allow_net_connect = false
8
+
9
+ class HoptoadTasksTest < Test::Unit::TestCase
10
+ def successful_response(body = "")
11
+ response = Net::HTTPSuccess.new('1.2', '200', 'OK')
12
+ response.stubs(:body).returns(body)
13
+ return response
14
+ end
15
+
16
+ def unsuccessful_response(body = "")
17
+ response = Net::HTTPClientError.new('1.2', '200', 'OK')
18
+ response.stubs(:body).returns(body)
19
+ return response
20
+ end
21
+
22
+ context "being quiet" do
23
+ setup { HoptoadTasks.stubs(:puts) }
24
+
25
+ context "in a configured project" do
26
+ setup { HoptoadNotifier.configure { |config| config.api_key = "1234123412341234" } }
27
+
28
+ context "on deploy({})" do
29
+ setup { @output = HoptoadTasks.deploy({}) }
30
+
31
+ before_should "complain about missing rails env" do
32
+ HoptoadTasks.expects(:puts).with(regexp_matches(/rails environment/i))
33
+ end
34
+
35
+ should "return false" do
36
+ assert !@output
37
+ end
38
+ end
39
+
40
+ context "given an optional HTTP proxy and valid options" do
41
+ setup do
42
+ @response = stub("response", :body => "stub body")
43
+ @http_proxy = stub("proxy", :post_form => @response)
44
+
45
+ Net::HTTP.expects(:Proxy).
46
+ with(HoptoadNotifier.configuration.proxy_host,
47
+ HoptoadNotifier.configuration.proxy_port,
48
+ HoptoadNotifier.configuration.proxy_user,
49
+ HoptoadNotifier.configuration.proxy_pass).
50
+ returns(@http_proxy)
51
+
52
+ @options = { :rails_env => "staging" }
53
+ end
54
+
55
+ context "on deploy(options)" do
56
+ setup do
57
+ @output = HoptoadTasks.deploy(@options)
58
+ end
59
+
60
+ before_should "post to http://hoptoadapp.com/deploys.txt" do
61
+ URI.stubs(:parse).with('http://hoptoadapp.com/deploys.txt').returns(:uri)
62
+ @http_proxy.expects(:post_form).with(:uri, kind_of(Hash)).returns(successful_response)
63
+ end
64
+
65
+ before_should "use the project api key" do
66
+ @http_proxy.expects(:post_form).
67
+ with(kind_of(URI), has_entries('api_key' => "1234123412341234")).
68
+ returns(successful_response)
69
+ end
70
+
71
+ before_should "use send the rails_env param" do
72
+ @http_proxy.expects(:post_form).
73
+ with(kind_of(URI), has_entries("deploy[rails_env]" => "staging")).
74
+ returns(successful_response)
75
+ end
76
+
77
+ [:local_username, :scm_repository, :scm_revision].each do |key|
78
+ before_should "use send the #{key} param if it's passed in." do
79
+ @options[key] = "value"
80
+ @http_proxy.expects(:post_form).
81
+ with(kind_of(URI), has_entries("deploy[#{key}]" => "value")).
82
+ returns(successful_response)
83
+ end
84
+ end
85
+
86
+ before_should "use the :api_key param if it's passed in." do
87
+ @options[:api_key] = "value"
88
+ @http_proxy.expects(:post_form).
89
+ with(kind_of(URI), has_entries("api_key" => "value")).
90
+ returns(successful_response)
91
+ end
92
+
93
+ before_should "puts the response body on success" do
94
+ HoptoadTasks.expects(:puts).with("body")
95
+ @http_proxy.expects(:post_form).with(any_parameters).returns(successful_response('body'))
96
+ end
97
+
98
+ before_should "puts the response body on failure" do
99
+ HoptoadTasks.expects(:puts).with("body")
100
+ @http_proxy.expects(:post_form).with(any_parameters).returns(unsuccessful_response('body'))
101
+ end
102
+
103
+ should "return false on failure", :before => lambda {
104
+ @http_proxy.expects(:post_form).with(any_parameters).returns(unsuccessful_response('body'))
105
+ } do
106
+ assert !@output
107
+ end
108
+
109
+ should "return true on success", :before => lambda {
110
+ @http_proxy.expects(:post_form).with(any_parameters).returns(successful_response('body'))
111
+ } do
112
+ assert @output
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ context "in a configured project with custom host" do
119
+ setup do
120
+ HoptoadNotifier.configure do |config|
121
+ config.api_key = "1234123412341234"
122
+ config.host = "custom.host"
123
+ end
124
+ end
125
+
126
+ context "on deploy(:rails_env => 'staging')" do
127
+ setup { @output = HoptoadTasks.deploy(:rails_env => "staging") }
128
+
129
+ before_should "post to the custom host" do
130
+ URI.stubs(:parse).with('http://custom.host/deploys.txt').returns(:uri)
131
+ Net::HTTP.expects(:post_form).with(:uri, kind_of(Hash)).returns(successful_response)
132
+ end
133
+ end
134
+ end
135
+
136
+ context "when not configured" do
137
+ setup { HoptoadNotifier.configure { |config| config.api_key = "" } }
138
+
139
+ context "on deploy(:rails_env => 'staging')" do
140
+ setup { @output = HoptoadTasks.deploy(:rails_env => "staging") }
141
+
142
+ before_should "complain about missing api key" do
143
+ HoptoadTasks.expects(:puts).with(regexp_matches(/api key/i))
144
+ end
145
+
146
+ should "return false" do
147
+ assert !@output
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'hoptoad_notifier/rails/javascript_notifier'
3
+ require 'ostruct'
4
+
5
+ class JavascriptNotifierTest < Test::Unit::TestCase
6
+ module FakeRenderer
7
+ def javascript_tag(text)
8
+ "<script>#{text}</script>"
9
+ end
10
+ def escape_javascript(text)
11
+ "ESC#{text}ESC"
12
+ end
13
+ end
14
+
15
+ class FakeController
16
+ def self.helper_method(*args)
17
+ end
18
+
19
+ include HoptoadNotifier::Rails::JavascriptNotifier
20
+
21
+ def action_name
22
+ "action"
23
+ end
24
+
25
+ def controller_name
26
+ "controller"
27
+ end
28
+
29
+ def request
30
+ @request ||= OpenStruct.new
31
+ end
32
+
33
+ def render_to_string(options)
34
+ context = OpenStruct.new(options[:locals])
35
+ context.extend(FakeRenderer)
36
+ context.instance_eval do
37
+ erb = ERB.new(IO.read(options[:file]))
38
+ erb.result(binding)
39
+ end
40
+ end
41
+ end
42
+
43
+ should "make sure escape_javacript is called on the request.url" do
44
+ HoptoadNotifier.configure do
45
+ end
46
+ controller = FakeController.new
47
+ controller.request.url = "bad_javascript"
48
+ assert controller.send(:hoptoad_javascript_notifier)['"ESCbad_javascriptESC"']
49
+ assert ! controller.send(:hoptoad_javascript_notifier)['"bad_javascript"']
50
+ end
51
+ end
52
+
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class LoggerTest < Test::Unit::TestCase
4
+ def stub_http(response, body = nil)
5
+ response.stubs(:body => body) if body
6
+ @http = stub(:post => response,
7
+ :read_timeout= => nil,
8
+ :open_timeout= => nil,
9
+ :use_ssl= => nil)
10
+ Net::HTTP.stubs(:new).returns(@http)
11
+ end
12
+
13
+ def send_notice
14
+ HoptoadNotifier.sender.send_to_hoptoad('data')
15
+ end
16
+
17
+ def stub_verbose_log
18
+ HoptoadNotifier.stubs(:write_verbose_log)
19
+ end
20
+
21
+ def assert_logged(expected)
22
+ assert_received(HoptoadNotifier, :write_verbose_log) do |expect|
23
+ expect.with {|actual| actual =~ expected }
24
+ end
25
+ end
26
+
27
+ def assert_not_logged(expected)
28
+ assert_received(HoptoadNotifier, :write_verbose_log) do |expect|
29
+ expect.with {|actual| actual =~ expected }.never
30
+ end
31
+ end
32
+
33
+ def configure
34
+ HoptoadNotifier.configure { |config| }
35
+ end
36
+
37
+ should "report that notifier is ready when configured" do
38
+ stub_verbose_log
39
+ configure
40
+ assert_logged /Notifier (.*) ready/
41
+ end
42
+
43
+ should "not report that notifier is ready when internally configured" do
44
+ stub_verbose_log
45
+ HoptoadNotifier.configure(true) { |config| }
46
+ assert_not_logged /.*/
47
+ end
48
+
49
+ should "print environment info a successful notification without a body" do
50
+ reset_config
51
+ stub_verbose_log
52
+ stub_http(Net::HTTPSuccess)
53
+ send_notice
54
+ assert_logged /Environment Info:/
55
+ assert_not_logged /Response from Hoptoad:/
56
+ end
57
+
58
+ should "print environment info on a failed notification without a body" do
59
+ reset_config
60
+ stub_verbose_log
61
+ stub_http(Net::HTTPError)
62
+ send_notice
63
+ assert_logged /Environment Info:/
64
+ assert_not_logged /Response from Hoptoad:/
65
+ end
66
+
67
+ should "print environment info and response on a success with a body" do
68
+ reset_config
69
+ stub_verbose_log
70
+ stub_http(Net::HTTPSuccess, 'test')
71
+ send_notice
72
+ assert_logged /Environment Info:/
73
+ assert_logged /Response from Hoptoad:/
74
+ end
75
+
76
+ should "print environment info and response on a failure with a body" do
77
+ reset_config
78
+ stub_verbose_log
79
+ stub_http(Net::HTTPError, 'test')
80
+ send_notice
81
+ assert_logged /Environment Info:/
82
+ assert_logged /Response from Hoptoad:/
83
+ end
84
+
85
+ end
@@ -0,0 +1,448 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class NoticeTest < Test::Unit::TestCase
4
+
5
+ include DefinesConstants
6
+
7
+ def configure
8
+ HoptoadNotifier::Configuration.new.tap do |config|
9
+ config.api_key = 'abc123def456'
10
+ end
11
+ end
12
+
13
+ def build_notice(args = {})
14
+ configuration = args.delete(:configuration) || configure
15
+ HoptoadNotifier::Notice.new(configuration.merge(args))
16
+ end
17
+
18
+ def stub_request(attrs = {})
19
+ stub('request', { :parameters => { 'one' => 'two' },
20
+ :protocol => 'http',
21
+ :host => 'some.host',
22
+ :request_uri => '/some/uri',
23
+ :session => { :to_hash => { 'a' => 'b' } },
24
+ :env => { 'three' => 'four' } }.update(attrs))
25
+ end
26
+
27
+ should "set the api key" do
28
+ api_key = 'key'
29
+ notice = build_notice(:api_key => api_key)
30
+ assert_equal api_key, notice.api_key
31
+ end
32
+
33
+ should "accept a project root" do
34
+ project_root = '/path/to/project'
35
+ notice = build_notice(:project_root => project_root)
36
+ assert_equal project_root, notice.project_root
37
+ end
38
+
39
+ should "accept a component" do
40
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
41
+ end
42
+
43
+ should "alias the component as controller" do
44
+ assert_equal 'users_controller', build_notice(:controller => 'users_controller').component
45
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
46
+ end
47
+
48
+ should "accept a action" do
49
+ assert_equal 'index', build_notice(:action => 'index').action
50
+ end
51
+
52
+ should "accept a url" do
53
+ url = 'http://some.host/uri'
54
+ notice = build_notice(:url => url)
55
+ assert_equal url, notice.url
56
+ end
57
+
58
+ should "accept a backtrace from an exception or hash" do
59
+ array = ["user.rb:34:in `crazy'"]
60
+ exception = build_exception
61
+ exception.set_backtrace array
62
+ backtrace = HoptoadNotifier::Backtrace.parse(array)
63
+ notice_from_exception = build_notice(:exception => exception)
64
+
65
+
66
+ assert_equal backtrace,
67
+ notice_from_exception.backtrace,
68
+ "backtrace was not correctly set from an exception"
69
+
70
+ notice_from_hash = build_notice(:backtrace => array)
71
+ assert_equal backtrace,
72
+ notice_from_hash.backtrace,
73
+ "backtrace was not correctly set from a hash"
74
+ end
75
+
76
+ should "pass its backtrace filters for parsing" do
77
+ backtrace_array = ['my/file/backtrace:3']
78
+ exception = build_exception
79
+ exception.set_backtrace(backtrace_array)
80
+ HoptoadNotifier::Backtrace.expects(:parse).with(backtrace_array, {:filters => 'foo'})
81
+
82
+ notice = HoptoadNotifier::Notice.new({:exception => exception, :backtrace_filters => 'foo'})
83
+ end
84
+
85
+ should "set the error class from an exception or hash" do
86
+ assert_accepts_exception_attribute :error_class do |exception|
87
+ exception.class.name
88
+ end
89
+ end
90
+
91
+ should "set the error message from an exception or hash" do
92
+ assert_accepts_exception_attribute :error_message do |exception|
93
+ "#{exception.class.name}: #{exception.message}"
94
+ end
95
+ end
96
+
97
+ should "accept parameters from a request or hash" do
98
+ parameters = { 'one' => 'two' }
99
+ notice_from_hash = build_notice(:parameters => parameters)
100
+ assert_equal notice_from_hash.parameters, parameters
101
+ end
102
+
103
+ should "accept session data from a session[:data] hash" do
104
+ data = { 'one' => 'two' }
105
+ notice = build_notice(:session => { :data => data })
106
+ assert_equal data, notice.session_data
107
+ end
108
+
109
+ should "accept session data from a session_data hash" do
110
+ data = { 'one' => 'two' }
111
+ notice = build_notice(:session_data => data)
112
+ assert_equal data, notice.session_data
113
+ end
114
+
115
+ should "accept an environment name" do
116
+ assert_equal 'development', build_notice(:environment_name => 'development').environment_name
117
+ end
118
+
119
+ should "accept CGI data from a hash" do
120
+ data = { 'string' => 'value' }
121
+ notice = build_notice(:cgi_data => data)
122
+ assert_equal data, notice.cgi_data, "should take CGI data from a hash"
123
+ end
124
+
125
+ should "accept notifier information" do
126
+ params = { :notifier_name => 'a name for a notifier',
127
+ :notifier_version => '1.0.5',
128
+ :notifier_url => 'http://notifiers.r.us/download' }
129
+ notice = build_notice(params)
130
+ assert_equal params[:notifier_name], notice.notifier_name
131
+ assert_equal params[:notifier_version], notice.notifier_version
132
+ assert_equal params[:notifier_url], notice.notifier_url
133
+ end
134
+
135
+ should "set sensible defaults without an exception" do
136
+ backtrace = HoptoadNotifier::Backtrace.parse(build_backtrace_array)
137
+ notice = build_notice(:backtrace => build_backtrace_array)
138
+
139
+ assert_equal 'Notification', notice.error_message
140
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
141
+ assert_equal({}, notice.parameters)
142
+ assert_equal({}, notice.session_data)
143
+ end
144
+
145
+ should "use the caller as the backtrace for an exception without a backtrace" do
146
+ filters = HoptoadNotifier::Configuration.new.backtrace_filters
147
+ backtrace = HoptoadNotifier::Backtrace.parse(caller, :filters => filters)
148
+ notice = build_notice(:exception => StandardError.new('error'), :backtrace => nil)
149
+
150
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
151
+ end
152
+
153
+ should "convert unserializable objects to strings" do
154
+ assert_serializes_hash(:parameters)
155
+ assert_serializes_hash(:cgi_data)
156
+ assert_serializes_hash(:session_data)
157
+ end
158
+
159
+ should "filter parameters" do
160
+ assert_filters_hash(:parameters)
161
+ end
162
+
163
+ should "filter cgi data" do
164
+ assert_filters_hash(:cgi_data)
165
+ end
166
+
167
+ should "filter session" do
168
+ assert_filters_hash(:session_data)
169
+ end
170
+
171
+ should "remove rack.request.form_vars" do
172
+ original = {
173
+ "rack.request.form_vars" => "story%5Btitle%5D=The+TODO+label",
174
+ "abc" => "123"
175
+ }
176
+
177
+ notice = build_notice(:cgi_data => original)
178
+ assert_equal({"abc" => "123"}, notice.cgi_data)
179
+ end
180
+
181
+ context "a Notice turned into XML" do
182
+ setup do
183
+ HoptoadNotifier.configure do |config|
184
+ config.api_key = "1234567890"
185
+ end
186
+
187
+ @exception = build_exception
188
+
189
+ @notice = build_notice({
190
+ :notifier_name => 'a name',
191
+ :notifier_version => '1.2.3',
192
+ :notifier_url => 'http://some.url/path',
193
+ :exception => @exception,
194
+ :controller => "controller",
195
+ :action => "action",
196
+ :url => "http://url.com",
197
+ :parameters => { "paramskey" => "paramsvalue",
198
+ "nestparentkey" => { "nestkey" => "nestvalue" } },
199
+ :session_data => { "sessionkey" => "sessionvalue" },
200
+ :cgi_data => { "cgikey" => "cgivalue" },
201
+ :project_root => "RAILS_ROOT",
202
+ :environment_name => "RAILS_ENV"
203
+ })
204
+
205
+ @xml = @notice.to_xml
206
+
207
+ @document = Nokogiri::XML::Document.parse(@xml)
208
+ end
209
+
210
+ should "validate against the XML schema" do
211
+ assert_valid_notice_document @document
212
+ end
213
+
214
+ should "serialize a Notice to XML when sent #to_xml" do
215
+ assert_valid_node(@document, "//api-key", @notice.api_key)
216
+
217
+ assert_valid_node(@document, "//notifier/name", @notice.notifier_name)
218
+ assert_valid_node(@document, "//notifier/version", @notice.notifier_version)
219
+ assert_valid_node(@document, "//notifier/url", @notice.notifier_url)
220
+
221
+ assert_valid_node(@document, "//error/class", @notice.error_class)
222
+ assert_valid_node(@document, "//error/message", @notice.error_message)
223
+
224
+ assert_valid_node(@document, "//error/backtrace/line/@number", @notice.backtrace.lines.first.number)
225
+ assert_valid_node(@document, "//error/backtrace/line/@file", @notice.backtrace.lines.first.file)
226
+ assert_valid_node(@document, "//error/backtrace/line/@method", @notice.backtrace.lines.first.method)
227
+
228
+ assert_valid_node(@document, "//request/url", @notice.url)
229
+ assert_valid_node(@document, "//request/component", @notice.controller)
230
+ assert_valid_node(@document, "//request/action", @notice.action)
231
+
232
+ assert_valid_node(@document, "//request/params/var/@key", "paramskey")
233
+ assert_valid_node(@document, "//request/params/var", "paramsvalue")
234
+ assert_valid_node(@document, "//request/params/var/@key", "nestparentkey")
235
+ assert_valid_node(@document, "//request/params/var/var/@key", "nestkey")
236
+ assert_valid_node(@document, "//request/params/var/var", "nestvalue")
237
+ assert_valid_node(@document, "//request/session/var/@key", "sessionkey")
238
+ assert_valid_node(@document, "//request/session/var", "sessionvalue")
239
+ assert_valid_node(@document, "//request/cgi-data/var/@key", "cgikey")
240
+ assert_valid_node(@document, "//request/cgi-data/var", "cgivalue")
241
+
242
+ assert_valid_node(@document, "//server-environment/project-root", "RAILS_ROOT")
243
+ assert_valid_node(@document, "//server-environment/environment-name", "RAILS_ENV")
244
+ end
245
+ end
246
+
247
+ should "not send empty request data" do
248
+ notice = build_notice
249
+ assert_nil notice.url
250
+ assert_nil notice.controller
251
+ assert_nil notice.action
252
+
253
+ xml = notice.to_xml
254
+ document = Nokogiri::XML.parse(xml)
255
+ assert_nil document.at('//request/url')
256
+ assert_nil document.at('//request/component')
257
+ assert_nil document.at('//request/action')
258
+
259
+ assert_valid_notice_document document
260
+ end
261
+
262
+ %w(url controller action).each do |var|
263
+ should "send a request if #{var} is present" do
264
+ notice = build_notice(var.to_sym => 'value')
265
+ xml = notice.to_xml
266
+ document = Nokogiri::XML.parse(xml)
267
+ assert_not_nil document.at('//request')
268
+ end
269
+ end
270
+
271
+ %w(parameters cgi_data session_data).each do |var|
272
+ should "send a request if #{var} is present" do
273
+ notice = build_notice(var.to_sym => { 'key' => 'value' })
274
+ xml = notice.to_xml
275
+ document = Nokogiri::XML.parse(xml)
276
+ assert_not_nil document.at('//request')
277
+ end
278
+ end
279
+
280
+ should "not ignore an exception not matching ignore filters" do
281
+ notice = build_notice(:error_class => 'ArgumentError',
282
+ :ignore => ['Argument'],
283
+ :ignore_by_filters => [lambda { |notice| false }])
284
+ assert !notice.ignore?
285
+ end
286
+
287
+ should "ignore an exception with a matching error class" do
288
+ notice = build_notice(:error_class => 'ArgumentError',
289
+ :ignore => [ArgumentError])
290
+ assert notice.ignore?
291
+ end
292
+
293
+ should "ignore an exception with a matching error class name" do
294
+ notice = build_notice(:error_class => 'ArgumentError',
295
+ :ignore => ['ArgumentError'])
296
+ assert notice.ignore?
297
+ end
298
+
299
+ should "ignore an exception with a matching filter" do
300
+ filter = lambda {|notice| notice.error_class == 'ArgumentError' }
301
+ notice = build_notice(:error_class => 'ArgumentError',
302
+ :ignore_by_filters => [filter])
303
+ assert notice.ignore?
304
+ end
305
+
306
+ should "not raise without an ignore list" do
307
+ notice = build_notice(:ignore => nil, :ignore_by_filters => nil)
308
+ assert_nothing_raised do
309
+ notice.ignore?
310
+ end
311
+ end
312
+
313
+ ignored_error_classes = %w(
314
+ ActiveRecord::RecordNotFound
315
+ AbstractController::ActionNotFound
316
+ ActionController::RoutingError
317
+ ActionController::InvalidAuthenticityToken
318
+ CGI::Session::CookieStore::TamperedWithCookie
319
+ ActionController::UnknownAction
320
+ )
321
+
322
+ ignored_error_classes.each do |ignored_error_class|
323
+ should "ignore #{ignored_error_class} error by default" do
324
+ notice = build_notice(:error_class => ignored_error_class)
325
+ assert notice.ignore?
326
+ end
327
+ end
328
+
329
+ should "act like a hash" do
330
+ notice = build_notice(:error_message => 'some message')
331
+ assert_equal notice.error_message, notice[:error_message]
332
+ end
333
+
334
+ should "return params on notice[:request][:params]" do
335
+ params = { 'one' => 'two' }
336
+ notice = build_notice(:parameters => params)
337
+ assert_equal params, notice[:request][:params]
338
+ end
339
+
340
+ should "ensure #to_hash is called on objects that support it" do
341
+ assert_nothing_raised do
342
+ build_notice(:session => { :object => stub(:to_hash => {}) })
343
+ end
344
+ end
345
+
346
+ should "extract data from a rack environment hash" do
347
+ url = "https://subdomain.happylane.com:100/test/file.rb?var=value&var2=value2"
348
+ parameters = { 'var' => 'value', 'var2' => 'value2' }
349
+ env = Rack::MockRequest.env_for(url)
350
+
351
+ notice = build_notice(:rack_env => env)
352
+
353
+ assert_equal url, notice.url
354
+ assert_equal parameters, notice.parameters
355
+ assert_equal 'GET', notice.cgi_data['REQUEST_METHOD']
356
+ end
357
+
358
+ should "extract data from a rack environment hash with action_dispatch info" do
359
+ params = { 'controller' => 'users', 'action' => 'index', 'id' => '7' }
360
+ env = Rack::MockRequest.env_for('/', { 'action_dispatch.request.parameters' => params })
361
+
362
+ notice = build_notice(:rack_env => env)
363
+
364
+ assert_equal params, notice.parameters
365
+ assert_equal params['controller'], notice.component
366
+ assert_equal params['action'], notice.action
367
+ end
368
+
369
+ should "extract session data from a rack environment" do
370
+ session_data = { 'something' => 'some value' }
371
+ env = Rack::MockRequest.env_for('/', 'rack.session' => session_data)
372
+
373
+ notice = build_notice(:rack_env => env)
374
+
375
+ assert_equal session_data, notice.session_data
376
+ end
377
+
378
+ should "prefer passed session data to rack session data" do
379
+ session_data = { 'something' => 'some value' }
380
+ env = Rack::MockRequest.env_for('/')
381
+
382
+ notice = build_notice(:rack_env => env, :session_data => session_data)
383
+
384
+ assert_equal session_data, notice.session_data
385
+ end
386
+
387
+ def assert_accepts_exception_attribute(attribute, args = {}, &block)
388
+ exception = build_exception
389
+ block ||= lambda { exception.send(attribute) }
390
+ value = block.call(exception)
391
+
392
+ notice_from_exception = build_notice(args.merge(:exception => exception))
393
+
394
+ assert_equal notice_from_exception.send(attribute),
395
+ value,
396
+ "#{attribute} was not correctly set from an exception"
397
+
398
+ notice_from_hash = build_notice(args.merge(attribute => value))
399
+ assert_equal notice_from_hash.send(attribute),
400
+ value,
401
+ "#{attribute} was not correctly set from a hash"
402
+ end
403
+
404
+ def assert_serializes_hash(attribute)
405
+ [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object|
406
+ hash = {
407
+ :strange_object => object,
408
+ :sub_hash => {
409
+ :sub_object => object
410
+ },
411
+ :array => [object]
412
+ }
413
+ notice = build_notice(attribute => hash)
414
+ hash = notice.send(attribute)
415
+ assert_equal object.to_s, hash[:strange_object], "objects should be serialized"
416
+ assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept"
417
+ assert_equal object.to_s, hash[:sub_hash][:sub_object], "subhash members should be serialized"
418
+ assert_kind_of Array, hash[:array], "arrays should be kept"
419
+ assert_equal object.to_s, hash[:array].first, "array members should be serialized"
420
+ end
421
+ end
422
+
423
+ def assert_valid_notice_document(document)
424
+ xsd_path = File.join(File.dirname(__FILE__), "hoptoad_2_0.xsd")
425
+ schema = Nokogiri::XML::Schema.new(IO.read(xsd_path))
426
+ errors = schema.validate(document)
427
+ assert errors.empty?, errors.collect{|e| e.message }.join
428
+ end
429
+
430
+ def assert_filters_hash(attribute)
431
+ filters = ["abc", :def]
432
+ original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' } }
433
+ filtered = { 'abc' => "[FILTERED]",
434
+ 'def' => "[FILTERED]",
435
+ 'ghi' => "789",
436
+ 'nested' => { 'abc' => '[FILTERED]' } }
437
+
438
+ notice = build_notice(:params_filters => filters, attribute => original)
439
+
440
+ assert_equal(filtered,
441
+ notice.send(attribute))
442
+ end
443
+
444
+ def build_backtrace_array
445
+ ["app/models/user.rb:13:in `magic'",
446
+ "app/controllers/users_controller.rb:8:in `index'"]
447
+ end
448
+ end