bugsnag-maglev- 2.8.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +52 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +15 -0
  6. data/CHANGELOG.md +425 -0
  7. data/CONTRIBUTING.md +43 -0
  8. data/Gemfile +2 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +804 -0
  11. data/Rakefile +29 -0
  12. data/VERSION +1 -0
  13. data/bugsnag.gemspec +32 -0
  14. data/lib/bugsnag.rb +129 -0
  15. data/lib/bugsnag/capistrano.rb +7 -0
  16. data/lib/bugsnag/capistrano2.rb +32 -0
  17. data/lib/bugsnag/configuration.rb +129 -0
  18. data/lib/bugsnag/delay/resque.rb +21 -0
  19. data/lib/bugsnag/delayed_job.rb +57 -0
  20. data/lib/bugsnag/delivery.rb +18 -0
  21. data/lib/bugsnag/delivery/synchronous.rb +51 -0
  22. data/lib/bugsnag/delivery/thread_queue.rb +53 -0
  23. data/lib/bugsnag/deploy.rb +35 -0
  24. data/lib/bugsnag/helpers.rb +127 -0
  25. data/lib/bugsnag/mailman.rb +28 -0
  26. data/lib/bugsnag/meta_data.rb +7 -0
  27. data/lib/bugsnag/middleware/callbacks.rb +19 -0
  28. data/lib/bugsnag/middleware/mailman.rb +13 -0
  29. data/lib/bugsnag/middleware/rack_request.rb +72 -0
  30. data/lib/bugsnag/middleware/rails2_request.rb +52 -0
  31. data/lib/bugsnag/middleware/rails3_request.rb +42 -0
  32. data/lib/bugsnag/middleware/rake.rb +23 -0
  33. data/lib/bugsnag/middleware/sidekiq.rb +13 -0
  34. data/lib/bugsnag/middleware/warden_user.rb +39 -0
  35. data/lib/bugsnag/middleware_stack.rb +98 -0
  36. data/lib/bugsnag/notification.rb +452 -0
  37. data/lib/bugsnag/rack.rb +53 -0
  38. data/lib/bugsnag/rails.rb +66 -0
  39. data/lib/bugsnag/rails/action_controller_rescue.rb +62 -0
  40. data/lib/bugsnag/rails/active_record_rescue.rb +20 -0
  41. data/lib/bugsnag/rails/controller_methods.rb +44 -0
  42. data/lib/bugsnag/railtie.rb +78 -0
  43. data/lib/bugsnag/rake.rb +25 -0
  44. data/lib/bugsnag/resque.rb +40 -0
  45. data/lib/bugsnag/sidekiq.rb +38 -0
  46. data/lib/bugsnag/tasks.rb +3 -0
  47. data/lib/bugsnag/tasks/bugsnag.cap +48 -0
  48. data/lib/bugsnag/tasks/bugsnag.rake +89 -0
  49. data/lib/bugsnag/version.rb +3 -0
  50. data/lib/generators/bugsnag/bugsnag_generator.rb +24 -0
  51. data/rails/init.rb +3 -0
  52. data/spec/code_spec.rb +86 -0
  53. data/spec/fixtures/crashes/end_of_file.rb +9 -0
  54. data/spec/fixtures/crashes/short_file.rb +1 -0
  55. data/spec/fixtures/crashes/start_of_file.rb +9 -0
  56. data/spec/fixtures/middleware/internal_info_setter.rb +11 -0
  57. data/spec/fixtures/middleware/public_info_setter.rb +11 -0
  58. data/spec/fixtures/tasks/Rakefile +15 -0
  59. data/spec/helper_spec.rb +144 -0
  60. data/spec/integration_spec.rb +110 -0
  61. data/spec/middleware_spec.rb +181 -0
  62. data/spec/notification_spec.rb +822 -0
  63. data/spec/rack_spec.rb +56 -0
  64. data/spec/spec_helper.rb +53 -0
  65. metadata +198 -0
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/middleware/public_info_setter'
3
+ require 'fixtures/middleware/internal_info_setter'
4
+
5
+ describe Bugsnag::MiddlewareStack do
6
+ it "runs before_bugsnag_notify callbacks, adding a tab" do
7
+ callback_run_count = 0
8
+ Bugsnag.before_notify_callbacks << lambda {|notif|
9
+ notif.add_tab(:some_tab, {
10
+ :info => "here",
11
+ :data => "also here"
12
+ })
13
+ callback_run_count += 1
14
+ }
15
+
16
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
17
+ expect(callback_run_count).to eq(1)
18
+
19
+ expect(Bugsnag).to have_sent_notification{ |payload|
20
+ event = get_event_from_payload(payload)
21
+ expect(event["metaData"]["some_tab"]).not_to be_nil
22
+ expect(event["metaData"]["some_tab"]["info"]).to eq("here")
23
+ expect(event["metaData"]["some_tab"]["data"]).to eq("also here")
24
+ }
25
+
26
+ end
27
+
28
+ it "runs before_bugsnag_notify callbacks, adding custom data" do
29
+ callback_run_count = 0
30
+ Bugsnag.before_notify_callbacks << lambda {|notif|
31
+ notif.add_custom_data(:info, "here")
32
+ notif.add_custom_data(:data, "also here")
33
+
34
+ callback_run_count += 1
35
+ }
36
+
37
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
38
+ expect(callback_run_count).to eq(1)
39
+
40
+ expect(Bugsnag).to have_sent_notification{ |payload|
41
+ event = get_event_from_payload(payload)
42
+ expect(event["metaData"]["custom"]).not_to be_nil
43
+ expect(event["metaData"]["custom"]["info"]).to eq("here")
44
+ expect(event["metaData"]["custom"]["data"]).to eq("also here")
45
+ }
46
+
47
+ end
48
+
49
+ it "runs before_bugsnag_notify callbacks, setting the user" do
50
+ callback_run_count = 0
51
+ Bugsnag.before_notify_callbacks << lambda {|notif|
52
+ notif.user = {:id => "here", :email => "also here", :name => "also here too", :random_key => "also here too too"}
53
+ callback_run_count += 1
54
+ }
55
+
56
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
57
+ expect(callback_run_count).to eq(1)
58
+
59
+ expect(Bugsnag).to have_sent_notification{ |payload|
60
+ event = get_event_from_payload(payload)
61
+ expect(event["user"]).not_to be_nil
62
+ expect(event["user"]["id"]).to eq("here")
63
+ expect(event["user"]["email"]).to eq("also here")
64
+ expect(event["user"]["name"]).to eq("also here too")
65
+ expect(event["user"]["random_key"]).to eq("also here too too")
66
+ }
67
+
68
+ end
69
+
70
+ it "allows overrides to override values set by internal middleware" do
71
+ Bugsnag.configuration.internal_middleware.use(InternalInfoSetter)
72
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:info => "overridden"})
73
+
74
+ expect(Bugsnag).to have_sent_notification{ |payload|
75
+ event = get_event_from_payload(payload)
76
+ expect(event["metaData"]["custom"]).not_to be_nil
77
+ expect(event["metaData"]["custom"]["info"]).not_to eq(InternalInfoSetter::MESSAGE)
78
+ expect(event["metaData"]["custom"]["info"]).to eq("overridden")
79
+ }
80
+ end
81
+
82
+ it "doesn't allow overrides to override public middleware" do
83
+ Bugsnag.configuration.middleware.use(PublicInfoSetter)
84
+
85
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:info => "overridden"})
86
+
87
+ expect(Bugsnag).to have_sent_notification{ |payload|
88
+ event = get_event_from_payload(payload)
89
+ expect(event["metaData"]["custom"]).not_to be_nil
90
+ expect(event["metaData"]["custom"]["info"]).not_to eq("overridden")
91
+ expect(event["metaData"]["custom"]["info"]).to eq(PublicInfoSetter::MESSAGE)
92
+ }
93
+ end
94
+
95
+ it "does not have have before or after callbacks by default" do
96
+ expect(Bugsnag.before_notify_callbacks.size).to eq(0)
97
+ expect(Bugsnag.after_notify_callbacks.size).to eq(0)
98
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
99
+ expect(Bugsnag).to have_sent_notification{ |payload|
100
+ event = get_event_from_payload(payload)
101
+ expect(event["metaData"].size).to eq(0)
102
+ }
103
+ end
104
+
105
+ it "runs after_bugsnag_notify callbacks" do
106
+ callback_run_count = 0
107
+ Bugsnag.after_notify_callbacks << lambda {|notif|
108
+ callback_run_count += 1
109
+ }
110
+
111
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
112
+
113
+ expect(callback_run_count).to eq(1)
114
+ expect(Bugsnag::Notification).to have_sent_notification { }
115
+ end
116
+
117
+ it "does not execute disabled bugsnag middleware" do
118
+ callback_run_count = 0
119
+ Bugsnag.configure do |config|
120
+ config.middleware.disable(Bugsnag::Middleware::Callbacks)
121
+ end
122
+
123
+ Bugsnag.before_notify_callbacks << lambda {|notif|
124
+ callback_run_count += 1
125
+ }
126
+
127
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
128
+ expect(callback_run_count).to eq(0)
129
+ end
130
+
131
+ it "does not notify if a callback told so" do
132
+ Bugsnag.before_notify_callbacks << lambda do |notif|
133
+ notif.ignore!
134
+ end
135
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
136
+ expect(Bugsnag::Notification).not_to have_sent_notification { }
137
+ end
138
+
139
+ it "allows inspection of meta_data before ignoring exception" do
140
+ # Use before notify callbacks as only the callback based metadata is
141
+ # available to before_notify_callbacks
142
+ Bugsnag.before_notify_callbacks << lambda do |notif|
143
+ notif.add_tab(:sidekiq, {:retry_count => 4})
144
+ end
145
+
146
+ Bugsnag.before_notify_callbacks << lambda do |notif|
147
+ notif.ignore! if notif.meta_data[:sidekiq][:retry_count] > 3
148
+ end
149
+
150
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
151
+ expect(Bugsnag::Notification).not_to have_sent_notification
152
+
153
+ end
154
+
155
+ it "allows meta_data to be modified in a middleware" do
156
+ MetaDataMunger = Class.new do
157
+ def initialize(bugsnag)
158
+ @bugsnag = bugsnag
159
+ end
160
+
161
+ def call(notification)
162
+ token = notification.meta_data[:sidekiq][:args].first
163
+ notification.meta_data[:sidekiq][:args] = ["#{token[0...6]}*****#{token[-4..-1]}"]
164
+ @bugsnag.call(notification)
165
+ end
166
+ end
167
+
168
+ Bugsnag.configure do |c|
169
+ c.middleware.use MetaDataMunger
170
+ end
171
+
172
+ notification = Bugsnag.notify(BugsnagTestException.new("It crashed"), {
173
+ :sidekiq => {
174
+ :args => ["abcdef123456abcdef123456abcdef123456"]
175
+ }
176
+ })
177
+
178
+ expect(notification.meta_data[:sidekiq][:args]).to eq(["abcdef*****3456"])
179
+ end
180
+
181
+ end
@@ -0,0 +1,822 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'securerandom'
4
+ require 'ostruct'
5
+
6
+ module ActiveRecord; class RecordNotFound < RuntimeError; end; end
7
+ class NestedException < StandardError; attr_accessor :original_exception; end
8
+ class BugsnagTestExceptionWithMetaData < Exception; include Bugsnag::MetaData; end
9
+ class BugsnagSubclassTestException < BugsnagTestException; end
10
+
11
+ class Ruby21Exception < RuntimeError
12
+ attr_accessor :cause
13
+ def self.raise!(msg)
14
+ e = new(msg)
15
+ e.cause = $!
16
+ raise e
17
+ end
18
+ end
19
+
20
+ class JRubyException
21
+ def self.raise!
22
+ new.gloops
23
+ end
24
+
25
+ def gloops
26
+ java.lang.System.out.printf(nil)
27
+ end
28
+ end
29
+
30
+ describe Bugsnag::Notification do
31
+ it "should contain an api_key if one is set" do
32
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
33
+
34
+ expect(Bugsnag).to have_sent_notification{ |payload|
35
+ expect(payload["apiKey"]).to eq("c9d60ae4c7e70c4b6c4ebd3e8056d2b8")
36
+ }
37
+ end
38
+
39
+ it "does not notify if api_key is not set" do
40
+ Bugsnag.configuration.api_key = nil
41
+
42
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
43
+
44
+ expect(Bugsnag).not_to have_sent_notification
45
+ end
46
+
47
+ it "does not notify if api_key is empty" do
48
+ Bugsnag.configuration.api_key = ""
49
+
50
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
51
+
52
+ expect(Bugsnag).not_to have_sent_notification
53
+ end
54
+
55
+ it "lets you override the api_key" do
56
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), :api_key => "9d84383f9be2ca94902e45c756a9979d")
57
+
58
+ expect(Bugsnag).to have_sent_notification{ |payload|
59
+ expect(payload["apiKey"]).to eq("9d84383f9be2ca94902e45c756a9979d")
60
+ }
61
+ end
62
+
63
+ it "lets you override the groupingHash" do
64
+
65
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:grouping_hash => "this is my grouping hash"})
66
+
67
+ expect(Bugsnag).to have_sent_notification{ |payload|
68
+ event = get_event_from_payload(payload)
69
+ expect(event["groupingHash"]).to eq("this is my grouping hash")
70
+ }
71
+ end
72
+
73
+ it "uses the env variable apiKey" do
74
+ ENV["BUGSNAG_API_KEY"] = "c9d60ae4c7e70c4b6c4ebd3e8056d2b9"
75
+
76
+ Bugsnag.instance_variable_set(:@configuration, Bugsnag::Configuration.new)
77
+ Bugsnag.configure do |config|
78
+ config.release_stage = "production"
79
+ config.delivery_method = :synchronous
80
+ end
81
+
82
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
83
+
84
+ expect(Bugsnag).to have_sent_notification{ |payload|
85
+ expect(payload["apiKey"]).to eq("c9d60ae4c7e70c4b6c4ebd3e8056d2b9")
86
+ }
87
+ end
88
+
89
+ it "has the right exception class" do
90
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
91
+
92
+ expect(Bugsnag).to have_sent_notification{ |payload|
93
+ exception = get_exception_from_payload(payload)
94
+ expect(exception["errorClass"]).to eq("BugsnagTestException")
95
+ }
96
+ end
97
+
98
+ it "has the right exception message" do
99
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
100
+
101
+ expect(Bugsnag).to have_sent_notification{ |payload|
102
+ exception = get_exception_from_payload(payload)
103
+ expect(exception["message"]).to eq("It crashed")
104
+ }
105
+ end
106
+
107
+ it "has a valid stacktrace" do
108
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
109
+
110
+ expect(Bugsnag).to have_sent_notification{ |payload|
111
+ exception = get_exception_from_payload(payload)
112
+ expect(exception["stacktrace"].length).to be > 0
113
+ }
114
+ end
115
+
116
+ # TODO: nested context
117
+
118
+ it "accepts tabs in overrides and adds them to metaData" do
119
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
120
+ :some_tab => {
121
+ :info => "here",
122
+ :data => "also here"
123
+ }
124
+ })
125
+
126
+ expect(Bugsnag).to have_sent_notification{ |payload|
127
+ event = get_event_from_payload(payload)
128
+ expect(event["metaData"]["some_tab"]).to eq(
129
+ "info" => "here",
130
+ "data" => "also here"
131
+ )
132
+ }
133
+ end
134
+
135
+ it "accepts non-hash overrides and adds them to the custom tab in metaData" do
136
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
137
+ :info => "here",
138
+ :data => "also here"
139
+ })
140
+
141
+ expect(Bugsnag).to have_sent_notification{ |payload|
142
+ event = get_event_from_payload(payload)
143
+ expect(event["metaData"]["custom"]).to eq(
144
+ "info" => "here",
145
+ "data" => "also here"
146
+ )
147
+ }
148
+ end
149
+
150
+ it "accepts meta data from an exception that mixes in Bugsnag::MetaData" do
151
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
152
+ exception.bugsnag_meta_data = {
153
+ :some_tab => {
154
+ :info => "here",
155
+ :data => "also here"
156
+ }
157
+ }
158
+
159
+ Bugsnag.notify(exception)
160
+
161
+ expect(Bugsnag).to have_sent_notification{ |payload|
162
+ event = get_event_from_payload(payload)
163
+ expect(event["metaData"]["some_tab"]).to eq(
164
+ "info" => "here",
165
+ "data" => "also here"
166
+ )
167
+ }
168
+ end
169
+
170
+ it "accepts meta data from an exception that mixes in Bugsnag::MetaData, but override using the overrides" do
171
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
172
+ exception.bugsnag_meta_data = {
173
+ :some_tab => {
174
+ :info => "here",
175
+ :data => "also here"
176
+ }
177
+ }
178
+
179
+ Bugsnag.notify(exception, {:some_tab => {:info => "overridden"}})
180
+
181
+ expect(Bugsnag).to have_sent_notification{ |payload|
182
+ event = get_event_from_payload(payload)
183
+ expect(event["metaData"]["some_tab"]).to eq(
184
+ "info" => "overridden",
185
+ "data" => "also here"
186
+ )
187
+ }
188
+ end
189
+
190
+ it "accepts user_id from an exception that mixes in Bugsnag::MetaData" do
191
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
192
+ exception.bugsnag_user_id = "exception_user_id"
193
+
194
+ Bugsnag.notify(exception)
195
+
196
+ expect(Bugsnag).to have_sent_notification{ |payload|
197
+ event = get_event_from_payload(payload)
198
+ expect(event["user"]["id"]).to eq("exception_user_id")
199
+ }
200
+ end
201
+
202
+ it "accepts user_id from an exception that mixes in Bugsnag::MetaData, but override using the overrides" do
203
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
204
+ exception.bugsnag_user_id = "exception_user_id"
205
+
206
+ Bugsnag.notify(exception, {:user_id => "override_user_id"})
207
+
208
+ expect(Bugsnag).to have_sent_notification{ |payload|
209
+ event = get_event_from_payload(payload)
210
+ expect(event["user"]["id"]).to eq("override_user_id")
211
+ }
212
+ end
213
+
214
+ it "accepts context from an exception that mixes in Bugsnag::MetaData" do
215
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
216
+ exception.bugsnag_context = "exception_context"
217
+
218
+ Bugsnag.notify(exception)
219
+
220
+ expect(Bugsnag).to have_sent_notification{ |payload|
221
+ event = get_event_from_payload(payload)
222
+ expect(event["context"]).to eq("exception_context")
223
+ }
224
+ end
225
+
226
+ it "accept contexts from an exception that mixes in Bugsnag::MetaData, but override using the overrides" do
227
+
228
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
229
+ exception.bugsnag_context = "exception_context"
230
+
231
+ Bugsnag.notify(exception, {:context => "override_context"})
232
+
233
+ expect(Bugsnag).to have_sent_notification{ |payload|
234
+ event = get_event_from_payload(payload)
235
+ expect(event["context"]).to eq("override_context")
236
+ }
237
+ end
238
+
239
+ it "accepts meta_data in overrides (for backwards compatibility) and merge it into metaData" do
240
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
241
+ :meta_data => {
242
+ :some_tab => {
243
+ :info => "here",
244
+ :data => "also here"
245
+ }
246
+ }
247
+ })
248
+
249
+ expect(Bugsnag).to have_sent_notification{ |payload|
250
+ event = get_event_from_payload(payload)
251
+ expect(event["metaData"]["some_tab"]).to eq(
252
+ "info" => "here",
253
+ "data" => "also here"
254
+ )
255
+ }
256
+ end
257
+
258
+ it "truncates large meta_data before sending" do
259
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
260
+ :meta_data => {
261
+ :some_tab => {
262
+ :giant => SecureRandom.hex(500_000/2),
263
+ :mega => SecureRandom.hex(500_000/2)
264
+ }
265
+ }
266
+ })
267
+
268
+ expect(Bugsnag).to have_sent_notification{ |payload|
269
+ # Truncated body should be no bigger than
270
+ # 2 truncated hashes (4096*2) + rest of payload (20000)
271
+ expect(::JSON.dump(payload).length).to be < 4096*2 + 20000
272
+ }
273
+ end
274
+
275
+ it "accepts a severity in overrides" do
276
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
277
+ :severity => "info"
278
+ })
279
+
280
+ expect(Bugsnag).to have_sent_notification{ |payload|
281
+ event = get_event_from_payload(payload)
282
+ expect(event["severity"]).to eq("info")
283
+ }
284
+
285
+ end
286
+
287
+ it "defaults to warning severity" do
288
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
289
+
290
+ expect(Bugsnag).to have_sent_notification{ |payload|
291
+ event = get_event_from_payload(payload)
292
+ expect(event["severity"]).to eq("warning")
293
+ }
294
+ end
295
+
296
+ it "does not accept a bad severity in overrides" do
297
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
298
+ :severity => "fatal"
299
+ })
300
+
301
+ expect(Bugsnag).to have_sent_notification{ |payload|
302
+ event = get_event_from_payload(payload)
303
+ expect(event["severity"]).to eq("warning")
304
+ }
305
+ end
306
+
307
+ it "autonotifies errors" do
308
+ Bugsnag.auto_notify(BugsnagTestException.new("It crashed"))
309
+
310
+ expect(Bugsnag).to have_sent_notification{ |payload|
311
+ event = get_event_from_payload(payload)
312
+ expect(event["severity"]).to eq("error")
313
+ }
314
+ end
315
+
316
+
317
+ it "accepts a context in overrides" do
318
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
319
+ :context => "test_context"
320
+ })
321
+
322
+ expect(Bugsnag).to have_sent_notification{ |payload|
323
+ event = get_event_from_payload(payload)
324
+ expect(event["context"]).to eq("test_context")
325
+ }
326
+ end
327
+
328
+ it "accepts a user_id in overrides" do
329
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {
330
+ :user_id => "test_user"
331
+ })
332
+
333
+ expect(Bugsnag).to have_sent_notification{ |payload|
334
+ event = get_event_from_payload(payload)
335
+ expect(event["user"]["id"]).to eq("test_user")
336
+ }
337
+ end
338
+
339
+ it "does not send a notification if auto_notify is false" do
340
+ Bugsnag.configure do |config|
341
+ config.auto_notify = false
342
+ end
343
+
344
+ Bugsnag.auto_notify(BugsnagTestException.new("It crashed"))
345
+
346
+ expect(Bugsnag).not_to have_sent_notification
347
+ end
348
+
349
+ it "contains a release_stage" do
350
+ Bugsnag.configure do |config|
351
+ config.release_stage = "production"
352
+ end
353
+
354
+ Bugsnag.auto_notify(BugsnagTestException.new("It crashed"))
355
+
356
+ expect(Bugsnag).to have_sent_notification{ |payload|
357
+ event = get_event_from_payload(payload)
358
+ expect(event["app"]["releaseStage"]).to eq("production")
359
+ }
360
+ end
361
+
362
+ it "respects the notify_release_stages setting by not sending in development" do
363
+ Bugsnag.configuration.notify_release_stages = ["production"]
364
+ Bugsnag.configuration.release_stage = "development"
365
+
366
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
367
+
368
+ expect(Bugsnag).not_to have_sent_notification
369
+ end
370
+
371
+ it "respects the notify_release_stages setting when set" do
372
+ Bugsnag.configuration.release_stage = "development"
373
+ Bugsnag.configuration.notify_release_stages = ["development"]
374
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
375
+
376
+ expect(Bugsnag).to have_sent_notification{ |payload|
377
+ event = get_event_from_payload(payload)
378
+ expect(event["exceptions"].length).to eq(1)
379
+ }
380
+ end
381
+
382
+ it "uses the https://notify.bugsnag.com endpoint by default" do
383
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
384
+
385
+ expect(WebMock).to have_requested(:post, "https://notify.bugsnag.com")
386
+ end
387
+
388
+ it "uses ssl when use_ssl is true" do
389
+ Bugsnag.configuration.use_ssl = true
390
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
391
+
392
+ expect(WebMock).to have_requested(:post, "https://notify.bugsnag.com")
393
+ end
394
+
395
+ it "does not use ssl when use_ssl is false" do
396
+ stub_request(:post, "http://notify.bugsnag.com/")
397
+ Bugsnag.configuration.use_ssl = false
398
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
399
+
400
+ expect(WebMock).to have_requested(:post, "http://notify.bugsnag.com")
401
+ end
402
+
403
+ it "uses ssl when use_ssl is unset" do
404
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
405
+
406
+ expect(WebMock).to have_requested(:post, "https://notify.bugsnag.com")
407
+ end
408
+
409
+ it "does not mark the top-most stacktrace line as inProject if out of project" do
410
+ Bugsnag.configuration.project_root = "/Random/location/here"
411
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
412
+
413
+ expect(Bugsnag).to have_sent_notification{ |payload|
414
+ exception = get_exception_from_payload(payload)
415
+ expect(exception["stacktrace"].size).to be >= 1
416
+ expect(exception["stacktrace"].first["inProject"]).to be_nil
417
+ }
418
+ end
419
+
420
+ it "marks the top-most stacktrace line as inProject if necessary" do
421
+ Bugsnag.configuration.project_root = File.expand_path File.dirname(__FILE__)
422
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
423
+
424
+ expect(Bugsnag).to have_sent_notification{ |payload|
425
+ exception = get_exception_from_payload(payload)
426
+ expect(exception["stacktrace"].size).to be >= 1
427
+ expect(exception["stacktrace"].first["inProject"]).to eq(true)
428
+ }
429
+ end
430
+
431
+ it "adds app_version to the payload if it is set" do
432
+ Bugsnag.configuration.app_version = "1.1.1"
433
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
434
+
435
+ expect(Bugsnag).to have_sent_notification{ |payload|
436
+ event = get_event_from_payload(payload)
437
+ expect(event["app"]["version"]).to eq("1.1.1")
438
+ }
439
+ end
440
+
441
+ it "filters params from all payload hashes if they are set in default params_filters" do
442
+
443
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "12345", :other_data => "123456"}}})
444
+
445
+ expect(Bugsnag).to have_sent_notification{ |payload|
446
+ event = get_event_from_payload(payload)
447
+ expect(event["metaData"]).not_to be_nil
448
+ expect(event["metaData"]["request"]).not_to be_nil
449
+ expect(event["metaData"]["request"]["params"]).not_to be_nil
450
+ expect(event["metaData"]["request"]["params"]["password"]).to eq("[FILTERED]")
451
+ expect(event["metaData"]["request"]["params"]["other_password"]).to eq("[FILTERED]")
452
+ expect(event["metaData"]["request"]["params"]["other_data"]).to eq("123456")
453
+ }
454
+ end
455
+
456
+ it "filters params from all payload hashes if they are added to params_filters" do
457
+
458
+ Bugsnag.configuration.params_filters << "other_data"
459
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
460
+
461
+ expect(Bugsnag).to have_sent_notification{ |payload|
462
+ event = get_event_from_payload(payload)
463
+ expect(event["metaData"]).not_to be_nil
464
+ expect(event["metaData"]["request"]).not_to be_nil
465
+ expect(event["metaData"]["request"]["params"]).not_to be_nil
466
+ expect(event["metaData"]["request"]["params"]["password"]).to eq("[FILTERED]")
467
+ expect(event["metaData"]["request"]["params"]["other_password"]).to eq("[FILTERED]")
468
+ expect(event["metaData"]["request"]["params"]["other_data"]).to eq("[FILTERED]")
469
+ }
470
+ end
471
+
472
+ it "filters params from all payload hashes if they are added to params_filters as regex" do
473
+
474
+ Bugsnag.configuration.params_filters << /other_data/
475
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
476
+
477
+ expect(Bugsnag).to have_sent_notification{ |payload|
478
+ event = get_event_from_payload(payload)
479
+ expect(event["metaData"]).not_to be_nil
480
+ expect(event["metaData"]["request"]).not_to be_nil
481
+ expect(event["metaData"]["request"]["params"]).not_to be_nil
482
+ expect(event["metaData"]["request"]["params"]["password"]).to eq("[FILTERED]")
483
+ expect(event["metaData"]["request"]["params"]["other_password"]).to eq("[FILTERED]")
484
+ expect(event["metaData"]["request"]["params"]["other_data"]).to eq("[FILTERED]")
485
+ }
486
+ end
487
+
488
+ it "filters params from all payload hashes if they are added to params_filters as partial regex" do
489
+
490
+ Bugsnag.configuration.params_filters << /r_data/
491
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
492
+
493
+ expect(Bugsnag).to have_sent_notification{ |payload|
494
+ event = get_event_from_payload(payload)
495
+ expect(event["metaData"]).not_to be_nil
496
+ expect(event["metaData"]["request"]).not_to be_nil
497
+ expect(event["metaData"]["request"]["params"]).not_to be_nil
498
+ expect(event["metaData"]["request"]["params"]["password"]).to eq("[FILTERED]")
499
+ expect(event["metaData"]["request"]["params"]["other_password"]).to eq("[FILTERED]")
500
+ expect(event["metaData"]["request"]["params"]["other_data"]).to eq("[FILTERED]")
501
+ }
502
+ end
503
+
504
+ it "does not filter params from payload hashes if their values are nil" do
505
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:nil_param => nil}}})
506
+
507
+ expect(Bugsnag).to have_sent_notification{ |payload|
508
+ event = get_event_from_payload(payload)
509
+ expect(event["metaData"]).not_to be_nil
510
+ expect(event["metaData"]["request"]).not_to be_nil
511
+ expect(event["metaData"]["request"]["params"]).not_to be_nil
512
+ expect(event["metaData"]["request"]["params"]).to have_key("nil_param")
513
+ }
514
+ end
515
+
516
+ it "does not notify if the exception class is in the default ignore_classes list" do
517
+ Bugsnag.notify_or_ignore(ActiveRecord::RecordNotFound.new("It crashed"))
518
+
519
+ expect(Bugsnag).not_to have_sent_notification
520
+ end
521
+
522
+ it "does not notify if the non-default exception class is added to the ignore_classes" do
523
+ Bugsnag.configuration.ignore_classes << "BugsnagTestException"
524
+
525
+ Bugsnag.notify_or_ignore(BugsnagTestException.new("It crashed"))
526
+
527
+ expect(Bugsnag).not_to have_sent_notification
528
+ end
529
+
530
+ it "does not notify if exception's ancestor is an ignored class" do
531
+ Bugsnag.configuration.ignore_classes << "BugsnagTestException"
532
+
533
+ Bugsnag.notify_or_ignore(BugsnagSubclassTestException.new("It crashed"))
534
+
535
+ expect(Bugsnag).not_to have_sent_notification
536
+ end
537
+
538
+ it "does not notify if any caused exception is an ignored class" do
539
+ Bugsnag.configuration.ignore_classes << "NestedException"
540
+
541
+ ex = NestedException.new("Self-referential exception")
542
+ ex.original_exception = BugsnagTestException.new("It crashed")
543
+
544
+ Bugsnag.notify_or_ignore(ex)
545
+
546
+ expect(Bugsnag).not_to have_sent_notification
547
+ end
548
+
549
+ it "accepts both String and Class instances as an ignored class" do
550
+ Bugsnag.configuration.ignore_classes << BugsnagTestException
551
+
552
+ Bugsnag.notify_or_ignore(BugsnagTestException.new("It crashed"))
553
+
554
+ expect(Bugsnag).not_to have_sent_notification
555
+ end
556
+
557
+ it "does not notify if the user agent is present and matches a regex in ignore_user_agents" do
558
+ Bugsnag.configuration.ignore_user_agents << %r{BugsnagUserAgent}
559
+
560
+ ((Thread.current["bugsnag_req_data"] ||= {})[:rack_env] ||= {})["HTTP_USER_AGENT"] = "BugsnagUserAgent"
561
+
562
+ Bugsnag.notify_or_ignore(BugsnagTestException.new("It crashed"))
563
+
564
+ expect(Bugsnag::Notification).not_to have_sent_notification
565
+ end
566
+
567
+ it "sends the cause of the exception" do
568
+ begin
569
+ begin
570
+ raise "jiminey"
571
+ rescue
572
+ Ruby21Exception.raise! "cricket"
573
+ end
574
+ rescue
575
+ Bugsnag.notify $!
576
+ end
577
+
578
+ expect(Bugsnag).to have_sent_notification{ |payload|
579
+ event = get_event_from_payload(payload)
580
+ expect(event["exceptions"].size).to eq(2)
581
+ }
582
+ end
583
+
584
+ it "does not unwrap the same exception twice" do
585
+ ex = NestedException.new("Self-referential exception")
586
+ ex.original_exception = ex
587
+
588
+ Bugsnag.notify_or_ignore(ex)
589
+
590
+ expect(Bugsnag).to have_sent_notification{ |payload|
591
+ event = get_event_from_payload(payload)
592
+ expect(event["exceptions"].size).to eq(1)
593
+ }
594
+ end
595
+
596
+ it "does not unwrap more than 5 exceptions" do
597
+
598
+ first_ex = ex = NestedException.new("Deep exception")
599
+ 10.times do |idx|
600
+ ex = ex.original_exception = NestedException.new("Deep exception #{idx}")
601
+ end
602
+
603
+ Bugsnag.notify_or_ignore(first_ex)
604
+ expect(Bugsnag).to have_sent_notification{ |payload|
605
+ event = get_event_from_payload(payload)
606
+ expect(event["exceptions"].size).to eq(5)
607
+ }
608
+ end
609
+
610
+ it "calls to_exception on i18n error objects" do
611
+ Bugsnag.notify(OpenStruct.new(:to_exception => BugsnagTestException.new("message")))
612
+
613
+ expect(Bugsnag).to have_sent_notification{ |payload|
614
+ exception = get_exception_from_payload(payload)
615
+ expect(exception["errorClass"]).to eq("BugsnagTestException")
616
+ expect(exception["message"]).to eq("message")
617
+ }
618
+ end
619
+
620
+ it "generates runtimeerror for non exceptions" do
621
+ notify_test_exception
622
+
623
+ expect(Bugsnag).to have_sent_notification{ |payload|
624
+ exception = get_exception_from_payload(payload)
625
+ expect(exception["errorClass"]).to eq("RuntimeError")
626
+ expect(exception["message"]).to eq("test message")
627
+ }
628
+ end
629
+
630
+ it "supports unix-style paths in backtraces" do
631
+ ex = BugsnagTestException.new("It crashed")
632
+ ex.set_backtrace([
633
+ "/Users/james/app/spec/notification_spec.rb:419",
634
+ "/Some/path/rspec/example.rb:113:in `instance_eval'"
635
+ ])
636
+
637
+ Bugsnag.notify(ex)
638
+
639
+ expect(Bugsnag).to have_sent_notification{ |payload|
640
+ exception = get_exception_from_payload(payload)
641
+ expect(exception["stacktrace"].length).to eq(2)
642
+
643
+ line = exception["stacktrace"][0]
644
+ expect(line["file"]).to eq("/Users/james/app/spec/notification_spec.rb")
645
+ expect(line["lineNumber"]).to eq(419)
646
+ expect(line["method"]).to be nil
647
+
648
+ line = exception["stacktrace"][1]
649
+ expect(line["file"]).to eq("/Some/path/rspec/example.rb")
650
+ expect(line["lineNumber"]).to eq(113)
651
+ expect(line["method"]).to eq("instance_eval")
652
+ }
653
+ end
654
+
655
+ it "supports windows-style paths in backtraces" do
656
+ ex = BugsnagTestException.new("It crashed")
657
+ ex.set_backtrace([
658
+ "C:/projects/test/app/controllers/users_controller.rb:13:in `index'",
659
+ "C:/ruby/1.9.1/gems/actionpack-2.3.10/filters.rb:638:in `block in run_before_filters'"
660
+ ])
661
+
662
+ Bugsnag.notify(ex)
663
+
664
+ expect(Bugsnag).to have_sent_notification{ |payload|
665
+ exception = get_exception_from_payload(payload)
666
+ expect(exception["stacktrace"].length).to eq(2)
667
+
668
+ line = exception["stacktrace"][0]
669
+ expect(line["file"]).to eq("C:/projects/test/app/controllers/users_controller.rb")
670
+ expect(line["lineNumber"]).to eq(13)
671
+ expect(line["method"]).to eq("index")
672
+
673
+ line = exception["stacktrace"][1]
674
+ expect(line["file"]).to eq("C:/ruby/1.9.1/gems/actionpack-2.3.10/filters.rb")
675
+ expect(line["lineNumber"]).to eq(638)
676
+ expect(line["method"]).to eq("block in run_before_filters")
677
+ }
678
+ end
679
+
680
+ it "should fix invalid utf8" do
681
+ invalid_data = "fl\xc3ff"
682
+ invalid_data.force_encoding('BINARY') if invalid_data.respond_to?(:force_encoding)
683
+
684
+ notify_test_exception(:fluff => {:fluff => invalid_data})
685
+
686
+ expect(Bugsnag).to have_sent_notification{ |payload|
687
+ if defined?(Encoding::UTF_8)
688
+ expect(payload.to_json).to match(/fl�ff/)
689
+ else
690
+ expect(payload.to_json).to match(/flff/)
691
+ end
692
+ }
693
+ end
694
+
695
+ it "should handle utf8 encoding errors in exceptions_list" do
696
+ invalid_data = "\"foo\xEBbar\""
697
+ invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
698
+
699
+ begin
700
+ JSON.parse(invalid_data)
701
+ rescue
702
+ Bugsnag.notify $!
703
+ end
704
+
705
+ expect(Bugsnag).to have_sent_notification { |payload|
706
+ if defined?(Encoding::UTF_8)
707
+ expect(payload.to_json).to match(/foo�bar/)
708
+ else
709
+ expect(payload.to_json).to match(/foobar/)
710
+ end
711
+ }
712
+ end
713
+
714
+ it "should handle utf8 encoding errors in notification context" do
715
+ invalid_data = "\"foo\xEBbar\""
716
+ invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
717
+
718
+ begin
719
+ raise
720
+ rescue
721
+ Bugsnag.notify($!, { :context => invalid_data })
722
+ end
723
+
724
+ expect(Bugsnag).to have_sent_notification { |payload|
725
+ if defined?(Encoding::UTF_8)
726
+ expect(payload.to_json).to match(/foo�bar/)
727
+ else
728
+ expect(payload.to_json).to match(/foobar/)
729
+ end
730
+ }
731
+ end
732
+
733
+ it "should handle utf8 encoding errors in notification app fields" do
734
+ invalid_data = "\"foo\xEBbar\""
735
+ invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
736
+
737
+ Bugsnag.configuration.app_version = invalid_data
738
+ Bugsnag.configuration.release_stage = invalid_data
739
+ Bugsnag.configuration.app_type = invalid_data
740
+
741
+ begin
742
+ raise
743
+ rescue
744
+ Bugsnag.notify $!
745
+ end
746
+
747
+ expect(Bugsnag).to have_sent_notification { |payload|
748
+ if defined?(Encoding::UTF_8)
749
+ expect(payload.to_json).to match(/foo�bar/)
750
+ else
751
+ expect(payload.to_json).to match(/foobar/)
752
+ end
753
+ }
754
+ end
755
+
756
+ it "should handle utf8 encoding errors in grouping_hash" do
757
+ invalid_data = "\"foo\xEBbar\""
758
+ invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
759
+
760
+ Bugsnag.before_notify_callbacks << lambda do |notif|
761
+ notif.grouping_hash = invalid_data
762
+ end
763
+
764
+ begin
765
+ raise
766
+ rescue
767
+ Bugsnag.notify $!
768
+ end
769
+
770
+ expect(Bugsnag).to have_sent_notification { |payload|
771
+ if defined?(Encoding::UTF_8)
772
+ expect(payload.to_json).to match(/foo�bar/)
773
+ else
774
+ expect(payload.to_json).to match(/foobar/)
775
+ end
776
+ }
777
+ end
778
+
779
+ it "should handle utf8 encoding errors in notification user fields" do
780
+ invalid_data = "\"foo\xEBbar\""
781
+ invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
782
+
783
+ Bugsnag.before_notify_callbacks << lambda do |notif|
784
+ notif.user = {
785
+ :email => "#{invalid_data}@foo.com",
786
+ :name => invalid_data
787
+ }
788
+ end
789
+
790
+ begin
791
+ raise
792
+ rescue
793
+ Bugsnag.notify $!
794
+ end
795
+
796
+ expect(Bugsnag).to have_sent_notification { |payload|
797
+ if defined?(Encoding::UTF_8)
798
+ expect(payload.to_json).to match(/foo�bar/)
799
+ else
800
+ expect(payload.to_json).to match(/foobar/)
801
+ end
802
+ }
803
+ end
804
+
805
+ if defined?(JRUBY_VERSION)
806
+
807
+ it "should work with java.lang.Throwables" do
808
+ begin
809
+ JRubyException.raise!
810
+ rescue
811
+ Bugsnag.notify $!
812
+ end
813
+
814
+ expect(Bugsnag).to have_sent_notification{ |payload|
815
+ exception = get_exception_from_payload(payload)
816
+ expect(exception["errorClass"]).to eq('Java::JavaLang::ArrayIndexOutOfBoundsException')
817
+ expect(exception["message"]).to eq("2")
818
+ expect(exception["stacktrace"].size).to be > 0
819
+ }
820
+ end
821
+ end
822
+ end