errorapp_notifier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/MIT-LICENSE +25 -0
  6. data/README.md +60 -0
  7. data/Rakefile +8 -0
  8. data/errorapp_notifier.gemspec +29 -0
  9. data/lib/errorapp_notifier/action_controller_methods.rb +35 -0
  10. data/lib/errorapp_notifier/application_environment_data.rb +77 -0
  11. data/lib/errorapp_notifier/config.rb +67 -0
  12. data/lib/errorapp_notifier/controller_failure_data.rb +96 -0
  13. data/lib/errorapp_notifier/exception_data.rb +19 -0
  14. data/lib/errorapp_notifier/failure_data.rb +87 -0
  15. data/lib/errorapp_notifier/monkeypatches.rb +11 -0
  16. data/lib/errorapp_notifier/notifier.rb +80 -0
  17. data/lib/errorapp_notifier/notifiers/rack_rails.rb +32 -0
  18. data/lib/errorapp_notifier/notifiers/rails.rb +34 -0
  19. data/lib/errorapp_notifier/notifiers/tester.rb +16 -0
  20. data/lib/errorapp_notifier/notify.rb +59 -0
  21. data/lib/errorapp_notifier/rack_failure_data.rb +29 -0
  22. data/lib/errorapp_notifier/railtie.rb +48 -0
  23. data/lib/errorapp_notifier/sanitizer.rb +56 -0
  24. data/lib/errorapp_notifier/tasks/errorapp_notifier.rake +6 -0
  25. data/lib/errorapp_notifier/version.rb +3 -0
  26. data/lib/errorapp_notifier.rb +74 -0
  27. data/lib/generators/errorapp_notifier/errorapp_notifier_generator.rb +54 -0
  28. data/lib/generators/errorapp_notifier/templates/errorapp_notifier.rb +2 -0
  29. data/spec/errorapp_notifier/config_spec.rb +76 -0
  30. data/spec/errorapp_notifier/controller_failure_data_spec.rb +277 -0
  31. data/spec/errorapp_notifier/failure_data_spec.rb +32 -0
  32. data/spec/errorapp_notifier/notifier_spec.rb +31 -0
  33. data/spec/errorapp_notifier/notify_spec.rb +128 -0
  34. data/spec/errorapp_notifier/rack_failure_data_spec.rb +87 -0
  35. data/spec/errorapp_notifier/sanitizer_spec.rb +57 -0
  36. data/spec/helper.rb +12 -0
  37. data/spec/spec_helper.rb +16 -0
  38. 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
@@ -0,0 +1,12 @@
1
+ class TestException < StandardError
2
+ attr_accessor :message, :backtrace
3
+
4
+ def initialize(opts={})
5
+ @message = opts[:message] || deafult_message
6
+ @backtrace = opts[:backtrace]
7
+ end
8
+
9
+ def deafult_message
10
+ "Something is not good"
11
+ end
12
+ end
@@ -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