summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormsquare <msquare@notrademark.de>2017-11-26 11:07:27 +0100
committerGitHub <noreply@github.com>2017-11-26 11:07:27 +0100
commiteda7f7788ea8012bd8be46405c56a666c11f3fa5 (patch)
tree50cf84d7d07d11bd65b45c2c17f37632f6cd8eff
parente54a10b81679bae9d19337617d6c58310d2f7a58 (diff)
parentb9bc03a1bdf146e0b4ef6529ebe814a0ac9c930d (diff)
Merge pull request #364 from MyIgel/master
Refactored error handling, changed tests from MySQL to MariaDB
-rw-r--r--.gitlab-ci.yml9
-rw-r--r--composer.json4
-rw-r--r--config/config.default.php2
-rw-r--r--includes/engelsystem.php6
-rw-r--r--src/Exceptions/ExceptionsServiceProvider.php47
-rw-r--r--src/Exceptions/Handler.php144
-rw-r--r--src/Exceptions/Handlers/HandlerInterface.php21
-rw-r--r--src/Exceptions/Handlers/Legacy.php42
-rw-r--r--src/Exceptions/Handlers/LegacyDevelopment.php57
-rw-r--r--src/Exceptions/Handlers/Whoops.php85
-rw-r--r--tests/Unit/Exceptions/ExceptionsServiceProviderTest.php94
-rw-r--r--tests/Unit/Exceptions/HandlerTest.php140
-rw-r--r--tests/Unit/Exceptions/Handlers/LegacyDevelopmentTest.php35
-rw-r--r--tests/Unit/Exceptions/Handlers/LegacyTest.php55
-rw-r--r--tests/Unit/Exceptions/Handlers/WhoopsTest.php83
15 files changed, 732 insertions, 92 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1cc9797b..6dde2330 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,12 +5,13 @@ cache:
- .composer
services:
- - mysql:5.6
+ - mariadb:10.2
variables:
MYSQL_DATABASE: engelsystem
MYSQL_USER: engel
MYSQL_PASSWORD: engelsystem
+ MYSQL_HOST: mariadb
COMPOSER_HOME: .composer
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
@@ -20,14 +21,14 @@ before_script:
- find . -type d -exec chmod 755 {} \;
# Install required Packages
- apt update -yqq
- - apt install -yqq git unzip mysql-client
+ - apt install -yqq git unzip mariadb-client
- docker-php-ext-install pdo pdo_mysql gettext
# Install xdebug
- pecl install xdebug
- docker-php-ext-enable xdebug
# MySQL DB
- - mysql -h mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" < db/install.sql
- - mysql -h mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" < db/update.sql
+ - mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" < db/install.sql
+ - mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" < db/update.sql
# Install Composer
- curl -sS https://getcomposer.org/installer | php -- --no-ansi --install-dir /usr/local/bin/ --filename composer
- /usr/local/bin/composer --no-ansi install
diff --git a/composer.json b/composer.json
index a8f0b0d6..ed34ba03 100644
--- a/composer.json
+++ b/composer.json
@@ -23,7 +23,9 @@
"twbs/bootstrap": "^3.3"
},
"require-dev": {
- "phpunit/phpunit": "^6.3"
+ "filp/whoops": "^2.1",
+ "phpunit/phpunit": "^6.3",
+ "symfony/var-dumper": "^3.3"
},
"autoload": {
"psr-4": {
diff --git a/config/config.default.php b/config/config.default.php
index 1bad9668..7594346a 100644
--- a/config/config.default.php
+++ b/config/config.default.php
@@ -5,7 +5,7 @@
return [
// MySQL-Connection Settings
'database' => [
- 'host' => env('MYSQL_HOST', (env('CI', false) ? 'mysql' : 'localhost')),
+ 'host' => env('MYSQL_HOST', (env('CI', false) ? 'mariadb' : 'localhost')),
'user' => env('MYSQL_USER', 'root'),
'pw' => env('MYSQL_PASSWORD', ''),
'db' => env('MYSQL_DATABASE', 'engelsystem'),
diff --git a/includes/engelsystem.php b/includes/engelsystem.php
index 97076895..07abbb42 100644
--- a/includes/engelsystem.php
+++ b/includes/engelsystem.php
@@ -2,7 +2,8 @@
use Engelsystem\Application;
use Engelsystem\Config\Config;
-use Engelsystem\Exceptions\Handler as ExceptionHandler;
+use Engelsystem\Exceptions\Handler;
+use Engelsystem\Exceptions\Handlers\HandlerInterface;
/**
* This file includes all needed functions, connects to the db etc.
@@ -32,7 +33,8 @@ date_default_timezone_set($app->get('config')->get('timezone'));
if (config('environment') == 'development') {
$errorHandler = $app->get('error.handler');
- $errorHandler->setEnvironment(ExceptionHandler::ENV_DEVELOPMENT);
+ $errorHandler->setEnvironment(Handler::ENV_DEVELOPMENT);
+ $app->bind(HandlerInterface::class, 'error.handler.development');
ini_set('display_errors', true);
error_reporting(E_ALL);
} else {
diff --git a/src/Exceptions/ExceptionsServiceProvider.php b/src/Exceptions/ExceptionsServiceProvider.php
index 7755e1e7..a9bc2b17 100644
--- a/src/Exceptions/ExceptionsServiceProvider.php
+++ b/src/Exceptions/ExceptionsServiceProvider.php
@@ -3,13 +3,56 @@
namespace Engelsystem\Exceptions;
use Engelsystem\Container\ServiceProvider;
-use Engelsystem\Exceptions\Handler as ExceptionHandler;
+use Engelsystem\Exceptions\Handlers\HandlerInterface;
+use Engelsystem\Exceptions\Handlers\Legacy;
+use Engelsystem\Exceptions\Handlers\LegacyDevelopment;
+use Engelsystem\Exceptions\Handlers\Whoops;
+use Whoops\Run as WhoopsRunner;
class ExceptionsServiceProvider extends ServiceProvider
{
public function register()
{
- $errorHandler = $this->app->make(ExceptionHandler::class);
+ $errorHandler = $this->app->make(Handler::class);
+ $this->addProductionHandler($errorHandler);
+ $this->addDevelopmentHandler($errorHandler);
$this->app->instance('error.handler', $errorHandler);
+ $this->app->bind(Handler::class, 'error.handler');
+ $errorHandler->register();
+ }
+
+ public function boot()
+ {
+ /** @var Handler $handler */
+ $handler = $this->app->get('error.handler');
+ $request = $this->app->get('request');
+
+ $handler->setRequest($request);
+ }
+
+ /**
+ * @param Handler $errorHandler
+ */
+ protected function addProductionHandler($errorHandler)
+ {
+ $handler = $this->app->make(Legacy::class);
+ $this->app->instance('error.handler.production', $handler);
+ $errorHandler->setHandler(Handler::ENV_PRODUCTION, $handler);
+ $this->app->bind(HandlerInterface::class, 'error.handler.production');
+ }
+
+ /**
+ * @param Handler $errorHandler
+ */
+ protected function addDevelopmentHandler($errorHandler)
+ {
+ $handler = $this->app->make(LegacyDevelopment::class);
+
+ if (class_exists(WhoopsRunner::class)) {
+ $handler = $this->app->make(Whoops::class);
+ }
+
+ $this->app->instance('error.handler.development', $handler);
+ $errorHandler->setHandler(Handler::ENV_DEVELOPMENT, $handler);
}
}
diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php
index 95bcd132..ee15717a 100644
--- a/src/Exceptions/Handler.php
+++ b/src/Exceptions/Handler.php
@@ -2,6 +2,9 @@
namespace Engelsystem\Exceptions;
+use Engelsystem\Exceptions\Handlers\HandlerInterface;
+use Engelsystem\Http\Request;
+use ErrorException;
use Throwable;
class Handler
@@ -9,34 +12,44 @@ class Handler
/** @var string */
protected $environment;
+ /** @var HandlerInterface[] */
+ protected $handler = [];
+
+ /** @var Request */
+ protected $request;
+
const ENV_PRODUCTION = 'prod';
const ENV_DEVELOPMENT = 'dev';
/**
* Handler constructor.
*
- * @param string $environment production|development
+ * @param string $environment prod|dev
*/
public function __construct($environment = self::ENV_PRODUCTION)
{
$this->environment = $environment;
+ }
+ /**
+ * Activate the error handler
+ */
+ public function register()
+ {
set_error_handler([$this, 'errorHandler']);
set_exception_handler([$this, 'exceptionHandler']);
}
/**
* @param int $number
- * @param string $string
+ * @param string $message
* @param string $file
* @param int $line
- * @param array $context
*/
- public function errorHandler($number, $string, $file, $line, $context)
+ public function errorHandler($number, $message, $file, $line)
{
- $trace = array_reverse(debug_backtrace());
-
- $this->handle('error', $number, $string, $file, $line, $context, $trace);
+ $exception = new ErrorException($message, 0, $number, $file, $line);
+ $this->exceptionHandler($exception);
}
/**
@@ -44,98 +57,79 @@ class Handler
*/
public function exceptionHandler($e)
{
- $this->handle(
- 'exception',
- $e->getCode(),
- get_class($e) . ': ' . $e->getMessage(),
- $e->getFile(),
- $e->getLine(),
- ['exception' => $e]
- );
+ if (!$this->request instanceof Request) {
+ $this->request = new Request();
+ }
+
+ $handler = $this->handler[$this->environment];
+ $handler->report($e);
+ $handler->render($this->request, $e);
+ $this->die();
}
/**
- * @param string $type
- * @param int $number
- * @param string $string
- * @param string $file
- * @param int $line
- * @param array $context
- * @param array $trace
+ * Exit the application
+ *
+ * @codeCoverageIgnore
+ * @param string $message
*/
- protected function handle($type, $number, $string, $file, $line, $context = [], $trace = [])
+ protected function die($message = '')
{
- error_log(sprintf('%s: Number: %s, String: %s, File: %s:%u, Context: %s',
- $type,
- $number,
- $string,
- $file,
- $line,
- json_encode($context)
- ));
-
- $file = $this->stripBasePath($file);
-
- if ($this->environment == self::ENV_DEVELOPMENT) {
- echo '<pre style="background-color:#333;color:#ccc;z-index:1000;position:fixed;bottom:1em;padding:1em;width:97%;max-height: 90%;overflow-y:auto;">';
- echo sprintf('%s: (%s)' . PHP_EOL, ucfirst($type), $number);
- var_export([
- 'string' => $string,
- 'file' => $file . ':' . $line,
- 'context' => $context,
- 'stacktrace' => $this->formatStackTrace($trace),
- ]);
- echo '</pre>';
- die();
- }
-
- echo 'An <del>un</del>expected error occurred, a team of untrained monkeys has been dispatched to deal with it.';
+ echo $message;
die();
}
/**
- * @param array $stackTrace
- * @return array
+ * @return string
*/
- protected function formatStackTrace($stackTrace)
+ public function getEnvironment()
{
- $return = [];
-
- foreach ($stackTrace as $trace) {
- $path = '';
- $line = '';
-
- if (isset($trace['file']) && isset($trace['line'])) {
- $path = $this->stripBasePath($trace['file']);
- $line = $trace['line'];
- }
+ return $this->environment;
+ }
- $functionName = $trace['function'];
+ /**
+ * @param string $environment
+ */
+ public function setEnvironment($environment)
+ {
+ $this->environment = $environment;
+ }
- $return[] = [
- 'file' => $path . ':' . $line,
- $functionName => $trace['args'],
- ];
+ /**
+ * @param string $environment
+ * @return HandlerInterface|HandlerInterface[]
+ */
+ public function getHandler($environment = null)
+ {
+ if (!is_null($environment)) {
+ return $this->handler[$environment];
}
- return $return;
+ return $this->handler;
}
/**
- * @param string $path
- * @return string
+ * @param string $environment
+ * @param HandlerInterface $handler
*/
- protected function stripBasePath($path)
+ public function setHandler($environment, HandlerInterface $handler)
{
- $basePath = realpath(__DIR__ . '/../..') . '/';
- return str_replace($basePath, '', $path);
+ $this->handler[$environment] = $handler;
}
/**
- * @param string $environment
+ * @return Request
*/
- public function setEnvironment($environment)
+ public function getRequest()
{
- $this->environment = $environment;
+ return $this->request;
+ }
+
+ /**
+ * @param Request $request
+ */
+ public function setRequest(Request $request)
+ {
+ $this->request = $request;
}
}
diff --git a/src/Exceptions/Handlers/HandlerInterface.php b/src/Exceptions/Handlers/HandlerInterface.php
new file mode 100644
index 00000000..9de33e1f
--- /dev/null
+++ b/src/Exceptions/Handlers/HandlerInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Engelsystem\Exceptions\Handlers;
+
+use Engelsystem\Http\Request;
+use Throwable;
+
+interface HandlerInterface
+{
+ /**
+ * @param Request $request
+ * @param Throwable $e
+ */
+ public function render($request, Throwable $e);
+
+ /**
+ * @param Throwable $e
+ * @return
+ */
+ public function report(Throwable $e);
+}
diff --git a/src/Exceptions/Handlers/Legacy.php b/src/Exceptions/Handlers/Legacy.php
new file mode 100644
index 00000000..39683f72
--- /dev/null
+++ b/src/Exceptions/Handlers/Legacy.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Engelsystem\Exceptions\Handlers;
+
+use Engelsystem\Http\Request;
+use Throwable;
+
+class Legacy implements HandlerInterface
+{
+ /**
+ * @param Request $request
+ * @param Throwable $e
+ */
+ public function render($request, Throwable $e)
+ {
+ echo 'An <del>un</del>expected error occurred, a team of untrained monkeys has been dispatched to deal with it.';
+ }
+
+ /**
+ * @param Throwable $e
+ */
+ public function report(Throwable $e)
+ {
+ error_log(sprintf('Exception: Code: %s, Message: %s, File: %s:%u, Trace: %s',
+ $e->getCode(),
+ $e->getMessage(),
+ $this->stripBasePath($e->getFile()),
+ $e->getLine(),
+ json_encode($e->getTrace())
+ ));
+ }
+
+ /**
+ * @param string $path
+ * @return string
+ */
+ protected function stripBasePath($path)
+ {
+ $basePath = realpath(__DIR__ . '/../../..') . '/';
+ return str_replace($basePath, '', $path);
+ }
+}
diff --git a/src/Exceptions/Handlers/LegacyDevelopment.php b/src/Exceptions/Handlers/LegacyDevelopment.php
new file mode 100644
index 00000000..86f86f4c
--- /dev/null
+++ b/src/Exceptions/Handlers/LegacyDevelopment.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Engelsystem\Exceptions\Handlers;
+
+use Engelsystem\Http\Request;
+use Throwable;
+
+class LegacyDevelopment extends Legacy
+{
+ /**
+ * @param Request $request
+ * @param Throwable $e
+ */
+ public function render($request, Throwable $e)
+ {
+ $file = $this->stripBasePath($e->getFile());
+
+ echo '<pre style="background-color:#333;color:#ccc;z-index:1000;position:fixed;bottom:1em;padding:1em;width:97%;max-height: 90%;overflow-y:auto;">';
+ echo sprintf('%s: (%s)' . PHP_EOL, get_class($e), $e->getCode());
+ $data = [
+ 'string' => $e->getMessage(),
+ 'file' => $file . ':' . $e->getLine(),
+ 'stacktrace' => $this->formatStackTrace($e->getTrace()),
+ ];
+ var_dump($data);
+ echo '</pre>';
+ }
+
+ /**
+ * @param array $stackTrace
+ * @return array
+ */
+ protected function formatStackTrace($stackTrace)
+ {
+ $return = [];
+ $stackTrace = array_reverse($stackTrace);
+
+ foreach ($stackTrace as $trace) {
+ $path = '';
+ $line = '';
+
+ if (isset($trace['file']) && isset($trace['line'])) {
+ $path = $this->stripBasePath($trace['file']);
+ $line = $trace['line'];
+ }
+
+ $functionName = $trace['function'];
+
+ $return[] = [
+ 'file' => $path . ':' . $line,
+ $functionName => isset($trace['args']) ? $trace['args'] : null,
+ ];
+ }
+
+ return $return;
+ }
+}
diff --git a/src/Exceptions/Handlers/Whoops.php b/src/Exceptions/Handlers/Whoops.php
new file mode 100644
index 00000000..807f5eb0
--- /dev/null
+++ b/src/Exceptions/Handlers/Whoops.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Engelsystem\Exceptions\Handlers;
+
+use Engelsystem\Application;
+use Engelsystem\Container\Container;
+use Engelsystem\Http\Request;
+use Throwable;
+use Whoops\Handler\JsonResponseHandler;
+use Whoops\Handler\PrettyPageHandler;
+use Whoops\Run as WhoopsRunner;
+
+class Whoops extends Legacy implements HandlerInterface
+{
+ /** @var Application */
+ protected $app;
+
+ public function __construct(Container $app)
+ {
+ $this->app = $app;
+ }
+
+ /**
+ * @param Request $request
+ * @param Throwable $e
+ */
+ public function render($request, Throwable $e)
+ {
+ $whoops = $this->app->make(WhoopsRunner::class);
+ $handler = $this->getPrettyPageHandler($e);
+ $whoops->pushHandler($handler);
+
+ if ($request->isXmlHttpRequest()) {
+ $handler = $this->getJsonResponseHandler();
+ $whoops->pushHandler($handler);
+ }
+
+ echo $whoops->handleException($e);
+ }
+
+ /**
+ * @param Throwable $e
+ * @return PrettyPageHandler
+ */
+ protected function getPrettyPageHandler(Throwable $e)
+ {
+ $handler = $this->app->make(PrettyPageHandler::class);
+
+ $handler->setPageTitle('Just another ' . get_class($e) . ' to fix :(');
+ $handler->setApplicationPaths([realpath(__DIR__ . '/../..')]);
+
+ $data = $this->getData();
+ $handler->addDataTable('Application', $data);
+
+ return $handler;
+ }
+
+ /**
+ * @return JsonResponseHandler
+ */
+ protected function getJsonResponseHandler()
+ {
+ $handler = $this->app->make(JsonResponseHandler::class);
+ $handler->setJsonApi(true);
+ $handler->addTraceToOutput(true);
+
+ return $handler;
+ }
+
+ /**
+ * Aggregate application data
+ *
+ * @return array
+ */
+ protected function getData()
+ {
+ global $user;
+
+ $data = [];
+ $data['user'] = $user;
+ $data['Booted'] = $this->app->isBooted();
+
+ return $data;
+ }
+}
diff --git a/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php b/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php
index 9c943d52..4f2ae654 100644
--- a/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php
+++ b/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php
@@ -3,27 +3,107 @@
namespace Engelsystem\Test\Unit\Exceptions;
use Engelsystem\Exceptions\ExceptionsServiceProvider;
-use Engelsystem\Exceptions\Handler as ExceptionHandler;
+use Engelsystem\Exceptions\Handler;
+use Engelsystem\Exceptions\Handlers\HandlerInterface;
+use Engelsystem\Exceptions\Handlers\Legacy;
+use Engelsystem\Exceptions\Handlers\LegacyDevelopment;
+use Engelsystem\Exceptions\Handlers\Whoops;
+use Engelsystem\Http\Request;
use Engelsystem\Test\Unit\ServiceProviderTest;
-use PHPUnit_Framework_MockObject_MockObject;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
class ExceptionsServiceProviderTest extends ServiceProviderTest
{
/**
* @covers \Engelsystem\Exceptions\ExceptionsServiceProvider::register()
+ * @covers \Engelsystem\Exceptions\ExceptionsServiceProvider::addProductionHandler()
+ * @covers \Engelsystem\Exceptions\ExceptionsServiceProvider::addDevelopmentHandler()
*/
public function testRegister()
{
- /** @var PHPUnit_Framework_MockObject_MockObject|ExceptionHandler $exceptionHandler */
- $exceptionHandler = $this->getMockBuilder(ExceptionHandler::class)
+ $app = $this->getApp(['make', 'instance', 'bind']);
+
+ /** @var MockObject|Handler $handler */
+ $handler = $this->createMock(Handler::class);
+ $this->setExpects($handler, 'register');
+ /** @var Legacy|MockObject $legacyHandler */
+ $legacyHandler = $this->createMock(Legacy::class);
+ /** @var LegacyDevelopment|MockObject $developmentHandler */
+ $developmentHandler = $this->createMock(LegacyDevelopment::class);
+
+ $whoopsHandler = $this->getMockBuilder(Whoops::class)
+ ->setConstructorArgs([$app])
->getMock();
- $app = $this->getApp();
+ $app->expects($this->exactly(3))
+ ->method('instance')
+ ->withConsecutive(
+ ['error.handler.production', $legacyHandler],
+ ['error.handler.development', $whoopsHandler],
+ ['error.handler', $handler]
+ );
+
+ $app->expects($this->exactly(4))
+ ->method('make')
+ ->withConsecutive(
+ [Handler::class],
+ [Legacy::class],
+ [LegacyDevelopment::class],
+ [Whoops::class]
+ )
+ ->willReturnOnConsecutiveCalls(
+ $handler,
+ $legacyHandler,
+ $developmentHandler,
+ $whoopsHandler
+ );
+
+ $app->expects($this->exactly(2))
+ ->method('bind')
+ ->withConsecutive(
+ [HandlerInterface::class, 'error.handler.production'],
+ [Handler::class, 'error.handler']
+ );
- $this->setExpects($app, 'make', [ExceptionHandler::class], $exceptionHandler);
- $this->setExpects($app, 'instance', ['error.handler', $exceptionHandler]);
+ $handler->expects($this->exactly(2))
+ ->method('setHandler')
+ ->withConsecutive(
+ [Handler::ENV_PRODUCTION, $legacyHandler],
+ [Handler::ENV_DEVELOPMENT, $whoopsHandler]
+ );
$serviceProvider = new ExceptionsServiceProvider($app);
$serviceProvider->register();
}
+
+ /**
+ * @covers \Engelsystem\Exceptions\ExceptionsServiceProvider::boot()
+ */
+ public function testBoot()
+ {
+ /** @var MockObject|Handler $handler */
+ $handler = $this->createMock(Handler::class);
+
+ /** @var MockObject|Request $request */
+ $request = $this->createMock(Request::class);
+
+ $handler->expects($this->once())
+ ->method('setRequest')
+ ->with($request);
+
+ $app = $this->getApp(['get']);
+ $app->expects($this->exactly(2))
+ ->method('get')
+ ->withConsecutive(
+ ['error.handler'],
+ ['request']
+ )
+ ->willReturnOnConsecutiveCalls(
+ $handler,
+ $request
+ );
+
+ $provider = new ExceptionsServiceProvider($app);
+ $provider->boot();
+ }
}
diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php
new file mode 100644
index 00000000..40202be8
--- /dev/null
+++ b/tests/Unit/Exceptions/HandlerTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Exceptions;
+
+use Engelsystem\Exceptions\Handler;
+use Engelsystem\Exceptions\Handlers\HandlerInterface;
+use Engelsystem\Http\Request;
+use ErrorException;
+use Exception;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject as Mock;
+
+class HandlerTest extends TestCase
+{
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::__construct()
+ */
+ public function testCreate()
+ {
+ /** @var Handler|Mock $handler */
+ $handler = new Handler();
+ $this->assertInstanceOf(Handler::class, $handler);
+ $this->assertEquals(Handler::ENV_PRODUCTION, $handler->getEnvironment());
+
+ $anotherHandler = new Handler(Handler::ENV_DEVELOPMENT);
+ $this->assertEquals(Handler::ENV_DEVELOPMENT, $anotherHandler->getEnvironment());
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::errorHandler()
+ */
+ public function testErrorHandler()
+ {
+ /** @var Handler|Mock $handler */
+ $handler = $this->getMockBuilder(Handler::class)
+ ->setMethods(['exceptionHandler'])
+ ->getMock();
+
+ $handler->expects($this->once())
+ ->method('exceptionHandler')
+ ->with($this->isInstanceOf(ErrorException::class));
+
+ $handler->errorHandler(1, 'Foo and bar!', '/lo/rem.php', 123);
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::exceptionHandler()
+ */
+ public function testExceptionHandler()
+ {
+ $exception = new Exception();
+
+ /** @var HandlerInterface|Mock $handlerMock */
+ $handlerMock = $this->getMockForAbstractClass(HandlerInterface::class);
+ $handlerMock->expects($this->once())
+ ->method('report')
+ ->with($exception);
+ $handlerMock->expects($this->once())
+ ->method('render')
+ ->with($this->isInstanceOf(Request::class), $exception);
+
+ /** @var Handler|Mock $handler */
+ $handler = $this->getMockBuilder(Handler::class)
+ ->setMethods(['die'])
+ ->getMock();
+ $handler->expects($this->once())
+ ->method('die');
+
+ $handler->setHandler(Handler::ENV_PRODUCTION, $handlerMock);
+
+ $handler->exceptionHandler($exception);
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::register()
+ */
+ public function testRegister()
+ {
+ /** @var Handler|Mock $handler */
+ $handler = $this->getMockForAbstractClass(Handler::class);
+ $handler->register();
+
+ set_error_handler($errorHandler = set_error_handler('var_dump'));
+ $this->assertEquals($handler, array_shift($errorHandler));
+
+ set_exception_handler($exceptionHandler = set_error_handler('var_dump'));
+ $this->assertEquals($handler, array_shift($exceptionHandler));
+
+ restore_error_handler();
+ restore_exception_handler();
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::setEnvironment()
+ * @covers \Engelsystem\Exceptions\Handler::getEnvironment()
+ */
+ public function testEnvironment()
+ {
+ $handler = new Handler();
+
+ $handler->setEnvironment(Handler::ENV_DEVELOPMENT);
+ $this->assertEquals(Handler::ENV_DEVELOPMENT, $handler->getEnvironment());
+
+ $handler->setEnvironment(Handler::ENV_PRODUCTION);
+ $this->assertEquals(Handler::ENV_PRODUCTION, $handler->getEnvironment());
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::setHandler()
+ * @covers \Engelsystem\Exceptions\Handler::getHandler()
+ */
+ public function testHandler()
+ {
+ $handler = new Handler();
+ /** @var HandlerInterface|Mock $devHandler */
+ $devHandler = $this->getMockForAbstractClass(HandlerInterface::class);
+ /** @var HandlerInterface|Mock $prodHandler */
+ $prodHandler = $this->getMockForAbstractClass(HandlerInterface::class);
+
+ $handler->setHandler(Handler::ENV_DEVELOPMENT, $devHandler);
+ $handler->setHandler(Handler::ENV_PRODUCTION, $prodHandler);
+ $this->assertEquals($devHandler, $handler->getHandler(Handler::ENV_DEVELOPMENT));
+ $this->assertEquals($prodHandler, $handler->getHandler(Handler::ENV_PRODUCTION));
+ $this->assertCount(2, $handler->getHandler());
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handler::setRequest()
+ * @covers \Engelsystem\Exceptions\Handler::getRequest()
+ */
+ public function testRequest()
+ {
+ $handler = new Handler();
+ /** @var Request|Mock $request */
+ $request = $this->createMock(Request::class);
+
+ $handler->setRequest($request);
+ $this->assertEquals($request, $handler->getRequest());
+ }
+}
diff --git a/tests/Unit/Exceptions/Handlers/LegacyDevelopmentTest.php b/tests/Unit/Exceptions/Handlers/LegacyDevelopmentTest.php
new file mode 100644
index 00000000..d5390c9e
--- /dev/null
+++ b/tests/Unit/Exceptions/Handlers/LegacyDevelopmentTest.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Exceptions\handlers;
+
+
+use Engelsystem\Exceptions\Handlers\LegacyDevelopment;
+use Engelsystem\Http\Request;
+use ErrorException;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject as Mock;
+
+class LegacyDevelopmentTest extends TestCase
+{
+ /**
+ * @covers \Engelsystem\Exceptions\Handlers\LegacyDevelopment::render()
+ * @covers \Engelsystem\Exceptions\Handlers\LegacyDevelopment::formatStackTrace()
+ */
+ public function testRender()
+ {
+ $handler = new LegacyDevelopment();
+ /** @var Request|Mock $request */
+ $request = $this->createMock(Request::class);
+ $exception = new ErrorException('Lorem Ipsum', 4242, 1, 'foo.php', 9999);
+
+ $regex = sprintf(
+ '%%<pre.*>.*ErrorException.*4242.*Lorem Ipsum.*%s.*%s.*%s.*</pre>%%is',
+ 'foo.php',
+ 9999,
+ __FUNCTION__
+ );
+ $this->expectOutputRegex($regex);
+
+ $handler->render($request, $exception);
+ }
+}
diff --git a/tests/Unit/Exceptions/Handlers/LegacyTest.php b/tests/Unit/Exceptions/Handlers/LegacyTest.php
new file mode 100644
index 00000000..04b214f2
--- /dev/null
+++ b/tests/Unit/Exceptions/Handlers/LegacyTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Exceptions\handlers;
+
+
+use Engelsystem\Exceptions\Handlers\Legacy;
+use Engelsystem\Http\Request;
+use Exception;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject as Mock;
+
+class LegacyTest extends TestCase
+{
+ /**
+ * @covers \Engelsystem\Exceptions\Handlers\Legacy::render()
+ */
+ public function testRender()
+ {
+ $handler = new Legacy();
+ /** @var Request|Mock $request */
+ $request = $this->createMock(Request::class);
+ /** @var Exception|Mock $exception */
+ $exception = $this->createMock(Exception::class);
+
+ $this->expectOutputRegex('/.*error occurred.*/i');
+
+ $handler->render($request, $exception);
+ }
+
+ /**
+ * @covers \Engelsystem\Exceptions\Handlers\Legacy::report()
+ * @covers \Engelsystem\Exceptions\Handlers\Legacy::stripBasePath()
+ */
+ public function testReport()
+ {
+ $handler = new Legacy();
+ $exception = new Exception('Lorem Ipsum', 4242);
+ $line = __LINE__ - 1;
+
+ $log = tempnam(sys_get_temp_dir(), 'engelsystem-log');
+ $errorLog = ini_get('error_log');
+ ini_set('error_log', $log);
+ $handler->report($exception);
+ ini_set('error_log', $errorLog);
+ $logContent = file_get_contents($log);
+ unset($log);
+
+ $this->assertContains('4242', $logContent);
+ $this->assertContains('Lorem Ipsum', $logContent);
+ $this->assertContains(basename(__FILE__), $logContent);
+ $this->assertContains((string)$line, $logContent);
+ $this->assertContains(__FUNCTION__, $logContent);
+ $this->assertContains(json_encode(__CLASS__), $logContent);
+ }
+}
diff --git a/tests/Unit/Exceptions/Handlers/WhoopsTest.php b/tests/Unit/Exceptions/Handlers/WhoopsTest.php
new file mode 100644
index 00000000..261ee83f
--- /dev/null
+++ b/tests/Unit/Exceptions/Handlers/WhoopsTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Exceptions\handlers;
+
+
+use Engelsystem\Application;
+use Engelsystem\Exceptions\Handlers\Whoops;
+use Engelsystem\Http\Request;
+use Exception;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject as Mock;
+use Whoops\Handler\JsonResponseHandler;
+use Whoops\Handler\PrettyPageHandler;
+use Whoops\Run as WhoopsRunner;
+use Whoops\RunInterface as WhoopsRunnerInterface;
+
+class WhoopsTest extends TestCase
+{
+ /**
+ * @covers \Engelsystem\Exceptions\Handlers\Whoops
+ */
+ public function testRender()
+ {
+ /** @var Application|Mock $app */
+ $app = $this->createMock(Application::class);
+ /** @var Request|Mock $request */
+ $request = $this->createMock(Request::class);
+ $request->expects($this->once())
+ ->method('isXmlHttpRequest')
+ ->willReturn(true);
+ /** @var WhoopsRunnerInterface|Mock $whoopsRunner */
+ $whoopsRunner = $this->getMockForAbstractClass(WhoopsRunnerInterface::class);
+ /** @var PrettyPageHandler|Mock $prettyPageHandler */
+ $prettyPageHandler = $this->createMock(PrettyPageHandler::class);
+ $prettyPageHandler
+ ->expects($this->atLeastOnce())
+ ->method('setApplicationPaths');
+ $prettyPageHandler
+ ->expects($this->once())
+ ->method('setApplicationPaths');
+ $prettyPageHandler
+ ->expects($this->once())
+ ->method('addDataTable');
+ /** @var JsonResponseHandler|Mock $jsonResponseHandler */
+ $jsonResponseHandler = $this->createMock(JsonResponseHandler::class);
+ $jsonResponseHandler->expects($this->once())
+ ->method('setJsonApi')
+ ->with(true);
+ $jsonResponseHandler->expects($this->once())
+ ->method('addTraceToOutput')
+ ->with(true);
+ /** @var Exception|Mock $exception */
+ $exception = $this->createMock(Exception::class);
+
+ $app->expects($this->exactly(3))
+ ->method('make')
+ ->withConsecutive(
+ [WhoopsRunner::class],
+ [PrettyPageHandler::class],
+ [JsonResponseHandler::class]
+ )
+ ->willReturnOnConsecutiveCalls(
+ $whoopsRunner,
+ $prettyPageHandler,
+ $jsonResponseHandler
+ );
+
+ $whoopsRunner
+ ->expects($this->exactly(2))
+ ->method('pushHandler')
+ ->withConsecutive(
+ [$prettyPageHandler],
+ [$jsonResponseHandler]
+ );
+ $whoopsRunner
+ ->expects($this->once())
+ ->method('handleException')
+ ->with($exception);
+
+ $handler = new Whoops($app);
+ $handler->render($request, $exception);
+ }
+}