honeybadger 1.13.2 → 1.14.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 (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'"]