174 строки
5.1 KiB
PHP
174 строки
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OCA\Mail\Tests\Unit\Http\Middleware;
|
|
|
|
use ChristophWurst\Nextcloud\Testing\TestCase;
|
|
use Exception;
|
|
use Horde_Imap_Client_Exception;
|
|
use OCA\Mail\Exception\NotImplemented;
|
|
use OCA\Mail\Exception\ServiceException;
|
|
use OCA\Mail\Http\Middleware\ErrorMiddleware;
|
|
use OCA\Mail\Http\TrapError;
|
|
use OCP\AppFramework\Controller;
|
|
use OCP\AppFramework\Db\DoesNotExistException;
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Http\JSONResponse;
|
|
use OCP\IConfig;
|
|
use OCP\IRequest;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use Psr\Log\LoggerInterface;
|
|
use Throwable;
|
|
|
|
class ErrorMiddlewareTest extends TestCase {
|
|
/** @var IConfig|MockObject */
|
|
private $config;
|
|
|
|
/** @var LoggerInterface|MockObject */
|
|
private $logger;
|
|
|
|
/** @var ErrorMiddleware */
|
|
private $middleware;
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
|
|
$this->config = $this->createMock(IConfig::class);
|
|
$this->logger = $this->createMock(LoggerInterface::class);
|
|
|
|
$this->middleware = new ErrorMiddleware(
|
|
$this->config,
|
|
$this->logger,
|
|
);
|
|
}
|
|
|
|
public function testDoesNotChangeSuccessfulResponses() {
|
|
$response = new JSONResponse();
|
|
$controller = $this->createMock(Controller::class);
|
|
|
|
$after = $this->middleware->afterController($controller, 'index', $response);
|
|
|
|
$this->assertSame($response, $after);
|
|
}
|
|
|
|
public function testDoesNotChangeUntaggedMethodResponses() {
|
|
$request = $this->createMock(IRequest::class);
|
|
$controller = new class($request) extends Controller {
|
|
public function __construct(IRequest $request) {
|
|
parent::__construct('myapp', $request);
|
|
}
|
|
public function foo() {
|
|
}
|
|
};
|
|
$exception = new DoesNotExistException('nope');
|
|
$this->expectException(DoesNotExistException::class);
|
|
|
|
$this->middleware->afterException($controller, 'foo', $exception);
|
|
}
|
|
|
|
public function trappedErrorsData() {
|
|
return [
|
|
[new DoesNotExistException('does not exist'), false, Http::STATUS_NOT_FOUND],
|
|
[new ServiceException(), true, Http::STATUS_INTERNAL_SERVER_ERROR],
|
|
[new NotImplemented(), false, Http::STATUS_NOT_IMPLEMENTED],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider trappedErrorsData
|
|
*/
|
|
public function testTrapsErrors($exception, $shouldLog, $expectedStatus) {
|
|
$request = $this->createMock(IRequest::class);
|
|
$controller = new class($request) extends Controller {
|
|
public function __construct(IRequest $request) {
|
|
parent::__construct('myapp', $request);
|
|
}
|
|
#[TrapError]
|
|
public function foo() {
|
|
}
|
|
};
|
|
$this->logger->expects($this->exactly($shouldLog ? 1 : 0))
|
|
->method('error')
|
|
->with($exception->getMessage(), [
|
|
'exception' => $exception,
|
|
]);
|
|
|
|
$response = $this->middleware->afterException($controller, 'foo', $exception);
|
|
|
|
$this->assertInstanceOf(JSONResponse::class, $response);
|
|
$this->assertEquals($expectedStatus, $response->getStatus());
|
|
}
|
|
|
|
public function testSerializesRecursively() {
|
|
$inner = new Exception();
|
|
$outer = new ServiceException('Test', 0, $inner);
|
|
$request = $this->createMock(IRequest::class);
|
|
$controller = new class($request) extends Controller {
|
|
public function __construct(IRequest $request) {
|
|
parent::__construct('myapp', $request);
|
|
}
|
|
|
|
#[TrapError]
|
|
public function foo() {
|
|
}
|
|
};
|
|
$this->logger->expects($this->once())
|
|
->method('error')
|
|
->with($outer->getMessage(), [
|
|
'exception' => $outer,
|
|
]);
|
|
|
|
$response = $this->middleware->afterException($controller, 'foo', $outer);
|
|
|
|
$this->assertInstanceOf(JSONResponse::class, $response);
|
|
}
|
|
|
|
public function temporaryExceptionsData(): array {
|
|
return [
|
|
[new ServiceException('not temporary'), false],
|
|
[new ServiceException('temporary', 0, new Horde_Imap_Client_Exception('', Horde_Imap_Client_Exception::DISCONNECT)), true],
|
|
[new ServiceException('temporary', 0, new Horde_Imap_Client_Exception('', Horde_Imap_Client_Exception::SERVER_CONNECT)), false],
|
|
[new ServiceException('temporary', 0, new Horde_Imap_Client_Exception('', Horde_Imap_Client_Exception::SERVER_READERROR)), true],
|
|
[new ServiceException('temporary', 0, new Horde_Imap_Client_Exception('', Horde_Imap_Client_Exception::SERVER_WRITEERROR)), true],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider temporaryExceptionsData
|
|
*/
|
|
public function testHandlesTemporaryErrors(Throwable $ex, bool $temporary): void {
|
|
$controller = $this->createMock(Controller::class);
|
|
$request = $this->createMock(IRequest::class);
|
|
$controller = new class($request) extends Controller {
|
|
public function __construct(IRequest $request) {
|
|
parent::__construct('myapp', $request);
|
|
}
|
|
|
|
#[TrapError]
|
|
public function foo() {
|
|
}
|
|
};
|
|
$this->logger->expects($this->once())
|
|
->method($temporary ? 'warning' : 'error')
|
|
->with($ex->getMessage(),
|
|
[
|
|
'exception' => $ex,
|
|
]
|
|
);
|
|
|
|
$response = $this->middleware->afterException($controller, 'foo', $ex);
|
|
|
|
$this->assertInstanceOf(JSONResponse::class, $response);
|
|
$this->assertSame(
|
|
$temporary ? Http::STATUS_SERVICE_UNAVAILABLE : Http::STATUS_INTERNAL_SERVER_ERROR,
|
|
$response->getStatus()
|
|
);
|
|
}
|
|
}
|