ErrorHandlerMiddlewareTest.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  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://cakephp.org CakePHP(tm) Project
  12. * @since 3.3.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Error\Middleware;
  16. use Cake\Core\Configure;
  17. use Cake\Error\Middleware\ErrorHandlerMiddleware;
  18. use Cake\Http\Response;
  19. use Cake\Http\ServerRequestFactory;
  20. use Cake\Log\Log;
  21. use Cake\TestSuite\TestCase;
  22. use LogicException;
  23. use Psr\Log\LoggerInterface;
  24. use Zend\Diactoros\Request;
  25. /**
  26. * Test for ErrorHandlerMiddleware
  27. */
  28. class ErrorHandlerMiddlewareTest extends TestCase
  29. {
  30. protected $logger;
  31. /**
  32. * setup
  33. *
  34. * @return void
  35. */
  36. public function setUp()
  37. {
  38. parent::setUp();
  39. Configure::write('App.namespace', 'TestApp');
  40. $this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
  41. Log::reset();
  42. Log::config('error_test', [
  43. 'engine' => $this->logger
  44. ]);
  45. }
  46. /**
  47. * Teardown
  48. *
  49. * @return void
  50. */
  51. public function tearDown()
  52. {
  53. parent::tearDown();
  54. Log::drop('error_test');
  55. }
  56. /**
  57. * Test returning a response works ok.
  58. *
  59. * @return void
  60. */
  61. public function testNoErrorResponse()
  62. {
  63. $this->logger->expects($this->never())->method('log');
  64. $request = ServerRequestFactory::fromGlobals();
  65. $response = new Response();
  66. $middleware = new ErrorHandlerMiddleware();
  67. $next = function ($req, $res) {
  68. return $res;
  69. };
  70. $result = $middleware($request, $response, $next);
  71. $this->assertSame($result, $response);
  72. }
  73. /**
  74. * Test an invalid rendering class.
  75. *
  76. * @expectedException \Exception
  77. * @expectedExceptionMessage The 'TotallyInvalid' renderer class could not be found
  78. */
  79. public function testInvalidRenderer()
  80. {
  81. $request = ServerRequestFactory::fromGlobals();
  82. $response = new Response();
  83. $middleware = new ErrorHandlerMiddleware('TotallyInvalid');
  84. $next = function ($req, $res) {
  85. throw new \Exception('Something bad');
  86. };
  87. $middleware($request, $response, $next);
  88. }
  89. /**
  90. * Test using a factory method to make a renderer.
  91. *
  92. * @return void
  93. */
  94. public function testRendererFactory()
  95. {
  96. $request = ServerRequestFactory::fromGlobals();
  97. $response = new Response();
  98. $factory = function ($exception) {
  99. $this->assertInstanceOf('LogicException', $exception);
  100. $response = new Response;
  101. $mock = $this->getMockBuilder('StdClass')
  102. ->setMethods(['render'])
  103. ->getMock();
  104. $mock->expects($this->once())
  105. ->method('render')
  106. ->will($this->returnValue($response));
  107. return $mock;
  108. };
  109. $middleware = new ErrorHandlerMiddleware($factory);
  110. $next = function ($req, $res) {
  111. throw new LogicException('Something bad');
  112. };
  113. $middleware($request, $response, $next);
  114. }
  115. /**
  116. * Test rendering an error page
  117. *
  118. * @return void
  119. */
  120. public function testHandleException()
  121. {
  122. $request = ServerRequestFactory::fromGlobals();
  123. $response = new Response();
  124. $middleware = new ErrorHandlerMiddleware();
  125. $next = function ($req, $res) {
  126. throw new \Cake\Network\Exception\NotFoundException('whoops');
  127. };
  128. $result = $middleware($request, $response, $next);
  129. $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $result);
  130. $this->assertInstanceOf('Cake\Http\Response', $result);
  131. $this->assertNotSame($result, $response);
  132. $this->assertEquals(404, $result->getStatusCode());
  133. $this->assertContains("was not found", '' . $result->getBody());
  134. }
  135. /**
  136. * Test rendering an error page logs errors
  137. *
  138. * @return void
  139. */
  140. public function testHandleExceptionLogAndTrace()
  141. {
  142. $this->logger->expects($this->at(0))
  143. ->method('log')
  144. ->with('error', $this->logicalAnd(
  145. $this->stringContains('[Cake\Network\Exception\NotFoundException] Kaboom!'),
  146. $this->stringContains('ErrorHandlerMiddlewareTest->testHandleException'),
  147. $this->stringContains('Request URL: /target/url'),
  148. $this->stringContains('Referer URL: /other/path')
  149. ));
  150. $request = ServerRequestFactory::fromGlobals([
  151. 'REQUEST_URI' => '/target/url',
  152. 'HTTP_REFERER' => '/other/path'
  153. ]);
  154. $response = new Response();
  155. $middleware = new ErrorHandlerMiddleware(null, ['log' => true, 'trace' => true]);
  156. $next = function ($req, $res) {
  157. throw new \Cake\Network\Exception\NotFoundException('Kaboom!');
  158. };
  159. $result = $middleware($request, $response, $next);
  160. $this->assertNotSame($result, $response);
  161. $this->assertEquals(404, $result->getStatusCode());
  162. $this->assertContains("was not found", '' . $result->getBody());
  163. }
  164. /**
  165. * Test rendering an error page skips logging for specific classes
  166. *
  167. * @return void
  168. */
  169. public function testHandleExceptionSkipLog()
  170. {
  171. $this->logger->expects($this->never())->method('log');
  172. $request = ServerRequestFactory::fromGlobals();
  173. $response = new Response();
  174. $middleware = new ErrorHandlerMiddleware(null, [
  175. 'log' => true,
  176. 'skipLog' => ['Cake\Network\Exception\NotFoundException']
  177. ]);
  178. $next = function ($req, $res) {
  179. throw new \Cake\Network\Exception\NotFoundException('Kaboom!');
  180. };
  181. $result = $middleware($request, $response, $next);
  182. $this->assertNotSame($result, $response);
  183. $this->assertEquals(404, $result->getStatusCode());
  184. $this->assertContains("was not found", '' . $result->getBody());
  185. }
  186. /**
  187. * Test rendering an error page logs exception attributes
  188. *
  189. * @return void
  190. */
  191. public function testHandleExceptionLogAttributes()
  192. {
  193. $this->logger->expects($this->at(0))
  194. ->method('log')
  195. ->with('error', $this->logicalAnd(
  196. $this->stringContains(
  197. '[Cake\Routing\Exception\MissingControllerException] ' .
  198. 'Controller class Articles could not be found.'
  199. ),
  200. $this->stringContains('Exception Attributes:'),
  201. $this->stringContains("'class' => 'Articles'"),
  202. $this->stringContains('Request URL:')
  203. ));
  204. $request = ServerRequestFactory::fromGlobals();
  205. $response = new Response();
  206. $middleware = new ErrorHandlerMiddleware(null, ['log' => true]);
  207. $next = function ($req, $res) {
  208. throw new \Cake\Routing\Exception\MissingControllerException(['class' => 'Articles']);
  209. };
  210. $result = $middleware($request, $response, $next);
  211. $this->assertNotSame($result, $response);
  212. $this->assertEquals(404, $result->getStatusCode());
  213. }
  214. /**
  215. * Test handling an error and having rendering fail.
  216. *
  217. * @return void
  218. */
  219. public function testHandleExceptionRenderingFails()
  220. {
  221. $request = ServerRequestFactory::fromGlobals();
  222. $response = new Response();
  223. $factory = function ($exception) {
  224. $mock = $this->getMockBuilder('StdClass')
  225. ->setMethods(['render'])
  226. ->getMock();
  227. $mock->expects($this->once())
  228. ->method('render')
  229. ->will($this->throwException(new LogicException('Rendering failed')));
  230. return $mock;
  231. };
  232. $middleware = new ErrorHandlerMiddleware($factory);
  233. $next = function ($req, $res) {
  234. throw new \Cake\Network\Exception\ServiceUnavailableException('whoops');
  235. };
  236. $response = $middleware($request, $response, $next);
  237. $this->assertEquals(500, $response->getStatusCode());
  238. $this->assertEquals('An Internal Server Error Occurred', '' . $response->getBody());
  239. }
  240. }