Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
15403 manish.sha 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
}