airbrake 3.1.6 → 3.1.7

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 (58) hide show
  1. data/CHANGELOG +116 -0
  2. data/LICENSE +61 -0
  3. data/README.md +28 -478
  4. data/README_FOR_HEROKU_ADDON.md +13 -5
  5. data/Rakefile +26 -103
  6. data/TESTED_AGAINST +6 -0
  7. data/airbrake.gemspec +13 -14
  8. data/features/metal.feature +27 -12
  9. data/features/rack.feature +59 -59
  10. data/features/rails.feature +79 -100
  11. data/features/rails_with_js_notifier.feature +9 -21
  12. data/features/rake.feature +6 -4
  13. data/features/sinatra.feature +32 -6
  14. data/features/step_definitions/file_steps.rb +4 -0
  15. data/features/step_definitions/rack_steps.rb +9 -5
  16. data/features/step_definitions/rails_application_steps.rb +46 -278
  17. data/features/step_definitions/rake_steps.rb +16 -11
  18. data/features/support/airbrake_shim.rb.template +0 -2
  19. data/features/support/aruba.rb +5 -0
  20. data/features/support/env.rb +12 -6
  21. data/features/support/rails.rb +30 -73
  22. data/features/support/rake/Rakefile +1 -2
  23. data/features/user_informer.feature +12 -19
  24. data/generators/airbrake/airbrake_generator.rb +1 -1
  25. data/lib/airbrake.rb +9 -8
  26. data/lib/airbrake/cli/project_factory.rb +6 -3
  27. data/lib/airbrake/configuration.rb +7 -0
  28. data/lib/airbrake/notice.rb +52 -3
  29. data/lib/airbrake/rack.rb +16 -7
  30. data/lib/airbrake/rails/action_controller_catcher.rb +5 -3
  31. data/lib/airbrake/rails/controller_methods.rb +6 -6
  32. data/lib/airbrake/rails/error_lookup.rb +4 -2
  33. data/lib/airbrake/rails/javascript_notifier.rb +7 -3
  34. data/lib/airbrake/rails/middleware.rb +65 -0
  35. data/lib/airbrake/rails3_tasks.rb +16 -21
  36. data/lib/airbrake/railtie.rb +9 -16
  37. data/lib/airbrake/rake_handler.rb +2 -2
  38. data/lib/airbrake/sender.rb +57 -6
  39. data/lib/airbrake/shared_tasks.rb +24 -11
  40. data/lib/airbrake/sinatra.rb +34 -0
  41. data/lib/airbrake/user_informer.rb +9 -0
  42. data/lib/airbrake/version.rb +1 -1
  43. data/lib/rails/generators/airbrake/airbrake_generator.rb +10 -10
  44. data/{test/airbrake_2_3.xsd → resources/airbrake_2_4.xsd} +1 -0
  45. data/resources/airbrake_3_0.json +52 -0
  46. data/test/catcher_test.rb +198 -240
  47. data/test/configuration_test.rb +2 -0
  48. data/test/helper.rb +123 -53
  49. data/test/notice_test.rb +45 -1
  50. data/test/sender_test.rb +63 -32
  51. metadata +60 -79
  52. data/MIT-LICENSE +0 -22
  53. data/SUPPORTED_RAILS_VERSIONS +0 -38
  54. data/TESTING.md +0 -41
  55. data/features/step_definitions/metal_steps.rb +0 -23
  56. data/features/support/terminal.rb +0 -107
  57. data/lib/airbrake/extensions/blank.rb +0 -73
  58. data/lib/airbrake/rails/middleware/exceptions_catcher.rb +0 -33
@@ -31,6 +31,7 @@ class ConfigurationTest < Test::Unit::TestCase
31
31
  assert_config_default :development_lookup, true
32
32
  assert_config_default :framework, 'Standalone'
33
33
  assert_config_default :async, nil
34
+ assert_config_default :project_id, nil
34
35
  end
35
36
 
36
37
  should "set GirlFriday-callable for async=true" do
@@ -86,6 +87,7 @@ class ConfigurationTest < Test::Unit::TestCase
86
87
  assert_config_overridable :development_lookup
87
88
  assert_config_overridable :logger
88
89
  assert_config_overridable :async
90
+ assert_config_overridable :project_id
89
91
  end
90
92
 
91
93
  should "have an api key" do
data/test/helper.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'simplecov'
2
- SimpleCov.start
3
1
  require 'test/unit'
4
2
  require 'rubygems'
5
3
 
@@ -10,19 +8,140 @@ require 'thread'
10
8
  require 'shoulda'
11
9
  require 'mocha'
12
10
 
11
+ require 'abstract_controller'
13
12
  require 'action_controller'
14
- require 'action_controller/test_process'
13
+ require 'action_dispatch'
14
+ require 'active_support/dependencies'
15
+ require 'active_model'
15
16
  require 'active_record'
16
- require 'active_support'
17
+ require 'active_support/core_ext/kernel/reporting'
18
+
17
19
  require 'nokogiri'
18
20
  require 'rack'
19
21
  require 'bourne'
20
22
  require 'sham_rack'
23
+ require 'json-schema'
21
24
 
22
25
  require "airbrake"
23
26
 
24
27
  begin require 'redgreen'; rescue LoadError; end
25
28
 
29
+ # Show backtraces for deprecated behavior for quicker cleanup.
30
+ ActiveSupport::Deprecation.debug = true
31
+
32
+ FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
33
+ FIXTURES = Pathname.new(FIXTURE_LOAD_PATH)
34
+
35
+ SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
36
+
37
+ class RoutedRackApp
38
+ attr_reader :routes
39
+
40
+ def initialize(routes, &blk)
41
+ @routes = routes
42
+ @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
43
+ end
44
+
45
+ def call(env)
46
+ @stack.call(env)
47
+ end
48
+ end
49
+
50
+ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
51
+ setup do
52
+ @routes = SharedTestRoutes
53
+ end
54
+ end
55
+
56
+ class ActionController::IntegrationTest < ActiveSupport::TestCase
57
+ def self.build_app(routes = nil)
58
+ RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
59
+ middleware.use "ActionDispatch::Callbacks"
60
+ middleware.use "ActionDispatch::ParamsParser"
61
+ middleware.use "ActionDispatch::Cookies"
62
+ middleware.use "ActionDispatch::Flash"
63
+ middleware.use "ActionDispatch::Head"
64
+ yield(middleware) if block_given?
65
+ end
66
+ end
67
+
68
+ self.app = build_app
69
+
70
+ # Stub Rails dispatcher so it does not get controller references and
71
+ # simply return the controller#action as Rack::Body.
72
+ class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
73
+ protected
74
+ def controller_reference(controller_param)
75
+ controller_param
76
+ end
77
+
78
+ def dispatch(controller, action, env)
79
+ [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
80
+ end
81
+ end
82
+
83
+ def self.stub_controllers
84
+ old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher
85
+ ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
86
+ ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher }
87
+ yield ActionDispatch::Routing::RouteSet.new
88
+ ensure
89
+ ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
90
+ ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher }
91
+ end
92
+
93
+ def with_routing(&block)
94
+ temporary_routes = ActionDispatch::Routing::RouteSet.new
95
+ old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
96
+ old_routes = SharedTestRoutes
97
+ silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
98
+
99
+ yield temporary_routes
100
+ ensure
101
+ self.class.app = old_app
102
+ silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
103
+ end
104
+
105
+ def with_autoload_path(path)
106
+ path = File.join(File.dirname(__FILE__), "fixtures", path)
107
+ if ActiveSupport::Dependencies.autoload_paths.include?(path)
108
+ yield
109
+ else
110
+ begin
111
+ ActiveSupport::Dependencies.autoload_paths << path
112
+ yield
113
+ ensure
114
+ ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
115
+ ActiveSupport::Dependencies.clear
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ module ActionController
122
+ class Base
123
+ include ActionController::Testing
124
+ end
125
+
126
+ Base.view_paths = FIXTURE_LOAD_PATH
127
+
128
+ class TestCase
129
+ include ActionDispatch::TestProcess
130
+
131
+ setup do
132
+ @routes = SharedTestRoutes
133
+ end
134
+ end
135
+ end
136
+
137
+ # This stub emulates the Railtie including the URL helpers from a Rails application
138
+ module ActionController
139
+ class Base
140
+ include SharedTestRoutes.url_helpers
141
+ end
142
+ end
143
+
144
+
26
145
  module TestMethods
27
146
  def rescue_action e
28
147
  raise e
@@ -56,41 +175,6 @@ module TestMethods
56
175
  end
57
176
 
58
177
  class Test::Unit::TestCase
59
- def request(action = nil, method = :get, user_agent = nil, params = {})
60
- @request = ActionController::TestRequest.new
61
- @request.action = action ? action.to_s : ""
62
-
63
- if user_agent
64
- if @request.respond_to?(:user_agent=)
65
- @request.user_agent = user_agent
66
- else
67
- @request.env["HTTP_USER_AGENT"] = user_agent
68
- end
69
- end
70
- @request.query_parameters = @request.query_parameters.merge(params)
71
- @response = ActionController::TestResponse.new
72
- @controller.process(@request, @response)
73
- end
74
-
75
- # Borrowed from ActiveSupport 2.3.2
76
- def assert_difference(expression, difference = 1, message = nil, &block)
77
- b = block.send(:binding)
78
- exps = Array.wrap(expression)
79
- before = exps.map { |e| eval(e, b) }
80
-
81
- yield
82
-
83
- exps.each_with_index do |e, i|
84
- error = "#{e.inspect} didn't change by #{difference}"
85
- error = "#{message}.\n#{error}" if message
86
- assert_equal(before[i] + difference, eval(e, b), error)
87
- end
88
- end
89
-
90
- def assert_no_difference(expression, message = nil, &block)
91
- assert_difference expression, 0, message, &block
92
- end
93
-
94
178
  def stub_sender
95
179
  stub('sender', :send_to_airbrake => nil)
96
180
  end
@@ -109,10 +193,6 @@ class Test::Unit::TestCase
109
193
  end
110
194
  end
111
195
 
112
- def create_dummy
113
- Airbrake::DummySender.new
114
- end
115
-
116
196
  def reset_config
117
197
  Airbrake.configuration = nil
118
198
  Airbrake.configure do |config|
@@ -187,8 +267,6 @@ class Test::Unit::TestCase
187
267
  expect.with {|actual| actual =~ expected }.never
188
268
  end
189
269
  end
190
-
191
-
192
270
  end
193
271
 
194
272
  module DefinesConstants
@@ -241,14 +319,6 @@ class CollectingSender
241
319
  end
242
320
  end
243
321
 
244
- class FakeLogger
245
- def info(*args); end
246
- def debug(*args); end
247
- def warn(*args); end
248
- def error(*args); end
249
- def fatal(*args); end
250
- end
251
-
252
322
  class BacktracedException < Exception
253
323
  attr_accessor :backtrace
254
324
  def initialize(opts)
data/test/notice_test.rb CHANGED
@@ -61,12 +61,18 @@ class NoticeTest < Test::Unit::TestCase
61
61
  end
62
62
 
63
63
  def assert_valid_notice_document(document)
64
- xsd_path = File.join(File.dirname(__FILE__), "airbrake_2_3.xsd")
64
+ xsd_path = File.expand_path(File.join(File.dirname(__FILE__),"..", "resources", "airbrake_2_4.xsd"))
65
65
  schema = Nokogiri::XML::Schema.new(IO.read(xsd_path))
66
66
  errors = schema.validate(document)
67
67
  assert errors.empty?, errors.collect{|e| e.message }.join
68
68
  end
69
69
 
70
+ def assert_valid_json(notice)
71
+ json_schema = File.expand_path(File.join(File.dirname(__FILE__),"..", "resources", "airbrake_3_0.json"))
72
+ errors = JSON::Validator.fully_validate(json_schema, notice)
73
+ assert errors.empty?, errors.join
74
+ end
75
+
70
76
  def assert_filters_hash(attribute)
71
77
  filters = ["abc", :def]
72
78
  original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
@@ -251,6 +257,15 @@ class NoticeTest < Test::Unit::TestCase
251
257
  should "filter session" do
252
258
  assert_filters_hash(:session_data)
253
259
  end
260
+
261
+ should "should always remove a Rails application's secret token" do
262
+ original = {
263
+ "action_dispatch.secret_token" => "abc123xyz456",
264
+ "abc" => "123"
265
+ }
266
+ notice = build_notice(:cgi_data => original)
267
+ assert_equal({"abc" => "123"}, notice.cgi_data)
268
+ end
254
269
 
255
270
  should "remove rack.request.form_vars" do
256
271
  original = {
@@ -262,6 +277,34 @@ class NoticeTest < Test::Unit::TestCase
262
277
  assert_equal({"abc" => "123"}, notice.cgi_data)
263
278
  end
264
279
 
280
+ context "a Notice turned into JSON" do
281
+ setup do
282
+ @exception = build_exception
283
+
284
+ @notice = build_notice({
285
+ :notifier_name => 'a name',
286
+ :notifier_version => '1.2.3',
287
+ :notifier_url => 'http://some.url/path',
288
+ :exception => @exception,
289
+ :controller => "controller",
290
+ :action => "action",
291
+ :url => "http://url.com",
292
+ :parameters => { "paramskey" => "paramsvalue",
293
+ "nestparentkey" => { "nestkey" => "nestvalue" } },
294
+ :session_data => { "sessionkey" => "sessionvalue" },
295
+ :cgi_data => { "cgikey" => "cgivalue" },
296
+ :project_root => "RAILS_ROOT",
297
+ :environment_name => "RAILS_ENV"
298
+ })
299
+
300
+ @json = @notice.to_json
301
+ end
302
+
303
+ should "validate against the JSON schema" do
304
+ assert_valid_json @json
305
+ end
306
+ end
307
+
265
308
  context "a Notice turned into XML" do
266
309
  setup do
267
310
  Airbrake.configure do |config|
@@ -295,6 +338,7 @@ class NoticeTest < Test::Unit::TestCase
295
338
  assert_valid_notice_document @document
296
339
  end
297
340
 
341
+
298
342
  should "serialize a Notice to XML when sent #to_xml" do
299
343
  assert_valid_node(@document, "//api-key", @notice.api_key)
300
344
 
data/test/sender_test.rb CHANGED
@@ -31,32 +31,6 @@ class SenderTest < Test::Unit::TestCase
31
31
  http
32
32
  end
33
33
 
34
- should "post to Airbrake with XML passed" do
35
- xml_notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar").to_xml
36
-
37
- http = stub_http
38
-
39
- sender = build_sender
40
- sender.send_to_airbrake(xml_notice)
41
-
42
- assert_received(http, :post) do |expect|
43
- expect.with(anything, xml_notice, Airbrake::HEADERS)
44
- end
45
- end
46
-
47
- should "post to Airbrake with a Notice instance passed" do
48
- notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar")
49
-
50
- http = stub_http
51
-
52
- sender = build_sender
53
- sender.send_to_airbrake(notice)
54
-
55
- assert_received(http, :post) do |expect|
56
- expect.with(anything, notice.to_xml, Airbrake::HEADERS)
57
- end
58
- end
59
-
60
34
  should "post to Airbrake when using an HTTP proxy" do
61
35
  response = stub(:body => 'body')
62
36
  http = stub(:post => response,
@@ -79,7 +53,7 @@ class SenderTest < Test::Unit::TestCase
79
53
  :proxy_user => proxy_user,
80
54
  :proxy_pass => proxy_pass)
81
55
  assert_received(http, :post) do |expect|
82
- expect.with(uri.path, anything, Airbrake::HEADERS)
56
+ expect.with(uri.path, anything, Airbrake::Sender::HEADERS[:xml])
83
57
  end
84
58
  assert_received(Net::HTTP, :Proxy) do |expect|
85
59
  expect.with(proxy_host, proxy_port, proxy_user, proxy_pass)
@@ -91,6 +65,66 @@ class SenderTest < Test::Unit::TestCase
91
65
  assert_equal "3799307", send_exception(:secure => false)
92
66
  end
93
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
+
111
+ end
112
+
113
+ should "post to Airbrake with notice passed" do
114
+ notice = Airbrake::Notice.new(:error_class => "FooBar", :error_message => "Foo Bar")
115
+
116
+ http = stub_http
117
+
118
+ sender = build_sender(:project_id => "PROJECT_ID", :host => "collect.airbrake.io")
119
+ sender.send_to_airbrake(notice)
120
+
121
+ assert_received(http, :post) do |expect|
122
+ expect.with(anything, notice.to_json, Airbrake::Sender::HEADERS[:json])
123
+ end
124
+
125
+ end
126
+ end
127
+
94
128
  context "when encountering exceptions: " do
95
129
  context "HTTP connection setup problems" do
96
130
  should "not be rescued" do
@@ -176,13 +210,13 @@ class SenderTest < Test::Unit::TestCase
176
210
  url = "http://api.airbrake.io:80#{Airbrake::Sender::NOTICES_URI}"
177
211
  uri = URI.parse(url)
178
212
  send_exception(:secure => false)
179
- assert_received(http, :post) {|expect| expect.with(uri.path, anything, Airbrake::HEADERS) }
213
+ assert_received(http, :post) {|expect| expect.with(uri.path, anything, Airbrake::Sender::HEADERS[:xml]) }
180
214
  end
181
215
 
182
216
  should "post to the right path for ssl" do
183
217
  http = stub_http
184
218
  send_exception(:secure => true)
185
- assert_received(http, :post) {|expect| expect.with(Airbrake::Sender::NOTICES_URI, anything, Airbrake::HEADERS) }
219
+ assert_received(http, :post) {|expect| expect.with(Airbrake::Sender::NOTICES_URI, anything, Airbrake::Sender::HEADERS[:xml]) }
186
220
  end
187
221
 
188
222
  should "verify the SSL peer when the use_ssl option is set to true" do
@@ -255,8 +289,6 @@ class SenderTest < Test::Unit::TestCase
255
289
  send_exception(:secure => false, :host => 'example.org')
256
290
  assert_received(Net::HTTP, :new) {|expect| expect.with('example.org', 80) }
257
291
  end
258
-
259
-
260
292
  end
261
293
 
262
294
  context "network timeouts" do
@@ -284,5 +316,4 @@ class SenderTest < Test::Unit::TestCase
284
316
  assert_received(http, :read_timeout=) {|expect| expect.with(10) }
285
317
  end
286
318
  end
287
-
288
319
  end