PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/Test/Case/Error/ExceptionRendererTest.php

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