bugsnag 6.12.1 → 6.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +470 -0
  3. data/.rubocop.yml +55 -0
  4. data/.rubocop_todo.yml +530 -160
  5. data/CHANGELOG.md +73 -0
  6. data/CONTRIBUTING.md +1 -9
  7. data/Gemfile +14 -7
  8. data/TESTING.md +81 -0
  9. data/VERSION +1 -1
  10. data/docker-compose.yml +46 -0
  11. data/dockerfiles/Dockerfile.jruby-unit-tests +13 -0
  12. data/dockerfiles/Dockerfile.ruby-maze-runner +26 -0
  13. data/dockerfiles/Dockerfile.ruby-unit-tests +12 -0
  14. data/features/delayed_job.feature +6 -22
  15. data/features/fixtures/delayed_job/Dockerfile +2 -4
  16. data/features/fixtures/delayed_job/app/Gemfile +1 -1
  17. data/features/fixtures/delayed_job/app/Rakefile +18 -0
  18. data/features/fixtures/docker-compose.yml +32 -40
  19. data/features/fixtures/expected_breadcrumbs/active_job.json +9 -0
  20. data/features/fixtures/expected_breadcrumbs/mongo_failed.json +15 -0
  21. data/features/fixtures/expected_breadcrumbs/mongo_filtered_request.json +15 -0
  22. data/features/fixtures/expected_breadcrumbs/mongo_filtered_result.json +15 -0
  23. data/features/fixtures/expected_breadcrumbs/mongo_success.json +14 -0
  24. data/features/fixtures/expected_breadcrumbs/request.json +13 -0
  25. data/features/fixtures/expected_breadcrumbs/sql_with_bindings.json +12 -0
  26. data/features/fixtures/expected_breadcrumbs/sql_without_bindings.json +11 -0
  27. data/features/fixtures/plain/Dockerfile +2 -2
  28. data/features/fixtures/plain/app/app.rb +1 -3
  29. data/features/fixtures/plain/app/delivery/fork_threadpool.rb +3 -1
  30. data/features/fixtures/plain/app/report_modification/initiators/handled_on_error.rb +10 -0
  31. data/features/fixtures/plain/app/report_modification/initiators/unhandled_on_error.rb +11 -0
  32. data/features/fixtures/plain/app/stack_frame_modification/initiators/handled_on_error.rb +29 -0
  33. data/features/fixtures/plain/app/stack_frame_modification/initiators/unhandled_on_error.rb +26 -0
  34. data/features/fixtures/plain/app/unhandled/{Interrupt.rb → interrupt.rb} +0 -0
  35. data/features/fixtures/rack1/Dockerfile +2 -2
  36. data/features/fixtures/rack2/Dockerfile +2 -2
  37. data/features/fixtures/rails3/Dockerfile +2 -2
  38. data/features/fixtures/rails3/app/Gemfile +4 -0
  39. data/features/fixtures/rails3/app/config/initializers/bugsnag.rb +11 -2
  40. data/features/fixtures/rails4/Dockerfile +2 -5
  41. data/features/fixtures/rails4/app/config/initializers/bugsnag.rb +10 -1
  42. data/features/fixtures/rails5/Dockerfile +2 -2
  43. data/features/fixtures/rails5/app/Gemfile +3 -2
  44. data/features/fixtures/rails5/app/config/initializers/bugsnag.rb +10 -1
  45. data/features/fixtures/rails6/Dockerfile +2 -2
  46. data/features/fixtures/rails6/app/Gemfile +3 -2
  47. data/features/fixtures/rails6/app/app/controllers/mongo_controller.rb +22 -0
  48. data/features/fixtures/rails6/app/app/models/mongo_model.rb +6 -0
  49. data/features/fixtures/rails6/app/config/environments/development.rb +2 -0
  50. data/features/fixtures/rails6/app/config/environments/production.rb +1 -0
  51. data/features/fixtures/rails6/app/config/environments/rails_env.rb +1 -0
  52. data/features/fixtures/rails6/app/config/environments/test.rb +1 -0
  53. data/features/fixtures/rails6/app/config/initializers/bugsnag.rb +10 -1
  54. data/features/fixtures/rails6/app/config/mongoid.yml +23 -0
  55. data/features/fixtures/rails6/app/config/routes.rb +4 -0
  56. data/features/fixtures/resque/Dockerfile +2 -2
  57. data/features/fixtures/sidekiq/Dockerfile +5 -7
  58. data/features/fixtures/sidekiq/app/Gemfile +2 -1
  59. data/features/fixtures/sidekiq/app/Rakefile.rb +14 -0
  60. data/features/fixtures/sinatra1/Dockerfile +2 -2
  61. data/features/fixtures/sinatra2/Dockerfile +2 -2
  62. data/features/plain_features/add_tab.feature +30 -97
  63. data/features/plain_features/app_type.feature +6 -25
  64. data/features/plain_features/app_version.feature +6 -25
  65. data/features/plain_features/auto_notify.feature +4 -20
  66. data/features/plain_features/delivery.feature +12 -60
  67. data/features/plain_features/exception_data.feature +24 -94
  68. data/features/plain_features/filters.feature +9 -43
  69. data/features/plain_features/handled_errors.feature +16 -78
  70. data/features/plain_features/ignore_classes.feature +5 -23
  71. data/features/plain_features/ignore_report.feature +8 -24
  72. data/features/plain_features/proxies.feature +13 -56
  73. data/features/plain_features/release_stages.feature +9 -40
  74. data/features/plain_features/report_api_key.feature +11 -35
  75. data/features/plain_features/report_severity.feature +10 -35
  76. data/features/plain_features/report_stack_frames.feature +29 -93
  77. data/features/plain_features/report_user.feature +29 -96
  78. data/features/plain_features/unhandled_errors.feature +17 -88
  79. data/features/rails_features/api_key.feature +12 -58
  80. data/features/rails_features/app_type.feature +13 -58
  81. data/features/rails_features/app_version.feature +19 -80
  82. data/features/rails_features/auto_capture_sessions.feature +31 -112
  83. data/features/rails_features/auto_notify.feature +28 -105
  84. data/features/rails_features/before_notify.feature +18 -83
  85. data/features/rails_features/breadcrumbs.feature +40 -137
  86. data/features/rails_features/handled.feature +18 -82
  87. data/features/rails_features/ignore_classes.feature +12 -51
  88. data/features/rails_features/meta_data_filters.feature +9 -33
  89. data/features/rails_features/mongo_breadcrumbs.feature +22 -96
  90. data/features/rails_features/on_error.feature +29 -0
  91. data/features/rails_features/project_root.feature +19 -84
  92. data/features/rails_features/release_stage.feature +20 -82
  93. data/features/rails_features/send_code.feature +13 -55
  94. data/features/rails_features/send_environment.feature +7 -33
  95. data/features/rails_features/unhandled.feature +6 -31
  96. data/features/rails_features/user_info.feature +27 -65
  97. data/features/sidekiq.feature +12 -79
  98. data/features/steps/ruby_notifier_steps.rb +59 -15
  99. data/features/support/env.rb +12 -45
  100. data/lib/bugsnag.rb +109 -21
  101. data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +0 -2
  102. data/lib/bugsnag/breadcrumbs/validator.rb +0 -6
  103. data/lib/bugsnag/cleaner.rb +129 -60
  104. data/lib/bugsnag/code_extractor.rb +137 -0
  105. data/lib/bugsnag/configuration.rb +58 -1
  106. data/lib/bugsnag/helpers.rb +2 -4
  107. data/lib/bugsnag/integrations/que.rb +7 -4
  108. data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
  109. data/lib/bugsnag/middleware/exception_meta_data.rb +15 -9
  110. data/lib/bugsnag/middleware/ignore_error_class.rb +2 -0
  111. data/lib/bugsnag/middleware/rack_request.rb +2 -4
  112. data/lib/bugsnag/middleware_stack.rb +38 -3
  113. data/lib/bugsnag/on_error_callbacks.rb +33 -0
  114. data/lib/bugsnag/report.rb +4 -14
  115. data/lib/bugsnag/session_tracker.rb +3 -3
  116. data/lib/bugsnag/stacktrace.rb +28 -75
  117. data/spec/breadcrumbs/breadcrumb_spec.rb +1 -1
  118. data/spec/breadcrumbs/validator_spec.rb +1 -26
  119. data/spec/bugsnag_spec.rb +2 -2
  120. data/spec/cleaner_spec.rb +202 -10
  121. data/spec/code_extractor_spec.rb +129 -0
  122. data/spec/configuration_spec.rb +16 -1
  123. data/spec/fixtures/apps/rails-initializer-config/Gemfile +5 -1
  124. data/spec/fixtures/apps/rails-invalid-initializer-config/Gemfile +5 -1
  125. data/spec/fixtures/apps/rails-no-config/Gemfile +5 -1
  126. data/spec/fixtures/crashes/file1.rb +29 -0
  127. data/spec/fixtures/crashes/file2.rb +25 -0
  128. data/spec/fixtures/crashes/file_with_long_lines.rb +7 -0
  129. data/spec/fixtures/crashes/functions.rb +29 -0
  130. data/spec/fixtures/crashes/short_file.rb +2 -0
  131. data/spec/helper_spec.rb +0 -31
  132. data/spec/integrations/logger_spec.rb +1 -1
  133. data/spec/integrations/rack_spec.rb +8 -6
  134. data/spec/integrations/rake_spec.rb +1 -1
  135. data/spec/on_error_spec.rb +332 -0
  136. data/spec/report_spec.rb +331 -30
  137. data/spec/spec_helper.rb +14 -1
  138. data/spec/stacktrace_spec.rb +427 -74
  139. metadata +36 -7
  140. data/.travis.yml +0 -117
  141. data/features/plain_features/api_key.feature +0 -25
@@ -10,6 +10,7 @@ end
10
10
 
11
11
  require 'bugsnag'
12
12
 
13
+ require 'tmpdir'
13
14
  require 'webmock/rspec'
14
15
  require 'rspec/expectations'
15
16
  require 'rspec/mocks'
@@ -29,6 +30,14 @@ def get_exception_from_payload(payload)
29
30
  event["exceptions"].last
30
31
  end
31
32
 
33
+ def get_code_from_payload(payload, index = 0)
34
+ exception = get_exception_from_payload(payload)
35
+
36
+ expect(exception["stacktrace"].size).to be > index
37
+
38
+ exception["stacktrace"][index]["code"]
39
+ end
40
+
32
41
  def notify_test_exception(*args)
33
42
  Bugsnag.notify(RuntimeError.new("test message"), *args)
34
43
  end
@@ -43,12 +52,16 @@ end
43
52
 
44
53
  RSpec.configure do |config|
45
54
  config.order = "random"
55
+ config.example_status_persistence_file_path = "#{Dir.tmpdir}/rspec_status"
46
56
 
47
57
  config.before(:each) do
48
58
  WebMock.stub_request(:post, "https://notify.bugsnag.com/")
49
59
  WebMock.stub_request(:post, "https://sessions.bugsnag.com/")
50
60
 
51
61
  Bugsnag.instance_variable_set(:@configuration, Bugsnag::Configuration.new)
62
+ Bugsnag.instance_variable_set(:@session_tracker, Bugsnag::SessionTracker.new)
63
+ Bugsnag.instance_variable_set(:@cleaner, Bugsnag::Cleaner.new(Bugsnag.configuration))
64
+
52
65
  Bugsnag.configure do |bugsnag|
53
66
  bugsnag.api_key = "c9d60ae4c7e70c4b6c4ebd3e8056d2b8"
54
67
  bugsnag.release_stage = "production"
@@ -83,4 +96,4 @@ def have_sent_notification(&matcher)
83
96
  raise "no matcher provided to have_sent_notification (did you use { })"
84
97
  end
85
98
  end
86
- end
99
+ end
@@ -1,90 +1,443 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Bugsnag::Stacktrace do
4
- it "includes code in the stack trace" do
5
- begin
6
- _a = 1
7
- _b = 2
8
- _c = 3
9
- "Test".prepnd "T"
10
- _d = 4
11
- _e = 5
12
- _f = 6
13
- rescue Exception => e
14
- Bugsnag.notify(e)
15
- end
16
-
17
- expect(Bugsnag).to have_sent_notification{ |payload, headers|
18
- exception = get_exception_from_payload(payload)
19
- starting_line = __LINE__ - 13
20
- expect(exception["stacktrace"][0]["code"]).to eq({
21
- (starting_line + 0).to_s => ' _a = 1',
22
- (starting_line + 1).to_s => ' _b = 2',
23
- (starting_line + 2).to_s => ' _c = 3',
24
- (starting_line + 3).to_s => ' "Test".prepnd "T"',
25
- (starting_line + 4).to_s => ' _d = 4',
26
- (starting_line + 5).to_s => ' _e = 5',
27
- (starting_line + 6).to_s => ' _f = 6'
4
+ context "sending code" do
5
+ it "includes code in the stack trace" do
6
+ begin
7
+ _a = 1
8
+ _b = 2
9
+ _c = 3
10
+ "Test".prepnd "T"
11
+ _d = 4
12
+ _e = 5
13
+ _f = 6
14
+ rescue Exception => e
15
+ Bugsnag.notify(e)
16
+ end
17
+
18
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
19
+ starting_line = __LINE__ - 12
20
+
21
+ expect(get_code_from_payload(payload).to_a).to eq({
22
+ (starting_line + 0).to_s => ' _a = 1',
23
+ (starting_line + 1).to_s => ' _b = 2',
24
+ (starting_line + 2).to_s => ' _c = 3',
25
+ (starting_line + 3).to_s => ' "Test".prepnd "T"',
26
+ (starting_line + 4).to_s => ' _d = 4',
27
+ (starting_line + 5).to_s => ' _e = 5',
28
+ (starting_line + 6).to_s => ' _f = 6'
29
+ }.to_a)
30
+ }
31
+ end
32
+
33
+ it "allows you to disable sending code" do
34
+ Bugsnag.configuration.send_code = false
35
+
36
+ notify_test_exception
37
+
38
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
39
+ expect(get_code_from_payload(payload)).to eq(nil)
40
+ }
41
+ end
42
+
43
+ it 'should send the first 7 lines of the file for exceptions near the top' do
44
+ load 'spec/fixtures/crashes/start_of_file.rb' rescue Bugsnag.notify $!
45
+
46
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
47
+ expect(get_code_from_payload(payload).to_a).to eq({
48
+ "1" => "#",
49
+ "2" => "raise 'hell'",
50
+ "3" => "#",
51
+ "4" => "#",
52
+ "5" => "#",
53
+ "6" => "#",
54
+ "7" => "#"
55
+ }.to_a)
56
+ }
57
+ end
58
+
59
+ it 'should send the last 7 lines of the file for exceptions near the bottom' do
60
+ load 'spec/fixtures/crashes/end_of_file.rb' rescue Bugsnag.notify $!
61
+
62
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
63
+ expect(get_code_from_payload(payload)).to eq({
64
+ "3" => "#",
65
+ "4" => "#",
66
+ "5" => "#",
67
+ "6" => "#",
68
+ "7" => "#",
69
+ "8" => "raise 'hell'",
70
+ "9" => "#"
28
71
  })
29
- }
30
- end
72
+ }
73
+ end
31
74
 
32
- it "allows you to disable sending code" do
33
- Bugsnag.configuration.send_code = false
75
+ it 'should send every line of a very short file' do
76
+ load 'spec/fixtures/crashes/short_file.rb' rescue Bugsnag.notify $!
34
77
 
35
- notify_test_exception
78
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
79
+ expect(get_code_from_payload(payload).to_a).to eq({
80
+ "1" => "#",
81
+ "2" => "raise 'hell'",
82
+ "3" => "#"
83
+ }.to_a)
84
+ }
85
+ end
36
86
 
37
- expect(Bugsnag).to have_sent_notification{ |payload, headers|
38
- exception = get_exception_from_payload(payload)
39
- expect(exception["stacktrace"][1]["code"]).to eq(nil)
40
- }
41
- end
87
+ it 'should send code for each line in the stacktrace' do
88
+ load 'spec/fixtures/crashes/functions.rb' rescue Bugsnag.notify $!
42
89
 
43
- it 'should send the first 7 lines of the file for exceptions near the top' do
44
- load 'spec/fixtures/crashes/start_of_file.rb' rescue Bugsnag.notify $!
45
-
46
- expect(Bugsnag).to have_sent_notification{ |payload, headers|
47
- exception = get_exception_from_payload(payload)
48
-
49
- expect(exception["stacktrace"][0]["code"]).to eq({
50
- "1" => "#",
51
- "2" => "raise 'hell'",
52
- "3" => "#",
53
- "4" => "#",
54
- "5" => "#",
55
- "6" => "#",
56
- "7" => "#"
57
- })
58
- }
90
+ expected_code = [
91
+ # The topmost frame is centered on where the exception was raised
92
+ {
93
+ "11" => "end",
94
+ "12" => "",
95
+ "13" => "def xyz",
96
+ "14" => " raise 'uh oh'",
97
+ "15" => "end",
98
+ "16" => "",
99
+ "17" => "def abc"
100
+ },
101
+ # then we get 'baz' which is where 'xyz' was called
102
+ {
103
+ "7" => "end",
104
+ "8" => "",
105
+ "9" => "def baz",
106
+ "10" => " xyz",
107
+ "11" => "end",
108
+ "12" => "",
109
+ "13" => "def xyz"
110
+ },
111
+ # then we get 'bar' which is where 'baz' was called
112
+ {
113
+ "3" => "end",
114
+ "4" => "",
115
+ "5" => "def bar",
116
+ "6" => " baz",
117
+ "7" => "end",
118
+ "8" => "",
119
+ "9" => "def baz"
120
+ },
121
+ # then we get 'foo' which is where 'bar' was called - this is the first
122
+ # 7 lines because the call to 'bar' is on line 2
123
+ {
124
+ "1" => "def foo",
125
+ "2" => " bar",
126
+ "3" => "end",
127
+ "4" => "",
128
+ "5" => "def bar",
129
+ "6" => " baz",
130
+ "7" => "end"
131
+ },
132
+ # finally we get the call to 'foo' - this is the last 7 lines because
133
+ # the call is on the last line of the file
134
+ {
135
+ "23" => "end",
136
+ "24" => "",
137
+ "25" => "def abcdefghi",
138
+ "26" => " puts 'abcdefghi'",
139
+ "27" => "end",
140
+ "28" => "",
141
+ "29" => "foo"
142
+ }
143
+ ]
144
+
145
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
146
+ (0...expected_code.size).each do |index|
147
+ expect(get_code_from_payload(payload, index).to_a).to eq(expected_code[index].to_a)
148
+ end
149
+ }
150
+ end
151
+
152
+ it 'should send code for each line in the stacktrace when split over multiple files' do
153
+ load 'spec/fixtures/crashes/file1.rb' rescue Bugsnag.notify $!
154
+
155
+ expected_code = [
156
+ {
157
+ "8" => " end",
158
+ "9" => "",
159
+ "10" => " def self.baz2",
160
+ "11" => " raise 'uh oh'",
161
+ "12" => " end",
162
+ "13" => "",
163
+ "14" => " def self.abc2"
164
+ },
165
+ {
166
+ "10" => " end",
167
+ "11" => "",
168
+ "12" => " def self.baz1",
169
+ "13" => " File2.baz2",
170
+ "14" => " end",
171
+ "15" => "",
172
+ "16" => " def self.abc1"
173
+ },
174
+ {
175
+ "4" => " end",
176
+ "5" => "",
177
+ "6" => " def self.bar2",
178
+ "7" => " File1.baz1",
179
+ "8" => " end",
180
+ "9" => "",
181
+ "10" => " def self.baz2"
182
+ },
183
+ {
184
+ "6" => " end",
185
+ "7" => "",
186
+ "8" => " def self.bar1",
187
+ "9" => " File2.bar2",
188
+ "10" => " end",
189
+ "11" => "",
190
+ "12" => " def self.baz1",
191
+ },
192
+ {
193
+ "1" => "module File2",
194
+ "2" => " def self.foo2",
195
+ "3" => " File1.bar1",
196
+ "4" => " end",
197
+ "5" => "",
198
+ "6" => " def self.bar2",
199
+ "7" => " File1.baz1"
200
+ },
201
+ {
202
+ "2" => "",
203
+ "3" => "module File1",
204
+ "4" => " def self.foo1",
205
+ "5" => " File2.foo2",
206
+ "6" => " end",
207
+ "7" => "",
208
+ "8" => " def self.bar1"
209
+ },
210
+ {
211
+ "23" => "",
212
+ "24" => " def self.abcdefghi1",
213
+ "25" => " puts 'abcdefghi1'",
214
+ "26" => " end",
215
+ "27" => "end",
216
+ "28" => "",
217
+ "29" => "File1.foo1"
218
+ }
219
+ ]
220
+
221
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
222
+ (0...expected_code.size).each do |index|
223
+ expect(get_code_from_payload(payload, index).to_a).to eq(expected_code[index].to_a)
224
+ end
225
+ }
226
+ end
227
+
228
+ it "can extract code from paths that will be mangled by the project root" do
229
+ # Set the project root to a nested directory, which will then be stripped
230
+ # from the file paths in the API call. This ensures that we read the files
231
+ # based off of the original path, rather than the final file path, e.g.
232
+ # "spec/fixtures/crashes/file1.rb" will be "file1.rb" in the API call, which
233
+ # isn't a path that's possible to read
234
+ project_root = "#{File.dirname(File.dirname(__FILE__))}/spec/fixtures/crashes"
235
+
236
+ configuration = Bugsnag::Configuration.new
237
+ configuration.project_root = project_root
238
+
239
+ backtrace = [
240
+ "spec/fixtures/crashes/file1.rb:13:in `baz1'",
241
+ "./spec/fixtures/crashes/functions.rb:17:in `abc'",
242
+ "#{project_root}/file2.rb:19:in `abcdef2'",
243
+ ]
244
+
245
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
246
+
247
+ expect(stacktrace).to eq([
248
+ {
249
+ file: "file1.rb",
250
+ lineNumber: 13,
251
+ method: "baz1",
252
+ inProject: true,
253
+ code: {
254
+ 10 => " end",
255
+ 11 => "",
256
+ 12 => " def self.baz1",
257
+ 13 => " File2.baz2",
258
+ 14 => " end",
259
+ 15 => "",
260
+ 16 => " def self.abc1"
261
+ }
262
+ },
263
+ {
264
+ file: "functions.rb",
265
+ lineNumber: 17,
266
+ method: "abc",
267
+ inProject: true,
268
+ code: {
269
+ 14 => " raise 'uh oh'",
270
+ 15 => "end",
271
+ 16 => "",
272
+ 17 => "def abc",
273
+ 18 => " puts 'abc'",
274
+ 19 => "end",
275
+ 20 => ""
276
+ },
277
+ },
278
+ {
279
+ file: "file2.rb",
280
+ lineNumber: 19,
281
+ method: "abcdef2",
282
+ inProject: true,
283
+ code: {
284
+ 16 => " end",
285
+ 17 => "",
286
+ 18 => " def self.abcdef2",
287
+ 19 => " puts 'abcdef2'",
288
+ 20 => " end",
289
+ 21 => "",
290
+ 22 => " def self.abcdefghi2"
291
+ },
292
+ },
293
+ ])
294
+ end
59
295
  end
60
296
 
61
- it 'should send the last 7 lines of the file for exceptions near the bottom' do
62
- load 'spec/fixtures/crashes/end_of_file.rb' rescue Bugsnag.notify $!
63
-
64
- expect(Bugsnag).to have_sent_notification{ |payload, headers|
65
- exception = get_exception_from_payload(payload)
66
-
67
- expect(exception["stacktrace"][0]["code"]).to eq({
68
- "3" => "#",
69
- "4" => "#",
70
- "5" => "#",
71
- "6" => "#",
72
- "7" => "#",
73
- "8" => "raise 'hell'",
74
- "9" => "#"
75
- })
76
- }
297
+ context "file paths" do
298
+ it "leaves absolute paths alone" do
299
+ configuration = Bugsnag::Configuration.new
300
+ configuration.send_code = false
301
+
302
+ backtrace = [
303
+ "/foo/bar/app/models/user.rb:1:in `something'",
304
+ "/foo/bar/other_vendor/lib/dont.rb:2:in `to_s'",
305
+ "/foo/bar/vendor/lib/ignore_me.rb:3:in `to_s'",
306
+ "/foo/bar/.bundle/lib/ignore_me.rb:4:in `to_s'",
307
+ ]
308
+
309
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
310
+
311
+ expect(stacktrace).to eq([
312
+ { file: "/foo/bar/app/models/user.rb", lineNumber: 1, method: "something" },
313
+ { file: "/foo/bar/other_vendor/lib/dont.rb", lineNumber: 2, method: "to_s" },
314
+ { file: "/foo/bar/vendor/lib/ignore_me.rb", lineNumber: 3, method: "to_s" },
315
+ { file: "/foo/bar/.bundle/lib/ignore_me.rb", lineNumber: 4, method: "to_s" },
316
+ ])
317
+ end
318
+
319
+ it "does not modify relative paths if they can't be resolved" do
320
+ configuration = Bugsnag::Configuration.new
321
+
322
+ backtrace = [
323
+ "./foo/bar/baz.rb:1:in `something'",
324
+ "../foo.rb:1:in `to_s'",
325
+ "../xyz.rb:1:in `to_s'",
326
+ "abc.rb:1:in `defg'",
327
+ ]
328
+
329
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
330
+
331
+ expect(stacktrace).to eq([
332
+ { code: nil, file: "./foo/bar/baz.rb", lineNumber: 1, method: "something" },
333
+ { code: nil, file: "../foo.rb", lineNumber: 1, method: "to_s" },
334
+ { code: nil, file: "../xyz.rb", lineNumber: 1, method: "to_s" },
335
+ { code: nil, file: "abc.rb", lineNumber: 1, method: "defg" },
336
+ ])
337
+ end
338
+
339
+ it "resolves relative paths when the files exist" do
340
+ configuration = Bugsnag::Configuration.new
341
+ configuration.send_code = false
342
+
343
+ dir = File.dirname(__FILE__)
344
+
345
+ backtrace = [
346
+ "./spec/spec_helper.rb:1:in `something'",
347
+ "./lib/bugsnag/breadcrumbs/../configuration.rb:100:in `to_s'",
348
+ "lib/bugsnag.rb:20:in `notify'",
349
+ "#{dir}/../spec/stacktrace_spec.rb:5:in `something_else'",
350
+ ]
351
+
352
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
353
+
354
+ expect(stacktrace).to eq([
355
+ { file: "#{dir}/spec_helper.rb", lineNumber: 1, method: "something" },
356
+ { file: "#{File.dirname(dir)}/lib/bugsnag/configuration.rb", lineNumber: 100, method: "to_s" },
357
+ { file: "#{File.dirname(dir)}/lib/bugsnag.rb", lineNumber: 20, method: "notify" },
358
+ { file: "#{dir}/stacktrace_spec.rb", lineNumber: 5, method: "something_else" },
359
+ ])
360
+ end
361
+
362
+ it "ignores lines in backtrace that it can't parse" do
363
+ configuration = Bugsnag::Configuration.new
364
+ configuration.send_code = false
365
+
366
+ backtrace = [
367
+ "/foo/bar/baz.rb:2:in `to_s'",
368
+ "this is not formatted correctly :O",
369
+ "/abc/xyz.rb:4:in `to_s'",
370
+ ]
371
+
372
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
373
+
374
+ expect(stacktrace).to eq([
375
+ { file: "/foo/bar/baz.rb", lineNumber: 2, method: "to_s" },
376
+ { file: "/abc/xyz.rb", lineNumber: 4, method: "to_s" },
377
+ ])
378
+ end
379
+
380
+ it "trims Gem prefix from paths" do
381
+ gem_path = Gem.path.first
382
+
383
+ # Sanity check that we have a gem path to strip
384
+ expect(gem_path).not_to be_empty
385
+
386
+ configuration = Bugsnag::Configuration.new
387
+ configuration.send_code = false
388
+
389
+ backtrace = [
390
+ "/foo/bar/baz.rb:2:in `to_s'",
391
+ "#{gem_path}/abc/xyz.rb:4:in `to_s'",
392
+ "/not/gem/path/but/has/gem.rb:6:in `to_s'"
393
+ ]
394
+
395
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
396
+
397
+ expect(stacktrace).to eq([
398
+ { file: "/foo/bar/baz.rb", lineNumber: 2, method: "to_s" },
399
+ { file: "abc/xyz.rb", lineNumber: 4, method: "to_s" },
400
+ { file: "/not/gem/path/but/has/gem.rb", lineNumber: 6, method: "to_s" },
401
+ ])
402
+ end
77
403
  end
78
404
 
79
- it 'should send the last 7 lines of the file for exceptions near the bottom' do
80
- load 'spec/fixtures/crashes/short_file.rb' rescue Bugsnag.notify $!
405
+ context "with configurable vendor_path" do
406
+ let(:configuration) do
407
+ configuration = Bugsnag::Configuration.new
408
+ configuration.project_root = "/foo/bar"
409
+ configuration
410
+ end
411
+
412
+ let(:backtrace) do
413
+ [
414
+ "/foo/bar/app/models/user.rb:1:in `something'",
415
+ "/foo/bar/other_vendor/lib/dont.rb:1:in `to_s'",
416
+ "/foo/bar/vendor/lib/ignore_me.rb:1:in `to_s'",
417
+ "/foo/bar/.bundle/lib/ignore_me.rb:1:in `to_s'",
418
+ ]
419
+ end
420
+
421
+ def out_project_trace(stacktrace)
422
+ stacktrace.map do |trace_line|
423
+ trace_line[:file] unless trace_line[:inProject]
424
+ end.compact
425
+ end
426
+
427
+ it "marks vendor/ and .bundle/ as out-project by default" do
428
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
429
+
430
+ expect(out_project_trace(stacktrace)).to eq([
431
+ "vendor/lib/ignore_me.rb",
432
+ ".bundle/lib/ignore_me.rb",
433
+ ])
434
+ end
81
435
 
82
- expect(Bugsnag).to have_sent_notification{ |payload, headers|
83
- exception = get_exception_from_payload(payload)
436
+ it "allows vendor_path to be configured and filters out backtrace file paths" do
437
+ configuration.vendor_path = /other_vendor\//
438
+ stacktrace = Bugsnag::Stacktrace.process(backtrace, configuration)
84
439
 
85
- expect(exception["stacktrace"][0]["code"]).to eq({
86
- "1" => "raise 'hell'"
87
- })
88
- }
440
+ expect(out_project_trace(stacktrace)).to eq(["other_vendor/lib/dont.rb"])
441
+ end
89
442
  end
90
443
  end