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,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
|
|