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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -0
- data/Gemfile +7 -6
- data/Gemfile.lock +26 -23
- data/README.md +0 -1
- data/Rakefile +4 -4
- data/config/exception_filters.yml +2 -3
- data/exception_handling.gemspec +12 -10
- data/lib/exception_handling/exception_catalog.rb +5 -4
- data/lib/exception_handling/exception_description.rb +28 -28
- data/lib/exception_handling/exception_info.rb +80 -72
- data/lib/exception_handling/honeybadger_callbacks.rb +41 -24
- data/lib/exception_handling/log_stub_error.rb +10 -8
- data/lib/exception_handling/mailer.rb +27 -48
- data/lib/exception_handling/methods.rb +15 -11
- data/lib/exception_handling/sensu.rb +7 -5
- data/lib/exception_handling/testing.rb +21 -19
- data/lib/exception_handling/version.rb +1 -1
- data/lib/exception_handling.rb +105 -200
- data/test/helpers/controller_helpers.rb +3 -1
- data/test/helpers/exception_helpers.rb +9 -0
- data/test/test_helper.rb +26 -21
- data/test/unit/exception_handling/exception_catalog_test.rb +15 -14
- data/test/unit/exception_handling/exception_description_test.rb +22 -31
- data/test/unit/exception_handling/exception_info_test.rb +76 -37
- data/test/unit/exception_handling/honeybadger_callbacks_test.rb +46 -9
- data/test/unit/exception_handling/log_error_stub_test.rb +6 -4
- data/test/unit/exception_handling/mailer_test.rb +8 -14
- data/test/unit/exception_handling/methods_test.rb +9 -6
- data/test/unit/exception_handling/sensu_test.rb +6 -4
- data/test/unit/exception_handling_test.rb +279 -364
- metadata +24 -24
- data/views/exception_handling/mailer/exception_notification.html.erb +0 -92
@@ -1,4 +1,6 @@
|
|
1
|
-
|
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(
|
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(
|
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,
|
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(
|
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(
|
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(
|
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
|
-
|
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],
|
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],
|
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],
|
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],
|
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("
|
170
|
-
|
171
|
-
|
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
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
-
|
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
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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(
|
405
|
-
assert_equal
|
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
|
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
|
-
|
425
|
-
|
426
|
-
logger.fatal
|
420
|
+
log_fatals = []
|
421
|
+
stub(ExceptionHandling.logger) do |logger|
|
422
|
+
logger.fatal.with_any_args { |*args| log_fatals << args }
|
427
423
|
end
|
428
|
-
|
429
|
-
|
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
|
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
|
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).
|
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(
|
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
|
-
|
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
|
-
|
486
|
-
|
487
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 "
|
532
|
-
|
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
|
-
|
513
|
+
ExceptionHandling.log_error(exception_with_nil_message)
|
562
514
|
|
563
|
-
|
564
|
-
|
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 "
|
573
|
-
|
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
|
-
|
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
|
-
|
593
|
-
|
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).
|
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
|
587
|
+
exception = StandardError.new("Some Exception")
|
659
588
|
exception.set_backtrace([
|
660
|
-
|
661
|
-
|
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
|
677
|
-
error_message: "Some
|
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
|
-
|
706
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
781
|
-
|
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
|
-
|
784
|
-
|
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
|
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") { |
|
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 = { :
|
808
|
-
:
|
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
|
744
|
+
stub(YAML).load_file { raise Errno::ENOENT, "File not found" }
|
745
|
+
|
746
|
+
capture_notifications
|
817
747
|
|
818
|
-
|
819
|
-
|
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
|
-
|
825
|
-
ExceptionHandling.log_error( "Error message is not in list" )
|
826
|
-
assert_emails 1
|
753
|
+
capture_notifications
|
827
754
|
|
828
|
-
|
829
|
-
|
830
|
-
|
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 = { :
|
764
|
+
filters = { exception1: { session: "data: my extra session data" } }
|
835
765
|
stub(YAML).load_file { ActiveSupport::HashWithIndifferentAccess.new(filters) }
|
836
766
|
|
837
|
-
|
838
|
-
|
767
|
+
capture_notifications
|
768
|
+
|
769
|
+
ExceptionHandling.log_error("No match here") do |data|
|
839
770
|
data[:session] = {
|
840
|
-
|
841
|
-
|
842
|
-
|
771
|
+
key: "@session_id",
|
772
|
+
data: "my extra session data"
|
773
|
+
}
|
843
774
|
end
|
844
|
-
|
775
|
+
assert_equal 0, sent_notifications.size, sent_notifications.inspect
|
845
776
|
|
846
|
-
|
847
|
-
ExceptionHandling.log_error( "No match here" ) do |data|
|
777
|
+
ExceptionHandling.log_error("No match here") do |data|
|
848
778
|
data[:session] = {
|
849
|
-
|
850
|
-
|
851
|
-
|
779
|
+
key: "@session_id",
|
780
|
+
data: "my extra session <no match!> data"
|
781
|
+
}
|
852
782
|
end
|
853
|
-
|
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
|
-
|
858
|
-
|
859
|
-
|
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 = { :
|
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
|
-
|
866
|
-
ExceptionHandling.log_error(
|
867
|
-
|
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
|
-
|
873
|
-
|
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
|
-
|
878
|
-
ExceptionHandling.log_error(
|
879
|
-
data[:session] = {:
|
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
|
-
|
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
|
-
|
886
|
-
|
887
|
-
|
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
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
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
|
-
|
897
|
-
|
898
|
-
|
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
|
-
|
901
|
-
mail =
|
902
|
-
|
903
|
-
|
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 = { :
|
908
|
-
:
|
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
|
-
|
913
|
-
|
914
|
-
|
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
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
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.
|
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 { |
|
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
|
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.
|
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
|
-
|
920
|
+
ActionMailer::Base.deliveries.clear
|
921
|
+
|
991
922
|
set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientErrbackStub)
|
992
|
-
mock(ExceptionHandling.logger).fatal(/Exception
|
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.
|
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 { |
|
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
|
-
|
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.
|
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
|
-
:
|
952
|
+
environment: {
|
1038
953
|
'HTTP_HOST' => "localhost",
|
1039
954
|
'HTTP_REFERER' => "http://localhost/action/controller/instance",
|
1040
955
|
},
|
1041
|
-
:
|
1042
|
-
:
|
1043
|
-
:
|
1044
|
-
:
|
1045
|
-
:
|
1046
|
-
:
|
1047
|
-
:
|
1048
|
-
:
|
1049
|
-
:
|
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
|
-
:
|
1053
|
-
:
|
1054
|
-
:
|
1055
|
-
:
|
1056
|
-
:
|
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],
|
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
|
-
@
|
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
|
-
@
|
1091
|
+
@exception_2 ||=
|
1177
1092
|
begin
|
1178
1093
|
raise StandardError, "Exception 2"
|
1179
1094
|
rescue => ex
|