ExceptionRendererTest.php 23 KB

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