統計
| ブランチ: | リビジョン:

pictcode / lib / Cake / Test / Case / Error / ExceptionRendererTest.php @ 0b1b8047

履歴 | 表示 | アノテート | ダウンロード (25.066 KB)

1
<?php
2
/**
3
 * ExceptionRendererTest file
4
 *
5
 * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
14
 * @package       Cake.Test.Case.Error
15
 * @since         CakePHP(tm) v 2.0
16
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
17
 */
18

    
19
App::uses('ExceptionRenderer', 'Error');
20
App::uses('Controller', 'Controller');
21
App::uses('Component', 'Controller');
22
App::uses('Router', 'Routing');
23

    
24
/**
25
 * Short description for class.
26
 *
27
 * @package       Cake.Test.Case.Error
28
 */
29
class AuthBlueberryUser extends CakeTestModel {
30

    
31
/**
32
 * useTable property
33
 *
34
 * @var string
35
 */
36
        public $useTable = false;
37
}
38

    
39
/**
40
 * BlueberryComponent class
41
 *
42
 * @package       Cake.Test.Case.Error
43
 */
44
class BlueberryComponent extends Component {
45

    
46
/**
47
 * testName property
48
 *
49
 * @return void
50
 */
51
        public $testName = null;
52

    
53
/**
54
 * initialize method
55
 *
56
 * @return void
57
 */
58
        public function initialize(Controller $controller) {
59
                $this->testName = 'BlueberryComponent';
60
        }
61

    
62
}
63

    
64
/**
65
 * TestErrorController class
66
 *
67
 * @package       Cake.Test.Case.Error
68
 */
69
class TestErrorController extends Controller {
70

    
71
/**
72
 * uses property
73
 *
74
 * @var array
75
 */
76
        public $uses = array();
77

    
78
/**
79
 * components property
80
 *
81
 * @return void
82
 */
83
        public $components = array('Blueberry');
84

    
85
/**
86
 * beforeRender method
87
 *
88
 * @return void
89
 */
90
        public function beforeRender() {
91
                echo $this->Blueberry->testName;
92
        }
93

    
94
/**
95
 * index method
96
 *
97
 * @return void
98
 */
99
        public function index() {
100
                $this->autoRender = false;
101
                return 'what up';
102
        }
103

    
104
}
105

    
106
/**
107
 * MyCustomExceptionRenderer class
108
 *
109
 * @package       Cake.Test.Case.Error
110
 */
111
class MyCustomExceptionRenderer extends ExceptionRenderer {
112

    
113
/**
114
 * custom error message type.
115
 *
116
 * @return void
117
 */
118
        public function missingWidgetThing() {
119
                echo 'widget thing is missing';
120
        }
121

    
122
}
123

    
124
/**
125
 * Exception class for testing app error handlers and custom errors.
126
 *
127
 * @package       Cake.Test.Case.Error
128
 */
129
class MissingWidgetThingException extends NotFoundException {
130
}
131

    
132
/**
133
 * ExceptionRendererTest class
134
 *
135
 * @package       Cake.Test.Case.Error
136
 */
137
class ExceptionRendererTest extends CakeTestCase {
138

    
139
        protected $_restoreError = false;
140

    
141
/**
142
 * setup create a request object to get out of router later.
143
 *
144
 * @return void
145
 */
146
        public function setUp() {
147
                parent::setUp();
148
                Configure::write('Config.language', 'eng');
149
                App::build(array(
150
                        'View' => array(
151
                                CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS
152
                        )
153
                ), App::RESET);
154
                Router::reload();
155

    
156
                $request = new CakeRequest(null, false);
157
                $request->base = '';
158
                Router::setRequestInfo($request);
159
                Configure::write('debug', 2);
160
        }
161

    
162
/**
163
 * tearDown
164
 *
165
 * @return void
166
 */
167
        public function tearDown() {
168
                parent::tearDown();
169
                if ($this->_restoreError) {
170
                        restore_error_handler();
171
                }
172
        }
173

    
174
/**
175
 * Mocks out the response on the ExceptionRenderer object so headers aren't modified.
176
 *
177
 * @return void
178
 */
179
        protected function _mockResponse($error) {
180
                $error->controller->response = $this->getMock('CakeResponse', array('_sendHeader'));
181
                return $error;
182
        }
183

    
184
/**
185
 * test that methods declared in an ExceptionRenderer subclass are not converted
186
 * into error400 when debug > 0
187
 *
188
 * @return void
189
 */
190
        public function testSubclassMethodsNotBeingConvertedToError() {
191
                Configure::write('debug', 2);
192

    
193
                $exception = new MissingWidgetThingException('Widget not found');
194
                $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
195

    
196
                ob_start();
197
                $ExceptionRenderer->render();
198
                $result = ob_get_clean();
199

    
200
                $this->assertEquals('widget thing is missing', $result);
201
        }
202

    
203
/**
204
 * test that subclass methods are not converted when debug = 0
205
 *
206
 * @return void
207
 */
208
        public function testSubclassMethodsNotBeingConvertedDebug0() {
209
                Configure::write('debug', 0);
210
                $exception = new MissingWidgetThingException('Widget not found');
211
                $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
212

    
213
                $this->assertEquals('missingWidgetThing', $ExceptionRenderer->method);
214

    
215
                ob_start();
216
                $ExceptionRenderer->render();
217
                $result = ob_get_clean();
218

    
219
                $this->assertEquals('widget thing is missing', $result, 'Method declared in subclass converted to error400');
220
        }
221

    
222
/**
223
 * test that ExceptionRenderer subclasses properly convert framework errors.
224
 *
225
 * @return void
226
 */
227
        public function testSubclassConvertingFrameworkErrors() {
228
                Configure::write('debug', 0);
229

    
230
                $exception = new MissingControllerException('PostsController');
231
                $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
232

    
233
                $this->assertEquals('error400', $ExceptionRenderer->method);
234

    
235
                ob_start();
236
                $ExceptionRenderer->render();
237
                $result = ob_get_clean();
238

    
239
                $this->assertRegExp('/Not Found/', $result, 'Method declared in error handler not converted to error400. %s');
240
        }
241

    
242
/**
243
 * test things in the constructor.
244
 *
245
 * @return void
246
 */
247
        public function testConstruction() {
248
                $exception = new NotFoundException('Page not found');
249
                $ExceptionRenderer = new ExceptionRenderer($exception);
250

    
251
                $this->assertInstanceOf('CakeErrorController', $ExceptionRenderer->controller);
252
                $this->assertEquals('error400', $ExceptionRenderer->method);
253
                $this->assertEquals($exception, $ExceptionRenderer->error);
254
        }
255

    
256
/**
257
 * test that method gets coerced when debug = 0
258
 *
259
 * @return void
260
 */
261
        public function testErrorMethodCoercion() {
262
                Configure::write('debug', 0);
263
                $exception = new MissingActionException('Page not found');
264
                $ExceptionRenderer = new ExceptionRenderer($exception);
265

    
266
                $this->assertInstanceOf('CakeErrorController', $ExceptionRenderer->controller);
267
                $this->assertEquals('error400', $ExceptionRenderer->method);
268
                $this->assertEquals($exception, $ExceptionRenderer->error);
269
        }
270

    
271
/**
272
 * test that helpers in custom CakeErrorController are not lost
273
 *
274
 * @return void
275
 */
276
        public function testCakeErrorHelpersNotLost() {
277
                $testApp = CAKE . 'Test' . DS . 'test_app' . DS;
278
                App::build(array(
279
                        'Controller' => array(
280
                                $testApp . 'Controller' . DS
281
                        ),
282
                        'View/Helper' => array(
283
                                $testApp . 'View' . DS . 'Helper' . DS
284
                        ),
285
                        'View/Layouts' => array(
286
                                $testApp . 'View' . DS . 'Layouts' . DS
287
                        ),
288
                        'Error' => array(
289
                                $testApp . 'Error' . DS
290
                        ),
291
                ), App::RESET);
292

    
293
                App::uses('TestAppsExceptionRenderer', 'Error');
294
                $exception = new SocketException('socket exception');
295
                $renderer = new TestAppsExceptionRenderer($exception);
296

    
297
                ob_start();
298
                $renderer->render();
299
                $result = ob_get_clean();
300
                $this->assertContains('<b>peeled</b>', $result);
301
        }
302

    
303
/**
304
 * test that unknown exception types with valid status codes are treated correctly.
305
 *
306
 * @return void
307
 */
308
        public function testUnknownExceptionTypeWithExceptionThatHasA400Code() {
309
                $exception = new MissingWidgetThingException('coding fail.');
310
                $ExceptionRenderer = new ExceptionRenderer($exception);
311
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
312
                $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
313

    
314
                ob_start();
315
                $ExceptionRenderer->render();
316
                $result = ob_get_clean();
317

    
318
                $this->assertFalse(method_exists($ExceptionRenderer, 'missingWidgetThing'), 'no method should exist.');
319
                $this->assertEquals('error400', $ExceptionRenderer->method, 'incorrect method coercion.');
320
                $this->assertContains('coding fail', $result, 'Text should show up.');
321
        }
322

    
323
/**
324
 * test that unknown exception types with valid status codes are treated correctly.
325
 *
326
 * @return void
327
 */
328
        public function testUnknownExceptionTypeWithNoCodeIsA500() {
329
                $exception = new OutOfBoundsException('foul ball.');
330
                $ExceptionRenderer = new ExceptionRenderer($exception);
331
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
332
                $ExceptionRenderer->controller->response->expects($this->once())
333
                        ->method('statusCode')
334
                        ->with(500);
335

    
336
                ob_start();
337
                $ExceptionRenderer->render();
338
                $result = ob_get_clean();
339

    
340
                $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
341
                $this->assertContains('foul ball.', $result, 'Text should show up as its debug mode.');
342
        }
343

    
344
/**
345
 * test that unknown exceptions have messages ignored.
346
 *
347
 * @return void
348
 */
349
        public function testUnknownExceptionInProduction() {
350
                Configure::write('debug', 0);
351

    
352
                $exception = new OutOfBoundsException('foul ball.');
353
                $ExceptionRenderer = new ExceptionRenderer($exception);
354
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
355
                $ExceptionRenderer->controller->response->expects($this->once())
356
                        ->method('statusCode')
357
                        ->with(500);
358

    
359
                ob_start();
360
                $ExceptionRenderer->render();
361
                $result = ob_get_clean();
362

    
363
                $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
364
                $this->assertNotContains('foul ball.', $result, 'Text should no show up.');
365
                $this->assertContains('Internal Error', $result, 'Generic message only.');
366
        }
367

    
368
/**
369
 * test that unknown exception types with valid status codes are treated correctly.
370
 *
371
 * @return void
372
 */
373
        public function testUnknownExceptionTypeWithCodeHigherThan500() {
374
                $exception = new OutOfBoundsException('foul ball.', 501);
375
                $ExceptionRenderer = new ExceptionRenderer($exception);
376
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
377
                $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(501);
378

    
379
                ob_start();
380
                $ExceptionRenderer->render();
381
                $result = ob_get_clean();
382

    
383
                $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
384
                $this->assertContains('foul ball.', $result, 'Text should show up as its debug mode.');
385
        }
386

    
387
/**
388
 * testerror400 method
389
 *
390
 * @return void
391
 */
392
        public function testError400() {
393
                Router::reload();
394

    
395
                $request = new CakeRequest('posts/view/1000', false);
396
                Router::setRequestInfo($request);
397

    
398
                $exception = new NotFoundException('Custom message');
399
                $ExceptionRenderer = new ExceptionRenderer($exception);
400
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
401
                $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
402

    
403
                ob_start();
404
                $ExceptionRenderer->render();
405
                $result = ob_get_clean();
406

    
407
                $this->assertRegExp('/<h2>Custom message<\/h2>/', $result);
408
                $this->assertRegExp("/<strong>'.*?\/posts\/view\/1000'<\/strong>/", $result);
409
        }
410

    
411
/**
412
 * test that error400 only modifies the messages on CakeExceptions.
413
 *
414
 * @return void
415
 */
416
        public function testerror400OnlyChangingCakeException() {
417
                Configure::write('debug', 0);
418

    
419
                $exception = new NotFoundException('Custom message');
420
                $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
421

    
422
                ob_start();
423
                $ExceptionRenderer->render();
424
                $result = ob_get_clean();
425
                $this->assertContains('Custom message', $result);
426

    
427
                $exception = new MissingActionException(array('controller' => 'PostsController', 'action' => 'index'));
428
                $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
429

    
430
                ob_start();
431
                $ExceptionRenderer->render();
432
                $result = ob_get_clean();
433
                $this->assertContains('Not Found', $result);
434
        }
435

    
436
/**
437
 * test that error400 doesn't expose XSS
438
 *
439
 * @return void
440
 */
441
        public function testError400NoInjection() {
442
                Router::reload();
443

    
444
                $request = new CakeRequest('pages/<span id=333>pink</span></id><script>document.body.style.background = t=document.getElementById(333).innerHTML;window.alert(t);</script>', false);
445
                Router::setRequestInfo($request);
446

    
447
                $exception = new NotFoundException('Custom message');
448
                $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
449

    
450
                ob_start();
451
                $ExceptionRenderer->render();
452
                $result = ob_get_clean();
453

    
454
                $this->assertNotRegExp('#<script>document#', $result);
455
                $this->assertNotRegExp('#alert\(t\);</script>#', $result);
456
        }
457

    
458
/**
459
 * testError500 method
460
 *
461
 * @return void
462
 */
463
        public function testError500Message() {
464
                $exception = new InternalErrorException('An Internal Error Has Occurred');
465
                $ExceptionRenderer = new ExceptionRenderer($exception);
466
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
467
                $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
468

    
469
                ob_start();
470
                $ExceptionRenderer->render();
471
                $result = ob_get_clean();
472

    
473
                $this->assertRegExp('/<h2>An Internal Error Has Occurred<\/h2>/', $result);
474
        }
475

    
476
/**
477
 * testExceptionResponseHeader method
478
 *
479
 * @return void
480
 */
481
        public function testExceptionResponseHeader() {
482
                $exception = new MethodNotAllowedException('Only allowing POST and DELETE');
483
                $exception->responseHeader(array('Allow: POST, DELETE'));
484
                $ExceptionRenderer = new ExceptionRenderer($exception);
485

    
486
                //Replace response object with mocked object add back the original headers which had been set in ExceptionRenderer constructor
487
                $headers = $ExceptionRenderer->controller->response->header();
488
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('_sendHeader'));
489
                $ExceptionRenderer->controller->response->header($headers);
490

    
491
                $ExceptionRenderer->controller->response->expects($this->at(1))->method('_sendHeader')->with('Allow', 'POST, DELETE');
492
                ob_start();
493
                $ExceptionRenderer->render();
494
                ob_get_clean();
495
        }
496

    
497
/**
498
 * testMissingController method
499
 *
500
 * @return void
501
 */
502
        public function testMissingController() {
503
                $exception = new MissingControllerException(array('class' => 'PostsController'));
504
                $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
505

    
506
                ob_start();
507
                $ExceptionRenderer->render();
508
                $result = ob_get_clean();
509

    
510
                $this->assertRegExp('/<h2>Missing Controller<\/h2>/', $result);
511
                $this->assertRegExp('/<em>PostsController<\/em>/', $result);
512
        }
513

    
514
/**
515
 * Returns an array of tests to run for the various CakeException classes.
516
 *
517
 * @return void
518
 */
519
        public static function testProvider() {
520
                return array(
521
                        array(
522
                                new MissingActionException(array('controller' => 'PostsController', 'action' => 'index')),
523
                                array(
524
                                        '/<h2>Missing Method in PostsController<\/h2>/',
525
                                        '/<em>PostsController::<\/em><em>index\(\)<\/em>/'
526
                                ),
527
                                404
528
                        ),
529
                        array(
530
                                new PrivateActionException(array('controller' => 'PostsController', 'action' => '_secretSauce')),
531
                                array(
532
                                        '/<h2>Private Method in PostsController<\/h2>/',
533
                                        '/<em>PostsController::<\/em><em>_secretSauce\(\)<\/em>/'
534
                                ),
535
                                404
536
                        ),
537
                        array(
538
                                new MissingTableException(array('table' => 'articles', 'class' => 'Article', 'ds' => 'test')),
539
                                array(
540
                                        '/<h2>Missing Database Table<\/h2>/',
541
                                        '/Table <em>articles<\/em> for model <em>Article<\/em> was not found in datasource <em>test<\/em>/'
542
                                ),
543
                                500
544
                        ),
545
                        array(
546
                                new MissingDatabaseException(array('connection' => 'default')),
547
                                array(
548
                                        '/<h2>Missing Database Connection<\/h2>/',
549
                                        '/Confirm you have created the file/'
550
                                ),
551
                                500
552
                        ),
553
                        array(
554
                                new MissingViewException(array('file' => '/posts/about.ctp')),
555
                                array(
556
                                        "/posts\/about.ctp/"
557
                                ),
558
                                500
559
                        ),
560
                        array(
561
                                new MissingLayoutException(array('file' => 'layouts/my_layout.ctp')),
562
                                array(
563
                                        "/Missing Layout/",
564
                                        "/layouts\/my_layout.ctp/"
565
                                ),
566
                                500
567
                        ),
568
                        array(
569
                                new MissingConnectionException(array('class' => 'Mysql')),
570
                                array(
571
                                        '/<h2>Missing Database Connection<\/h2>/',
572
                                        '/A Database connection using "Mysql" was missing or unable to connect./',
573
                                ),
574
                                500
575
                        ),
576
                        array(
577
                                new MissingConnectionException(array('class' => 'Mysql', 'enabled' => false)),
578
                                array(
579
                                        '/<h2>Missing Database Connection<\/h2>/',
580
                                        '/A Database connection using "Mysql" was missing or unable to connect./',
581
                                        '/Mysql driver is NOT enabled/'
582
                                ),
583
                                500
584
                        ),
585
                        array(
586
                                new MissingDatasourceConfigException(array('config' => 'default')),
587
                                array(
588
                                        '/<h2>Missing Datasource Configuration<\/h2>/',
589
                                        '/The datasource configuration <em>default<\/em> was not found in database.php/'
590
                                ),
591
                                500
592
                        ),
593
                        array(
594
                                new MissingDatasourceException(array('class' => 'MyDatasource', 'plugin' => 'MyPlugin')),
595
                                array(
596
                                        '/<h2>Missing Datasource<\/h2>/',
597
                                        '/Datasource class <em>MyPlugin.MyDatasource<\/em> could not be found/'
598
                                ),
599
                                500
600
                        ),
601
                        array(
602
                                new MissingHelperException(array('class' => 'MyCustomHelper')),
603
                                array(
604
                                        '/<h2>Missing Helper<\/h2>/',
605
                                        '/<em>MyCustomHelper<\/em> could not be found./',
606
                                        '/Create the class <em>MyCustomHelper<\/em> below in file:/',
607
                                        '/(\/|\\\)MyCustomHelper.php/'
608
                                ),
609
                                500
610
                        ),
611
                        array(
612
                                new MissingBehaviorException(array('class' => 'MyCustomBehavior')),
613
                                array(
614
                                        '/<h2>Missing Behavior<\/h2>/',
615
                                        '/Create the class <em>MyCustomBehavior<\/em> below in file:/',
616
                                        '/(\/|\\\)MyCustomBehavior.php/'
617
                                ),
618
                                500
619
                        ),
620
                        array(
621
                                new MissingComponentException(array('class' => 'SideboxComponent')),
622
                                array(
623
                                        '/<h2>Missing Component<\/h2>/',
624
                                        '/Create the class <em>SideboxComponent<\/em> below in file:/',
625
                                        '/(\/|\\\)SideboxComponent.php/'
626
                                ),
627
                                500
628
                        ),
629
                        array(
630
                                new Exception('boom'),
631
                                array(
632
                                        '/Internal Error/'
633
                                ),
634
                                500
635
                        ),
636
                        array(
637
                                new RuntimeException('another boom'),
638
                                array(
639
                                        '/Internal Error/'
640
                                ),
641
                                500
642
                        ),
643
                        array(
644
                                new CakeException('base class'),
645
                                array('/Internal Error/'),
646
                                500
647
                        ),
648
                        array(
649
                                new ConfigureException('No file'),
650
                                array('/Internal Error/'),
651
                                500
652
                        )
653
                );
654
        }
655

    
656
/**
657
 * Test the various CakeException sub classes
658
 *
659
 * @dataProvider testProvider
660
 * @return void
661
 */
662
        public function testCakeExceptionHandling($exception, $patterns, $code) {
663
                $ExceptionRenderer = new ExceptionRenderer($exception);
664
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
665
                $ExceptionRenderer->controller->response->expects($this->once())
666
                        ->method('statusCode')
667
                        ->with($code);
668

    
669
                ob_start();
670
                $ExceptionRenderer->render();
671
                $result = ob_get_clean();
672

    
673
                foreach ($patterns as $pattern) {
674
                        $this->assertRegExp($pattern, $result);
675
                }
676
        }
677

    
678
/**
679
 * Test exceptions being raised when helpers are missing.
680
 *
681
 * @return void
682
 */
683
        public function testMissingRenderSafe() {
684
                $exception = new MissingHelperException(array('class' => 'Fail'));
685
                $ExceptionRenderer = new ExceptionRenderer($exception);
686

    
687
                $ExceptionRenderer->controller = $this->getMock('Controller', array('render'));
688
                $ExceptionRenderer->controller->helpers = array('Fail', 'Boom');
689
                $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
690
                $ExceptionRenderer->controller->expects($this->at(0))
691
                        ->method('render')
692
                        ->with('missingHelper')
693
                        ->will($this->throwException($exception));
694

    
695
                $response = $this->getMock('CakeResponse');
696
                $response->expects($this->once())
697
                        ->method('body')
698
                        ->with($this->stringContains('Helper class Fail'));
699

    
700
                $ExceptionRenderer->controller->response = $response;
701
                $ExceptionRenderer->render();
702
                sort($ExceptionRenderer->controller->helpers);
703
                $this->assertEquals(array('Form', 'Html', 'Session'), $ExceptionRenderer->controller->helpers);
704
        }
705

    
706
/**
707
 * Test that exceptions in beforeRender() are handled by outputMessageSafe
708
 *
709
 * @return void
710
 */
711
        public function testRenderExceptionInBeforeRender() {
712
                $exception = new NotFoundException('Not there, sorry');
713
                $ExceptionRenderer = new ExceptionRenderer($exception);
714

    
715
                $ExceptionRenderer->controller = $this->getMock('Controller', array('beforeRender'));
716
                $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
717
                $ExceptionRenderer->controller->expects($this->any())
718
                        ->method('beforeRender')
719
                        ->will($this->throwException($exception));
720

    
721
                $response = $this->getMock('CakeResponse');
722
                $response->expects($this->once())
723
                        ->method('body')
724
                        ->with($this->stringContains('Not there, sorry'));
725

    
726
                $ExceptionRenderer->controller->response = $response;
727
                $ExceptionRenderer->render();
728
        }
729

    
730
/**
731
 * Test that missing subDir/layoutPath don't cause other fatal errors.
732
 *
733
 * @return void
734
 */
735
        public function testMissingSubdirRenderSafe() {
736
                $exception = new NotFoundException();
737
                $ExceptionRenderer = new ExceptionRenderer($exception);
738

    
739
                $ExceptionRenderer->controller = $this->getMock('Controller', array('render'));
740
                $ExceptionRenderer->controller->helpers = array('Fail', 'Boom');
741
                $ExceptionRenderer->controller->layoutPath = 'json';
742
                $ExceptionRenderer->controller->subDir = 'json';
743
                $ExceptionRenderer->controller->viewClass = 'Json';
744
                $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
745

    
746
                $ExceptionRenderer->controller->expects($this->once())
747
                        ->method('render')
748
                        ->with('error400')
749
                        ->will($this->throwException($exception));
750

    
751
                $response = $this->getMock('CakeResponse');
752
                $response->expects($this->once())
753
                        ->method('body')
754
                        ->with($this->stringContains('Not Found'));
755
                $response->expects($this->once())
756
                        ->method('type')
757
                        ->with('html');
758

    
759
                $ExceptionRenderer->controller->response = $response;
760

    
761
                $ExceptionRenderer->render();
762
                $this->assertEquals('', $ExceptionRenderer->controller->layoutPath);
763
                $this->assertEquals('', $ExceptionRenderer->controller->subDir);
764
                $this->assertEquals('Errors', $ExceptionRenderer->controller->viewPath);
765
        }
766

    
767
/**
768
 * Test that missing plugin disables Controller::$plugin if the two are the same plugin.
769
 *
770
 * @return void
771
 */
772
        public function testMissingPluginRenderSafe() {
773
                $exception = new NotFoundException();
774
                $ExceptionRenderer = new ExceptionRenderer($exception);
775

    
776
                $ExceptionRenderer->controller = $this->getMock('Controller', array('render'));
777
                $ExceptionRenderer->controller->plugin = 'TestPlugin';
778
                $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
779

    
780
                $exception = new MissingPluginException(array('plugin' => 'TestPlugin'));
781
                $ExceptionRenderer->controller->expects($this->once())
782
                        ->method('render')
783
                        ->with('error400')
784
                        ->will($this->throwException($exception));
785

    
786
                $response = $this->getMock('CakeResponse');
787
                $response->expects($this->once())
788
                        ->method('body')
789
                        ->with($this->logicalAnd(
790
                                $this->logicalNot($this->stringContains('test plugin error500')),
791
                                $this->stringContains('Not Found')
792
                        ));
793

    
794
                $ExceptionRenderer->controller->response = $response;
795
                $ExceptionRenderer->render();
796
        }
797

    
798
/**
799
 * Test that missing plugin doesn't disable Controller::$plugin if the two aren't the same plugin.
800
 *
801
 * @return void
802
 */
803
        public function testMissingPluginRenderSafeWithPlugin() {
804
                App::build(array(
805
                        'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS)
806
                ), App::RESET);
807
                CakePlugin::load('TestPlugin');
808
                $exception = new NotFoundException();
809
                $ExceptionRenderer = new ExceptionRenderer($exception);
810

    
811
                $ExceptionRenderer->controller = $this->getMock('Controller', array('render'));
812
                $ExceptionRenderer->controller->plugin = 'TestPlugin';
813
                $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
814

    
815
                $exception = new MissingPluginException(array('plugin' => 'TestPluginTwo'));
816
                $ExceptionRenderer->controller->expects($this->once())
817
                        ->method('render')
818
                        ->with('error400')
819
                        ->will($this->throwException($exception));
820

    
821
                $response = $this->getMock('CakeResponse');
822
                $response->expects($this->once())
823
                        ->method('body')
824
                        ->with($this->logicalAnd(
825
                                $this->stringContains('test plugin error500'),
826
                                $this->stringContains('Not Found')
827
                        ));
828

    
829
                $ExceptionRenderer->controller->response = $response;
830
                $ExceptionRenderer->render();
831
                CakePlugin::unload();
832
        }
833

    
834
/**
835
 * Test that exceptions can be rendered when an request hasn't been registered
836
 * with Router
837
 *
838
 * @return void
839
 */
840
        public function testRenderWithNoRequest() {
841
                Router::reload();
842
                $this->assertNull(Router::getRequest(false));
843

    
844
                $exception = new Exception('Terrible');
845
                $ExceptionRenderer = new ExceptionRenderer($exception);
846
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
847
                $ExceptionRenderer->controller->response->expects($this->once())
848
                        ->method('statusCode')
849
                        ->with(500);
850

    
851
                ob_start();
852
                $ExceptionRenderer->render();
853
                $result = ob_get_clean();
854

    
855
                $this->assertContains('Internal Error', $result);
856
        }
857

    
858
/**
859
 * Tests the output of rendering a PDOException
860
 *
861
 * @return void
862
 */
863
        public function testPDOException() {
864
                $exception = new PDOException('There was an error in the SQL query');
865
                $exception->queryString = 'SELECT * from poo_query < 5 and :seven';
866
                $exception->params = array('seven' => 7);
867
                $ExceptionRenderer = new ExceptionRenderer($exception);
868
                $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
869
                $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
870

    
871
                ob_start();
872
                $ExceptionRenderer->render();
873
                $result = ob_get_clean();
874

    
875
                $this->assertContains('<h2>Database Error</h2>', $result);
876
                $this->assertContains('There was an error in the SQL query', $result);
877
                $this->assertContains(h('SELECT * from poo_query < 5 and :seven'), $result);
878
                $this->assertContains("'seven' => (int) 7", $result);
879
        }
880
}