exception_handling 2.17.0.pre.tstarck.1 → 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.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -3
  3. data/.ruby-version +1 -1
  4. data/Gemfile +16 -12
  5. data/Gemfile.lock +138 -153
  6. data/README.md +21 -90
  7. data/Rakefile +11 -8
  8. data/exception_handling.gemspec +10 -14
  9. data/lib/exception_handling/exception_info.rb +11 -15
  10. data/lib/exception_handling/honeybadger_callbacks.rb +59 -0
  11. data/lib/exception_handling/log_stub_error.rb +1 -2
  12. data/lib/exception_handling/methods.rb +53 -6
  13. data/lib/exception_handling/testing.rb +10 -20
  14. data/lib/exception_handling/version.rb +1 -1
  15. data/lib/exception_handling.rb +34 -135
  16. data/semaphore_ci/setup.sh +3 -0
  17. data/{spec → test}/helpers/exception_helpers.rb +2 -2
  18. data/{spec/spec_helper.rb → test/test_helper.rb} +45 -75
  19. data/test/unit/exception_handling/exception_catalog_test.rb +85 -0
  20. data/test/unit/exception_handling/exception_description_test.rb +82 -0
  21. data/{spec/unit/exception_handling/exception_info_spec.rb → test/unit/exception_handling/exception_info_test.rb} +114 -170
  22. data/test/unit/exception_handling/honeybadger_callbacks_test.rb +122 -0
  23. data/{spec/unit/exception_handling/log_error_stub_spec.rb → test/unit/exception_handling/log_error_stub_test.rb} +22 -38
  24. data/{spec/unit/exception_handling/mailer_spec.rb → test/unit/exception_handling/mailer_test.rb} +18 -17
  25. data/test/unit/exception_handling/methods_test.rb +84 -0
  26. data/test/unit/exception_handling/sensu_test.rb +52 -0
  27. data/test/unit/exception_handling_test.rb +1109 -0
  28. metadata +59 -99
  29. data/.github/CODEOWNERS +0 -1
  30. data/.github/workflows/pipeline.yml +0 -36
  31. data/.rspec +0 -3
  32. data/.tool-versions +0 -1
  33. data/Appraisals +0 -19
  34. data/CHANGELOG.md +0 -149
  35. data/gemfiles/rails_5.gemfile +0 -18
  36. data/gemfiles/rails_6.gemfile +0 -18
  37. data/gemfiles/rails_7.gemfile +0 -18
  38. data/lib/exception_handling/escalate_callback.rb +0 -19
  39. data/lib/exception_handling/logging_methods.rb +0 -27
  40. data/spec/rake_test_warning_false.rb +0 -20
  41. data/spec/unit/exception_handling/escalate_callback_spec.rb +0 -81
  42. data/spec/unit/exception_handling/exception_catalog_spec.rb +0 -85
  43. data/spec/unit/exception_handling/exception_description_spec.rb +0 -82
  44. data/spec/unit/exception_handling/logging_methods_spec.rb +0 -38
  45. data/spec/unit/exception_handling/methods_spec.rb +0 -105
  46. data/spec/unit/exception_handling/sensu_spec.rb +0 -51
  47. data/spec/unit/exception_handling_spec.rb +0 -1465
  48. /data/{spec → test}/helpers/controller_helpers.rb +0 -0
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'exception_handling/escalate_callback'
4
- require File.expand_path('../../spec_helper', __dir__)
5
-
6
- module ExceptionHandling
7
- describe EscalateCallback do
8
- before do
9
- class TestGem
10
- class << self
11
- attr_accessor :logger
12
- end
13
- include Escalate.mixin
14
- end
15
- TestGem.logger = logger
16
- Escalate.clear_on_escalate_callbacks
17
- end
18
-
19
- after do
20
- Escalate.clear_on_escalate_callbacks
21
- end
22
-
23
- let(:exception) do
24
- raise "boom!"
25
- rescue => ex
26
- ex
27
- end
28
- let(:location_message) { "happened in TestGem" }
29
- let(:context_hash) { { cuid: 'AABBCD' } }
30
- let(:logger) { double("logger") }
31
-
32
- describe '.register_if_configured!' do
33
- context 'when already configured' do
34
- before do
35
- @original_logger = ExceptionHandling.logger
36
- ExceptionHandling.logger = ::Logger.new('/dev/null')
37
- end
38
-
39
- after do
40
- ExceptionHandling.logger = @original_logger
41
- end
42
-
43
- it 'registers a callback' do
44
- EscalateCallback.register_if_configured!
45
-
46
- expect(logger).to_not receive(:error)
47
- expect(logger).to_not receive(:fatal)
48
- expect(ExceptionHandling).to receive(:log_error).with(exception, location_message, context_hash)
49
-
50
- TestGem.escalate(exception, location_message, context: context_hash)
51
- end
52
- end
53
-
54
- context 'when not yet configured' do
55
- before do
56
- @original_logger = ExceptionHandling.logger
57
- ExceptionHandling.logger = nil
58
- end
59
-
60
- after do
61
- ExceptionHandling.logger = @original_logger
62
- end
63
-
64
- it 'registers a callback once the logger is set' do
65
- EscalateCallback.register_if_configured!
66
-
67
- expect(Escalate.on_escalate_callbacks).to be_empty
68
-
69
- ExceptionHandling.logger = ::Logger.new('/dev/null')
70
- expect(Escalate.on_escalate_callbacks).to_not be_empty
71
-
72
- expect(logger).to_not receive(:error)
73
- expect(logger).to_not receive(:fatal)
74
- expect(ExceptionHandling).to receive(:log_error).with(exception, location_message, context_hash)
75
-
76
- TestGem.escalate(exception, location_message, context: context_hash)
77
- end
78
- end
79
- end
80
- end
81
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- describe ExceptionCatalog do
7
-
8
- context "With stubbed yaml content" do
9
- before do
10
- filter_list = { exception1: { error: "my error message" },
11
- exception2: { error: "some other message", session: "misc data" } }
12
- allow(YAML).to receive(:load_file) { filter_list }
13
-
14
- # bump modified time up to get the above filter loaded
15
- allow(File).to receive(:mtime) { incrementing_mtime }
16
- end
17
-
18
- context "with loaded data" do
19
- before do
20
- allow(File).to receive(:mtime) { incrementing_mtime }
21
- @exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
22
- @exception_catalog.send :load_file
23
- end
24
-
25
- it "have loaded filters" do
26
- expect(@exception_catalog.instance_eval("@filters").size).to eq(2)
27
- end
28
-
29
- it "find messages in the catalog" do
30
- expect(!@exception_catalog.find(error: "Scott says unlikely to ever match")).to be_truthy
31
- end
32
-
33
- it "find matching data" do
34
- exception_description = @exception_catalog.find(error: "this is my error message, which should match something")
35
- expect(exception_description).to be_truthy
36
- expect(exception_description.filter_name).to eq(:exception1)
37
- end
38
- end
39
-
40
- it "write errors loading the yaml file directly to the log file" do
41
- @exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
42
-
43
- expect(ExceptionHandling).to receive(:log_error).never
44
- expect(ExceptionHandling).to receive(:write_exception_to_log).with(anything, "ExceptionCatalog#refresh_filters: ./config/exception_filters.yml", any_args)
45
- expect(@exception_catalog).to receive(:load_file) { raise "noooooo" }
46
-
47
- @exception_catalog.find({})
48
- end
49
- end
50
-
51
- context "with live yaml content" do
52
- before do
53
- @filename = File.expand_path('../../../config/exception_filters.yml', __dir__)
54
- @exception_catalog = ExceptionCatalog.new(@filename)
55
- expect do
56
- @exception_catalog.send :load_file
57
- end.not_to raise_error
58
- end
59
-
60
- it "load the filter data" do
61
- expect(!@exception_catalog.find(error: "Scott says unlikely to ever match")).to be_truthy
62
- expect(!@exception_catalog.find(error: "Scott says unlikely to ever match")).to be_truthy
63
- end
64
- end
65
-
66
- context "with no yaml content" do
67
- before do
68
- @exception_catalog = ExceptionCatalog.new(nil)
69
- end
70
-
71
- it "not load filter data" do
72
- expect(ExceptionHandling).to receive(:write_exception_to_log).with(any_args).never
73
- @exception_catalog.find(error: "Scott says unlikely to ever match")
74
- end
75
- end
76
-
77
- private
78
-
79
- def incrementing_mtime
80
- @mtime ||= Time.now
81
- @mtime += 1.day
82
- end
83
-
84
- end
85
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- describe ExceptionDescription do
7
-
8
- context "Filter" do
9
- it "allow direct matching of strings" do
10
- @f = ExceptionDescription.new(:filter1, error: "my error message")
11
- expect(@f.match?('error' => "my error message")).to be_truthy
12
- end
13
-
14
- it "allow direct matching of strings on with symbol keys" do
15
- @f = ExceptionDescription.new(:filter1, error: "my error message")
16
- expect(@f.match?(error: "my error message")).to be_truthy
17
- end
18
-
19
- it "allow wildcards to cross line boundries" do
20
- @f = ExceptionDescription.new(:filter1, error: "my error message.*with multiple lines")
21
- expect(@f.match?(error: "my error message\nwith more than one, with multiple lines")).to be_truthy
22
- end
23
-
24
- it "complain when no regexps have a value" do
25
- expect { ExceptionDescription.new(:filter1, error: nil) }.to raise_exception(ArgumentError, /has all blank regexes/)
26
- end
27
-
28
- it "report when an invalid key is passed" do
29
- expect { ExceptionDescription.new(:filter1, error: "my error message", not_a_parameter: false) }.to raise_exception(ArgumentError, "Unknown section: not_a_parameter")
30
- end
31
-
32
- it "allow send_to_honeybadger to be specified and have it disabled by default" do
33
- expect(!ExceptionDescription.new(:filter1, error: "my error message", send_to_honeybadger: false).send_to_honeybadger).to be_truthy
34
- expect(ExceptionDescription.new(:filter1, error: "my error message", send_to_honeybadger: true).send_to_honeybadger).to be_truthy
35
- expect(!ExceptionDescription.new(:filter1, error: "my error message").send_to_honeybadger).to be_truthy
36
- end
37
-
38
- it "allow send_metric to be configured" do
39
- expect(!ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).send_metric).to be_truthy
40
- expect(ExceptionDescription.new(:filter1, error: "my error message").send_metric).to be_truthy
41
- end
42
-
43
- it "provide metric name" do
44
- expect(ExceptionDescription.new(:filter1, error: "my error message").metric_name).to eq("filter1")
45
- expect(ExceptionDescription.new(:filter1, error: "my error message", metric_name: :some_other_metric_name).metric_name).to eq("some_other_metric_name")
46
- end
47
-
48
- it "replace spaces in metric name" do
49
- @f = ExceptionDescription.new(:"filter has spaces", error: "my error message")
50
- expect(@f.metric_name).to eq( "filter_has_spaces")
51
- end
52
-
53
- it "allow notes to be recorded" do
54
- expect(ExceptionDescription.new(:filter1, error: "my error message").notes).to be_nil
55
- expect(ExceptionDescription.new(:filter1, error: "my error message", notes: "a long string").notes).to eq("a long string")
56
- end
57
-
58
- it "not consider config options in the filter set" do
59
- expect(ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).match?(error: "my error message")).to be_truthy
60
- expect(ExceptionDescription.new(:filter1, error: "my error message", metric_name: "false").match?(error: "my error message")).to be_truthy
61
- expect(ExceptionDescription.new(:filter1, error: "my error message", notes: "hey").match?(error: "my error message")).to be_truthy
62
- end
63
-
64
- it "provide exception details" do
65
- exception_description = ExceptionDescription.new(:filter1, error: "my error message", notes: "hey")
66
-
67
- expected = { "send_metric" => true, "metric_name" => "filter1", "notes" => "hey" }
68
-
69
- expect(exception_description.exception_data).to eq( expected)
70
- end
71
-
72
- it "match multiple email addresses" do
73
- mobi = "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for 'mcc@mobistreak.com'"
74
- credit = "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for 'damon@thecreditpros.com'"
75
-
76
- exception_description = ExceptionDescription.new(:filter1, error: "ExceptionHandling::Warning: LoginAttempt::IPAddressLocked: failed login for '(mcc\@mobistreak|damon\@thecreditpros).com'")
77
- expect(exception_description.match?(error: mobi)).to be_truthy
78
- expect(exception_description.match?(error: credit)).to be_truthy
79
- end
80
- end
81
- end
82
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- require_relative '../../helpers/exception_helpers'
6
-
7
- require "exception_handling/testing"
8
-
9
- module ExceptionHandling
10
- describe LoggingMethods do
11
- include ExceptionHelpers
12
-
13
- def dont_stub_log_error
14
- true
15
- end
16
-
17
- context "ExceptionHandling::LoggingMethods" do
18
- before do
19
- @controller = Testing::LoggingMethodsControllerStub.new
20
- ExceptionHandling.stub_handler = nil
21
- end
22
-
23
- context "#log_warning" do
24
- it "be available to the controller" do
25
- klass = Class.new
26
- klass.include ExceptionHandling::LoggingMethods
27
- instance = klass.new
28
- expect(instance.methods.include?(:log_warning)).to eq(true)
29
- end
30
-
31
- it "call ExceptionHandling#log_warning" do
32
- expect(ExceptionHandling).to receive(:log_warning).with("Hi mom")
33
- @controller.send(:log_warning, "Hi mom")
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- require "exception_handling/testing"
6
- require_relative '../../helpers/exception_helpers.rb'
7
-
8
- module ExceptionHandling
9
- describe Methods do
10
- include ExceptionHelpers
11
-
12
- def dont_stub_log_error
13
- true
14
- end
15
-
16
- context "ExceptionHandling::Methods" do
17
- before do
18
- @controller = Testing::MethodsControllerStub.new
19
- ExceptionHandling.stub_handler = nil
20
- end
21
-
22
- it "set the around filter" do
23
- expect(Testing::MethodsControllerStub.around_filter_method).to eq(:set_current_controller)
24
- expect(ExceptionHandling.current_controller).to be_nil
25
- @controller.simulate_around_filter do
26
- expect(ExceptionHandling.current_controller).to eq(@controller)
27
- end
28
- expect(ExceptionHandling.current_controller).to be_nil
29
- end
30
-
31
- it "use the current_controller when available" do
32
- capture_notifications
33
-
34
- expect(ExceptionHandling.logger).to receive(:fatal).with(/blah/, any_args).at_least(:once)
35
- @controller.simulate_around_filter do
36
- ExceptionHandling.log_error(ArgumentError.new("blah"))
37
- expect(sent_notifications.size).to eq(1)
38
- expect(/#{Regexp.new(Regexp.escape(@controller.request.request_uri))}/).to match(sent_notifications.last.enhanced_data['request'].to_s)
39
- end
40
- end
41
-
42
- it "report long running controller action" do
43
- expect(@controller.send(:long_controller_action_timeout)).to eq(2)
44
- expect(ExceptionHandling).to receive(:log_error).with(/Long controller action detected in #{@controller.class.name.split("::").last}::test_action/)
45
- @controller.simulate_around_filter do
46
- sleep(3)
47
- end
48
- end
49
-
50
- it "not report long running controller actions if it is less than the timeout" do
51
- expect(@controller.send(:long_controller_action_timeout)).to eq(2)
52
- allow(ExceptionHandling).to receive(:log_error).and_return("Should not timeout")
53
- @controller.simulate_around_filter do
54
- sleep(1)
55
- end
56
- end
57
-
58
- it "default long running controller action(300/30 for test/prod)" do
59
- class DummyController
60
- include ExceptionHandling::Methods
61
- end
62
-
63
- controller = DummyController.new
64
-
65
- Rails.env = 'production'
66
- expect(controller.send(:long_controller_action_timeout)).to eq(30)
67
-
68
- Rails.env = 'test'
69
- expect(controller.send(:long_controller_action_timeout)).to eq(300)
70
- end
71
-
72
- context "#log_warning" do
73
- it "be available to the controller" do
74
- expect(@controller.methods.include?(:log_warning)).to eq(true)
75
- end
76
- end
77
-
78
- context "included deprecation" do
79
- before do
80
- mock_deprecation_3_0
81
- end
82
-
83
- it "deprecate when no around_filter in included hook" do
84
- k = Class.new
85
- k.include ExceptionHandling::Methods
86
- end
87
-
88
- it "deprecate controller around_filter in included hook" do
89
- controller = Class.new
90
- class << controller
91
- def around_filter(*)
92
- end
93
- end
94
- controller.include ExceptionHandling::Methods
95
- end
96
- end
97
-
98
- private
99
-
100
- def mock_deprecation_3_0
101
- expect(STDERR).to receive(:puts).with(/DEPRECATION WARNING: ExceptionHandling::Methods is deprecated and will be removed from exception_handling 3\.0/)
102
- end
103
- end
104
- end
105
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- describe Sensu do
7
- context "#generate_event" do
8
- it "create an event" do
9
- expect(ExceptionHandling::Sensu).to receive(:send_event).with({ 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
- it "add the sensu prefix" do
15
- ExceptionHandling.sensu_prefix = "cnn_"
16
-
17
- expect(ExceptionHandling::Sensu).to receive(:send_event).with({ 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
- it "allow the level to be set to critical" do
23
- expect(ExceptionHandling::Sensu).to receive(:send_event).with({ 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
- it "error if an invalid level is supplied" do
29
- expect(ExceptionHandling::Sensu).to_not receive(:send_event)
30
-
31
- expect do
32
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye", :hair_on_fire)
33
- end.to raise_exception(RuntimeError, /Invalid alert level/)
34
- end
35
- end
36
-
37
- context "#send_event" do
38
- before 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
- it "send event json to sensu client" do
44
- expect_any_instance_of(Addrinfo).to receive(:connect).with(any_args) { @socket }
45
- ExceptionHandling::Sensu.send_event(@event)
46
-
47
- expect(@socket.sent.first).to eq(@event.to_json)
48
- end
49
- end
50
- end
51
- end