honeybadger 1.13.2 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +60 -45
  3. data/CHANGELOG.md +30 -1
  4. data/Gemfile.lock +1 -1
  5. data/MIT-LICENSE +2 -1
  6. data/Rakefile +8 -4
  7. data/features/standalone.feature +73 -0
  8. data/features/step_definitions/rack_steps.rb +1 -2
  9. data/features/step_definitions/standalone_steps.rb +12 -0
  10. data/features/step_definitions/thor_steps.rb +4 -0
  11. data/features/support/env.rb +2 -0
  12. data/features/support/test.thor +22 -0
  13. data/features/thor.feature +5 -0
  14. data/gemfiles/binding_of_caller.gemfile +8 -0
  15. data/gemfiles/rails.gemfile +11 -0
  16. data/gemfiles/rake.gemfile +1 -1
  17. data/gemfiles/standalone.gemfile +7 -0
  18. data/gemfiles/thor.gemfile +8 -0
  19. data/honeybadger.gemspec +27 -11
  20. data/lib/honeybadger.rb +15 -10
  21. data/lib/honeybadger/configuration.rb +9 -4
  22. data/lib/honeybadger/dependency.rb +65 -0
  23. data/lib/honeybadger/exception_extensions.rb +35 -0
  24. data/lib/honeybadger/integrations.rb +5 -0
  25. data/lib/honeybadger/integrations/delayed_job.rb +20 -0
  26. data/lib/honeybadger/integrations/delayed_job/plugin.rb +31 -0
  27. data/lib/honeybadger/integrations/sidekiq.rb +17 -9
  28. data/lib/honeybadger/integrations/thor.rb +29 -0
  29. data/lib/honeybadger/notice.rb +47 -99
  30. data/lib/honeybadger/payload.rb +101 -0
  31. data/lib/honeybadger/rack.rb +8 -54
  32. data/lib/honeybadger/rack/error_notifier.rb +60 -0
  33. data/lib/honeybadger/rack/user_feedback.rb +74 -0
  34. data/lib/honeybadger/rack/user_informer.rb +28 -0
  35. data/lib/honeybadger/rails.rb +5 -4
  36. data/lib/honeybadger/railtie.rb +4 -3
  37. data/lib/honeybadger/user_feedback.rb +3 -67
  38. data/lib/honeybadger/user_informer.rb +3 -21
  39. data/spec/honeybadger/configuration_spec.rb +5 -1
  40. data/spec/honeybadger/dependency_spec.rb +134 -0
  41. data/spec/honeybadger/exception_extensions_spec.rb +40 -0
  42. data/spec/honeybadger/integrations/delayed_job_spec.rb +48 -0
  43. data/spec/honeybadger/integrations/sidekiq_spec.rb +60 -0
  44. data/spec/honeybadger/integrations/thor_spec.rb +29 -0
  45. data/spec/honeybadger/notice_spec.rb +137 -127
  46. data/spec/honeybadger/payload_spec.rb +162 -0
  47. data/spec/honeybadger/rack_spec.rb +6 -6
  48. data/spec/honeybadger/rails/action_controller_spec.rb +2 -0
  49. data/spec/honeybadger/rails_spec.rb +4 -2
  50. data/spec/honeybadger/user_feedback_spec.rb +2 -2
  51. data/spec/honeybadger/user_informer_spec.rb +3 -3
  52. metadata +49 -66
  53. data/gemfiles/rack.gemfile.lock +0 -125
  54. data/gemfiles/rails2.3.gemfile.lock +0 -141
  55. data/gemfiles/rails3.0.gemfile.lock +0 -193
  56. data/gemfiles/rails3.1.gemfile.lock +0 -203
  57. data/gemfiles/rails3.2.gemfile.lock +0 -201
  58. data/gemfiles/rails4.0.gemfile.lock +0 -197
  59. data/gemfiles/rails4.1.gemfile.lock +0 -202
  60. data/gemfiles/rake.gemfile.lock +0 -124
  61. data/gemfiles/sinatra.gemfile.lock +0 -124
@@ -1,26 +1,8 @@
1
1
  module Honeybadger
2
- class UserInformer
2
+ class UserInformer < Rack::UserInformer
3
3
  def initialize(app)
4
- @app = app
5
- end
6
-
7
- def replacement(with)
8
- Honeybadger.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
9
- end
10
-
11
- def call(env)
12
- status, headers, body = @app.call(env)
13
- if env['honeybadger.error_id'] && Honeybadger.configuration.user_information
14
- new_body = []
15
- replace = replacement(env['honeybadger.error_id'])
16
- body.each do |chunk|
17
- new_body << chunk.gsub("<!-- HONEYBADGER ERROR -->", replace)
18
- end
19
- body.close if body.respond_to?(:close)
20
- headers['Content-Length'] = new_body.reduce(0) { |a,e| a += e.bytesize }.to_s
21
- body = new_body
22
- end
23
- [status, headers, body]
4
+ warn '[DEPRECATION] Honeybadger::UserInformer is deprecated in 2.0. Use Honeybadger::Rack::UserInformer.'
5
+ super
24
6
  end
25
7
  end
26
8
  end
@@ -30,11 +30,13 @@ describe Honeybadger::Configuration do
30
30
  assert_config_default :source_extract_radius, 2
31
31
  assert_config_default :async, nil
32
32
  assert_config_default :send_request_session, true
33
+ assert_config_default :send_local_variables, false
33
34
  assert_config_default :debug, false
34
35
  assert_config_default :log_exception_on_send_failure, false
35
36
  assert_config_default :fingerprint, nil
36
37
  assert_config_default :hostname, Socket.gethostname
37
38
  assert_config_default :feedback, true
39
+ assert_config_default :features, {'notices' => true, 'local_variables' => true}
38
40
  end
39
41
 
40
42
  it "configures async as Proc" do
@@ -116,6 +118,7 @@ describe Honeybadger::Configuration do
116
118
  assert_config_overridable :send_request_session
117
119
  assert_config_overridable :debug
118
120
  assert_config_overridable :hostname
121
+ assert_config_overridable :features
119
122
  assert_config_overridable :metrics
120
123
  assert_config_overridable :feedback
121
124
  assert_config_overridable :log_exception_on_send_failure
@@ -134,7 +137,8 @@ describe Honeybadger::Configuration do
134
137
  :notifier_version, :params_filters, :project_root, :port, :protocol,
135
138
  :proxy_host, :proxy_pass, :proxy_port, :proxy_user, :secure,
136
139
  :source_extract_radius, :async, :send_request_session, :debug,
137
- :fingerprint, :hostname, :metrics, :feedback, :log_exception_on_send_failure].each do |option|
140
+ :fingerprint, :hostname, :features, :metrics, :feedback,
141
+ :log_exception_on_send_failure].each do |option|
138
142
  expect(hash[option]).to eq config[option]
139
143
  end
140
144
  end
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ describe Honeybadger::Dependency do
4
+ let(:dependency) { Honeybadger::Dependency.new }
5
+ subject { dependency }
6
+
7
+ before { Honeybadger::Dependency.stub(:instances).and_return([]) }
8
+
9
+ describe ".register" do
10
+ it "returns a new dependency" do
11
+ instance = double()
12
+ Honeybadger::Dependency.stub(:new).and_return(instance)
13
+ expect(Honeybadger::Dependency.register {}).to eq [instance]
14
+ end
15
+
16
+ it "registers a new dependency" do
17
+ expect { Honeybadger::Dependency.register {} }.to change(described_class, :instances).from([]).to([kind_of(Honeybadger::Dependency)])
18
+ end
19
+ end
20
+
21
+ describe ".inject!" do
22
+ it "injects all satisfied instances" do
23
+ Honeybadger::Dependency.instances.replace([mock_dependency, mock_dependency])
24
+ Honeybadger::Dependency.inject!
25
+ end
26
+
27
+ it "skips all unsatisfied instances" do
28
+ Honeybadger::Dependency.instances.replace([mock_dependency(false), mock_dependency(false)])
29
+ Honeybadger::Dependency.inject!
30
+ end
31
+ end
32
+
33
+ describe "#requirement" do
34
+ let(:block) { Proc.new {} }
35
+
36
+ it "returns and Array of requirements" do
37
+ expect(subject.requirement(&block)).to eq [block]
38
+ end
39
+
40
+ it "registers a new requirement" do
41
+ expect { subject.requirement(&block) }.to change(subject, :requirements).from([]).to([block])
42
+ end
43
+ end
44
+
45
+ describe "#injection" do
46
+ let(:block) { Proc.new {} }
47
+
48
+ it "returns an Array of injections" do
49
+ expect(subject.injection(&block)).to eq [block]
50
+ end
51
+
52
+ it "registers a new injection" do
53
+ expect { subject.injection(&block) }.to change(subject, :injections).from([]).to([block])
54
+ end
55
+ end
56
+
57
+ describe "#ok?" do
58
+ subject { dependency.ok? }
59
+
60
+ context "when not injected yet" do
61
+ it { should be_true }
62
+ end
63
+
64
+ context "when already injected" do
65
+ before { dependency.inject! }
66
+
67
+ it { should be_false }
68
+ end
69
+
70
+ context "all requirements are met" do
71
+ before do
72
+ 3.times { dependency.requirement { true } }
73
+ end
74
+ end
75
+
76
+ context "some requirements fail" do
77
+ before do
78
+ 3.times { dependency.requirement { true } }
79
+ dependency.requirement { false }
80
+ end
81
+
82
+ it { should be_false }
83
+ end
84
+
85
+ context "some requirements error" do
86
+ before do
87
+ dependency.requirement { true }
88
+ dependency.requirement { fail 'oops!' }
89
+ end
90
+
91
+ it { should be_false }
92
+
93
+ it "logs the failure" do
94
+ Honeybadger.should_receive(:write_verbose_log).with(/oops!/, :error).once
95
+ dependency.ok?
96
+ end
97
+ end
98
+ end
99
+
100
+ describe "#inject!" do
101
+ it "calls injections" do
102
+ dependency.injections.replace([mock_injection, mock_injection])
103
+ dependency.inject!
104
+ end
105
+
106
+ context "some injections fail" do
107
+ before do
108
+ failing_injection = Proc.new { fail 'oh noes!' }
109
+ dependency.injections.replace([mock_injection, failing_injection, mock_injection(false)])
110
+ end
111
+
112
+ it "halts injection silently" do
113
+ expect { dependency.inject! }.not_to raise_error
114
+ end
115
+
116
+ it "logs the failure" do
117
+ Honeybadger.should_receive(:write_verbose_log).with(/oh noes!/, :error).once
118
+ dependency.inject!
119
+ end
120
+
121
+ it "marks the dependency as injected" do
122
+ expect { dependency.inject!}.to change(dependency, :injected?).from(false).to(true)
123
+ end
124
+ end
125
+ end
126
+
127
+ def mock_dependency(ok = true)
128
+ double(:ok? => ok).tap { |d| d.send(ok ? :should_receive : :should_not_receive, :inject!) }
129
+ end
130
+
131
+ def mock_injection(positive = true)
132
+ double().tap { |d| d.send(positive ? :should_receive : :should_not_receive, :call) }
133
+ end
134
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Exception, :unless => defined?(BindingOfCaller) do
4
+ should { respond_to :__honeybadger_bindings_stack }
5
+ its(:__honeybadger_bindings_stack) { should eq([]) }
6
+ end
7
+
8
+ describe Exception, :if => defined?(BindingOfCaller) do
9
+ describe "#set_backtrace" do
10
+ context "call stack does not match current file" do
11
+ it "changes the bindings stack" do
12
+ expect { subject.set_backtrace(['foo.rb:1']) }.to change(subject, :__honeybadger_bindings_stack).from([])
13
+ end
14
+ end
15
+
16
+ context "call stack includes current file" do
17
+ before do
18
+ subject.stub(:caller).and_return(["#{File.expand_path('../../../lib/honeybadger/exception_extensions.rb', __FILE__)}:1"])
19
+ end
20
+
21
+ it "does not change the bindings stack" do
22
+ expect { subject.set_backtrace(['foo.rb:1']) }.not_to change(subject, :__honeybadger_bindings_stack).from([])
23
+ end
24
+ end
25
+
26
+ context "call stack includes a non-matching line" do
27
+ before do
28
+ subject.stub(:caller).and_return(['(foo)'])
29
+ end
30
+
31
+ it "skips the non-matching line" do
32
+ expect { subject.set_backtrace(['foo.rb:1']) }.not_to raise_error
33
+ end
34
+
35
+ it "changes the bindings stack" do
36
+ expect { subject.set_backtrace(['foo.rb:1']) }.to change(subject, :__honeybadger_bindings_stack).from([])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe "DelayedJob Dependency" do
4
+ before do
5
+ Honeybadger::Dependency.reset!
6
+ end
7
+
8
+ context "when delayed_job is not installed" do
9
+ it "fails quietly" do
10
+ expect { Honeybadger::Dependency.inject! }.not_to raise_error
11
+ end
12
+ end
13
+
14
+ context "when delayed_job is installed" do
15
+ let(:plugins_array) { [] }
16
+ let(:plugin_class) do
17
+ Class.new do
18
+ def self.callbacks(&block)
19
+ end
20
+ end
21
+ end
22
+
23
+ before do
24
+ Object.const_set(:Delayed, Module.new)
25
+ ::Delayed.const_set(:Plugin, plugin_class)
26
+ ::Delayed.const_set(:Worker, double(:plugins => plugins_array))
27
+ end
28
+
29
+ after { Object.send(:remove_const, :Delayed) }
30
+
31
+ it "adds the plugin to DelayedJob" do
32
+ Honeybadger::Dependency.inject!
33
+ expect(plugins_array).to include(Honeybadger::Integrations::DelayedJob::Plugin)
34
+ end
35
+
36
+ context "and delayed_job_honeybadger is installed" do
37
+ before do
38
+ ::Delayed.const_set(:Plugins, Module.new)
39
+ ::Delayed::Plugins.const_set(:Honeybadger, Class.new(plugin_class))
40
+ end
41
+
42
+ it "warns the user of the conflict" do
43
+ Honeybadger.should_receive(:write_verbose_log).with(/Support for Delayed Job has been moved/, :warn).once
44
+ Honeybadger::Dependency.inject!
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Sidekiq Dependency" do
4
+ before do
5
+ Honeybadger::Dependency.reset!
6
+ end
7
+
8
+ context "when sidekiq is not installed" do
9
+ it "fails quietly" do
10
+ expect { Honeybadger::Dependency.inject! }.not_to raise_error
11
+ end
12
+ end
13
+
14
+ context "when sidekiq is installed" do
15
+ let(:shim) do
16
+ Class.new do
17
+ def self.configure_server
18
+ end
19
+ end
20
+ end
21
+
22
+ let(:config) { double('config', :error_handlers => []) }
23
+ let(:chain) { double('chain', :add => true) }
24
+
25
+ before do
26
+ Object.const_set(:Sidekiq, shim)
27
+ ::Sidekiq.stub(:configure_server).and_yield(config)
28
+ config.stub(:server_middleware).and_yield(chain)
29
+ end
30
+
31
+ after { Object.send(:remove_const, :Sidekiq) }
32
+
33
+ context "when version is less than 3" do
34
+ before do
35
+ ::Sidekiq.const_set(:VERSION, '2.17.7')
36
+ end
37
+
38
+ it "adds the server middleware" do
39
+ chain.should_receive(:add).with(Honeybadger::Integrations::Sidekiq::Middleware)
40
+ Honeybadger::Dependency.inject!
41
+ end
42
+
43
+ it "doesn't add the error handler" do
44
+ Honeybadger::Dependency.inject!
45
+ expect(config.error_handlers).to be_empty
46
+ end
47
+ end
48
+
49
+ context "when version is 3 or greater" do
50
+ before do
51
+ ::Sidekiq.const_set(:VERSION, '3.0.0')
52
+ end
53
+
54
+ it "adds the error handler" do
55
+ Honeybadger::Dependency.inject!
56
+ expect(config.error_handlers).not_to be_empty
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Thor Dependency" do
4
+ before do
5
+ Honeybadger::Dependency.reset!
6
+ end
7
+
8
+ context "when thor is not installed" do
9
+ it "fails quietly" do
10
+ expect { Honeybadger::Dependency.inject! }.not_to raise_error
11
+ end
12
+ end
13
+
14
+ context "when thor is installed" do
15
+ let(:shim) do
16
+ double('fake thor')
17
+ end
18
+
19
+ before do
20
+ Object.const_set(:Thor, shim)
21
+ end
22
+ after { Object.send(:remove_const, :Thor) }
23
+
24
+ it "includes integration module into Thor" do
25
+ shim.should_receive(:send).with(:include, Honeybadger::Integrations::Thor)
26
+ Honeybadger::Dependency.inject!
27
+ end
28
+ end
29
+ end
@@ -76,6 +76,17 @@ describe Honeybadger::Notice do
76
76
  expect(notice.url).to eq url
77
77
  end
78
78
 
79
+ it "initializes default features" do
80
+ notice = build_notice(:features => nil)
81
+ expect(notice.features).to eq({})
82
+ end
83
+
84
+ it "accepts features" do
85
+ features = {'foo' => 'bar'}
86
+ notice = build_notice(:features => features)
87
+ expect(notice.features).to eq features
88
+ end
89
+
79
90
  it "sets the host name" do
80
91
  notice = build_notice
81
92
  expect(notice.hostname).to eq hostname
@@ -286,76 +297,6 @@ describe Honeybadger::Notice do
286
297
  assert_array_starts_with backtrace.lines, notice.backtrace.lines
287
298
  end
288
299
 
289
- it "converts unserializable objects to strings" do
290
- assert_serializes_hash(:parameters)
291
- assert_serializes_hash(:cgi_data)
292
- assert_serializes_hash(:session_data)
293
- end
294
-
295
- it "filters parameters" do
296
- assert_filters_hash(:parameters)
297
- end
298
-
299
- it "filters cgi data" do
300
- assert_filters_hash(:cgi_data)
301
- end
302
-
303
- it "filters session" do
304
- assert_filters_hash(:session_data)
305
- end
306
-
307
- context 'filtered parameters in query string' do
308
- let(:params_filters) { [:foo, :bar] }
309
-
310
- describe '#url' do
311
- let(:notice) { build_notice(:params_filters => params_filters, :url => 'https://www.honeybadger.io/?foo=1&bar=2&baz=3') }
312
- it 'filters query' do
313
- expect(notice.url).to eq 'https://www.honeybadger.io/?foo=[FILTERED]&bar=[FILTERED]&baz=3'
314
- end
315
- end
316
-
317
- describe '#cgi_data' do
318
- let(:cgi_data) { { 'QUERY_STRING' => 'foo=1&bar=2&baz=3', 'ORIGINAL_FULLPATH' => '/?foo=1&bar=2&baz=3' } }
319
- let(:notice) { build_notice(:params_filters => params_filters, :cgi_data => cgi_data) }
320
-
321
- subject { notice.cgi_data }
322
-
323
- it 'filters QUERY_STRING key' do
324
- expect(subject['QUERY_STRING']).to eq 'foo=[FILTERED]&bar=[FILTERED]&baz=3'
325
- end
326
-
327
- it 'filters ORIGINAL_FULLPATH key' do
328
- expect(subject['ORIGINAL_FULLPATH']).to eq '/?foo=[FILTERED]&bar=[FILTERED]&baz=3'
329
- end
330
- end
331
- end
332
-
333
- describe '#filter_url' do
334
- let(:notice) { build_notice(:params_filters => [], :url => url) }
335
- subject { notice.send(:filter_url, url) }
336
-
337
- context 'malformed query' do
338
- let(:url) { 'https://www.honeybadger.io/?foobar12' }
339
- it { should eq url }
340
- end
341
-
342
- context 'no query' do
343
- let(:url) { 'https://www.honeybadger.io' }
344
- it { should eq url }
345
- end
346
-
347
- context 'malformed url' do
348
- let(:url) { 'http s ! honeybadger' }
349
- before { expect { URI.parse(url) }.to raise_error }
350
- it { should eq url }
351
- end
352
-
353
- context 'complex url' do
354
- let(:url) { 'https://foo:bar@www.honeybadger.io:123/asdf/?foo=1&bar=2&baz=3' }
355
- it { should eq url }
356
- end
357
- end
358
-
359
300
  it "removes rack.request.form_vars" do
360
301
  original = {
361
302
  "rack.request.form_vars" => "story%5Btitle%5D=The+TODO+label",
@@ -481,6 +422,17 @@ describe Honeybadger::Notice do
481
422
  expect(notice[:request][:context]).to eq({ 'one' => 'two', 'three' => 'four', 'foo' => 'baz' })
482
423
  end
483
424
 
425
+ it "doesn't mutate global context" do
426
+ Honeybadger.context({ 'one' => 'two' })
427
+ expect { build_notice(:context => { 'foo' => 'bar' }) }.not_to change { Thread.current[:honeybadger_context] }
428
+ end
429
+
430
+ it "doesn't mutate local context" do
431
+ Honeybadger.context({ 'one' => 'two' })
432
+ hash = { 'foo' => 'bar' }
433
+ expect { build_notice(:context => hash) }.not_to change { hash }
434
+ end
435
+
484
436
  it "returns nil context when context is not set" do
485
437
  notice = build_notice
486
438
  notice[:request][:context].should be_nil
@@ -493,14 +445,6 @@ describe Honeybadger::Notice do
493
445
  expect(hash['request']['context']).to eq({ 'debuga' => true, 'debugb' => false })
494
446
  end
495
447
 
496
- it "ensures #to_hash is called on objects that support it" do
497
- expect { build_notice(:session => { :object => double(:to_hash => {}) }) }.not_to raise_error
498
- end
499
-
500
- it "ensures #to_ary is called on objects that support it" do
501
- expect { build_notice(:session => { :object => double(:to_ary => {}) }) }.not_to raise_error
502
- end
503
-
504
448
  context "with a rack environment hash" do
505
449
  it "extracts data from a rack environment hash" do
506
450
  url = "https://subdomain.happylane.com:100/test/file.rb?var=value&var2=value2"
@@ -557,7 +501,12 @@ describe Honeybadger::Notice do
557
501
  expect(notice.session_data).to eq session_data
558
502
  end
559
503
 
560
- unless Gem::Version.new(Rack.release) < Gem::Version.new('1.2')
504
+ if Gem::Version.new(Rack.release) < Gem::Version.new('1.3')
505
+ it "parses params which are malformed in Rack >= 1.3" do
506
+ rack_env = Rack::MockRequest.env_for('http://www.example.com/explode', :method => 'POST', :input => 'foo=bar&bar=baz%')
507
+ expect { Honeybadger::Notice.new(:rack_env => rack_env) }.not_to raise_error
508
+ end
509
+ else
561
510
  it "fails gracefully when Rack params cannot be parsed" do
562
511
  rack_env = Rack::MockRequest.env_for('http://www.example.com/explode', :method => 'POST', :input => 'foo=bar&bar=baz%')
563
512
  notice = Honeybadger::Notice.new(:rack_env => rack_env)
@@ -572,13 +521,6 @@ describe Honeybadger::Notice do
572
521
  notice.session_data.should be_nil
573
522
  end
574
523
 
575
- it "does not allow infinite recursion" do
576
- hash = {:a => :a}
577
- hash[:hash] = hash
578
- notice = Honeybadger::Notice.new(:parameters => hash)
579
- expect(notice.parameters[:hash]).to eq "[possible infinite recursion halted]"
580
- end
581
-
582
524
  it "trims error message to 1k" do
583
525
  message = 'asdfghjkl'*200
584
526
  e = StandardError.new(message)
@@ -594,6 +536,115 @@ describe Honeybadger::Notice do
594
536
  expect(notice.error_message).to eq 'Something very specific went wrong.'
595
537
  end
596
538
 
539
+ describe "#to_json" do
540
+ context "when local variables are not found" do
541
+ it "sends local_variables in request payload" do
542
+ notice = build_notice
543
+ hash = {'foo' => 'bar'}
544
+ notice.stub(:local_variables).and_return(hash)
545
+ expect(JSON.parse(notice.to_json)['request']['local_variables']).to eq(hash)
546
+ end
547
+ end
548
+
549
+ context "when local variables are found" do
550
+ it "sends local_variables in request payload" do
551
+ notice = build_notice
552
+ expect(JSON.parse(notice.to_json)['request']['local_variables']).to eq({})
553
+ end
554
+ end
555
+ end
556
+
557
+ describe "#local_variables" do
558
+ let(:notice) { build_notice(:exception => @exception, :configuration => configuration) }
559
+ let(:configuration) { configure }
560
+
561
+ before do
562
+ foo = 'bar'
563
+ begin
564
+ fail 'oops'
565
+ rescue
566
+ @exception = $!
567
+ end
568
+ end
569
+
570
+ context "when binding_of_caller is not installed", :unless => defined?(BindingOfCaller) do
571
+ context "when local variables aren't enabled" do
572
+ it "does not attempt to find them" do
573
+ expect(notice.local_variables).to eq({})
574
+ end
575
+ end
576
+
577
+ context "when local variables are enabled" do
578
+ before do
579
+ configuration.send_local_variables = true
580
+ end
581
+
582
+ it "does not attempt to find them" do
583
+ expect(notice.local_variables).to eq({})
584
+ end
585
+ end
586
+ end
587
+
588
+ context "when binding_of_caller is installed", :if => defined?(BindingOfCaller) do
589
+ context "when local variables aren't enabled" do
590
+ it "does not attempt to find them" do
591
+ expect(notice.local_variables).to eq({})
592
+ end
593
+ end
594
+
595
+ context "when local variables are enabled" do
596
+ before do
597
+ configuration.send_local_variables = true
598
+ end
599
+
600
+ it "finds the local variables from first frame of trace" do
601
+ expect(notice.local_variables[:foo]).to eq 'bar'
602
+ end
603
+
604
+ context "when the feature is disabled" do
605
+ before do
606
+ configuration.features['local_variables'] = false
607
+ end
608
+
609
+ it "assigns empty Hash" do
610
+ expect(notice.local_variables).to eq({})
611
+ end
612
+ end
613
+
614
+ context "with an application trace" do
615
+ before do
616
+ @exception.__honeybadger_bindings_stack.unshift(double('Binding', :eval => nil))
617
+ configuration.project_root = File.dirname(__FILE__)
618
+ end
619
+
620
+ it "finds the local variables from first frame of application trace" do
621
+ expect(notice.local_variables[:foo]).to eq 'bar'
622
+ end
623
+
624
+ context "and project_root is a Pathname" do
625
+ before do
626
+ configuration.project_root = Pathname.new(File.dirname(__FILE__))
627
+ end
628
+
629
+ specify { expect { notice }.not_to raise_error }
630
+ end
631
+ end
632
+
633
+ context "without an exception" do
634
+ it "assigns empty Hash" do
635
+ expect(build_notice(:exception => nil).local_variables).to eq({})
636
+ end
637
+ end
638
+
639
+ context "without bindings" do
640
+ it "assigns empty Hash" do
641
+ expect(build_notice(:exception => RuntimeError.new).local_variables).to eq({})
642
+ end
643
+ end
644
+ end
645
+ end
646
+ end
647
+
597
648
  def assert_accepts_exception_attribute(attribute, args = {}, &block)
598
649
  exception = build_exception
599
650
  block ||= lambda { exception.send(attribute) }
@@ -607,47 +658,6 @@ describe Honeybadger::Notice do
607
658
  expect(notice_from_hash.send(attribute)).to eq value
608
659
  end
609
660
 
610
- def assert_serializes_hash(attribute)
611
- [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object|
612
- hash = {
613
- :strange_object => object,
614
- :sub_hash => {
615
- :sub_object => object
616
- },
617
- :array => [object]
618
- }
619
- notice = build_notice(attribute => hash)
620
- hash = notice.send(attribute)
621
- expect(object.to_s).to eq hash[:strange_object] # objects should be serialized
622
-
623
- expect(hash[:sub_hash]).to be_a Hash # subhashes should be kept
624
- expect(object.to_s).to eq hash[:sub_hash][:sub_object] # subhash members should be serialized
625
- expect(hash[:array]).to be_a Array # arrays should be kept
626
- expect(object.to_s).to eq hash[:array].first # array members should be serialized
627
- end
628
- end
629
-
630
- def assert_filters_hash(attribute)
631
- filters = ["abc", :def, /private/, /^foo_.*$/]
632
- original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
633
- 'something_with_abc' => 'match the entire string', 'private_param' => 'prra',
634
- 'foo_param' => 'bar', 'not_foo_param' => 'baz', 'nested_foo' => { 'foo_nested' => 'bla'} }
635
- filtered = { 'abc' => "[FILTERED]",
636
- 'def' => "[FILTERED]",
637
- 'something_with_abc' => "match the entire string",
638
- 'ghi' => "789",
639
- 'nested' => { 'abc' => '[FILTERED]' },
640
- 'private_param' => '[FILTERED]',
641
- 'foo_param' => '[FILTERED]',
642
- 'not_foo_param' => 'baz',
643
- 'nested_foo' => { 'foo_nested' => '[FILTERED]'}
644
- }
645
-
646
- notice = build_notice(:params_filters => filters, attribute => original)
647
-
648
- expect(notice.send(attribute)).to eq filtered
649
- end
650
-
651
661
  def build_backtrace_array
652
662
  ["app/models/user.rb:13:in `magic'",
653
663
  "app/controllers/users_controller.rb:8:in `index'"]