airbrakeV4rails5 4.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1716 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +6 -0
  5. data/INSTALL +20 -0
  6. data/LICENSE +61 -0
  7. data/README.md +148 -0
  8. data/README_FOR_HEROKU_ADDON.md +102 -0
  9. data/Rakefile +179 -0
  10. data/TESTED_AGAINST +7 -0
  11. data/airbrake.gemspec +41 -0
  12. data/bin/airbrake +12 -0
  13. data/features/metal.feature +34 -0
  14. data/features/rack.feature +60 -0
  15. data/features/rails.feature +324 -0
  16. data/features/rake.feature +33 -0
  17. data/features/sinatra.feature +126 -0
  18. data/features/step_definitions/file_steps.rb +14 -0
  19. data/features/step_definitions/rack_steps.rb +27 -0
  20. data/features/step_definitions/rails_application_steps.rb +267 -0
  21. data/features/step_definitions/rake_steps.rb +22 -0
  22. data/features/support/airbrake_shim.rb.template +11 -0
  23. data/features/support/aruba.rb +5 -0
  24. data/features/support/env.rb +39 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +156 -0
  27. data/features/support/rake/Rakefile +77 -0
  28. data/features/user_informer.feature +57 -0
  29. data/generators/airbrake/airbrake_generator.rb +94 -0
  30. data/generators/airbrake/lib/insert_commands.rb +34 -0
  31. data/generators/airbrake/lib/rake_commands.rb +24 -0
  32. data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
  33. data/generators/airbrake/templates/capistrano_hook.rb +6 -0
  34. data/generators/airbrake/templates/initializer.rb +4 -0
  35. data/install.rb +1 -0
  36. data/lib/airbrake.rb +191 -0
  37. data/lib/airbrake/backtrace.rb +103 -0
  38. data/lib/airbrake/capistrano.rb +103 -0
  39. data/lib/airbrake/capistrano3.rb +3 -0
  40. data/lib/airbrake/cli/client.rb +76 -0
  41. data/lib/airbrake/cli/options.rb +45 -0
  42. data/lib/airbrake/cli/printer.rb +33 -0
  43. data/lib/airbrake/cli/project.rb +17 -0
  44. data/lib/airbrake/cli/project_factory.rb +33 -0
  45. data/lib/airbrake/cli/runner.rb +49 -0
  46. data/lib/airbrake/cli/validator.rb +8 -0
  47. data/lib/airbrake/configuration.rb +366 -0
  48. data/lib/airbrake/jobs/send_job.rb +7 -0
  49. data/lib/airbrake/notice.rb +411 -0
  50. data/lib/airbrake/rack.rb +64 -0
  51. data/lib/airbrake/rails.rb +45 -0
  52. data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
  53. data/lib/airbrake/rails/controller_methods.rb +146 -0
  54. data/lib/airbrake/rails/error_lookup.rb +35 -0
  55. data/lib/airbrake/rails/middleware.rb +63 -0
  56. data/lib/airbrake/rails3_tasks.rb +126 -0
  57. data/lib/airbrake/railtie.rb +44 -0
  58. data/lib/airbrake/rake_handler.rb +75 -0
  59. data/lib/airbrake/response.rb +29 -0
  60. data/lib/airbrake/sender.rb +213 -0
  61. data/lib/airbrake/shared_tasks.rb +59 -0
  62. data/lib/airbrake/sidekiq.rb +8 -0
  63. data/lib/airbrake/sinatra.rb +40 -0
  64. data/lib/airbrake/tasks.rb +81 -0
  65. data/lib/airbrake/tasks/airbrake.cap +28 -0
  66. data/lib/airbrake/user_informer.rb +36 -0
  67. data/lib/airbrake/utils/params_cleaner.rb +141 -0
  68. data/lib/airbrake/utils/rack_filters.rb +45 -0
  69. data/lib/airbrake/version.rb +3 -0
  70. data/lib/airbrake_tasks.rb +62 -0
  71. data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
  72. data/lib/templates/rescue.erb +91 -0
  73. data/rails/init.rb +1 -0
  74. data/resources/README.md +34 -0
  75. data/resources/airbrake_2_4.xsd +89 -0
  76. data/resources/airbrake_3_0.json +52 -0
  77. data/resources/ca-bundle.crt +3376 -0
  78. data/script/integration_test.rb +35 -0
  79. data/test/airbrake_tasks_test.rb +161 -0
  80. data/test/backtrace_test.rb +215 -0
  81. data/test/capistrano_test.rb +44 -0
  82. data/test/configuration_test.rb +303 -0
  83. data/test/controller_methods_test.rb +230 -0
  84. data/test/helper.rb +233 -0
  85. data/test/integration.rb +13 -0
  86. data/test/integration/catcher_test.rb +371 -0
  87. data/test/logger_test.rb +79 -0
  88. data/test/notice_test.rb +494 -0
  89. data/test/notifier_test.rb +288 -0
  90. data/test/params_cleaner_test.rb +204 -0
  91. data/test/rack_test.rb +62 -0
  92. data/test/rails_initializer_test.rb +36 -0
  93. data/test/recursion_test.rb +10 -0
  94. data/test/response_test.rb +18 -0
  95. data/test/sender_test.rb +335 -0
  96. data/test/support/response_shim.xml +4 -0
  97. data/test/user_informer_test.rb +29 -0
  98. metadata +469 -0
@@ -0,0 +1,62 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class RackTest < Test::Unit::TestCase
4
+
5
+ should "call the upstream app with the environment" do
6
+ environment = { 'key' => 'value' }
7
+ app = lambda { |env| ['response', {}, env] }
8
+ stack = Airbrake::Rack.new(app)
9
+
10
+ response = stack.call(environment)
11
+
12
+ assert_equal ['response', {}, environment], response
13
+ end
14
+
15
+ should "deliver an exception raised while calling an upstream app" do
16
+ Airbrake.stubs(:notify_or_ignore)
17
+
18
+ exception = build_exception
19
+ environment = { 'key' => 'value' }
20
+ app = lambda do |env|
21
+ raise exception
22
+ end
23
+
24
+ begin
25
+ stack = Airbrake::Rack.new(app)
26
+ stack.call(environment)
27
+ rescue Exception => raised
28
+ assert_equal exception, raised
29
+ else
30
+ flunk "Didn't raise an exception"
31
+ end
32
+
33
+ assert_received(Airbrake, :notify_or_ignore) do |expect|
34
+ expect.with(exception, :rack_env => environment)
35
+ end
36
+ end
37
+
38
+ %w(rack.exception sinatra.error).each do |ex|
39
+
40
+ should "deliver an exception in #{ex}" do
41
+ Airbrake.stubs(:notify_or_ignore)
42
+ exception = build_exception
43
+ environment = { 'key' => 'value' }
44
+
45
+ response = [200, {}, ['okay']]
46
+ app = lambda do |env|
47
+ env[ex] = exception
48
+ response
49
+ end
50
+ stack = Airbrake::Rack.new(app)
51
+
52
+ actual_response = stack.call(environment)
53
+
54
+ assert_equal response, actual_response
55
+ assert_received(Airbrake, :notify_or_ignore) do |expect|
56
+ expect.with(exception, :rack_env => environment)
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ require 'airbrake/rails'
4
+
5
+ class RailsInitializerTest < Test::Unit::TestCase
6
+ include DefinesConstants
7
+
8
+ should "trigger use of Rails' logger if logger isn't set and Rails' logger exists" do
9
+ rails = Module.new do
10
+ def self.logger
11
+ "RAILS LOGGER"
12
+ end
13
+ end
14
+ define_constant("Rails", rails)
15
+ Airbrake::Rails.initialize
16
+ assert_equal "RAILS LOGGER", Airbrake.logger
17
+ end
18
+
19
+ should "trigger use of Rails' default logger if logger isn't set and Rails.logger doesn't exist" do
20
+ define_constant("RAILS_DEFAULT_LOGGER", "RAILS DEFAULT LOGGER")
21
+
22
+ Airbrake::Rails.initialize
23
+ assert_equal "RAILS DEFAULT LOGGER", Airbrake.logger
24
+ end
25
+
26
+ should "allow overriding of the logger if already assigned" do
27
+ define_constant("RAILS_DEFAULT_LOGGER", "RAILS DEFAULT LOGGER")
28
+ Airbrake::Rails.initialize
29
+
30
+ Airbrake.configure(true) do |config|
31
+ config.logger = "OVERRIDDEN LOGGER"
32
+ end
33
+
34
+ assert_equal "OVERRIDDEN LOGGER", Airbrake.logger
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class RecursionTest < Test::Unit::TestCase
4
+ should "not allow infinite recursion" do
5
+ hash = {:a => :a}
6
+ hash[:hash] = hash
7
+ notice = Airbrake::Notice.new(:parameters => hash)
8
+ assert_equal "[possible infinite recursion halted]", notice.parameters[:hash]
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class ResponseTest < Test::Unit::TestCase
4
+ include DefinesConstants
5
+
6
+ def response_body
7
+ File.read File.expand_path('../support/response_shim.xml', __FILE__)
8
+ end
9
+
10
+ should "output a nicely formatted notice details" do
11
+ output = Airbrake::Response.pretty_format(response_body)
12
+
13
+ assert %r{ID: b6817316-9c45-ed26-45eb-780dbb86aadb}, "#{output}"
14
+ assert %r{URL: http://airbrake.io/locate/b6817316-9c45-ed26-45eb-780dbb86aadb},
15
+ "#{output}"
16
+
17
+ end
18
+ end
@@ -0,0 +1,335 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class SenderTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ reset_config
7
+ end
8
+
9
+ def build_sender(opts = {})
10
+ Airbrake.configure do |conf|
11
+ opts.each {|opt, value| conf.send(:"#{opt}=", value) }
12
+ end
13
+ end
14
+
15
+ def send_exception(args = {})
16
+ notice = args.delete(:notice) || build_notice_data
17
+ notice.stubs(:to_xml)
18
+ sender = args.delete(:sender) || build_sender(args)
19
+ sender.send_to_airbrake(notice)
20
+ end
21
+
22
+ def stub_http(options = {})
23
+ response = stub(:body => options[:body] || 'body')
24
+ http = stub(:post => response,
25
+ :read_timeout= => nil,
26
+ :open_timeout= => nil,
27
+ :ca_file= => nil,
28
+ :verify_mode= => nil,
29
+ :use_ssl= => nil)
30
+ Net::HTTP.stubs(:new => http)
31
+ http
32
+ end
33
+
34
+ should "post to Airbrake when using an HTTP proxy" do
35
+ response = stub(:body => 'body')
36
+ http = stub(:post => response,
37
+ :read_timeout= => nil,
38
+ :open_timeout= => nil,
39
+ :use_ssl= => nil)
40
+ proxy = stub(:new => http)
41
+ Net::HTTP.stubs(:Proxy => proxy)
42
+
43
+ url = "http://api.airbrake.io:80#{Airbrake::Sender::NOTICES_URI}"
44
+ uri = URI.parse(url)
45
+
46
+ proxy_host = 'some.host'
47
+ proxy_port = 88
48
+ proxy_user = 'login'
49
+ proxy_pass = 'passwd'
50
+
51
+ send_exception(:proxy_host => proxy_host,
52
+ :proxy_port => proxy_port,
53
+ :proxy_user => proxy_user,
54
+ :proxy_pass => proxy_pass)
55
+ assert_received(http, :post) do |expect|
56
+ expect.with(uri.path, anything, Airbrake::Sender::HEADERS[:xml])
57
+ end
58
+ assert_received(Net::HTTP, :Proxy) do |expect|
59
+ expect.with(proxy_host, proxy_port, proxy_user, proxy_pass)
60
+ end
61
+ end
62
+
63
+ should "return the created group's id on successful posting" do
64
+ stub_http(:body => '<id type="integer">3799307</id>')
65
+ assert_equal "3799307", send_exception(:secure => false)
66
+ end
67
+
68
+ context "when using the XML API" do
69
+
70
+ should "post to Airbrake with XML passed" do
71
+ xml_notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar").to_xml
72
+
73
+ http = stub_http
74
+
75
+ sender = build_sender
76
+ sender.send_to_airbrake(xml_notice)
77
+
78
+ assert_received(http, :post) do |expect|
79
+ expect.with(anything, xml_notice, Airbrake::Sender::HEADERS[:xml])
80
+ end
81
+ end
82
+
83
+ should "post to Airbrake with a Notice instance passed" do
84
+ notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar")
85
+
86
+ http = stub_http
87
+
88
+ sender = build_sender
89
+ sender.send_to_airbrake(notice)
90
+
91
+ assert_received(http, :post) do |expect|
92
+ expect.with(anything, notice.to_xml, Airbrake::Sender::HEADERS[:xml])
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ context "when using new JSON API" do
99
+ should "post to Airbrake with JSON passed" do
100
+ json_notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar").to_json
101
+
102
+ http = stub_http
103
+
104
+ sender = build_sender(:project_id => "PROJECT_ID", :host => "collect.airbrake.io")
105
+ sender.send_to_airbrake(json_notice)
106
+
107
+ assert_received(http, :post) do |expect|
108
+ expect.with(anything, json_notice, Airbrake::Sender::HEADERS[:json])
109
+ end
110
+ end
111
+
112
+ should "post to Airbrake keeping the apiKey in the URL" do
113
+ json_notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar").to_json
114
+
115
+ http = stub_http
116
+
117
+ sender = build_sender(:project_id => "PROJECT_ID", :host => "collect.airbrake.io")
118
+ sender.send_to_airbrake(json_notice)
119
+
120
+ expected_url = format("%s/PROJECT_ID/notices?key=%s",
121
+ Airbrake::Sender::JSON_API_URI,
122
+ Airbrake.configuration[:api_key])
123
+
124
+ assert_received(http, :post) do |expect|
125
+ expect.with(expected_url, json_notice, Airbrake::Sender::HEADERS[:json])
126
+ end
127
+ end
128
+
129
+ should "post to Airbrake with notice passed" do
130
+ notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar")
131
+
132
+ http = stub_http
133
+
134
+ sender = build_sender(:project_id => "PROJECT_ID", :host => "collect.airbrake.io")
135
+ sender.send_to_airbrake(notice)
136
+
137
+ assert_received(http, :post) do |expect|
138
+ expect.with(anything, notice.to_json, Airbrake::Sender::HEADERS[:json])
139
+ end
140
+
141
+ end
142
+ end
143
+
144
+ context "when encountering exceptions: " do
145
+ context "HTTP connection setup problems" do
146
+ should "not be rescued" do
147
+ proxy = stub()
148
+ proxy.stubs(:new).raises(NoMemoryError)
149
+ Net::HTTP.stubs(:Proxy => proxy)
150
+
151
+ assert_raise NoMemoryError do
152
+ build_sender.send(:setup_http_connection)
153
+ end
154
+ end
155
+
156
+ should "be logged" do
157
+ proxy = stub()
158
+ proxy.stubs(:new).raises(RuntimeError)
159
+ Net::HTTP.stubs(:Proxy => proxy)
160
+
161
+ sender = build_sender
162
+ sender.expects(:log)
163
+
164
+ assert_raise RuntimeError do
165
+ sender.send(:setup_http_connection)
166
+ end
167
+
168
+ end
169
+ end
170
+
171
+ context "unexpected exception sending problems" do
172
+ should "be logged" do
173
+ sender = build_sender
174
+ sender.stubs(:setup_http_connection).raises(RuntimeError.new)
175
+
176
+ sender.expects(:log)
177
+ send_exception(:sender => sender)
178
+ end
179
+
180
+ should "return nil no matter what" do
181
+ sender = build_sender
182
+ sender.stubs(:setup_http_connection).raises(LocalJumpError)
183
+
184
+ assert_nothing_thrown do
185
+ assert_nil sender.send_to_airbrake(build_notice_data)
186
+ end
187
+ end
188
+ end
189
+
190
+ should "return nil on failed posting" do
191
+ http = stub_http
192
+ http.stubs(:post).raises(Errno::ECONNREFUSED)
193
+ assert_equal nil, send_exception(:secure => false)
194
+ end
195
+
196
+ should "not fail when posting and a timeout exception occurs" do
197
+ http = stub_http
198
+ http.stubs(:post).raises(TimeoutError)
199
+ assert_nothing_thrown do
200
+ send_exception(:secure => false)
201
+ end
202
+ end
203
+
204
+ should "not fail when posting and a connection refused exception occurs" do
205
+ http = stub_http
206
+ http.stubs(:post).raises(Errno::ECONNREFUSED)
207
+ assert_nothing_thrown do
208
+ send_exception(:secure => false)
209
+ end
210
+ end
211
+
212
+ should "not fail when posting any http exception occurs" do
213
+ http = stub_http
214
+ Airbrake::Sender::HTTP_ERRORS.each do |error|
215
+ http.stubs(:post).raises(error)
216
+ assert_nothing_thrown do
217
+ send_exception(:secure => false)
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ context "SSL" do
224
+ should "post to the right url for non-ssl" do
225
+ http = stub_http
226
+ url = "http://api.airbrake.io:80#{Airbrake::Sender::NOTICES_URI}"
227
+ uri = URI.parse(url)
228
+ send_exception(:secure => false)
229
+ assert_received(http, :post) {|expect| expect.with(uri.path, anything, Airbrake::Sender::HEADERS[:xml]) }
230
+ end
231
+
232
+ should "post to the right path for ssl" do
233
+ http = stub_http
234
+ send_exception(:secure => true)
235
+ assert_received(http, :post) {|expect| expect.with(Airbrake::Sender::NOTICES_URI, anything, Airbrake::Sender::HEADERS[:xml]) }
236
+ end
237
+
238
+ should "verify the SSL peer when the use_ssl option is set to true" do
239
+ url = "https://api.airbrake.io#{Airbrake::Sender::NOTICES_URI}"
240
+ uri = URI.parse(url)
241
+
242
+ real_http = Net::HTTP.new(uri.host, uri.port)
243
+ real_http.stubs(:post => nil)
244
+ proxy = stub(:new => real_http)
245
+ Net::HTTP.stubs(:Proxy => proxy)
246
+ File.stubs(:exist?).with(OpenSSL::X509::DEFAULT_CERT_FILE).returns(false)
247
+
248
+ send_exception(:secure => true)
249
+ assert(real_http.use_ssl?)
250
+ assert_equal(OpenSSL::SSL::VERIFY_PEER, real_http.verify_mode)
251
+ assert_equal(Airbrake.configuration.local_cert_path, real_http.ca_file)
252
+ end
253
+
254
+ should "use the default DEFAULT_CERT_FILE if asked to" do
255
+ config = Airbrake::Configuration.new
256
+ config.use_system_ssl_cert_chain = true
257
+ sender = Airbrake::Sender.new(config)
258
+
259
+ assert(sender.use_system_ssl_cert_chain?)
260
+
261
+ http = sender.send(:setup_http_connection)
262
+ assert_not_equal http.ca_file, config.local_cert_path
263
+ end
264
+
265
+ should "verify the connection when the use_ssl option is set (VERIFY_PEER)" do
266
+ sender = build_sender(:secure => true)
267
+ http = sender.send(:setup_http_connection)
268
+ assert_equal(OpenSSL::SSL::VERIFY_PEER, http.verify_mode)
269
+ end
270
+
271
+ should "use the default cert (OpenSSL::X509::DEFAULT_CERT_FILE) only if explicitly told to" do
272
+ sender = build_sender(:secure => true)
273
+ http = sender.send(:setup_http_connection)
274
+
275
+ assert_equal(Airbrake.configuration.local_cert_path, http.ca_file)
276
+
277
+ File.stubs(:exist?).with(OpenSSL::X509::DEFAULT_CERT_FILE).returns(true)
278
+ sender = build_sender(:secure => true, :use_system_ssl_cert_chain => true)
279
+ http = sender.send(:setup_http_connection)
280
+
281
+ assert_not_equal(Airbrake.configuration.local_cert_path, http.ca_file)
282
+ assert_equal(OpenSSL::X509::DEFAULT_CERT_FILE, http.ca_file)
283
+ end
284
+
285
+ should "connect to the right port for ssl" do
286
+ stub_http
287
+ send_exception(:secure => true)
288
+ assert_received(Net::HTTP, :new) {|expect| expect.with("api.airbrake.io", 443) }
289
+ end
290
+
291
+ should "connect to the right port for non-ssl" do
292
+ stub_http
293
+ send_exception(:secure => false)
294
+ assert_received(Net::HTTP, :new) {|expect| expect.with("api.airbrake.io", 80) }
295
+ end
296
+
297
+ should "use ssl if secure" do
298
+ stub_http
299
+ send_exception(:secure => true, :host => 'example.org')
300
+ assert_received(Net::HTTP, :new) {|expect| expect.with('example.org', 443) }
301
+ end
302
+
303
+ should "not use ssl if not secure" do
304
+ stub_http
305
+ send_exception(:secure => false, :host => 'example.org')
306
+ assert_received(Net::HTTP, :new) {|expect| expect.with('example.org', 80) }
307
+ end
308
+ end
309
+
310
+ context "network timeouts" do
311
+ should "default the open timeout to 2 seconds" do
312
+ http = stub_http
313
+ send_exception
314
+ assert_received(http, :open_timeout=) {|expect| expect.with(2) }
315
+ end
316
+
317
+ should "default the read timeout to 5 seconds" do
318
+ http = stub_http
319
+ send_exception
320
+ assert_received(http, :read_timeout=) {|expect| expect.with(5) }
321
+ end
322
+
323
+ should "allow override of the open timeout" do
324
+ http = stub_http
325
+ send_exception(:http_open_timeout => 4)
326
+ assert_received(http, :open_timeout=) {|expect| expect.with(4) }
327
+ end
328
+
329
+ should "allow override of the read timeout" do
330
+ http = stub_http
331
+ send_exception(:http_read_timeout => 10)
332
+ assert_received(http, :read_timeout=) {|expect| expect.with(10) }
333
+ end
334
+ end
335
+ end