honeybadger 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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