bugsnag 6.9.0 → 6.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +0 -1
  4. data/VERSION +1 -1
  5. data/features/delayed_job.feature +9 -10
  6. data/features/fixtures/delayed_job/Dockerfile +2 -2
  7. data/features/fixtures/delayed_job/app/config/initializers/bugsnag.rb +12 -12
  8. data/features/fixtures/docker-compose.yml +118 -124
  9. data/features/fixtures/plain/Dockerfile +5 -6
  10. data/features/fixtures/plain/app/Gemfile +1 -1
  11. data/features/fixtures/plain/app/app.rb +19 -19
  12. data/features/fixtures/plain/app/configuration/api_key.rb +1 -1
  13. data/features/fixtures/plain/app/stack_frame_modification/initiators/handled_before_notify.rb +5 -1
  14. data/features/fixtures/rack1/Dockerfile +7 -6
  15. data/features/fixtures/rack1/Gemfile +1 -1
  16. data/features/fixtures/rack2/Dockerfile +7 -6
  17. data/features/fixtures/rack2/Gemfile +1 -1
  18. data/features/fixtures/rails3/Dockerfile +5 -6
  19. data/features/fixtures/rails3/app/Gemfile +1 -1
  20. data/features/fixtures/rails3/app/config/initializers/bugsnag.rb +12 -12
  21. data/features/fixtures/rails4/Dockerfile +7 -5
  22. data/features/fixtures/rails4/app/Gemfile +1 -1
  23. data/features/fixtures/rails4/app/config/initializers/bugsnag.rb +12 -12
  24. data/features/fixtures/rails5/Dockerfile +5 -6
  25. data/features/fixtures/rails5/app/Gemfile +1 -1
  26. data/features/fixtures/rails5/app/config/initializers/bugsnag.rb +12 -12
  27. data/features/fixtures/resque/Dockerfile +6 -5
  28. data/features/fixtures/resque/Gemfile +1 -1
  29. data/features/fixtures/sidekiq/Dockerfile +4 -2
  30. data/features/fixtures/sidekiq/app/Gemfile +3 -1
  31. data/features/fixtures/sidekiq/app/app.rb +4 -4
  32. data/features/fixtures/sinatra1/Dockerfile +6 -5
  33. data/features/fixtures/sinatra1/Gemfile +1 -1
  34. data/features/fixtures/sinatra2/Dockerfile +6 -5
  35. data/features/fixtures/sinatra2/Gemfile +1 -1
  36. data/features/plain_features/add_tab.feature +15 -17
  37. data/features/plain_features/api_key.feature +2 -3
  38. data/features/plain_features/app_type.feature +3 -5
  39. data/features/plain_features/app_version.feature +3 -5
  40. data/features/plain_features/auto_notify.feature +1 -3
  41. data/features/plain_features/delivery.feature +3 -5
  42. data/features/plain_features/exception_data.feature +4 -6
  43. data/features/plain_features/filters.feature +4 -6
  44. data/features/plain_features/handled_errors.feature +48 -30
  45. data/features/plain_features/ignore_classes.feature +0 -2
  46. data/features/plain_features/ignore_report.feature +0 -2
  47. data/features/plain_features/proxies.feature +4 -6
  48. data/features/plain_features/release_stages.feature +8 -10
  49. data/features/plain_features/report_api_key.feature +1 -3
  50. data/features/plain_features/report_severity.feature +3 -5
  51. data/features/plain_features/report_stack_frames.feature +54 -21
  52. data/features/plain_features/report_user.feature +10 -12
  53. data/features/plain_features/unhandled_errors.feature +4 -6
  54. data/features/rails_features/api_key.feature +3 -3
  55. data/features/rails_features/app_type.feature +4 -4
  56. data/features/rails_features/app_version.feature +2 -2
  57. data/features/rails_features/auto_capture_sessions.feature +4 -4
  58. data/features/rails_features/auto_notify.feature +5 -5
  59. data/features/rails_features/before_notify.feature +4 -4
  60. data/features/rails_features/handled.feature +6 -6
  61. data/features/rails_features/ignore_classes.feature +2 -2
  62. data/features/rails_features/meta_data_filters.feature +2 -2
  63. data/features/rails_features/project_root.feature +8 -8
  64. data/features/rails_features/release_stage.feature +2 -2
  65. data/features/rails_features/send_code.feature +2 -2
  66. data/features/rails_features/send_environment.feature +3 -3
  67. data/features/rails_features/unhandled.feature +4 -4
  68. data/features/rails_features/user_info.feature +4 -4
  69. data/features/sidekiq.feature +9 -10
  70. data/features/steps/ruby_notifier_steps.rb +6 -25
  71. data/features/support/env.rb +18 -9
  72. data/lib/bugsnag/configuration.rb +3 -3
  73. data/lib/bugsnag/integrations/mailman.rb +0 -1
  74. data/lib/bugsnag/integrations/shoryuken.rb +6 -8
  75. data/lib/bugsnag/integrations/sidekiq.rb +14 -11
  76. data/lib/bugsnag/stacktrace.rb +1 -2
  77. data/spec/configuration_spec.rb +1 -1
  78. data/spec/integrations/sidekiq_spec.rb +73 -7
  79. data/spec/report_spec.rb +62 -20
  80. data/spec/spec_helper.rb +1 -1
  81. data/spec/stacktrace_spec.rb +20 -16
  82. metadata +2 -2
@@ -1,19 +1,19 @@
1
1
  Feature: Send environment
2
2
 
3
3
  Background:
4
- Given I set environment variable "MAZE_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
4
+ Given I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
5
5
  And I set environment variable "APP_PATH" to "/usr/src"
6
6
  And I configure the bugsnag endpoint
7
7
 
8
8
  Scenario Outline: Send_environment should send environment in handled errors when true
9
9
  Given I set environment variable "RUBY_VERSION" to "<ruby_version>"
10
- And I set environment variable "MAZE_SEND_ENVIRONMENT" to "true"
10
+ And I set environment variable "BUGSNAG_SEND_ENVIRONMENT" to "true"
11
11
  And I start the service "rails<rails_version>"
12
12
  And I wait for the app to respond on port "6128<rails_version>"
13
13
  When I navigate to the route "/send_environment/initializer" on port "6128<rails_version>"
14
14
  Then I should receive a request
15
15
  And the request is a valid for the error reporting API
16
- And the request used the Ruby notifier
16
+ And the request used the "Ruby Bugsnag Notifier" notifier
17
17
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
18
18
  And the payload field "events" is an array with 1 element
19
19
  And the exception "errorClass" equals "RuntimeError"
@@ -1,7 +1,7 @@
1
1
  Feature: Unhandled exceptions support
2
2
 
3
3
  Background:
4
- Given I set environment variable "MAZE_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
4
+ Given I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
5
5
  And I set environment variable "APP_PATH" to "/usr/src"
6
6
  And I configure the bugsnag endpoint
7
7
 
@@ -12,7 +12,7 @@ Scenario Outline: Unhandled RuntimeError
12
12
  When I navigate to the route "/unhandled/error" on port "6128<rails_version>"
13
13
  Then I should receive a request
14
14
  And the request is a valid for the error reporting API
15
- And the request used the Ruby notifier
15
+ And the request used the "Ruby Bugsnag Notifier" notifier
16
16
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
17
17
  And the payload field "events" is an array with 1 element
18
18
  And the event "unhandled" is true
@@ -21,8 +21,8 @@ Scenario Outline: Unhandled RuntimeError
21
21
  And the event "app.type" equals "rails"
22
22
  And the event "metaData.request.url" ends with "/unhandled/error"
23
23
  And the event "severity" equals "error"
24
- And the event "severityReason.type" is "unhandledExceptionMiddleware"
25
- And the event "severityReason.attributes.framework" is "Rack"
24
+ And the event "severityReason.type" equals "unhandledExceptionMiddleware"
25
+ And the event "severityReason.attributes.framework" equals "Rack"
26
26
 
27
27
  Examples:
28
28
  | ruby_version | rails_version |
@@ -1,7 +1,7 @@
1
1
  Feature: Capture user information
2
2
 
3
3
  Background:
4
- Given I set environment variable "MAZE_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
4
+ Given I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
5
5
  And I set environment variable "APP_PATH" to "/usr/src"
6
6
  And I configure the bugsnag endpoint
7
7
 
@@ -13,7 +13,7 @@ Scenario Outline: Warden user information is sent
13
13
  And I navigate to the route "/warden/<route>?email=testtest@test.test" on port "6128<rails_version>"
14
14
  Then I should receive a request
15
15
  And the request is a valid for the error reporting API
16
- And the request used the Ruby notifier
16
+ And the request used the "Ruby Bugsnag Notifier" notifier
17
17
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
18
18
  And the payload field "events" is an array with 1 element
19
19
  And the event "user.email" equals "testtest@test.test"
@@ -45,7 +45,7 @@ Scenario Outline: Devise user information is sent
45
45
  And I navigate to the route "/devise/<route>" on port "6128<rails_version>"
46
46
  Then I should receive a request
47
47
  And the request is a valid for the error reporting API
48
- And the request used the Ruby notifier
48
+ And the request used the "Ruby Bugsnag Notifier" notifier
49
49
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
50
50
  And the payload field "events" is an array with 1 element
51
51
  And the event "user.email" equals "test+test@test.test"
@@ -68,7 +68,7 @@ Scenario Outline: Clearance user information is sent
68
68
  And I navigate to the route "/clearance/<route>" on port "6128<rails_version>"
69
69
  Then I should receive a request
70
70
  And the request is a valid for the error reporting API
71
- And the request used the Ruby notifier
71
+ And the request used the "Ruby Bugsnag Notifier" notifier
72
72
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
73
73
  And the payload field "events" is an array with 1 element
74
74
  And the event "user.email" equals "testtest@test.test"
@@ -2,7 +2,6 @@ Feature: Bugsnag raises errors in Sidekiq workers
2
2
 
3
3
  Background:
4
4
  Given I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
5
- Given I set environment variable "MAZE_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa"
6
5
  And I set environment variable "APP_PATH" to "/usr/src"
7
6
  And I configure the bugsnag endpoint
8
7
 
@@ -13,14 +12,14 @@ Scenario Outline: An unhandled RuntimeError sends a report
13
12
  And I run the command "bundle exec ruby initializers/UnhandledError.rb" on the service "sidekiq"
14
13
  And I wait for 1 seconds
15
14
  Then I should receive a request
16
- And the request used the Ruby notifier
15
+ And the request used the "Ruby Bugsnag Notifier" notifier
17
16
  And the request used payload v4 headers
18
17
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
19
18
  And the event "unhandled" is true
20
- And the event "severity" is "error"
21
- And the event "context" is "UnhandledError@default"
22
- And the event "severityReason.type" is "unhandledExceptionMiddleware"
23
- And the event "severityReason.attributes.framework" is "Sidekiq"
19
+ And the event "severity" equals "error"
20
+ And the event "context" equals "UnhandledError@default"
21
+ And the event "severityReason.type" equals "unhandledExceptionMiddleware"
22
+ And the event "severityReason.attributes.framework" equals "Sidekiq"
24
23
  And the exception "errorClass" equals "RuntimeError"
25
24
  And the "file" of stack frame 0 equals "/usr/src/app.rb"
26
25
  And the "lineNumber" of stack frame 0 equals 33
@@ -59,13 +58,13 @@ Scenario Outline: A handled RuntimeError can be notified
59
58
  And I run the command "bundle exec ruby initializers/HandledError.rb" on the service "sidekiq"
60
59
  And I wait for 1 seconds
61
60
  Then I should receive a request
62
- And the request used the Ruby notifier
61
+ And the request used the "Ruby Bugsnag Notifier" notifier
63
62
  And the request used payload v4 headers
64
63
  And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa"
65
64
  And the event "unhandled" is false
66
- And the event "context" is "HandledError@default"
67
- And the event "severity" is "warning"
68
- And the event "severityReason.type" is "handledException"
65
+ And the event "context" equals "HandledError@default"
66
+ And the event "severity" equals "warning"
67
+ And the event "severityReason.type" equals "handledException"
69
68
  And the exception "errorClass" equals "RuntimeError"
70
69
  And the "file" of stack frame 0 equals "/usr/src/app.rb"
71
70
  And the "lineNumber" of stack frame 0 equals 24
@@ -1,44 +1,25 @@
1
- require 'net/http'
2
- require 'open3'
3
-
4
- When("I configure the bugsnag endpoint") do
5
- steps %Q{
6
- When I set environment variable "MAZE_ENDPOINT" to "http://#{current_ip}:#{MOCK_API_PORT}"
7
- }
8
- end
9
-
10
- When("I navigate to the route {string} on port {string}") do |route, port|
11
- steps %Q{
12
- When I open the URL "http://localhost:#{port}#{route}"
13
- And I wait for 1 second
14
- }
15
- end
16
-
17
1
  When("I set environment variable {string} to the current IP") do |env_var|
18
2
  steps %Q{
19
3
  When I set environment variable "#{env_var}" to "#{current_ip}"
20
4
  }
21
5
  end
6
+
22
7
  When("I set environment variable {string} to the mock API port") do |env_var|
23
8
  steps %Q{
24
9
  When I set environment variable "#{env_var}" to "#{MOCK_API_PORT}"
25
10
  }
26
11
  end
12
+
27
13
  When("I set environment variable {string} to the proxy settings with credentials {string}") do |env_var, credentials|
28
14
  steps %Q{
29
15
  When I set environment variable "#{env_var}" to "#{credentials}@#{current_ip}:#{MOCK_API_PORT}"
30
16
  }
31
17
  end
32
- Then("the request used the Ruby notifier") do
33
- bugsnag_regex = /^http(s?):\/\/www.bugsnag.com/
34
- steps %Q{
35
- Then the payload field "notifier.name" equals "Ruby Bugsnag Notifier"
36
- And the payload field "notifier.url" matches the regex "#{bugsnag_regex}"
37
- }
38
- end
39
18
 
40
- Then("the event {string} is {string}") do |key, value|
19
+ Then(/^the "(.+)" of the top non-bugsnag stackframe equals (\d+|".+")(?: for request (\d+))?$/) do |element, value, request_index|
20
+ stacktrace = read_key_path(find_request(request_index)[:body], 'events.0.exceptions.0.stacktrace')
21
+ frame_index = stacktrace.find_index { |frame| ! /.*lib\/bugsnag.*\.rb/.match(frame["file"]) }
41
22
  steps %Q{
42
- Then the event "#{key}" equals "#{value}"
23
+ the "#{element}" of stack frame #{frame_index} equals #{value}
43
24
  }
44
25
  end
@@ -1,5 +1,4 @@
1
- require 'open3'
2
- require 'os'
1
+ require 'fileutils'
3
2
 
4
3
  Before do
5
4
  find_default_docker_compose
@@ -34,14 +33,24 @@ def install_fixture_gems
34
33
  end
35
34
  end
36
35
 
37
- def current_ip
38
- if OS.mac?
39
- 'host.docker.internal'
40
- else
41
- ip_addr = `ifconfig | grep -Eo 'inet (addr:)?([0-9]*\\\.){3}[0-9]*' | grep -v '127.0.0.1'`
42
- ip_list = /((?:[0-9]*\.){3}[0-9]*)/.match(ip_addr)
43
- ip_list.captures.first
36
+ # Added to ensure that multiple versions of Gems do not exist within the fixture folders,
37
+ # which can be difficult to track down and clear up
38
+ def remove_installed_gems
39
+ removal_targets = ['temp-bugsnag-lib', 'bugsnag-*.gem']
40
+ Dir.entries('features/fixtures').reject { |entry| ['.', '..'].include?(entry) }.each do |entry|
41
+ target_dir = "features/fixtures/#{entry}"
42
+ target_entries = []
43
+ removal_targets.each do |r_target|
44
+ target_entries += Dir.glob("#{target_dir}/#{r_target}")
45
+ end
46
+ target_entries.each do |d_target|
47
+ FileUtils.rm_rf(d_target)
48
+ end
44
49
  end
45
50
  end
46
51
 
52
+ at_exit do
53
+ remove_installed_gems
54
+ end
55
+
47
56
  install_fixture_gems
@@ -69,9 +69,9 @@ module Bugsnag
69
69
  self.auto_capture_sessions = false
70
70
  self.session_endpoint = DEFAULT_SESSION_ENDPOINT
71
71
 
72
- # SystemExit and Interrupt are common Exception types seen with successful
73
- # exits and are not automatically reported to Bugsnag
74
- self.ignore_classes = Set.new([SystemExit, Interrupt])
72
+ # SystemExit and SignalException are common Exception types seen with
73
+ # successful exits and are not automatically reported to Bugsnag
74
+ self.ignore_classes = Set.new([SystemExit, SignalException])
75
75
 
76
76
  # Read the API key from the environment
77
77
  self.api_key = ENV["BUGSNAG_API_KEY"]
@@ -21,7 +21,6 @@ module Bugsnag
21
21
  Bugsnag.configuration.set_request_data :mailman_msg, mail.to_s
22
22
  yield
23
23
  rescue Exception => ex
24
- raise ex if [Interrupt, SystemExit, SignalException].include? ex.class
25
24
  Bugsnag.notify(ex, true) do |report|
26
25
  report.severity = "error"
27
26
  report.severity_reason = {
@@ -27,14 +27,12 @@ module Bugsnag
27
27
 
28
28
  yield
29
29
  rescue Exception => ex
30
- unless [Interrupt, SystemExit, SignalException].include?(ex.class)
31
- Bugsnag.notify(ex, true) do |report|
32
- report.severity = "error"
33
- report.severity_reason = {
34
- :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
35
- :attributes => Bugsnag::Shoryuken::FRAMEWORK_ATTRIBUTES
36
- }
37
- end
30
+ Bugsnag.notify(ex, true) do |report|
31
+ report.severity = "error"
32
+ report.severity_reason = {
33
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
34
+ :attributes => Bugsnag::Shoryuken::FRAMEWORK_ATTRIBUTES
35
+ }
38
36
  end
39
37
  raise
40
38
  ensure
@@ -18,20 +18,22 @@ module Bugsnag
18
18
  end
19
19
 
20
20
  def call(worker, msg, queue)
21
- begin
22
- # store msg/queue in thread local state to be read by Bugsnag::Middleware::Sidekiq
23
- Bugsnag.configuration.set_request_data :sidekiq, { :msg => msg, :queue => queue }
24
- yield
25
- rescue Exception => ex
26
- self.class.notify(ex) unless self.class.sidekiq_supports_error_handlers
27
- raise
28
- ensure
29
- Bugsnag.configuration.clear_request_data
30
- end
21
+ # store msg/queue in thread local state to be read by Bugsnag::Middleware::Sidekiq
22
+ Bugsnag.configuration.set_request_data(:sidekiq, { :msg => msg, :queue => queue })
23
+ error_raised = false
24
+ yield
25
+ rescue Exception => ex
26
+ error_raised = true
27
+ self.class.notify(ex) unless self.class.sidekiq_supports_error_handlers
28
+ raise
29
+ ensure
30
+ # if an error was raised and error handlers are installed, the data will be cleared after
31
+ # the notification is sent. Otherwise, the data must be cleared.
32
+ keep_data = error_raised && self.class.sidekiq_supports_error_handlers
33
+ Bugsnag.configuration.clear_request_data unless keep_data
31
34
  end
32
35
 
33
36
  def self.notify(exception)
34
- return if [Interrupt, SystemExit, SignalException].include? exception.class
35
37
  Bugsnag.notify(exception, true) do |report|
36
38
  report.severity = "error"
37
39
  report.severity_reason = {
@@ -49,6 +51,7 @@ module Bugsnag
49
51
  if Bugsnag::Sidekiq.sidekiq_supports_error_handlers
50
52
  server.error_handlers << proc do |ex, _context|
51
53
  Bugsnag::Sidekiq.notify(ex)
54
+ Bugsnag.configuration.clear_request_data
52
55
  end
53
56
  end
54
57
 
@@ -26,8 +26,7 @@ module Bugsnag
26
26
 
27
27
  # Parse the stacktrace line
28
28
 
29
- # Skip stacktrace lines inside lib/bugsnag
30
- next(nil) if file.nil? || file =~ %r{lib/bugsnag(/|\.rb)}
29
+ next(nil) if file.nil?
31
30
 
32
31
  # Expand relative paths
33
32
  p = Pathname.new(file)
@@ -219,7 +219,7 @@ describe Bugsnag::Configuration do
219
219
  end
220
220
 
221
221
  it "should have exit exception classes ignored by default" do
222
- expect(subject.ignore_classes).to eq(Set.new([SystemExit, Interrupt]))
222
+ expect(subject.ignore_classes).to eq(Set.new([SystemExit, SignalException]))
223
223
  end
224
224
 
225
225
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'sidekiq/testing'
3
3
 
4
- class FailingWorker
4
+ class Worker
5
5
  include Sidekiq::Worker
6
6
  def perform(value)
7
7
  puts "Work: #{100/value}"
@@ -21,15 +21,11 @@ describe Bugsnag::Sidekiq do
21
21
  end
22
22
 
23
23
  it "works" do
24
- begin
25
- FailingWorker.perform_async(-0)
26
- fail("shouldn't be here")
27
- rescue
28
- end
24
+ expect{ Worker.perform_async(-0) }.to raise_error(ZeroDivisionError)
29
25
 
30
26
  expect(Bugsnag).to have_sent_notification {|payload, headers|
31
27
  event = get_event_from_payload(payload)
32
- expect(event["metaData"]["sidekiq"]["msg"]["class"]).to eq("FailingWorker")
28
+ expect(event["metaData"]["sidekiq"]["msg"]["class"]).to eq("Worker")
33
29
  expect(event["metaData"]["sidekiq"]["msg"]["args"]).to eq([-0])
34
30
  expect(event["metaData"]["sidekiq"]["msg"]["queue"]).to eq("default")
35
31
  expect(event["severity"]).to eq("error")
@@ -66,4 +62,74 @@ describe Bugsnag::Sidekiq do
66
62
  expect(error_handlers.size).to eq(1)
67
63
  end
68
64
  end
65
+
66
+ context "ensuring data reset" do
67
+ context "v2" do
68
+ before do
69
+ Sidekiq::Testing.inline!
70
+ stub_const('Sidekiq::VERSION', '2.0.0')
71
+ Sidekiq::Testing.server_middleware do |chain|
72
+ chain.add ::Bugsnag::Sidekiq
73
+ end
74
+ end
75
+
76
+ it "resets with an exception" do
77
+ expect(Bugsnag.configuration).to receive(:set_request_data).with(:sidekiq, hash_including(:queue => "default"))
78
+ expect(Bugsnag.configuration).to receive(:clear_request_data).at_least(:once)
79
+ expect{ Worker.perform_async(-0) }.to raise_error(ZeroDivisionError)
80
+ end
81
+
82
+ it "resets without an exception" do
83
+ expect(Bugsnag.configuration).to receive(:set_request_data).with(:sidekiq, hash_including(:queue => "default"))
84
+ expect(Bugsnag.configuration).to receive(:clear_request_data).at_least(:once)
85
+ expect{ Worker.perform_async(1) }.to_not raise_error
86
+ end
87
+ end
88
+
89
+ context "v3" do
90
+ before do
91
+ Sidekiq::Testing.inline!
92
+ stub_const('Sidekiq::VERSION', '3.0.0')
93
+ Sidekiq::Testing.server_middleware do |chain|
94
+ chain.add ::Bugsnag::Sidekiq
95
+ end
96
+ end
97
+
98
+ it "doesn't reset with an exception" do
99
+ # This test ensures that when an exception occurs the request data isn't immediately reset
100
+ expect(Bugsnag.configuration).to receive(:set_request_data).with(:sidekiq, hash_including(:queue => "default"))
101
+ # Always received once due to the spec_helper setup
102
+ expect(Bugsnag.configuration).to receive(:clear_request_data).once
103
+ expect{ Worker.perform_async(-0) }.to raise_error(ZeroDivisionError)
104
+ end
105
+
106
+ it "resets without an exception" do
107
+ expect(Bugsnag.configuration).to receive(:set_request_data).with(:sidekiq, hash_including(:queue => "default"))
108
+ expect(Bugsnag.configuration).to receive(:clear_request_data).twice
109
+ expect{ Worker.perform_async(1) }.to_not raise_error
110
+ end
111
+
112
+ it "always resets in the error handler" do
113
+ config = double
114
+ allow(::Sidekiq).to receive(:configure_server).and_yield(config)
115
+
116
+ error_handlers = []
117
+ allow(config).to receive(:error_handlers).and_return(error_handlers)
118
+
119
+ chain = double
120
+ allow(config).to receive(:server_middleware).and_yield(chain)
121
+ allow(chain).to receive(:add).with(::Bugsnag::Sidekiq)
122
+
123
+ Bugsnag::Sidekiq.configure_server(config)
124
+
125
+ expect(error_handlers.size).to equal(1)
126
+
127
+ ex = double
128
+ expect(Bugsnag::Sidekiq).to receive(:notify).with(ex)
129
+ expect(Bugsnag.configuration).to receive(:clear_request_data).twice
130
+
131
+ error_handlers[0].call(ex)
132
+ end
133
+ end
134
+ end
69
135
  end
@@ -146,18 +146,27 @@ describe Bugsnag::Report do
146
146
  end
147
147
 
148
148
  it "sets correct severity and reason for specific error classes" do
149
- Bugsnag.notify(SignalException.new("TERM"))
150
- expect(Bugsnag).to have_sent_notification{ |payload, headers|
151
- event = get_event_from_payload(payload)
152
- expect(event["unhandled"]).to be false
153
- expect(event["severity"]).to eq("info")
154
- expect(event["severityReason"]).to eq({
155
- "type" => "errorClass",
156
- "attributes" => {
157
- "errorClass" => "SignalException"
158
- }
159
- })
160
- }
149
+ original_ignore_classes = Bugsnag.configuration.ignore_classes
150
+
151
+ begin
152
+ # The default ignore_classes includes SignalException, so we need to
153
+ # temporarily set it to something else.
154
+ Bugsnag.configuration.ignore_classes = Set[SystemExit]
155
+ Bugsnag.notify(SignalException.new("TERM"))
156
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
157
+ event = get_event_from_payload(payload)
158
+ expect(event["unhandled"]).to be false
159
+ expect(event["severity"]).to eq("info")
160
+ expect(event["severityReason"]).to eq({
161
+ "type" => "errorClass",
162
+ "attributes" => {
163
+ "errorClass" => "SignalException"
164
+ }
165
+ })
166
+ }
167
+ ensure
168
+ Bugsnag.configuration.ignore_classes = original_ignore_classes
169
+ end
161
170
  end
162
171
 
163
172
  # TODO: nested context
@@ -503,7 +512,12 @@ describe Bugsnag::Report do
503
512
 
504
513
  it "does not mark the top-most stacktrace line as inProject if out of project" do
505
514
  Bugsnag.configuration.project_root = "/Random/location/here"
506
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
515
+
516
+ begin
517
+ "Test".prepnd "T"
518
+ rescue Exception => e
519
+ Bugsnag.notify(e)
520
+ end
507
521
 
508
522
  expect(Bugsnag).to have_sent_notification{ |payload, headers|
509
523
  exception = get_exception_from_payload(payload)
@@ -514,12 +528,17 @@ describe Bugsnag::Report do
514
528
 
515
529
  it "marks the top-most stacktrace line as inProject if necessary" do
516
530
  Bugsnag.configuration.project_root = File.expand_path File.dirname(__FILE__)
517
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
531
+
532
+ begin
533
+ "Test".prepnd "T"
534
+ rescue Exception => e
535
+ Bugsnag.notify(e)
536
+ end
518
537
 
519
538
  expect(Bugsnag).to have_sent_notification{ |payload, headers|
520
539
  exception = get_exception_from_payload(payload)
521
540
  expect(exception["stacktrace"].size).to be >= 1
522
- expect(exception["stacktrace"].first["inProject"]).to eq(true)
541
+ expect(exception["stacktrace"][0]["inProject"]).to eq(true)
523
542
  }
524
543
  end
525
544
 
@@ -1050,12 +1069,35 @@ describe Bugsnag::Report do
1050
1069
  exception = event["exceptions"][0]
1051
1070
  expect(exception["errorClass"]).to eq("RuntimeError")
1052
1071
  expect(exception["message"]).to eq("'nil' was notified as an exception")
1072
+ }
1073
+ end
1053
1074
 
1054
- stacktrace = exception["stacktrace"][0]
1055
- expect(stacktrace["lineNumber"]).to eq(1047)
1056
- expect(stacktrace["file"]).to end_with("spec/report_spec.rb")
1057
- expect(stacktrace["code"]["1046"]).to eq(" it 'uses an appropriate message if nil is notified' do")
1058
- expect(stacktrace["code"]["1047"]).to eq(" Bugsnag.notify(nil)")
1075
+ it "includes bugsnag lines marked out of project" do
1076
+ notify_test_exception
1077
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1078
+ exception = get_exception_from_payload(payload)
1079
+ bugsnag_count = 0
1080
+ exception["stacktrace"].each do |frame|
1081
+ if /.*lib\/bugsnag.*\.rb/.match(frame["file"])
1082
+ bugsnag_count += 1
1083
+ expect(frame["inProject"]).to be_nil
1084
+ end
1085
+ end
1086
+ # 7 is used here as the called bugsnag frames for a `notify` call should be:
1087
+ # - Bugsnag.notify
1088
+ # - Report.new
1089
+ # - Report.initialize
1090
+ # - Report.generate_exceptions_list
1091
+ # - Report.generate_exceptions_list | raw_exceptions.map
1092
+ # - Report.generate_exceptions_list | raw_exceptions.map | block
1093
+ # - Report.generate_exceptions_list | raw_exceptions.map | block | Stacktrace.new
1094
+ # However, JRUBY does not include the two `new` frames, resulting in 5 bugsnag frames
1095
+ if defined?(JRUBY_VERSION)
1096
+ frame_count = 5
1097
+ else
1098
+ frame_count = 7
1099
+ end
1100
+ expect(bugsnag_count).to equal frame_count
1059
1101
  }
1060
1102
  end
1061
1103