honeybadger 1.8.1 → 1.9.0.beta1
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.
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +61 -19
- data/Guardfile +4 -4
- data/MIT-LICENSE +1 -0
- data/README.md +2 -2
- data/Rakefile +4 -14
- data/features/rails.feature +1 -1
- data/gemfiles/rack.gemfile.lock +62 -27
- data/gemfiles/rails2.3.gemfile.lock +73 -36
- data/gemfiles/rails3.0.gemfile.lock +59 -26
- data/gemfiles/rails3.1.gemfile.lock +59 -26
- data/gemfiles/rails3.2.gemfile.lock +63 -30
- data/gemfiles/rails4.gemfile.lock +69 -36
- data/gemfiles/rake.gemfile.lock +62 -27
- data/gemfiles/sinatra.gemfile.lock +62 -27
- data/honeybadger.gemspec +31 -17
- data/lib/honeybadger.rb +2 -3
- data/lib/honeybadger/array.rb +53 -0
- data/lib/honeybadger/configuration.rb +19 -2
- data/lib/honeybadger/monitor.rb +16 -0
- data/lib/honeybadger/monitor/railtie.rb +52 -0
- data/lib/honeybadger/monitor/sender.rb +33 -0
- data/lib/honeybadger/monitor/worker.rb +71 -0
- data/lib/honeybadger/railtie.rb +10 -0
- data/lib/honeybadger/sender.rb +60 -41
- data/{test/unit/backtrace_test.rb → spec/honeybadger/backtrace_spec.rb} +69 -71
- data/{test/unit/capistrano_test.rb → spec/honeybadger/capistrano_spec.rb} +8 -9
- data/{test/unit/configuration_test.rb → spec/honeybadger/configuration_spec.rb} +85 -59
- data/spec/honeybadger/logger_spec.rb +65 -0
- data/spec/honeybadger/monitor/worker_spec.rb +189 -0
- data/{test/unit/notice_test.rb → spec/honeybadger/notice_spec.rb} +169 -185
- data/spec/honeybadger/notifier_spec.rb +252 -0
- data/spec/honeybadger/rack_spec.rb +84 -0
- data/{test/unit/rails/action_controller_catcher_test.rb → spec/honeybadger/rails/action_controller_spec.rb} +65 -57
- data/{test/unit/rails_test.rb → spec/honeybadger/rails_spec.rb} +8 -8
- data/spec/honeybadger/sender_spec.rb +249 -0
- data/spec/honeybadger_tasks_spec.rb +165 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/array_including.rb +31 -0
- data/spec/support/backtraced_exception.rb +9 -0
- data/spec/support/collected_sender.rb +12 -0
- data/spec/support/defines_constants.rb +18 -0
- data/{test/test_helper.rb → spec/support/helpers.rb} +8 -61
- metadata +93 -45
- data/test/unit/honeybadger_tasks_test.rb +0 -167
- data/test/unit/logger_test.rb +0 -74
- data/test/unit/notifier_test.rb +0 -265
- data/test/unit/rack_test.rb +0 -88
- data/test/unit/sender_test.rb +0 -290
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Honeybadger do
|
4
|
+
def stub_http(options = {})
|
5
|
+
response = options[:response] || Faraday::Response.new(:status => 200)
|
6
|
+
response.stub(:body => options.include?(:body) ? options[:body] : '{"id":"1234"}')
|
7
|
+
http = double(:post => response,
|
8
|
+
:adapter => nil,
|
9
|
+
:url_prefix= => nil,
|
10
|
+
:headers => nil)
|
11
|
+
Faraday.stub(:new => http)
|
12
|
+
http
|
13
|
+
end
|
14
|
+
|
15
|
+
def send_notice
|
16
|
+
Honeybadger.sender.send_to_honeybadger('data')
|
17
|
+
end
|
18
|
+
|
19
|
+
def stub_verbose_log
|
20
|
+
Honeybadger.stub(:write_verbose_log)
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure
|
24
|
+
Honeybadger.configure { |config| }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "reports that notifier is ready when configured" do
|
28
|
+
stub_verbose_log
|
29
|
+
Honeybadger.should_receive(:write_verbose_log).with(/Notifier (.*) ready/, anything)
|
30
|
+
configure
|
31
|
+
end
|
32
|
+
|
33
|
+
it "does not report that notifier is ready when internally configured" do
|
34
|
+
stub_verbose_log
|
35
|
+
Honeybadger.should_not_receive(:write_verbose_log)
|
36
|
+
Honeybadger.configure(true) { |config| }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "prints environment info on a failed notification without a body" do
|
40
|
+
reset_config
|
41
|
+
stub_verbose_log
|
42
|
+
stub_http(:response => Faraday::Response.new(:status => 500), :body => nil)
|
43
|
+
Honeybadger.should_receive(:write_verbose_log).with(/Environment Info:/)
|
44
|
+
Honeybadger.should_not_receive(:write_verbose_log).with(/Response from Honeybadger:/, anything)
|
45
|
+
send_notice
|
46
|
+
end
|
47
|
+
|
48
|
+
it "prints environment info and response on a success with a body" do
|
49
|
+
reset_config
|
50
|
+
stub_verbose_log
|
51
|
+
stub_http
|
52
|
+
Honeybadger.should_receive(:write_verbose_log).with(/Environment Info:/)
|
53
|
+
Honeybadger.should_receive(:write_verbose_log).with(/Response from Honeybadger:/)
|
54
|
+
send_notice
|
55
|
+
end
|
56
|
+
|
57
|
+
it "prints environment info and response on a failure with a body" do
|
58
|
+
reset_config
|
59
|
+
stub_verbose_log
|
60
|
+
stub_http(:response => Faraday::Response.new(:status => 500))
|
61
|
+
Honeybadger.should_receive(:write_verbose_log).with(/Environment Info:/)
|
62
|
+
Honeybadger.should_receive(:write_verbose_log).with(/Response from Honeybadger:/)
|
63
|
+
send_notice
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'honeybadger/monitor'
|
3
|
+
|
4
|
+
describe Honeybadger::Monitor::Worker do
|
5
|
+
let(:instance) { Honeybadger::Monitor::Worker.send(:new) }
|
6
|
+
subject { instance }
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Thread.stub(:new)
|
10
|
+
# Create an attr_reader for @metrics and @sender
|
11
|
+
instance.stub(:metrics) { instance.instance_variable_get(:@metrics) }
|
12
|
+
instance.stub(:sender) { instance.instance_variable_get(:@sender) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'initializing' do
|
16
|
+
describe '@metrics' do
|
17
|
+
subject { instance.instance_variable_get(:@metrics) }
|
18
|
+
|
19
|
+
it { should have_key(:timing) }
|
20
|
+
it { should have_key(:counter) }
|
21
|
+
|
22
|
+
it 'is initialized timing with empty hash' do
|
23
|
+
expect(subject[:timing]).to eq({})
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is initialized counter with empty hash' do
|
27
|
+
expect(subject[:counter]).to eq({})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '@delay' do
|
32
|
+
subject { instance.instance_variable_get(:@delay) }
|
33
|
+
it { should eq 60 }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '@per_request' do
|
37
|
+
subject { instance.instance_variable_get(:@per_request) }
|
38
|
+
it { should eq 100 }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '@sender' do
|
42
|
+
subject { instance.instance_variable_get(:@sender) }
|
43
|
+
|
44
|
+
it { should be_a Honeybadger::Monitor::Sender }
|
45
|
+
it { should be_a Honeybadger::Sender }
|
46
|
+
|
47
|
+
it 'is initialized with Honeybadger configuration' do
|
48
|
+
Honeybadger::Monitor::Sender.should_receive(:new).with(Honeybadger.configuration)
|
49
|
+
Honeybadger::Monitor::Worker.send(:new)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#timing' do
|
55
|
+
before(:each) do
|
56
|
+
expect(instance.metrics[:timing]).to be_empty
|
57
|
+
instance.timing(:test, 50)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'adds value to metrics hash' do
|
61
|
+
expect(instance.metrics[:timing][:test]).to eq [50]
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'appends to existing values' do
|
65
|
+
instance.timing(:test, 60)
|
66
|
+
expect(instance.metrics[:timing][:test]).to eq [50, 60]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#increment' do
|
71
|
+
before(:each) do
|
72
|
+
expect(instance.metrics[:counter]).to be_empty
|
73
|
+
instance.increment(:test, 50)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'adds value to metrics hash' do
|
77
|
+
expect(instance.metrics[:counter][:test]).to eq [50]
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'appends to existing values' do
|
81
|
+
instance.increment(:test, 60)
|
82
|
+
expect(instance.metrics[:counter][:test]).to eq [50, 60]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#send_metrics' do
|
87
|
+
subject { instance.send(:send_metrics) }
|
88
|
+
|
89
|
+
it 're-inits metrics' do
|
90
|
+
instance.increment(:test, 60)
|
91
|
+
expect { subject }.to change(instance, :metrics).to({ :timing => {}, :counter => {} })
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns nil when there are no metrics to send' do
|
95
|
+
expect(instance.metrics[:timing]).to be_empty
|
96
|
+
expect(instance.metrics[:counter]).to be_empty
|
97
|
+
instance.sender.should_not_receive(:send_metrics)
|
98
|
+
expect(subject).to be_nil
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns nil after sending metrics' do
|
102
|
+
instance.increment(:test, 60)
|
103
|
+
instance.should_not_receive(:log)
|
104
|
+
instance.sender.should_receive(:send_metrics)
|
105
|
+
expect(subject).to be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when constructingtiming metrics' do
|
109
|
+
before(:each) { 10.times { |i| instance.timing(:test, i) } }
|
110
|
+
after(:each) { subject }
|
111
|
+
|
112
|
+
it 'includes the mean value' do
|
113
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(['test:mean 4.5'])))
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'includes the median value' do
|
117
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(['test:median 5'])))
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'includes the percentile_90 value' do
|
121
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(['test:percentile_90 9'])))
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'includes the min value' do
|
125
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(['test:min 0'])))
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'includes the max value' do
|
129
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(['test:max 9'])))
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'includes the stddev value' do
|
133
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(/test:stddev 3.027/)))
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'includes a count of total metrics' do
|
137
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including(['test 10'])))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'when constructing counter metrics' do
|
142
|
+
it 'sums the values of each metric' do
|
143
|
+
10.times { instance.increment(:test, 1) }
|
144
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:metrics => array_including('test 10')))
|
145
|
+
subject
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when sending metrics' do
|
150
|
+
before(:each) { instance.increment(:test, 1) }
|
151
|
+
after(:each) { subject }
|
152
|
+
|
153
|
+
it 'executes batches of 100' do
|
154
|
+
199.times { |i| instance.increment(:"test_#{i}", 1) }
|
155
|
+
instance.sender.should_receive(:send_metrics).exactly(2).times
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'includes the configured environment' do
|
159
|
+
Honeybadger.configure do |c|
|
160
|
+
c.environment_name = 'asdf'
|
161
|
+
end
|
162
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:environment => 'asdf'))
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'includes the configured hostname' do
|
166
|
+
Honeybadger.configure do |c|
|
167
|
+
c.hostname = 'zxcv'
|
168
|
+
end
|
169
|
+
instance.sender.should_receive(:send_metrics).with(hash_including(:hostname => 'zxcv'))
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when an exception occurrs' do
|
174
|
+
before(:each) do
|
175
|
+
instance.increment(:test, 1)
|
176
|
+
instance.sender.stub(:send_metrics).and_raise(RuntimeError.new('cobra attack!'))
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'logs the exception' do
|
180
|
+
instance.should_receive(:log).with(:error, /cobra attack/)
|
181
|
+
expect { subject }.not_to raise_error
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'returns nil' do
|
185
|
+
expect(subject).to be_nil
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
|
-
require '
|
1
|
+
require 'spec_helper'
|
2
2
|
require 'json'
|
3
|
+
require 'rack'
|
3
4
|
|
4
|
-
|
5
|
+
describe Honeybadger::Notice do
|
5
6
|
def configure
|
6
7
|
Honeybadger::Configuration.new.tap do |config|
|
7
8
|
config.api_key = 'abc123def456'
|
@@ -14,7 +15,7 @@ class NoticeTest < Test::Unit::TestCase
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def stub_request(attrs = {})
|
17
|
-
|
18
|
+
double('request', { :parameters => { 'one' => 'two' },
|
18
19
|
:protocol => 'http',
|
19
20
|
:host => 'some.host',
|
20
21
|
:request_uri => '/some/uri',
|
@@ -24,103 +25,100 @@ class NoticeTest < Test::Unit::TestCase
|
|
24
25
|
|
25
26
|
context '#deliver' do
|
26
27
|
context 'sender is configured' do
|
27
|
-
|
28
|
+
it "delivers to sender" do
|
28
29
|
sender = stub_sender!
|
29
30
|
notice = build_notice
|
30
|
-
notice.
|
31
|
+
notice.stub(:to_json => { :foo => 'bar' })
|
31
32
|
|
33
|
+
sender.should_receive(:send_to_honeybadger).with(notice)
|
32
34
|
notice.deliver
|
33
|
-
|
34
|
-
assert_received(sender, :send_to_honeybadger) { |expect| expect.with(notice) }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
context 'sender is not configured' do
|
39
|
-
|
39
|
+
it "returns false" do
|
40
40
|
notice = build_notice
|
41
41
|
Honeybadger.sender = nil
|
42
|
-
|
42
|
+
expect(notice.deliver).to be_false
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
it "generates json from as_json template" do
|
48
48
|
notice = build_notice
|
49
49
|
hash = {'foo' => 'bar'}
|
50
|
-
notice.
|
50
|
+
notice.should_receive(:as_json).once.and_return(hash)
|
51
51
|
json = notice.to_json
|
52
52
|
|
53
53
|
payload = nil
|
54
|
-
|
55
|
-
payload = JSON.parse(json)
|
56
|
-
end
|
54
|
+
expect { payload = JSON.parse(json) }.not_to raise_error
|
57
55
|
|
58
|
-
|
56
|
+
expect(payload).to eq hash
|
59
57
|
end
|
60
58
|
|
61
|
-
|
59
|
+
it "accepts a project root" do
|
62
60
|
project_root = '/path/to/project'
|
63
61
|
notice = build_notice(:project_root => project_root)
|
64
|
-
|
62
|
+
expect(notice.project_root).to eq project_root
|
65
63
|
end
|
66
64
|
|
67
|
-
|
68
|
-
|
65
|
+
it "accepts a component" do
|
66
|
+
expect(build_notice(:component => 'users_controller').controller).to eq 'users_controller'
|
69
67
|
end
|
70
68
|
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
it "aliases the component as controller" do
|
70
|
+
expect(build_notice(:controller => 'users_controller').component).to eq 'users_controller'
|
71
|
+
expect(build_notice(:controller => 'users_controller').controller).to eq 'users_controller'
|
74
72
|
end
|
75
73
|
|
76
|
-
|
77
|
-
|
74
|
+
it "accepts a action" do
|
75
|
+
expect(build_notice(:action => 'index').action).to eq 'index'
|
78
76
|
end
|
79
77
|
|
80
|
-
|
81
|
-
|
78
|
+
it "accepts source excerpt radius" do
|
79
|
+
expect(build_notice(:source_extract_radius => 3).source_extract_radius).to eq 3
|
82
80
|
end
|
83
81
|
|
84
|
-
|
82
|
+
it "accepts a url" do
|
85
83
|
url = 'http://some.host/uri'
|
86
84
|
notice = build_notice(:url => url)
|
87
|
-
|
85
|
+
expect(notice.url).to eq url
|
88
86
|
end
|
89
87
|
|
90
|
-
|
88
|
+
it "sets the host name" do
|
91
89
|
notice = build_notice
|
92
|
-
|
90
|
+
expect(notice.hostname).to eq hostname
|
93
91
|
end
|
94
92
|
|
95
|
-
|
93
|
+
it "overrides the host name" do
|
96
94
|
notice = build_notice({ :hostname => 'asdf' })
|
97
|
-
|
95
|
+
expect(notice.hostname).to eq 'asdf'
|
98
96
|
end
|
99
97
|
|
100
98
|
context "custom fingerprint" do
|
101
|
-
|
99
|
+
it "includes nil fingerprint when no fingerprint is specified" do
|
102
100
|
notice = build_notice
|
103
|
-
|
101
|
+
expect(notice.fingerprint).to be_nil
|
104
102
|
end
|
105
103
|
|
106
|
-
|
104
|
+
it "accepts fingerprint as string" do
|
107
105
|
notice = build_notice({ :fingerprint => 'foo' })
|
108
|
-
|
106
|
+
expect(notice.fingerprint).to eq '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
|
109
107
|
end
|
110
108
|
|
111
|
-
|
112
|
-
notice = build_notice({ :fingerprint =>
|
113
|
-
|
109
|
+
it "accepts fingerprint responding to #call" do
|
110
|
+
notice = build_notice({ :fingerprint => double(:call => 'foo') })
|
111
|
+
expect(notice.fingerprint).to eq '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
|
114
112
|
end
|
115
113
|
|
116
|
-
|
117
|
-
notice = build_notice({ :fingerprint =>
|
118
|
-
|
114
|
+
it "accepts fingerprint using #to_s" do
|
115
|
+
notice = build_notice({ :fingerprint => double(:to_s => 'foo') })
|
116
|
+
expect(notice.fingerprint).to eq '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
|
119
117
|
end
|
120
118
|
end
|
121
119
|
|
122
120
|
context "with a backtrace" do
|
123
|
-
|
121
|
+
before(:each) do
|
124
122
|
@source = <<-RUBY
|
125
123
|
$:<<'lib'
|
126
124
|
require 'honeybadger'
|
@@ -141,73 +139,69 @@ class NoticeTest < Test::Unit::TestCase
|
|
141
139
|
@exception.set_backtrace(@backtrace_array)
|
142
140
|
end
|
143
141
|
|
144
|
-
|
145
|
-
Honeybadger::Backtrace.
|
142
|
+
it "passes its backtrace filters for parsing" do
|
143
|
+
Honeybadger::Backtrace.should_receive(:parse).with(@backtrace_array, {:filters => 'foo'}).and_return(double(:lines => []))
|
146
144
|
|
147
|
-
|
145
|
+
Honeybadger::Notice.new({:exception => @exception, :backtrace_filters => 'foo'})
|
148
146
|
end
|
149
147
|
|
150
|
-
|
148
|
+
it "passes its backtrace line filters for parsing" do
|
151
149
|
@backtrace_array.each do |line|
|
152
|
-
Honeybadger::Backtrace::Line.
|
150
|
+
Honeybadger::Backtrace::Line.should_receive(:parse).with(line, {:filters => 'foo'})
|
153
151
|
end
|
154
152
|
|
155
|
-
|
153
|
+
Honeybadger::Notice.new({:exception => @exception, :backtrace_filters => 'foo'})
|
156
154
|
end
|
157
155
|
|
158
|
-
|
156
|
+
it "accepts a backtrace from an exception or hash" do
|
159
157
|
backtrace = Honeybadger::Backtrace.parse(@backtrace_array)
|
160
158
|
notice_from_exception = build_notice(:exception => @exception)
|
161
159
|
|
162
|
-
|
163
|
-
notice_from_exception.backtrace,
|
164
|
-
"backtrace was not correctly set from an exception"
|
160
|
+
expect(notice_from_exception.backtrace).to eq backtrace # backtrace was not correctly set from an exception
|
165
161
|
|
166
162
|
notice_from_hash = build_notice(:backtrace => @backtrace_array)
|
167
|
-
|
168
|
-
notice_from_hash.backtrace,
|
169
|
-
"backtrace was not correctly set from a hash"
|
163
|
+
expect(notice_from_hash.backtrace).to eq backtrace # backtrace was not correctly set from a hash
|
170
164
|
end
|
171
165
|
|
172
166
|
context "without application trace" do
|
173
|
-
|
167
|
+
before(:each) do
|
174
168
|
Honeybadger.configuration.project_root = '/foo/bar'
|
175
169
|
@string_io = StringIO.new(@source)
|
176
|
-
File.
|
177
|
-
File.
|
170
|
+
File.stub(:exists?).with('my/file/backtrace').and_return true
|
171
|
+
File.stub(:open).with('my/file/backtrace').and_yield @string_io
|
178
172
|
end
|
179
173
|
|
180
|
-
|
174
|
+
it "includes source extract from backtrace" do
|
181
175
|
backtrace = Honeybadger::Backtrace.parse(@backtrace_array)
|
182
176
|
notice_from_exception = build_notice(:exception => @exception)
|
183
177
|
@string_io.rewind
|
184
178
|
|
185
|
-
|
186
|
-
|
179
|
+
expect(notice_from_exception.source_extract).not_to be_empty # Expected backtrace source extract to be found
|
180
|
+
expect(notice_from_exception.source_extract).to eq backtrace.lines.first.source
|
187
181
|
end
|
188
182
|
end
|
189
183
|
|
190
184
|
context 'with an application trace' do
|
191
|
-
|
185
|
+
before(:each) do
|
192
186
|
Honeybadger.configuration.project_root = 'test/honeybadger/'
|
193
187
|
|
194
188
|
@string_io = StringIO.new(@source)
|
195
|
-
File.
|
196
|
-
File.
|
189
|
+
File.stub(:exists?).with('test/honeybadger/rack_test.rb').and_return true
|
190
|
+
File.stub(:open).with('test/honeybadger/rack_test.rb').and_yield @string_io
|
197
191
|
end
|
198
192
|
|
199
|
-
|
193
|
+
it "includes source extract from first line of application trace" do
|
200
194
|
backtrace = Honeybadger::Backtrace.parse(@backtrace_array)
|
201
195
|
notice_from_exception = build_notice(:exception => @exception)
|
202
196
|
@string_io.rewind
|
203
197
|
|
204
|
-
|
205
|
-
|
198
|
+
expect(notice_from_exception.source_extract).not_to be_empty # Expected backtrace source extract to be found
|
199
|
+
expect(notice_from_exception.source_extract).to eq backtrace.lines[1].source
|
206
200
|
end
|
207
201
|
end
|
208
202
|
end
|
209
203
|
|
210
|
-
|
204
|
+
it "Uses source extract from view when reporting an ActionView::Template::Error" do
|
211
205
|
# TODO: I would like to stub out a real ActionView::Template::Error, but we're
|
212
206
|
# currently locked at actionpack 2.3.8. Perhaps if one day we upgrade...
|
213
207
|
source = <<-ERB
|
@@ -217,73 +211,73 @@ class NoticeTest < Test::Unit::TestCase
|
|
217
211
|
4: <div>
|
218
212
|
ERB
|
219
213
|
exception = build_exception
|
220
|
-
exception.
|
214
|
+
exception.stub(:source_extract).and_return(source)
|
221
215
|
notice = Honeybadger::Notice.new({:exception => exception})
|
222
216
|
|
223
|
-
|
217
|
+
expect(notice.source_extract).to eq({ '1' => ' <%= current_user.name %>', '2' => '</div>', '3' => '', '4' => '<div>'})
|
224
218
|
end
|
225
219
|
|
226
|
-
|
220
|
+
it "sets the error class from an exception or hash" do
|
227
221
|
assert_accepts_exception_attribute :error_class do |exception|
|
228
222
|
exception.class.name
|
229
223
|
end
|
230
224
|
end
|
231
225
|
|
232
|
-
|
226
|
+
it "sets the error message from an exception or hash" do
|
233
227
|
assert_accepts_exception_attribute :error_message do |exception|
|
234
228
|
"#{exception.class.name}: #{exception.message}"
|
235
229
|
end
|
236
230
|
end
|
237
231
|
|
238
|
-
|
232
|
+
it "accepts parameters from a request or hash" do
|
239
233
|
parameters = { 'one' => 'two' }
|
240
234
|
notice_from_hash = build_notice(:parameters => parameters)
|
241
|
-
|
235
|
+
expect(notice_from_hash.parameters).to eq parameters
|
242
236
|
end
|
243
237
|
|
244
|
-
|
238
|
+
it "accepts session data from a session[:data] hash" do
|
245
239
|
data = { 'one' => 'two' }
|
246
240
|
notice = build_notice(:session => { :data => data })
|
247
|
-
|
241
|
+
expect(notice.session_data).to eq data
|
248
242
|
end
|
249
243
|
|
250
|
-
|
244
|
+
it "accepts session data from a session_data hash" do
|
251
245
|
data = { 'one' => 'two' }
|
252
246
|
notice = build_notice(:session_data => data)
|
253
|
-
|
247
|
+
expect(notice.session_data).to eq data
|
254
248
|
end
|
255
249
|
|
256
|
-
|
257
|
-
|
250
|
+
it "accepts an environment name" do
|
251
|
+
expect(build_notice(:environment_name => 'development').environment_name).to eq 'development'
|
258
252
|
end
|
259
253
|
|
260
|
-
|
254
|
+
it "accepts CGI data from a hash" do
|
261
255
|
data = { 'string' => 'value' }
|
262
256
|
notice = build_notice(:cgi_data => data)
|
263
|
-
|
257
|
+
expect(notice.cgi_data).to eq data
|
264
258
|
end
|
265
259
|
|
266
|
-
|
260
|
+
it "accepts notifier information" do
|
267
261
|
params = { :notifier_name => 'a name for a notifier',
|
268
262
|
:notifier_version => '1.0.5',
|
269
263
|
:notifier_url => 'http://notifiers.r.us/download' }
|
270
264
|
notice = build_notice(params)
|
271
|
-
|
272
|
-
|
273
|
-
|
265
|
+
expect(notice.notifier_name).to eq params[:notifier_name]
|
266
|
+
expect(notice.notifier_version).to eq params[:notifier_version]
|
267
|
+
expect(notice.notifier_url).to eq params[:notifier_url]
|
274
268
|
end
|
275
269
|
|
276
|
-
|
270
|
+
it "sets sensible defaults without an exception" do
|
277
271
|
backtrace = Honeybadger::Backtrace.parse(build_backtrace_array)
|
278
272
|
notice = build_notice(:backtrace => build_backtrace_array)
|
279
273
|
|
280
|
-
|
274
|
+
expect(notice.error_message).to eq 'Notification'
|
281
275
|
assert_array_starts_with backtrace.lines, notice.backtrace.lines
|
282
|
-
|
283
|
-
|
276
|
+
expect(notice.parameters).to be_empty
|
277
|
+
expect(notice.session_data).to be_empty
|
284
278
|
end
|
285
279
|
|
286
|
-
|
280
|
+
it "uses the caller as the backtrace for an exception without a backtrace" do
|
287
281
|
filters = Honeybadger::Configuration.new.backtrace_filters
|
288
282
|
backtrace = Honeybadger::Backtrace.parse(caller, :filters => filters)
|
289
283
|
notice = build_notice(:exception => StandardError.new('error'), :backtrace => nil)
|
@@ -291,110 +285,108 @@ class NoticeTest < Test::Unit::TestCase
|
|
291
285
|
assert_array_starts_with backtrace.lines, notice.backtrace.lines
|
292
286
|
end
|
293
287
|
|
294
|
-
|
288
|
+
it "converts unserializable objects to strings" do
|
295
289
|
assert_serializes_hash(:parameters)
|
296
290
|
assert_serializes_hash(:cgi_data)
|
297
291
|
assert_serializes_hash(:session_data)
|
298
292
|
end
|
299
293
|
|
300
|
-
|
294
|
+
it "filters parameters" do
|
301
295
|
assert_filters_hash(:parameters)
|
302
296
|
end
|
303
297
|
|
304
|
-
|
298
|
+
it "filters cgi data" do
|
305
299
|
assert_filters_hash(:cgi_data)
|
306
300
|
end
|
307
301
|
|
308
|
-
|
302
|
+
it "filters session" do
|
309
303
|
assert_filters_hash(:session_data)
|
310
304
|
end
|
311
305
|
|
312
|
-
|
306
|
+
it "removes rack.request.form_vars" do
|
313
307
|
original = {
|
314
308
|
"rack.request.form_vars" => "story%5Btitle%5D=The+TODO+label",
|
315
309
|
"abc" => "123"
|
316
310
|
}
|
317
311
|
|
318
312
|
notice = build_notice(:cgi_data => original)
|
319
|
-
|
313
|
+
expect(notice.cgi_data).to eq({"abc" => "123"})
|
320
314
|
end
|
321
315
|
|
322
|
-
|
316
|
+
it "does not send empty request data" do
|
323
317
|
notice = build_notice
|
324
|
-
|
325
|
-
|
326
|
-
|
318
|
+
notice.url.should be_nil
|
319
|
+
notice.controller.should be_nil
|
320
|
+
notice.action.should be_nil
|
327
321
|
|
328
322
|
json = notice.to_json
|
329
323
|
payload = JSON.parse(json)
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
324
|
+
payload['request']['url'].should be_nil
|
325
|
+
payload['request']['component'].should be_nil
|
326
|
+
payload['request']['action'].should be_nil
|
327
|
+
payload['request']['user'].should be_nil
|
334
328
|
end
|
335
329
|
|
336
330
|
%w(url controller action).each do |var|
|
337
|
-
|
331
|
+
it "sends a request if #{var} is present" do
|
338
332
|
notice = build_notice(var.to_sym => 'value')
|
339
333
|
json = notice.to_json
|
340
334
|
payload = JSON.parse(json)
|
341
|
-
|
335
|
+
payload['request'].should_not be_nil
|
342
336
|
end
|
343
337
|
end
|
344
338
|
|
345
339
|
%w(parameters cgi_data session_data context).each do |var|
|
346
|
-
|
340
|
+
it "sends a request if #{var} is present" do
|
347
341
|
notice = build_notice(var.to_sym => { 'key' => 'value' })
|
348
342
|
json = notice.to_json
|
349
343
|
payload = JSON.parse(json)
|
350
|
-
|
344
|
+
payload['request'].should_not be_nil
|
351
345
|
end
|
352
346
|
end
|
353
347
|
|
354
|
-
|
348
|
+
it "does not ignore an exception not matching ignore filters" do
|
355
349
|
notice = build_notice(:error_class => 'ArgumentError',
|
356
350
|
:ignore => ['Argument'],
|
357
|
-
:ignore_by_filters => [lambda { |
|
358
|
-
|
351
|
+
:ignore_by_filters => [lambda { |n| false }])
|
352
|
+
expect(notice.ignore?).to be_false
|
359
353
|
end
|
360
354
|
|
361
|
-
|
355
|
+
it "ignores an exception with a matching error class" do
|
362
356
|
notice = build_notice(:error_class => 'ArgumentError',
|
363
357
|
:ignore => [ArgumentError])
|
364
|
-
|
358
|
+
expect(notice.ignore?).to be_true
|
365
359
|
end
|
366
360
|
|
367
|
-
|
361
|
+
it "ignores an exception with an equal error class name" do
|
368
362
|
notice = build_notice(:error_class => 'ArgumentError',
|
369
363
|
:ignore => ['ArgumentError'])
|
370
|
-
|
364
|
+
expect(notice.ignore?).to be_true # Expected ArgumentError to ignore ArgumentError
|
371
365
|
end
|
372
366
|
|
373
|
-
|
367
|
+
it "ignores an exception matching error class name" do
|
374
368
|
notice = build_notice(:error_class => 'ArgumentError',
|
375
369
|
:ignore => [/Error$/])
|
376
|
-
|
370
|
+
expect(notice.ignore?).to be_true # Expected /Error$/ to ignore ArgumentError
|
377
371
|
end
|
378
372
|
|
379
|
-
|
373
|
+
it "ignores an exception that inherits from ignored error class" do
|
380
374
|
class ::FooError < ArgumentError ; end
|
381
375
|
notice = build_notice(:exception => FooError.new('Oh noes!'),
|
382
376
|
:ignore => [ArgumentError])
|
383
|
-
|
377
|
+
expect(notice.ignore?).to be_true # Expected ArgumentError to ignore FooError
|
384
378
|
end
|
385
379
|
|
386
|
-
|
380
|
+
it "ignores an exception with a matching filter" do
|
387
381
|
filter = lambda {|notice| notice.error_class == 'ArgumentError' }
|
388
382
|
notice = build_notice(:error_class => 'ArgumentError',
|
389
383
|
:ignore_by_filters => [filter])
|
390
|
-
|
384
|
+
expect(notice.ignore?).to be_true
|
391
385
|
end
|
392
386
|
|
393
|
-
|
387
|
+
it "does not raise without an ignore list" do
|
394
388
|
notice = build_notice(:ignore => nil, :ignore_by_filters => nil)
|
395
|
-
|
396
|
-
notice.ignore?
|
397
|
-
end
|
389
|
+
expect { notice.ignore? }.not_to raise_error
|
398
390
|
end
|
399
391
|
|
400
392
|
ignored_error_classes = %w(
|
@@ -407,127 +399,123 @@ class NoticeTest < Test::Unit::TestCase
|
|
407
399
|
)
|
408
400
|
|
409
401
|
ignored_error_classes.each do |ignored_error_class|
|
410
|
-
|
402
|
+
it "ignores #{ignored_error_class} error by default" do
|
411
403
|
notice = build_notice(:error_class => ignored_error_class)
|
412
|
-
|
404
|
+
expect(notice.ignore?).to be_true
|
413
405
|
end
|
414
406
|
end
|
415
407
|
|
416
|
-
|
408
|
+
it "acts like a hash" do
|
417
409
|
notice = build_notice(:error_message => 'some message')
|
418
|
-
|
410
|
+
expect(notice[:error_message]).to eq notice.error_message
|
419
411
|
end
|
420
412
|
|
421
|
-
|
413
|
+
it "returns params on notice[:request][:params]" do
|
422
414
|
params = { 'one' => 'two' }
|
423
415
|
notice = build_notice(:parameters => params)
|
424
|
-
|
416
|
+
expect(notice[:request][:params]).to eq params
|
425
417
|
end
|
426
418
|
|
427
|
-
|
419
|
+
it "returns context on notice[:request][:context]" do
|
428
420
|
context = { 'one' => 'two' }
|
429
421
|
notice = build_notice(:context => context)
|
430
|
-
|
422
|
+
expect(notice[:request][:context]).to eq context
|
431
423
|
end
|
432
424
|
|
433
|
-
|
425
|
+
it "merges context from args with context from Honeybadger#context" do
|
434
426
|
Honeybadger.context({ 'one' => 'two', 'foo' => 'bar' })
|
435
427
|
notice = build_notice(:context => { 'three' => 'four', 'foo' => 'baz' })
|
436
|
-
|
428
|
+
expect(notice[:request][:context]).to eq({ 'one' => 'two', 'three' => 'four', 'foo' => 'baz' })
|
437
429
|
end
|
438
430
|
|
439
|
-
|
431
|
+
it "returns nil context when context is not set" do
|
440
432
|
notice = build_notice
|
441
|
-
|
433
|
+
notice[:request][:context].should be_nil
|
442
434
|
end
|
443
435
|
|
444
|
-
|
445
|
-
|
446
|
-
build_notice(:session => { :object => stub(:to_hash => {}) })
|
447
|
-
end
|
436
|
+
it "ensures #to_hash is called on objects that support it" do
|
437
|
+
expect { build_notice(:session => { :object => double(:to_hash => {}) }) }.not_to raise_error
|
448
438
|
end
|
449
439
|
|
450
|
-
|
451
|
-
|
452
|
-
build_notice(:session => { :object => stub(:to_ary => {}) })
|
453
|
-
end
|
440
|
+
it "ensures #to_ary is called on objects that support it" do
|
441
|
+
expect { build_notice(:session => { :object => double(:to_ary => {}) }) }.not_to raise_error
|
454
442
|
end
|
455
443
|
|
456
|
-
|
444
|
+
it "extracts data from a rack environment hash" do
|
457
445
|
url = "https://subdomain.happylane.com:100/test/file.rb?var=value&var2=value2"
|
458
446
|
parameters = { 'var' => 'value', 'var2' => 'value2' }
|
459
447
|
env = Rack::MockRequest.env_for(url)
|
460
448
|
|
461
449
|
notice = build_notice(:rack_env => env)
|
462
450
|
|
463
|
-
|
464
|
-
|
465
|
-
|
451
|
+
expect(notice.url).to eq url
|
452
|
+
expect(notice.parameters).to eq parameters
|
453
|
+
expect(notice.cgi_data['REQUEST_METHOD']).to eq 'GET'
|
466
454
|
end
|
467
455
|
|
468
|
-
|
456
|
+
it "extracts data from a rack environment hash with action_dispatch info" do
|
469
457
|
params = { 'controller' => 'users', 'action' => 'index', 'id' => '7' }
|
470
458
|
env = Rack::MockRequest.env_for('/', { 'action_dispatch.request.parameters' => params })
|
471
459
|
|
472
460
|
notice = build_notice(:rack_env => env)
|
473
461
|
|
474
|
-
|
475
|
-
|
476
|
-
|
462
|
+
expect(notice.parameters).to eq params
|
463
|
+
expect(notice.component).to eq params['controller']
|
464
|
+
expect(notice.action).to eq params['action']
|
477
465
|
end
|
478
466
|
|
479
|
-
|
467
|
+
it "extracts session data from a rack environment" do
|
480
468
|
session_data = { 'something' => 'some value' }
|
481
469
|
env = Rack::MockRequest.env_for('/', 'rack.session' => session_data)
|
482
470
|
|
483
471
|
notice = build_notice(:rack_env => env)
|
484
472
|
|
485
|
-
|
473
|
+
expect(notice.session_data).to eq session_data
|
486
474
|
end
|
487
475
|
|
488
|
-
|
476
|
+
it "prefers passed session data to rack session data" do
|
489
477
|
session_data = { 'something' => 'some value' }
|
490
478
|
env = Rack::MockRequest.env_for('/')
|
491
479
|
|
492
480
|
notice = build_notice(:rack_env => env, :session_data => session_data)
|
493
481
|
|
494
|
-
|
482
|
+
expect(notice.session_data).to eq session_data
|
495
483
|
end
|
496
484
|
|
497
485
|
unless Gem::Version.new(Rack.release) < Gem::Version.new('1.2')
|
498
|
-
|
486
|
+
it "fails gracefully when Rack params cannot be parsed" do
|
499
487
|
rack_env = Rack::MockRequest.env_for('http://www.example.com/explode', :method => 'POST', :input => 'foo=bar&bar=baz%')
|
500
488
|
notice = Honeybadger::Notice.new(:rack_env => rack_env)
|
501
|
-
|
502
|
-
|
489
|
+
expect(notice.params.size).to eq 1
|
490
|
+
expect(notice.params[:error]).to match(/Failed to call params on Rack::Request/)
|
503
491
|
end
|
504
492
|
end
|
505
493
|
|
506
|
-
|
494
|
+
it "does not send session data when send_request_session is false" do
|
507
495
|
notice = build_notice(:send_request_session => false, :session_data => { :foo => :bar })
|
508
|
-
|
496
|
+
notice.session_data.should be_nil
|
509
497
|
end
|
510
498
|
|
511
|
-
|
499
|
+
it "does not allow infinite recursion" do
|
512
500
|
hash = {:a => :a}
|
513
501
|
hash[:hash] = hash
|
514
502
|
notice = Honeybadger::Notice.new(:parameters => hash)
|
515
|
-
|
503
|
+
expect(notice.parameters[:hash]).to eq "[possible infinite recursion halted]"
|
516
504
|
end
|
517
505
|
|
518
|
-
|
506
|
+
it "trims error message to 1k" do
|
519
507
|
message = 'asdfghjkl'*200
|
520
508
|
e = StandardError.new(message)
|
521
509
|
notice = Honeybadger::Notice.new(:exception => e)
|
522
|
-
|
523
|
-
|
510
|
+
message.bytesize.should > 1024
|
511
|
+
expect(notice.error_message.bytesize).to eq 1024
|
524
512
|
end
|
525
513
|
|
526
|
-
|
514
|
+
it "prefers notice args to exception attributes" do
|
527
515
|
e = RuntimeError.new('Not very helpful')
|
528
516
|
notice = Honeybadger::Notice.new(:exception => e, :error_class => 'MyClass', :error_message => 'Something very specific went wrong.')
|
529
|
-
|
530
|
-
|
517
|
+
expect(notice.error_class).to eq 'MyClass'
|
518
|
+
expect(notice.error_message).to eq 'Something very specific went wrong.'
|
531
519
|
end
|
532
520
|
|
533
521
|
def assert_accepts_exception_attribute(attribute, args = {}, &block)
|
@@ -537,14 +525,10 @@ class NoticeTest < Test::Unit::TestCase
|
|
537
525
|
|
538
526
|
notice_from_exception = build_notice(args.merge(:exception => exception))
|
539
527
|
|
540
|
-
|
541
|
-
value,
|
542
|
-
"#{attribute} was not correctly set from an exception"
|
528
|
+
expect(notice_from_exception.send(attribute)).to eq value
|
543
529
|
|
544
530
|
notice_from_hash = build_notice(args.merge(attribute => value))
|
545
|
-
|
546
|
-
value,
|
547
|
-
"#{attribute} was not correctly set from a hash"
|
531
|
+
expect(notice_from_hash.send(attribute)).to eq value
|
548
532
|
end
|
549
533
|
|
550
534
|
def assert_serializes_hash(attribute)
|
@@ -558,11 +542,12 @@ class NoticeTest < Test::Unit::TestCase
|
|
558
542
|
}
|
559
543
|
notice = build_notice(attribute => hash)
|
560
544
|
hash = notice.send(attribute)
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
545
|
+
expect(object.to_s).to eq hash[:strange_object] # objects should be serialized
|
546
|
+
|
547
|
+
expect(hash[:sub_hash]).to be_a Hash # subhashes should be kept
|
548
|
+
expect(object.to_s).to eq hash[:sub_hash][:sub_object] # subhash members should be serialized
|
549
|
+
expect(hash[:array]).to be_a Array # arrays should be kept
|
550
|
+
expect(object.to_s).to eq hash[:array].first # array members should be serialized
|
566
551
|
end
|
567
552
|
end
|
568
553
|
|
@@ -578,8 +563,7 @@ class NoticeTest < Test::Unit::TestCase
|
|
578
563
|
|
579
564
|
notice = build_notice(:params_filters => filters, attribute => original)
|
580
565
|
|
581
|
-
|
582
|
-
notice.send(attribute))
|
566
|
+
expect(notice.send(attribute)).to eq filtered
|
583
567
|
end
|
584
568
|
|
585
569
|
def build_backtrace_array
|