ExceptionRendererTest.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  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. * @since CakePHP(tm) v 2.0
  15. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  16. */
  17. namespace Cake\Test\TestCase\Error;
  18. use Cake\Controller\Component;
  19. use Cake\Controller\Controller;
  20. use Cake\Core\App;
  21. use Cake\Core\Configure;
  22. use Cake\Error;
  23. use Cake\Error\ExceptionRenderer;
  24. use Cake\Event\Event;
  25. use Cake\Network\Request;
  26. use Cake\Routing\Router;
  27. use Cake\TestSuite\TestCase;
  28. /**
  29. * BlueberryComponent class
  30. *
  31. */
  32. class BlueberryComponent extends Component {
  33. /**
  34. * testName property
  35. *
  36. * @return void
  37. */
  38. public $testName = null;
  39. /**
  40. * initialize method
  41. *
  42. * @return void
  43. */
  44. public function initialize(Event $event) {
  45. $this->testName = 'BlueberryComponent';
  46. }
  47. }
  48. /**
  49. * TestErrorController class
  50. *
  51. */
  52. class TestErrorController extends Controller {
  53. /**
  54. * uses property
  55. *
  56. * @var array
  57. */
  58. public $uses = array();
  59. /**
  60. * components property
  61. *
  62. * @return void
  63. */
  64. public $components = array('Blueberry');
  65. /**
  66. * beforeRender method
  67. *
  68. * @return void
  69. */
  70. public function beforeRender(Event $event) {
  71. echo $this->Blueberry->testName;
  72. }
  73. /**
  74. * index method
  75. *
  76. * @return void
  77. */
  78. public function index() {
  79. $this->autoRender = false;
  80. return 'what up';
  81. }
  82. }
  83. /**
  84. * MyCustomExceptionRenderer class
  85. *
  86. */
  87. class MyCustomExceptionRenderer extends ExceptionRenderer {
  88. /**
  89. * custom error message type.
  90. *
  91. * @return void
  92. */
  93. public function missingWidgetThing() {
  94. echo 'widget thing is missing';
  95. }
  96. }
  97. /**
  98. * Exception class for testing app error handlers and custom errors.
  99. *
  100. */
  101. class MissingWidgetThingException extends Error\NotFoundException {
  102. }
  103. /**
  104. * ExceptionRendererTest class
  105. *
  106. */
  107. class ExceptionRendererTest extends TestCase {
  108. protected $_restoreError = false;
  109. /**
  110. * setup create a request object to get out of router later.
  111. *
  112. * @return void
  113. */
  114. public function setUp() {
  115. parent::setUp();
  116. Configure::write('Config.language', 'eng');
  117. Router::reload();
  118. $request = new Request();
  119. $request->base = '';
  120. Router::setRequestInfo($request);
  121. Configure::write('debug', 2);
  122. }
  123. /**
  124. * tearDown
  125. *
  126. * @return void
  127. */
  128. public function tearDown() {
  129. parent::tearDown();
  130. if ($this->_restoreError) {
  131. restore_error_handler();
  132. }
  133. }
  134. /**
  135. * Mocks out the response on the ExceptionRenderer object so headers aren't modified.
  136. *
  137. * @return void
  138. */
  139. protected function _mockResponse($error) {
  140. $error->controller->response = $this->getMock('Cake\Network\Response', array('_sendHeader'));
  141. return $error;
  142. }
  143. /**
  144. * test that methods declared in an ExceptionRenderer subclass are not converted
  145. * into error400 when debug > 0
  146. *
  147. * @return void
  148. */
  149. public function testSubclassMethodsNotBeingConvertedToError() {
  150. Configure::write('debug', 2);
  151. $exception = new MissingWidgetThingException('Widget not found');
  152. $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
  153. ob_start();
  154. $ExceptionRenderer->render();
  155. $result = ob_get_clean();
  156. $this->assertEquals('widget thing is missing', $result);
  157. }
  158. /**
  159. * test that subclass methods are not converted when debug = 0
  160. *
  161. * @return void
  162. */
  163. public function testSubclassMethodsNotBeingConvertedDebug0() {
  164. Configure::write('debug', 0);
  165. $exception = new MissingWidgetThingException('Widget not found');
  166. $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
  167. $this->assertEquals('missingWidgetThing', $ExceptionRenderer->method);
  168. ob_start();
  169. $ExceptionRenderer->render();
  170. $result = ob_get_clean();
  171. $this->assertEquals('widget thing is missing', $result, 'Method declared in subclass converted to error400');
  172. }
  173. /**
  174. * test that ExceptionRenderer subclasses properly convert framework errors.
  175. *
  176. * @return void
  177. */
  178. public function testSubclassConvertingFrameworkErrors() {
  179. Configure::write('debug', 0);
  180. $exception = new Error\MissingControllerException('PostsController');
  181. $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
  182. $this->assertEquals('error400', $ExceptionRenderer->method);
  183. ob_start();
  184. $ExceptionRenderer->render();
  185. $result = ob_get_clean();
  186. $this->assertRegExp('/Not Found/', $result, 'Method declared in error handler not converted to error400. %s');
  187. }
  188. /**
  189. * test things in the constructor.
  190. *
  191. * @return void
  192. */
  193. public function testConstruction() {
  194. $exception = new Error\NotFoundException('Page not found');
  195. $ExceptionRenderer = new ExceptionRenderer($exception);
  196. $this->assertInstanceOf('Cake\Controller\ErrorController', $ExceptionRenderer->controller);
  197. $this->assertEquals('error400', $ExceptionRenderer->method);
  198. $this->assertEquals($exception, $ExceptionRenderer->error);
  199. }
  200. /**
  201. * test that method gets coerced when debug = 0
  202. *
  203. * @return void
  204. */
  205. public function testErrorMethodCoercion() {
  206. Configure::write('debug', 0);
  207. $exception = new Error\MissingActionException('Page not found');
  208. $ExceptionRenderer = new ExceptionRenderer($exception);
  209. $this->assertInstanceOf('Cake\Controller\ErrorController', $ExceptionRenderer->controller);
  210. $this->assertEquals('error400', $ExceptionRenderer->method);
  211. $this->assertEquals($exception, $ExceptionRenderer->error);
  212. }
  213. /**
  214. * test that helpers in custom CakeErrorController are not lost
  215. */
  216. public function testCakeErrorHelpersNotLost() {
  217. Configure::write('App.namespace', 'TestApp');
  218. $exception = new Error\SocketException('socket exception');
  219. $renderer = new \TestApp\Error\TestAppsExceptionRenderer($exception);
  220. ob_start();
  221. $renderer->render();
  222. $result = ob_get_clean();
  223. $this->assertContains('<b>peeled</b>', $result);
  224. }
  225. /**
  226. * test that unknown exception types with valid status codes are treated correctly.
  227. *
  228. * @return void
  229. */
  230. public function testUnknownExceptionTypeWithExceptionThatHasA400Code() {
  231. $exception = new MissingWidgetThingException('coding fail.');
  232. $ExceptionRenderer = new ExceptionRenderer($exception);
  233. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  234. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
  235. ob_start();
  236. $ExceptionRenderer->render();
  237. $result = ob_get_clean();
  238. $this->assertFalse(method_exists($ExceptionRenderer, 'missingWidgetThing'), 'no method should exist.');
  239. $this->assertEquals('error400', $ExceptionRenderer->method, 'incorrect method coercion.');
  240. $this->assertContains('coding fail', $result, 'Text should show up.');
  241. }
  242. /**
  243. * test that unknown exception types with valid status codes are treated correctly.
  244. *
  245. * @return void
  246. */
  247. public function testUnknownExceptionTypeWithNoCodeIsA500() {
  248. $exception = new \OutOfBoundsException('foul ball.');
  249. $ExceptionRenderer = new ExceptionRenderer($exception);
  250. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  251. $ExceptionRenderer->controller->response->expects($this->once())
  252. ->method('statusCode')
  253. ->with(500);
  254. ob_start();
  255. $ExceptionRenderer->render();
  256. $result = ob_get_clean();
  257. $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
  258. $this->assertContains('foul ball.', $result, 'Text should show up as its debug mode.');
  259. }
  260. /**
  261. * test that unknown exceptions have messages ignored.
  262. *
  263. * @return void
  264. */
  265. public function testUnknownExceptionInProduction() {
  266. Configure::write('debug', 0);
  267. $exception = new \OutOfBoundsException('foul ball.');
  268. $ExceptionRenderer = new ExceptionRenderer($exception);
  269. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  270. $ExceptionRenderer->controller->response->expects($this->once())
  271. ->method('statusCode')
  272. ->with(500);
  273. ob_start();
  274. $ExceptionRenderer->render();
  275. $result = ob_get_clean();
  276. $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
  277. $this->assertNotContains('foul ball.', $result, 'Text should no show up.');
  278. $this->assertContains('Internal Error', $result, 'Generic message only.');
  279. }
  280. /**
  281. * test that unknown exception types with valid status codes are treated correctly.
  282. *
  283. * @return void
  284. */
  285. public function testUnknownExceptionTypeWithCodeHigherThan500() {
  286. $exception = new \OutOfBoundsException('foul ball.', 501);
  287. $ExceptionRenderer = new ExceptionRenderer($exception);
  288. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  289. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(501);
  290. ob_start();
  291. $ExceptionRenderer->render();
  292. $result = ob_get_clean();
  293. $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
  294. $this->assertContains('foul ball.', $result, 'Text should show up as its debug mode.');
  295. }
  296. /**
  297. * testerror400 method
  298. *
  299. * @return void
  300. */
  301. public function testError400() {
  302. Router::reload();
  303. $request = new Request('posts/view/1000');
  304. Router::setRequestInfo($request);
  305. $exception = new Error\NotFoundException('Custom message');
  306. $ExceptionRenderer = new ExceptionRenderer($exception);
  307. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  308. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
  309. ob_start();
  310. $ExceptionRenderer->render();
  311. $result = ob_get_clean();
  312. $this->assertRegExp('/<h2>Custom message<\/h2>/', $result);
  313. $this->assertRegExp("/<strong>'.*?\/posts\/view\/1000'<\/strong>/", $result);
  314. }
  315. /**
  316. * test that error400 only modifies the messages on Cake Exceptions.
  317. *
  318. * @return void
  319. */
  320. public function testerror400OnlyChangingCakeException() {
  321. Configure::write('debug', 0);
  322. $exception = new Error\NotFoundException('Custom message');
  323. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  324. ob_start();
  325. $ExceptionRenderer->render();
  326. $result = ob_get_clean();
  327. $this->assertContains('Custom message', $result);
  328. $exception = new Error\MissingActionException(array('controller' => 'PostsController', 'action' => 'index'));
  329. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  330. ob_start();
  331. $ExceptionRenderer->render();
  332. $result = ob_get_clean();
  333. $this->assertContains('Not Found', $result);
  334. }
  335. /**
  336. * test that error400 doesn't expose XSS
  337. *
  338. * @return void
  339. */
  340. public function testError400NoInjection() {
  341. Router::reload();
  342. $request = new Request('pages/<span id=333>pink</span></id><script>document.body.style.background = t=document.getElementById(333).innerHTML;window.alert(t);</script>');
  343. Router::setRequestInfo($request);
  344. $exception = new Error\NotFoundException('Custom message');
  345. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  346. ob_start();
  347. $ExceptionRenderer->render();
  348. $result = ob_get_clean();
  349. $this->assertNotRegExp('#<script>document#', $result);
  350. $this->assertNotRegExp('#alert\(t\);</script>#', $result);
  351. }
  352. /**
  353. * testError500 method
  354. *
  355. * @return void
  356. */
  357. public function testError500Message() {
  358. $exception = new Error\InternalErrorException('An Internal Error Has Occurred');
  359. $ExceptionRenderer = new ExceptionRenderer($exception);
  360. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  361. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
  362. ob_start();
  363. $ExceptionRenderer->render();
  364. $result = ob_get_clean();
  365. $this->assertRegExp('/<h2>An Internal Error Has Occurred<\/h2>/', $result);
  366. }
  367. /**
  368. * testExceptionResponseHeader method
  369. *
  370. * @return void
  371. */
  372. public function testExceptionResponseHeader() {
  373. $exception = new Error\MethodNotAllowedException('Only allowing POST and DELETE');
  374. $exception->responseHeader(array('Allow: POST, DELETE'));
  375. $ExceptionRenderer = new ExceptionRenderer($exception);
  376. //Replace response object with mocked object add back the original headers which had been set in ExceptionRenderer constructor
  377. $headers = $ExceptionRenderer->controller->response->header();
  378. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('_sendHeader'));
  379. $ExceptionRenderer->controller->response->header($headers);
  380. $ExceptionRenderer->controller->response->expects($this->at(1))->method('_sendHeader')->with('Allow', 'POST, DELETE');
  381. ob_start();
  382. $ExceptionRenderer->render();
  383. ob_get_clean();
  384. }
  385. /**
  386. * testMissingController method
  387. *
  388. * @return void
  389. */
  390. public function testMissingController() {
  391. $exception = new Error\MissingControllerException(array(
  392. 'class' => 'Posts',
  393. 'prefix' => '',
  394. 'plugin' => '',
  395. ));
  396. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  397. ob_start();
  398. $ExceptionRenderer->render();
  399. $result = ob_get_clean();
  400. $this->assertRegExp('/<h2>Missing Controller<\/h2>/', $result);
  401. $this->assertRegExp('/<em>PostsController<\/em>/', $result);
  402. }
  403. /**
  404. * Returns an array of tests to run for the various Cake Exception classes.
  405. *
  406. * @return void
  407. */
  408. public static function testProvider() {
  409. return array(
  410. array(
  411. new Error\MissingActionException(array(
  412. 'controller' => 'PostsController',
  413. 'action' => 'index',
  414. 'prefix' => '',
  415. 'plugin' => '',
  416. )),
  417. array(
  418. '/<h2>Missing Method in PostsController<\/h2>/',
  419. '/<em>PostsController::index\(\)<\/em>/'
  420. ),
  421. 404
  422. ),
  423. array(
  424. new Error\PrivateActionException(array('controller' => 'PostsController', 'action' => '_secretSauce')),
  425. array(
  426. '/<h2>Private Method in PostsController<\/h2>/',
  427. '/<em>PostsController::_secretSauce\(\)<\/em>/'
  428. ),
  429. 404
  430. ),
  431. array(
  432. new Error\MissingTableException(array('table' => 'articles', 'class' => 'Article', 'ds' => 'test')),
  433. array(
  434. '/<h2>Missing Database Table<\/h2>/',
  435. '/Table <em>articles<\/em> for model <em>Article<\/em> was not found in datasource <em>test<\/em>/'
  436. ),
  437. 500
  438. ),
  439. array(
  440. new Error\MissingDatabaseException(array('connection' => 'default')),
  441. array(
  442. '/<h2>Missing Database Connection<\/h2>/',
  443. '/Confirm you have created the file/'
  444. ),
  445. 500
  446. ),
  447. array(
  448. new Error\MissingViewException(array('file' => '/posts/about.ctp')),
  449. array(
  450. "/posts\/about.ctp/"
  451. ),
  452. 500
  453. ),
  454. array(
  455. new Error\MissingLayoutException(array('file' => 'layouts/my_layout.ctp')),
  456. array(
  457. "/Missing Layout/",
  458. "/layouts\/my_layout.ctp/"
  459. ),
  460. 500
  461. ),
  462. array(
  463. new Error\MissingConnectionException(array('class' => 'Mysql')),
  464. array(
  465. '/<h2>Missing Database Connection<\/h2>/',
  466. '/A Database connection using "Mysql" was missing or unable to connect./',
  467. ),
  468. 500
  469. ),
  470. array(
  471. new Error\MissingConnectionException(array('class' => 'Mysql', 'enabled' => false)),
  472. array(
  473. '/<h2>Missing Database Connection<\/h2>/',
  474. '/A Database connection using "Mysql" was missing or unable to connect./',
  475. '/Mysql driver is NOT enabled/'
  476. ),
  477. 500
  478. ),
  479. array(
  480. new Error\MissingDatasourceConfigException(array('config' => 'default')),
  481. array(
  482. '/<h2>Missing Datasource Configuration<\/h2>/',
  483. '/The datasource configuration <em>default<\/em> was not found in datasources.php/'
  484. ),
  485. 500
  486. ),
  487. array(
  488. new Error\MissingDatasourceException(array('class' => 'MyDatasource', 'plugin' => 'MyPlugin')),
  489. array(
  490. '/<h2>Missing Datasource<\/h2>/',
  491. '/Datasource class <em>MyPlugin.MyDatasource<\/em> could not be found/'
  492. ),
  493. 500
  494. ),
  495. array(
  496. new Error\MissingHelperException(array('class' => 'MyCustomHelper')),
  497. array(
  498. '/<h2>Missing Helper<\/h2>/',
  499. '/<em>MyCustomHelper<\/em> could not be found./',
  500. '/Create the class <em>MyCustomHelper<\/em> below in file:/',
  501. '/(\/|\\\)MyCustomHelper.php/'
  502. ),
  503. 500
  504. ),
  505. array(
  506. new Error\MissingBehaviorException(array('class' => 'MyCustomBehavior')),
  507. array(
  508. '/<h2>Missing Behavior<\/h2>/',
  509. '/Create the class <em>MyCustomBehavior<\/em> below in file:/',
  510. '/(\/|\\\)MyCustomBehavior.php/'
  511. ),
  512. 500
  513. ),
  514. array(
  515. new Error\MissingComponentException(array('class' => 'SideboxComponent')),
  516. array(
  517. '/<h2>Missing Component<\/h2>/',
  518. '/Create the class <em>SideboxComponent<\/em> below in file:/',
  519. '/(\/|\\\)SideboxComponent.php/'
  520. ),
  521. 500
  522. ),
  523. array(
  524. new \Exception('boom'),
  525. array(
  526. '/Internal Error/'
  527. ),
  528. 500
  529. ),
  530. array(
  531. new \RuntimeException('another boom'),
  532. array(
  533. '/Internal Error/'
  534. ),
  535. 500
  536. ),
  537. array(
  538. new Error\Exception('base class'),
  539. array('/Internal Error/'),
  540. 500
  541. ),
  542. array(
  543. new Error\ConfigureException('No file'),
  544. array('/Internal Error/'),
  545. 500
  546. )
  547. );
  548. }
  549. /**
  550. * Test the various Cake Exception sub classes
  551. *
  552. * @dataProvider testProvider
  553. * @return void
  554. */
  555. public function testCakeExceptionHandling($exception, $patterns, $code) {
  556. $ExceptionRenderer = new ExceptionRenderer($exception);
  557. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  558. $ExceptionRenderer->controller->response->expects($this->once())
  559. ->method('statusCode')
  560. ->with($code);
  561. ob_start();
  562. $ExceptionRenderer->render();
  563. $result = ob_get_clean();
  564. foreach ($patterns as $pattern) {
  565. $this->assertRegExp($pattern, $result);
  566. }
  567. }
  568. /**
  569. * Test exceptions being raised when helpers are missing.
  570. *
  571. * @return void
  572. */
  573. public function testMissingRenderSafe() {
  574. $exception = new Error\MissingHelperException(array('class' => 'Fail'));
  575. $ExceptionRenderer = new ExceptionRenderer($exception);
  576. $ExceptionRenderer->controller = $this->getMock('Cake\Controller\Controller', array('render'));
  577. $ExceptionRenderer->controller->helpers = array('Fail', 'Boom');
  578. $ExceptionRenderer->controller->request = $this->getMock('Cake\Network\Request');
  579. $ExceptionRenderer->controller->expects($this->at(0))
  580. ->method('render')
  581. ->with('missingHelper')
  582. ->will($this->throwException($exception));
  583. $response = $this->getMock('Cake\Network\Response');
  584. $response->expects($this->once())
  585. ->method('body')
  586. ->with($this->stringContains('Helper class Fail'));
  587. $ExceptionRenderer->controller->response = $response;
  588. $ExceptionRenderer->render();
  589. sort($ExceptionRenderer->controller->helpers);
  590. $this->assertEquals(array('Form', 'Html', 'Session'), $ExceptionRenderer->controller->helpers);
  591. }
  592. /**
  593. * Test that exceptions in beforeRender() are handled by outputMessageSafe
  594. *
  595. * @return void
  596. */
  597. public function testRenderExceptionInBeforeRender() {
  598. $exception = new Error\NotFoundException('Not there, sorry');
  599. $ExceptionRenderer = new ExceptionRenderer($exception);
  600. $ExceptionRenderer->controller = $this->getMock('Cake\Controller\Controller', array('beforeRender'));
  601. $ExceptionRenderer->controller->request = $this->getMock('Cake\Network\Request');
  602. $ExceptionRenderer->controller->expects($this->any())
  603. ->method('beforeRender')
  604. ->will($this->throwException($exception));
  605. $response = $this->getMock('Cake\Network\Response');
  606. $response->expects($this->once())
  607. ->method('body')
  608. ->with($this->stringContains('Not there, sorry'));
  609. $ExceptionRenderer->controller->response = $response;
  610. $ExceptionRenderer->render();
  611. }
  612. /**
  613. * Test that missing subDir/layoutPath don't cause other fatal errors.
  614. *
  615. * @return void
  616. */
  617. public function testMissingSubdirRenderSafe() {
  618. $exception = new Error\NotFoundException();
  619. $ExceptionRenderer = new ExceptionRenderer($exception);
  620. $ExceptionRenderer->controller = $this->getMock('Cake\Controller\Controller', array('render'));
  621. $ExceptionRenderer->controller->helpers = array('Fail', 'Boom');
  622. $ExceptionRenderer->controller->layoutPath = 'json';
  623. $ExceptionRenderer->controller->subDir = 'json';
  624. $ExceptionRenderer->controller->viewClass = 'Json';
  625. $ExceptionRenderer->controller->request = $this->getMock('Cake\Network\Request');
  626. $ExceptionRenderer->controller->expects($this->once())
  627. ->method('render')
  628. ->with('error400')
  629. ->will($this->throwException($exception));
  630. $response = $this->getMock('Cake\Network\Response');
  631. $response->expects($this->once())
  632. ->method('body')
  633. ->with($this->stringContains('Not Found'));
  634. $response->expects($this->once())
  635. ->method('type')
  636. ->with('html');
  637. $ExceptionRenderer->controller->response = $response;
  638. $ExceptionRenderer->render();
  639. $this->assertEquals('', $ExceptionRenderer->controller->layoutPath);
  640. $this->assertEquals('', $ExceptionRenderer->controller->subDir);
  641. $this->assertEquals('Error', $ExceptionRenderer->controller->viewPath);
  642. }
  643. /**
  644. * Test that exceptions can be rendered when an request hasn't been registered
  645. * with Router
  646. *
  647. * @return void
  648. */
  649. public function testRenderWithNoRequest() {
  650. Router::reload();
  651. $this->assertNull(Router::getRequest(false));
  652. $exception = new \Exception('Terrible');
  653. $ExceptionRenderer = new ExceptionRenderer($exception);
  654. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  655. $ExceptionRenderer->controller->response->expects($this->once())
  656. ->method('statusCode')
  657. ->with(500);
  658. ob_start();
  659. $ExceptionRenderer->render();
  660. $result = ob_get_clean();
  661. $this->assertContains('Internal Error', $result);
  662. }
  663. /**
  664. * Tests the output of rendering a PDOException
  665. *
  666. * @return void
  667. */
  668. public function testPDOException() {
  669. $exception = new \PDOException('There was an error in the SQL query');
  670. $exception->queryString = 'SELECT * from poo_query < 5 and :seven';
  671. $exception->params = array('seven' => 7);
  672. $ExceptionRenderer = new ExceptionRenderer($exception);
  673. $ExceptionRenderer->controller->response = $this->getMock('Cake\Network\Response', array('statusCode', '_sendHeader'));
  674. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
  675. ob_start();
  676. $ExceptionRenderer->render();
  677. $result = ob_get_clean();
  678. $this->assertContains('<h2>Database Error</h2>', $result);
  679. $this->assertContains('There was an error in the SQL query', $result);
  680. $this->assertContains(h('SELECT * from poo_query < 5 and :seven'), $result);
  681. $this->assertContains("'seven' => (int) 7", $result);
  682. }
  683. }