exception_handling 3.0.pre.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/workflows/pipeline.yml +36 -0
  4. data/.gitignore +3 -0
  5. data/.rspec +3 -0
  6. data/.ruby-version +1 -1
  7. data/.tool-versions +1 -0
  8. data/Appraisals +13 -0
  9. data/CHANGELOG.md +150 -0
  10. data/Gemfile +10 -16
  11. data/Gemfile.lock +65 -128
  12. data/README.md +51 -19
  13. data/Rakefile +8 -11
  14. data/exception_handling.gemspec +11 -13
  15. data/gemfiles/rails_5.gemfile +16 -0
  16. data/gemfiles/rails_6.gemfile +16 -0
  17. data/gemfiles/rails_7.gemfile +16 -0
  18. data/lib/exception_handling/escalate_callback.rb +19 -0
  19. data/lib/exception_handling/exception_info.rb +15 -11
  20. data/lib/exception_handling/log_stub_error.rb +2 -1
  21. data/lib/exception_handling/logging_methods.rb +21 -0
  22. data/lib/exception_handling/testing.rb +9 -12
  23. data/lib/exception_handling/version.rb +1 -1
  24. data/lib/exception_handling.rb +83 -173
  25. data/{test → spec}/helpers/exception_helpers.rb +2 -2
  26. data/spec/rake_test_warning_false.rb +20 -0
  27. data/{test/test_helper.rb → spec/spec_helper.rb} +63 -66
  28. data/spec/unit/exception_handling/escalate_callback_spec.rb +81 -0
  29. data/spec/unit/exception_handling/exception_catalog_spec.rb +85 -0
  30. data/spec/unit/exception_handling/exception_description_spec.rb +82 -0
  31. data/{test/unit/exception_handling/exception_info_test.rb → spec/unit/exception_handling/exception_info_spec.rb} +170 -114
  32. data/{test/unit/exception_handling/log_error_stub_test.rb → spec/unit/exception_handling/log_error_stub_spec.rb} +38 -22
  33. data/spec/unit/exception_handling/logging_methods_spec.rb +38 -0
  34. data/spec/unit/exception_handling_spec.rb +1063 -0
  35. metadata +62 -91
  36. data/lib/exception_handling/honeybadger_callbacks.rb +0 -59
  37. data/lib/exception_handling/mailer.rb +0 -70
  38. data/lib/exception_handling/methods.rb +0 -101
  39. data/lib/exception_handling/sensu.rb +0 -28
  40. data/semaphore_ci/setup.sh +0 -3
  41. data/test/unit/exception_handling/exception_catalog_test.rb +0 -85
  42. data/test/unit/exception_handling/exception_description_test.rb +0 -82
  43. data/test/unit/exception_handling/honeybadger_callbacks_test.rb +0 -122
  44. data/test/unit/exception_handling/mailer_test.rb +0 -98
  45. data/test/unit/exception_handling/methods_test.rb +0 -84
  46. data/test/unit/exception_handling/sensu_test.rb +0 -52
  47. data/test/unit/exception_handling_test.rb +0 -1109
  48. data/views/exception_handling/mailer/escalate_custom.html.erb +0 -17
  49. data/views/exception_handling/mailer/escalation_notification.html.erb +0 -17
  50. data/views/exception_handling/mailer/log_parser_exception_notification.html.erb +0 -82
  51. /data/{test → spec}/helpers/controller_helpers.rb +0 -0
@@ -1,122 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../test_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- class HoneybadgerCallbacksTest < ActiveSupport::TestCase
7
-
8
- class TestPoroWithAttribute
9
- attr_reader :test_attribute
10
-
11
- def initialize
12
- @test_attribute = 'test'
13
- end
14
- end
15
-
16
- class TestPoroWithFilteredAttribute
17
- attr_reader :password
18
-
19
- def initialize
20
- @password = 'secret'
21
- end
22
- end
23
-
24
- class TestPoroWithFilteredAttributeAndId < TestPoroWithFilteredAttribute
25
- attr_reader :id
26
-
27
- def initialize
28
- super
29
- @id = 1
30
- end
31
- end
32
-
33
- class TestPoroWithFilteredAttributePkAndId < TestPoroWithFilteredAttributeAndId
34
- def to_pk
35
- 'TestPoroWithFilteredAttributePkAndId_1'
36
- end
37
- end
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
-
57
- context "register_callbacks" do
58
- should "store the callbacks in the honeybadger object" do
59
- HoneybadgerCallbacks.register_callbacks
60
- result = Honeybadger.config.local_variable_filter.call(:variable_name, 'test', [])
61
- assert_equal('test', result)
62
- end
63
- end
64
-
65
- context "local_variable_filter" do
66
- should "not inspect String, Hash, Array, Set, Numeric, TrueClass, FalseClass, NilClass" do
67
- [
68
- ['test', String],
69
- [{ a: 1 }, Hash],
70
- [[1, 2], Array],
71
- [Set.new([1, 2]), Set],
72
- [4.5, Numeric],
73
- [true, TrueClass],
74
- [false, FalseClass],
75
- [nil, NilClass]
76
- ].each do |object, expected_class|
77
- result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, object, [])
78
- assert result.is_a?(expected_class), "Expected #{expected_class.name} but got #{result.class.name}"
79
- end
80
- end
81
-
82
- should "inspect other classes" do
83
- result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithAttribute.new, ['password'])
84
- assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithAttribute:.* @test_attribute="test">/, result)
85
- end
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
-
104
- context "not inspect objects that contain filter keys" do
105
- should "use to_pk if available, even if id is available" do
106
- result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithFilteredAttributePkAndId.new, ['password'])
107
- assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithFilteredAttributePkAndId @pk=TestPoroWithFilteredAttributePkAndId_1, \[FILTERED\]>/, result)
108
- end
109
-
110
- should "use id if to_pk is not available" do
111
- result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithFilteredAttributeAndId.new, ['password'])
112
- assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithFilteredAttributeAndId @id=1, \[FILTERED\]>/, result)
113
- end
114
-
115
- should "print the object name if no id or to_pk" do
116
- result = HoneybadgerCallbacks.send(:local_variable_filter, :variable_name, TestPoroWithFilteredAttribute.new, ['password'])
117
- assert_match(/#<ExceptionHandling::HoneybadgerCallbacksTest::TestPoroWithFilteredAttribute \[FILTERED\]>/, result)
118
- end
119
- end
120
- end
121
- end
122
- end
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../test_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- class MailerTest < ActionMailer::TestCase
7
-
8
- include ::Rails::Dom::Testing::Assertions::SelectorAssertions
9
- tests ExceptionHandling::Mailer
10
-
11
- def dont_stub_log_error
12
- true
13
- end
14
-
15
- context "ExceptionHandling::Mailer" do
16
- setup do
17
- ExceptionHandling.email_environment = 'Test'
18
- ExceptionHandling.sender_address = %("Test Exception Mailer" <null_exception@invoca.com>)
19
- ExceptionHandling.exception_recipients = ['test_exception@invoca.com']
20
- ExceptionHandling.escalation_recipients = ['test_escalation@invoca.com']
21
- end
22
-
23
- context "log_parser_exception_notification" do
24
- should "send with string" do
25
- result = ExceptionHandling::Mailer.log_parser_exception_notification("This is my fake error", "My Fake Subj").deliver_now
26
- assert_equal "Test exception: My Fake Subj: This is my fake error", result.subject
27
- assert_match(/This is my fake error/, result.body.to_s)
28
- assert_emails 1
29
- end
30
- end
31
-
32
- context "escalation_notification" do
33
- setup do
34
- def document_root_element
35
- @body_html.root
36
- end
37
- end
38
-
39
- should "send all the information" do
40
- ExceptionHandling.email_environment = 'Staging Full'
41
- ExceptionHandling.server_name = 'test-fe3'
42
-
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
44
-
45
- assert_emails 1
46
- result = ActionMailer::Base.deliveries.last
47
- @body_html = Nokogiri::HTML(result.body.to_s)
48
- assert_equal_with_diff ['test_escalation@invoca.com'], result.to
49
- assert_equal ["Test Escalation Mailer <null_escalation@invoca.com>"], result[:from].formatted
50
- assert_equal "Staging Full Escalation: Your Favorite <b>Feature<b> Failed", result.subject
51
- assert_select "title", "Exception Escalation"
52
- assert_select "html" do
53
- assert_select "body br", { count: 4 }, result.body.to_s # plus 1 for the multiline summary
54
- assert_select "body h3", "Your Favorite <b>Feature<b> Failed", result.body.to_s
55
- assert_select "body", /1234567/
56
- assert_select "body", /It failed because of an error/
57
- assert_select "body", /\n <i>More Info<i>/
58
- assert_select "body", /test-fe3/
59
- # assert_select "body", /#{Web::Application::GIT_REVISION}/
60
- end
61
- end
62
-
63
- should "use defaults for missing fields" do
64
- result = ExceptionHandling::Mailer.escalation_notification("Your Favorite Feature Failed", error_string: "It failed because of an error\n More Info")
65
- @body_html = Nokogiri::HTML(result.body.to_s)
66
-
67
- assert_equal_with_diff ['test_escalation@invoca.com'], result.to
68
- assert_equal ["null_escalation@invoca.com"], result.from
69
- assert_equal 'Test Escalation: Your Favorite Feature Failed', result.subject
70
- assert_select "html" do
71
- assert_select "body i", true, result.body.to_s do |is|
72
- assert_select is, "i", 'no error #'
73
- end
74
- end
75
- end
76
-
77
- context "ExceptionHandling.escalate_to_production_support" do
78
- setup do
79
- Time.now_override = Time.parse('1986-5-21 4:17 am UTC')
80
- end
81
-
82
- should "notify production support" do
83
- subject = "Runtime Error found!"
84
- exception = RuntimeError.new("Test")
85
- recipients = ["prodsupport@example.com"]
86
-
87
- ExceptionHandling.production_support_recipients = recipients
88
- ExceptionHandling.last_exception_timestamp = Time.now.to_i
89
-
90
- mock(ExceptionHandling).escalate(subject, exception, Time.now.to_i, recipients)
91
- ExceptionHandling.escalate_to_production_support(exception, subject)
92
- end
93
- end
94
- end
95
- end
96
-
97
- end
98
- end
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../test_helper', __dir__)
4
-
5
- require "exception_handling/testing"
6
-
7
- module ExceptionHandling
8
- class MethodsTest < ActiveSupport::TestCase
9
- include ExceptionHelpers
10
-
11
- def dont_stub_log_error
12
- true
13
- end
14
-
15
- context "ExceptionHandling.Methods" do
16
- setup do
17
- @controller = Testing::ControllerStub.new
18
- ExceptionHandling.stub_handler = nil
19
- end
20
-
21
- should "set the around filter" do
22
- assert_equal :set_current_controller, Testing::ControllerStub.around_filter_method
23
- assert_nil ExceptionHandling.current_controller
24
- @controller.simulate_around_filter do
25
- assert_equal @controller, ExceptionHandling.current_controller
26
- end
27
- assert_nil ExceptionHandling.current_controller
28
- end
29
-
30
- should "use the current_controller when available" do
31
- capture_notifications
32
-
33
- mock(ExceptionHandling.logger).fatal(/blah/, anything)
34
- @controller.simulate_around_filter do
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)
38
- end
39
- end
40
-
41
- should "report long running controller action" do
42
- assert_equal 2, @controller.send(:long_controller_action_timeout)
43
- mock(ExceptionHandling).log_error(/Long controller action detected in #{@controller.class.name.split("::").last}::test_action/, anything, anything)
44
- @controller.simulate_around_filter do
45
- sleep(3)
46
- end
47
- end
48
-
49
- should "not report long running controller actions if it is less than the timeout" do
50
- assert_equal 2, @controller.send(:long_controller_action_timeout)
51
- stub(ExceptionHandling).log_error { flunk "Should not timeout" }
52
- @controller.simulate_around_filter do
53
- sleep(1)
54
- end
55
- end
56
-
57
- should "default long running controller action(300/30 for test/prod)" do
58
- class DummyController
59
- include ExceptionHandling::Methods
60
- end
61
-
62
- controller = DummyController.new
63
-
64
- Rails.env = 'production'
65
- assert_equal 30, controller.send(:long_controller_action_timeout)
66
-
67
- Rails.env = 'test'
68
- assert_equal 300, controller.send(:long_controller_action_timeout)
69
- end
70
-
71
- context "#log_warning" do
72
- should "be available to the controller" do
73
- assert_equal true, @controller.methods.include?(:log_warning)
74
- end
75
-
76
- should "call ExceptionHandling#log_warning" do
77
- mock(ExceptionHandling).log_warning("Hi mom")
78
- @controller.send(:log_warning, "Hi mom")
79
- end
80
- end
81
- end
82
-
83
- end
84
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../test_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- class SensuTest < ActiveSupport::TestCase
7
- context "#generate_event" do
8
- should "create an event" do
9
- mock(ExceptionHandling::Sensu).send_event(name: "world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 1)
10
-
11
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye")
12
- end
13
-
14
- should "add the sensu prefix" do
15
- ExceptionHandling.sensu_prefix = "cnn_"
16
-
17
- mock(ExceptionHandling::Sensu).send_event(name: "cnn_world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 1)
18
-
19
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye")
20
- end
21
-
22
- should "allow the level to be set to critical" do
23
- mock(ExceptionHandling::Sensu).send_event(name: "world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 2)
24
-
25
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye", :critical)
26
- end
27
-
28
- should "error if an invalid level is supplied" do
29
- dont_allow(ExceptionHandling::Sensu).send_event
30
-
31
- assert_raise(RuntimeError, "Invalid alert level") do
32
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye", :hair_on_fire)
33
- end
34
- end
35
- end
36
-
37
- context "#send_event" do
38
- setup do
39
- @event = { name: "world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 1 }
40
- @socket = SocketStub.new
41
- end
42
-
43
- should "send event json to sensu client" do
44
- mock.any_instance_of(Addrinfo).connect.with_any_args { @socket }
45
-
46
- ExceptionHandling::Sensu.send_event(@event)
47
-
48
- assert_equal @event.to_json, @socket.sent.first
49
- end
50
- end
51
- end
52
- end