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.
- checksums.yaml +5 -5
- data/.gitignore +0 -3
- data/.ruby-version +1 -1
- data/Gemfile +16 -16
- data/Gemfile.lock +114 -110
- data/README.md +3 -7
- data/Rakefile +11 -8
- data/exception_handling.gemspec +10 -12
- data/lib/exception_handling/exception_info.rb +10 -13
- data/lib/exception_handling/honeybadger_callbacks.rb +59 -0
- data/lib/exception_handling/log_stub_error.rb +1 -2
- data/lib/exception_handling/methods.rb +53 -6
- data/lib/exception_handling/testing.rb +10 -20
- data/lib/exception_handling/version.rb +1 -1
- data/lib/exception_handling.rb +33 -68
- data/semaphore_ci/setup.sh +3 -0
- data/{spec → test}/helpers/controller_helpers.rb +0 -0
- data/{spec → test}/helpers/exception_helpers.rb +2 -2
- data/{spec/spec_helper.rb → test/test_helper.rb} +42 -63
- data/test/unit/exception_handling/exception_catalog_test.rb +85 -0
- data/test/unit/exception_handling/exception_description_test.rb +82 -0
- data/{spec/unit/exception_handling/exception_info_spec.rb → test/unit/exception_handling/exception_info_test.rb} +114 -153
- data/test/unit/exception_handling/honeybadger_callbacks_test.rb +122 -0
- data/{spec/unit/exception_handling/log_error_stub_spec.rb → test/unit/exception_handling/log_error_stub_test.rb} +22 -38
- data/{spec/unit/exception_handling/mailer_spec.rb → test/unit/exception_handling/mailer_test.rb} +18 -17
- data/test/unit/exception_handling/methods_test.rb +84 -0
- data/test/unit/exception_handling/sensu_test.rb +52 -0
- data/test/unit/exception_handling_test.rb +1109 -0
- metadata +60 -115
- data/.github/workflows/pipeline.yml +0 -33
- data/.rspec +0 -3
- data/Appraisals +0 -13
- data/CHANGELOG.md +0 -119
- data/gemfiles/rails_5.gemfile +0 -18
- data/gemfiles/rails_6.gemfile +0 -18
- data/lib/exception_handling/escalate_callback.rb +0 -19
- data/lib/exception_handling/logging_methods.rb +0 -27
- data/spec/rake_test_warning_false.rb +0 -20
- data/spec/unit/exception_handling/escalate_callback_spec.rb +0 -81
- data/spec/unit/exception_handling/exception_catalog_spec.rb +0 -85
- data/spec/unit/exception_handling/exception_description_spec.rb +0 -82
- data/spec/unit/exception_handling/logging_methods_spec.rb +0 -38
- data/spec/unit/exception_handling/methods_spec.rb +0 -105
- data/spec/unit/exception_handling/sensu_spec.rb +0 -51
- 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
|
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
|
-
|
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
|
data/lib/exception_handling.rb
CHANGED
@@ -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
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
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 =
|
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
|
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
|
-
"
|
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 = '',
|
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
|
-
|
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(
|
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
|
-
|
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,
|
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,
|
315
|
-
ExceptionHandling.logger.info(message,
|
284
|
+
def log_info(message, log_context = {})
|
285
|
+
ExceptionHandling.logger.info(message, log_context)
|
316
286
|
end
|
317
287
|
|
318
|
-
def log_debug(message,
|
319
|
-
ExceptionHandling.logger.debug(message,
|
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 = "",
|
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 = "",
|
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,
|
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,
|
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,
|
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,
|
329
|
+
escalate_error(ex, email_subject, custom_recipients, log_context)
|
360
330
|
nil
|
361
331
|
end
|
362
332
|
|
363
|
-
|
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,
|
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,
|
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,
|
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
|
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
|
-
|
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
|
-
|
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 '
|
4
|
-
require '
|
5
|
-
require '
|
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
|
16
|
-
attr_accessor :logged
|
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
|
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
|
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
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
110
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
170
|
+
assert true # To keep the assertion count accurate
|
160
171
|
else
|
161
|
-
|
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
|