errorapp_notifier 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|