ExceptionRendererTest.php 24 KB

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