Refactored and fixed unit tests

This commit is contained in:
Alejandro Celaya 2019-11-26 22:12:52 +01:00
parent 5213faa0a1
commit 3b56fc3760
15 changed files with 11 additions and 171 deletions

View File

@ -16,7 +16,7 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Cannot delete short URL';
public const TYPE = 'INVALID_SHORTCODE_DELETION'; // FIXME Should be INVALID_SHORT_URL_DELETION
private const TYPE = 'INVALID_SHORTCODE_DELETION'; // FIXME Should be INVALID_SHORT_URL_DELETION
/** @var int */
private $visitsThreshold;

View File

@ -16,7 +16,7 @@ class InvalidUrlException extends DomainException implements ProblemDetailsExcep
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Invalid URL';
public const TYPE = 'INVALID_URL';
private const TYPE = 'INVALID_URL';
public static function fromUrl(string $url, ?Throwable $previous = null): self
{

View File

@ -15,7 +15,7 @@ class NonUniqueSlugException extends InvalidArgumentException implements Problem
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Invalid custom slug';
public const TYPE = 'INVALID_SLUG';
private const TYPE = 'INVALID_SLUG';
public static function fromSlug(string $slug, ?string $domain): self
{

View File

@ -15,7 +15,7 @@ class ShortUrlNotFoundException extends DomainException implements ProblemDetail
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Short URL not found';
public const TYPE = 'INVALID_SHORTCODE';
private const TYPE = 'INVALID_SHORTCODE';
public static function fromNotFoundShortCode(string $shortCode, ?string $domain = null): self
{

View File

@ -15,7 +15,7 @@ class TagNotFoundException extends DomainException implements ProblemDetailsExce
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Tag not found';
public const TYPE = 'TAG_NOT_FOUND';
private const TYPE = 'TAG_NOT_FOUND';
public static function fromTag(string $tag): self
{

View File

@ -22,7 +22,7 @@ class ValidationException extends InvalidArgumentException implements ProblemDet
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Invalid data';
public const TYPE = 'INVALID_ARGUMENT';
private const TYPE = 'INVALID_ARGUMENT';
/** @var array */
private $invalidElements;

View File

@ -10,7 +10,6 @@ use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Rest\Authentication\JWTServiceInterface;
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
use Shlinkio\Shlink\Rest\Util\RestUtils;
use Zend\Diactoros\Response\JsonResponse;
/** @deprecated */

View File

@ -9,7 +9,6 @@ use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Util\RestUtils;
use Zend\Diactoros\Response\JsonResponse;
class EditShortUrlTagsAction extends AbstractRestAction

View File

@ -16,7 +16,7 @@ class MissingAuthenticationException extends RuntimeException implements Problem
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Invalid authorization';
public const TYPE = 'INVALID_AUTHORIZATION';
private const TYPE = 'INVALID_AUTHORIZATION';
public static function fromExpectedTypes(array $expectedTypes): self
{

View File

@ -14,17 +14,10 @@ class VerifyAuthenticationException extends RuntimeException implements ProblemD
{
use CommonProblemDetailsExceptionTrait;
/** @var string */
private $errorCode;
/** @var string */
private $publicMessage;
public static function forInvalidApiKey(): self
{
$e = new self('Provided API key does not exist or is invalid.');
$e->publicMessage = $e->getMessage();
$e->errorCode = 'INVALID_API_KEY';
$e->detail = $e->getMessage();
$e->title = 'Invalid API key';
$e->type = 'INVALID_API_KEY';
@ -41,8 +34,6 @@ class VerifyAuthenticationException extends RuntimeException implements ProblemD
. 'token on every new request on the Authorization header'
);
$e->publicMessage = $e->getMessage();
$e->errorCode = 'INVALID_AUTH_TOKEN';
$e->detail = $e->getMessage();
$e->title = 'Invalid auth token';
$e->type = 'INVALID_AUTH_TOKEN';
@ -56,8 +47,6 @@ class VerifyAuthenticationException extends RuntimeException implements ProblemD
{
$e = new self('You need to provide the Bearer type in the Authorization header.');
$e->publicMessage = $e->getMessage();
$e->errorCode = 'INVALID_AUTHORIZATION';
$e->detail = $e->getMessage();
$e->title = 'Invalid authorization';
$e->type = 'INVALID_AUTHORIZATION';
@ -71,8 +60,6 @@ class VerifyAuthenticationException extends RuntimeException implements ProblemD
{
$e = new self(sprintf('Provided authorization type %s is not supported. Use Bearer instead.', $providedType));
$e->publicMessage = $e->getMessage();
$e->errorCode = 'INVALID_AUTHORIZATION';
$e->detail = $e->getMessage();
$e->title = 'Invalid authorization';
$e->type = 'INVALID_AUTHORIZATION';
@ -80,14 +67,4 @@ class VerifyAuthenticationException extends RuntimeException implements ProblemD
return $e;
}
public function getErrorCode(): string
{
return $this->errorCode;
}
public function getPublicMessage(): string
{
return $this->publicMessage;
}
}

View File

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Util;
use Shlinkio\Shlink\Rest\Exception as Rest;
/** @deprecated */
class RestUtils
{
public const INVALID_AUTHORIZATION_ERROR = Rest\MissingAuthenticationException::TYPE;
}

View File

@ -37,7 +37,7 @@ class RequestToAuthPluginTest extends TestCase
$this->expectException(MissingAuthenticationException::class);
$this->expectExceptionMessage(sprintf(
'None of the valid authentication mechanisms where provided. Expected one of ["%s"]',
'Expected one of the following authentication headers, but none were provided, ["%s"]',
implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)
));

View File

@ -4,62 +4,16 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Exception;
use Exception;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException;
use Throwable;
use function array_map;
use function random_int;
use function range;
class VerifyAuthenticationExceptionTest extends TestCase
{
use StringUtilsTrait;
/**
* @test
* @dataProvider provideConstructorData
*/
public function constructCreatesExpectedException(
string $errorCode,
string $publicMessage,
string $message,
int $code,
?Throwable $prev
): void {
$e = new VerifyAuthenticationException($errorCode, $publicMessage, $message, $code, $prev);
$this->assertEquals($code, $e->getCode());
$this->assertEquals($message, $e->getMessage());
$this->assertEquals($errorCode, $e->getErrorCode());
$this->assertEquals($publicMessage, $e->getPublicMessage());
$this->assertEquals($prev, $e->getPrevious());
}
public function provideConstructorData(): iterable
{
return array_map(function (int $i) {
return [
$this->generateRandomString(),
$this->generateRandomString(30),
$this->generateRandomString(50),
$i,
random_int(0, 1) === 1 ? new Exception('Prev') : null,
];
}, range(10, 20));
}
/** @test */
public function defaultConstructorValuesAreKept(): void
public function createsExpectedExceptionForInvalidApiKey(): void
{
$e = new VerifyAuthenticationException('foo', 'bar');
$e = VerifyAuthenticationException::forInvalidApiKey();
$this->assertEquals(0, $e->getCode());
$this->assertEquals('', $e->getMessage());
$this->assertEquals('foo', $e->getErrorCode());
$this->assertEquals('bar', $e->getPublicMessage());
$this->assertNull($e->getPrevious());
$this->assertEquals('Provided API key does not exist or is invalid.', $e->getMessage());
}
}

View File

@ -4,12 +4,10 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Middleware;
use Exception;
use Fig\Http\Message\RequestMethodInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Container\ContainerExceptionInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
@ -17,20 +15,13 @@ use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Rest\Action\AuthenticateAction;
use Shlinkio\Shlink\Rest\Authentication\Plugin\AuthenticationPluginInterface;
use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPlugin;
use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPluginInterface;
use Shlinkio\Shlink\Rest\Exception\MissingAuthenticationException;
use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
use Shlinkio\Shlink\Rest\Util\RestUtils;
use Throwable;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequest;
use Zend\Expressive\Router\Route;
use Zend\Expressive\Router\RouteResult;
use function implode;
use function sprintf;
use function Zend\Stratigility\middleware;
class AuthenticationMiddlewareTest extends TestCase
@ -93,72 +84,6 @@ class AuthenticationMiddlewareTest extends TestCase
)->withMethod(RequestMethodInterface::METHOD_OPTIONS)];
}
/**
* @test
* @dataProvider provideExceptions
*/
public function errorIsReturnedWhenNoValidAuthIsProvided(Throwable $e): void
{
$request = (new ServerRequest())->withAttribute(
RouteResult::class,
RouteResult::fromRoute(new Route('bar', $this->getDummyMiddleware()), [])
);
$fromRequest = $this->requestToPlugin->fromRequest(Argument::any())->willThrow($e);
$logWarning = $this->logger->warning('Invalid or no authentication provided. {e}', ['e' => $e])->will(
function () {
}
);
/** @var Response\JsonResponse $response */
$response = $this->middleware->process($request, $this->prophesize(RequestHandlerInterface::class)->reveal());
$payload = $response->getPayload();
$this->assertEquals(RestUtils::INVALID_AUTHORIZATION_ERROR, $payload['error']);
$this->assertEquals(sprintf(
'Expected one of the following authentication headers, but none were provided, ["%s"]',
implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)
), $payload['message']);
$fromRequest->shouldHaveBeenCalledOnce();
$logWarning->shouldHaveBeenCalledOnce();
}
public function provideExceptions(): iterable
{
$containerException = new class extends Exception implements ContainerExceptionInterface {
};
yield 'container exception' => [$containerException];
yield 'authentication exception' => [MissingAuthenticationException::fromExpectedTypes([])];
}
/** @test */
public function errorIsReturnedWhenVerificationFails(): void
{
$request = (new ServerRequest())->withAttribute(
RouteResult::class,
RouteResult::fromRoute(new Route('bar', $this->getDummyMiddleware()), [])
);
$e = VerifyAuthenticationException::forInvalidApiKey();
$plugin = $this->prophesize(AuthenticationPluginInterface::class);
$verify = $plugin->verify($request)->willThrow($e);
$fromRequest = $this->requestToPlugin->fromRequest(Argument::any())->willReturn($plugin->reveal());
$logWarning = $this->logger->warning('Authentication verification failed. {e}', ['e' => $e])->will(
function () {
}
);
/** @var Response\JsonResponse $response */
$response = $this->middleware->process($request, $this->prophesize(RequestHandlerInterface::class)->reveal());
$payload = $response->getPayload();
$this->assertEquals('the_error', $payload['error']);
$this->assertEquals('the_message', $payload['message']);
$verify->shouldHaveBeenCalledOnce();
$fromRequest->shouldHaveBeenCalledOnce();
$logWarning->shouldHaveBeenCalledOnce();
}
/** @test */
public function updatedResponseIsReturnedWhenVerificationPasses(): void
{

View File

@ -1,6 +1,5 @@
parameters:
ignoreErrors:
- '#is not subtype of Throwable#'
- '#Undefined variable: \$metadata#'
- '#AbstractQuery::setParameters()#'
- '#mustRun()#'