exception_handling 2.7.0.pre.1 → 2.8.1.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.jenkins/Jenkinsfile +24 -8
  4. data/.rspec +3 -0
  5. data/CHANGELOG.md +7 -2
  6. data/Gemfile +4 -4
  7. data/Gemfile.lock +66 -57
  8. data/Rakefile +7 -6
  9. data/gemfiles/rails_4.gemfile +4 -4
  10. data/gemfiles/rails_5.gemfile +4 -4
  11. data/gemfiles/rails_6.gemfile +4 -4
  12. data/lib/exception_handling.rb +3 -0
  13. data/lib/exception_handling/exception_info.rb +4 -3
  14. data/lib/exception_handling/log_stub_error.rb +2 -1
  15. data/lib/exception_handling/logging_methods.rb +1 -7
  16. data/lib/exception_handling/version.rb +1 -1
  17. data/{test → spec}/helpers/controller_helpers.rb +0 -0
  18. data/{test → spec}/helpers/exception_helpers.rb +2 -2
  19. data/{test → spec}/rake_test_warning_false.rb +0 -0
  20. data/{test/test_helper.rb → spec/spec_helper.rb} +50 -39
  21. data/spec/unit/exception_handling/exception_catalog_spec.rb +85 -0
  22. data/spec/unit/exception_handling/exception_description_spec.rb +82 -0
  23. data/{test/unit/exception_handling/exception_info_test.rb → spec/unit/exception_handling/exception_info_spec.rb} +125 -107
  24. data/{test/unit/exception_handling/honeybadger_callbacks_test.rb → spec/unit/exception_handling/honeybadger_callbacks_spec.rb} +20 -20
  25. data/{test/unit/exception_handling/log_error_stub_test.rb → spec/unit/exception_handling/log_error_stub_spec.rb} +38 -22
  26. data/{test/unit/exception_handling/logging_methods_test.rb → spec/unit/exception_handling/logging_methods_spec.rb} +8 -7
  27. data/{test/unit/exception_handling/mailer_test.rb → spec/unit/exception_handling/mailer_spec.rb} +17 -17
  28. data/spec/unit/exception_handling/methods_spec.rb +105 -0
  29. data/spec/unit/exception_handling/sensu_spec.rb +51 -0
  30. data/{test/unit/exception_handling_test.rb → spec/unit/exception_handling_spec.rb} +348 -329
  31. metadata +34 -33
  32. data/test/unit/exception_handling/exception_catalog_test.rb +0 -85
  33. data/test/unit/exception_handling/exception_description_test.rb +0 -82
  34. data/test/unit/exception_handling/methods_test.rb +0 -105
  35. data/test/unit/exception_handling/sensu_test.rb +0 -52
@@ -9,13 +9,7 @@ module ExceptionHandling
9
9
 
10
10
  protected
11
11
 
12
- delegate :log_error_rack, :log_warning, :log_info, :log_debug, :escalate_error, :escalate_warning, :ensure_escalation, :alert_warning, to: ExceptionHandling
13
-
14
- # TODO: delegate log_error as well
15
- def log_error(exception_or_string, exception_context = '')
16
- controller = self if respond_to?(:request) && respond_to?(:session)
17
- ExceptionHandling.log_error(exception_or_string, exception_context, controller)
18
- end
12
+ delegate :log_error_rack, :log_warning, :log_info, :log_debug, :escalate_error, :escalate_warning, :ensure_escalation, :alert_warning, :log_error, to: ExceptionHandling
19
13
 
20
14
  def ensure_safe(exception_context = "")
21
15
  yield
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ExceptionHandling
4
- VERSION = '2.7.0.pre.1'
4
+ VERSION = '2.8.1.pre.1'
5
5
  end
@@ -7,7 +7,7 @@ module ExceptionHelpers
7
7
 
8
8
  def exception_with_nil_message
9
9
  exception_with_nil_message = RuntimeError.new(nil)
10
- stub(exception_with_nil_message).message { nil }
10
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
11
11
  exception_with_nil_message
12
12
  end
13
13
 
@@ -15,6 +15,6 @@ module ExceptionHelpers
15
15
 
16
16
  def capture_notifications
17
17
  @sent_notifications = []
18
- stub(ExceptionHandling).send_exception_to_honeybadger(anything) { |exception_info| @sent_notifications << exception_info }
18
+ allow(ExceptionHandling).to receive(:send_exception_to_honeybadger).with(anything) { |exception_info| @sent_notifications << exception_info }
19
19
  end
20
20
  end
@@ -1,17 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support'
4
- require 'active_support/time'
5
- require 'active_support/test_case'
6
- require 'action_mailer'
7
- require 'action_dispatch'
8
- require 'shoulda'
9
- require 'rr'
10
- require 'minitest/autorun'
11
- require "minitest/reporters"
12
-
13
- junit_ouptut_dir = ENV["JUNIT_OUTPUT_DIR"].presence || "test/reports"
14
- Minitest::Reporters.use!([Minitest::Reporters::DefaultReporter.new, Minitest::Reporters::JUnitReporter.new(junit_ouptut_dir)])
3
+ require 'rspec'
4
+ require 'rspec/mocks'
5
+ require 'rspec_junit_formatter'
15
6
 
16
7
  require 'pry'
17
8
  require 'honeybadger'
@@ -20,8 +11,6 @@ require 'contextual_logger'
20
11
  require 'exception_handling'
21
12
  require 'exception_handling/testing'
22
13
 
23
- ActiveSupport::TestCase.test_order = :sorted
24
-
25
14
  class LoggerStub
26
15
  include ContextualLogger::LoggerMixin
27
16
  attr_accessor :logged, :level
@@ -87,27 +76,17 @@ end
87
76
 
88
77
  ActionMailer::Base.delivery_method = :test
89
78
 
90
- _ = ActiveSupport
91
- _ = ActiveSupport::TestCase
92
79
 
93
- class ActiveSupport::TestCase
94
- @@constant_overrides = []
80
+ module TestHelper
81
+ @constant_overrides = []
82
+ class << self
83
+ attr_accessor :constant_overrides
84
+ end
95
85
 
96
- setup do
97
- unless @@constant_overrides.nil? || @@constant_overrides.empty?
98
- raise "Uh-oh! constant_overrides left over: #{@@constant_overrides.inspect}"
99
- end
100
86
 
101
- unless defined?(Rails) && defined?(Rails.env)
102
- module ::Rails
103
- class << self
104
- attr_writer :env
105
-
106
- def env
107
- @env ||= 'test'
108
- end
109
- end
110
- end
87
+ def setup_constant_overrides
88
+ unless TestHelper.constant_overrides.nil? || TestHelper.constant_overrides.empty?
89
+ raise "Uh-oh! constant_overrides left over: #{TestHelper.constant_overrides.inspect}"
111
90
  end
112
91
 
113
92
  Time.now_override = nil
@@ -127,8 +106,8 @@ class ActiveSupport::TestCase
127
106
  ExceptionHandling.sensu_prefix = ""
128
107
  end
129
108
 
130
- teardown do
131
- @@constant_overrides&.reverse&.each do |parent_module, k, v|
109
+ def teardown_constant_overrides
110
+ TestHelper.constant_overrides&.reverse&.each do |parent_module, k, v|
132
111
  ExceptionHandling.ensure_safe "constant cleanup #{k.inspect}, #{parent_module}(#{parent_module.class})::#{v.inspect}(#{v.class})" do
133
112
  silence_warnings do
134
113
  if v == :never_defined
@@ -139,7 +118,7 @@ class ActiveSupport::TestCase
139
118
  end
140
119
  end
141
120
  end
142
- @@constant_overrides = []
121
+ TestHelper.constant_overrides = []
143
122
  end
144
123
 
145
124
  def set_test_const(const_name, value)
@@ -159,7 +138,7 @@ class ActiveSupport::TestCase
159
138
  end
160
139
  end
161
140
 
162
- @@constant_overrides << [final_parent_module, final_const_name, original_value]
141
+ TestHelper.constant_overrides << [final_parent_module, final_const_name, original_value]
163
142
 
164
143
  silence_warnings { final_parent_module.const_set(final_const_name, value) }
165
144
  end
@@ -171,15 +150,15 @@ class ActiveSupport::TestCase
171
150
  else
172
151
  original_count = 0
173
152
  end
174
- assert_equal expected, ActionMailer::Base.deliveries.size - original_count, "wrong number of emails#{': ' + message.to_s if message}"
153
+ expect(ActionMailer::Base.deliveries.size - original_count).to eq(expected), "wrong number of emails#{': ' + message.to_s if message}"
175
154
  end
176
155
  end
177
156
 
178
157
  def assert_equal_with_diff(arg1, arg2, msg = '')
179
158
  if arg1 == arg2
180
- assert true # To keep the assertion count accurate
159
+ expect(true).to be_truthy # To keep the assertion count accurate
181
160
  else
182
- assert_equal arg1, arg2, "#{msg}\n#{Diff.compare(arg1, arg2)}"
161
+ expect(arg1).to eq(arg2), "#{msg}\n#{Diff.compare(arg1, arg2)}"
183
162
  end
184
163
  end
185
164
 
@@ -210,3 +189,35 @@ class Time
210
189
  end
211
190
  end
212
191
  end
192
+
193
+ RSpec.configure do |config|
194
+ config.add_formatter(RspecJunitFormatter, 'spec/reports/rspec.xml')
195
+ config.include TestHelper
196
+
197
+ config.before(:each) do
198
+ setup_constant_overrides
199
+ unless defined?(Rails) && defined?(Rails.env)
200
+ module Rails
201
+ class << self
202
+ attr_writer :env
203
+
204
+ def env
205
+ @env ||= 'test'
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ config.after(:each) do
213
+ teardown_constant_overrides
214
+ end
215
+
216
+ config.mock_with :rspec do |mocks|
217
+ mocks.verify_partial_doubles = true
218
+ end
219
+
220
+ config.expect_with(:rspec, :test_unit)
221
+
222
+ RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = 2_000
223
+ end
@@ -0,0 +1,85 @@
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", anything)
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
@@ -0,0 +1,82 @@
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,58 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../../test_helper', __dir__)
3
+ require File.expand_path('../../spec_helper', __dir__)
4
4
  require_test_helper 'controller_helpers'
5
5
  require_test_helper 'exception_helpers'
6
6
 
7
7
  module ExceptionHandling
8
- class ExceptionInfoTest < ActiveSupport::TestCase
8
+ describe ExceptionInfo do
9
9
  include ControllerHelpers
10
10
  include ExceptionHelpers
11
11
 
12
12
  context "initialize" do
13
- setup do
13
+ before do
14
14
  @exception = StandardError.new("something went wrong")
15
15
  @timestamp = Time.now
16
16
  @controller = Object.new
17
17
  end
18
18
 
19
19
  context "controller_from_context" do
20
- should "extract controller from context when not specified explicitly" do
20
+ it "extract controller from context when not specified explicitly" do
21
21
  exception_context = {
22
22
  "action_controller.instance" => @controller
23
23
  }
24
24
  exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp)
25
- assert_equal @controller, exception_info.controller
25
+ expect(exception_info.controller).to eq(@controller)
26
26
  end
27
27
 
28
- should "prefer the explicit controller over the one from context" do
28
+ it "prefer the explicit controller over the one from context" do
29
29
  exception_context = {
30
30
  "action_controller.instance" => Object.new
31
31
  }
32
32
  exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp, controller: @controller)
33
- assert_equal @controller, exception_info.controller
34
- assert_not_equal exception_context["action_controller.instance"], exception_info.controller
33
+ expect(exception_info.controller).to eq(@controller)
34
+ expect(exception_info.controller).not_to eq(exception_context["action_controller.instance"])
35
35
  end
36
36
 
37
- should "leave controller unset when not included in the context hash" do
37
+ it "leave controller unset when not included in the context hash" do
38
38
  exception_info = ExceptionInfo.new(@exception, {}, @timestamp)
39
- assert_nil exception_info.controller
39
+ expect(exception_info.controller).to be_nil
40
40
  end
41
41
 
42
- should "leave controller unset when context is not in hash format" do
42
+ it "leave controller unset when context is not in hash format" do
43
43
  exception_info = ExceptionInfo.new(@exception, "string context", @timestamp)
44
- assert_nil exception_info.controller
44
+ expect(exception_info.controller).to be_nil
45
45
  end
46
46
  end
47
47
  end
48
48
 
49
49
  context "data" do
50
- setup do
50
+ before do
51
51
  @exception = StandardError.new("something went wrong")
52
52
  @timestamp = Time.now
53
53
  end
54
54
 
55
- should "return a hash with exception specific data including context hash" do
55
+ it "return a hash with exception specific data including context hash" do
56
56
  exception_context = {
57
57
  "rack.session" => {
58
58
  user_id: 23,
@@ -73,17 +73,17 @@ module ExceptionHandling
73
73
  }
74
74
  }
75
75
 
76
- assert_equal_with_diff expected_data, exception_info.data
76
+ expect(exception_info.data).to eq(expected_data)
77
77
  end
78
78
 
79
- should "generate exception data appropriately if exception message is nil" do
79
+ it "generate exception data appropriately if exception message is nil" do
80
80
  exception_info = ExceptionInfo.new(exception_with_nil_message, "custom context data", @timestamp)
81
81
  exception_data = exception_info.data
82
- assert_equal "RuntimeError: ", exception_data["error_string"]
83
- assert_equal "RuntimeError: : custom context data", exception_data["error"]
82
+ expect(exception_data["error_string"]).to eq("RuntimeError: ")
83
+ expect(exception_data["error"]).to eq("RuntimeError: : custom context data")
84
84
  end
85
85
 
86
- should "return a hash with exception specific data including context string" do
86
+ it "return a hash with exception specific data including context string" do
87
87
  exception_context = "custom context data"
88
88
  exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp)
89
89
  expected_data = {
@@ -96,11 +96,10 @@ module ExceptionHandling
96
96
  "message" => "custom context data"
97
97
  }
98
98
  }
99
-
100
- assert_equal_with_diff expected_data, exception_info.data
99
+ expect(exception_info.data).to eq(expected_data)
101
100
  end
102
101
 
103
- should "not include enhanced data from controller or custom data callback" do
102
+ it "not include enhanced data from controller or custom data callback" do
104
103
  env = { server: "fe98" }
105
104
  parameters = { advertiser_id: 435 }
106
105
  session = { username: "jsmith" }
@@ -109,8 +108,8 @@ module ExceptionHandling
109
108
  data_callback = ->(data) { data[:custom_section] = "value" }
110
109
  exception_info = ExceptionInfo.new(@exception, "custom context data", @timestamp, controller: controller, data_callback: data_callback)
111
110
 
112
- dont_allow(exception_info).extract_and_merge_controller_data
113
- dont_allow(exception_info).customize_from_data_callback
111
+ expect(exception_info).to_not receive(:extract_and_merge_controller_data)
112
+ expect(exception_info).to_not receive(:customize_from_data_callback)
114
113
  expected_data = {
115
114
  "error_class" => "StandardError",
116
115
  "error_string" => "StandardError: something went wrong",
@@ -122,12 +121,12 @@ module ExceptionHandling
122
121
  }
123
122
  }
124
123
 
125
- assert_equal_with_diff expected_data, exception_info.data
124
+ expect(exception_info.data).to eq(expected_data)
126
125
  end
127
126
  end
128
127
 
129
128
  context "enhanced_data" do
130
- setup do
129
+ before do
131
130
  @exception = StandardError.new("something went wrong")
132
131
  @timestamp = Time.now
133
132
  @exception_context = {
@@ -145,13 +144,13 @@ module ExceptionHandling
145
144
  @data_callback = ->(data) { data[:custom_section] = "check this out" }
146
145
  end
147
146
 
148
- should "not return a mutable object for the session" do
147
+ it "not return a mutable object for the session" do
149
148
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
150
149
  exception_info.enhanced_data["session"]["hello"] = "world"
151
- assert_nil @controller.session["hello"]
150
+ expect(@controller.session["hello"]).to be_nil
152
151
  end
153
152
 
154
- should "return a hash with generic exception attributes as well as context data" do
153
+ it "return a hash with generic exception attributes as well as context data" do
155
154
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
156
155
  expected_data = {
157
156
  "error_class" => "StandardError",
@@ -164,19 +163,19 @@ module ExceptionHandling
164
163
  "location" => { "file" => "<no backtrace>", "line" => nil }
165
164
  }
166
165
 
167
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
166
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
168
167
  end
169
168
 
170
- should "generate exception data appropriately if exception message is nil" do
169
+ it "generate exception data appropriately if exception message is nil" do
171
170
  exception_with_nil_message = RuntimeError.new(nil)
172
- stub(exception_with_nil_message).message { nil }
171
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
173
172
  exception_info = ExceptionInfo.new(exception_with_nil_message, @exception_context, @timestamp)
174
173
  exception_data = exception_info.enhanced_data
175
- assert_equal "RuntimeError: ", exception_data["error_string"]
176
- assert_equal "RuntimeError: ", exception_data["error"]
174
+ expect(exception_data["error_string"]).to eq("RuntimeError: ")
175
+ expect(exception_data["error"]).to eq("RuntimeError: ")
177
176
  end
178
177
 
179
- should "include controller data when available" do
178
+ it "include controller data when available" do
180
179
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: @controller)
181
180
  expected_data = {
182
181
  "error_class" => "StandardError",
@@ -194,10 +193,10 @@ module ExceptionHandling
194
193
  "location" => { "controller" => "dummy", "action" => "fail", "file" => "<no backtrace>", "line" => nil }
195
194
  }
196
195
 
197
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
196
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
198
197
  end
199
198
 
200
- should "extract controller from rack specific exception context when not provided explicitly" do
199
+ it "extract controller from rack specific exception context when not provided explicitly" do
201
200
  @exception_context["action_controller.instance"] = @controller
202
201
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
203
202
  expected_data = {
@@ -216,10 +215,10 @@ module ExceptionHandling
216
215
  "location" => { "controller" => "dummy", "action" => "fail", "file" => "<no backtrace>", "line" => nil }
217
216
  }
218
217
 
219
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
218
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
220
219
  end
221
220
 
222
- should "add to_s attribute to specific sections that have their content in hash format" do
221
+ it "add to_s attribute to specific sections that have their content in hash format" do
223
222
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: @controller)
224
223
  expected_data = {
225
224
  "error_class" => "StandardError",
@@ -245,10 +244,10 @@ module ExceptionHandling
245
244
  "location" => { "controller" => "dummy", "action" => "fail", "file" => "<no backtrace>", "line" => nil }
246
245
  }
247
246
 
248
- assert_equal_with_diff expected_data, exception_info.enhanced_data
247
+ expect(exception_info.enhanced_data).to eq(expected_data)
249
248
  end
250
249
 
251
- should "filter out sensitive parameters like passwords" do
250
+ it "filter out sensitive parameters like passwords" do
252
251
  @controller.request.parameters[:password] = "super_secret"
253
252
  @controller.request.parameters[:user] = { "password" => "also super secret", "password_confirmation" => "also super secret" }
254
253
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: @controller)
@@ -261,10 +260,10 @@ module ExceptionHandling
261
260
  "password_confirmation" => "[FILTERED]"
262
261
  }
263
262
  }
264
- assert_equal_with_diff expected_params, exception_info.enhanced_data["request"]["params"]
263
+ expect(exception_info.enhanced_data["request"]["params"]).to eq(expected_params)
265
264
  end
266
265
 
267
- should "include the changes from the custom data callback" do
266
+ it "include the changes from the custom data callback" do
268
267
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: nil, data_callback: @data_callback)
269
268
  expected_data = {
270
269
  "error_class" => "StandardError",
@@ -278,11 +277,11 @@ module ExceptionHandling
278
277
  "location" => { "file" => "<no backtrace>", "line" => nil }
279
278
  }
280
279
 
281
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
280
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
282
281
  end
283
282
 
284
- should "apply the custom_data_hook results" do
285
- stub(ExceptionHandling).custom_data_hook { ->(data) { data[:custom_hook] = "changes from custom hook" } }
283
+ it "apply the custom_data_hook results" do
284
+ allow(ExceptionHandling).to receive(:custom_data_hook).and_return(->(data) { data[:custom_hook] = "changes from custom hook" })
286
285
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
287
286
  expected_data = {
288
287
  "error_class" => "StandardError",
@@ -296,35 +295,35 @@ module ExceptionHandling
296
295
  "location" => { "file" => "<no backtrace>", "line" => nil }
297
296
  }
298
297
 
299
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
298
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
300
299
  end
301
300
 
302
- should "log info if the custom data hook results in a nil message exception" do
301
+ it "log info if the custom data hook results in a nil message exception" do
303
302
  ExceptionHandling.custom_data_hook = ->(_data) do
304
303
  raise_exception_with_nil_message
305
304
  end
306
305
  log_info_messages = []
307
- stub(ExceptionHandling.logger).info.with_any_args do |message, _|
306
+ allow(ExceptionHandling.logger).to receive(:info).with(any_args) do |message, _|
308
307
  log_info_messages << message
309
308
  end
310
309
 
311
310
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
312
311
  exception_info.enhanced_data
313
- assert log_info_messages.find { |message| message =~ /Unable to execute custom custom_data_hook callback/ }
312
+ expect(log_info_messages.find { |message| message =~ /Unable to execute custom custom_data_hook callback/ }).to be_truthy
314
313
  ExceptionHandling.custom_data_hook = nil
315
314
  end
316
315
  end
317
316
 
318
317
  context "exception_description" do
319
- should "return the exception description from the global exception filter list" do
318
+ it "return the exception description from the global exception filter list" do
320
319
  exception = StandardError.new("No route matches")
321
320
  exception_info = ExceptionInfo.new(exception, {}, Time.now)
322
321
  description = exception_info.exception_description
323
- assert_not_nil description
324
- assert_equal :NoRoute, description.filter_name
322
+ expect(description).to_not be_nil
323
+ expect(description.filter_name).to eq(:NoRoute)
325
324
  end
326
325
 
327
- should "find the description when filter criteria includes section in hash format" do
326
+ it "find the description when filter criteria includes section in hash format" do
328
327
  env = { server: "fe98" }
329
328
  parameters = { advertiser_id: 435, controller: "sessions", action: "fail" }
330
329
  session = { username: "jsmith", id: "session_key" }
@@ -332,25 +331,25 @@ module ExceptionHandling
332
331
  controller = create_dummy_controller(env, parameters, session, request_uri)
333
332
  exception = StandardError.new("Request to click domain rejected")
334
333
  exception_info = ExceptionInfo.new(exception, nil, Time.now, controller: controller)
335
- assert_equal true, exception_info.enhanced_data[:request].is_a?(Hash)
334
+ expect(exception_info.enhanced_data[:request].is_a?(Hash)).to eq(true)
336
335
  description = exception_info.exception_description
337
- assert_not_nil description
338
- assert_equal :"Click Request Rejected", description.filter_name
336
+ expect(description).to_not be_nil
337
+ expect(description.filter_name).to eq(:"Click Request Rejected")
339
338
  end
340
339
 
341
- should "return same description object for related errors (avoid reloading exception catalog from disk)" do
340
+ it "return same description object for related errors (avoid reloading exception catalog from disk)" do
342
341
  exception = StandardError.new("No route matches")
343
342
  exception_info = ExceptionInfo.new(exception, nil, Time.now)
344
343
  description = exception_info.exception_description
345
344
 
346
345
  repeat_ex = StandardError.new("No route matches 2")
347
346
  repeat_ex_info = ExceptionInfo.new(repeat_ex, nil, Time.now)
348
- assert_equal description.object_id, repeat_ex_info.exception_description.object_id
347
+ expect(repeat_ex_info.exception_description.object_id).to eq(description.object_id)
349
348
  end
350
349
  end
351
350
 
352
351
  context "controller_name" do
353
- setup do
352
+ before do
354
353
  @exception = StandardError.new('something went wrong')
355
354
  @timestamp = Time.now
356
355
  @exception_context = {
@@ -362,7 +361,27 @@ module ExceptionHandling
362
361
  }
363
362
  end
364
363
 
365
- should "return controller_name when controller is present" do
364
+ it "returns honeybadger_grouping from the log context" do
365
+ allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ honeybadger_grouping: 'Group 1' })
366
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
367
+
368
+ expect(exception_info.controller_name).to eq('Group 1')
369
+ end
370
+
371
+ it "returns honeybadger_grouping from log context even if controller is defined" do
372
+ allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ honeybadger_grouping: 'Group 1' })
373
+
374
+ env = { server: 'fe98' }
375
+ parameters = { controller: 'some_controller' }
376
+ session = { username: 'smith' }
377
+ request_uri = "host/path"
378
+ controller = create_dummy_controller(env, parameters, session, request_uri)
379
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: controller)
380
+
381
+ expect(exception_info.controller_name).to eq('Group 1')
382
+ end
383
+
384
+ it "return controller_name when controller is present" do
366
385
  env = { server: 'fe98' }
367
386
  parameters = { controller: 'some_controller' }
368
387
  session = { username: 'smith' }
@@ -370,85 +389,85 @@ module ExceptionHandling
370
389
  controller = create_dummy_controller(env, parameters, session, request_uri)
371
390
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: controller)
372
391
 
373
- assert_equal 'some_controller', exception_info.controller_name
392
+ expect(exception_info.controller_name).to eq('some_controller')
374
393
  end
375
394
 
376
- should "return an empty string when controller is not present" do
395
+ it "return an empty string when controller is not present" do
377
396
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
378
397
 
379
- assert_equal '', exception_info.controller_name
398
+ expect(exception_info.controller_name).to eq('')
380
399
  end
381
400
  end
382
401
 
383
402
  context "send_to_honeybadger?" do
384
- should "be enabled when Honeybadger is defined and exception is not in the filter list" do
385
- stub(ExceptionHandling).honeybadger_defined? { true }
403
+ it "be enabled when Honeybadger is defined and exception is not in the filter list" do
404
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(true)
386
405
  exception = StandardError.new("something went wrong")
387
406
  exception_info = ExceptionInfo.new(exception, nil, Time.now)
388
- assert_nil exception_info.exception_description
389
- assert_equal true, exception_info.send_to_honeybadger?
407
+ expect(exception_info.exception_description).to be_nil
408
+ expect(exception_info.send_to_honeybadger?).to eq(true)
390
409
  end
391
410
 
392
- should "be enabled when Honeybadger is defined and exception is on the filter list with the flag turned on" do
393
- stub(ExceptionHandling).honeybadger_defined? { true }
411
+ it "be enabled when Honeybadger is defined and exception is on the filter list with the flag turned on" do
412
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(true)
394
413
  exception = StandardError.new("No route matches")
395
414
  exception_info = ExceptionInfo.new(exception, nil, Time.now)
396
- assert_not_nil exception_info.exception_description
397
- assert_equal true, exception_info.exception_description.send_to_honeybadger
398
- assert_equal true, exception_info.send_to_honeybadger?
415
+ expect(exception_info.exception_description).to_not be_nil
416
+ expect(exception_info.exception_description.send_to_honeybadger).to eq(true)
417
+ expect(exception_info.send_to_honeybadger?).to eq(true)
399
418
  end
400
419
 
401
- should "be disabled when Honeybadger is defined and exception is on the filter list with the flag turned off" do
402
- stub(ExceptionHandling).honeybadger_defined? { true }
420
+ it "be disabled when Honeybadger is defined and exception is on the filter list with the flag turned off" do
421
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(true)
403
422
  exception = StandardError.new("No route matches")
404
423
  exception_info = ExceptionInfo.new(exception, nil, Time.now)
405
- assert_not_nil exception_info.exception_description
406
- stub(exception_info.exception_description).send_to_honeybadger { false }
407
- assert_equal false, exception_info.send_to_honeybadger?
424
+ expect(exception_info.exception_description).to_not be_nil
425
+ allow(exception_info.exception_description).to receive(:send_to_honeybadger).and_return(false)
426
+ expect(exception_info.send_to_honeybadger?).to eq(false)
408
427
  end
409
428
 
410
- should "be disabled when Honeybadger is not defined" do
411
- stub(ExceptionHandling).honeybadger_defined? { false }
429
+ it "be disabled when Honeybadger is not defined" do
430
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(false)
412
431
  exception = StandardError.new("something went wrong")
413
432
  exception_info = ExceptionInfo.new(exception, nil, Time.now)
414
- assert_nil exception_info.exception_description
415
- assert_equal false, exception_info.send_to_honeybadger?
433
+ expect(exception_info.exception_description).to be_nil
434
+ expect(exception_info.send_to_honeybadger?).to eq(false)
416
435
  end
417
436
  end
418
437
 
419
438
  context "honeybadger_context_data" do
420
- setup do
421
- stub(ExceptionHandling.logger).current_context_for_thread { { cuid: 'ABCD' } }
439
+ before do
440
+ allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ cuid: 'ABCD' })
422
441
  end
423
442
 
424
- should "include thread_context when log_context: is nil" do
443
+ it "include thread_context when log_context: is nil" do
425
444
  exception_with_nil_message = RuntimeError.new(nil)
426
- stub(exception_with_nil_message).message { nil }
445
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
427
446
  exception_info = ExceptionInfo.new(exception_with_nil_message, @exception_context, @timestamp)
428
447
  honeybadger_context_data = exception_info.honeybadger_context_data
429
- assert_equal({ "cuid" => 'ABCD' }, honeybadger_context_data[:log_context])
448
+ expect(honeybadger_context_data[:log_context]).to eq({ "cuid" => 'ABCD' })
430
449
  end
431
450
 
432
- should "include thread context merged with log_context:" do
451
+ it "include thread context merged with log_context:" do
433
452
  exception_with_nil_message = RuntimeError.new(nil)
434
- stub(exception_with_nil_message).message { nil }
453
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
435
454
  exception_info = ExceptionInfo.new(exception_with_nil_message, @exception_context, @timestamp, log_context: { url: 'http://example.com' })
436
455
  honeybadger_context_data = exception_info.honeybadger_context_data
437
- assert_equal({ "cuid" => 'ABCD', "url" => 'http://example.com' }, honeybadger_context_data[:log_context])
456
+ expect(honeybadger_context_data[:log_context]).to eq({ "cuid" => 'ABCD', "url" => 'http://example.com' })
438
457
  end
439
458
 
440
- should "return the error details and relevant context data to be used as honeybadger notification context while filtering sensitive data" do
459
+ it "return the error details and relevant context data to be used as honeybadger notification context while filtering sensitive data" do
441
460
  env = { server: "fe98" }
442
461
  parameters = { advertiser_id: 435 }
443
462
  session = { username: "jsmith" }
444
463
  request_uri = "host/path"
445
464
  controller = create_dummy_controller(env, parameters, session, request_uri)
446
- stub(ExceptionHandling).server_name { "invoca_fe98" }
465
+ allow(ExceptionHandling).to receive(:server_name).and_return("invoca_fe98")
447
466
 
448
467
  exception = StandardError.new("Some Exception")
449
468
  exception.set_backtrace([
450
- "test/unit/exception_handling_test.rb:847:in `exception_1'",
451
- "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
469
+ "spec/unit/exception_handling_test.rb:847:in `exception_1'",
470
+ "spec/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
452
471
  ])
453
472
  exception_context = { "SERVER_NAME" => "exceptional.com" }
454
473
  data_callback = ->(data) do
@@ -466,7 +485,6 @@ module ExceptionHandling
466
485
  exception_context: { "SERVER_NAME" => "exceptional.com" },
467
486
  server: "invoca_fe98",
468
487
  scm_revision: "5b24eac37aaa91f5784901e9aabcead36fd9df82",
469
- notes: "this is used by a test",
470
488
  user_details: { "username" => "jsmith" },
471
489
  request: {
472
490
  "params" => { "advertiser_id" => 435 },
@@ -481,29 +499,30 @@ module ExceptionHandling
481
499
  "SERVER_NAME" => "exceptional.com"
482
500
  },
483
501
  backtrace: [
484
- "test/unit/exception_handling_test.rb:847:in `exception_1'",
485
- "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
502
+ "spec/unit/exception_handling_test.rb:847:in `exception_1'",
503
+ "spec/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
486
504
  ],
487
505
  event_response: "Event successfully received",
488
- log_context: { "cuid" => "ABCD" }
506
+ log_context: { "cuid" => "ABCD" },
507
+ notes: "this is used by a test"
489
508
  }
490
- assert_equal_with_diff expected_data, exception_info.honeybadger_context_data
509
+ expect(exception_info.honeybadger_context_data).to eq(expected_data)
491
510
  end
492
511
 
493
512
  [['Hash', { 'cookie' => 'cookie_context' }],
494
513
  ['String', 'Entering Error State'],
495
514
  ['Array', ['Error1', 'Error2']]].each do |klass, value|
496
- should "extract context from exception_context when it is a #{klass}" do
515
+ it "extract context from exception_context when it is a #{klass}" do
497
516
  exception = StandardError.new("Exception")
498
517
  exception_context = value
499
518
  exception_info = ExceptionInfo.new(exception, exception_context, Time.now)
500
519
 
501
- assert_equal klass, value.class.name
502
- assert_equal value, exception_info.honeybadger_context_data[:exception_context]
520
+ expect(value.class.name).to eq(klass)
521
+ expect(exception_info.honeybadger_context_data[:exception_context]).to eq(value)
503
522
  end
504
523
  end
505
524
 
506
- should "filter out sensitive data from exception context such as [password, password_confirmation, oauth_token]" do
525
+ it "filter out sensitive data from exception context such as [password, password_confirmation, oauth_token]" do
507
526
  sensitive_data = {
508
527
  "password" => "super_secret",
509
528
  "password_confirmation" => "super_secret_confirmation",
@@ -538,15 +557,14 @@ module ExceptionHandling
538
557
  }
539
558
  }.merge(expected_sensitive_data)
540
559
 
541
- assert_equal_with_diff expected_exception_context, exception_info.honeybadger_context_data[:exception_context]
560
+ expect(exception_info.honeybadger_context_data[:exception_context]).to eq(expected_exception_context)
542
561
  end
543
562
 
544
- should "omit context if exception_context is empty" do
563
+ it "omit context if exception_context is empty" do
545
564
  exception = StandardError.new("Exception")
546
565
  exception_context = ""
547
566
  exception_info = ExceptionInfo.new(exception, exception_context, Time.now)
548
-
549
- assert_nil exception_info.honeybadger_context_data[:exception_context]
567
+ expect(exception_info.honeybadger_context_data[:exception_context]).to be_nil
550
568
  end
551
569
  end
552
570