honeybadger 1.13.2 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Appraisals +60 -45
- data/CHANGELOG.md +30 -1
- data/Gemfile.lock +1 -1
- data/MIT-LICENSE +2 -1
- data/Rakefile +8 -4
- data/features/standalone.feature +73 -0
- data/features/step_definitions/rack_steps.rb +1 -2
- data/features/step_definitions/standalone_steps.rb +12 -0
- data/features/step_definitions/thor_steps.rb +4 -0
- data/features/support/env.rb +2 -0
- data/features/support/test.thor +22 -0
- data/features/thor.feature +5 -0
- data/gemfiles/binding_of_caller.gemfile +8 -0
- data/gemfiles/rails.gemfile +11 -0
- data/gemfiles/rake.gemfile +1 -1
- data/gemfiles/standalone.gemfile +7 -0
- data/gemfiles/thor.gemfile +8 -0
- data/honeybadger.gemspec +27 -11
- data/lib/honeybadger.rb +15 -10
- data/lib/honeybadger/configuration.rb +9 -4
- data/lib/honeybadger/dependency.rb +65 -0
- data/lib/honeybadger/exception_extensions.rb +35 -0
- data/lib/honeybadger/integrations.rb +5 -0
- data/lib/honeybadger/integrations/delayed_job.rb +20 -0
- data/lib/honeybadger/integrations/delayed_job/plugin.rb +31 -0
- data/lib/honeybadger/integrations/sidekiq.rb +17 -9
- data/lib/honeybadger/integrations/thor.rb +29 -0
- data/lib/honeybadger/notice.rb +47 -99
- data/lib/honeybadger/payload.rb +101 -0
- data/lib/honeybadger/rack.rb +8 -54
- data/lib/honeybadger/rack/error_notifier.rb +60 -0
- data/lib/honeybadger/rack/user_feedback.rb +74 -0
- data/lib/honeybadger/rack/user_informer.rb +28 -0
- data/lib/honeybadger/rails.rb +5 -4
- data/lib/honeybadger/railtie.rb +4 -3
- data/lib/honeybadger/user_feedback.rb +3 -67
- data/lib/honeybadger/user_informer.rb +3 -21
- data/spec/honeybadger/configuration_spec.rb +5 -1
- data/spec/honeybadger/dependency_spec.rb +134 -0
- data/spec/honeybadger/exception_extensions_spec.rb +40 -0
- data/spec/honeybadger/integrations/delayed_job_spec.rb +48 -0
- data/spec/honeybadger/integrations/sidekiq_spec.rb +60 -0
- data/spec/honeybadger/integrations/thor_spec.rb +29 -0
- data/spec/honeybadger/notice_spec.rb +137 -127
- data/spec/honeybadger/payload_spec.rb +162 -0
- data/spec/honeybadger/rack_spec.rb +6 -6
- data/spec/honeybadger/rails/action_controller_spec.rb +2 -0
- data/spec/honeybadger/rails_spec.rb +4 -2
- data/spec/honeybadger/user_feedback_spec.rb +2 -2
- data/spec/honeybadger/user_informer_spec.rb +3 -3
- metadata +49 -66
- data/gemfiles/rack.gemfile.lock +0 -125
- data/gemfiles/rails2.3.gemfile.lock +0 -141
- data/gemfiles/rails3.0.gemfile.lock +0 -193
- data/gemfiles/rails3.1.gemfile.lock +0 -203
- data/gemfiles/rails3.2.gemfile.lock +0 -201
- data/gemfiles/rails4.0.gemfile.lock +0 -197
- data/gemfiles/rails4.1.gemfile.lock +0 -202
- data/gemfiles/rake.gemfile.lock +0 -124
- 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
|
-
|
5
|
-
|
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,
|
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
|
-
|
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'"]
|