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.
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