crashlog 0.0.1 → 0.0.2

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 (77) hide show
  1. data/.gitignore +2 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +161 -0
  6. data/INSTALL +22 -0
  7. data/README.md +14 -4
  8. data/Rakefile +13 -0
  9. data/crashlog.gemspec +12 -3
  10. data/generators/crashlog/templates/initializer.rb +6 -0
  11. data/install.rb +2 -0
  12. data/lib/crash_log/backtrace/line.rb +105 -0
  13. data/lib/crash_log/backtrace/line_cache.rb +23 -0
  14. data/lib/crash_log/backtrace.rb +66 -0
  15. data/lib/crash_log/configuration.bak.rb +199 -0
  16. data/lib/crash_log/configuration.rb +188 -0
  17. data/lib/crash_log/logging.rb +51 -0
  18. data/lib/crash_log/payload.rb +157 -0
  19. data/lib/crash_log/rack.rb +47 -0
  20. data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
  21. data/lib/crash_log/rails/controller_methods.rb +45 -0
  22. data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
  23. data/lib/crash_log/rails.rb +32 -0
  24. data/lib/crash_log/railtie.rb +41 -0
  25. data/lib/crash_log/reporter.rb +105 -0
  26. data/lib/crash_log/system_information.rb +64 -0
  27. data/lib/crash_log/templates/payload.rabl +7 -0
  28. data/lib/crash_log/version.rb +1 -1
  29. data/lib/crash_log.rb +118 -0
  30. data/lib/faraday/request/hmac_authentication.rb +73 -0
  31. data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
  32. data/rails/init.rb +1 -0
  33. data/spec/crash_log/backtrace_spec.rb +79 -0
  34. data/spec/crash_log/initializer_spec.rb +53 -0
  35. data/spec/crash_log/payload_spec.rb +124 -0
  36. data/spec/crash_log/reporter_spec.rb +179 -0
  37. data/spec/crash_log_spec.rb +153 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/controllers/break_controller.rb +10 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/.gitkeep +0 -0
  46. data/spec/dummy/app/models/.gitkeep +0 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/config/application.rb +59 -0
  49. data/spec/dummy/config/boot.rb +10 -0
  50. data/spec/dummy/config/database.yml +44 -0
  51. data/spec/dummy/config/environment.rb +5 -0
  52. data/spec/dummy/config/environments/development.rb +37 -0
  53. data/spec/dummy/config/environments/production.rb +67 -0
  54. data/spec/dummy/config/environments/test.rb +37 -0
  55. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/spec/dummy/config/initializers/crashlog.rb +6 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/routes.rb +6 -0
  63. data/spec/dummy/config.ru +4 -0
  64. data/spec/dummy/lib/assets/.gitkeep +0 -0
  65. data/spec/dummy/log/.gitkeep +0 -0
  66. data/spec/dummy/public/404.html +26 -0
  67. data/spec/dummy/public/422.html +26 -0
  68. data/spec/dummy/public/500.html +25 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/dummy/script/rails +6 -0
  71. data/spec/requests/rack_spec.rb +29 -0
  72. data/spec/requests/rails_controller_rescue_spec.rb +46 -0
  73. data/spec/spec_helper.rb +23 -0
  74. data/spec/support/doing.rb +1 -0
  75. data/spec/support/dummy_app.rb +13 -0
  76. data/spec/support/hash_ext.rb +7 -0
  77. metadata +197 -7
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ # require 'crash_log/rails'
3
+
4
+ describe "Initializer" do
5
+
6
+ let(:logger) { stub("Logger") }
7
+ let(:other_logger) { stub("OtherLogger") }
8
+
9
+ # before(:all) do
10
+ # load_dummy_app
11
+ # end
12
+
13
+ describe 'auto configure logger' do
14
+ before do
15
+ load_dummy_app
16
+ # unless defined?(Rails)
17
+ # module Rails
18
+ # end
19
+ # end
20
+ Rails.stub(:logger).and_return(logger)
21
+ logger.stub(:error)
22
+ other_logger.stub(:error)
23
+ end
24
+
25
+
26
+ it 'detects presence of Rails logger' do
27
+ pending
28
+ # CrashLog::Rails.__send__(:initialize)
29
+ CrashLog.logger.should be(logger)
30
+ end
31
+
32
+ it "allows overriding of the logger if already assigned" do
33
+ pending
34
+ # unless defined?(::Rails)
35
+ # module Rails
36
+ # end
37
+ # end
38
+
39
+ Rails.stub(:logger).and_return(logger)
40
+
41
+ CrashLog.logger.should_not == logger
42
+ # CrashLog::Rails.initialize
43
+ CrashLog.logger.should == logger
44
+
45
+ CrashLog.configure do |config|
46
+ config.logger = other_logger
47
+ end
48
+
49
+ CrashLog.logger.should == other_logger
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+ require 'json_spec'
3
+
4
+ describe CrashLog::Payload do
5
+ include JsonSpec
6
+
7
+ let(:configuration) do
8
+ stub('configuration').tap do |config|
9
+ config.stub(:[])
10
+ end
11
+ end
12
+
13
+ subject { CrashLog::Payload.build(raised_error, configuration) }
14
+
15
+ let(:raised_error) do
16
+ begin
17
+ raise RuntimeError, "This broke"
18
+ rescue RuntimeError => e
19
+ e
20
+ end
21
+ end
22
+
23
+ describe '#add_context' do
24
+ it 'user_data should be empty' do
25
+ subject.context.should be_empty
26
+ end
27
+
28
+ it 'merges in new user data' do
29
+ data = {:email => "user@example.com"}
30
+ subject.add_context(data)
31
+ subject.context.should == data
32
+ end
33
+ end
34
+
35
+ describe '#add_session_data' do
36
+ it 'is empty by default' do
37
+ subject.environment[:session].should be_nil
38
+ end
39
+
40
+ it 'allows merging in data' do
41
+ data = {:path => '/problematic/path'}
42
+ subject.add_session_data(data)
43
+ subject.environment[:session].should == data
44
+ end
45
+
46
+ it 'allows adding more data' do
47
+ data_1 = {:path => '/problematic/path'}
48
+ data_2 = {:count => 42}
49
+ subject.add_session_data(data_1)
50
+ subject.add_session_data(data_2)
51
+
52
+ subject.environment[:session][:path].should == data_1[:path]
53
+ subject.environment[:session][:count].should == data_2[:count]
54
+ end
55
+ end
56
+
57
+ describe 'private.unwrap_exception' do
58
+ it 'unwraps exception objects' do
59
+ subject.__send__(:unwrap_exception, raised_error).should == raised_error
60
+ end
61
+ end
62
+
63
+ describe '#body' do
64
+
65
+ describe 'notifier' do
66
+ it 'has name' do
67
+ subject.body.to_json.should have_json_path('notifier/name')
68
+ end
69
+
70
+ it 'has version' do
71
+ subject.body.to_json.should have_json_path('notifier/version')
72
+ end
73
+ end
74
+
75
+ describe 'exception' do
76
+
77
+ it 'has class_name' do
78
+ subject.body.to_json.should have_json_path('event/type')
79
+ end
80
+
81
+ it 'has message' do
82
+ subject.body.to_json.should have_json_path('event/message')
83
+ end
84
+
85
+ describe 'backtrace' do
86
+ it 'has line number' do
87
+ subject.body.to_json.should have_json_path('backtrace/0/number')
88
+ end
89
+
90
+ it 'has integer as line number' do
91
+ subject.body.to_json.should have_json_type(Integer).at_path('backtrace/0/number')
92
+ end
93
+
94
+ it 'has filename' do
95
+ subject.body.to_json.should have_json_path('backtrace/0/file')
96
+ end
97
+
98
+ it 'has method' do
99
+ subject.body.to_json.should have_json_path('backtrace/0/method')
100
+ end
101
+ end
102
+
103
+ it 'has backtrace' do
104
+ subject.body.to_json.should have_json_path('backtrace/0')
105
+ end
106
+
107
+ it 'has timestamp' do
108
+ subject.body.to_json.should have_json_path('event/timestamp')
109
+ end
110
+ end
111
+
112
+ describe 'session' do
113
+
114
+ end
115
+
116
+ describe 'user_data' do
117
+ it 'has first key provided by user' do
118
+ pending
119
+ # subject.add_user_data({:email => "user@example.com"})
120
+ # subject.body.to_json.should have_json_path('user_data/email')
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrashLog::Reporter do
4
+ let(:uuid) { UUID.generate }
5
+
6
+ let(:config) {
7
+ CrashLog::Configuration.new.tap do |config|
8
+ config.project_id = 'PROJECT_ID'
9
+ config.api_key = 'API_KEY'
10
+ # config.adapter = test_adapter
11
+ config.scheme = 'http'
12
+ end
13
+ }
14
+
15
+ # let(:config) { stub("Configuration", {
16
+ # :host => "io.crashlog.io",
17
+ # :scheme => "https",
18
+ # :port => 443,
19
+ # :endpoint => '/notify',
20
+ # :announce => true,
21
+ # :announce_endpoint => '/announce',
22
+ # :dry_run => false,
23
+ # :api_key => 'API_TOKEN',
24
+ # :project_id => "PROJECT_ID",
25
+ # :http_read_timeout => 1,
26
+ # :http_open_timeout => 1
27
+ # }).tap do |config|
28
+ # config.stub(:adapter).and_return(:test, &test_adapter)
29
+ # end
30
+ # }
31
+
32
+ let(:test_adapter) {
33
+ lambda { |stub|
34
+ stub.post('/events') do |env|
35
+ [201, {}, env[:request_headers]]
36
+ end
37
+ }
38
+ }
39
+
40
+ subject { CrashLog::Reporter.new(config) }
41
+
42
+ let(:positive_response) do
43
+ {:result_url => "https://crashlog.io/collect/#{uuid}"}
44
+ end
45
+
46
+ let(:announce_response) do
47
+ {:application => "CrashLog Test"}
48
+ end
49
+
50
+ let(:positive_response_json) { positive_response.to_json }
51
+ let(:announce_json) { announce_response.to_json }
52
+
53
+ let(:payload) {
54
+ {}
55
+ }
56
+
57
+ before do
58
+ CrashLog.stub(:report_for_duty!)
59
+ end
60
+
61
+ it 'should not be doing a dry run' do
62
+ subject.should_not be_dry_run
63
+ end
64
+
65
+ describe '#notify' do
66
+ before do
67
+ test_connection = Faraday.new(:url => subject.url) do |builder|
68
+ builder.adapter :test, stubs
69
+ builder.request :hmac_authentication, 'PROJECT_ID', 'SECRET', {:service_id => 'CrashLog'}
70
+ builder.request :url_encoded
71
+ end
72
+
73
+ subject.stub(:connection).and_return(test_connection)
74
+ end
75
+
76
+ let!(:stubs) do
77
+ Faraday::Adapter::Test::Stubs.new do |stub|
78
+ stub.post('/events') { [200, {}, positive_response_json] }
79
+ end
80
+ end
81
+
82
+ after do
83
+ #stubs.verify_stubbed_calls
84
+ end
85
+
86
+ it 'makes a post request' do
87
+ subject.send(:connection).should_receive(:post).once
88
+ subject.notify(payload)
89
+ end
90
+
91
+ it 'authenticates request with HMAC' do
92
+ subject.notify(payload).should be_true
93
+ subject.response.env[:request_headers]['Authorization'].should ==
94
+ CrashLog::AuthHMAC.new({}, {:service_id => 'CrashLog'}).authorization(subject.response.env, 'PROJECT_ID', 'SECRET')
95
+ stubs.verify_stubbed_calls
96
+ end
97
+
98
+ it 'sends a serialized payload to crashlog.io' do
99
+ subject.notify(payload).should be_true
100
+
101
+ stubs.verify_stubbed_calls
102
+ end
103
+
104
+ it 'captures result body' do
105
+ subject.notify(payload).should be_true
106
+ subject.result.should == positive_response
107
+ end
108
+ end
109
+
110
+ describe '#announce' do
111
+ let!(:stubs) do
112
+ Faraday::Adapter::Test::Stubs.new do |stub|
113
+ stub.post('/announce') { [201, {}, announce_json] }
114
+ end
115
+ end
116
+
117
+ before do
118
+ test_connection = Faraday.new(:url => subject.url) do |builder|
119
+ builder.adapter :test, stubs
120
+ end
121
+
122
+ subject.stub(:connection).and_return(test_connection)
123
+ end
124
+
125
+ it 'sends an identification payload to CrashLog'
126
+
127
+ it 'responds with an application name' do
128
+ subject.announce.should === 'CrashLog Test'
129
+ stubs.verify_stubbed_calls
130
+ end
131
+ end
132
+
133
+ describe 'url' do
134
+ it 'constructs url from configuration' do
135
+ subject.url.to_s.should == 'http://stdin.crashlog.io'
136
+ end
137
+
138
+ it 'sends HMACAuth header'
139
+
140
+ # # config.stub(:adapter).and_return(:test, &test_adapter)
141
+
142
+ # # .and_return(:test).and_yield do |stub|
143
+ # # stub.post('/notify') do |env|
144
+ # # [200, {}, env[:request_headers]]
145
+ # # end
146
+ # # end
147
+
148
+ # # test_connection = Faraday.new(:url => subject.url) do |faraday|
149
+ # # faraday.adapter :test do |stub|
150
+ # # stub.post('/notify') do |env|
151
+ # # [200, {}, env[:request_headers]]
152
+ # # end
153
+ # # end
154
+ # # end
155
+
156
+ # # #faraday.request :url_encoded
157
+ # # faraday.request :token_auth, "config.api_key"
158
+ # # #faraday.response :logger
159
+ # # # faraday.token_auth config.api_key
160
+ # # faraday.options[:timeout] = 1
161
+ # # faraday.options[:open_timeout] = 1
162
+ # # faraday.ssl[:verify] = false
163
+ # # end
164
+
165
+ # # subject.stub(:connection).and_return(test_connection)
166
+ # # subject.connection.adapter(:test) do |stub|
167
+ # # stub.post('/notify') do |env|
168
+ # # [200, {}, env[:request_headers]]
169
+ # # end
170
+ # # end
171
+
172
+ # subject.notify(payload).should be_true
173
+
174
+ # subject.response.should == 'Token token=""'
175
+
176
+ # stubs.verify_stubbed_calls
177
+ # end
178
+ end
179
+ end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrashLog do
4
+ def set_public_env
5
+ CrashLog.configure { |config| config.stage = 'production' }
6
+ end
7
+
8
+ def set_development_env
9
+ CrashLog.configure { |config| config.stage = 'development' }
10
+ end
11
+
12
+ let(:raised_error) do
13
+ begin
14
+ raise RuntimeError, "This broke"
15
+ rescue RuntimeError => e
16
+ e
17
+ end
18
+ end
19
+
20
+ after do
21
+ CrashLog.instance_variable_set("@configuration", nil)
22
+ end
23
+
24
+ let(:user) do
25
+ stub('User', :email => 'user@example.com')
26
+ end
27
+
28
+ describe '.notify' do
29
+ it 'does not send if not live' do
30
+ CrashLog::Reporter.any_instance.should_receive(:notify).never
31
+
32
+ CrashLog.stub(:live?).and_return(false)
33
+ CrashLog.notify(raised_error)
34
+ end
35
+
36
+ it 'handles being passed an exception object' do
37
+ CrashLog::Reporter.any_instance.should_receive(:notify).once
38
+
39
+ CrashLog.stub(:live?).and_return(true)
40
+ CrashLog.notify(raised_error)
41
+ end
42
+
43
+ it 'handles being passed an exception object and context' do
44
+ payload = {}
45
+
46
+ CrashLog::Reporter.any_instance.stub(:notify)
47
+ #should_receive(:notify).with(payload).once
48
+ CrashLog.stub(:live?).and_return(true)
49
+
50
+ context = {:current_user => user}
51
+ CrashLog.notify(raised_error, context)
52
+ end
53
+
54
+ it 'handles being passed a hash'
55
+ it 'handles being passed a string'
56
+ end
57
+
58
+ describe '.logger' do
59
+ it 'detects Rails.logger'
60
+ it 'defaults to STDOUT'
61
+ end
62
+
63
+ describe '.configuration' do
64
+ after do
65
+ CrashLog.instance_variable_set("@configuration", nil)
66
+ end
67
+
68
+ it 'handles being configured with a block' do
69
+ logger = stub("Logger")
70
+ logger.stub(:error)
71
+
72
+ CrashLog.configuration.logger.should be_nil
73
+ CrashLog.configure do |config|
74
+ config.logger = logger
75
+ end
76
+ CrashLog.configuration.logger.should == logger
77
+ end
78
+
79
+ it 'handles directly configuring attributes' do
80
+ logger = stub("Logger")
81
+ CrashLog.configuration.logger.should be_nil
82
+ CrashLog.configuration.logger = logger
83
+ CrashLog.configuration.logger.should == logger
84
+ end
85
+
86
+ it 'accepts api_key' do
87
+ key = stub("THIS IS AN API KEY")
88
+ CrashLog.configuration.api_key.should be_nil
89
+ CrashLog.configuration.api_key = key
90
+ CrashLog.configuration.api_key.should == key
91
+ end
92
+ end
93
+
94
+ describe '.ready' do
95
+ it 'logs an ready message' do
96
+ CrashLog::Reporter.any_instance.stub(:announce).and_return("Test Application")
97
+ logger = stub('Logger')
98
+ logger.should_receive(:info).
99
+ with("** [CrashLog] Initialized and ready to handle exceptions for Test Application")
100
+
101
+ CrashLog.stub(:logger).and_return(logger)
102
+ CrashLog.report_for_duty!
103
+ end
104
+ end
105
+
106
+ describe '.live?' do
107
+ it 'is live if current stage is included in release stages' do
108
+ CrashLog.configure do |c|
109
+ c.release_stages = ['test']
110
+ c.stage = 'test'
111
+ end
112
+
113
+ CrashLog.should be_live
114
+ end
115
+
116
+ it 'is not live if current stage is not included in release stages' do
117
+ CrashLog.configure do |c|
118
+ c.release_stages = ['production']
119
+ c.stage = 'test'
120
+ end
121
+
122
+ CrashLog.should_not be_live
123
+ end
124
+
125
+ it 'handles irregular stage names' do
126
+ CrashLog.configure do |c|
127
+ c.release_stages = ['test']
128
+ c.stage = 'Test'
129
+ end
130
+
131
+ CrashLog.configuration.stage.should === 'test'
132
+ CrashLog.should be_live
133
+ end
134
+ end
135
+
136
+ describe '#ignored?' do
137
+ it 'returns true if current exception is on ignored list' do
138
+ CrashLog.ignored?(RuntimeError.new).should be_false
139
+ end
140
+
141
+ it 'ignores ActiveRecord::RecordNotFound' do
142
+ # load_dummy_app
143
+ unless defined?(ActiveRecord)
144
+ module ActiveRecord
145
+ class RecordNotFound < RuntimeError
146
+ end
147
+ end
148
+ end
149
+
150
+ CrashLog.ignored?(ActiveRecord::RecordNotFound).should be_true
151
+ end
152
+ end
153
+ end