exception_handling 0.2.0 → 1.0.0

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.
@@ -1,15 +1,14 @@
1
1
  require 'timeout'
2
2
  require 'active_support'
3
3
  require 'active_support/core_ext/hash'
4
- require "#{File.dirname(__FILE__)}/exception_handling_mailer"
5
4
 
6
- EXCEPTION_HANDLING_MAILER_SEND_MAIL = true unless defined?(EXCEPTION_HANDLING_MAILER_SEND_MAIL)
5
+ require 'invoca/utils'
7
6
 
8
- _ = ActiveSupport::HashWithIndifferentAccess
7
+ require "exception_handling/mailer"
8
+ require "exception_handling/methods"
9
+ require "exception_handling/log_stub_error"
9
10
 
10
- if defined?(EVENTMACHINE_EXCEPTION_HANDLING) && EVENTMACHINE_EXCEPTION_HANDLING
11
- require 'em/protocols/smtpclient'
12
- end
11
+ _ = ActiveSupport::HashWithIndifferentAccess
13
12
 
14
13
  module ExceptionHandling # never included
15
14
 
@@ -20,9 +19,7 @@ module ExceptionHandling # never included
20
19
  SUMMARY_THRESHOLD = 5
21
20
  SUMMARY_PERIOD = 60*60 # 1.hour
22
21
 
23
-
24
22
  SECTIONS = [:request, :session, :environment, :backtrace, :event_response]
25
- EXCEPTION_FILTER_LIST_PATH = "#{defined?(Rails) ? Rails.root : '.'}/config/exception_filters.yml"
26
23
 
27
24
  ENVIRONMENT_WHITELIST = [
28
25
  /^HTTP_/,
@@ -71,19 +68,77 @@ EOF
71
68
 
72
69
  AUTHENTICATION_HEADERS = ['HTTP_AUTHORIZATION','X-HTTP_AUTHORIZATION','X_HTTP_AUTHORIZATION','REDIRECT_X_HTTP_AUTHORIZATION']
73
70
 
74
- @logger = Rails.logger if defined?(Rails)
75
-
76
-
77
71
  class << self
78
- attr_accessor :email_environment
72
+
73
+ #
74
+ # required settings
75
+ #
79
76
  attr_accessor :server_name
80
77
  attr_accessor :sender_address
81
78
  attr_accessor :exception_recipients
79
+ attr_accessor :logger
80
+
81
+ def server_name
82
+ @server_name or raise ArgumentError, "You must assign a value to #{self.name}.server_name"
83
+ end
84
+
85
+ def sender_address
86
+ @sender_address or raise ArgumentError, "You must assign a value to #{self.name}.sender_address"
87
+ end
88
+
89
+ def exception_recipients
90
+ @exception_recipients or raise ArgumentError, "You must assign a value to #{self.name}.exception_recipients"
91
+ end
92
+
93
+ def logger
94
+ @logger or raise ArgumentError, "You must assign a value to #{self.name}.logger"
95
+ end
96
+
97
+ #
98
+ # optional settings
99
+ #
100
+ attr_accessor :escalation_recipients
101
+ attr_accessor :email_environment
102
+ attr_accessor :filter_list_filename
103
+ attr_accessor :mailer_send_enabled
104
+ attr_accessor :eventmachine_safe
105
+ attr_accessor :eventmachine_synchrony
106
+ attr_accessor :custom_data_hook
107
+ attr_accessor :post_log_error_hook
108
+ attr_accessor :stub_handler
109
+
110
+ @filter_list_filename = "./config/exception_filters.yml"
111
+ @mailer_send_enabled = true
112
+ @email_environment = ""
113
+ @eventmachine_safe = false
114
+ @eventmachine_synchrony = false
115
+
116
+ # set this for operation within an eventmachine reactor
117
+ def eventmachine_safe=(bool)
118
+ if bool != true && bool != false
119
+ raise ArgumentError, "#{self.name}.eventmachine_safe must be a boolean."
120
+ end
121
+ if bool
122
+ require 'eventmachine'
123
+ require 'em/protocols/smtpclient'
124
+ end
125
+ @eventmachine_safe = bool
126
+ end
82
127
 
128
+ # set this for EM::Synchrony async operation
129
+ def eventmachine_synchrony=(bool)
130
+ if bool != true && bool != false
131
+ raise ArgumentError, "#{self.name}.eventmachine_synchrony must be a boolean."
132
+ end
133
+ @eventmachine_synchrony = bool
134
+ end
135
+
136
+ #
137
+ # internal settings (don't set directly)
138
+ #
83
139
  attr_accessor :current_controller
84
140
  attr_accessor :last_exception_timestamp
85
141
  attr_accessor :periodic_exception_intervals
86
- attr_accessor :logger
87
142
 
88
143
  #
89
144
  # Gets called by Rack Middleware: DebugExceptions or ShowExceptions
@@ -97,6 +152,10 @@ EOF
97
152
  timestamp = set_log_error_timestamp
98
153
  exception_data = exception_to_data(exception, env, timestamp)
99
154
 
155
+ if stub_handler
156
+ return stub_handler.handle_stub_log_error(exception_data)
157
+ end
158
+
100
159
  # TODO: add a more interesting custom description, like:
101
160
  # custom_description = ": caught and processed by Rack middleware filter #{rack_filter}"
102
161
  # which would be nice, but would also require changing quite a few tests
@@ -106,7 +165,10 @@ EOF
106
165
  if should_send_email?
107
166
  controller = env['action_controller.instance']
108
167
  # controller may not exist in some cases (like most 404 errors)
109
- extract_and_merge_controller_data(controller, exception_data) if controller
168
+ if controller
169
+ extract_and_merge_controller_data(controller, exception_data)
170
+ controller.session["last_exception_timestamp"] = ExceptionHandling.last_exception_timestamp
171
+ end
110
172
  log_error_email(exception_data, exception)
111
173
  end
112
174
  end
@@ -125,6 +187,10 @@ EOF
125
187
  timestamp = set_log_error_timestamp
126
188
  data = exception_to_data(ex, exception_context, timestamp)
127
189
 
190
+ if stub_handler
191
+ return stub_handler.handle_stub_log_error(data)
192
+ end
193
+
128
194
  write_exception_to_log(ex, exception_context, timestamp)
129
195
 
130
196
  if treat_as_local
@@ -151,6 +217,8 @@ EOF
151
217
  log_error_email(data, ex)
152
218
  end
153
219
 
220
+ rescue LogErrorStub::UnexpectedExceptionLogged, LogErrorStub::ExpectedExceptionNotLogged
221
+ raise
154
222
  rescue Exception => ex
155
223
  $stderr.puts("ExceptionHandling.log_error rescued exception while logging #{exception_context}: #{exception_or_string}:\n#{ex.class}: #{ex}\n#{ex.backtrace.join("\n")}")
156
224
  write_exception_to_log(ex, "ExceptionHandling.log_error rescued exception while logging #{exception_context}: #{exception_or_string}", timestamp)
@@ -178,7 +246,7 @@ EOF
178
246
  def extract_and_merge_controller_data(controller, data)
179
247
  data[:request] = {
180
248
  :params => controller.request.parameters.to_hash,
181
- :rails_root => Rails.root,
249
+ :rails_root => defined?(Rails) ? Rails.root : "Rails.root not defined. Is this a test environment?",
182
250
  :url => controller.complete_request_uri
183
251
  }
184
252
  data[:environment].merge!(controller.request.env.to_hash)
@@ -217,12 +285,23 @@ EOF
217
285
  log_error ex, exception_context
218
286
  end
219
287
 
220
- def ensure_escalation( email_subject )
288
+ def escalate_error(exception_or_string, email_subject)
289
+ ex = make_exception(exception_or_string)
290
+ log_error(ex)
291
+ escalate(email_subject, ex, ExceptionHandling.last_exception_timestamp)
292
+ end
293
+
294
+ def escalate_warning(message, email_subject)
295
+ ex = Warning.new(message)
296
+ log_error(ex)
297
+ escalate(email_subject, ex, ExceptionHandling.last_exception_timestamp)
298
+ end
299
+
300
+ def ensure_escalation(email_subject)
221
301
  begin
222
302
  yield
223
303
  rescue => ex
224
- log_error ex
225
- escalate(email_subject, ex, ExceptionHandling.last_exception_timestamp)
304
+ escalate_error(ex, email_subject)
226
305
  nil
227
306
  end
228
307
  end
@@ -232,7 +311,7 @@ EOF
232
311
  end
233
312
 
234
313
  def should_send_email?
235
- defined?( EXCEPTION_HANDLING_MAILER_SEND_MAIL ) && EXCEPTION_HANDLING_MAILER_SEND_MAIL
314
+ ExceptionHandling.mailer_send_enabled
236
315
  end
237
316
 
238
317
  def trace_timing(description)
@@ -253,9 +332,14 @@ EOF
253
332
  end
254
333
  end
255
334
 
256
- # TODO: fix test to not use this.
257
335
  def enhance_exception_data(data)
258
-
336
+ return if ! ExceptionHandling.custom_data_hook
337
+ begin
338
+ ExceptionHandling.custom_data_hook.call(data)
339
+ rescue Exception => ex
340
+ # can't call log_error here or we will blow the call stack
341
+ log_info( "Unable to execute custom custom_data_hook callback. #{ex.message} #{ex.backtrace.each {|l| "#{l}\n"}}" )
342
+ end
259
343
  end
260
344
 
261
345
  private
@@ -281,18 +365,30 @@ EOF
281
365
  Errplane.transmit(exc, :custom_data => data) unless exc.is_a?(Warning)
282
366
  end
283
367
 
368
+ execute_custom_log_error_callback(data, exc)
369
+
284
370
  nil
285
371
  end
286
372
 
373
+ def execute_custom_log_error_callback(exception_data, exception)
374
+ return if ! ExceptionHandling.post_log_error_hook
375
+ begin
376
+ ExceptionHandling.post_log_error_hook.call(exception_data, exception)
377
+ rescue Exception => ex
378
+ # can't call log_error here or we will blow the call stack
379
+ log_info( "Unable to execute custom log_error callback. #{ex.message} #{ex.backtrace.each {|l| "#{l}\n"}}" )
380
+ end
381
+ end
382
+
287
383
  def escalate( email_subject, ex, timestamp )
288
384
  data = exception_to_data( ex, nil, timestamp )
289
385
  deliver(ExceptionHandling::Mailer.escalation_notification(email_subject, data))
290
386
  end
291
387
 
292
388
  def deliver(mail_object)
293
- if defined?(EVENTMACHINE_EXCEPTION_HANDLING) && EVENTMACHINE_EXCEPTION_HANDLING
389
+ if ExceptionHandling.eventmachine_safe
294
390
  EventMachine.schedule do # in case we're running outside the reactor
295
- async_send_method = EVENTMACHINE_EXCEPTION_HANDLING == :Synchrony ? :asend : :send
391
+ async_send_method = ExceptionHandling.eventmachine_synchrony ? :asend : :send
296
392
  smtp_settings = ActionMailer::Base.smtp_settings
297
393
  send_deferrable = EventMachine::Protocols::SmtpClient.__send__(
298
394
  async_send_method,
@@ -303,7 +399,7 @@ EOF
303
399
  :auth => {:type=>:plain, :username=>smtp_settings[:user_name], :password=>smtp_settings[:password]},
304
400
  :from => mail_object['from'].to_s,
305
401
  :to => mail_object['to'].to_s,
306
- :body => mail_object.body.to_s
402
+ :content => "#{mail_object}.\r\n"
307
403
  }
308
404
  )
309
405
  send_deferrable.errback { |err| ExceptionHandling.logger.fatal("Failed to email by SMTP: #{err.inspect}") }
@@ -320,7 +416,7 @@ EOF
320
416
  yield
321
417
  end
322
418
  rescue StandardError, MailerTimeout => ex
323
- $stderr.puts("ExceptionHandling::safe_email_deliver rescued: #{ex.class}: #{ex}\n#{ex.backtrace.join("\n")}")
419
+ #$stderr.puts("ExceptionHandling::safe_email_deliver rescued: #{ex.class}: #{ex}\n#{ex.backtrace.join("\n")}")
324
420
  log_error( ex, "ExceptionHandling::safe_email_deliver", nil, true )
325
421
  end
326
422
 
@@ -341,10 +437,10 @@ EOF
341
437
  def normalize_exception_data( data )
342
438
  if data[:location].nil?
343
439
  data[:location] = {}
344
- if data[:request] && data[:request].key?( :params )
345
- data[:location][:controller] = data[:request][:params]['controller']
346
- data[:location][:action] = "fake action"
347
- end
440
+ if data[:request] && data[:request].key?( :params )
441
+ data[:location][:controller] = data[:request][:params]['controller']
442
+ data[:location][:action] = data[:request][:params]['action']
443
+ end
348
444
  end
349
445
  if data[:backtrace] && data[:backtrace].first
350
446
  first_line = data[:backtrace].first
@@ -375,7 +471,7 @@ EOF
375
471
  end
376
472
 
377
473
  def exception_filters
378
- @exception_filters ||= ExceptionFilters.new( EXCEPTION_FILTER_LIST_PATH )
474
+ @exception_filters ||= ExceptionFilters.new( ExceptionHandling.filter_list_filename )
379
475
  end
380
476
 
381
477
  def clean_backtrace(exception)
@@ -464,7 +560,7 @@ EOF
464
560
  data[:session] = exception_context['rack.session']
465
561
  data[:environment] = exception_context
466
562
  else
467
- data[:error] = "#{data[:error_string]}#{': ' + exception_context unless exception_context.blank?}"
563
+ data[:error] = "#{data[:error_string]}#{': ' + exception_context.to_s unless exception_context.blank?}"
468
564
  data[:environment] = { :message => exception_context }
469
565
  end
470
566
  data
@@ -573,66 +669,6 @@ EOF
573
669
  def last_modified_time
574
670
  File.mtime( @filter_path )
575
671
  end
576
- end
577
-
578
-
579
- public
580
-
581
- module Methods # included on models and controllers
582
- protected
583
- def log_error(exception_or_string, exception_context = '')
584
- controller = self if respond_to?(:request) && respond_to?(:session)
585
- ExceptionHandling.log_error(exception_or_string, exception_context, controller)
586
- end
587
672
 
588
- def log_error_rack(exception_or_string, exception_context = '', rack_filter = '')
589
- ExceptionHandling.log_error_rack(exception_or_string, exception_context, controller)
590
- end
591
-
592
- def log_warning(message)
593
- log_error(Warning.new(message))
594
- end
595
-
596
- def log_info(message)
597
- ExceptionHandling.logger.info( message )
598
- end
599
-
600
- def log_debug(message)
601
- ExceptionHandling.logger.debug( message )
602
- end
603
-
604
- def ensure_safe(exception_context = "")
605
- begin
606
- yield
607
- rescue => ex
608
- log_error ex, exception_context
609
- nil
610
- end
611
- end
612
-
613
- def ensure_escalation(*args)
614
- ExceptionHandling.ensure_escalation(*args) do
615
- yield
616
- end
617
- end
618
-
619
- # Store aside the current controller when included
620
- LONG_REQUEST_SECONDS = (defined?(Rails) && Rails.env == 'test' ? 300 : 30)
621
- def set_current_controller
622
- ExceptionHandling.current_controller = self
623
- result = nil
624
- time = Benchmark.measure do
625
- result = yield
626
- end
627
- name = " in #{controller_name}::#{action_name}" rescue " "
628
- log_error( "Long controller action detected#{name} %.4fs " % time.real ) if time.real > LONG_REQUEST_SECONDS && !['development', 'test'].include?(Rails.env)
629
- result
630
- ensure
631
- ExceptionHandling.current_controller = nil
632
- end
633
-
634
- def self.included( controller )
635
- controller.around_filter :set_current_controller if controller.respond_to? :around_filter
636
- end
637
673
  end
638
674
  end
data/test/test_helper.rb CHANGED
@@ -2,9 +2,44 @@ require 'active_support'
2
2
  require 'active_support/time'
3
3
  require 'active_support/test_case'
4
4
  require 'action_mailer'
5
+ require 'hobo_support'
5
6
  require 'shoulda'
6
- require 'mocha/setup'
7
- require './test/mocha_patch'
7
+ require 'rr'
8
+ require 'minitest/autorun'
9
+ require 'pry'
10
+
11
+ require 'exception_handling'
12
+ require 'exception_handling/testing'
13
+
14
+ class LoggerStub
15
+ attr_accessor :logged
16
+
17
+ def initialize
18
+ clear
19
+ end
20
+
21
+ def info(message)
22
+ logged << message
23
+ end
24
+
25
+ def warn(message)
26
+ logged << message
27
+ end
28
+
29
+ def fatal(message)
30
+ logged << message
31
+ end
32
+
33
+ def clear
34
+ @logged = []
35
+ end
36
+ end
37
+
38
+ ExceptionHandling.logger = LoggerStub.new
39
+
40
+ def dont_stub_log_error
41
+ true
42
+ end
8
43
 
9
44
  ActionMailer::Base.delivery_method = :test
10
45
 
@@ -22,6 +57,16 @@ class ActiveSupport::TestCase
22
57
  Time.now_override = nil
23
58
 
24
59
  ActionMailer::Base.deliveries.clear
60
+
61
+ ExceptionHandling.email_environment = 'Test'
62
+ ExceptionHandling.sender_address = 'server@example.com'
63
+ ExceptionHandling.exception_recipients = 'exceptions@example.com'
64
+ ExceptionHandling.escalation_recipients = 'escalation@example.com'
65
+ ExceptionHandling.server_name = 'server'
66
+ ExceptionHandling.mailer_send_enabled = true
67
+ ExceptionHandling.filter_list_filename = "./config/exception_filters.yml"
68
+ ExceptionHandling.eventmachine_safe = false
69
+ ExceptionHandling.eventmachine_synchrony = false
25
70
  end
26
71
 
27
72
  teardown do
@@ -68,6 +113,14 @@ class ActiveSupport::TestCase
68
113
  end
69
114
  end
70
115
 
116
+ def assert_equal_with_diff arg1, arg2, msg = ''
117
+ if arg1 == arg2
118
+ assert true # To keep the assertion count accurate
119
+ else
120
+ assert_equal arg1, arg2, "#{msg}\n#{Diff.compare(arg1, arg2)}"
121
+ end
122
+ end
123
+
71
124
  class Time
72
125
  class << self
73
126
  attr_reader :now_override
@@ -0,0 +1,83 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module ExceptionHandling
4
+ class LogErrorStubTest < ActiveSupport::TestCase
5
+
6
+ include LogErrorStub
7
+
8
+ context "while running tests" do
9
+ setup do
10
+ setup_log_error_stub
11
+ end
12
+
13
+ teardown do
14
+ teardown_log_error_stub
15
+ end
16
+
17
+ should "raise an error when log_error and log_warning are called" do
18
+ begin
19
+ ExceptionHandling.log_error("Something happened")
20
+ flunk
21
+ rescue Exception => ex #LogErrorStub::UnexpectedExceptionLogged => ex
22
+ assert ex.to_s.starts_with?("StandardError: Something happened"), ex.to_s
23
+ end
24
+
25
+ begin
26
+ class ::RaisedError < StandardError; end
27
+ raise ::RaisedError, "This should raise"
28
+ rescue => ex
29
+ begin
30
+ ExceptionHandling.log_error(ex)
31
+ rescue LogErrorStub::UnexpectedExceptionLogged => ex_inner
32
+ assert ex_inner.to_s.starts_with?("RaisedError: This should raise"), ex_inner.to_s
33
+ end
34
+ end
35
+ end
36
+
37
+ should "allow for the regex specification of an expected exception to be ignored" do
38
+ exception_pattern = /StandardError: This is a test error/
39
+ assert_nil exception_whitelist # test that exception expectations are cleared
40
+ expects_exception(exception_pattern)
41
+ assert_equal exception_pattern, exception_whitelist[0][0]
42
+ begin
43
+ ExceptionHandling.log_error("This is a test error")
44
+ rescue => ex
45
+ flunk # Shouldn't raise an error in this case
46
+ end
47
+ end
48
+
49
+ should "allow for the string specification of an expected exception to be ignored" do
50
+ exception_pattern = "StandardError: This is a test error"
51
+ assert_nil exception_whitelist # test that exception expectations are cleared
52
+ expects_exception(exception_pattern)
53
+ assert_equal exception_pattern, exception_whitelist[0][0]
54
+ begin
55
+ ExceptionHandling.log_error("This is a test error")
56
+ rescue => ex
57
+ flunk # Shouldn't raise an error in this case
58
+ end
59
+ end
60
+
61
+ should "allow multiple errors to be ignored" do
62
+ class IgnoredError < StandardError; end
63
+ assert_nil exception_whitelist # test that exception expectations are cleared
64
+ expects_exception(/StandardError: This is a test error/)
65
+ expects_exception(/IgnoredError: This should be ignored/)
66
+ ExceptionHandling.log_error("This is a test error")
67
+ begin
68
+ raise IgnoredError, "This should be ignored"
69
+ rescue IgnoredError => ex
70
+ ExceptionHandling.log_error(ex)
71
+ end
72
+ end
73
+
74
+ should "expect exception twice if declared twice" do
75
+ expects_exception(/StandardError: ERROR: I love lamp/)
76
+ expects_exception(/StandardError: ERROR: I love lamp/)
77
+ ExceptionHandling.log_error("ERROR: I love lamp")
78
+ ExceptionHandling.log_error("ERROR: I love lamp")
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,79 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module ExceptionHandling
4
+ class MailerTest < ActionMailer::TestCase
5
+
6
+ include ActionDispatch::Assertions::SelectorAssertions
7
+ tests ExceptionHandling::Mailer
8
+
9
+ def dont_stub_log_error
10
+ true
11
+ end
12
+
13
+ context "ExceptionHandling::Mailer" do
14
+ setup do
15
+ ExceptionHandling.email_environment = 'Test'
16
+ ExceptionHandling.sender_address = %("Test Exception Mailer" <null_exception@invoca.com>)
17
+ ExceptionHandling.exception_recipients = ['test_exception@invoca.com']
18
+ ExceptionHandling.escalation_recipients = ['test_escalation@invoca.com']
19
+ end
20
+
21
+ should "deliver" do
22
+ #ActionMailer::Base.delivery_method = :smtp
23
+ result = ExceptionHandling::Mailer.exception_notification({ :error => "Test Error."}).deliver
24
+ assert_match /Test Error./, result.body.to_s
25
+ assert_equal_with_diff ['test_exception@invoca.com'], result.to
26
+ assert_emails 1
27
+ end
28
+
29
+ context "log_parser_exception_notification" do
30
+ should "send with string" do
31
+ result = ExceptionHandling::Mailer.log_parser_exception_notification("This is my fake error", "My Fake Subj").deliver
32
+ assert_equal "Test exception: My Fake Subj: This is my fake error", result.subject
33
+ assert_match(/This is my fake error/, result.body.to_s)
34
+ assert_emails 1
35
+ end
36
+ end
37
+
38
+ context "escalation_notification" do
39
+ should "send all the information" do
40
+ ExceptionHandling.email_environment = 'Staging Full'
41
+ ExceptionHandling.server_name = 'test-fe3'
42
+
43
+ ExceptionHandling::Mailer.escalation_notification("Your Favorite <b>Feature<b> Failed", :error_string => "It failed because of an error\n <i>More Info<i>", :timestamp => 1234567 ).deliver
44
+
45
+ assert_emails 1
46
+ result = ActionMailer::Base.deliveries.last
47
+ body_html = HTML::Document.new(result.body.to_s)
48
+ assert_equal_with_diff ['test_escalation@invoca.com'], result.to
49
+ assert_equal ["Test Escalation Mailer <null_escalation@invoca.com>"], result[:from].formatted
50
+ assert_equal "Staging Full Escalation: Your Favorite <b>Feature<b> Failed", result.subject
51
+ assert_select body_html.root, "html" do
52
+ assert_select "title", "Exception Escalation"
53
+ assert_select "body br", { :count => 4 }, result.body.to_s # plus 1 for the multiline summary
54
+ assert_select "body h3", "Your Favorite &lt;b&gt;Feature&lt;b&gt; Failed", result.body.to_s
55
+ assert_select "body", /1234567/
56
+ assert_select "body", /It failed because of an error\n &lt;i&gt;More Info&lt;i&gt;/
57
+ assert_select "body", /test-fe3/
58
+ #assert_select "body", /#{Web::Application::GIT_REVISION}/
59
+ end
60
+ end
61
+
62
+ should "use defaults for missing fields" do
63
+ result = ExceptionHandling::Mailer.escalation_notification("Your Favorite Feature Failed", :error_string => "It failed because of an error\n More Info")
64
+ body_html = HTML::Document.new(result.body.to_s)
65
+
66
+ assert_equal_with_diff ['test_escalation@invoca.com'], result.to
67
+ assert_equal ["null_escalation@invoca.com"], result.from
68
+ assert_equal 'Test Escalation: Your Favorite Feature Failed', result.subject
69
+ assert_select body_html.root, "html" do
70
+ assert_select "body i", true, result.body.to_s do |is|
71
+ assert_select is[0], "i", 'no error #'
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ require "exception_handling/testing"
4
+
5
+ module ExceptionHandling
6
+ class MethodsTest < ActiveSupport::TestCase
7
+
8
+ def dont_stub_log_error
9
+ true
10
+ end
11
+
12
+ context "ExceptionHandling.Methods" do
13
+ setup do
14
+ @controller = Testing::ControllerStub.new
15
+ ExceptionHandling.stub_handler = nil
16
+ end
17
+
18
+ teardown do
19
+ Time.now_override = nil
20
+ end
21
+
22
+ should "set the around filter" do
23
+ assert_equal :set_current_controller, Testing::ControllerStub.around_filter_method
24
+ assert_nil ExceptionHandling.current_controller
25
+ @controller.simulate_around_filter( ) do
26
+ assert_equal @controller, ExceptionHandling.current_controller
27
+ end
28
+ assert_nil ExceptionHandling.current_controller
29
+ end
30
+
31
+ should "use the current_controller when available" do
32
+ mock(ExceptionHandling.logger).fatal(/blah/)
33
+ @controller.simulate_around_filter do
34
+ ExceptionHandling.log_error( ArgumentError.new("blah") )
35
+ mail = ActionMailer::Base.deliveries.last
36
+ assert_match( @controller.request.request_uri, mail.body.to_s )
37
+ # assert_match( Username.first.username.to_s, mail.body.to_s ) if defined?(Username)
38
+ end
39
+ end
40
+
41
+ should "report long running controller action" do
42
+ # This was the original stub:
43
+ # Rails.expects(:env).times(2).returns('production')
44
+ # but this stubbing approach no longer works
45
+ # Rails is setting the long controller timeout on module load
46
+ # in exception_handling.rb - that happens before this test ever gets run
47
+ # we can set Rails.env here, which we do, because it affects part of the real-time
48
+ # logic check for whether to raise (which we want). To overcome the setting
49
+ # on the long controller timeout I have set the Time.now_override to 1.day.from_now
50
+ # instead of 1.hour.from_now
51
+ mock(ExceptionHandling).log_error(/Long controller action detected in #{@controller.class.name.split("::").last}::test_action/, anything, anything)
52
+ @controller.simulate_around_filter( ) do
53
+ Time.now_override = 1.day.from_now
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end