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.
- 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'"]
|