exception_handling 3.0.pre.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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