exception_handling 2.2.1 → 2.3.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,12 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper', __dir__)
|
2
4
|
|
3
5
|
module ExceptionHandling
|
4
6
|
class ExceptionCatalogTest < ActiveSupport::TestCase
|
5
7
|
|
6
8
|
context "With stubbed yaml content" do
|
7
9
|
setup do
|
8
|
-
filter_list = { :
|
9
|
-
:
|
10
|
+
filter_list = { exception1: { error: "my error message" },
|
11
|
+
exception2: { error: "some other message", session: "misc data" } }
|
10
12
|
stub(YAML).load_file { filter_list }
|
11
13
|
|
12
14
|
# bump modified time up to get the above filter loaded
|
@@ -16,7 +18,7 @@ module ExceptionHandling
|
|
16
18
|
context "with loaded data" do
|
17
19
|
setup do
|
18
20
|
stub(File).mtime { incrementing_mtime }
|
19
|
-
@exception_catalog = ExceptionCatalog.new(
|
21
|
+
@exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
|
20
22
|
@exception_catalog.send :load_file
|
21
23
|
end
|
22
24
|
|
@@ -25,7 +27,7 @@ module ExceptionHandling
|
|
25
27
|
end
|
26
28
|
|
27
29
|
should "find messages in the catalog" do
|
28
|
-
assert !@exception_catalog.find(
|
30
|
+
assert !@exception_catalog.find(error: "Scott says unlikely to ever match")
|
29
31
|
end
|
30
32
|
|
31
33
|
should "find matching data" do
|
@@ -36,29 +38,28 @@ module ExceptionHandling
|
|
36
38
|
end
|
37
39
|
|
38
40
|
should "write errors loading the yaml file directly to the log file" do
|
39
|
-
@exception_catalog = ExceptionCatalog.new(
|
41
|
+
@exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
|
40
42
|
|
41
43
|
mock(ExceptionHandling).log_error.never
|
42
|
-
mock(ExceptionHandling).write_exception_to_log(anything
|
43
|
-
mock(@exception_catalog).load_file { raise "noooooo"}
|
44
|
+
mock(ExceptionHandling).write_exception_to_log(anything, "ExceptionCatalog#refresh_filters: ./config/exception_filters.yml", anything)
|
45
|
+
mock(@exception_catalog).load_file { raise "noooooo" }
|
44
46
|
|
45
47
|
@exception_catalog.find({})
|
46
48
|
end
|
47
|
-
|
48
49
|
end
|
49
50
|
|
50
51
|
context "with live yaml content" do
|
51
52
|
setup do
|
52
|
-
@filename = File.expand_path('
|
53
|
-
@exception_catalog = ExceptionCatalog.new(
|
53
|
+
@filename = File.expand_path('../../../config/exception_filters.yml', __dir__)
|
54
|
+
@exception_catalog = ExceptionCatalog.new(@filename)
|
54
55
|
assert_nothing_raised "Loading the exception filter should not raise" do
|
55
56
|
@exception_catalog.send :load_file
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
59
60
|
should "load the filter data" do
|
60
|
-
assert !@exception_catalog.find(
|
61
|
-
assert !@exception_catalog.find(
|
61
|
+
assert !@exception_catalog.find(error: "Scott says unlikely to ever match")
|
62
|
+
assert !@exception_catalog.find(error: "Scott says unlikely to ever match")
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
@@ -69,7 +70,7 @@ module ExceptionHandling
|
|
69
70
|
|
70
71
|
should "not load filter data" do
|
71
72
|
mock(ExceptionHandling).write_exception_to_log.with_any_args.never
|
72
|
-
@exception_catalog.find(
|
73
|
+
@exception_catalog.find(error: "Scott says unlikely to ever match")
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
@@ -1,23 +1,24 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper', __dir__)
|
2
4
|
|
3
5
|
module ExceptionHandling
|
4
6
|
class ExceptionDescriptionTest < ActiveSupport::TestCase
|
5
7
|
|
6
8
|
context "Filter" do
|
7
|
-
|
8
9
|
should "allow direct matching of strings" do
|
9
|
-
@f = ExceptionDescription.new(:filter1, :
|
10
|
-
assert @f.match?(
|
10
|
+
@f = ExceptionDescription.new(:filter1, error: "my error message")
|
11
|
+
assert @f.match?('error' => "my error message")
|
11
12
|
end
|
12
13
|
|
13
14
|
should "allow direct matching of strings on with symbol keys" do
|
14
|
-
@f = ExceptionDescription.new(:filter1, :
|
15
|
-
assert @f.match?(
|
15
|
+
@f = ExceptionDescription.new(:filter1, error: "my error message")
|
16
|
+
assert @f.match?(error: "my error message")
|
16
17
|
end
|
17
18
|
|
18
19
|
should "allow wildcards to cross line boundries" do
|
19
|
-
@f = ExceptionDescription.new(:filter1, :
|
20
|
-
assert @f.match?(
|
20
|
+
@f = ExceptionDescription.new(:filter1, error: "my error message.*with multiple lines")
|
21
|
+
assert @f.match?(error: "my error message\nwith more than one, with multiple lines")
|
21
22
|
end
|
22
23
|
|
23
24
|
should "complain when no regexps have a value" do
|
@@ -28,12 +29,6 @@ module ExceptionHandling
|
|
28
29
|
assert_raise(ArgumentError, "Unknown section: not_a_parameter") { ExceptionDescription.new(:filter1, error: "my error message", not_a_parameter: false) }
|
29
30
|
end
|
30
31
|
|
31
|
-
should "allow send email to be specified" do
|
32
|
-
assert !ExceptionDescription.new(:filter1, error: "my error message", send_email: false ).send_email
|
33
|
-
assert ExceptionDescription.new(:filter1, error: "my error message", send_email: true ).send_email
|
34
|
-
assert !ExceptionDescription.new(:filter1, error: "my error message" ).send_email
|
35
|
-
end
|
36
|
-
|
37
32
|
should "allow send_to_honeybadger to be specified and have it disabled by default" do
|
38
33
|
assert !ExceptionDescription.new(:filter1, error: "my error message", send_to_honeybadger: false).send_to_honeybadger
|
39
34
|
assert ExceptionDescription.new(:filter1, error: "my error message", send_to_honeybadger: true).send_to_honeybadger
|
@@ -41,37 +36,35 @@ module ExceptionHandling
|
|
41
36
|
end
|
42
37
|
|
43
38
|
should "allow send_metric to be configured" do
|
44
|
-
assert !ExceptionDescription.new(:filter1, error: "my error message", send_metric: false
|
45
|
-
assert ExceptionDescription.new(:filter1, error: "my error message"
|
46
|
-
assert ExceptionDescription.new(:filter1, error: "my error message" ).send_metric
|
39
|
+
assert !ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).send_metric
|
40
|
+
assert ExceptionDescription.new(:filter1, error: "my error message").send_metric
|
47
41
|
end
|
48
42
|
|
49
43
|
should "provide metric name" do
|
50
|
-
assert_equal "filter1", ExceptionDescription.new(:filter1, error: "my error message"
|
51
|
-
assert_equal "some_other_metric_name", ExceptionDescription.new(:filter1, error: "my error message", metric_name: :some_other_metric_name
|
44
|
+
assert_equal "filter1", ExceptionDescription.new(:filter1, error: "my error message").metric_name
|
45
|
+
assert_equal "some_other_metric_name", ExceptionDescription.new(:filter1, error: "my error message", metric_name: :some_other_metric_name).metric_name
|
52
46
|
end
|
53
47
|
|
54
48
|
should "replace spaces in metric name" do
|
55
|
-
@f = ExceptionDescription.new(:"filter has spaces", :
|
49
|
+
@f = ExceptionDescription.new(:"filter has spaces", error: "my error message")
|
56
50
|
assert_equal "filter_has_spaces", @f.metric_name
|
57
51
|
end
|
58
52
|
|
59
53
|
should "allow notes to be recorded" do
|
60
|
-
assert_nil ExceptionDescription.new(:filter1, error: "my error message"
|
61
|
-
assert_equal "a long string", ExceptionDescription.new(:filter1, error: "my error message", notes: "a long string"
|
54
|
+
assert_nil ExceptionDescription.new(:filter1, error: "my error message").notes
|
55
|
+
assert_equal "a long string", ExceptionDescription.new(:filter1, error: "my error message", notes: "a long string").notes
|
62
56
|
end
|
63
57
|
|
64
58
|
should "not consider config options in the filter set" do
|
65
|
-
assert ExceptionDescription.new(:filter1, error: "my error message",
|
66
|
-
assert ExceptionDescription.new(:filter1, error: "my error message",
|
67
|
-
assert ExceptionDescription.new(:filter1, error: "my error message",
|
68
|
-
assert ExceptionDescription.new(:filter1, error: "my error message", notes: "hey" ).match?( :error => "my error message")
|
59
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).match?(error: "my error message")
|
60
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", metric_name: "false").match?(error: "my error message")
|
61
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", notes: "hey").match?(error: "my error message")
|
69
62
|
end
|
70
63
|
|
71
64
|
should "provide exception details" do
|
72
|
-
exception_description = ExceptionDescription.new(:filter1, error: "my error message", notes: "hey"
|
65
|
+
exception_description = ExceptionDescription.new(:filter1, error: "my error message", notes: "hey")
|
73
66
|
|
74
|
-
expected = {"send_metric" => true, "metric_name" => "filter1", "notes" => "hey"}
|
67
|
+
expected = { "send_metric" => true, "metric_name" => "filter1", "notes" => "hey" }
|
75
68
|
|
76
69
|
assert_equal expected, exception_description.exception_data
|
77
70
|
end
|
@@ -80,12 +73,10 @@ module ExceptionHandling
|
|
80
73
|
mobi = "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for 'mcc@mobistreak.com'"
|
81
74
|
credit = "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for 'damon@thecreditpros.com'"
|
82
75
|
|
83
|
-
exception_description = ExceptionDescription.new(:filter1, error: "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for '(mcc\@mobistreak|damon\@thecreditpros).com'"
|
76
|
+
exception_description = ExceptionDescription.new(:filter1, error: "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for '(mcc\@mobistreak|damon\@thecreditpros).com'")
|
84
77
|
assert exception_description.match?(error: mobi), "does not match mobi"
|
85
78
|
assert exception_description.match?(error: credit), "does not match credit"
|
86
79
|
end
|
87
|
-
|
88
80
|
end
|
89
|
-
|
90
81
|
end
|
91
82
|
end
|
@@ -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
|
|
@@ -228,10 +230,12 @@ module ExceptionHandling
|
|
228
230
|
"session" => {
|
229
231
|
"key" => "session_key",
|
230
232
|
"data" => { "username" => "jsmith", "id" => "session_key" },
|
231
|
-
"to_s" => "data:\n id: session_key\n username: jsmith\nkey: session_key\n"
|
233
|
+
"to_s" => "data:\n id: session_key\n username: jsmith\nkey: session_key\n"
|
234
|
+
},
|
232
235
|
"environment" => {
|
233
236
|
"SERVER_NAME" => "exceptional.com",
|
234
|
-
"to_s" => "SERVER_NAME: exceptional.com\n"
|
237
|
+
"to_s" => "SERVER_NAME: exceptional.com\n"
|
238
|
+
},
|
235
239
|
"request" => {
|
236
240
|
"params" => { "advertiser_id" => 435, "controller" => "dummy", "action" => "fail" },
|
237
241
|
"rails_root" => "Rails.root not defined. Is this a test environment?",
|
@@ -249,11 +253,11 @@ module ExceptionHandling
|
|
249
253
|
@controller.request.parameters[:user] = { "password" => "also super secret", "password_confirmation" => "also super secret" }
|
250
254
|
exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, @controller)
|
251
255
|
expected_params = {
|
252
|
-
"password"
|
256
|
+
"password" => "[FILTERED]",
|
253
257
|
"advertiser_id" => 435, "controller" => "dummy",
|
254
|
-
"action"
|
255
|
-
"user"
|
256
|
-
"password"
|
258
|
+
"action" => "fail",
|
259
|
+
"user" => {
|
260
|
+
"password" => "[FILTERED]",
|
257
261
|
"password_confirmation" => "[FILTERED]"
|
258
262
|
}
|
259
263
|
}
|
@@ -296,7 +300,7 @@ module ExceptionHandling
|
|
296
300
|
end
|
297
301
|
|
298
302
|
should "log info if the custom data hook results in a nil message exception" do
|
299
|
-
ExceptionHandling.custom_data_hook =
|
303
|
+
ExceptionHandling.custom_data_hook = ->(_data) do
|
300
304
|
raise_exception_with_nil_message
|
301
305
|
end
|
302
306
|
log_info_messages = []
|
@@ -345,9 +349,40 @@ module ExceptionHandling
|
|
345
349
|
end
|
346
350
|
end
|
347
351
|
|
352
|
+
context "controller_name" do
|
353
|
+
setup do
|
354
|
+
@exception = StandardError.new('something went wrong')
|
355
|
+
@timestamp = Time.now
|
356
|
+
@exception_context = {
|
357
|
+
'rack.session' => {
|
358
|
+
user_id: 23,
|
359
|
+
user_name: 'John'
|
360
|
+
},
|
361
|
+
'SERVER_NAME' => 'exceptional.com'
|
362
|
+
}
|
363
|
+
end
|
364
|
+
|
365
|
+
should "return controller_name when controller is present" do
|
366
|
+
env = { server: 'fe98' }
|
367
|
+
parameters = { controller: 'some_controller' }
|
368
|
+
session = { username: 'smith' }
|
369
|
+
request_uri = "host/path"
|
370
|
+
controller = create_dummy_controller(env, parameters, session, request_uri)
|
371
|
+
exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller)
|
372
|
+
|
373
|
+
assert_equal 'some_controller', exception_info.controller_name
|
374
|
+
end
|
375
|
+
|
376
|
+
should "return an empty string when controller is not present" do
|
377
|
+
exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
|
378
|
+
|
379
|
+
assert_equal '', exception_info.controller_name
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
348
383
|
context "send_to_honeybadger?" do
|
349
384
|
should "be enabled when Honeybadger is defined and exception is not in the filter list" do
|
350
|
-
stub(ExceptionHandling).
|
385
|
+
stub(ExceptionHandling).honeybadger_defined? { true }
|
351
386
|
exception = StandardError.new("something went wrong")
|
352
387
|
exception_info = ExceptionInfo.new(exception, {}, Time.now)
|
353
388
|
assert_nil exception_info.exception_description
|
@@ -355,7 +390,7 @@ module ExceptionHandling
|
|
355
390
|
end
|
356
391
|
|
357
392
|
should "be enabled when Honeybadger is defined and exception is on the filter list with the flag turned on" do
|
358
|
-
stub(ExceptionHandling).
|
393
|
+
stub(ExceptionHandling).honeybadger_defined? { true }
|
359
394
|
exception = StandardError.new("No route matches")
|
360
395
|
exception_info = ExceptionInfo.new(exception, {}, Time.now)
|
361
396
|
assert_not_nil exception_info.exception_description
|
@@ -364,7 +399,7 @@ module ExceptionHandling
|
|
364
399
|
end
|
365
400
|
|
366
401
|
should "be disabled when Honeybadger is defined and exception is on the filter list with the flag turned off" do
|
367
|
-
stub(ExceptionHandling).
|
402
|
+
stub(ExceptionHandling).honeybadger_defined? { true }
|
368
403
|
exception = StandardError.new("No route matches")
|
369
404
|
exception_info = ExceptionInfo.new(exception, {}, Time.now)
|
370
405
|
assert_not_nil exception_info.exception_description
|
@@ -373,7 +408,7 @@ module ExceptionHandling
|
|
373
408
|
end
|
374
409
|
|
375
410
|
should "be disabled when Honeybadger is not defined" do
|
376
|
-
stub(ExceptionHandling).
|
411
|
+
stub(ExceptionHandling).honeybadger_defined? { false }
|
377
412
|
exception = StandardError.new("something went wrong")
|
378
413
|
exception_info = ExceptionInfo.new(exception, {}, Time.now)
|
379
414
|
assert_nil exception_info.exception_description
|
@@ -390,10 +425,11 @@ module ExceptionHandling
|
|
390
425
|
controller = create_dummy_controller(env, parameters, session, request_uri)
|
391
426
|
stub(ExceptionHandling).server_name { "invoca_fe98" }
|
392
427
|
|
393
|
-
exception = StandardError.new("Some
|
428
|
+
exception = StandardError.new("Some Exception")
|
394
429
|
exception.set_backtrace([
|
395
|
-
|
396
|
-
|
430
|
+
"test/unit/exception_handling_test.rb:847:in `exception_1'",
|
431
|
+
"test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
|
432
|
+
])
|
397
433
|
exception_context = { "SERVER_NAME" => "exceptional.com" }
|
398
434
|
data_callback = ->(data) do
|
399
435
|
data[:scm_revision] = "5b24eac37aaa91f5784901e9aabcead36fd9df82"
|
@@ -415,46 +451,49 @@ module ExceptionHandling
|
|
415
451
|
request: {
|
416
452
|
"params" => { "advertiser_id" => 435 },
|
417
453
|
"rails_root" => "Rails.root not defined. Is this a test environment?",
|
418
|
-
"url" => "host/path"
|
454
|
+
"url" => "host/path"
|
455
|
+
},
|
419
456
|
session: {
|
420
457
|
"key" => nil,
|
421
|
-
"data" => { "username" => "jsmith" }
|
458
|
+
"data" => { "username" => "jsmith" }
|
459
|
+
},
|
422
460
|
environment: {
|
423
|
-
"SERVER_NAME" => "exceptional.com"
|
461
|
+
"SERVER_NAME" => "exceptional.com"
|
462
|
+
},
|
424
463
|
backtrace: [
|
425
464
|
"test/unit/exception_handling_test.rb:847:in `exception_1'",
|
426
|
-
"test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
|
465
|
+
"test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
|
466
|
+
],
|
427
467
|
event_response: "Event successfully received"
|
428
468
|
}
|
429
469
|
assert_equal_with_diff expected_data, exception_info.honeybadger_context_data
|
430
470
|
end
|
431
471
|
|
432
|
-
[
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
exception_info = ExceptionInfo.new(exception, exception_context, Time.now)
|
472
|
+
[['Hash', { 'cookie' => 'cookie_context' }],
|
473
|
+
['String', 'Entering Error State'],
|
474
|
+
['Array', ['Error1', 'Error2']]].each do |klass, value|
|
475
|
+
should "extract context from exception_context when it is a #{klass}" do
|
476
|
+
exception = StandardError.new("Exception")
|
477
|
+
exception_context = value
|
478
|
+
exception_info = ExceptionInfo.new(exception, exception_context, Time.now)
|
440
479
|
|
441
|
-
|
442
|
-
|
443
|
-
|
480
|
+
assert_equal klass, value.class.name
|
481
|
+
assert_equal value, exception_info.honeybadger_context_data[:exception_context]
|
482
|
+
end
|
444
483
|
end
|
445
484
|
|
446
485
|
should "filter out sensitive data from exception context such as [password, password_confirmation, oauth_token]" do
|
447
486
|
sensitive_data = {
|
448
|
-
"password"
|
487
|
+
"password" => "super_secret",
|
449
488
|
"password_confirmation" => "super_secret_confirmation",
|
450
|
-
"oauth_token"
|
489
|
+
"oauth_token" => "super_secret_oauth_token"
|
451
490
|
}
|
452
491
|
|
453
492
|
exception = StandardError.new("boom")
|
454
493
|
exception_context = {
|
455
494
|
"SERVER_NAME" => "exceptional.com",
|
456
|
-
"one_layer"
|
457
|
-
"two_layers"
|
495
|
+
"one_layer" => sensitive_data,
|
496
|
+
"two_layers" => {
|
458
497
|
"sensitive_data" => sensitive_data
|
459
498
|
},
|
460
499
|
"rack.request.form_vars" => "username=investor%40invoca.com&password=my_special_password&commit=Log+In",
|
@@ -468,8 +507,8 @@ module ExceptionHandling
|
|
468
507
|
expected_sensitive_data = ["password", "password_confirmation", "oauth_token"].build_hash { |key| [key, "[FILTERED]"] }
|
469
508
|
expected_exception_context = {
|
470
509
|
"SERVER_NAME" => "exceptional.com",
|
471
|
-
"one_layer"
|
472
|
-
"two_layers"
|
510
|
+
"one_layer" => expected_sensitive_data,
|
511
|
+
"two_layers" => {
|
473
512
|
"sensitive_data" => expected_sensitive_data
|
474
513
|
},
|
475
514
|
"rack.request.form_vars" => "username=investor%40invoca.com&password=[FILTERED]&commit=Log+In",
|
@@ -491,7 +530,7 @@ module ExceptionHandling
|
|
491
530
|
end
|
492
531
|
|
493
532
|
def prepare_data(data)
|
494
|
-
data.each do |
|
533
|
+
data.each do |_key, section|
|
495
534
|
if section.is_a?(Hash)
|
496
535
|
section.delete(:to_s)
|
497
536
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper', __dir__)
|
2
4
|
|
3
5
|
module ExceptionHandling
|
4
6
|
class HoneybadgerCallbacksTest < ActiveSupport::TestCase
|
@@ -34,6 +36,24 @@ module ExceptionHandling
|
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
39
|
+
class TestRaiseOnInspect < TestPoroWithAttribute
|
40
|
+
def inspect
|
41
|
+
raise "some error"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class TestRaiseOnInspectWithId < TestRaiseOnInspect
|
46
|
+
def id
|
47
|
+
123
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class TestRaiseOnInspectWithToPk < TestRaiseOnInspect
|
52
|
+
def to_pk
|
53
|
+
"SomeRecord-123"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
37
57
|
context "register_callbacks" do
|
38
58
|
should "store the callbacks in the honeybadger object" do
|
39
59
|
HoneybadgerCallbacks.register_callbacks
|
@@ -46,37 +66,54 @@ module ExceptionHandling
|
|
46
66
|
should "not inspect String, Hash, Array, Set, Numeric, TrueClass, FalseClass, NilClass" do
|
47
67
|
[
|
48
68
|
['test', String],
|
49
|
-
[{a: 1}, Hash],
|
50
|
-
[[1,2], Array],
|
51
|
-
[Set.new([1,2]), Set],
|
69
|
+
[{ a: 1 }, Hash],
|
70
|
+
[[1, 2], Array],
|
71
|
+
[Set.new([1, 2]), Set],
|
52
72
|
[4.5, Numeric],
|
53
73
|
[true, TrueClass],
|
54
74
|
[false, FalseClass],
|
55
75
|
[nil, NilClass]
|
56
76
|
].each do |object, expected_class|
|
57
|
-
result = HoneybadgerCallbacks.
|
77
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, object, [])
|
58
78
|
assert result.is_a?(expected_class), "Expected #{expected_class.name} but got #{result.class.name}"
|
59
79
|
end
|
60
80
|
end
|
61
81
|
|
62
82
|
should "inspect other classes" do
|
63
|
-
result = HoneybadgerCallbacks.
|
83
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithAttribute.new, ['password'])
|
64
84
|
assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithAttribute:.* @test_attribute="test">/, result)
|
65
85
|
end
|
66
86
|
|
87
|
+
context "when inspect raises exceptions" do
|
88
|
+
should "handle exceptions for objects" do
|
89
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestRaiseOnInspect.new, ['password'])
|
90
|
+
assert_equal "#<ExceptionHandling::HoneybadgerCallbacksTest::TestRaiseOnInspect [error 'RuntimeError: some error' while calling #inspect]>", result
|
91
|
+
end
|
92
|
+
|
93
|
+
should "handle exceptions for objects responding to id" do
|
94
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestRaiseOnInspectWithId.new, ['password'])
|
95
|
+
assert_equal "#<ExceptionHandling::HoneybadgerCallbacksTest::TestRaiseOnInspectWithId @id=123 [error 'RuntimeError: some error' while calling #inspect]>", result
|
96
|
+
end
|
97
|
+
|
98
|
+
should "handle exceptions for objects responding to to_pik" do
|
99
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestRaiseOnInspectWithToPk.new, ['password'])
|
100
|
+
assert_equal "#<ExceptionHandling::HoneybadgerCallbacksTest::TestRaiseOnInspectWithToPk @pk=SomeRecord-123 [error 'RuntimeError: some error' while calling #inspect]>", result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
67
104
|
context "not inspect objects that contain filter keys" do
|
68
105
|
should "use to_pk if available, even if id is available" do
|
69
|
-
result = HoneybadgerCallbacks.
|
106
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithFilteredAttributePkAndId.new, ['password'])
|
70
107
|
assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithFilteredAttributePkAndId @pk=TestPoroWithFilteredAttributePkAndId_1, \[FILTERED\]>/, result)
|
71
108
|
end
|
72
109
|
|
73
110
|
should "use id if to_pk is not available" do
|
74
|
-
result = HoneybadgerCallbacks.
|
111
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithFilteredAttributeAndId.new, ['password'])
|
75
112
|
assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithFilteredAttributeAndId @id=1, \[FILTERED\]>/, result)
|
76
113
|
end
|
77
114
|
|
78
115
|
should "print the object name if no id or to_pk" do
|
79
|
-
result = HoneybadgerCallbacks.
|
116
|
+
result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithFilteredAttribute.new, ['password'])
|
80
117
|
assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithFilteredAttribute \[FILTERED\]>/, result)
|
81
118
|
end
|
82
119
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper', __dir__)
|
2
4
|
|
3
5
|
module ExceptionHandling
|
4
6
|
class LogErrorStubTest < ActiveSupport::TestCase
|
@@ -18,7 +20,7 @@ module ExceptionHandling
|
|
18
20
|
begin
|
19
21
|
ExceptionHandling.log_error("Something happened")
|
20
22
|
flunk
|
21
|
-
rescue Exception => ex #LogErrorStub::UnexpectedExceptionLogged => ex
|
23
|
+
rescue Exception => ex # LogErrorStub::UnexpectedExceptionLogged => ex
|
22
24
|
assert ex.to_s.starts_with?("StandardError: Something happened"), ex.to_s
|
23
25
|
end
|
24
26
|
|
@@ -28,8 +30,8 @@ module ExceptionHandling
|
|
28
30
|
rescue => ex
|
29
31
|
begin
|
30
32
|
ExceptionHandling.log_error(ex)
|
31
|
-
rescue LogErrorStub::UnexpectedExceptionLogged =>
|
32
|
-
assert
|
33
|
+
rescue LogErrorStub::UnexpectedExceptionLogged => ex
|
34
|
+
assert ex.to_s.starts_with?("RaisedError: This should raise"), ex.to_s
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper', __dir__)
|
2
4
|
|
3
5
|
module ExceptionHandling
|
4
6
|
class MailerTest < ActionMailer::TestCase
|
@@ -18,14 +20,6 @@ module ExceptionHandling
|
|
18
20
|
ExceptionHandling.escalation_recipients = ['test_escalation@invoca.com']
|
19
21
|
end
|
20
22
|
|
21
|
-
should "deliver" do
|
22
|
-
#ActionMailer::Base.delivery_method = :smtp
|
23
|
-
result = ExceptionHandling::Mailer.exception_notification({ :error => "Test Error."}).deliver_now
|
24
|
-
assert_match(/Test Error./, result.body.to_s)
|
25
|
-
assert_equal_with_diff ['test_exception@invoca.com'], result.to
|
26
|
-
assert_emails 1
|
27
|
-
end
|
28
|
-
|
29
23
|
context "log_parser_exception_notification" do
|
30
24
|
should "send with string" do
|
31
25
|
result = ExceptionHandling::Mailer.log_parser_exception_notification("This is my fake error", "My Fake Subj").deliver_now
|
@@ -46,7 +40,7 @@ module ExceptionHandling
|
|
46
40
|
ExceptionHandling.email_environment = 'Staging Full'
|
47
41
|
ExceptionHandling.server_name = 'test-fe3'
|
48
42
|
|
49
|
-
ExceptionHandling::Mailer.escalation_notification("Your Favorite <b>Feature<b> Failed", :
|
43
|
+
ExceptionHandling::Mailer.escalation_notification("Your Favorite <b>Feature<b> Failed", error_string: "It failed because of an error\n <i>More Info<i>", timestamp: 1234567).deliver_now
|
50
44
|
|
51
45
|
assert_emails 1
|
52
46
|
result = ActionMailer::Base.deliveries.last
|
@@ -56,18 +50,18 @@ module ExceptionHandling
|
|
56
50
|
assert_equal "Staging Full Escalation: Your Favorite <b>Feature<b> Failed", result.subject
|
57
51
|
assert_select "title", "Exception Escalation"
|
58
52
|
assert_select "html" do
|
59
|
-
assert_select "body br", { :
|
53
|
+
assert_select "body br", { count: 4 }, result.body.to_s # plus 1 for the multiline summary
|
60
54
|
assert_select "body h3", "Your Favorite <b>Feature<b> Failed", result.body.to_s
|
61
55
|
assert_select "body", /1234567/
|
62
56
|
assert_select "body", /It failed because of an error/
|
63
57
|
assert_select "body", /\n <i>More Info<i>/
|
64
58
|
assert_select "body", /test-fe3/
|
65
|
-
#assert_select "body", /#{Web::Application::GIT_REVISION}/
|
59
|
+
# assert_select "body", /#{Web::Application::GIT_REVISION}/
|
66
60
|
end
|
67
61
|
end
|
68
62
|
|
69
63
|
should "use defaults for missing fields" do
|
70
|
-
result = ExceptionHandling::Mailer.escalation_notification("Your Favorite Feature Failed", :
|
64
|
+
result = ExceptionHandling::Mailer.escalation_notification("Your Favorite Feature Failed", error_string: "It failed because of an error\n More Info")
|
71
65
|
@body_html = Nokogiri::HTML(result.body.to_s)
|
72
66
|
|
73
67
|
assert_equal_with_diff ['test_escalation@invoca.com'], result.to
|
@@ -93,7 +87,7 @@ module ExceptionHandling
|
|
93
87
|
ExceptionHandling.production_support_recipients = recipients
|
94
88
|
ExceptionHandling.last_exception_timestamp = Time.now.to_i
|
95
89
|
|
96
|
-
mock(ExceptionHandling).
|
90
|
+
mock(ExceptionHandling).escalate(subject, exception, Time.now.to_i, recipients)
|
97
91
|
ExceptionHandling.escalate_to_production_support(exception, subject)
|
98
92
|
end
|
99
93
|
end
|
@@ -1,9 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper', __dir__)
|
2
4
|
|
3
5
|
require "exception_handling/testing"
|
4
6
|
|
5
7
|
module ExceptionHandling
|
6
8
|
class MethodsTest < ActiveSupport::TestCase
|
9
|
+
include ExceptionHelpers
|
7
10
|
|
8
11
|
def dont_stub_log_error
|
9
12
|
true
|
@@ -25,13 +28,13 @@ module ExceptionHandling
|
|
25
28
|
end
|
26
29
|
|
27
30
|
should "use the current_controller when available" do
|
28
|
-
|
31
|
+
capture_notifications
|
32
|
+
|
29
33
|
mock(ExceptionHandling.logger).fatal(/blah/, anything)
|
30
34
|
@controller.simulate_around_filter do
|
31
|
-
ExceptionHandling.log_error(
|
32
|
-
|
33
|
-
assert_match(
|
34
|
-
# assert_match( Username.first.username.to_s, mail.body.to_s ) if defined?(Username)
|
35
|
+
ExceptionHandling.log_error(ArgumentError.new("blah"))
|
36
|
+
assert_equal 1, sent_notifications.size, sent_notifications.inspect
|
37
|
+
assert_match(@controller.request.request_uri, sent_notifications.last.enhanced_data['request'].to_s)
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|