exception_handling 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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