honeybadger 1.0.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 (65) hide show
  1. data/Gemfile +13 -0
  2. data/Gemfile.lock +114 -0
  3. data/Guardfile +5 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +271 -0
  6. data/Rakefile +261 -0
  7. data/SUPPORTED_RAILS_VERSIONS +26 -0
  8. data/TESTING.md +33 -0
  9. data/features/metal.feature +18 -0
  10. data/features/rack.feature +56 -0
  11. data/features/rails.feature +211 -0
  12. data/features/rake.feature +27 -0
  13. data/features/sinatra.feature +29 -0
  14. data/features/step_definitions/file_steps.rb +10 -0
  15. data/features/step_definitions/metal_steps.rb +23 -0
  16. data/features/step_definitions/rack_steps.rb +23 -0
  17. data/features/step_definitions/rails_application_steps.rb +394 -0
  18. data/features/step_definitions/rake_steps.rb +17 -0
  19. data/features/support/env.rb +17 -0
  20. data/features/support/honeybadger_shim.rb.template +8 -0
  21. data/features/support/rails.rb +201 -0
  22. data/features/support/rake/Rakefile +68 -0
  23. data/features/support/terminal.rb +107 -0
  24. data/generators/honeybadger/honeybadger_generator.rb +94 -0
  25. data/generators/honeybadger/lib/insert_commands.rb +34 -0
  26. data/generators/honeybadger/lib/rake_commands.rb +24 -0
  27. data/generators/honeybadger/templates/capistrano_hook.rb +6 -0
  28. data/generators/honeybadger/templates/honeybadger_tasks.rake +25 -0
  29. data/generators/honeybadger/templates/initializer.rb +6 -0
  30. data/honeybadger.gemspec +109 -0
  31. data/lib/honeybadger.rb +162 -0
  32. data/lib/honeybadger/backtrace.rb +123 -0
  33. data/lib/honeybadger/capistrano.rb +43 -0
  34. data/lib/honeybadger/configuration.rb +273 -0
  35. data/lib/honeybadger/notice.rb +314 -0
  36. data/lib/honeybadger/rack.rb +55 -0
  37. data/lib/honeybadger/rails.rb +34 -0
  38. data/lib/honeybadger/rails/action_controller_catcher.rb +30 -0
  39. data/lib/honeybadger/rails/controller_methods.rb +69 -0
  40. data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +29 -0
  41. data/lib/honeybadger/rails3_tasks.rb +84 -0
  42. data/lib/honeybadger/railtie.rb +45 -0
  43. data/lib/honeybadger/rake_handler.rb +65 -0
  44. data/lib/honeybadger/sender.rb +120 -0
  45. data/lib/honeybadger/shared_tasks.rb +36 -0
  46. data/lib/honeybadger/tasks.rb +82 -0
  47. data/lib/honeybadger_tasks.rb +65 -0
  48. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +99 -0
  49. data/rails/init.rb +1 -0
  50. data/resources/README.md +34 -0
  51. data/resources/ca-bundle.crt +3376 -0
  52. data/script/integration_test.rb +38 -0
  53. data/test/test_helper.rb +143 -0
  54. data/test/unit/backtrace_test.rb +180 -0
  55. data/test/unit/capistrano_test.rb +34 -0
  56. data/test/unit/configuration_test.rb +201 -0
  57. data/test/unit/honeybadger_tasks_test.rb +163 -0
  58. data/test/unit/logger_test.rb +72 -0
  59. data/test/unit/notice_test.rb +406 -0
  60. data/test/unit/notifier_test.rb +245 -0
  61. data/test/unit/rack_test.rb +56 -0
  62. data/test/unit/rails/action_controller_catcher_test.rb +300 -0
  63. data/test/unit/rails_test.rb +35 -0
  64. data/test/unit/sender_test.rb +257 -0
  65. metadata +315 -0
@@ -0,0 +1,163 @@
1
+ require 'test_helper'
2
+ require 'rubygems'
3
+
4
+ require File.expand_path('../../../lib/honeybadger_tasks', __FILE__)
5
+ require 'fakeweb'
6
+
7
+ FakeWeb.allow_net_connect = false
8
+
9
+ class HoneybadgerTasksTest < Honeybadger::UnitTest
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 { HoneybadgerTasks.stubs(:puts) }
24
+
25
+ context "in a configured project" do
26
+ setup { Honeybadger.configure { |config| config.api_key = "1234123412341234" } }
27
+
28
+ context "on deploy({})" do
29
+ setup { @output = HoneybadgerTasks.deploy({}) }
30
+
31
+ before_should "complain about missing rails env" do
32
+ HoneybadgerTasks.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", :request => @response)
44
+ @http_proxy_class = stub("proxy_class", :new => @http_proxy)
45
+ @post = stub("post", :set_form_data => nil)
46
+
47
+ @post.stubs(:[]=).with('X-API-Key', '1234123412341234').returns(true)
48
+
49
+ Net::HTTP.expects(:Proxy).
50
+ with(Honeybadger.configuration.proxy_host,
51
+ Honeybadger.configuration.proxy_port,
52
+ Honeybadger.configuration.proxy_user,
53
+ Honeybadger.configuration.proxy_pass).
54
+ returns(@http_proxy_class)
55
+ Net::HTTP::Post.expects(:new).with("/deploys.txt").returns(@post)
56
+
57
+ @options = { :rails_env => "staging", :dry_run => false }
58
+ end
59
+
60
+ context "performing a dry run" do
61
+ setup { @output = HoneybadgerTasks.deploy(@options.merge(:dry_run => true)) }
62
+
63
+ should "return true without performing any actual request" do
64
+ assert_equal true, @output
65
+ assert_received(@http_proxy, :request) do |expects|
66
+ expects.never
67
+ end
68
+ end
69
+ end
70
+
71
+ context "on deploy(options)" do
72
+ setup do
73
+ @output = HoneybadgerTasks.deploy(@options)
74
+ end
75
+
76
+ before_should "post to http://api.honeybadger.io:80/deploys.txt" do
77
+ @http_proxy_class.expects(:new).with("api.honeybadger.io", 80).returns(@http_proxy)
78
+ @post.expects(:set_form_data).with(kind_of(Hash))
79
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response)
80
+ end
81
+
82
+ before_should "use send the rails_env param" do
83
+ @post.expects(:set_form_data).
84
+ with(has_entries("deploy[rails_env]" => "staging"))
85
+ end
86
+
87
+ [:local_username, :scm_repository, :scm_revision].each do |key|
88
+ before_should "use send the #{key} param if it's passed in." do
89
+ @options[key] = "value"
90
+ @post.expects(:set_form_data).
91
+ with(has_entries("deploy[#{key}]" => "value"))
92
+ end
93
+ end
94
+
95
+ before_should "puts the response body on success" do
96
+ HoneybadgerTasks.expects(:puts).with("body")
97
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response('body'))
98
+ end
99
+
100
+ before_should "puts the response body on failure" do
101
+ HoneybadgerTasks.expects(:puts).with("body")
102
+ @http_proxy.expects(:request).with(any_parameters).returns(unsuccessful_response('body'))
103
+ end
104
+
105
+ should "return false on failure", :before => lambda {
106
+ @http_proxy.expects(:request).with(any_parameters).returns(unsuccessful_response('body'))
107
+ } do
108
+ assert !@output
109
+ end
110
+
111
+ should "return true on success", :before => lambda {
112
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response('body'))
113
+ } do
114
+ assert @output
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ context "in a configured project with custom host" do
121
+ setup do
122
+ Honeybadger.configure do |config|
123
+ config.api_key = "1234123412341234"
124
+ config.host = "custom.host"
125
+ end
126
+ end
127
+
128
+ context "on deploy(:rails_env => 'staging')" do
129
+ setup { @output = HoneybadgerTasks.deploy(:rails_env => "staging") }
130
+
131
+ before_should "post to the custom host" do
132
+ @post = stub("post", :set_form_data => nil)
133
+ @http_proxy = stub("proxy", :request => @response)
134
+
135
+ @post.stubs(:[]=).with('X-API-Key', '1234123412341234').returns(true)
136
+
137
+ @http_proxy_class = stub("proxy_class", :new => @http_proxy)
138
+ @http_proxy_class.expects(:new).with("custom.host", 80).returns(@http_proxy)
139
+ Net::HTTP.expects(:Proxy).with(any_parameters).returns(@http_proxy_class)
140
+ Net::HTTP::Post.expects(:new).with("/deploys.txt").returns(@post)
141
+ @post.expects(:set_form_data).with(kind_of(Hash))
142
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response)
143
+ end
144
+ end
145
+ end
146
+
147
+ context "when not configured" do
148
+ setup { Honeybadger.configure { |config| config.api_key = "" } }
149
+
150
+ context "on deploy(:rails_env => 'staging')" do
151
+ setup { @output = HoneybadgerTasks.deploy(:rails_env => "staging") }
152
+
153
+ before_should "complain about missing api key" do
154
+ HoneybadgerTasks.expects(:puts).with(regexp_matches(/api key/i))
155
+ end
156
+
157
+ should "return false" do
158
+ assert !@output
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+
3
+ class LoggerTest < Honeybadger::UnitTest
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
+ Honeybadger.sender.send_to_honeybadger('data')
15
+ end
16
+
17
+ def stub_verbose_log
18
+ Honeybadger.stubs(:write_verbose_log)
19
+ end
20
+
21
+ def configure
22
+ Honeybadger.configure { |config| }
23
+ end
24
+
25
+ should "report that notifier is ready when configured" do
26
+ stub_verbose_log
27
+ configure
28
+ assert_logged /Notifier (.*) ready/
29
+ end
30
+
31
+ should "not report that notifier is ready when internally configured" do
32
+ stub_verbose_log
33
+ Honeybadger.configure(true) { |config| }
34
+ assert_not_logged /.*/
35
+ end
36
+
37
+ should "print environment info a successful notification without a body" do
38
+ reset_config
39
+ stub_verbose_log
40
+ stub_http(Net::HTTPSuccess)
41
+ send_notice
42
+ assert_logged /Environment Info:/
43
+ assert_not_logged /Response from Honeybadger:/
44
+ end
45
+
46
+ should "print environment info on a failed notification without a body" do
47
+ reset_config
48
+ stub_verbose_log
49
+ stub_http(Net::HTTPError)
50
+ send_notice
51
+ assert_logged /Environment Info:/
52
+ assert_not_logged /Response from Honeybadger:/
53
+ end
54
+
55
+ should "print environment info and response on a success with a body" do
56
+ reset_config
57
+ stub_verbose_log
58
+ stub_http(Net::HTTPSuccess, '{}')
59
+ send_notice
60
+ assert_logged /Environment Info:/
61
+ assert_logged /Response from Honeybadger:/
62
+ end
63
+
64
+ should "print environment info and response on a failure with a body" do
65
+ reset_config
66
+ stub_verbose_log
67
+ stub_http(Net::HTTPError, '{}')
68
+ send_notice
69
+ assert_logged /Environment Info:/
70
+ assert_logged /Response from Honeybadger:/
71
+ end
72
+ end
@@ -0,0 +1,406 @@
1
+ require 'test_helper'
2
+ require 'json'
3
+
4
+ class NoticeTest < Honeybadger::UnitTest
5
+ include DefinesConstants
6
+
7
+ def configure
8
+ Honeybadger::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
+ Honeybadger::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 "generate json from as_json template" do
28
+ notice = build_notice
29
+ hash = {'foo' => 'bar'}
30
+ notice.expects(:as_json).once.returns(hash)
31
+ json = notice.to_json
32
+
33
+ payload = nil
34
+ assert_nothing_raised do
35
+ payload = JSON.parse(json)
36
+ end
37
+
38
+ assert_equal payload, hash
39
+ end
40
+
41
+ should "accept a project root" do
42
+ project_root = '/path/to/project'
43
+ notice = build_notice(:project_root => project_root)
44
+ assert_equal project_root, notice.project_root
45
+ end
46
+
47
+ should "accept a component" do
48
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
49
+ end
50
+
51
+ should "alias the component as controller" do
52
+ assert_equal 'users_controller', build_notice(:controller => 'users_controller').component
53
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
54
+ end
55
+
56
+ should "accept a action" do
57
+ assert_equal 'index', build_notice(:action => 'index').action
58
+ end
59
+
60
+ should "accept a url" do
61
+ url = 'http://some.host/uri'
62
+ notice = build_notice(:url => url)
63
+ assert_equal url, notice.url
64
+ end
65
+
66
+ should "set the host name" do
67
+ notice = build_notice
68
+ assert_equal hostname, notice.hostname
69
+ end
70
+
71
+ should "accept a backtrace from an exception or hash" do
72
+ array = ["user.rb:34:in `crazy'"]
73
+ exception = build_exception
74
+ exception.set_backtrace array
75
+ backtrace = Honeybadger::Backtrace.parse(array)
76
+ notice_from_exception = build_notice(:exception => exception)
77
+
78
+
79
+ assert_equal backtrace,
80
+ notice_from_exception.backtrace,
81
+ "backtrace was not correctly set from an exception"
82
+
83
+ notice_from_hash = build_notice(:backtrace => array)
84
+ assert_equal backtrace,
85
+ notice_from_hash.backtrace,
86
+ "backtrace was not correctly set from a hash"
87
+ end
88
+
89
+ should "pass its backtrace filters for parsing" do
90
+ backtrace_array = ['my/file/backtrace:3']
91
+ exception = build_exception
92
+ exception.set_backtrace(backtrace_array)
93
+ Honeybadger::Backtrace.expects(:parse).with(backtrace_array, {:filters => 'foo'})
94
+
95
+ notice = Honeybadger::Notice.new({:exception => exception, :backtrace_filters => 'foo'})
96
+ end
97
+
98
+ should "set the error class from an exception or hash" do
99
+ assert_accepts_exception_attribute :error_class do |exception|
100
+ exception.class.name
101
+ end
102
+ end
103
+
104
+ should "set the error message from an exception or hash" do
105
+ assert_accepts_exception_attribute :error_message do |exception|
106
+ "#{exception.class.name}: #{exception.message}"
107
+ end
108
+ end
109
+
110
+ should "accept parameters from a request or hash" do
111
+ parameters = { 'one' => 'two' }
112
+ notice_from_hash = build_notice(:parameters => parameters)
113
+ assert_equal notice_from_hash.parameters, parameters
114
+ end
115
+
116
+ should "accept session data from a session[:data] hash" do
117
+ data = { 'one' => 'two' }
118
+ notice = build_notice(:session => { :data => data })
119
+ assert_equal data, notice.session_data
120
+ end
121
+
122
+ should "accept session data from a session_data hash" do
123
+ data = { 'one' => 'two' }
124
+ notice = build_notice(:session_data => data)
125
+ assert_equal data, notice.session_data
126
+ end
127
+
128
+ should "accept an environment name" do
129
+ assert_equal 'development', build_notice(:environment_name => 'development').environment_name
130
+ end
131
+
132
+ should "accept CGI data from a hash" do
133
+ data = { 'string' => 'value' }
134
+ notice = build_notice(:cgi_data => data)
135
+ assert_equal data, notice.cgi_data, "should take CGI data from a hash"
136
+ end
137
+
138
+ should "accept notifier information" do
139
+ params = { :notifier_name => 'a name for a notifier',
140
+ :notifier_version => '1.0.5',
141
+ :notifier_url => 'http://notifiers.r.us/download' }
142
+ notice = build_notice(params)
143
+ assert_equal params[:notifier_name], notice.notifier_name
144
+ assert_equal params[:notifier_version], notice.notifier_version
145
+ assert_equal params[:notifier_url], notice.notifier_url
146
+ end
147
+
148
+ should "set sensible defaults without an exception" do
149
+ backtrace = Honeybadger::Backtrace.parse(build_backtrace_array)
150
+ notice = build_notice(:backtrace => build_backtrace_array)
151
+
152
+ assert_equal 'Notification', notice.error_message
153
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
154
+ assert_equal({}, notice.parameters)
155
+ assert_equal({}, notice.session_data)
156
+ end
157
+
158
+ should "use the caller as the backtrace for an exception without a backtrace" do
159
+ filters = Honeybadger::Configuration.new.backtrace_filters
160
+ backtrace = Honeybadger::Backtrace.parse(caller, :filters => filters)
161
+ notice = build_notice(:exception => StandardError.new('error'), :backtrace => nil)
162
+
163
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
164
+ end
165
+
166
+ should "convert unserializable objects to strings" do
167
+ assert_serializes_hash(:parameters)
168
+ assert_serializes_hash(:cgi_data)
169
+ assert_serializes_hash(:session_data)
170
+ end
171
+
172
+ should "filter parameters" do
173
+ assert_filters_hash(:parameters)
174
+ end
175
+
176
+ should "filter cgi data" do
177
+ assert_filters_hash(:cgi_data)
178
+ end
179
+
180
+ should "filter session" do
181
+ assert_filters_hash(:session_data)
182
+ end
183
+
184
+ should "remove rack.request.form_vars" do
185
+ original = {
186
+ "rack.request.form_vars" => "story%5Btitle%5D=The+TODO+label",
187
+ "abc" => "123"
188
+ }
189
+
190
+ notice = build_notice(:cgi_data => original)
191
+ assert_equal({"abc" => "123"}, notice.cgi_data)
192
+ end
193
+
194
+ should "not send empty request data" do
195
+ notice = build_notice
196
+ assert_nil notice.url
197
+ assert_nil notice.controller
198
+ assert_nil notice.action
199
+
200
+ json = notice.to_json
201
+ payload = JSON.parse(json)
202
+ assert_nil payload['request']['url']
203
+ assert_nil payload['request']['component']
204
+ assert_nil payload['request']['action']
205
+ end
206
+
207
+ %w(url controller action).each do |var|
208
+ should "send a request if #{var} is present" do
209
+ notice = build_notice(var.to_sym => 'value')
210
+ json = notice.to_json
211
+ payload = JSON.parse(json)
212
+ assert_not_nil payload['request']
213
+ end
214
+ end
215
+
216
+ %w(parameters cgi_data session_data).each do |var|
217
+ should "send a request if #{var} is present" do
218
+ notice = build_notice(var.to_sym => { 'key' => 'value' })
219
+ json = notice.to_json
220
+ payload = JSON.parse(json)
221
+ assert_not_nil payload['request']
222
+ end
223
+ end
224
+
225
+ should "not ignore an exception not matching ignore filters" do
226
+ notice = build_notice(:error_class => 'ArgumentError',
227
+ :ignore => ['Argument'],
228
+ :ignore_by_filters => [lambda { |notice| false }])
229
+ assert !notice.ignore?
230
+ end
231
+
232
+ should "ignore an exception with a matching error class" do
233
+ notice = build_notice(:error_class => 'ArgumentError',
234
+ :ignore => [ArgumentError])
235
+ assert notice.ignore?
236
+ end
237
+
238
+ should "ignore an exception with a matching error class name" do
239
+ notice = build_notice(:error_class => 'ArgumentError',
240
+ :ignore => ['ArgumentError'])
241
+ assert notice.ignore?
242
+ end
243
+
244
+ should "ignore an exception with a matching filter" do
245
+ filter = lambda {|notice| notice.error_class == 'ArgumentError' }
246
+ notice = build_notice(:error_class => 'ArgumentError',
247
+ :ignore_by_filters => [filter])
248
+ assert notice.ignore?
249
+ end
250
+
251
+ should "not raise without an ignore list" do
252
+ notice = build_notice(:ignore => nil, :ignore_by_filters => nil)
253
+ assert_nothing_raised do
254
+ notice.ignore?
255
+ end
256
+ end
257
+
258
+ ignored_error_classes = %w(
259
+ ActiveRecord::RecordNotFound
260
+ AbstractController::ActionNotFound
261
+ ActionController::RoutingError
262
+ ActionController::InvalidAuthenticityToken
263
+ CGI::Session::CookieStore::TamperedWithCookie
264
+ ActionController::UnknownAction
265
+ )
266
+
267
+ ignored_error_classes.each do |ignored_error_class|
268
+ should "ignore #{ignored_error_class} error by default" do
269
+ notice = build_notice(:error_class => ignored_error_class)
270
+ assert notice.ignore?
271
+ end
272
+ end
273
+
274
+ should "act like a hash" do
275
+ notice = build_notice(:error_message => 'some message')
276
+ assert_equal notice.error_message, notice[:error_message]
277
+ end
278
+
279
+ should "return params on notice[:request][:params]" do
280
+ params = { 'one' => 'two' }
281
+ notice = build_notice(:parameters => params)
282
+ assert_equal params, notice[:request][:params]
283
+ end
284
+
285
+ should "ensure #to_hash is called on objects that support it" do
286
+ assert_nothing_raised do
287
+ build_notice(:session => { :object => stub(:to_hash => {}) })
288
+ end
289
+ end
290
+
291
+
292
+ should "ensure #to_ary is called on objects that support it" do
293
+ assert_nothing_raised do
294
+ build_notice(:session => { :object => stub(:to_ary => {}) })
295
+ end
296
+ end
297
+
298
+ should "extract data from a rack environment hash" do
299
+ url = "https://subdomain.happylane.com:100/test/file.rb?var=value&var2=value2"
300
+ parameters = { 'var' => 'value', 'var2' => 'value2' }
301
+ env = Rack::MockRequest.env_for(url)
302
+
303
+ notice = build_notice(:rack_env => env)
304
+
305
+ assert_equal url, notice.url
306
+ assert_equal parameters, notice.parameters
307
+ assert_equal 'GET', notice.cgi_data['REQUEST_METHOD']
308
+ end
309
+
310
+ should "extract data from a rack environment hash with action_dispatch info" do
311
+ params = { 'controller' => 'users', 'action' => 'index', 'id' => '7' }
312
+ env = Rack::MockRequest.env_for('/', { 'action_dispatch.request.parameters' => params })
313
+
314
+ notice = build_notice(:rack_env => env)
315
+
316
+ assert_equal params, notice.parameters
317
+ assert_equal params['controller'], notice.component
318
+ assert_equal params['action'], notice.action
319
+ end
320
+
321
+ should "extract session data from a rack environment" do
322
+ session_data = { 'something' => 'some value' }
323
+ env = Rack::MockRequest.env_for('/', 'rack.session' => session_data)
324
+
325
+ notice = build_notice(:rack_env => env)
326
+
327
+ assert_equal session_data, notice.session_data
328
+ end
329
+
330
+ should "prefer passed session data to rack session data" do
331
+ session_data = { 'something' => 'some value' }
332
+ env = Rack::MockRequest.env_for('/')
333
+
334
+ notice = build_notice(:rack_env => env, :session_data => session_data)
335
+
336
+ assert_equal session_data, notice.session_data
337
+ end
338
+
339
+ should "not allow infinite recursion" do
340
+ hash = {:a => :a}
341
+ hash[:hash] = hash
342
+ notice = Honeybadger::Notice.new(:parameters => hash)
343
+ assert_equal "[possible infinite recursion halted]", notice.parameters[:hash]
344
+ end
345
+
346
+ def assert_accepts_exception_attribute(attribute, args = {}, &block)
347
+ exception = build_exception
348
+ block ||= lambda { exception.send(attribute) }
349
+ value = block.call(exception)
350
+
351
+ notice_from_exception = build_notice(args.merge(:exception => exception))
352
+
353
+ assert_equal notice_from_exception.send(attribute),
354
+ value,
355
+ "#{attribute} was not correctly set from an exception"
356
+
357
+ notice_from_hash = build_notice(args.merge(attribute => value))
358
+ assert_equal notice_from_hash.send(attribute),
359
+ value,
360
+ "#{attribute} was not correctly set from a hash"
361
+ end
362
+
363
+ def assert_serializes_hash(attribute)
364
+ [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object|
365
+ hash = {
366
+ :strange_object => object,
367
+ :sub_hash => {
368
+ :sub_object => object
369
+ },
370
+ :array => [object]
371
+ }
372
+ notice = build_notice(attribute => hash)
373
+ hash = notice.send(attribute)
374
+ assert_equal object.to_s, hash[:strange_object], "objects should be serialized"
375
+ assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept"
376
+ assert_equal object.to_s, hash[:sub_hash][:sub_object], "subhash members should be serialized"
377
+ assert_kind_of Array, hash[:array], "arrays should be kept"
378
+ assert_equal object.to_s, hash[:array].first, "array members should be serialized"
379
+ end
380
+ end
381
+
382
+ def assert_filters_hash(attribute)
383
+ filters = ["abc", :def]
384
+ original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
385
+ 'something_with_abc' => 'match the entire string'}
386
+ filtered = { 'abc' => "[FILTERED]",
387
+ 'def' => "[FILTERED]",
388
+ 'something_with_abc' => "match the entire string",
389
+ 'ghi' => "789",
390
+ 'nested' => { 'abc' => '[FILTERED]' } }
391
+
392
+ notice = build_notice(:params_filters => filters, attribute => original)
393
+
394
+ assert_equal(filtered,
395
+ notice.send(attribute))
396
+ end
397
+
398
+ def build_backtrace_array
399
+ ["app/models/user.rb:13:in `magic'",
400
+ "app/controllers/users_controller.rb:8:in `index'"]
401
+ end
402
+
403
+ def hostname
404
+ `hostname`.chomp
405
+ end
406
+ end