ExceptionRendererTest.php 23 KB

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