ExceptionRendererTest.php 23 KB

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