ExceptionTrapTest.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP Project
  13. * @since 4.4.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Error;
  17. use Cake\Error\ErrorLogger;
  18. use Cake\Error\ExceptionRenderer;
  19. use Cake\Error\ExceptionTrap;
  20. use Cake\Error\Renderer\TextExceptionRenderer;
  21. use Cake\Log\Log;
  22. use Cake\TestSuite\TestCase;
  23. use InvalidArgumentException;
  24. use stdClass;
  25. use Throwable;
  26. class ExceptionTrapTest extends TestCase
  27. {
  28. public function setUp(): void
  29. {
  30. parent::setUp();
  31. Log::drop('test_error');
  32. }
  33. public function testConfigRendererInvalid()
  34. {
  35. $trap = new ExceptionTrap(['exceptionRenderer' => stdClass::class]);
  36. $this->expectException(InvalidArgumentException::class);
  37. $error = new InvalidArgumentException('nope');
  38. $trap->renderer($error);
  39. }
  40. public function testConfigExceptionRendererFallback()
  41. {
  42. $this->markTestIncomplete();
  43. $trap = new ExceptionTrap(['exceptionRenderer' => null]);
  44. $error = new InvalidArgumentException('nope');
  45. $this->assertInstanceOf(ConsoleRenderer::class, $trap->renderer($error));
  46. }
  47. public function testConfigExceptionRenderer()
  48. {
  49. $trap = new ExceptionTrap(['exceptionRenderer' => ExceptionRenderer::class]);
  50. $error = new InvalidArgumentException('nope');
  51. $this->assertInstanceOf(ExceptionRenderer::class, $trap->renderer($error));
  52. }
  53. public function testConfigExceptionRendererFactory()
  54. {
  55. $trap = new ExceptionTrap(['exceptionRenderer' => function ($err, $req) {
  56. return new ExceptionRenderer($err, $req);
  57. }]);
  58. $error = new InvalidArgumentException('nope');
  59. $this->assertInstanceOf(ExceptionRenderer::class, $trap->renderer($error));
  60. }
  61. public function testConfigRendererHandleUnsafeOverwrite()
  62. {
  63. $this->markTestIncomplete();
  64. $trap = new ExceptionTrap();
  65. $trap->setConfig('exceptionRenderer', null);
  66. $error = new InvalidArgumentException('nope');
  67. $this->assertInstanceOf(ConsoleRenderer::class, $trap->renderer($error));
  68. }
  69. public function testLoggerConfigInvalid()
  70. {
  71. $trap = new ExceptionTrap(['logger' => stdClass::class]);
  72. $this->expectException(InvalidArgumentException::class);
  73. $trap->logger();
  74. }
  75. public function testLoggerConfig()
  76. {
  77. $trap = new ExceptionTrap(['logger' => ErrorLogger::class]);
  78. $this->assertInstanceOf(ErrorLogger::class, $trap->logger());
  79. }
  80. public function testLoggerHandleUnsafeOverwrite()
  81. {
  82. $trap = new ExceptionTrap();
  83. $trap->setConfig('logger', null);
  84. $this->assertInstanceOf(ErrorLogger::class, $trap->logger());
  85. }
  86. public function testRenderExceptionText()
  87. {
  88. $trap = new ExceptionTrap([
  89. 'exceptionRenderer' => TextExceptionRenderer::class,
  90. ]);
  91. $error = new InvalidArgumentException('nope');
  92. ob_start();
  93. $trap->handleException($error);
  94. $out = ob_get_clean();
  95. $this->assertStringContainsString('nope', $out);
  96. $this->assertStringContainsString('ExceptionTrapTest', $out);
  97. }
  98. /**
  99. * Test integration with HTML exception rendering
  100. *
  101. * Run in a separate process because HTML output writes headers.
  102. *
  103. * @preserveGlobalState disabled
  104. * @runInSeparateProcess
  105. */
  106. public function testRenderExceptionHtml()
  107. {
  108. $trap = new ExceptionTrap([
  109. 'exceptionRenderer' => ExceptionRenderer::class,
  110. ]);
  111. $error = new InvalidArgumentException('nope');
  112. ob_start();
  113. $trap->handleException($error);
  114. $out = ob_get_clean();
  115. $this->assertStringContainsString('<!DOCTYPE', $out);
  116. $this->assertStringContainsString('<html', $out);
  117. $this->assertStringContainsString('nope', $out);
  118. $this->assertStringContainsString('ExceptionTrapTest', $out);
  119. }
  120. public function testLogException()
  121. {
  122. Log::setConfig('test_error', [
  123. 'className' => 'Array',
  124. ]);
  125. $trap = new ExceptionTrap();
  126. $error = new InvalidArgumentException('nope');
  127. $trap->logException($error);
  128. $logs = Log::engine('test_error')->read();
  129. $this->assertStringContainsString('nope', $logs[0]);
  130. }
  131. public function testAddCallback()
  132. {
  133. $trap = new ExceptionTrap(['exceptionRenderer' => TextExceptionRenderer::class]);
  134. $trap->addCallback(function (Throwable $error) {
  135. $this->assertEquals(100, $error->getCode());
  136. $this->assertStringContainsString('nope', $error->getMessage());
  137. });
  138. $error = new InvalidArgumentException('nope', 100);
  139. ob_start();
  140. $trap->handleException($error);
  141. $out = ob_get_clean();
  142. $this->assertNotEmpty($out);
  143. }
  144. }