exception_handling 2.13.0 → 3.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|