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,977 +0,0 @@
1
- require './test/test_helper'
2
- require 'minitest/autorun'
3
- require 'exception_handling'
4
-
5
- ExceptionHandling.email_environment = 'test'
6
- ExceptionHandling.sender_address = 'server@example.com'
7
- ExceptionHandling.exception_recipients = 'exceptions@example.com'
8
- ExceptionHandling.server_name = 'server'
9
-
10
- class ExceptionHandlingTest < ActiveSupport::TestCase
11
- class LoggerStub
12
- attr_accessor :logged
13
-
14
- def initialize
15
- clear
16
- end
17
-
18
- def info(message)
19
- logged << message
20
- end
21
-
22
- def warn(message)
23
- logged << message
24
- end
25
-
26
- def fatal(message)
27
- logged << message
28
- end
29
-
30
- def clear
31
- @logged = []
32
- end
33
- end
34
-
35
-
36
- ExceptionHandling.logger = LoggerStub.new
37
-
38
- def dont_stub_log_error
39
- true
40
- end
41
-
42
- class TestController
43
- class Request
44
- attr_accessor :parameters, :protocol, :host, :request_uri, :env, :session_options
45
- def initialize
46
- @parameters = {:id => "1"}
47
- @protocol = 'http'
48
- @host = 'localhost'
49
- @request_uri = "/fun/testing.html?foo=bar"
50
- @env = {:HOST => "local"}
51
- @session_options = { :id => '93951506217301' }
52
- end
53
- end
54
-
55
- attr_accessor :request, :session
56
- class << self
57
- attr_accessor :around_filter_method
58
- end
59
-
60
- def initialize
61
- @request = Request.new
62
- @session_id = "ZKL95"
63
- @session =
64
- if defined?(Username)
65
- {
66
- :login_count => 22,
67
- :username_id => Username.first.id,
68
- :user_id => User.first.id,
69
- }
70
- else
71
- { }
72
- end
73
- end
74
-
75
- def simulate_around_filter( &block )
76
- set_current_controller( &block )
77
- end
78
-
79
- def controller_name
80
- "TestController"
81
- end
82
-
83
- def action_name
84
- "test_action"
85
- end
86
-
87
- def self.around_filter( method )
88
- TestController.around_filter_method = method
89
- end
90
-
91
- def complete_request_uri
92
- "#{@request.protocol}#{@request.host}#{@request.request_uri}"
93
- end
94
-
95
- include ExceptionHandling::Methods
96
- end
97
-
98
- if defined?(Rails)
99
- class TestAdvertiser < Advertiser
100
- def test_log_error( ex, message=nil )
101
- log_error(ex, message)
102
- end
103
-
104
- def test_ensure_escalation(summary)
105
- ensure_escalation(summary) do
106
- yield
107
- end
108
- end
109
-
110
- def test_log_warning( message )
111
- log_warning(message)
112
- end
113
-
114
- def test_ensure_safe(message="",&blk)
115
- ensure_safe(message,&blk)
116
- end
117
-
118
- def self.task_exception exception
119
- @@task_exception = exception
120
- end
121
- end
122
- end
123
-
124
- module EventMachineStub
125
- class << self
126
- attr_accessor :block
127
-
128
- def schedule(&block)
129
- @block = block
130
- end
131
- end
132
- end
133
-
134
- class SmtpClientStub
135
- class << self
136
- attr_reader :block
137
- attr_reader :last_method
138
-
139
- def errback(&block)
140
- @block = block
141
- end
142
-
143
- def send_hash
144
- @send_hash ||= {}
145
- end
146
-
147
- def send(hash)
148
- @last_method = :send
149
- send_hash.clear
150
- send_hash.merge!(hash)
151
- self
152
- end
153
-
154
- def asend(hash)
155
- send(hash)
156
- @last_method = :asend
157
- self
158
- end
159
- end
160
- end
161
-
162
- class SmtpClientErrbackStub < SmtpClientStub
163
- end
164
-
165
- context "Exception Handling" do
166
- setup do
167
- ExceptionHandling.send(:clear_exception_summary)
168
- end
169
-
170
- context "exception filter parsing and loading" do
171
- should "happen without an error" do
172
- File.stubs(:mtime).returns( incrementing_mtime )
173
- exception_filters = ExceptionHandling.send( :exception_filters )
174
- assert( exception_filters.is_a?( ExceptionHandling::ExceptionFilters ) )
175
- assert_nothing_raised "Loading the exception filter should not raise" do
176
- exception_filters.send :load_file
177
- end
178
- assert !exception_filters.filtered?( "Scott says unlikely to ever match" )
179
- end
180
- end
181
-
182
- context "ExceptionHandling.ensure_safe" do
183
- should "log an exception if an exception is raised." do
184
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
185
- ExceptionHandling.ensure_safe { raise ArgumentError.new("blah") }
186
- end
187
-
188
- should "should not log an exception if an exception is not raised." do
189
- ExceptionHandling.logger.expects(:fatal).never
190
- ExceptionHandling.ensure_safe { ; }
191
- end
192
-
193
- should "return its value if used during an assignment" do
194
- ExceptionHandling.logger.expects(:fatal).never
195
- b = ExceptionHandling.ensure_safe { 5 }
196
- assert_equal 5, b
197
- end
198
-
199
- should "return nil if an exception is raised during an assignment" do
200
- ExceptionHandling.logger.expects(:fatal).returns(nil)
201
- b = ExceptionHandling.ensure_safe { raise ArgumentError.new("blah") }
202
- assert_equal nil, b
203
- end
204
-
205
- should "allow a message to be appended to the error when logged." do
206
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
207
- b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError.new("blah") }
208
- assert_nil b
209
- end
210
-
211
- should "only rescue StandardError and descendents" do
212
- assert_raise(Exception) { ExceptionHandling.ensure_safe("mooo") { raise Exception } }
213
-
214
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
215
- b = ExceptionHandling.ensure_safe("mooo") { raise StandardError.new("blah") }
216
- assert_nil b
217
- end
218
- end
219
-
220
- context "ExceptionHandling.ensure_completely_safe" do
221
- should "log an exception if an exception is raised." do
222
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
223
- ExceptionHandling.ensure_completely_safe { raise ArgumentError.new("blah") }
224
- end
225
-
226
- should "should not log an exception if an exception is not raised." do
227
- ExceptionHandling.logger.expects(:fatal).never
228
- ExceptionHandling.ensure_completely_safe { ; }
229
- end
230
-
231
- should "return its value if used during an assignment" do
232
- ExceptionHandling.logger.expects(:fatal).never
233
- b = ExceptionHandling.ensure_completely_safe { 5 }
234
- assert_equal 5, b
235
- end
236
-
237
- should "return nil if an exception is raised during an assignment" do
238
- ExceptionHandling.logger.expects(:fatal).returns(nil)
239
- b = ExceptionHandling.ensure_completely_safe { raise ArgumentError.new("blah") }
240
- assert_equal nil, b
241
- end
242
-
243
- should "allow a message to be appended to the error when logged." do
244
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
245
- b = ExceptionHandling.ensure_completely_safe("mooo") { raise ArgumentError.new("blah") }
246
- assert_nil b
247
- end
248
-
249
- should "rescue any instance or child of Exception" do
250
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
251
- ExceptionHandling::ensure_completely_safe { raise Exception.new("blah") }
252
- end
253
-
254
- should "not rescue the special exceptions that Ruby uses" do
255
- [SystemExit, SystemStackError, NoMemoryError, SecurityError].each do |exception|
256
- assert_raise exception do
257
- ExceptionHandling.ensure_completely_safe do
258
- raise exception.new
259
- end
260
- end
261
- end
262
- end
263
- end
264
-
265
- context "ExceptionHandling.ensure_escalation" do
266
- should "log the exception as usual and send the proper email" do
267
- assert_equal 0, ActionMailer::Base.deliveries.count
268
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
269
- ExceptionHandling.ensure_escalation( "Favorite Feature") { raise ArgumentError.new("blah") }
270
- assert_equal 2, ActionMailer::Base.deliveries.count
271
- email = ActionMailer::Base.deliveries.last
272
- assert_equal 'test Escalation: Favorite Feature', email.subject
273
- assert_match 'ArgumentError: blah', email.body.to_s
274
- assert_match ExceptionHandling.last_exception_timestamp.to_s, email.body.to_s
275
- end
276
-
277
- should "should not escalate if an exception is not raised." do
278
- assert_equal 0, ActionMailer::Base.deliveries.count
279
- ExceptionHandling.logger.expects(:fatal).never
280
- ExceptionHandling.ensure_escalation('Ignored') { ; }
281
- assert_equal 0, ActionMailer::Base.deliveries.count
282
- end
283
-
284
- should "log if the escalation email can not be sent" do
285
- Mail::Message.any_instance.expects(:deliver).times(2).returns(nil).then.raises(RuntimeError.new "Delivery Error")
286
- exception_count = 0
287
- exception_regexs = [/first_test_exception/, /safe_email_deliver .*Delivery Error/]
288
-
289
- $stderr.stubs(:puts)
290
- ExceptionHandling.logger.expects(:fatal).times(2).with { |ex| ex =~ exception_regexs[exception_count] or raise "Unexpected [#{exception_count}]: #{ex.inspect}"; exception_count += 1; true }
291
- ExceptionHandling.ensure_escalation("Not Used") { raise ArgumentError.new("first_test_exception") }
292
- #assert_equal 0, ActionMailer::Base.deliveries.count
293
- end
294
- end
295
-
296
- context "exception timestamp" do
297
- setup do
298
- Time.now_override = Time.parse( '1986-5-21 4:17 am UTC' )
299
- end
300
-
301
- should "include the timestamp when the exception is logged" do
302
- ExceptionHandling.logger.expects(:fatal).with { |ex| ex =~ /\(Error:517033020\) ArgumentError mooo \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
303
- b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError.new("blah") }
304
- assert_nil b
305
-
306
- assert_equal 517033020, ExceptionHandling.last_exception_timestamp
307
-
308
- assert_emails 1
309
- assert_match /517033020/, ActionMailer::Base.deliveries[-1].body.to_s
310
- end
311
- end
312
-
313
- if defined?(LogErrorStub)
314
- context "while running tests" do
315
- setup do
316
- stub_log_error
317
- end
318
-
319
- should "raise an error when log_error and log_warning are called" do
320
- begin
321
- ExceptionHandling.log_error("Something happened")
322
- flunk
323
- rescue LogErrorStub::UnexpectedExceptionLogged => ex
324
- assert ex.to_s.starts_with?("StandardError: Something happened"), ex.to_s
325
- end
326
-
327
- begin
328
- class ::RaisedError < StandardError; end
329
- raise ::RaisedError, "This should raise"
330
- rescue => ex
331
- begin
332
- ExceptionHandling.log_error(ex)
333
- rescue LogErrorStub::UnexpectedExceptionLogged => ex_inner
334
- assert ex_inner.to_s.starts_with?("RaisedError: This should raise"), ex_inner.to_s
335
- end
336
- end
337
- end
338
-
339
- should "allow for the regex specification of an expected exception to be ignored" do
340
- exception_pattern = /StandardError: This is a test error/
341
- assert_nil exception_whitelist # test that exception expectations are cleared
342
- expects_exception(exception_pattern)
343
- assert_equal exception_pattern, exception_whitelist[0][0]
344
- begin
345
- ExceptionHandling.log_error("This is a test error")
346
- rescue => ex
347
- flunk # Shouldn't raise an error in this case
348
- end
349
- end
350
-
351
- should "allow for the string specification of an expected exception to be ignored" do
352
- exception_pattern = "StandardError: This is a test error"
353
- assert_nil exception_whitelist # test that exception expectations are cleared
354
- expects_exception(exception_pattern)
355
- assert_equal exception_pattern, exception_whitelist[0][0]
356
- begin
357
- ExceptionHandling.log_error("This is a test error")
358
- rescue => ex
359
- flunk # Shouldn't raise an error in this case
360
- end
361
- end
362
-
363
- should "allow multiple errors to be ignored" do
364
- class IgnoredError < StandardError; end
365
- assert_nil exception_whitelist # test that exception expectations are cleared
366
- expects_exception /StandardError: This is a test error/
367
- expects_exception /IgnoredError: This should be ignored/
368
- ExceptionHandling.log_error("This is a test error")
369
- begin
370
- raise IgnoredError, "This should be ignored"
371
- rescue IgnoredError => ex
372
- ExceptionHandling.log_error(ex)
373
- end
374
- end
375
-
376
- should "expect exception twice if declared twice" do
377
- expects_exception /StandardError: ERROR: I love lamp/
378
- expects_exception /StandardError: ERROR: I love lamp/
379
- ExceptionHandling.log_error("ERROR: I love lamp")
380
- ExceptionHandling.log_error("ERROR: I love lamp")
381
- end
382
- end
383
- end
384
-
385
- should "send just one copy of exceptions that don't repeat" do
386
- ExceptionHandling.log_error(exception_1)
387
- ExceptionHandling.log_error(exception_2)
388
- assert_emails 2
389
- assert_match /Exception 1/, ActionMailer::Base.deliveries[-2].subject
390
- assert_match /Exception 2/, ActionMailer::Base.deliveries[-1].subject
391
- end
392
-
393
- should "only send 5 of a repeated error" do
394
- assert_emails 5 do
395
- 10.times do
396
- ExceptionHandling.log_error(exception_1)
397
- end
398
- end
399
- end
400
-
401
- should "only send 5 of a repeated error but don't send summary if 6th is different" do
402
- assert_emails 5 do
403
- 5.times do
404
- ExceptionHandling.log_error(exception_1)
405
- end
406
- end
407
- assert_emails 1 do
408
- ExceptionHandling.log_error(exception_2)
409
- end
410
- end
411
-
412
- should "send the summary when the error is encountered an hour after the first occurrence" do
413
- assert_emails 5 do # 5 exceptions, 4 summarized
414
- 9.times do |t|
415
- ExceptionHandling.log_error(exception_1)
416
- end
417
- end
418
- Time.now_override = 2.hours.from_now
419
- assert_emails 1 do # 1 summary (4 + 1 = 5) after 2 hours
420
- ExceptionHandling.log_error(exception_1)
421
- end
422
- assert_match /\[5 SUMMARIZED\]/, ActionMailer::Base.deliveries.last.subject
423
- assert_match /This exception occurred 5 times since/, ActionMailer::Base.deliveries.last.body.to_s
424
-
425
- assert_emails 0 do # still summarizing...
426
- 7.times do
427
- ExceptionHandling.log_error(exception_1)
428
- end
429
- end
430
-
431
- Time.now_override = 3.hours.from_now
432
-
433
- assert_emails 1 + 2 do # 1 summary and 2 new
434
- 2.times do
435
- ExceptionHandling.log_error(exception_2)
436
- end
437
- end
438
- assert_match /\[7 SUMMARIZED\]/, ActionMailer::Base.deliveries[-3].subject
439
- assert_match /This exception occurred 7 times since/, ActionMailer::Base.deliveries[-3].body.to_s
440
- end
441
-
442
- should "send the summary if a summary is available, but not sent when another exception comes up" do
443
- assert_emails 5 do # 5 to start summarizing
444
- 6.times do
445
- ExceptionHandling.log_error(exception_1)
446
- end
447
- end
448
-
449
- assert_emails 1 + 1 do # 1 summary of previous, 1 from new exception
450
- ExceptionHandling.log_error(exception_2)
451
- end
452
-
453
- assert_match /\[1 SUMMARIZED\]/, ActionMailer::Base.deliveries[-2].subject
454
- assert_match /This exception occurred 1 times since/, ActionMailer::Base.deliveries[-2].body.to_s
455
-
456
- assert_emails 5 do # 5 to start summarizing
457
- 10.times do
458
- ExceptionHandling.log_error(exception_1)
459
- end
460
- end
461
-
462
- assert_emails 0 do # still summarizing
463
- 11.times do
464
- ExceptionHandling.log_error(exception_1)
465
- end
466
- end
467
- end
468
-
469
- class EventResponse
470
- def to_s
471
- "message from to_s!"
472
- end
473
- end
474
-
475
- should "allow sections to have data with just a to_s method" do
476
- ExceptionHandling.log_error("This is my RingSwitch example. Log, don't email!") do |data|
477
- data.merge!(:event_response => EventResponse.new)
478
- end
479
- assert_emails 1
480
- assert_match /message from to_s!/, ActionMailer::Base.deliveries.last.body.to_s
481
- end
482
- end
483
-
484
- should "rescue exceptions that happen in log_error" do
485
- ExceptionHandling.stubs(:make_exception).raises(ArgumentError.new("Bad argument"))
486
- ExceptionHandling.expects(:write_exception_to_log).with do |ex, context, timestamp|
487
- ex.to_s['Bad argument'] or raise "Unexpected ex #{ex.class} - #{ex}"
488
- context['ExceptionHandling.log_error rescued exception while logging Runtime message'] or raise "Unexpected context #{context}"
489
- true
490
- end
491
- $stderr.stubs(:puts)
492
- ExceptionHandling.log_error(RuntimeError.new("A runtime error"), "Runtime message")
493
- end
494
-
495
- should "rescue exceptions that happen when log_error yields" do
496
- ExceptionHandling.expects(:write_exception_to_log).with do |ex, context, timestamp|
497
- ex.to_s['Bad argument'] or raise "=================================================\nUnexpected ex #{ex.class} - #{ex}\n#{ex.backtrace.join("\n")}\n=============================================\n"
498
- context['Context message'] or raise "Unexpected context #{context}"
499
- true
500
- end
501
- ExceptionHandling.log_error(ArgumentError.new("Bad argument"), "Context message") { |data| raise 'Error!!!' }
502
- end
503
-
504
- context "Exception Filtering" do
505
- setup do
506
- filter_list = { :exception1 => { :error => "my error message" },
507
- :exception2 => { :error => "some other message", :session => "misc data" } }
508
- YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filter_list ) )
509
-
510
- # bump modified time up to get the above filter loaded
511
- File.stubs(:mtime).returns( incrementing_mtime )
512
- end
513
-
514
- should "handle case where filter list is not found" do
515
- YAML.stubs(:load_file).raises( Errno::ENOENT.new( "File not found" ) )
516
-
517
- ExceptionHandling.log_error( "My error message is in list" )
518
- assert_emails 1
519
- end
520
-
521
- should "log exception and suppress email when exception is on filter list" do
522
- ExceptionHandling.log_error( "Error message is not in list" )
523
- assert_emails 1
524
-
525
- ActionMailer::Base.deliveries.clear
526
- ExceptionHandling.log_error( "My error message is in list" )
527
- assert_emails 0
528
- end
529
-
530
- should "allow filtering exception on any text in exception data" do
531
- filters = { :exception1 => { :session => "^data: my extra session data" } }
532
- YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filters ) )
533
-
534
- ActionMailer::Base.deliveries.clear
535
- ExceptionHandling.log_error( "No match here" ) do |data|
536
- data[:session] = {
537
- :key => "@session_id",
538
- :data => "my extra session data"
539
- }
540
- end
541
- assert_emails 0
542
-
543
- ActionMailer::Base.deliveries.clear
544
- ExceptionHandling.log_error( "No match here" ) do |data|
545
- data[:session] = {
546
- :key => "@session_id",
547
- :data => "my extra session <no match!> data"
548
- }
549
- end
550
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
551
- end
552
-
553
- should "reload filter list on the next exception if file was modified" do
554
- ExceptionHandling.log_error( "Error message is not in list" )
555
- assert_emails 1
556
-
557
- filter_list = { :exception1 => { :error => "Error message is not in list" } }
558
- YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filter_list ) )
559
- File.stubs(:mtime).returns( incrementing_mtime )
560
-
561
- ActionMailer::Base.deliveries.clear
562
- ExceptionHandling.log_error( "Error message is not in list" )
563
- assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
564
- end
565
-
566
- should "not consider filter if both error message and body do not match" do
567
- # error message matches, but not full text
568
- ExceptionHandling.log_error( "some other message" )
569
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
570
-
571
- # now both match
572
- ActionMailer::Base.deliveries.clear
573
- ExceptionHandling.log_error( "some other message" ) do |data|
574
- data[:session] = {:some_random_key => "misc data"}
575
- end
576
- assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
577
- end
578
-
579
- should "skip environment keys not on whitelist" do
580
- ExceptionHandling.log_error( "some message" ) do |data|
581
- data[:environment] = { :SERVER_PROTOCOL => "HTTP/1.0", :RAILS_SECRETS_YML_CONTENTS => 'password: VERY_SECRET_PASSWORD' }
582
- end
583
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
584
- mail = ActionMailer::Base.deliveries.last
585
- assert_nil mail.body.to_s["RAILS_SECRETS_YML_CONTENTS"], mail.body.to_s # this is not on whitelist
586
- assert mail.body.to_s["SERVER_PROTOCOL: HTTP/1.0" ], mail.body.to_s # this is
587
- end
588
-
589
- should "omit environment defaults" do
590
- ExceptionHandling.log_error( "some message" ) do |data|
591
- data[:environment] = {:SERVER_PORT => '80', :SERVER_PROTOCOL => "HTTP/1.0"}
592
- end
593
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
594
- mail = ActionMailer::Base.deliveries.last
595
- assert_nil mail.body.to_s["SERVER_PORT" ], mail.body.to_s # this was default
596
- assert mail.body.to_s["SERVER_PROTOCOL: HTTP/1.0"], mail.body.to_s # this was not
597
- end
598
-
599
- should "reject the filter file if any contain all empty regexes" do
600
- filter_list = { :exception1 => { :error => "", :session => "" },
601
- :exception2 => { :error => "is not in list", :session => "" } }
602
- YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filter_list ) )
603
- File.stubs(:mtime).returns( incrementing_mtime )
604
-
605
- ActionMailer::Base.deliveries.clear
606
- ExceptionHandling.log_error( "Error message is not in list" )
607
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
608
- end
609
-
610
- context "Exception Handling Mailer" do
611
- should "create email" do
612
- ExceptionHandling.log_error(exception_1) do |data|
613
- data[:request] = { :params => {:id => 10993}, :url => "www.ringrevenue.com" }
614
- data[:session] = { :key => "DECAFE" }
615
- end
616
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
617
- assert mail = ActionMailer::Base.deliveries.last
618
- assert_equal ['exceptions@example.com'], mail.to
619
- assert_equal ['server@example.com'].to_s, mail.from.to_s
620
- assert_match /Exception 1/, mail.to_s
621
- assert_match /key: DECAFE/, mail.to_s
622
- assert_match /id: 10993/, mail.to_s
623
- end
624
-
625
- EXPECTED_SMTP_HASH =
626
- {
627
- :host => 'localhost',
628
- :domain => 'localhost.localdomain',
629
- :from => 'server@example.com',
630
- :to => 'exceptions@example.com'
631
- }
632
-
633
- [true, :Synchrony].each do |synchrony_flag|
634
- context "EVENTMACHINE_EXCEPTION_HANDLING = #{synchrony_flag}" do
635
- setup do
636
- set_test_const('EVENTMACHINE_EXCEPTION_HANDLING', synchrony_flag)
637
- EventMachineStub.block = nil
638
- set_test_const('EventMachine', EventMachineStub)
639
- set_test_const('EventMachine::Protocols', Module.new)
640
- end
641
-
642
- should "schedule EventMachine STMP when EventMachine defined" do
643
- set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientStub)
644
-
645
- ExceptionHandling.log_error(exception_1)
646
- assert EventMachineStub.block
647
- EventMachineStub.block.call
648
- EXPECTED_SMTP_HASH.map { |key, value| assert_equal value, SmtpClientStub.send_hash[key].to_s, SmtpClientStub.send_hash.inspect }
649
- assert_equal (synchrony_flag == :Synchrony ? :asend : :send), SmtpClientStub.last_method
650
- assert_match /Exception 1/, SmtpClientStub.send_hash[:body]
651
- assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
652
- end
653
-
654
- should "log fatal on EventMachine STMP errback" do
655
- set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientErrbackStub)
656
- ExceptionHandling.logger.expects(:fatal).twice.with do |message|
657
- assert message =~ /Failed to email by SMTP: "credential mismatch"/ || message =~ /Exception 1/, message
658
- true
659
- end
660
- ExceptionHandling.log_error(exception_1)
661
- assert EventMachineStub.block
662
- EventMachineStub.block.call
663
- SmtpClientErrbackStub.block.call("credential mismatch")
664
- EXPECTED_SMTP_HASH.map { |key, value| assert_equal value, SmtpClientErrbackStub.send_hash[key].to_s, SmtpClientErrbackStub.send_hash.inspect }
665
- assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
666
- end
667
- end
668
- end
669
- end
670
-
671
- should "truncate email subject" do
672
- text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM".split('').join("123456789")
673
- begin
674
- raise text
675
- rescue => ex
676
- ExceptionHandling.log_error( ex )
677
- end
678
- assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.inspect }
679
- mail = ActionMailer::Base.deliveries.last
680
- subject = "test exception: RuntimeError: " + text
681
- assert_equal subject[0,300], mail.subject
682
- end
683
- end
684
-
685
- if defined?(Rails)
686
- context "ExceptionHandling.Methods" do
687
- setup do
688
- @controller = TestController.new
689
- end
690
-
691
- teardown do
692
- Time.now_override = nil
693
- end
694
-
695
- should "set the around filter" do
696
- assert_equal :set_current_controller, TestController.around_filter_method
697
- assert_nil ExceptionHandling.current_controller
698
- @controller.simulate_around_filter( ) do
699
- assert_equal @controller, ExceptionHandling.current_controller
700
- end
701
- assert_nil ExceptionHandling.current_controller
702
- end
703
-
704
- should "use the current controller when included in a Model" do
705
- ActiveRecord::Base.logger.expects(:fatal).with { |ex| ex =~ /blah/ }
706
- @controller.simulate_around_filter( ) do
707
- a = TestAdvertiser.new :name => 'Joe Ads'
708
- a.test_log_error( ArgumentError.new("blah") )
709
- mail = ActionMailer::Base.deliveries.last
710
- assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
711
- assert_match( @controller.request.request_uri, mail.body.to_s )
712
- assert_match( Username.first.username.to_s, mail.body.to_s )
713
- end
714
- end
715
-
716
- should "use the current_controller when available" do
717
- ActiveRecord::Base.logger.expects(:fatal).with( ) { |ex| ex =~ /blah/ }
718
- @controller.simulate_around_filter do
719
- ExceptionHandling.log_error( ArgumentError.new("blah") )
720
- mail = ActionMailer::Base.deliveries.last
721
- assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
722
- assert_match( @controller.request.request_uri, mail.body )
723
- assert_match( Username.first.username.to_s, mail.body )
724
- mail = ActionMailer::Base.deliveries.last
725
- assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
726
- assert_match( @controller.request.request_uri, mail.body.to_s )
727
- assert_match( Username.first.username.to_s, mail.body.to_s )
728
- assert mail = ActionMailer::Base.deliveries.last
729
- assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
730
- assert_match( @controller.request.request_uri, mail.body.to_s )
731
- assert_match( Username.first.username.to_s, mail.body.to_s )
732
- end
733
- end
734
-
735
- should "report long running controller action" do
736
- # If stubbing this causes problems, retreat.
737
- Rails.expects(:env).returns('production')
738
- ExceptionHandling.logger.expects(:fatal).with { |ex| ex =~ /Long controller action detected in TestController::test_action/ or raise "Unexpected: #{ex.inspect}"}
739
- @controller.simulate_around_filter( ) do
740
- Time.now_override = 1.hour.from_now
741
- end
742
- end
743
- end
744
-
745
- context "a model object" do
746
- setup do
747
- @a = TestAdvertiser.new :name => 'Joe Ads'
748
- end
749
-
750
- context "with an argument error" do
751
- setup do
752
- begin
753
- raise ArgumentError.new("blah");
754
- rescue => ex
755
- @argument_error = ex
756
- end
757
- end
758
-
759
- context "log_error on a model" do
760
- should "log errors" do
761
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
762
- @a.test_log_error( @argument_error )
763
- end
764
-
765
- should "log errors from strings" do
766
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling\.rb/ or raise "Unexpected: #{ex.inspect}" }
767
- @a.test_log_error( "blah" )
768
- end
769
-
770
- should "log errors with strings" do
771
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo.* \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
772
- @a.test_log_error( @argument_error, "mooo" )
773
- end
774
- end
775
-
776
- context "ensure_escalation on a model" do
777
- should "work" do
778
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
779
- @a.test_ensure_escalation 'Favorite Feature' do
780
- raise @argument_error
781
- end
782
- assert_equal 2, ActionMailer::Base.deliveries.count
783
- email = ActionMailer::Base.deliveries.last
784
- assert_equal 'development-local Escalation: Favorite Feature', email.subject
785
- assert_match 'ArgumentError: blah', email.body.to_s
786
- end
787
- end
788
-
789
- context "ExceptionHandling.log_error" do
790
- should "log errors" do
791
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
792
- ExceptionHandling.log_error( @argument_error )
793
- end
794
-
795
- should "log errors from strings" do
796
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling\.rb/ or raise "Unexpected: #{ex.inspect}" }
797
- ExceptionHandling.log_error( "blah" )
798
- end
799
-
800
- should "log errors with strings" do
801
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo.*\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
802
- ExceptionHandling.log_error( @argument_error, "mooo" )
803
- end
804
- end
805
- end
806
-
807
- should "log warnings" do
808
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /blah/ }
809
- @a.test_log_warning("blah")
810
- end
811
-
812
- context "ensure_safe on the model" do
813
- should "log an exception if an exception is raised." do
814
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
815
- @a.test_ensure_safe { raise ArgumentError.new("blah") }
816
- end
817
-
818
- should "should not log an exception if an exception is not raised." do
819
- ExceptionHandling.logger.expects(:fatal).never
820
- @a.test_ensure_safe { ; }
821
- end
822
-
823
- should "return its value if used during an assignment" do
824
- ExceptionHandling.logger.expects(:fatal).never
825
- b = @a.test_ensure_safe { 5 }
826
- assert_equal 5, b
827
- end
828
-
829
- should "return nil if an exception is raised during an assignment" do
830
- ExceptionHandling.logger.expects(:fatal).returns(nil)
831
- b = @a.test_ensure_safe { raise ArgumentError.new("blah") }
832
- assert_nil b
833
- end
834
-
835
- should "allow a message to be appended to the error when logged." do
836
- ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo.*\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
837
- b = @a.test_ensure_safe("mooo") { raise ArgumentError.new("blah") }
838
- assert_nil b
839
- end
840
- end
841
- end
842
- end
843
-
844
- context "Exception mapping" do
845
- setup do
846
- @data = {
847
- :environment=>{
848
- 'HTTP_HOST' => "localhost",
849
- 'HTTP_REFERER' => "http://localhost/action/controller/instance",
850
- },
851
- :session=>{
852
- :data=>{
853
- :affiliate_id=> defined?(Affiliate) ? Affiliate.first.id : '1',
854
- :edit_mode=> true,
855
- :advertiser_id=> defined?(Advertiser) ? Advertiser.first.id : '1',
856
- :username_id=> defined?(Username) ? Username.first.id : '1',
857
- :user_id=> defined?(User) ? User.first.id : '1',
858
- :flash=>{},
859
- :impersonated_organization_pk=> 'Advertiser_1'
860
- }
861
- },
862
- :request=>{},
863
- :backtrace=>["[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:580:in `call_filters'", "[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:601:in `run_before_filters'"],
864
- :api_key=>"none",
865
- :error_class=>"StandardError",
866
- :error=>'Some error message'
867
- }
868
- end
869
-
870
- should "clean backtraces" do
871
- begin
872
- raise "test exception"
873
- rescue => ex
874
- backtrace = ex.backtrace
875
- end
876
- result = ExceptionHandling.send(:clean_backtrace, ex).to_s
877
- assert_not_equal result, backtrace
878
- end
879
-
880
- should "clean params" do
881
- p = {'password' => 'apple', 'username' => 'sam' }
882
- ExceptionHandling.send( :clean_params, p )
883
- assert_equal "[FILTERED]", p['password']
884
- assert_equal 'sam', p['username']
885
- end
886
- end
887
-
888
- context "log_perodically" do
889
- setup do
890
- Time.now_override = Time.now # Freeze time
891
- ExceptionHandling.logger.clear
892
- end
893
-
894
- teardown do
895
- Time.now_override = nil
896
- end
897
-
898
- should "log immediately when we are expected to log" do
899
- logger_stub = ExceptionHandling.logger
900
-
901
- ExceptionHandling.log_periodically(:test_periodic_exception, 30.minutes, "this will be written")
902
- assert_equal 1, logger_stub.logged.size
903
-
904
- Time.now_override = Time.now + 5.minutes
905
- ExceptionHandling.log_periodically(:test_periodic_exception, 30.minutes, "this will not be written")
906
- assert_equal 1, logger_stub.logged.size
907
-
908
- ExceptionHandling.log_periodically(:test_another_periodic_exception, 30.minutes, "this will be written")
909
- assert_equal 2, logger_stub.logged.size
910
-
911
- Time.now_override = Time.now + 26.minutes
912
-
913
- ExceptionHandling.log_periodically(:test_periodic_exception, 30.minutes, "this will be written")
914
- assert_equal 3, logger_stub.logged.size
915
- end
916
- end
917
-
918
- context "Errplane" do
919
- module ErrplaneStub
920
- end
921
-
922
- setup do
923
- set_test_const('Errplane', ErrplaneStub)
924
- end
925
-
926
- should "forward exceptions" do
927
- ex = data = nil
928
- Errplane.expects(:transmit).with do |ex_, data_|
929
- ex, data = ex_, data_
930
- true
931
- end
932
-
933
- ExceptionHandling.log_error(exception_1, "context")
934
-
935
- assert_equal exception_1, ex, ex.inspect
936
- custom_data = data[:custom_data]
937
- custom_data["error"].include?("context") or raise "Wrong custom_data #{custom_data["error"].inspect}"
938
- end
939
-
940
- should "not forward warnings" do
941
- never = true
942
- Errplane.stubs(:transmit).with do
943
- never = false
944
- true
945
- end
946
-
947
- ExceptionHandling.log_warning("warning message")
948
-
949
- assert never, "transmit should not have been called"
950
- end
951
- end
952
-
953
- private
954
-
955
- def incrementing_mtime
956
- @mtime ||= Time.now
957
- @mtime += 1.day
958
- end
959
-
960
- def exception_1
961
- @ex1 ||=
962
- begin
963
- raise StandardError, "Exception 1"
964
- rescue => ex
965
- ex
966
- end
967
- end
968
-
969
- def exception_2
970
- @ex2 ||=
971
- begin
972
- raise StandardError, "Exception 2"
973
- rescue => ex
974
- ex
975
- end
976
- end
977
- end