errorapp_notifier 0.1.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/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +25 -0
- data/README.md +60 -0
- data/Rakefile +8 -0
- data/errorapp_notifier.gemspec +29 -0
- data/lib/errorapp_notifier/action_controller_methods.rb +35 -0
- data/lib/errorapp_notifier/application_environment_data.rb +77 -0
- data/lib/errorapp_notifier/config.rb +67 -0
- data/lib/errorapp_notifier/controller_failure_data.rb +96 -0
- data/lib/errorapp_notifier/exception_data.rb +19 -0
- data/lib/errorapp_notifier/failure_data.rb +87 -0
- data/lib/errorapp_notifier/monkeypatches.rb +11 -0
- data/lib/errorapp_notifier/notifier.rb +80 -0
- data/lib/errorapp_notifier/notifiers/rack_rails.rb +32 -0
- data/lib/errorapp_notifier/notifiers/rails.rb +34 -0
- data/lib/errorapp_notifier/notifiers/tester.rb +16 -0
- data/lib/errorapp_notifier/notify.rb +59 -0
- data/lib/errorapp_notifier/rack_failure_data.rb +29 -0
- data/lib/errorapp_notifier/railtie.rb +48 -0
- data/lib/errorapp_notifier/sanitizer.rb +56 -0
- data/lib/errorapp_notifier/tasks/errorapp_notifier.rake +6 -0
- data/lib/errorapp_notifier/version.rb +3 -0
- data/lib/errorapp_notifier.rb +74 -0
- data/lib/generators/errorapp_notifier/errorapp_notifier_generator.rb +54 -0
- data/lib/generators/errorapp_notifier/templates/errorapp_notifier.rb +2 -0
- data/spec/errorapp_notifier/config_spec.rb +76 -0
- data/spec/errorapp_notifier/controller_failure_data_spec.rb +277 -0
- data/spec/errorapp_notifier/failure_data_spec.rb +32 -0
- data/spec/errorapp_notifier/notifier_spec.rb +31 -0
- data/spec/errorapp_notifier/notify_spec.rb +128 -0
- data/spec/errorapp_notifier/rack_failure_data_spec.rb +87 -0
- data/spec/errorapp_notifier/sanitizer_spec.rb +57 -0
- data/spec/helper.rb +12 -0
- data/spec/spec_helper.rb +16 -0
- metadata +188 -0
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ErrorappNotifier::ControllerFailureData do
|
4
|
+
|
5
|
+
class ErrorappNotifier::OmgError < StandardError
|
6
|
+
def backtrace
|
7
|
+
['omg-backtrace']
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class BrokenJSON
|
12
|
+
def to_json
|
13
|
+
omg
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "parses session data" do
|
18
|
+
request = ActionDispatch::TestRequest.new
|
19
|
+
brokenJson = BrokenJSON.new
|
20
|
+
session = {:foo => brokenJson}
|
21
|
+
request.stub(:session).and_return(session)
|
22
|
+
data = ErrorappNotifier::ControllerFailureData.new(ErrorappNotifier::OmgError.new, nil, request)
|
23
|
+
|
24
|
+
JSON.parse(data.to_json)['request']['session']['data'].should == {'foo' => brokenJson.to_s}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises useful error when to_json isn't available on to_hash" do
|
28
|
+
data = ErrorappNotifier::FailureData.new(ErrorappNotifier::OmgError.new)
|
29
|
+
hash_without_json = {}
|
30
|
+
hash_without_json.stub(:to_json).and_raise(NoMethodError)
|
31
|
+
data.stub(:to_hash).and_return(hash_without_json)
|
32
|
+
expect { data.to_json }.to raise_exception(/to_json/)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'when no request/controller/params' do
|
36
|
+
before do
|
37
|
+
ENV['LOGNAME'] = 'bob'
|
38
|
+
ENV['SOMETHING_SECRET'] = 'secretPasswords'
|
39
|
+
ENV['DATABASE_URL'] = 'something'
|
40
|
+
ENV['SOMETHING_INTERESTING'] = 'instagram'
|
41
|
+
ENV['HTTP_SOMETHING'] = 'should be stripped'
|
42
|
+
ENV['FILTERED_BY_OLD_FILTER_CONFIG'] = 'should_be_filtered'
|
43
|
+
ErrorappNotifier::ENVIRONMENT_WHITELIST << /_INTERESTING/
|
44
|
+
ErrorappNotifier::ENVIRONMENT_WHITELIST << 'FILTERED_BY_OLD_FILTER_CONFIG'
|
45
|
+
ErrorappNotifier::ENVIRONMENT_FILTER << 'FILTERED_BY_OLD_FILTER_CONFIG'
|
46
|
+
RAILS_ENV = 'test' unless defined?(RAILS_ENV)
|
47
|
+
@occured_at = Time.mktime(1970, 1, 1)
|
48
|
+
Time.stub(:now).and_return(Time.mktime(1970, 1, 1))
|
49
|
+
error = ErrorappNotifier::OmgError.new('some message')
|
50
|
+
@data = ErrorappNotifier::ControllerFailureData.new(error)
|
51
|
+
@hash = @data.to_hash
|
52
|
+
end
|
53
|
+
|
54
|
+
it "capture exception details" do
|
55
|
+
error_hash = @hash[:exception]
|
56
|
+
error_hash[:exception_class].should == 'ErrorappNotifier::OmgError'
|
57
|
+
error_hash[:message].should == 'some message'
|
58
|
+
error_hash[:backtrace].should == ['omg-backtrace']
|
59
|
+
DateTime.parse(error_hash[:occurred_at]).should == @occured_at
|
60
|
+
client_hash = @hash[:client]
|
61
|
+
client_hash[:name].should == ErrorappNotifier::CLIENT_NAME
|
62
|
+
client_hash[:version].should == ErrorappNotifier::VERSION
|
63
|
+
client_hash[:protocol_version].should == ErrorappNotifier::PROTOCOL_VERSION
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
it "has sensible initial ENVIRONMENT_WHITELIST" do
|
68
|
+
%w(HOME PATH PWD RUBYOPT GEM_HOME RACK_ENV RAILS_ENV BUNDLE_GEMFILE BUNDLE_BIN_PATH).each do |expected_to_be_whitelisted|
|
69
|
+
ErrorappNotifier::ENVIRONMENT_WHITELIST.should include(expected_to_be_whitelisted)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "uses a whitelist for ENV variables aswell as existing filter" do
|
74
|
+
env = @hash[:application_environment][:env]
|
75
|
+
env['SOMETHING_SECRET'].should be_nil
|
76
|
+
env['DATABASE_URL'].should be_nil
|
77
|
+
env['HTTP_SOMETHING'].should be_nil
|
78
|
+
env['FILTERED_BY_OLD_FILTER_CONFIG'].should be_nil
|
79
|
+
env['SOMETHING_INTERESTING'].should == 'instagram'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "generates parseable json" do
|
83
|
+
require 'json'
|
84
|
+
JSON.parse(@data.to_json)['exception']['exception_class'].should == 'ErrorappNotifier::OmgError'
|
85
|
+
end
|
86
|
+
|
87
|
+
it "capture application_environment" do
|
88
|
+
application_env_hash = @hash[:application_environment]
|
89
|
+
application_env_hash[:environment].should == 'test'
|
90
|
+
application_env_hash[:env].should_not be_nil
|
91
|
+
application_env_hash[:host].should == `hostname`.strip
|
92
|
+
application_env_hash[:run_as_user].should == 'bob'
|
93
|
+
application_env_hash[:application_root_directory].should == Dir.pwd
|
94
|
+
application_env_hash[:language].should == 'ruby'
|
95
|
+
application_env_hash[:language_version].should == "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} #{RUBY_RELEASE_DATE} #{RUBY_PLATFORM}"
|
96
|
+
application_env_hash[:libraries_loaded]['rspec'].should == '2.14.0'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'with request/controller/params' do
|
101
|
+
|
102
|
+
class ErrorappNotifier::SomeController
|
103
|
+
end
|
104
|
+
|
105
|
+
before :each do
|
106
|
+
@controller = ErrorappNotifier::SomeController.new
|
107
|
+
@request = ActionDispatch::TestRequest.new
|
108
|
+
@request.stub(:parameters).and_return({'var1' => 'abc', 'action' => 'some_action', 'filter_me' => 'private'})
|
109
|
+
@request.stub(:url).and_return('http://youtube.com/watch?v=oHg5SJYRHA0')
|
110
|
+
@request.stub(:request_method).and_return(:get)
|
111
|
+
@request.stub(:remote_ip).and_return('1.2.3.4')
|
112
|
+
@request.stub(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_CONTENT_TYPE' => 'text/html'})
|
113
|
+
@request.env["action_dispatch.parameter_filter"] = [:filter_me]
|
114
|
+
@error = ErrorappNotifier::OmgError.new('some message')
|
115
|
+
data = ErrorappNotifier::ControllerFailureData.new(@error, @controller, @request)
|
116
|
+
@hash = data.to_hash
|
117
|
+
end
|
118
|
+
|
119
|
+
it "captures request" do
|
120
|
+
request_hash = @hash[:request]
|
121
|
+
request_hash[:url].should == 'http://youtube.com/watch?v=oHg5SJYRHA0'
|
122
|
+
request_hash[:controller].should == 'ErrorappNotifier::SomeController'
|
123
|
+
request_hash[:action].should == 'some_action'
|
124
|
+
request_hash[:parameters].should == {'var1' => 'abc', 'action' => 'some_action', 'filter_me' => '[FILTERED]'}
|
125
|
+
request_hash[:request_method].should == 'get'
|
126
|
+
request_hash[:remote_ip].should == '1.2.3.4'
|
127
|
+
request_hash[:headers].should == {'Content-Type' => 'text/html'}
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
it "filter params specified in env['action_dispatch.parameter_filter']" do
|
132
|
+
@request.stub(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_CONTENT_TYPE' => 'text/html', 'action_dispatch.parameter_filter' => [:var1]})
|
133
|
+
@request.stub(:parameters).and_return({'var1' => 'abc'})
|
134
|
+
data = ErrorappNotifier::ControllerFailureData.new(@error, @controller, @request)
|
135
|
+
data.to_hash[:request][:parameters].should == {'var1' => '[FILTERED]'}
|
136
|
+
end
|
137
|
+
|
138
|
+
it "filter nested params specified in env['action_dispatch.parameter_filter']" do
|
139
|
+
@request.stub(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_CONTENT_TYPE' => 'text/html', 'action_dispatch.parameter_filter' => [:var1]})
|
140
|
+
@request.stub(:parameters).and_return({'var1' => {'var2' => 'abc','var3' => "abc"}})
|
141
|
+
data = ErrorappNotifier::ControllerFailureData.new(@error, @controller, @request)
|
142
|
+
data.to_hash[:request][:parameters].should == {'var1' => '[FILTERED]'}
|
143
|
+
end
|
144
|
+
|
145
|
+
it "formats the occurred_at as iso8601" do
|
146
|
+
@request.stub(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_CONTENT_TYPE' => 'text/html', 'action_dispatch.parameter_filter' => [:var1]})
|
147
|
+
@request.stub(:parameters).and_return({'var1' => 'abc'})
|
148
|
+
data = ErrorappNotifier::ControllerFailureData.new(@error, @controller, @request)
|
149
|
+
data.to_hash[:exception][:occurred_at].should match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.{1,6}$/)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "filter session cookies from headers" do
|
153
|
+
@request.stub(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_COOKIE' => '_something_else=faafsafafasfa; _myapp-lick-nation_session=BAh7DDoMbnVtYmVyc1sJaQZpB2kIaQk6FnNvbWVfY3Jhenlfb2JqZWN0bzobU3Bpa2VDb250cm9sbGVyOjpDcmF6eQY6CUBiYXJABzoTc29tZXRoaW5nX2Vsc2UiCGNjYzoKYXBwbGUiDUJyYWVidXJuOgloYXNoewdpBmkHaQhpCToPc2Vzc2lvbl9pZCIlMmJjZTM4MjVjMThkNzYxOWEyZDA4NTJhNWY1NGQzMmU6C3RvbWF0byIJQmVlZg%3D%3D--66fb4606851f06bf409b8bc4ba7aea47f0259bf7'})
|
154
|
+
@hash = ErrorappNotifier::ControllerFailureData.new(ErrorappNotifier::OmgError.new('some message'), @controller, @request).to_hash
|
155
|
+
@hash[:request][:headers].should == {'Cookie' => '_something_else=faafsafafasfa; _myapp-lick-nation_session=[FILTERED]'}
|
156
|
+
end
|
157
|
+
|
158
|
+
it "creates a uniq_key from backtrace" do
|
159
|
+
exception = Exception.new
|
160
|
+
exception.stub(:backtrace).and_return(['123'])
|
161
|
+
data = ErrorappNotifier::ControllerFailureData.new(exception)
|
162
|
+
data.uniq_key.should == Digest::MD5.hexdigest('123')
|
163
|
+
end
|
164
|
+
|
165
|
+
it "creates a nil uniq_key if nil backtrace" do
|
166
|
+
exception = Exception.new
|
167
|
+
exception.stub(:backtrace).and_return(nil)
|
168
|
+
data = ErrorappNotifier::ControllerFailureData.new(exception)
|
169
|
+
data.uniq_key.should == nil
|
170
|
+
end
|
171
|
+
|
172
|
+
it "creates a uniq_key from backtrace" do
|
173
|
+
exception = Exception.new
|
174
|
+
exception.stub(:backtrace).and_return([])
|
175
|
+
data = ErrorappNotifier::ControllerFailureData.new(exception)
|
176
|
+
data.uniq_key.should == nil
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe ErrorappNotifier::ControllerDataExtractor do
|
182
|
+
before do
|
183
|
+
@request = double(
|
184
|
+
:protocol => "http://",
|
185
|
+
:host => "errorapp",
|
186
|
+
:request_uri => "/projects",
|
187
|
+
:params =>
|
188
|
+
{
|
189
|
+
"action" => "index",
|
190
|
+
"controller" => "projects",
|
191
|
+
"foo" => "bar"
|
192
|
+
},
|
193
|
+
:request_method => "GET",
|
194
|
+
:ip => "1.2.3.4",
|
195
|
+
:env => "fuzzy"
|
196
|
+
)
|
197
|
+
@controller = "Controller"
|
198
|
+
end
|
199
|
+
|
200
|
+
subject { ErrorappNotifier::ControllerDataExtractor.new(@controller, @request) }
|
201
|
+
|
202
|
+
it "should extract the controller class" do
|
203
|
+
subject.controller.should == "String"
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should extract the URL" do
|
207
|
+
subject.url.should == "http://errorapp/projects"
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should extract action" do
|
211
|
+
subject.action.should == "index"
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should extract parameters" do
|
215
|
+
subject.parameters.should == {
|
216
|
+
"action" => "index",
|
217
|
+
"controller" => "projects",
|
218
|
+
"foo" => "bar"
|
219
|
+
}
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should extract request method" do
|
223
|
+
subject.request_method.should == "GET"
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should extract remote ip" do
|
227
|
+
subject.remote_ip.should == "1.2.3.4"
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should extract env" do
|
231
|
+
subject.env.should == "fuzzy"
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should make request available" do
|
235
|
+
subject.request.should == @request
|
236
|
+
end
|
237
|
+
|
238
|
+
context "with params available in request" do
|
239
|
+
before do
|
240
|
+
@request = double(
|
241
|
+
:url => "http://errorapp/projects",
|
242
|
+
:parameters =>
|
243
|
+
{
|
244
|
+
"action" => "index",
|
245
|
+
"controller" => "projects",
|
246
|
+
"foo" => "bar"
|
247
|
+
},
|
248
|
+
:request_method => "GET",
|
249
|
+
:remote_ip => "1.2.3.4",
|
250
|
+
:env => "fuzzy"
|
251
|
+
)
|
252
|
+
@controller = "Controller"
|
253
|
+
end
|
254
|
+
|
255
|
+
subject { ErrorappNotifier::ControllerDataExtractor.new(@controller, @request) }
|
256
|
+
|
257
|
+
it "should extract the URL" do
|
258
|
+
subject.url.should == "http://errorapp/projects"
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should extract action" do
|
262
|
+
subject.action.should == "index"
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should extract parameters" do
|
266
|
+
subject.parameters.should == {
|
267
|
+
"action" => "index",
|
268
|
+
"controller" => "projects",
|
269
|
+
"foo" => "bar"
|
270
|
+
}
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should extract remote ip" do
|
274
|
+
subject.remote_ip.should == "1.2.3.4"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
describe ErrorappNotifier::FailureData do
|
6
|
+
describe "#to_hash" do
|
7
|
+
before do
|
8
|
+
@exception = ErrorappNotifier::FailureData.new(build_exception).to_hash
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should get exception data" do
|
12
|
+
expect(@exception[:exception][:exception_class]).to eq("TestException")
|
13
|
+
expect(@exception[:exception][:message]).to match(/Something is not good/)
|
14
|
+
expect(@exception[:exception][:backtrace]).not_to be_empty
|
15
|
+
expect(@exception[:exception][:occurred_at]).to eq(Time.now.utc.iso8601)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should get application data" do
|
19
|
+
expect(@exception[:application_environment][:environment]).to eq('test')
|
20
|
+
|
21
|
+
expect(@exception[:application_environment][:env].class).to eq(Hash)
|
22
|
+
expect(@exception[:application_environment][:env].keys).to include('PATH','HOME', 'GEM_HOME', 'BUNDLE_BIN_PATH')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_exception
|
28
|
+
backtrace = ["errorapp/spec/helper.rb:8:in `deafult_message'",
|
29
|
+
"errorapp/spec/spec_helper.rb:4:in `require'",
|
30
|
+
"lib/active_support/dependencies.rb:247:in `require'"]
|
31
|
+
TestException.new(:backtrace => backtrace)
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ErrorappNotifier::Notifier do
|
4
|
+
before :each do
|
5
|
+
ErrorappNotifier.configure do|config|
|
6
|
+
config.api_key = 'api-key'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should call notify_error to send exception data' do
|
11
|
+
ErrorappNotifier::Notifier.stub(:notify_error)
|
12
|
+
exception_data = double(:uniq_key => 1, :to_json => 'something')
|
13
|
+
|
14
|
+
ErrorappNotifier::Notifier.notify_error(exception_data)
|
15
|
+
|
16
|
+
expect(ErrorappNotifier::Notifier).to have_received(:notify_error)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'notify_error' do
|
20
|
+
it 'should get 200 when sending exception' do
|
21
|
+
exception_data = double(:uniq_key => 1, :to_json => 'something')
|
22
|
+
|
23
|
+
stub_request(
|
24
|
+
:post,
|
25
|
+
"http://errorapp.com/api/projects/api-key/fails?hash=#{exception_data.uniq_key}&protocol_version=#{ErrorappNotifier::PROTOCOL_VERSION}"
|
26
|
+
).with(:body => exception_data.to_json).to_return(:status => 200, :body => "", :headers => {})
|
27
|
+
|
28
|
+
ErrorappNotifier::Notifier.notify_error(exception_data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ErrorappNotifier::Notify do
|
4
|
+
describe "when ErrorappNotifier reporting is on" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@config = ErrorappNotifier::configuration
|
8
|
+
@config.stub(:should_send_to_api?).and_return(true)
|
9
|
+
@config.ignore_user_agents = []
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#notify" do
|
13
|
+
it "should create FailureData object and send json to the api" do
|
14
|
+
exception = double("exception")
|
15
|
+
name = double("name")
|
16
|
+
args = [exception, name]
|
17
|
+
data = double("data")
|
18
|
+
|
19
|
+
ErrorappNotifier::FailureData.should_receive(:new).with(*args).and_return(data)
|
20
|
+
ErrorappNotifier::Notifier.should_receive(:notify_error).with(data)
|
21
|
+
ErrorappNotifier::Notify.notify(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#notify_with_controller" do
|
26
|
+
it "should create ControllerFailureData object and send json to the api" do
|
27
|
+
exception = double('exception')
|
28
|
+
controller = double('controller')
|
29
|
+
request = double('request')
|
30
|
+
args = [exception, controller, request]
|
31
|
+
data = double("data")
|
32
|
+
|
33
|
+
ErrorappNotifier::ControllerFailureData.should_receive(:new).with(*args).and_return(data)
|
34
|
+
ErrorappNotifier::Notifier.should_receive(:notify_error).with(data)
|
35
|
+
ErrorappNotifier::Notify.notify_with_controller(*args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#notify_with_rack" do
|
40
|
+
it "should create RackFailureData object and send json to the api" do
|
41
|
+
exception = double("exception")
|
42
|
+
environment = double("environment")
|
43
|
+
request = double("request")
|
44
|
+
args = [exception, environment, request]
|
45
|
+
data = double("data")
|
46
|
+
|
47
|
+
ErrorappNotifier::RackFailureData.should_receive(:new).with(*args).and_return(data)
|
48
|
+
ErrorappNotifier::Notifier.should_receive(:notify_error).with(data)
|
49
|
+
ErrorappNotifier::Notify.notify_with_rack(*args)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#ignore?" do
|
54
|
+
|
55
|
+
before do
|
56
|
+
@exception = double('exception')
|
57
|
+
@controller = double('controller')
|
58
|
+
@request = double('request')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should check for ignored classes and agents" do
|
62
|
+
ErrorappNotifier::Notify.should_receive(:ignore_class?).with(@exception)
|
63
|
+
ErrorappNotifier::Notify.should_receive(:ignore_user_agent?).with(@request)
|
64
|
+
ErrorappNotifier::ControllerFailureData.should_receive(:new).
|
65
|
+
with(@exception,@controller,@request).
|
66
|
+
and_return(data = double('data'))
|
67
|
+
ErrorappNotifier::Notifier.should_receive(:notify_error).with(data)
|
68
|
+
|
69
|
+
ErrorappNotifier::Notify.notify_with_controller(@exception,
|
70
|
+
@controller,
|
71
|
+
@request)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should ignore exceptions by class name" do
|
75
|
+
request = double("request")
|
76
|
+
exception = double("exception")
|
77
|
+
exception.stub(:class).and_return("ignore_me")
|
78
|
+
exception.should_receive(:class)
|
79
|
+
|
80
|
+
@config.ignore_exceptions = ["ignore_me",/funky/]
|
81
|
+
ErrorappNotifier::Notify.ignore_class?(exception).should be_true
|
82
|
+
funky_exception = double("exception")
|
83
|
+
funky_exception.stub(:class).and_return("really_funky_exception")
|
84
|
+
funky_exception.should_receive(:class)
|
85
|
+
|
86
|
+
ErrorappNotifier::Notify.ignore_class?(funky_exception).should be_true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should ignore exceptions by user agent" do
|
90
|
+
request = double("request")
|
91
|
+
request.stub(:user_agent).and_return("botmeister")
|
92
|
+
request.should_receive(:user_agent)
|
93
|
+
|
94
|
+
@config.ignore_user_agents = [/bot/]
|
95
|
+
ErrorappNotifier::Notify.ignore_user_agent?(request).should be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "when ErrorappNotifier reporting is off" do
|
102
|
+
|
103
|
+
before do
|
104
|
+
ErrorappNotifier::Config.stub(:should_send_to_api?).and_return(false)
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#notify, #notify_with_controller and #notify_with_rack" do
|
108
|
+
|
109
|
+
it "should reraise the exception and not report it" do
|
110
|
+
exception = double('exception')
|
111
|
+
controller = double('controller')
|
112
|
+
request = double('request')
|
113
|
+
|
114
|
+
ErrorappNotifier::ControllerFailureData.should_not_receive(:new)
|
115
|
+
ErrorappNotifier::Notifier.should_not_receive(:notify_error)
|
116
|
+
|
117
|
+
["rails", "rack", ""].each do |notify|
|
118
|
+
method_name = "notify"
|
119
|
+
method_name << "_with_#{notify}" unless notify.empty?
|
120
|
+
expect do
|
121
|
+
ErrorappNotifier::Notify.send(method_name, exception, controller, request)
|
122
|
+
end.to raise_exception
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
describe ErrorappNotifier::RackFailureData do
|
6
|
+
|
7
|
+
class ErrorappNotifier::OmgError < StandardError
|
8
|
+
def backtrace
|
9
|
+
['omg-backtrace']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before :each do
|
14
|
+
app = lambda { |env| [200, {'Content-Type'=>'text/plain'}, 'Hello World']}
|
15
|
+
|
16
|
+
@env = {
|
17
|
+
"HTTP_HOST" =>"localhost:4242",
|
18
|
+
"HTTP_ACCEPT" =>"*/*",
|
19
|
+
"SERVER_NAME" =>"localhost",
|
20
|
+
"REQUEST_PATH" =>"/",
|
21
|
+
"rack.url_scheme" =>"http",
|
22
|
+
"HTTP_USER_AGENT" =>"curl/7.19.6 (i386-apple-darwin9.8.0) libcurl/7.19.6 zlib/1.2.3",
|
23
|
+
"REMOTE_HOST" =>"testing.com",
|
24
|
+
"rack.errors" => StringIO.new,
|
25
|
+
"SERVER_PROTOCOL" =>"HTTP/1.1",
|
26
|
+
"rack.version" =>[1, 1],
|
27
|
+
"rack.run_once" =>false,
|
28
|
+
"SERVER_SOFTWARE" =>"WEBrick/1.3.1 (Ruby/1.8.7/2009-06-12)",
|
29
|
+
"REMOTE_ADDR" =>"127.0.0.1",
|
30
|
+
"PATH_INFO" => "/",
|
31
|
+
"SCRIPT_NAME" =>"",
|
32
|
+
"HTTP_VERSION" =>"HTTP/1.1",
|
33
|
+
"rack.multithread" =>true,
|
34
|
+
"rack.multiprocess" =>false,
|
35
|
+
"REQUEST_URI" =>"http://localhost:4242/",
|
36
|
+
"SERVER_PORT" =>"4242",
|
37
|
+
"REQUEST_METHOD" =>"GET",
|
38
|
+
"rack.input" => StringIO.new,
|
39
|
+
"QUERY_STRING" =>"cockle=shell&bay=cool",
|
40
|
+
"GATEWAY_INTERFACE" =>"CGI/1.1"
|
41
|
+
}
|
42
|
+
|
43
|
+
error = ErrorappNotifier::OmgError.new('some message')
|
44
|
+
request = Rack::Request.new(@env)
|
45
|
+
@data = ErrorappNotifier::RackFailureData.new(error, @env, request)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "capture exception details" do
|
49
|
+
error_hash = @data.to_hash[:exception]
|
50
|
+
error_hash[:exception_class].should == 'ErrorappNotifier::OmgError'
|
51
|
+
error_hash[:message].should == 'some message'
|
52
|
+
error_hash[:backtrace].should == ['omg-backtrace']
|
53
|
+
DateTime.parse(error_hash[:occurred_at]).should == DateTime.parse(Time.now.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should capture request details" do
|
57
|
+
request_hash = @data.to_hash[:request]
|
58
|
+
request_hash[:url].should == 'http://localhost:4242/?cockle=shell&bay=cool'
|
59
|
+
request_hash[:parameters].should == {"cockle"=>"shell", "bay"=>"cool"}
|
60
|
+
request_hash[:request_method].should == 'GET'
|
61
|
+
request_hash[:remote_ip].should == '127.0.0.1'
|
62
|
+
request_hash[:headers].should == {"Host"=>"localhost:4242", "Accept"=>"*/*", "User-Agent"=>"curl/7.19.6 (i386-apple-darwin9.8.0) libcurl/7.19.6 zlib/1.2.3", "Version"=>"HTTP/1.1"}
|
63
|
+
request_hash[:session].should == {'data' => {}, 'session_id' => ''}
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should capture client detais" do
|
67
|
+
client_hash = @data.to_hash[:client]
|
68
|
+
client_hash[:name].should == 'errorapp_notifier-gem'
|
69
|
+
client_hash[:version].should == ErrorappNotifier::VERSION
|
70
|
+
client_hash[:protocol_version].should == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should captire application environment" do
|
74
|
+
env_hash = @data.to_hash[:application_environment]
|
75
|
+
env_hash[:env].should_not be_empty
|
76
|
+
env_hash[:libraries_loaded].should_not be_empty
|
77
|
+
env_hash[:language].should == 'ruby'
|
78
|
+
|
79
|
+
env_hash[:language_version].should_not be_empty
|
80
|
+
env_hash[:environment].should == 'test'
|
81
|
+
env_hash[:application_root_directory].should_not be_empty
|
82
|
+
env_hash[:run_as_user].should_not be_empty
|
83
|
+
env_hash[:host].should_not be_empty
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ErrorappNotifier::Sanitizer do
|
4
|
+
describe '.sanitize_hash' do
|
5
|
+
it "filter out objects that aren't jsonable" do
|
6
|
+
class Crazy
|
7
|
+
def initialize
|
8
|
+
@bar = self
|
9
|
+
end
|
10
|
+
end
|
11
|
+
crazy = Crazy.new
|
12
|
+
input = {'crazy' => crazy, :simple => '123',
|
13
|
+
:some_hash => {'1' => '2'}, :array => ['1', '2']}
|
14
|
+
ErrorappNotifier::Sanitizer.sanitize_hash(input).should == {'crazy' => crazy.to_s,
|
15
|
+
:simple => '123', :some_hash => {'1' => '2'}, :array => ['1', '2']}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "to_strings regex because JSON.parse(/aa/.to_json) doesn't work" do
|
19
|
+
input = {'crazy' => /abc.*/}
|
20
|
+
ErrorappNotifier::Sanitizer.sanitize_hash(input).should == {'crazy' => /abc.*/.to_s}
|
21
|
+
end
|
22
|
+
|
23
|
+
it "handles session objects with various interfaces" do
|
24
|
+
class SessionWithInstanceVariables
|
25
|
+
def initialize
|
26
|
+
@data = {'a' => '1', 'b' => /hello there Im a regex/i}
|
27
|
+
@session_id = '123'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
request = ActionDispatch::TestRequest.new
|
32
|
+
session = SessionWithInstanceVariables.new
|
33
|
+
request.stub(:session).and_return(session)
|
34
|
+
request.stub(:session_options).and_return({})
|
35
|
+
ErrorappNotifier::Sanitizer.sanitize_session(request).should == {'session_id' => '123', 'data' => {'a' => '1', 'b' => "(?i-mx:hello there Im a regex)"}}
|
36
|
+
session = double('session', :session_id => '123', :instance_variable_get => {'a' => '1', 'b' => /another(.+) regex/mx})
|
37
|
+
request.stub(:session).and_return(session)
|
38
|
+
ErrorappNotifier::Sanitizer.sanitize_session(request).should == {'session_id' => '123', 'data' => {'a' => '1', 'b' => "(?mx-i:another(.+) regex)"}}
|
39
|
+
session = double('session', :session_id => nil, :to_hash => {:session_id => '123', 'a' => '1'})
|
40
|
+
request.stub(:session).and_return(session)
|
41
|
+
ErrorappNotifier::Sanitizer.sanitize_session(request).should == {'session_id' => '123', 'data' => {'a' => '1'}}
|
42
|
+
request.stub(:session_options).and_return({:id => 'xyz'})
|
43
|
+
ErrorappNotifier::Sanitizer.sanitize_session(request).should == {'session_id' => 'xyz', 'data' => {'a' => '1'}}
|
44
|
+
end
|
45
|
+
|
46
|
+
it "allow if non jsonable objects are hidden in an array" do
|
47
|
+
class Bonkers
|
48
|
+
def to_json
|
49
|
+
no.can.do!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
crazy = Bonkers.new
|
53
|
+
input = {'crazy' => [crazy]}
|
54
|
+
ErrorappNotifier::Sanitizer.sanitize_hash(input).should == {'crazy' => [crazy.to_s]}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/helper.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'errorapp_notifier'
|
2
|
+
require 'action_controller'
|
3
|
+
require 'json'
|
4
|
+
require 'webmock/rspec'
|
5
|
+
require 'helper'
|
6
|
+
|
7
|
+
ENV['RAILS_ENV'] = 'test'
|
8
|
+
|
9
|
+
WebMock.disable_net_connect!(:allow_localhost => true)
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
config.filter_run :focus
|
15
|
+
config.order = 'random'
|
16
|
+
end
|