exception_handling 2.16.0 → 3.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
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 +114 -109
  6. data/README.md +21 -61
  7. data/Rakefile +11 -8
  8. data/exception_handling.gemspec +10 -12
  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 -95
  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} +42 -63
  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 +60 -100
  29. data/.github/CODEOWNERS +0 -1
  30. data/.github/workflows/pipeline.yml +0 -40
  31. data/.rspec +0 -3
  32. data/.tool-versions +0 -1
  33. data/Appraisals +0 -19
  34. data/CHANGELOG.md +0 -136
  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 -1361
  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