mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-09 23:15:41 -06:00
Merge pull request #559 from acelaya-forks/feature/msi-80
Feature/msi 80
This commit is contained in:
commit
03825469ca
@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
#### Changed
|
||||
|
||||
* [#492](https://github.com/shlinkio/shlink/issues/492) Updated to monolog 2, together with other dependencies, like Symfony 5 and infection-php.
|
||||
* [#527](https://github.com/shlinkio/shlink/issues/527) Increased minimum required mutation score for unit tests to 80%.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
|
@ -130,9 +130,9 @@
|
||||
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite",
|
||||
"test:api": "bin/test/run-api-tests.sh",
|
||||
"test:unit:pretty": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage",
|
||||
"infect": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered",
|
||||
"infect:ci": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --coverage=build",
|
||||
"infect:show": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --show-mutations",
|
||||
"infect": "infection --threads=4 --min-msi=80 --log-verbosity=default --only-covered",
|
||||
"infect:ci": "infection --threads=4 --min-msi=80 --log-verbosity=default --only-covered --coverage=build",
|
||||
"infect:show": "infection --threads=4 --min-msi=80 --log-verbosity=default --only-covered --show-mutations",
|
||||
"infect:test": [
|
||||
"@test:unit:ci",
|
||||
"@infect:ci"
|
||||
|
@ -27,6 +27,14 @@ class DeleteShortUrlExceptionTest extends TestCase
|
||||
|
||||
$this->assertEquals($threshold, $e->getVisitsThreshold());
|
||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getDetail());
|
||||
$this->assertEquals([
|
||||
'shortCode' => $shortCode,
|
||||
'threshold' => $threshold,
|
||||
], $e->getAdditionalData());
|
||||
$this->assertEquals('Cannot delete short URL', $e->getTitle());
|
||||
$this->assertEquals('INVALID_SHORTCODE_DELETION', $e->getType());
|
||||
$this->assertEquals(422, $e->getStatus());
|
||||
}
|
||||
|
||||
public function provideThresholds(): array
|
||||
|
@ -10,6 +10,8 @@ use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||
use Throwable;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class InvalidUrlExceptionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
@ -18,10 +20,17 @@ class InvalidUrlExceptionTest extends TestCase
|
||||
*/
|
||||
public function properlyCreatesExceptionFromUrl(?Throwable $prev): void
|
||||
{
|
||||
$e = InvalidUrlException::fromUrl('http://the_url.com', $prev);
|
||||
$url = 'http://the_url.com';
|
||||
$expectedMessage = sprintf('Provided URL %s is invalid. Try with a different one.', $url);
|
||||
$e = InvalidUrlException::fromUrl($url, $prev);
|
||||
|
||||
$this->assertEquals('Provided URL http://the_url.com is invalid. Try with a different one.', $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getDetail());
|
||||
$this->assertEquals('Invalid URL', $e->getTitle());
|
||||
$this->assertEquals('INVALID_URL', $e->getType());
|
||||
$this->assertEquals(['url' => $url], $e->getAdditionalData());
|
||||
$this->assertEquals(StatusCodeInterface::STATUS_BAD_REQUEST, $e->getCode());
|
||||
$this->assertEquals(StatusCodeInterface::STATUS_BAD_REQUEST, $e->getStatus());
|
||||
$this->assertEquals($prev, $e->getPrevious());
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,19 @@ class NonUniqueSlugExceptionTest extends TestCase
|
||||
*/
|
||||
public function properlyCreatesExceptionFromSlug(string $expectedMessage, string $slug, ?string $domain): void
|
||||
{
|
||||
$expectedAdditional = ['customSlug' => $slug];
|
||||
if ($domain !== null) {
|
||||
$expectedAdditional['domain'] = $domain;
|
||||
}
|
||||
|
||||
$e = NonUniqueSlugException::fromSlug($slug, $domain);
|
||||
|
||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getDetail());
|
||||
$this->assertEquals('Invalid custom slug', $e->getTitle());
|
||||
$this->assertEquals('INVALID_SLUG', $e->getType());
|
||||
$this->assertEquals(400, $e->getStatus());
|
||||
$this->assertEquals($expectedAdditional, $e->getAdditionalData());
|
||||
}
|
||||
|
||||
public function provideMessages(): iterable
|
||||
|
@ -18,8 +18,19 @@ class ShortUrlNotFoundExceptionTest extends TestCase
|
||||
string $shortCode,
|
||||
?string $domain
|
||||
): void {
|
||||
$expectedAdditional = ['shortCode' => $shortCode];
|
||||
if ($domain !== null) {
|
||||
$expectedAdditional['domain'] = $domain;
|
||||
}
|
||||
|
||||
$e = ShortUrlNotFoundException::fromNotFoundShortCode($shortCode, $domain);
|
||||
|
||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getDetail());
|
||||
$this->assertEquals('Short URL not found', $e->getTitle());
|
||||
$this->assertEquals('INVALID_SHORTCODE', $e->getType());
|
||||
$this->assertEquals(404, $e->getStatus());
|
||||
$this->assertEquals($expectedAdditional, $e->getAdditionalData());
|
||||
}
|
||||
|
||||
public function provideMessages(): iterable
|
||||
|
28
module/Core/test/Exception/TagNotFoundExceptionTest.php
Normal file
28
module/Core/test/Exception/TagNotFoundExceptionTest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\Rest\Exception;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class TagNotFoundExceptionTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function properlyCreatesExceptionFromNotFoundTag(): void
|
||||
{
|
||||
$tag = 'foo';
|
||||
$expectedMessage = sprintf('Tag with name "%s" could not be found', $tag);
|
||||
$e = TagNotFoundException::fromTag($tag);
|
||||
|
||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getDetail());
|
||||
$this->assertEquals('Tag not found', $e->getTitle());
|
||||
$this->assertEquals('TAG_NOT_FOUND', $e->getType());
|
||||
$this->assertEquals(['tag' => $tag], $e->getAdditionalData());
|
||||
$this->assertEquals(404, $e->getStatus());
|
||||
}
|
||||
}
|
@ -4,19 +4,26 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest;
|
||||
|
||||
use Zend\Config\Factory;
|
||||
use Zend\Stdlib\Glob;
|
||||
|
||||
use function Shlinkio\Shlink\Common\loadConfigFromGlob;
|
||||
use function sprintf;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
private const ROUTES_PREFIX = '/rest/v{version:1|2}';
|
||||
|
||||
/** @var callable */
|
||||
private $loadConfig;
|
||||
|
||||
public function __construct(?callable $loadConfig = null)
|
||||
{
|
||||
$this->loadConfig = $loadConfig ?? function (string $glob) {
|
||||
return loadConfigFromGlob($glob);
|
||||
};
|
||||
}
|
||||
|
||||
public function __invoke()
|
||||
{
|
||||
/** @var array $config */
|
||||
$config = Factory::fromFiles(Glob::glob(__DIR__ . '/../config/{,*.}config.php', Glob::GLOB_BRACE));
|
||||
$config = ($this->loadConfig)(__DIR__ . '/../config/{,*.}config.php');
|
||||
return $this->applyRoutesPrefix($config);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class MissingAuthenticationException extends RuntimeException implements Problem
|
||||
public static function fromExpectedTypes(array $expectedTypes): self
|
||||
{
|
||||
$e = new self(sprintf(
|
||||
'Expected one of the following authentication headers, but none were provided, ["%s"]',
|
||||
'Expected one of the following authentication headers, ["%s"], but none were provided',
|
||||
implode('", "', $expectedTypes)
|
||||
));
|
||||
|
||||
|
@ -17,7 +17,7 @@ class AuthenticationTest extends ApiTestCase
|
||||
public function authorizationErrorIsReturnedIfNoApiKeyIsSent(): void
|
||||
{
|
||||
$expectedDetail = sprintf(
|
||||
'Expected one of the following authentication headers, but none were provided, ["%s"]',
|
||||
'Expected one of the following authentication headers, ["%s"], but none were provided',
|
||||
implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)
|
||||
);
|
||||
|
||||
|
@ -4,14 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl;
|
||||
|
||||
use Cake\Chronos\Chronos;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use Shlinkio\Shlink\Rest\Action\ShortUrl\CreateShortUrlAction;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\Diactoros\Uri;
|
||||
|
||||
use function strpos;
|
||||
@ -41,20 +44,41 @@ class CreateShortUrlActionTest extends TestCase
|
||||
$this->action->handle(new ServerRequest());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function properShortcodeConversionReturnsData(): void
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider provideRequestBodies
|
||||
*/
|
||||
public function properShortcodeConversionReturnsData(array $body, ShortUrlMeta $expectedMeta): void
|
||||
{
|
||||
$shortUrl = new ShortUrl('');
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())
|
||||
->willReturn($shortUrl)
|
||||
->shouldBeCalledOnce();
|
||||
$shorten = $this->urlShortener->urlToShortCode(
|
||||
Argument::type(Uri::class),
|
||||
Argument::type('array'),
|
||||
$expectedMeta
|
||||
)->willReturn($shortUrl);
|
||||
|
||||
$request = (new ServerRequest())->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
]);
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody($body);
|
||||
$response = $this->action->handle($request);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), $shortUrl->toString(self::DOMAIN_CONFIG)) > 0);
|
||||
$shorten->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
|
||||
public function provideRequestBodies(): iterable
|
||||
{
|
||||
$fullMeta = [
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
'validSince' => Chronos::now()->toAtomString(),
|
||||
'validUntil' => Chronos::now()->toAtomString(),
|
||||
'customSlug' => 'foo-bar-baz',
|
||||
'maxVisits' => 50,
|
||||
'findIfExists' => true,
|
||||
'domain' => 'my-domain.com',
|
||||
];
|
||||
|
||||
yield [['longUrl' => 'http://www.domain.com/foo/bar'], ShortUrlMeta::createEmpty()];
|
||||
yield [$fullMeta, ShortUrlMeta::createFromRawData($fullMeta)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,7 +37,7 @@ class RequestToAuthPluginTest extends TestCase
|
||||
|
||||
$this->expectException(MissingAuthenticationException::class);
|
||||
$this->expectExceptionMessage(sprintf(
|
||||
'Expected one of the following authentication headers, but none were provided, ["%s"]',
|
||||
'Expected one of the following authentication headers, ["%s"], but none were provided',
|
||||
implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)
|
||||
));
|
||||
|
||||
|
@ -20,9 +20,31 @@ class ConfigProviderTest extends TestCase
|
||||
/** @test */
|
||||
public function properConfigIsReturned(): void
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
$config = ($this->configProvider)();
|
||||
|
||||
$this->assertArrayHasKey('routes', $config);
|
||||
$this->assertArrayHasKey('dependencies', $config);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function routesAreProperlyPrefixed(): void
|
||||
{
|
||||
$configProvider = new ConfigProvider(function () {
|
||||
return [
|
||||
'routes' => [
|
||||
['path' => '/foo'],
|
||||
['path' => '/bar'],
|
||||
['path' => '/baz/foo'],
|
||||
],
|
||||
];
|
||||
});
|
||||
|
||||
$config = $configProvider();
|
||||
|
||||
$this->assertEquals([
|
||||
['path' => '/rest/v{version:1|2}/foo'],
|
||||
['path' => '/rest/v{version:1|2}/bar'],
|
||||
['path' => '/rest/v{version:1|2}/baz/foo'],
|
||||
], $config['routes']);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\Rest\Exception;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Rest\Exception\MissingAuthenticationException;
|
||||
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
|
||||
class MissingAuthenticationExceptionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider provideExpectedTypes
|
||||
*/
|
||||
public function exceptionIsProperlyCreatedFromExpectedTypes(array $expectedTypes): void
|
||||
{
|
||||
$expectedMessage = sprintf(
|
||||
'Expected one of the following authentication headers, ["%s"], but none were provided',
|
||||
implode('", "', $expectedTypes)
|
||||
);
|
||||
|
||||
$e = MissingAuthenticationException::fromExpectedTypes($expectedTypes);
|
||||
|
||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||
$this->assertEquals($expectedMessage, $e->getDetail());
|
||||
$this->assertEquals('Invalid authorization', $e->getTitle());
|
||||
$this->assertEquals('INVALID_AUTHORIZATION', $e->getType());
|
||||
$this->assertEquals(401, $e->getStatus());
|
||||
$this->assertEquals(['expectedTypes' => $expectedTypes], $e->getAdditionalData());
|
||||
}
|
||||
|
||||
public function provideExpectedTypes(): iterable
|
||||
{
|
||||
yield [['foo', 'bar']];
|
||||
yield [['something']];
|
||||
yield [[]];
|
||||
yield [['foo', 'bar', 'baz']];
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Rest\Middleware;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ProphecyInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Shlinkio\Shlink\Rest\Middleware\BodyParserMiddleware;
|
||||
@ -31,7 +32,10 @@ class BodyParserMiddlewareTest extends TestCase
|
||||
*/
|
||||
public function requestsFromOtherMethodsJustFallbackToNextMiddleware(string $method): void
|
||||
{
|
||||
$request = (new ServerRequest())->withMethod($method);
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getMethod()->willReturn($method);
|
||||
$request->getParsedBody()->willReturn([]);
|
||||
|
||||
$this->assertHandlingRequestJustFallsBackToNext($request);
|
||||
}
|
||||
|
||||
@ -45,18 +49,25 @@ class BodyParserMiddlewareTest extends TestCase
|
||||
/** @test */
|
||||
public function requestsWithNonEmptyBodyJustFallbackToNextMiddleware(): void
|
||||
{
|
||||
$request = (new ServerRequest())->withParsedBody(['foo' => 'bar'])->withMethod('POST');
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getMethod()->willReturn('POST');
|
||||
$request->getParsedBody()->willReturn(['foo' => 'bar']);
|
||||
|
||||
$this->assertHandlingRequestJustFallsBackToNext($request);
|
||||
}
|
||||
|
||||
private function assertHandlingRequestJustFallsBackToNext(ServerRequestInterface $request): void
|
||||
private function assertHandlingRequestJustFallsBackToNext(ProphecyInterface $requestMock): void
|
||||
{
|
||||
$getContentType = $requestMock->getHeaderLine('Content-type')->willReturn('');
|
||||
$request = $requestMock->reveal();
|
||||
|
||||
$nextHandler = $this->prophesize(RequestHandlerInterface::class);
|
||||
$handle = $nextHandler->handle($request)->willReturn(new Response());
|
||||
|
||||
$this->middleware->process($request, $nextHandler->reveal());
|
||||
|
||||
$handle->shouldHaveBeenCalledOnce();
|
||||
$getContentType->shouldNotHaveBeenCalled();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -8,12 +8,14 @@ use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Shlinkio\Shlink\Rest\Authentication;
|
||||
use Shlinkio\Shlink\Rest\Middleware\CrossDomainMiddleware;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Zend\Expressive\Router\Route;
|
||||
use Zend\Expressive\Router\RouteResult;
|
||||
|
||||
use function implode;
|
||||
use function Zend\Stratigility\middleware;
|
||||
|
||||
class CrossDomainMiddlewareTest extends TestCase
|
||||
@ -39,6 +41,7 @@ class CrossDomainMiddlewareTest extends TestCase
|
||||
$this->assertSame($originalResponse, $response);
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
|
||||
$this->assertArrayNotHasKey('Access-Control-Allow-Origin', $headers);
|
||||
$this->assertArrayNotHasKey('Access-Control-Expose-Headers', $headers);
|
||||
$this->assertArrayNotHasKey('Access-Control-Allow-Methods', $headers);
|
||||
@ -59,8 +62,12 @@ class CrossDomainMiddlewareTest extends TestCase
|
||||
$this->assertNotSame($originalResponse, $response);
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
$this->assertArrayHasKey('Access-Control-Allow-Origin', $headers);
|
||||
$this->assertArrayHasKey('Access-Control-Expose-Headers', $headers);
|
||||
|
||||
$this->assertEquals('local', $response->getHeaderLine('Access-Control-Allow-Origin'));
|
||||
$this->assertEquals(implode(', ', [
|
||||
Authentication\Plugin\ApiKeyHeaderPlugin::HEADER_NAME,
|
||||
Authentication\Plugin\AuthorizationHeaderPlugin::HEADER_NAME,
|
||||
]), $response->getHeaderLine('Access-Control-Expose-Headers'));
|
||||
$this->assertArrayNotHasKey('Access-Control-Allow-Methods', $headers);
|
||||
$this->assertArrayNotHasKey('Access-Control-Max-Age', $headers);
|
||||
$this->assertArrayNotHasKey('Access-Control-Allow-Headers', $headers);
|
||||
@ -70,18 +77,25 @@ class CrossDomainMiddlewareTest extends TestCase
|
||||
public function optionsRequestIncludesMoreHeaders(): void
|
||||
{
|
||||
$originalResponse = new Response();
|
||||
$request = (new ServerRequest())->withMethod('OPTIONS')->withHeader('Origin', 'local');
|
||||
$request = (new ServerRequest())
|
||||
->withMethod('OPTIONS')
|
||||
->withHeader('Origin', 'local')
|
||||
->withHeader('Access-Control-Request-Headers', 'foo, bar, baz');
|
||||
$this->handler->handle(Argument::any())->willReturn($originalResponse)->shouldBeCalledOnce();
|
||||
|
||||
$response = $this->middleware->process($request, $this->handler->reveal());
|
||||
$this->assertNotSame($originalResponse, $response);
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
$this->assertArrayHasKey('Access-Control-Allow-Origin', $headers);
|
||||
$this->assertArrayHasKey('Access-Control-Expose-Headers', $headers);
|
||||
|
||||
$this->assertEquals('local', $response->getHeaderLine('Access-Control-Allow-Origin'));
|
||||
$this->assertEquals(implode(', ', [
|
||||
Authentication\Plugin\ApiKeyHeaderPlugin::HEADER_NAME,
|
||||
Authentication\Plugin\AuthorizationHeaderPlugin::HEADER_NAME,
|
||||
]), $response->getHeaderLine('Access-Control-Expose-Headers'));
|
||||
$this->assertArrayHasKey('Access-Control-Allow-Methods', $headers);
|
||||
$this->assertArrayHasKey('Access-Control-Max-Age', $headers);
|
||||
$this->assertArrayHasKey('Access-Control-Allow-Headers', $headers);
|
||||
$this->assertEquals('1000', $response->getHeaderLine('Access-Control-Max-Age'));
|
||||
$this->assertEquals('foo, bar, baz', $response->getHeaderLine('Access-Control-Allow-Headers'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,65 +27,49 @@ class ApiKeyServiceTest extends TestCase
|
||||
$this->service = new ApiKeyService($this->em->reveal());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function keyIsProperlyCreated()
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider provideCreationDate
|
||||
*/
|
||||
public function apiKeyIsProperlyCreated(?Chronos $date): void
|
||||
{
|
||||
$this->em->flush()->shouldBeCalledOnce();
|
||||
$this->em->persist(Argument::type(ApiKey::class))->shouldBeCalledOnce();
|
||||
|
||||
$key = $this->service->create();
|
||||
$this->assertNull($key->getExpirationDate());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function keyIsProperlyCreatedWithExpirationDate()
|
||||
{
|
||||
$this->em->flush()->shouldBeCalledOnce();
|
||||
$this->em->persist(Argument::type(ApiKey::class))->shouldBeCalledOnce();
|
||||
|
||||
$date = Chronos::parse('2030-01-01');
|
||||
$key = $this->service->create($date);
|
||||
$this->assertSame($date, $key->getExpirationDate());
|
||||
|
||||
$this->assertEquals($date, $key->getExpirationDate());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkReturnsFalseWhenKeyIsInvalid()
|
||||
public function provideCreationDate(): iterable
|
||||
{
|
||||
yield 'no expiration date' => [null];
|
||||
yield 'expiration date' => [Chronos::parse('2030-01-01')];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider provideInvalidApiKeys
|
||||
*/
|
||||
public function checkReturnsFalseForInvalidApiKeys(?ApiKey $invalidKey): void
|
||||
{
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['key' => '12345'])->willReturn(null)
|
||||
$repo->findOneBy(['key' => '12345'])->willReturn($invalidKey)
|
||||
->shouldBeCalledOnce();
|
||||
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->assertFalse($this->service->check('12345'));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkReturnsFalseWhenKeyIsDisabled()
|
||||
public function provideInvalidApiKeys(): iterable
|
||||
{
|
||||
$key = new ApiKey();
|
||||
$key->disable();
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['key' => '12345'])->willReturn($key)
|
||||
->shouldBeCalledOnce();
|
||||
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->assertFalse($this->service->check('12345'));
|
||||
yield 'non-existent api key' => [null];
|
||||
yield 'disabled api key' => [(new ApiKey())->disable()];
|
||||
yield 'expired api key' => [new ApiKey(Chronos::now()->subDay())];
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkReturnsFalseWhenKeyIsExpired()
|
||||
{
|
||||
$key = new ApiKey(Chronos::now()->subDay());
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['key' => '12345'])->willReturn($key)
|
||||
->shouldBeCalledOnce();
|
||||
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->assertFalse($this->service->check('12345'));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkReturnsTrueWhenConditionsAreFavorable()
|
||||
public function checkReturnsTrueWhenConditionsAreFavorable(): void
|
||||
{
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['key' => '12345'])->willReturn(new ApiKey())
|
||||
@ -96,7 +80,7 @@ class ApiKeyServiceTest extends TestCase
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function disableThrowsExceptionWhenNoTokenIsFound()
|
||||
public function disableThrowsExceptionWhenNoApiKeyIsFound(): void
|
||||
{
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['key' => '12345'])->willReturn(null)
|
||||
@ -104,11 +88,12 @@ class ApiKeyServiceTest extends TestCase
|
||||
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
$this->service->disable('12345');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function disableReturnsDisabledKeyWhenFOund()
|
||||
public function disableReturnsDisabledApiKeyWhenFound(): void
|
||||
{
|
||||
$key = new ApiKey();
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
@ -125,24 +110,32 @@ class ApiKeyServiceTest extends TestCase
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function listFindsAllApiKeys()
|
||||
public function listFindsAllApiKeys(): void
|
||||
{
|
||||
$expectedApiKeys = [new ApiKey(), new ApiKey(), new ApiKey()];
|
||||
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findBy([])->willReturn([])
|
||||
$repo->findBy([])->willReturn($expectedApiKeys)
|
||||
->shouldBeCalledOnce();
|
||||
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->service->listKeys();
|
||||
$result = $this->service->listKeys();
|
||||
|
||||
$this->assertEquals($expectedApiKeys, $result);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function listEnabledFindsOnlyEnabledApiKeys()
|
||||
public function listEnabledFindsOnlyEnabledApiKeys(): void
|
||||
{
|
||||
$expectedApiKeys = [new ApiKey(), new ApiKey(), new ApiKey()];
|
||||
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findBy(['enabled' => true])->willReturn([])
|
||||
$repo->findBy(['enabled' => true])->willReturn($expectedApiKeys)
|
||||
->shouldBeCalledOnce();
|
||||
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->service->listKeys(true);
|
||||
$result = $this->service->listKeys(true);
|
||||
|
||||
$this->assertEquals($expectedApiKeys, $result);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user