exception_handling 2.2.1 → 2.3.0.pre.1

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 (34) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +5 -0
  4. data/Gemfile +7 -6
  5. data/Gemfile.lock +26 -23
  6. data/README.md +0 -1
  7. data/Rakefile +4 -4
  8. data/config/exception_filters.yml +2 -3
  9. data/exception_handling.gemspec +12 -10
  10. data/lib/exception_handling/exception_catalog.rb +5 -4
  11. data/lib/exception_handling/exception_description.rb +28 -28
  12. data/lib/exception_handling/exception_info.rb +80 -72
  13. data/lib/exception_handling/honeybadger_callbacks.rb +41 -24
  14. data/lib/exception_handling/log_stub_error.rb +10 -8
  15. data/lib/exception_handling/mailer.rb +27 -48
  16. data/lib/exception_handling/methods.rb +15 -11
  17. data/lib/exception_handling/sensu.rb +7 -5
  18. data/lib/exception_handling/testing.rb +21 -19
  19. data/lib/exception_handling/version.rb +1 -1
  20. data/lib/exception_handling.rb +105 -200
  21. data/test/helpers/controller_helpers.rb +3 -1
  22. data/test/helpers/exception_helpers.rb +9 -0
  23. data/test/test_helper.rb +26 -21
  24. data/test/unit/exception_handling/exception_catalog_test.rb +15 -14
  25. data/test/unit/exception_handling/exception_description_test.rb +22 -31
  26. data/test/unit/exception_handling/exception_info_test.rb +76 -37
  27. data/test/unit/exception_handling/honeybadger_callbacks_test.rb +46 -9
  28. data/test/unit/exception_handling/log_error_stub_test.rb +6 -4
  29. data/test/unit/exception_handling/mailer_test.rb +8 -14
  30. data/test/unit/exception_handling/methods_test.rb +9 -6
  31. data/test/unit/exception_handling/sensu_test.rb +6 -4
  32. data/test/unit/exception_handling_test.rb +279 -364
  33. metadata +24 -24
  34. data/views/exception_handling/mailer/exception_notification.html.erb +0 -92
@@ -1,4 +1,6 @@
1
- require File.expand_path('../../test_helper', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../test_helper', __dir__)
2
4
  require_test_helper 'controller_helpers'
3
5
  require_test_helper 'exception_helpers'
4
6
 
@@ -18,26 +20,26 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
18
20
  # don't let these out!
19
21
  end
20
22
 
21
- def custom_data_callback_returns_nil_message_exception(data)
23
+ def custom_data_callback_returns_nil_message_exception(_data)
22
24
  raise_exception_with_nil_message
23
25
  end
24
26
 
25
- def log_error_callback(data, ex, treat_like_warning, honeybadger_status)
27
+ def log_error_callback(_data, _ex, _treat_like_warning, _honeybadger_status)
26
28
  @fail_count += 1
27
29
  end
28
30
 
29
- def log_error_callback_config(data, ex, treat_like_warning, honeybadger_status)
31
+ def log_error_callback_config(data, _ex, treat_like_warning, honeybadger_status)
30
32
  @callback_data = data
31
33
  @treat_like_warning = treat_like_warning
32
34
  @fail_count += 1
33
35
  @honeybadger_status = honeybadger_status
34
36
  end
35
37
 
36
- def log_error_callback_with_failure(data, ex)
38
+ def log_error_callback_with_failure(_data, _ex)
37
39
  raise "this should be rescued"
38
40
  end
39
41
 
40
- def log_error_callback_returns_nil_message_exception(data, ex)
42
+ def log_error_callback_returns_nil_message_exception(_data, _ex)
41
43
  raise_exception_with_nil_message
42
44
  end
43
45
 
@@ -56,7 +58,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
56
58
  attr_accessor :callback_block
57
59
  attr_accessor :errback_block
58
60
 
59
- def resolve(hostname)
61
+ def resolve(_hostname)
60
62
  self
61
63
  end
62
64
 
@@ -101,31 +103,18 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
101
103
  class SmtpClientErrbackStub < SmtpClientStub
102
104
  end
103
105
 
104
- context "with honeybadger notify stubbed" do
106
+ context "with warn and honeybadger notify stubbed" do
105
107
  setup do
108
+ stub(ExceptionHandling).warn(anything)
106
109
  stub(Honeybadger).notify(anything)
107
110
  end
108
111
 
109
112
  context "#log_error" do
110
- setup do
111
- ExceptionHandling.mailer_send_enabled = true
112
- end
113
-
114
- should "take in additional key word args as logging context and pass them to the logger" do
113
+ should "take in additional keyword args as logging context and pass them to the logger" do
115
114
  ExceptionHandling.log_error('This is an Error', 'This is the prefix context', service_name: 'exception_handling')
116
115
  assert_match(/This is an Error/, logged_excluding_reload_filter.last[:message])
117
116
  assert_not_empty logged_excluding_reload_filter.last[:context]
118
- assert_equal logged_excluding_reload_filter.last[:context], { service_name: 'exception_handling' }
119
- end
120
-
121
- should "log the info and not raise another exception when sending email fails" do
122
- 9.times { ExceptionHandling.log_error('SomeError', 'Error Context') }
123
- mock(ExceptionHandling::Mailer).exception_notification(anything, anything, anything) { raise 'An Error' }
124
- mock(ExceptionHandling.logger) do |logger|
125
- logger.info(/ExceptionHandling.log_error_email rescued exception while logging StandardError: SomeError/, anything)
126
- end
127
- stub($stderr).puts
128
- ExceptionHandling.log_error('SomeError', 'Error Context')
117
+ assert_equal logged_excluding_reload_filter.last[:context], service_name: 'exception_handling'
129
118
  end
130
119
  end
131
120
 
@@ -141,7 +130,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
141
130
  ExceptionHandling.log_warning('This is a Warning', service_name: 'exception_handling')
142
131
  assert_match(/This is a Warning/, logged_excluding_reload_filter.last[:message])
143
132
  assert_not_empty logged_excluding_reload_filter.last[:context]
144
- assert_equal logged_excluding_reload_filter.last[:context], { service_name: 'exception_handling' }
133
+ assert_equal logged_excluding_reload_filter.last[:context], service_name: 'exception_handling'
145
134
  end
146
135
  end
147
136
 
@@ -150,7 +139,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
150
139
  ExceptionHandling.log_warning('This is an Info', service_name: 'exception_handling')
151
140
  assert_match(/This is an Info/, logged_excluding_reload_filter.last[:message])
152
141
  assert_not_empty logged_excluding_reload_filter.last[:context]
153
- assert_equal logged_excluding_reload_filter.last[:context], { service_name: 'exception_handling' }
142
+ assert_equal logged_excluding_reload_filter.last[:context], service_name: 'exception_handling'
154
143
  end
155
144
  end
156
145
 
@@ -159,48 +148,60 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
159
148
  ExceptionHandling.log_warning('This is a Debug', service_name: 'exception_handling')
160
149
  assert_match(/This is a Debug/, logged_excluding_reload_filter.last[:message])
161
150
  assert_not_empty logged_excluding_reload_filter.last[:context]
162
- assert_equal logged_excluding_reload_filter.last[:context], { service_name: 'exception_handling' }
151
+ assert_equal logged_excluding_reload_filter.last[:context], service_name: 'exception_handling'
163
152
  end
164
153
  end
165
154
 
166
- context "configuration" do
155
+ context "configuration with custom_data_hook or post_log_error_hook" do
156
+ teardown do
157
+ ExceptionHandling.custom_data_hook = nil
158
+ ExceptionHandling.post_log_error_hook = nil
159
+ end
160
+
167
161
  should "support a custom_data_hook" do
162
+ capture_notifications
163
+
168
164
  ExceptionHandling.custom_data_hook = method(:append_organization_info_config)
169
- ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
170
- assert_match(/Invoca Engineering Dept./, ActionMailer::Base.deliveries[-1].body.to_s)
171
- ExceptionHandling.custom_data_hook = nil
165
+ ExceptionHandling.ensure_safe("context") { raise "Some Exception" }
166
+
167
+ assert_match(/Invoca Engineering Dept./, sent_notifications.last.enhanced_data['user_details'].to_s)
172
168
  end
173
169
 
174
- should "support a log_error hook, and pass exception data, treat like warning, and logged_to_honeybadger to it" do
175
- begin
176
- @fail_count = 0
177
- @honeybadger_status = nil
178
- ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
179
- mock(Honeybadger).notify.with_any_args { '06220c5a-b471-41e5-baeb-de247da45a56' }
180
- ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
181
- assert_equal 1, @fail_count
182
- assert_equal false, @treat_like_warning
183
- assert_equal :success, @honeybadger_status
184
- ensure
185
- ExceptionHandling.post_log_error_hook = nil
186
- end
170
+ should "support a log_error hook, and pass exception_data, treat_like_warning, and logged_to_honeybadger to it" do
171
+ @fail_count = 0
172
+ @honeybadger_status = nil
173
+ ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
174
+
175
+ notify_args = []
176
+ mock(Honeybadger).notify.with_any_args { |info| notify_args << info; '06220c5a-b471-41e5-baeb-de247da45a56' }
177
+ ExceptionHandling.ensure_safe("context") { raise "Some Exception" }
178
+ assert_equal 1, @fail_count
179
+ assert_equal false, @treat_like_warning
180
+ assert_equal :success, @honeybadger_status
187
181
 
188
182
  assert_equal "this is used by a test", @callback_data["notes"]
189
- assert_match(/this is used by a test/, ActionMailer::Base.deliveries[-1].body.to_s)
183
+ assert_equal 1, notify_args.size, notify_args.inspect
184
+ assert_match(/this is used by a test/, notify_args.last[:context].to_s)
190
185
  end
191
186
 
192
- should "plumb treat like warning and logged_to_honeybadger to log error hook" do
193
- begin
194
- @fail_count = 0
195
- @honeybadger_status = nil
196
- ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
197
- ExceptionHandling.log_error(StandardError.new("Some BS"), "mooo", treat_like_warning: true)
198
- assert_equal 1, @fail_count
199
- assert_equal true, @treat_like_warning
200
- assert_equal :skipped, @honeybadger_status
201
- ensure
202
- ExceptionHandling.post_log_error_hook = nil
203
- end
187
+ should "plumb treat_like_warning and logged_to_honeybadger to log error hook" do
188
+ @fail_count = 0
189
+ @honeybadger_status = nil
190
+ ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
191
+ ExceptionHandling.log_error(StandardError.new("Some Exception"), "mooo", treat_like_warning: true)
192
+ assert_equal 1, @fail_count
193
+ assert_equal true, @treat_like_warning
194
+ assert_equal :skipped, @honeybadger_status
195
+ end
196
+
197
+ should "include logging context in the exception data" do
198
+ ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
199
+ ExceptionHandling.log_error(StandardError.new("Some Exception"), "mooo", nil, treat_like_warning: true, log_context_test: "contextual_logging")
200
+
201
+ expected_log_context = {
202
+ "log_context_test" => "contextual_logging"
203
+ }
204
+ assert_equal expected_log_context, @callback_data[:log_context]
204
205
  end
205
206
 
206
207
  should "support rescue exceptions from a log_error hook" do
@@ -209,9 +210,8 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
209
210
  stub(ExceptionHandling.logger).info.with_any_args do |message, _|
210
211
  log_info_messages << message
211
212
  end
212
- assert_nothing_raised { ExceptionHandling.ensure_safe("mooo") { raise "Some BS" } }
213
+ assert_nothing_raised { ExceptionHandling.ensure_safe("mooo") { raise "Some Exception" } }
213
214
  assert log_info_messages.find { |message| message =~ /Unable to execute custom log_error callback/ }
214
- ExceptionHandling.post_log_error_hook = nil
215
215
  end
216
216
 
217
217
  should "handle nil message exceptions resulting from the log_error hook" do
@@ -220,9 +220,8 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
220
220
  stub(ExceptionHandling.logger).info.with_any_args do |message, _|
221
221
  log_info_messages << message
222
222
  end
223
- assert_nothing_raised { ExceptionHandling.ensure_safe("mooo") { raise "Some BS" } }
223
+ assert_nothing_raised { ExceptionHandling.ensure_safe("mooo") { raise "Some Exception" } }
224
224
  assert log_info_messages.find { |message| message =~ /Unable to execute custom log_error callback/ }
225
- ExceptionHandling.post_log_error_hook = nil
226
225
  end
227
226
 
228
227
  should "handle nil message exceptions resulting from the custom data hook" do
@@ -231,20 +230,13 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
231
230
  stub(ExceptionHandling.logger).info.with_any_args do |message, _|
232
231
  log_info_messages << message
233
232
  end
234
- assert_nothing_raised { ExceptionHandling.ensure_safe("mooo") { raise "Some BS" } }
233
+ assert_nothing_raised { ExceptionHandling.ensure_safe("mooo") { raise "Some Exception" } }
235
234
  assert log_info_messages.find { |message| message =~ /Unable to execute custom custom_data_hook callback/ }
236
- ExceptionHandling.custom_data_hook = nil
237
235
  end
238
236
  end
239
237
 
240
238
  context "Exception Handling" do
241
- setup do
242
- ActionMailer::Base.deliveries.clear
243
- ExceptionHandling.send(:clear_exception_summary)
244
- end
245
-
246
239
  context "default_metric_name" do
247
-
248
240
  context "when metric_name is present in exception_data" do
249
241
  should "include metric_name in resulting metric name" do
250
242
  exception = StandardError.new('this is an exception')
@@ -311,7 +303,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
311
303
  context "ExceptionHandling.ensure_safe" do
312
304
  should "log an exception with call stack if an exception is raised." do
313
305
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
314
- ExceptionHandling.ensure_safe { raise ArgumentError.new("blah") }
306
+ ExceptionHandling.ensure_safe { raise ArgumentError, "blah" }
315
307
  end
316
308
 
317
309
  should "log an exception with call stack if an ActionView template exception is raised." do
@@ -332,13 +324,13 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
332
324
 
333
325
  should "return nil if an exception is raised during an assignment" do
334
326
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
335
- b = ExceptionHandling.ensure_safe { raise ArgumentError.new("blah") }
327
+ b = ExceptionHandling.ensure_safe { raise ArgumentError, "blah" }
336
328
  assert_nil b
337
329
  end
338
330
 
339
331
  should "allow a message to be appended to the error when logged." do
340
332
  mock(ExceptionHandling.logger).fatal(/mooo \(blah\):\n.*exception_handling_test\.rb/, anything)
341
- b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError.new("blah") }
333
+ b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError, "blah" }
342
334
  assert_nil b
343
335
  end
344
336
 
@@ -347,7 +339,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
347
339
 
348
340
  mock(ExceptionHandling.logger).fatal(/mooo \(blah\):\n.*exception_handling_test\.rb/, anything)
349
341
 
350
- b = ExceptionHandling.ensure_safe("mooo") { raise StandardError.new("blah") }
342
+ b = ExceptionHandling.ensure_safe("mooo") { raise StandardError, "blah" }
351
343
  assert_nil b
352
344
  end
353
345
  end
@@ -355,7 +347,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
355
347
  context "ExceptionHandling.ensure_completely_safe" do
356
348
  should "log an exception if an exception is raised." do
357
349
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
358
- ExceptionHandling.ensure_completely_safe { raise ArgumentError.new("blah") }
350
+ ExceptionHandling.ensure_completely_safe { raise ArgumentError, "blah" }
359
351
  end
360
352
 
361
353
  should "should not log an exception if an exception is not raised." do
@@ -371,26 +363,26 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
371
363
 
372
364
  should "return nil if an exception is raised during an assignment" do
373
365
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything) { nil }
374
- b = ExceptionHandling.ensure_completely_safe { raise ArgumentError.new("blah") }
366
+ b = ExceptionHandling.ensure_completely_safe { raise ArgumentError, "blah" }
375
367
  assert_nil b
376
368
  end
377
369
 
378
370
  should "allow a message to be appended to the error when logged." do
379
371
  mock(ExceptionHandling.logger).fatal(/mooo \(blah\):\n.*exception_handling_test\.rb/, anything)
380
- b = ExceptionHandling.ensure_completely_safe("mooo") { raise ArgumentError.new("blah") }
372
+ b = ExceptionHandling.ensure_completely_safe("mooo") { raise ArgumentError, "blah" }
381
373
  assert_nil b
382
374
  end
383
375
 
384
376
  should "rescue any instance or child of Exception" do
385
377
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
386
- ExceptionHandling::ensure_completely_safe { raise Exception.new("blah") }
378
+ ExceptionHandling.ensure_completely_safe { raise Exception, "blah" }
387
379
  end
388
380
 
389
381
  should "not rescue the special exceptions that Ruby uses" do
390
382
  [SystemExit, SystemStackError, NoMemoryError, SecurityError].each do |exception|
391
383
  assert_raise exception do
392
384
  ExceptionHandling.ensure_completely_safe do
393
- raise exception.new
385
+ raise exception
394
386
  end
395
387
  end
396
388
  end
@@ -398,11 +390,17 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
398
390
  end
399
391
 
400
392
  context "ExceptionHandling.ensure_escalation" do
393
+ setup do
394
+ capture_notifications
395
+ ActionMailer::Base.deliveries.clear
396
+ end
397
+
401
398
  should "log the exception as usual and send the proper email" do
402
- assert_equal 0, ActionMailer::Base.deliveries.count
403
399
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
404
- ExceptionHandling.ensure_escalation( "Favorite Feature") { raise ArgumentError.new("blah") }
405
- assert_equal 2, ActionMailer::Base.deliveries.count
400
+ ExceptionHandling.ensure_escalation("Favorite Feature") { raise ArgumentError, "blah" }
401
+ assert_equal 1, ActionMailer::Base.deliveries.count
402
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
403
+
406
404
  email = ActionMailer::Base.deliveries.last
407
405
  assert_equal "#{ExceptionHandling.email_environment} Escalation: Favorite Feature", email.subject
408
406
  assert_match 'ArgumentError: blah', email.body.to_s
@@ -410,23 +408,42 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
410
408
  end
411
409
 
412
410
  should "should not escalate if an exception is not raised." do
413
- assert_equal 0, ActionMailer::Base.deliveries.count
414
411
  dont_allow(ExceptionHandling.logger).fatal
415
412
  ExceptionHandling.ensure_escalation('Ignored') { ; }
416
413
  assert_equal 0, ActionMailer::Base.deliveries.count
417
414
  end
418
415
 
419
- should "log if the escalation email can not be sent" do
416
+ should "log if the escalation email cannot be sent" do
420
417
  any_instance_of(Mail::Message) do |message|
421
- mock(message).deliver
422
418
  mock(message).deliver { raise RuntimeError.new, "Delivery Error" }
423
419
  end
424
- mock(ExceptionHandling.logger) do |logger|
425
- logger.fatal(/first_test_exception/, anything)
426
- logger.fatal(/safe_email_deliver .*Delivery Error/, anything)
420
+ log_fatals = []
421
+ stub(ExceptionHandling.logger) do |logger|
422
+ logger.fatal.with_any_args { |*args| log_fatals << args }
427
423
  end
428
- ExceptionHandling.ensure_escalation("Not Used") { raise ArgumentError.new("first_test_exception") }
429
- assert_equal 0, ActionMailer::Base.deliveries.count
424
+
425
+ ExceptionHandling.ensure_escalation("ensure context") { raise ArgumentError, "first_test_exception" }
426
+
427
+ assert_match /ArgumentError.*first_test_exception/, log_fatals[0].first
428
+ assert_match /safe_email_deliver.*Delivery Error/, log_fatals[1].first
429
+
430
+ assert_equal 2, log_fatals.size, log_fatals.inspect
431
+
432
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect # still sent to honeybadger
433
+ end
434
+
435
+ should "allow the caller to specify custom recipients" do
436
+ custom_recipients = ['something@invoca.com']
437
+ mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
438
+ ExceptionHandling.ensure_escalation("Favorite Feature", custom_recipients) { raise ArgumentError, "blah" }
439
+ assert_equal 1, ActionMailer::Base.deliveries.count
440
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
441
+
442
+ email = ActionMailer::Base.deliveries.last
443
+ assert_equal "#{ExceptionHandling.email_environment} Escalation: Favorite Feature", email.subject
444
+ assert_match 'ArgumentError: blah', email.body.to_s
445
+ assert_match ExceptionHandling.last_exception_timestamp.to_s, email.body.to_s
446
+ assert_equal custom_recipients, email.to
430
447
  end
431
448
  end
432
449
 
@@ -434,7 +451,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
434
451
  should "log the exception as usual and fire a sensu event" do
435
452
  mock(ExceptionHandling::Sensu).generate_event("Favorite Feature", "test context\nblah")
436
453
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/, anything)
437
- ExceptionHandling.ensure_alert('Favorite Feature', 'test context') { raise ArgumentError.new("blah") }
454
+ ExceptionHandling.ensure_alert('Favorite Feature', 'test context') { raise ArgumentError, "blah" }
438
455
  end
439
456
 
440
457
  should "should not send sensu event if an exception is not raised." do
@@ -449,7 +466,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
449
466
  logger.fatal(/first_test_exception/, anything)
450
467
  logger.fatal(/Failed to send/, anything)
451
468
  end
452
- ExceptionHandling.ensure_alert("Not Used", 'test context') { raise ArgumentError.new("first_test_exception") }
469
+ ExceptionHandling.ensure_alert("Not Used", 'test context') { raise ArgumentError, "first_test_exception" }
453
470
  end
454
471
 
455
472
  should "log if the exception message is nil" do
@@ -465,141 +482,53 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
465
482
  recipients = ["prodsupport@example.com"]
466
483
 
467
484
  mock(ExceptionHandling).production_support_recipients { recipients }.times(2)
468
- mock(ExceptionHandling).escalate_custom(subject, exception, ExceptionHandling.last_exception_timestamp, recipients)
485
+ mock(ExceptionHandling).escalate(subject, exception, ExceptionHandling.last_exception_timestamp, recipients)
469
486
  ExceptionHandling.escalate_to_production_support(exception, subject)
470
487
  end
471
488
  end
472
489
 
473
490
  context "exception timestamp" do
474
491
  setup do
475
- Time.now_override = Time.parse( '1986-5-21 4:17 am UTC' )
492
+ Time.now_override = Time.parse('1986-5-21 4:17 am UTC')
476
493
  end
477
494
 
478
495
  should "include the timestamp when the exception is logged" do
479
- mock(ExceptionHandling.logger).fatal(/\(Error:517033020\) ArgumentError mooo \(blah\):\n.*exception_handling_test\.rb/, anything)
480
- b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError.new("blah") }
481
- assert_nil b
482
-
483
- assert_equal 517033020, ExceptionHandling.last_exception_timestamp
496
+ capture_notifications
484
497
 
485
- assert_emails 1
486
- assert_match(/517033020/, ActionMailer::Base.deliveries[-1].body.to_s)
487
- end
488
- end
489
-
490
- should "send just one copy of exceptions that don't repeat" do
491
- ExceptionHandling.log_error(exception_1)
492
- ExceptionHandling.log_error(exception_2)
493
- assert_emails 2
494
- assert_match(/Exception 1/, ActionMailer::Base.deliveries[-2].subject)
495
- assert_match(/Exception 2/, ActionMailer::Base.deliveries[-1].subject)
496
- end
498
+ mock(ExceptionHandling.logger).fatal(/\(Error:517033020\) ArgumentError context \(blah\):\n.*exception_handling_test\.rb/, anything)
499
+ b = ExceptionHandling.ensure_safe("context") { raise ArgumentError, "blah" }
500
+ assert_nil b
497
501
 
498
- should "not send emails if exception is a warning" do
499
- ExceptionHandling.log_error(ExceptionHandling::Warning.new("Don't send email"))
500
- assert_emails 0
501
- end
502
+ assert_equal 517_033_020, ExceptionHandling.last_exception_timestamp
502
503
 
503
- should "not send emails when log_warning is called" do
504
- ExceptionHandling.log_warning("Don't send email")
505
- assert_emails 0
506
- end
504
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
507
505
 
508
- should "log the error if the exception message is nil" do
509
- ExceptionHandling.log_error(exception_with_nil_message)
510
- assert_emails(1)
511
- assert_match(/RuntimeError/, ActionMailer::Base.deliveries.last.subject)
512
- end
513
-
514
- should "log the error if the exception message is nil and the exception context is a hash" do
515
- ExceptionHandling.log_error(exception_with_nil_message, "SERVER_NAME" => "exceptional.com")
516
- assert_emails(1)
517
- assert_match(/RuntimeError/, ActionMailer::Base.deliveries.last.subject)
518
- end
519
-
520
- should "only send 5 of a repeated error, but call post hook for every exception" do
521
- @fail_count = 0
522
- ExceptionHandling.post_log_error_hook = method(:log_error_callback)
523
- assert_emails 5 do
524
- 10.times do
525
- ExceptionHandling.log_error(exception_1)
526
- end
506
+ assert_equal 517_033_020, sent_notifications.last.enhanced_data['timestamp']
527
507
  end
528
- assert_equal 10, @fail_count
529
508
  end
530
509
 
531
- should "only send 5 of a repeated error but don't send summary if 6th is different" do
532
- assert_emails 5 do
533
- 5.times do
534
- ExceptionHandling.log_error(exception_1)
535
- end
536
- end
537
- assert_emails 1 do
538
- ExceptionHandling.log_error(exception_2)
539
- end
540
- end
541
-
542
- should "send the summary when the error is encountered an hour after the first occurrence" do
543
- assert_emails 5 do # 5 exceptions, 4 summarized
544
- 9.times do |t|
545
- ExceptionHandling.log_error(exception_1)
546
- end
547
- end
548
- Time.now_override = 2.hours.from_now
549
- assert_emails 1 do # 1 summary (4 + 1 = 5) after 2 hours
550
- ExceptionHandling.log_error(exception_1)
551
- end
552
- assert_match(/\[5 SUMMARIZED\]/, ActionMailer::Base.deliveries.last.subject)
553
- assert_match(/This exception occurred 5 times since/, ActionMailer::Base.deliveries.last.body.to_s)
554
-
555
- assert_emails 0 do # still summarizing...
556
- 7.times do
557
- ExceptionHandling.log_error(exception_1)
558
- end
559
- end
510
+ should "log the error if the exception message is nil" do
511
+ capture_notifications
560
512
 
561
- Time.now_override = 3.hours.from_now
513
+ ExceptionHandling.log_error(exception_with_nil_message)
562
514
 
563
- assert_emails 1 + 2 do # 1 summary and 2 new
564
- 2.times do
565
- ExceptionHandling.log_error(exception_2)
566
- end
567
- end
568
- assert_match(/\[7 SUMMARIZED\]/, ActionMailer::Base.deliveries[-3].subject)
569
- assert_match(/This exception occurred 7 times since/, ActionMailer::Base.deliveries[-3].body.to_s)
515
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
516
+ assert_equal 'RuntimeError: ', sent_notifications.last.enhanced_data['error_string']
570
517
  end
571
518
 
572
- should "send the summary if a summary is available, but not sent when another exception comes up" do
573
- assert_emails 5 do # 5 to start summarizing
574
- 6.times do
575
- ExceptionHandling.log_error(exception_1)
576
- end
577
- end
578
-
579
- assert_emails 1 + 1 do # 1 summary of previous, 1 from new exception
580
- ExceptionHandling.log_error(exception_2)
581
- end
582
-
583
- assert_match(/\[1 SUMMARIZED\]/, ActionMailer::Base.deliveries[-2].subject)
584
- assert_match(/This exception occurred 1 times since/, ActionMailer::Base.deliveries[-2].body.to_s)
519
+ should "log the error if the exception message is nil and the exception context is a hash" do
520
+ capture_notifications
585
521
 
586
- assert_emails 5 do # 5 to start summarizing
587
- 10.times do
588
- ExceptionHandling.log_error(exception_1)
589
- end
590
- end
522
+ ExceptionHandling.log_error(exception_with_nil_message, "SERVER_NAME" => "exceptional.com")
591
523
 
592
- assert_emails 0 do # still summarizing
593
- 11.times do
594
- ExceptionHandling.log_error(exception_1)
595
- end
596
- end
524
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
525
+ assert_equal 'RuntimeError: ', sent_notifications.last.enhanced_data['error_string']
597
526
  end
598
527
 
599
528
  context "Honeybadger integration" do
600
529
  context "with Honeybadger not defined" do
601
530
  setup do
602
- stub(ExceptionHandling).honeybadger? { false }
531
+ stub(ExceptionHandling).honeybadger_defined? { false }
603
532
  end
604
533
 
605
534
  should "not invoke send_exception_to_honeybadger when log_error is executed" do
@@ -649,16 +578,17 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
649
578
  should "send error details and relevant context data to Honeybadger" do
650
579
  Time.now_override = Time.now
651
580
  env = { server: "fe98" }
652
- parameters = { advertiser_id: 435 }
581
+ parameters = { advertiser_id: 435, controller: "some_controller" }
653
582
  session = { username: "jsmith" }
654
583
  request_uri = "host/path"
655
584
  controller = create_dummy_controller(env, parameters, session, request_uri)
656
585
  stub(ExceptionHandling).server_name { "invoca_fe98" }
657
586
 
658
- exception = StandardError.new("Some BS")
587
+ exception = StandardError.new("Some Exception")
659
588
  exception.set_backtrace([
660
- "test/unit/exception_handling_test.rb:847:in `exception_1'",
661
- "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"])
589
+ "test/unit/exception_handling_test.rb:847:in `exception_1'",
590
+ "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
591
+ ])
662
592
  exception_context = { "SERVER_NAME" => "exceptional.com" }
663
593
 
664
594
  honeybadger_data = nil
@@ -673,8 +603,9 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
673
603
  end
674
604
 
675
605
  expected_data = {
676
- error_class: :"Test BS",
677
- error_message: "Some BS",
606
+ error_class: :"Test Exception",
607
+ error_message: "Some Exception",
608
+ controller: "some_controller",
678
609
  exception: exception,
679
610
  context: {
680
611
  timestamp: Time.now.to_i,
@@ -685,25 +616,33 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
685
616
  notes: "this is used by a test",
686
617
  user_details: { "username" => "jsmith" },
687
618
  request: {
688
- "params" => { "advertiser_id" => 435 },
619
+ "params" => { "advertiser_id" => 435, "controller" => "some_controller" },
689
620
  "rails_root" => "Rails.root not defined. Is this a test environment?",
690
- "url" => "host/path" },
621
+ "url" => "host/path"
622
+ },
691
623
  session: {
692
624
  "key" => nil,
693
- "data" => { "username" => "jsmith" } },
625
+ "data" => { "username" => "jsmith" }
626
+ },
694
627
  environment: {
695
- "SERVER_NAME" => "exceptional.com" },
628
+ "SERVER_NAME" => "exceptional.com"
629
+ },
696
630
  backtrace: [
697
631
  "test/unit/exception_handling_test.rb:847:in `exception_1'",
698
- "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"],
632
+ "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
633
+ ],
699
634
  event_response: "Event successfully received"
700
635
  }
701
636
  }
702
637
  assert_equal_with_diff expected_data, honeybadger_data
703
638
  end
704
639
 
705
- should "not send notification to honeybadger when exception description has the flag turned off and call log error callback with logged_to_honeybadger set to nil" do
706
- begin
640
+ context "with post_log_error_hook set" do
641
+ teardown do
642
+ ExceptionHandling.post_log_error_hook = nil
643
+ end
644
+
645
+ should "not send notification to honeybadger when exception description has the flag turned off and call log error callback with logged_to_honeybadger set to nil" do
707
646
  @fail_count = 0
708
647
  @honeybadger_status = nil
709
648
  ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
@@ -716,55 +655,37 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
716
655
  stub(File).mtime { incrementing_mtime }
717
656
  mock(YAML).load_file.with_any_args { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }.at_least(1)
718
657
 
719
- exception = StandardError.new("suppress Honeybadger notification")
720
- mock.proxy(ExceptionHandling).send_exception_to_honeybadger.with_any_args.once
658
+ mock.proxy(ExceptionHandling).send_exception_to_honeybadger_unless_filtered.with_any_args.once
721
659
  dont_allow(Honeybadger).notify
722
- ExceptionHandling.log_error(exception)
660
+ ExceptionHandling.log_error(StandardError.new("suppress Honeybadger notification"))
723
661
  assert_equal :skipped, @honeybadger_status
724
- ensure
725
- ExceptionHandling.post_log_error_hook = nil
726
662
  end
727
- end
728
663
 
729
- should "call log error callback with logged_to_honeybadger set to false if an error occurs while attempting to notify honeybadger" do
730
- begin
664
+ should "call log error callback with logged_to_honeybadger set to false if an error occurs while attempting to notify honeybadger" do
731
665
  @fail_count = 0
732
666
  @honeybadger_status = nil
733
667
  ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
734
- stub($stderr).puts
735
668
  mock(Honeybadger).notify.with_any_args { raise "Honeybadger Notification Failure" }
736
669
  ExceptionHandling.log_error(exception_1)
737
670
  assert_equal :failure, @honeybadger_status
738
- ensure
739
- ExceptionHandling.post_log_error_hook = nil
740
671
  end
741
- end
742
672
 
743
- should "call log error callback with logged_to_honeybadger set to false on unsuccessful honeybadger notification" do
744
- begin
673
+ should "call log error callback with logged_to_honeybadger set to false on unsuccessful honeybadger notification" do
745
674
  @fail_count = 0
746
675
  @honeybadger_status = nil
747
676
  ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
748
- stub($stderr).puts
749
677
  mock(Honeybadger).notify.with_any_args { false }
750
678
  ExceptionHandling.log_error(exception_1)
751
679
  assert_equal :failure, @honeybadger_status
752
- ensure
753
- ExceptionHandling.post_log_error_hook = nil
754
680
  end
755
- end
756
681
 
757
- should "call log error callback with logged_to_honeybadger set to true on successful honeybadger notification" do
758
- begin
682
+ should "call log error callback with logged_to_honeybadger set to true on successful honeybadger notification" do
759
683
  @fail_count = 0
760
684
  @honeybadger_status = nil
761
685
  ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
762
- stub($stderr).puts
763
686
  mock(Honeybadger).notify.with_any_args { '06220c5a-b471-41e5-baeb-de247da45a56' }
764
687
  ExceptionHandling.log_error(exception_1)
765
688
  assert_equal :success, @honeybadger_status
766
- ensure
767
- ExceptionHandling.post_log_error_hook = nil
768
689
  end
769
690
  end
770
691
  end
@@ -777,20 +698,27 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
777
698
  end
778
699
 
779
700
  should "allow sections to have data with just a to_s method" do
780
- ExceptionHandling.log_error("This is my RingSwitch example. Log, don't email!") do |data|
781
- data.merge!(:event_response => EventResponse.new)
701
+ capture_notifications
702
+
703
+ ExceptionHandling.log_error("This is my RingSwitch example.") do |data|
704
+ data.merge!(event_response: EventResponse.new)
782
705
  end
783
- assert_emails 1
784
- assert_match(/message from to_s!/, ActionMailer::Base.deliveries.last.body.to_s)
706
+
707
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
708
+ assert_match(/message from to_s!/, sent_notifications.last.enhanced_data['event_response'].to_s)
785
709
  end
786
710
  end
787
711
 
712
+ should "return the error ID (timestamp)" do
713
+ result = ExceptionHandling.log_error(RuntimeError.new("A runtime error"), "Runtime message")
714
+ assert_equal ExceptionHandling.last_exception_timestamp, result
715
+ end
716
+
788
717
  should "rescue exceptions that happen in log_error" do
789
- stub(ExceptionHandling).make_exception { raise ArgumentError.new("Bad argument") }
718
+ stub(ExceptionHandling).make_exception { raise ArgumentError, "Bad argument" }
790
719
  mock(ExceptionHandling).write_exception_to_log(satisfy { |ex| ex.to_s['Bad argument'] },
791
720
  satisfy { |context| context['ExceptionHandlingError: log_error rescued exception while logging Runtime message'] },
792
721
  anything)
793
- stub($stderr).puts
794
722
  ExceptionHandling.log_error(RuntimeError.new("A runtime error"), "Runtime message")
795
723
  end
796
724
 
@@ -799,13 +727,13 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
799
727
  satisfy { |context| context['Context message'] },
800
728
  anything,
801
729
  anything)
802
- ExceptionHandling.log_error(ArgumentError.new("Bad argument"), "Context message") { |data| raise 'Error!!!' }
730
+ ExceptionHandling.log_error(ArgumentError.new("Bad argument"), "Context message") { |_data| raise 'Error!!!' }
803
731
  end
804
732
 
805
733
  context "Exception Filtering" do
806
734
  setup do
807
- filter_list = { :exception1 => { 'error' => "my error message" },
808
- :exception2 => { 'error' => "some other message", :session => "misc data" } }
735
+ filter_list = { exception1: { 'error' => "my error message" },
736
+ exception2: { 'error' => "some other message", :session => "misc data" } }
809
737
  stub(YAML).load_file { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }
810
738
 
811
739
  # bump modified time up to get the above filter loaded
@@ -813,105 +741,119 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
813
741
  end
814
742
 
815
743
  should "handle case where filter list is not found" do
816
- stub(YAML).load_file { raise Errno::ENOENT.new("File not found") }
744
+ stub(YAML).load_file { raise Errno::ENOENT, "File not found" }
745
+
746
+ capture_notifications
817
747
 
818
- ActionMailer::Base.deliveries.clear
819
- ExceptionHandling.log_error( "My error message is in list" )
820
- assert_emails 1
748
+ ExceptionHandling.log_error("My error message is in list")
749
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
821
750
  end
822
751
 
823
752
  should "log exception and suppress email when exception is on filter list" do
824
- ActionMailer::Base.deliveries.clear
825
- ExceptionHandling.log_error( "Error message is not in list" )
826
- assert_emails 1
753
+ capture_notifications
827
754
 
828
- ActionMailer::Base.deliveries.clear
829
- ExceptionHandling.log_error( "My error message is in list" )
830
- assert_emails 0
755
+ ExceptionHandling.log_error("Error message is not in list")
756
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
757
+
758
+ sent_notifications.clear
759
+ ExceptionHandling.log_error("My error message is in list")
760
+ assert_equal 0, sent_notifications.size, sent_notifications.inspect
831
761
  end
832
762
 
833
763
  should "allow filtering exception on any text in exception data" do
834
- filters = { :exception1 => { :session => "data: my extra session data" } }
764
+ filters = { exception1: { session: "data: my extra session data" } }
835
765
  stub(YAML).load_file { ActiveSupport::HashWithIndifferentAccess.new(filters) }
836
766
 
837
- ActionMailer::Base.deliveries.clear
838
- ExceptionHandling.log_error( "No match here" ) do |data|
767
+ capture_notifications
768
+
769
+ ExceptionHandling.log_error("No match here") do |data|
839
770
  data[:session] = {
840
- :key => "@session_id",
841
- :data => "my extra session data"
842
- }
771
+ key: "@session_id",
772
+ data: "my extra session data"
773
+ }
843
774
  end
844
- assert_emails 0
775
+ assert_equal 0, sent_notifications.size, sent_notifications.inspect
845
776
 
846
- ActionMailer::Base.deliveries.clear
847
- ExceptionHandling.log_error( "No match here" ) do |data|
777
+ ExceptionHandling.log_error("No match here") do |data|
848
778
  data[:session] = {
849
- :key => "@session_id",
850
- :data => "my extra session <no match!> data"
851
- }
779
+ key: "@session_id",
780
+ data: "my extra session <no match!> data"
781
+ }
852
782
  end
853
- assert_emails 1, ActionMailer::Base.deliveries.*.body.*.inspect
783
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
854
784
  end
855
785
 
856
786
  should "reload filter list on the next exception if file was modified" do
857
- ActionMailer::Base.deliveries.clear
858
- ExceptionHandling.log_error( "Error message is not in list" )
859
- assert_emails 1
787
+ capture_notifications
788
+
789
+ ExceptionHandling.log_error("Error message is not in list")
790
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
860
791
 
861
- filter_list = { :exception1 => { 'error' => "Error message is not in list" } }
792
+ filter_list = { exception1: { 'error' => "Error message is not in list" } }
862
793
  stub(YAML).load_file { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }
863
794
  stub(File).mtime { incrementing_mtime }
864
795
 
865
- ActionMailer::Base.deliveries.clear
866
- ExceptionHandling.log_error( "Error message is not in list" )
867
- assert_emails 0, ActionMailer::Base.deliveries.*.body.*.inspect
796
+ sent_notifications.clear
797
+ ExceptionHandling.log_error("Error message is not in list")
798
+ assert_equal 0, sent_notifications.size, sent_notifications.inspect
868
799
  end
869
800
 
870
801
  should "not consider filter if both error message and body do not match" do
802
+ capture_notifications
803
+
871
804
  # error message matches, but not full text
872
- ActionMailer::Base.deliveries.clear
873
- ExceptionHandling.log_error( "some other message" )
874
- assert_emails 1, ActionMailer::Base.deliveries.*.body.*.inspect
805
+ ExceptionHandling.log_error("some other message")
806
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
875
807
 
876
808
  # now both match
877
- ActionMailer::Base.deliveries.clear
878
- ExceptionHandling.log_error( "some other message" ) do |data|
879
- data[:session] = {:some_random_key => "misc data"}
809
+ sent_notifications.clear
810
+ ExceptionHandling.log_error("some other message") do |data|
811
+ data[:session] = { some_random_key: "misc data" }
880
812
  end
881
- assert_emails 0, ActionMailer::Base.deliveries.*.body.*.inspect
813
+ assert_equal 0, sent_notifications.size, sent_notifications.inspect
882
814
  end
883
815
 
884
816
  should "skip environment keys not on whitelist" do
885
- ActionMailer::Base.deliveries.clear
886
- ExceptionHandling.log_error( "some message" ) do |data|
887
- data[:environment] = { :SERVER_PROTOCOL => "HTTP/1.0", :RAILS_SECRETS_YML_CONTENTS => 'password: VERY_SECRET_PASSWORD' }
817
+ capture_notifications
818
+
819
+ ExceptionHandling.log_error("some message") do |data|
820
+ data[:environment] = { SERVER_PROTOCOL: "HTTP/1.0", RAILS_SECRETS_YML_CONTENTS: 'password: VERY_SECRET_PASSWORD' }
888
821
  end
889
- assert_emails 1, ActionMailer::Base.deliveries.*.body.*.inspect
890
- mail = ActionMailer::Base.deliveries.last
891
- assert_nil mail.body.to_s["RAILS_SECRETS_YML_CONTENTS"], mail.body.to_s # this is not on whitelist
892
- assert mail.body.to_s["SERVER_PROTOCOL: HTTP/1.0" ], mail.body.to_s # this is
822
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
823
+
824
+ mail = sent_notifications.last
825
+ environment = mail.enhanced_data['environment']
826
+
827
+ assert_nil environment["RAILS_SECRETS_YML_CONTENTS"], environment.inspect # this is not on whitelist
828
+ assert environment["SERVER_PROTOCOL"], environment.inspect # this is
893
829
  end
894
830
 
895
831
  should "omit environment defaults" do
896
- ActionMailer::Base.deliveries.clear
897
- ExceptionHandling.log_error( "some message" ) do |data|
898
- data[:environment] = {:SERVER_PORT => '80', :SERVER_PROTOCOL => "HTTP/1.0"}
832
+ capture_notifications
833
+
834
+ stub(ExceptionHandling).send_exception_to_honeybadger(anything) { |exception_info| sent_notifications << exception_info }
835
+
836
+ ExceptionHandling.log_error("some message") do |data|
837
+ data[:environment] = { SERVER_PORT: '80', SERVER_PROTOCOL: "HTTP/1.0" }
899
838
  end
900
- assert_emails 1, ActionMailer::Base.deliveries.*.body.*.inspect
901
- mail = ActionMailer::Base.deliveries.last
902
- assert_nil mail.body.to_s["SERVER_PORT" ], mail.body.to_s # this was default
903
- assert mail.body.to_s["SERVER_PROTOCOL: HTTP/1.0"], mail.body.to_s # this was not
839
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
840
+ mail = sent_notifications.last
841
+ environment = mail.enhanced_data['environment']
842
+
843
+ assert_nil environment["SERVER_PORT"], environment.inspect # this was default
844
+ assert environment["SERVER_PROTOCOL"], environment # this was not
904
845
  end
905
846
 
906
847
  should "reject the filter file if any contain all empty regexes" do
907
- filter_list = { :exception1 => { 'error' => "", :session => "" },
908
- :exception2 => { 'error' => "is not in list", :session => "" } }
848
+ filter_list = { exception1: { 'error' => "", :session => "" },
849
+ exception2: { 'error' => "is not in list", :session => "" } }
909
850
  stub(YAML).load_file { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }
910
851
  stub(File).mtime { incrementing_mtime }
911
852
 
912
- ActionMailer::Base.deliveries.clear
913
- ExceptionHandling.log_error( "Error message is not in list" )
914
- assert_emails 1, ActionMailer::Base.deliveries.*.inspect
853
+ capture_notifications
854
+
855
+ ExceptionHandling.log_error("Error message is not in list")
856
+ assert_equal 1, sent_notifications.size, sent_notifications.inspect
915
857
  end
916
858
 
917
859
  should "reload filter file if filename changes" do
@@ -921,27 +863,13 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
921
863
  end
922
864
 
923
865
  context "Exception Handling Mailer" do
924
- should "create email" do
925
- ExceptionHandling.log_error(exception_1) do |data|
926
- data[:request] = { :params => {:id => 10993}, :url => "www.ringrevenue.com" }
927
- data[:session] = { :key => "DECAFE" }
928
- end
929
- assert_emails 1, ActionMailer::Base.deliveries.*.inspect
930
- assert mail = ActionMailer::Base.deliveries.last
931
- assert_equal ['exceptions@example.com'], mail.to
932
- assert_equal ['server@example.com'].to_s, mail.from.to_s
933
- assert_match(/Exception 1/, mail.to_s)
934
- assert_match(/key: DECAFE/, mail.to_s)
935
- assert_match(/id: 10993/, mail.to_s)
936
- end
937
-
938
866
  EXPECTED_SMTP_HASH =
939
- {
940
- :host => '127.0.0.1',
941
- :domain => 'localhost.localdomain',
942
- :from => 'server@example.com',
943
- :to => 'exceptions@example.com'
944
- }
867
+ {
868
+ host: '127.0.0.1',
869
+ domain: 'localhost.localdomain',
870
+ from: 'server@example.com',
871
+ to: 'escalation@example.com'
872
+ }.freeze
945
873
 
946
874
  [[true, false], [true, true]].each do |em_flag, synchrony_flag|
947
875
  context "eventmachine_safe = #{em_flag} && eventmachine_synchrony = #{synchrony_flag}" do
@@ -961,22 +889,24 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
961
889
  end
962
890
 
963
891
  should "schedule EventMachine STMP when EventMachine defined" do
892
+ ActionMailer::Base.deliveries.clear
893
+
964
894
  set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientStub)
965
895
 
966
- ExceptionHandling.log_error(exception_1)
896
+ ExceptionHandling.ensure_escalation("ensure message") { raise 'Exception to escalate!' }
967
897
  assert EventMachineStub.block
968
898
  EventMachineStub.block.call
969
899
  assert DNSResolvStub.callback_block
970
900
  DNSResolvStub.callback_block.call ['127.0.0.1']
971
- assert_equal_with_diff EXPECTED_SMTP_HASH, (SmtpClientStub.send_hash & EXPECTED_SMTP_HASH.keys).map_hash { |k,v| v.to_s }, SmtpClientStub.send_hash.inspect
901
+ assert_equal_with_diff EXPECTED_SMTP_HASH, (SmtpClientStub.send_hash & EXPECTED_SMTP_HASH.keys).map_hash { |_k, v| v.to_s }, SmtpClientStub.send_hash.inspect
972
902
  assert_equal((synchrony_flag ? :asend : :send), SmtpClientStub.last_method)
973
- assert_match(/Exception 1/, SmtpClientStub.send_hash[:content])
903
+ assert_match(/Exception to escalate/, SmtpClientStub.send_hash[:content])
974
904
  assert_emails 0, ActionMailer::Base.deliveries.*.to_s
975
905
  end
976
906
 
977
907
  should "pass the content as a proper rfc 2822 message" do
978
908
  set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientStub)
979
- ExceptionHandling.log_error(exception_1)
909
+ ExceptionHandling.ensure_escalation("ensure message") { raise 'Exception to escalate!' }
980
910
  assert EventMachineStub.block
981
911
  EventMachineStub.block.call
982
912
  assert DNSResolvStub.callback_block
@@ -987,27 +917,26 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
987
917
  end
988
918
 
989
919
  should "log fatal on EventMachine STMP errback" do
990
- assert_emails 0, ActionMailer::Base.deliveries.*.to_s
920
+ ActionMailer::Base.deliveries.clear
921
+
991
922
  set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientErrbackStub)
992
- mock(ExceptionHandling.logger).fatal(/Exception 1/, anything)
923
+ mock(ExceptionHandling.logger).fatal(/Exception to escalate/, anything)
993
924
  mock(ExceptionHandling.logger).fatal(/Failed to email by SMTP: "credential mismatch"/)
994
925
 
995
- ExceptionHandling.log_error(exception_1)
926
+ ExceptionHandling.ensure_escalation("ensure message") { raise 'Exception to escalate!' }
996
927
  assert EventMachineStub.block
997
928
  EventMachineStub.block.call
998
929
  assert DNSResolvStub.callback_block
999
930
  DNSResolvStub.callback_block.call(['127.0.0.1'])
1000
931
  SmtpClientErrbackStub.block.call("credential mismatch")
1001
- assert_equal_with_diff EXPECTED_SMTP_HASH, (SmtpClientErrbackStub.send_hash & EXPECTED_SMTP_HASH.keys).map_hash { |k,v| v.to_s }, SmtpClientErrbackStub.send_hash.inspect
1002
- #assert_emails 0, ActionMailer::Base.deliveries.*.to_s
932
+ assert_equal_with_diff EXPECTED_SMTP_HASH, (SmtpClientErrbackStub.send_hash & EXPECTED_SMTP_HASH.keys).map_hash { |_k, v| v.to_s }, SmtpClientErrbackStub.send_hash.inspect
1003
933
  end
1004
934
 
1005
935
  should "log fatal on EventMachine dns resolver errback" do
1006
- assert_emails 0, ActionMailer::Base.deliveries.*.to_s
1007
- mock(ExceptionHandling.logger).fatal(/Exception 1/, anything)
936
+ mock(ExceptionHandling.logger).fatal(/Exception to escalate/, anything)
1008
937
  mock(ExceptionHandling.logger).fatal(/Failed to resolv DNS for localhost: "softlayer sucks"/)
1009
938
 
1010
- ExceptionHandling.log_error(exception_1)
939
+ ExceptionHandling.ensure_escalation("ensure message") { raise 'Exception to escalate!' }
1011
940
  assert EventMachineStub.block
1012
941
  EventMachineStub.block.call
1013
942
  DNSResolvStub.errback_block.call("softlayer sucks")
@@ -1015,45 +944,31 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
1015
944
  end
1016
945
  end
1017
946
  end
1018
-
1019
- should "truncate email subject" do
1020
- ActionMailer::Base.deliveries.clear
1021
- text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM".split('').join("123456789")
1022
- begin
1023
- raise text
1024
- rescue => ex
1025
- ExceptionHandling.log_error( ex )
1026
- end
1027
- assert_emails 1, ActionMailer::Base.deliveries.*.inspect
1028
- mail = ActionMailer::Base.deliveries.last
1029
- subject = "#{ExceptionHandling.email_environment} exception: RuntimeError: " + text
1030
- assert_equal subject[0,300], mail.subject
1031
- end
1032
947
  end
1033
948
 
1034
949
  context "Exception mapping" do
1035
950
  setup do
1036
951
  @data = {
1037
- :environment=>{
952
+ environment: {
1038
953
  'HTTP_HOST' => "localhost",
1039
954
  'HTTP_REFERER' => "http://localhost/action/controller/instance",
1040
955
  },
1041
- :session=>{
1042
- :data=>{
1043
- :affiliate_id=> defined?(Affiliate) ? Affiliate.first.id : '1',
1044
- :edit_mode=> true,
1045
- :advertiser_id=> defined?(Advertiser) ? Advertiser.first.id : '1',
1046
- :username_id=> defined?(Username) ? Username.first.id : '1',
1047
- :user_id=> defined?(User) ? User.first.id : '1',
1048
- :flash=>{},
1049
- :impersonated_organization_pk=> 'Advertiser_1'
956
+ session: {
957
+ data: {
958
+ affiliate_id: defined?(Affiliate) ? Affiliate.first.id : '1',
959
+ edit_mode: true,
960
+ advertiser_id: defined?(Advertiser) ? Advertiser.first.id : '1',
961
+ username_id: defined?(Username) ? Username.first.id : '1',
962
+ user_id: defined?(User) ? User.first.id : '1',
963
+ flash: {},
964
+ impersonated_organization_pk: 'Advertiser_1'
1050
965
  }
1051
966
  },
1052
- :request=>{},
1053
- :backtrace=>["[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:580:in `call_filters'", "[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:601:in `run_before_filters'"],
1054
- :api_key=>"none",
1055
- :error_class=>"StandardError",
1056
- :error=>'Some error message'
967
+ request: {},
968
+ backtrace: ["[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:580:in `call_filters'", "[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:601:in `run_before_filters'"],
969
+ api_key: "none",
970
+ error_class: "StandardError",
971
+ error: 'Some error message'
1057
972
  }
1058
973
  end
1059
974
 
@@ -1130,7 +1045,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
1130
1045
  should "take in additional key word args as logging context and pass them to the logger" do
1131
1046
  ExceptionHandling.log_periodically(:test_context_with_periodic, 30.minutes, "this will be written", service_name: 'exception_handling')
1132
1047
  assert_not_empty logged_excluding_reload_filter.last[:context]
1133
- assert_equal logged_excluding_reload_filter.last[:context], { service_name: 'exception_handling' }
1048
+ assert_equal logged_excluding_reload_filter.last[:context], service_name: 'exception_handling'
1134
1049
  end
1135
1050
 
1136
1051
  should "log immediately when we are expected to log" do
@@ -1164,7 +1079,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
1164
1079
  end
1165
1080
 
1166
1081
  def exception_1
1167
- @ex1 ||=
1082
+ @exception_1 ||=
1168
1083
  begin
1169
1084
  raise StandardError, "Exception 1"
1170
1085
  rescue => ex
@@ -1173,7 +1088,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
1173
1088
  end
1174
1089
 
1175
1090
  def exception_2
1176
- @ex2 ||=
1091
+ @exception_2 ||=
1177
1092
  begin
1178
1093
  raise StandardError, "Exception 2"
1179
1094
  rescue => ex