ExceptionRendererTest.php 22 KB

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