exception_handling 2.13.0 → 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 (45) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -3
  3. data/.ruby-version +1 -1
  4. data/Gemfile +16 -16
  5. data/Gemfile.lock +114 -110
  6. data/README.md +3 -7
  7. data/Rakefile +11 -8
  8. data/exception_handling.gemspec +10 -12
  9. data/lib/exception_handling/exception_info.rb +10 -13
  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 +33 -68
  16. data/semaphore_ci/setup.sh +3 -0
  17. data/{spec → test}/helpers/controller_helpers.rb +0 -0
  18. data/{spec → test}/helpers/exception_helpers.rb +2 -2
  19. data/{spec/spec_helper.rb → test/test_helper.rb} +42 -63
  20. data/test/unit/exception_handling/exception_catalog_test.rb +85 -0
  21. data/test/unit/exception_handling/exception_description_test.rb +82 -0
  22. data/{spec/unit/exception_handling/exception_info_spec.rb → test/unit/exception_handling/exception_info_test.rb} +114 -153
  23. data/test/unit/exception_handling/honeybadger_callbacks_test.rb +122 -0
  24. data/{spec/unit/exception_handling/log_error_stub_spec.rb → test/unit/exception_handling/log_error_stub_test.rb} +22 -38
  25. data/{spec/unit/exception_handling/mailer_spec.rb → test/unit/exception_handling/mailer_test.rb} +18 -17
  26. data/test/unit/exception_handling/methods_test.rb +84 -0
  27. data/test/unit/exception_handling/sensu_test.rb +52 -0
  28. data/test/unit/exception_handling_test.rb +1109 -0
  29. metadata +60 -115
  30. data/.github/workflows/pipeline.yml +0 -33
  31. data/.rspec +0 -3
  32. data/Appraisals +0 -13
  33. data/CHANGELOG.md +0 -119
  34. data/gemfiles/rails_5.gemfile +0 -18
  35. data/gemfiles/rails_6.gemfile +0 -18
  36. data/lib/exception_handling/escalate_callback.rb +0 -19
  37. data/lib/exception_handling/logging_methods.rb +0 -27
  38. data/spec/rake_test_warning_false.rb +0 -20
  39. data/spec/unit/exception_handling/escalate_callback_spec.rb +0 -81
  40. data/spec/unit/exception_handling/exception_catalog_spec.rb +0 -85
  41. data/spec/unit/exception_handling/exception_description_spec.rb +0 -82
  42. data/spec/unit/exception_handling/logging_methods_spec.rb +0 -38
  43. data/spec/unit/exception_handling/methods_spec.rb +0 -105
  44. data/spec/unit/exception_handling/sensu_spec.rb +0 -51
  45. data/spec/unit/exception_handling_spec.rb +0 -1303
@@ -4,7 +4,7 @@
4
4
 
5
5
  module ExceptionHandling
6
6
  module Testing
7
- class ControllerStubBase
7
+ class ControllerStub
8
8
 
9
9
  class Request
10
10
  attr_accessor :parameters, :protocol, :host, :request_uri, :env, :session_options
@@ -25,7 +25,7 @@ module ExceptionHandling
25
25
  attr_accessor :around_filter_method
26
26
 
27
27
  def around_filter(method)
28
- self.around_filter_method = method
28
+ ControllerStub.around_filter_method = method
29
29
  end
30
30
  end
31
31
 
@@ -44,6 +44,14 @@ module ExceptionHandling
44
44
  end
45
45
  end
46
46
 
47
+ def simulate_around_filter(&block)
48
+ set_current_controller(&block)
49
+ end
50
+
51
+ def controller_name
52
+ "ControllerStub"
53
+ end
54
+
47
55
  def action_name
48
56
  "test_action"
49
57
  end
@@ -51,27 +59,9 @@ module ExceptionHandling
51
59
  def complete_request_uri
52
60
  "#{@request.protocol}#{@request.host}#{@request.request_uri}"
53
61
  end
54
- end
55
-
56
- class LoggingMethodsControllerStub < ControllerStubBase
57
- include ExceptionHandling::LoggingMethods
58
-
59
- def controller_name
60
- "LoggingMethodsControllerStub"
61
- end
62
- end
63
62
 
64
- class MethodsControllerStub < ControllerStubBase
65
63
  include ExceptionHandling::Methods
66
64
  set_long_controller_action_timeout 2
67
-
68
- def simulate_around_filter(&block)
69
- set_current_controller(&block)
70
- end
71
-
72
- def controller_name
73
- "MethodsControllerStub"
74
- end
75
65
  end
76
66
  end
77
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ExceptionHandling
4
- VERSION = '2.13.0'
4
+ VERSION = '3.0.pre.1'
5
5
  end
@@ -1,21 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'digest'
4
3
  require 'timeout'
5
4
  require 'active_support'
6
- require 'active_support/core_ext'
5
+ require 'active_support/core_ext/hash'
7
6
  require 'contextual_logger'
8
7
 
9
8
  require 'invoca/utils'
10
9
 
11
- require 'exception_handling/mailer'
12
- require 'exception_handling/sensu'
13
- require 'exception_handling/methods'
14
- require 'exception_handling/log_stub_error'
15
- require 'exception_handling/exception_description'
16
- require 'exception_handling/exception_catalog'
17
- require 'exception_handling/exception_info'
18
- require 'exception_handling/escalate_callback'
10
+ require "exception_handling/mailer"
11
+ require "exception_handling/sensu"
12
+ require "exception_handling/methods"
13
+ require "exception_handling/log_stub_error"
14
+ require "exception_handling/exception_description"
15
+ require "exception_handling/exception_catalog"
16
+ require "exception_handling/exception_info"
17
+ require "exception_handling/honeybadger_callbacks.rb"
19
18
 
20
19
  _ = ActiveSupport::HashWithIndifferentAccess
21
20
 
@@ -30,8 +29,6 @@ module ExceptionHandling # never included
30
29
  AUTHENTICATION_HEADERS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION', 'REDIRECT_X_HTTP_AUTHORIZATION'].freeze
31
30
  HONEYBADGER_STATUSES = [:success, :failure, :skipped].freeze
32
31
 
33
- Deprecation3_0 = ActiveSupport::Deprecation.new('3.0', 'exception_handling')
34
-
35
32
  class << self
36
33
 
37
34
  #
@@ -53,28 +50,15 @@ module ExceptionHandling # never included
53
50
  @exception_recipients or raise ArgumentError, "You must assign a value to #{name}.exception_recipients"
54
51
  end
55
52
 
56
- def configured?
57
- !@logger.nil?
58
- end
59
-
60
53
  def logger
61
54
  @logger or raise ArgumentError, "You must assign a value to #{name}.logger"
62
55
  end
63
56
 
64
57
  def logger=(logger)
65
- @logger = if logger.nil? || logger.is_a?(ContextualLogger::LoggerMixin)
66
- logger
67
- else
68
- Deprecation3_0.deprecation_warning('implicit extend with ContextualLogger::LoggerMixin', 'extend your logger instance or include into your logger class first')
69
- logger.extend(ContextualLogger::LoggerMixin)
70
- end
71
- EscalateCallback.register_if_configured!
58
+ @logger = logger.is_a?(ContextualLogger) ? logger : ContextualLogger.new(logger)
72
59
  end
73
60
 
74
- def default_metric_name(exception_data, exception, treat_like_warning, include_prefix: true)
75
- include_prefix and Deprecation3_0.deprecation_warning("the 'expection_handling.' prefix in ExceptionHandling::default_metric_name",
76
- "do not rely on metric names including the 'exception_handling.' prefix.")
77
-
61
+ def default_metric_name(exception_data, exception, treat_like_warning)
78
62
  metric_name = if exception_data['metric_name']
79
63
  exception_data['metric_name']
80
64
  elsif exception.is_a?(ExceptionHandling::Warning)
@@ -86,7 +70,7 @@ module ExceptionHandling # never included
86
70
  "exception"
87
71
  end
88
72
 
89
- "#{'exception_handling.' if include_prefix}#{metric_name}"
73
+ "exception_handling.#{metric_name}"
90
74
  end
91
75
 
92
76
  def default_honeybadger_metric_name(honeybadger_status)
@@ -193,18 +177,13 @@ module ExceptionHandling # never included
193
177
  # Called directly by our code, usually from rescue blocks.
194
178
  # Writes to log file and may send to honeybadger
195
179
  #
196
- # TODO: the **log_context means we can never have context named treat_like_warning. In general, keyword args will be conflated with log_context.
197
- # Ideally we'd separate to log_context from the other keywords so they don't interfere in any way. Or have no keyword args.
198
- #
199
180
  # Functional Test Operation:
200
181
  # Calls into handle_stub_log_error and returns. no log file. no honeybadger
201
182
  #
202
- def log_error(exception_or_string, exception_context = '', controller = nil, treat_like_warning: false, **log_context, &data_callback)
183
+ def log_error(exception_or_string, exception_context = '', treat_like_warning: false, **log_context, &data_callback)
203
184
  ex = make_exception(exception_or_string)
204
185
  timestamp = set_log_error_timestamp
205
- exception_info = ExceptionInfo.new(ex, exception_context, timestamp,
206
- controller: controller || current_controller, data_callback: data_callback,
207
- log_context: log_context)
186
+ exception_info = ExceptionInfo.new(ex, exception_context, timestamp, current_controller, data_callback)
208
187
 
209
188
  if stub_handler
210
189
  stub_handler.handle_stub_log_error(exception_info.data)
@@ -231,13 +210,7 @@ module ExceptionHandling # never included
231
210
  #
232
211
  def write_exception_to_log(ex, exception_context, timestamp, log_context = {})
233
212
  ActiveSupport::Deprecation.silence do
234
- log_message = "#{exception_context}\n#{ex.class}: (#{encode_utf8(ex.message.to_s)}):\n " + clean_backtrace(ex).join("\n ") + "\n\n"
235
-
236
- if ex.is_a?(Warning)
237
- ExceptionHandling.logger.warn("\nExceptionHandlingWarning (Warning:#{timestamp}) #{log_message}", **log_context)
238
- else
239
- ExceptionHandling.logger.fatal("\nExceptionHandlingError (Error:#{timestamp}) #{log_message}", **log_context)
240
- end
213
+ ExceptionHandling.logger.fatal("\nExceptionHandlingError (Error:#{timestamp}) #{ex.class} #{exception_context} (#{encode_utf8(ex.message.to_s)}):\n " + clean_backtrace(ex).join("\n ") + "\n\n", log_context)
241
214
  end
242
215
  end
243
216
 
@@ -292,41 +265,38 @@ module ExceptionHandling # never included
292
265
  #
293
266
  # Expects passed in hash to only include keys which be directly set on the Honeybadger config
294
267
  #
295
- def enable_honeybadger(**config)
268
+ def enable_honeybadger(config = {})
296
269
  Bundler.require(:honeybadger)
270
+ HoneybadgerCallbacks.register_callbacks
297
271
  Honeybadger.configure do |config_klass|
298
272
  config.each do |k, v|
299
- if k == :before_notify
300
- config_klass.send(k, v)
301
- else
302
- config_klass.send(:"#{k}=", v)
303
- end
273
+ config_klass.send(:"#{k}=", v)
304
274
  end
305
275
  end
306
276
  end
307
277
 
308
- def log_warning(message, **log_context)
278
+ def log_warning(message, log_context = {})
309
279
  warning = Warning.new(message)
310
280
  warning.set_backtrace([])
311
281
  log_error(warning, **log_context)
312
282
  end
313
283
 
314
- def log_info(message, **log_context)
315
- ExceptionHandling.logger.info(message, **log_context)
284
+ def log_info(message, log_context = {})
285
+ ExceptionHandling.logger.info(message, log_context)
316
286
  end
317
287
 
318
- def log_debug(message, **log_context)
319
- ExceptionHandling.logger.debug(message, **log_context)
288
+ def log_debug(message, log_context = {})
289
+ ExceptionHandling.logger.debug(message, log_context)
320
290
  end
321
291
 
322
- def ensure_safe(exception_context = "", **log_context)
292
+ def ensure_safe(exception_context = "", log_context = {})
323
293
  yield
324
294
  rescue => ex
325
295
  log_error(ex, exception_context, **log_context)
326
296
  nil
327
297
  end
328
298
 
329
- def ensure_completely_safe(exception_context = "", **log_context)
299
+ def ensure_completely_safe(exception_context = "", log_context = {})
330
300
  yield
331
301
  rescue SystemExit, SystemStackError, NoMemoryError, SecurityError, SignalException
332
302
  raise
@@ -341,29 +311,26 @@ module ExceptionHandling # never included
341
311
  escalate(email_subject, ex, last_exception_timestamp, production_support_recipients)
342
312
  end
343
313
 
344
- def escalate_error(exception_or_string, email_subject, custom_recipients = nil, **log_context)
314
+ def escalate_error(exception_or_string, email_subject, custom_recipients = nil, log_context = {})
345
315
  ex = make_exception(exception_or_string)
346
316
  log_error(ex, **log_context)
347
317
  escalate(email_subject, ex, last_exception_timestamp, custom_recipients)
348
318
  end
349
319
 
350
- def escalate_warning(message, email_subject, custom_recipients = nil, **log_context)
320
+ def escalate_warning(message, email_subject, custom_recipients = nil, log_context = {})
351
321
  ex = Warning.new(message)
352
322
  log_error(ex, **log_context)
353
323
  escalate(email_subject, ex, last_exception_timestamp, custom_recipients)
354
324
  end
355
325
 
356
- def ensure_escalation(email_subject, custom_recipients = nil, **log_context)
326
+ def ensure_escalation(email_subject, custom_recipients = nil, log_context = {})
357
327
  yield
358
328
  rescue => ex
359
- escalate_error(ex, email_subject, custom_recipients, **log_context)
329
+ escalate_error(ex, email_subject, custom_recipients, log_context)
360
330
  nil
361
331
  end
362
332
 
363
- deprecate :escalate_to_production_support, :escalate_error, :escalate_warning, :ensure_escalation,
364
- deprecator: ActiveSupport::Deprecation.new('3.0', 'ExceptionHandling')
365
-
366
- def alert_warning(exception_or_string, alert_name, exception_context, **log_context)
333
+ def alert_warning(exception_or_string, alert_name, exception_context, log_context)
367
334
  ex = make_exception(exception_or_string)
368
335
  log_error(ex, exception_context, **log_context)
369
336
  begin
@@ -373,10 +340,10 @@ module ExceptionHandling # never included
373
340
  end
374
341
  end
375
342
 
376
- def ensure_alert(alert_name, exception_context, **log_context)
343
+ def ensure_alert(alert_name, exception_context, log_context = {})
377
344
  yield
378
345
  rescue => ex
379
- alert_warning(ex, alert_name, exception_context, **log_context)
346
+ alert_warning(ex, alert_name, exception_context, log_context)
380
347
  nil
381
348
  end
382
349
 
@@ -393,7 +360,7 @@ module ExceptionHandling # never included
393
360
  result
394
361
  end
395
362
 
396
- def log_periodically(exception_key, interval, message, **log_context)
363
+ def log_periodically(exception_key, interval, message, log_context = {})
397
364
  self.periodic_exception_intervals ||= {}
398
365
  last_logged = self.periodic_exception_intervals[exception_key]
399
366
  if !last_logged || ((last_logged + interval) < Time.now)
@@ -496,6 +463,4 @@ module ExceptionHandling # never included
496
463
  end
497
464
  end
498
465
  end
499
-
500
- EscalateCallback.register_if_configured!
501
466
  end
@@ -0,0 +1,3 @@
1
+ #!/bin/sh -x
2
+
3
+ bundle install --path vendor/bundle
File without changes
@@ -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
- allow(exception_with_nil_message).to receive(:message).and_return(nil)
10
+ stub(exception_with_nil_message).message { 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
- allow(ExceptionHandling).to receive(:send_exception_to_honeybadger).with(any_args) { |exception_info| @sent_notifications << exception_info }
18
+ stub(ExceptionHandling).send_exception_to_honeybadger(anything) { |exception_info| @sent_notifications << exception_info }
19
19
  end
20
20
  end
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rspec'
4
- require 'rspec/mocks'
5
- require 'rspec_junit_formatter'
6
-
3
+ require 'active_support'
4
+ require 'active_support/time'
5
+ require 'active_support/test_case'
6
+ require 'active_model'
7
+ require 'action_mailer'
8
+ require 'action_dispatch'
9
+ require 'hobo_support'
10
+ require 'shoulda'
11
+ require 'rr'
12
+ require 'minitest/autorun'
7
13
  require 'pry'
8
14
  require 'honeybadger'
9
15
  require 'contextual_logger'
@@ -11,31 +17,26 @@ require 'contextual_logger'
11
17
  require 'exception_handling'
12
18
  require 'exception_handling/testing'
13
19
 
20
+ ActiveSupport::TestCase.test_order = :sorted
21
+
14
22
  class LoggerStub
15
- include ContextualLogger::LoggerMixin
16
- attr_accessor :logged, :level
23
+ include ContextualLogger
24
+ attr_accessor :logged
17
25
 
18
26
  def initialize
19
- @level = Logger::Severity::DEBUG
20
- @progname = nil
21
- @logdev = nil
22
27
  clear
23
28
  end
24
29
 
25
- def debug(message, log_context = {})
26
- logged << { message: message, context: log_context, severity: 'DEBUG' }
27
- end
28
-
29
30
  def info(message, log_context = {})
30
- logged << { message: message, context: log_context, severity: 'INFO' }
31
+ logged << { message: message, context: log_context }
31
32
  end
32
33
 
33
34
  def warn(message, log_context = {})
34
- logged << { message: message, context: log_context, severity: 'WARN' }
35
+ logged << { message: message, context: log_context }
35
36
  end
36
37
 
37
38
  def fatal(message, log_context = {})
38
- logged << { message: message, context: log_context, severity: 'FATAL' }
39
+ logged << { message: message, context: log_context }
39
40
  end
40
41
 
41
42
  def clear
@@ -76,17 +77,27 @@ end
76
77
 
77
78
  ActionMailer::Base.delivery_method = :test
78
79
 
80
+ _ = ActiveSupport
81
+ _ = ActiveSupport::TestCase
79
82
 
80
- module TestHelper
81
- @constant_overrides = []
82
- class << self
83
- attr_accessor :constant_overrides
84
- end
83
+ class ActiveSupport::TestCase
84
+ @@constant_overrides = []
85
+
86
+ setup do
87
+ unless @@constant_overrides.nil? || @@constant_overrides.empty?
88
+ raise "Uh-oh! constant_overrides left over: #{@@constant_overrides.inspect}"
89
+ end
85
90
 
91
+ unless defined?(Rails) && defined?(Rails.env)
92
+ module ::Rails
93
+ class << self
94
+ attr_writer :env
86
95
 
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}"
96
+ def env
97
+ @env ||= 'test'
98
+ end
99
+ end
100
+ end
90
101
  end
91
102
 
92
103
  Time.now_override = nil
@@ -106,8 +117,8 @@ module TestHelper
106
117
  ExceptionHandling.sensu_prefix = ""
107
118
  end
108
119
 
109
- def teardown_constant_overrides
110
- TestHelper.constant_overrides&.reverse&.each do |parent_module, k, v|
120
+ teardown do
121
+ @@constant_overrides&.reverse&.each do |parent_module, k, v|
111
122
  ExceptionHandling.ensure_safe "constant cleanup #{k.inspect}, #{parent_module}(#{parent_module.class})::#{v.inspect}(#{v.class})" do
112
123
  silence_warnings do
113
124
  if v == :never_defined
@@ -118,7 +129,7 @@ module TestHelper
118
129
  end
119
130
  end
120
131
  end
121
- TestHelper.constant_overrides = []
132
+ @@constant_overrides = []
122
133
  end
123
134
 
124
135
  def set_test_const(const_name, value)
@@ -138,7 +149,7 @@ module TestHelper
138
149
  end
139
150
  end
140
151
 
141
- TestHelper.constant_overrides << [final_parent_module, final_const_name, original_value]
152
+ @@constant_overrides << [final_parent_module, final_const_name, original_value]
142
153
 
143
154
  silence_warnings { final_parent_module.const_set(final_const_name, value) }
144
155
  end
@@ -150,15 +161,15 @@ module TestHelper
150
161
  else
151
162
  original_count = 0
152
163
  end
153
- expect(ActionMailer::Base.deliveries.size - original_count).to eq(expected), "wrong number of emails#{': ' + message.to_s if message}"
164
+ assert_equal expected, ActionMailer::Base.deliveries.size - original_count, "wrong number of emails#{': ' + message.to_s if message}"
154
165
  end
155
166
  end
156
167
 
157
168
  def assert_equal_with_diff(arg1, arg2, msg = '')
158
169
  if arg1 == arg2
159
- expect(true).to be_truthy # To keep the assertion count accurate
170
+ assert true # To keep the assertion count accurate
160
171
  else
161
- expect(arg1).to eq(arg2), "#{msg}\n#{Diff.compare(arg1, arg2)}"
172
+ assert_equal arg1, arg2, "#{msg}\n#{Diff.compare(arg1, arg2)}"
162
173
  end
163
174
  end
164
175
 
@@ -189,35 +200,3 @@ class Time
189
200
  end
190
201
  end
191
202
  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('../../test_helper', __dir__)
4
+
5
+ module ExceptionHandling
6
+ class ExceptionCatalogTest < ActiveSupport::TestCase
7
+
8
+ context "With stubbed yaml content" do
9
+ setup do
10
+ filter_list = { exception1: { error: "my error message" },
11
+ exception2: { error: "some other message", session: "misc data" } }
12
+ stub(YAML).load_file { filter_list }
13
+
14
+ # bump modified time up to get the above filter loaded
15
+ stub(File).mtime { incrementing_mtime }
16
+ end
17
+
18
+ context "with loaded data" do
19
+ setup do
20
+ stub(File).mtime { incrementing_mtime }
21
+ @exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
22
+ @exception_catalog.send :load_file
23
+ end
24
+
25
+ should "have loaded filters" do
26
+ assert_equal 2, @exception_catalog.instance_eval("@filters").size
27
+ end
28
+
29
+ should "find messages in the catalog" do
30
+ assert !@exception_catalog.find(error: "Scott says unlikely to ever match")
31
+ end
32
+
33
+ should "find matching data" do
34
+ exception_description = @exception_catalog.find(error: "this is my error message, which should match something")
35
+ assert exception_description
36
+ assert_equal :exception1, exception_description.filter_name
37
+ end
38
+ end
39
+
40
+ should "write errors loading the yaml file directly to the log file" do
41
+ @exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
42
+
43
+ mock(ExceptionHandling).log_error.never
44
+ mock(ExceptionHandling).write_exception_to_log(anything, "ExceptionCatalog#refresh_filters: ./config/exception_filters.yml", anything)
45
+ mock(@exception_catalog).load_file { raise "noooooo" }
46
+
47
+ @exception_catalog.find({})
48
+ end
49
+ end
50
+
51
+ context "with live yaml content" do
52
+ setup do
53
+ @filename = File.expand_path('../../../config/exception_filters.yml', __dir__)
54
+ @exception_catalog = ExceptionCatalog.new(@filename)
55
+ assert_nothing_raised "Loading the exception filter should not raise" do
56
+ @exception_catalog.send :load_file
57
+ end
58
+ end
59
+
60
+ should "load the filter data" do
61
+ assert !@exception_catalog.find(error: "Scott says unlikely to ever match")
62
+ assert !@exception_catalog.find(error: "Scott says unlikely to ever match")
63
+ end
64
+ end
65
+
66
+ context "with no yaml content" do
67
+ setup do
68
+ @exception_catalog = ExceptionCatalog.new(nil)
69
+ end
70
+
71
+ should "not load filter data" do
72
+ mock(ExceptionHandling).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('../../test_helper', __dir__)
4
+
5
+ module ExceptionHandling
6
+ class ExceptionDescriptionTest < ActiveSupport::TestCase
7
+
8
+ context "Filter" do
9
+ should "allow direct matching of strings" do
10
+ @f = ExceptionDescription.new(:filter1, error: "my error message")
11
+ assert @f.match?('error' => "my error message")
12
+ end
13
+
14
+ should "allow direct matching of strings on with symbol keys" do
15
+ @f = ExceptionDescription.new(:filter1, error: "my error message")
16
+ assert @f.match?(error: "my error message")
17
+ end
18
+
19
+ should "allow wildcards to cross line boundries" do
20
+ @f = ExceptionDescription.new(:filter1, error: "my error message.*with multiple lines")
21
+ assert @f.match?(error: "my error message\nwith more than one, with multiple lines")
22
+ end
23
+
24
+ should "complain when no regexps have a value" do
25
+ assert_raise(ArgumentError, "has all blank regexe") { ExceptionDescription.new(:filter1, error: nil) }
26
+ end
27
+
28
+ should "report when an invalid key is passed" do
29
+ assert_raise(ArgumentError, "Unknown section: not_a_parameter") { ExceptionDescription.new(:filter1, error: "my error message", not_a_parameter: false) }
30
+ end
31
+
32
+ should "allow send_to_honeybadger to be specified and have it disabled by default" do
33
+ assert !ExceptionDescription.new(:filter1, error: "my error message", send_to_honeybadger: false).send_to_honeybadger
34
+ assert ExceptionDescription.new(:filter1, error: "my error message", send_to_honeybadger: true).send_to_honeybadger
35
+ assert !ExceptionDescription.new(:filter1, error: "my error message").send_to_honeybadger
36
+ end
37
+
38
+ should "allow send_metric to be configured" do
39
+ assert !ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).send_metric
40
+ assert ExceptionDescription.new(:filter1, error: "my error message").send_metric
41
+ end
42
+
43
+ should "provide metric name" do
44
+ assert_equal "filter1", ExceptionDescription.new(:filter1, error: "my error message").metric_name
45
+ assert_equal "some_other_metric_name", ExceptionDescription.new(:filter1, error: "my error message", metric_name: :some_other_metric_name).metric_name
46
+ end
47
+
48
+ should "replace spaces in metric name" do
49
+ @f = ExceptionDescription.new(:"filter has spaces", error: "my error message")
50
+ assert_equal "filter_has_spaces", @f.metric_name
51
+ end
52
+
53
+ should "allow notes to be recorded" do
54
+ assert_nil ExceptionDescription.new(:filter1, error: "my error message").notes
55
+ assert_equal "a long string", ExceptionDescription.new(:filter1, error: "my error message", notes: "a long string").notes
56
+ end
57
+
58
+ should "not consider config options in the filter set" do
59
+ assert ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).match?(error: "my error message")
60
+ assert ExceptionDescription.new(:filter1, error: "my error message", metric_name: "false").match?(error: "my error message")
61
+ assert ExceptionDescription.new(:filter1, error: "my error message", notes: "hey").match?(error: "my error message")
62
+ end
63
+
64
+ should "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
+ assert_equal expected, exception_description.exception_data
70
+ end
71
+
72
+ should "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
+ assert exception_description.match?(error: mobi), "does not match mobi"
78
+ assert exception_description.match?(error: credit), "does not match credit"
79
+ end
80
+ end
81
+ end
82
+ end