Merge pull request #1586 from acelaya-forks/feature/phpunit-mocks

Feature/phpunit mocks
This commit is contained in:
Alejandro Celaya 2022-10-23 23:18:49 +02:00 committed by GitHub
commit aeafb244d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 645 additions and 918 deletions

View file

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
### Changed
* [#1563](https://github.com/shlinkio/shlink/issues/1563) Moved logic to reuse command options to option classes instead of base abstract command classes.
* [#1569](https://github.com/shlinkio/shlink/issues/1569) Migrated test doubles from phpspec/prophecy to PHPUnit mocks.
### Deprecated
* *Nothing*

View file

@ -65,7 +65,6 @@
"dms/phpunit-arraysubset-asserts": "^0.4.0",
"infection/infection": "^0.26.15",
"openswoole/ide-helper": "~4.11.5",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-doctrine": "^1.3",
"phpstan/phpstan-symfony": "^1.2",

View file

@ -4,45 +4,37 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Tag\Paginator\Adapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Tag\Model\TagsParams;
use Shlinkio\Shlink\Core\Tag\Paginator\Adapter\TagsInfoPaginatorAdapter;
use Shlinkio\Shlink\Core\Tag\Repository\TagRepositoryInterface;
class TagsInfoPaginatorAdapterTest extends TestCase
{
use ProphecyTrait;
private TagsInfoPaginatorAdapter $adapter;
private ObjectProphecy $repo;
private MockObject $repo;
protected function setUp(): void
{
$this->repo = $this->prophesize(TagRepositoryInterface::class);
$this->adapter = new TagsInfoPaginatorAdapter($this->repo->reveal(), TagsParams::fromRawData([]), null);
$this->repo = $this->createMock(TagRepositoryInterface::class);
$this->adapter = new TagsInfoPaginatorAdapter($this->repo, TagsParams::fromRawData([]), null);
}
/** @test */
public function getSliceIsDelegatedToRepository(): void
{
$findTags = $this->repo->findTagsWithInfo(Argument::cetera())->willReturn([]);
$this->repo->expects($this->once())->method('findTagsWithInfo')->willReturn([]);
$this->adapter->getSlice(1, 1);
$findTags->shouldHaveBeenCalledOnce();
}
/** @test */
public function getNbResultsIsDelegatedToRepository(): void
{
$match = $this->repo->matchSingleScalarResult(Argument::cetera())->willReturn(3);
$this->repo->expects($this->once())->method('matchSingleScalarResult')->willReturn(3);
$result = $this->adapter->getNbResults();
self::assertEquals(3, $result);
$match->shouldHaveBeenCalledOnce();
}
}

View file

@ -4,34 +4,27 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Tag\Paginator\Adapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Tag\Model\TagsParams;
use Shlinkio\Shlink\Core\Tag\Paginator\Adapter\TagsPaginatorAdapter;
use Shlinkio\Shlink\Core\Tag\Repository\TagRepositoryInterface;
class TagsPaginatorAdapterTest extends TestCase
{
use ProphecyTrait;
private TagsPaginatorAdapter $adapter;
private ObjectProphecy $repo;
private MockObject $repo;
protected function setUp(): void
{
$this->repo = $this->prophesize(TagRepositoryInterface::class);
$this->adapter = new TagsPaginatorAdapter($this->repo->reveal(), TagsParams::fromRawData([]), null);
$this->repo = $this->createMock(TagRepositoryInterface::class);
$this->adapter = new TagsPaginatorAdapter($this->repo, TagsParams::fromRawData([]), null);
}
/** @test */
public function getSliceDelegatesToRepository(): void
{
$match = $this->repo->match(Argument::cetera())->willReturn([]);
$this->repo->expects($this->once())->method('match')->willReturn([]);
$this->adapter->getSlice(1, 1);
$match->shouldHaveBeenCalledOnce();
}
}

View file

@ -5,10 +5,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Tag;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Exception\ForbiddenTagOperationException;
use Shlinkio\Shlink\Core\Exception\TagConflictException;
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
@ -27,19 +25,18 @@ use ShlinkioTest\Shlink\Core\Util\ApiKeyHelpersTrait;
class TagServiceTest extends TestCase
{
use ApiKeyHelpersTrait;
use ProphecyTrait;
private TagService $service;
private ObjectProphecy $em;
private ObjectProphecy $repo;
private MockObject $em;
private MockObject $repo;
protected function setUp(): void
{
$this->em = $this->prophesize(EntityManagerInterface::class);
$this->repo = $this->prophesize(TagRepository::class);
$this->em->getRepository(Tag::class)->willReturn($this->repo->reveal());
$this->em = $this->createMock(EntityManagerInterface::class);
$this->repo = $this->createMock(TagRepository::class);
$this->em->method('getRepository')->with(Tag::class)->willReturn($this->repo);
$this->service = new TagService($this->em->reveal());
$this->service = new TagService($this->em);
}
/** @test */
@ -47,14 +44,12 @@ class TagServiceTest extends TestCase
{
$expected = [new Tag('foo'), new Tag('bar')];
$match = $this->repo->match(Argument::cetera())->willReturn($expected);
$count = $this->repo->matchSingleScalarResult(Argument::cetera())->willReturn(2);
$this->repo->expects($this->once())->method('match')->willReturn($expected);
$this->repo->expects($this->once())->method('matchSingleScalarResult')->willReturn(2);
$result = $this->service->listTags(TagsParams::fromRawData([]));
self::assertEquals($expected, $result->getCurrentPageResults());
$match->shouldHaveBeenCalled();
$count->shouldHaveBeenCalled();
}
/**
@ -69,14 +64,14 @@ class TagServiceTest extends TestCase
): void {
$expected = [new TagInfo('foo', 1, 1), new TagInfo('bar', 3, 10)];
$find = $this->repo->findTagsWithInfo($expectedFiltering)->willReturn($expected);
$count = $this->repo->matchSingleScalarResult(Argument::cetera())->willReturn(2);
$this->repo->expects($this->once())->method('findTagsWithInfo')->with($expectedFiltering)->willReturn(
$expected,
);
$this->repo->expects($this->exactly($countCalls))->method('matchSingleScalarResult')->willReturn(2);
$result = $this->service->tagsInfo($params, $apiKey);
self::assertEquals($expected, $result->getCurrentPageResults());
$find->shouldHaveBeenCalledOnce();
$count->shouldHaveBeenCalledTimes($countCalls);
}
public function provideApiKeysAndSearchTerm(): iterable
@ -113,21 +108,17 @@ class TagServiceTest extends TestCase
*/
public function deleteTagsDelegatesOnRepository(?ApiKey $apiKey): void
{
$delete = $this->repo->deleteByName(['foo', 'bar'])->willReturn(4);
$this->repo->expects($this->once())->method('deleteByName')->with(['foo', 'bar'])->willReturn(4);
$this->service->deleteTags(['foo', 'bar'], $apiKey);
$delete->shouldHaveBeenCalled();
}
/** @test */
public function deleteTagsThrowsExceptionWhenProvidedApiKeyIsNotAdmin(): void
{
$delete = $this->repo->deleteByName(['foo', 'bar']);
$this->repo->expects($this->never())->method('deleteByName');
$this->expectException(ForbiddenTagOperationException::class);
$this->expectExceptionMessage('You are not allowed to delete tags');
$delete->shouldNotBeCalled();
$this->service->deleteTags(
['foo', 'bar'],
@ -141,9 +132,7 @@ class TagServiceTest extends TestCase
*/
public function renameInvalidTagThrowsException(?ApiKey $apiKey): void
{
$find = $this->repo->findOneBy(Argument::cetera())->willReturn(null);
$find->shouldBeCalled();
$this->repo->expects($this->once())->method('findOneBy')->willReturn(null);
$this->expectException(TagNotFoundException::class);
$this->service->renameTag(TagRenaming::fromNames('foo', 'bar'), $apiKey);
@ -157,17 +146,14 @@ class TagServiceTest extends TestCase
{
$expected = new Tag('foo');
$find = $this->repo->findOneBy(Argument::cetera())->willReturn($expected);
$countTags = $this->repo->count(Argument::cetera())->willReturn($count);
$flush = $this->em->flush()->willReturn(null);
$this->repo->expects($this->once())->method('findOneBy')->willReturn($expected);
$this->repo->expects($this->exactly($count > 0 ? 0 : 1))->method('count')->willReturn($count);
$this->em->expects($this->once())->method('flush');
$tag = $this->service->renameTag(TagRenaming::fromNames($oldName, $newName));
self::assertSame($expected, $tag);
self::assertEquals($newName, (string) $tag);
$find->shouldHaveBeenCalled();
$flush->shouldHaveBeenCalled();
$countTags->shouldHaveBeenCalledTimes($count > 0 ? 0 : 1);
}
public function provideValidRenames(): iterable
@ -182,13 +168,10 @@ class TagServiceTest extends TestCase
*/
public function renameTagToAnExistingNameThrowsException(?ApiKey $apiKey): void
{
$find = $this->repo->findOneBy(Argument::cetera())->willReturn(new Tag('foo'));
$countTags = $this->repo->count(Argument::cetera())->willReturn(1);
$flush = $this->em->flush(Argument::any())->willReturn(null);
$this->repo->expects($this->once())->method('findOneBy')->willReturn(new Tag('foo'));
$this->repo->expects($this->once())->method('count')->willReturn(1);
$this->em->expects($this->never())->method('flush');
$find->shouldBeCalled();
$countTags->shouldBeCalled();
$flush->shouldNotBeCalled();
$this->expectException(TagConflictException::class);
$this->service->renameTag(TagRenaming::fromNames('foo', 'bar'), $apiKey);
@ -197,11 +180,10 @@ class TagServiceTest extends TestCase
/** @test */
public function renamingTagThrowsExceptionWhenProvidedApiKeyIsNotAdmin(): void
{
$getRepo = $this->em->getRepository(Tag::class);
$this->em->expects($this->never())->method('getRepository')->with(Tag::class);
$this->expectExceptionMessage(ForbiddenTagOperationException::class);
$this->expectExceptionMessage('You are not allowed to rename tags');
$getRepo->shouldNotBeCalled();
$this->service->renameTag(
TagRenaming::fromNames('foo', 'bar'),

View file

@ -5,23 +5,20 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Util;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use RuntimeException;
use Shlinkio\Shlink\Core\Util\DoctrineBatchHelper;
class DoctrineBatchHelperTest extends TestCase
{
use ProphecyTrait;
private DoctrineBatchHelper $helper;
private ObjectProphecy $em;
private MockObject $em;
protected function setUp(): void
{
$this->em = $this->prophesize(EntityManagerInterface::class);
$this->helper = new DoctrineBatchHelper($this->em->reveal());
$this->em = $this->createMock(EntityManagerInterface::class);
$this->helper = new DoctrineBatchHelper($this->em);
}
/**
@ -33,17 +30,17 @@ class DoctrineBatchHelperTest extends TestCase
int $batchSize,
int $expectedCalls,
): void {
$this->em->expects($this->once())->method('beginTransaction');
$this->em->expects($this->once())->method('commit');
$this->em->expects($this->never())->method('rollback');
$this->em->expects($this->exactly($expectedCalls))->method('flush');
$this->em->expects($this->exactly($expectedCalls))->method('clear');
$wrappedIterable = $this->helper->wrapIterable($iterable, $batchSize);
foreach ($wrappedIterable as $item) {
// Iterable needs to be iterated for the logic to be invoked
}
$this->em->beginTransaction()->shouldHaveBeenCalledOnce();
$this->em->commit()->shouldHaveBeenCalledOnce();
$this->em->rollback()->shouldNotHaveBeenCalled();
$this->em->flush()->shouldHaveBeenCalledTimes($expectedCalls);
$this->em->clear()->shouldHaveBeenCalledTimes($expectedCalls);
}
public function provideIterables(): iterable
@ -56,15 +53,14 @@ class DoctrineBatchHelperTest extends TestCase
/** @test */
public function transactionIsRolledBackWhenAnErrorOccurs(): void
{
$flush = $this->em->flush()->willThrow(RuntimeException::class);
$this->em->expects($this->once())->method('flush')->willThrowException(new RuntimeException());
$this->em->expects($this->once())->method('beginTransaction');
$this->em->expects($this->never())->method('commit');
$this->em->expects($this->once())->method('rollback');
$wrappedIterable = $this->helper->wrapIterable([1, 2, 3], 1);
self::expectException(RuntimeException::class);
$flush->shouldBeCalledOnce();
$this->em->beginTransaction()->shouldBeCalledOnce();
$this->em->commit()->shouldNotBeCalled();
$this->em->rollback()->shouldBeCalledOnce();
foreach ($wrappedIterable as $item) {
// Iterable needs to be iterated for the logic to be invoked

View file

@ -7,35 +7,30 @@ namespace ShlinkioTest\Shlink\Core\Util;
use Fig\Http\Message\RequestMethodInterface;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\Stream;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\Util\UrlValidator;
class UrlValidatorTest extends TestCase
{
use ProphecyTrait;
private ObjectProphecy $httpClient;
private MockObject $httpClient;
protected function setUp(): void
{
$this->httpClient = $this->prophesize(ClientInterface::class);
$this->httpClient = $this->createMock(ClientInterface::class);
}
/** @test */
public function exceptionIsThrownWhenUrlIsInvalid(): void
{
$request = $this->httpClient->request(Argument::cetera())->willThrow(ClientException::class);
$request->shouldBeCalledOnce();
$this->httpClient->expects($this->once())->method('request')->willThrowException($this->clientException());
$this->expectException(InvalidUrlException::class);
$this->urlValidator()->validateUrl('http://foobar.com/12345/hello?foo=bar', true);
@ -46,10 +41,10 @@ class UrlValidatorTest extends TestCase
{
$expectedUrl = 'http://foobar.com';
$request = $this->httpClient->request(
$this->httpClient->expects($this->once())->method('request')->with(
RequestMethodInterface::METHOD_GET,
$expectedUrl,
Argument::that(function (array $options) {
$this->callback(function (array $options) {
Assert::assertArrayHasKey(RequestOptions::ALLOW_REDIRECTS, $options);
Assert::assertEquals(['max' => 15], $options[RequestOptions::ALLOW_REDIRECTS]);
Assert::assertArrayHasKey(RequestOptions::IDN_CONVERSION, $options);
@ -62,92 +57,91 @@ class UrlValidatorTest extends TestCase
)->willReturn(new Response());
$this->urlValidator()->validateUrl($expectedUrl, true);
$request->shouldHaveBeenCalledOnce();
}
/** @test */
public function noCheckIsPerformedWhenUrlValidationIsDisabled(): void
{
$request = $this->httpClient->request(Argument::cetera())->willReturn(new Response());
$this->httpClient->expects($this->never())->method('request');
$this->urlValidator()->validateUrl('', false);
$request->shouldNotHaveBeenCalled();
}
/** @test */
public function validateUrlWithTitleReturnsNullWhenRequestFailsAndValidationIsDisabled(): void
{
$request = $this->httpClient->request(Argument::cetera())->willThrow(ClientException::class);
$this->httpClient->expects($this->once())->method('request')->willThrowException($this->clientException());
$result = $this->urlValidator(true)->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', false);
self::assertNull($result);
$request->shouldHaveBeenCalledOnce();
}
/** @test */
public function validateUrlWithTitleReturnsNullWhenAutoResolutionIsDisabled(): void
{
$request = $this->httpClient->request(Argument::cetera())->willReturn($this->respWithTitle());
$this->httpClient->expects($this->never())->method('request');
$result = $this->urlValidator()->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', false);
self::assertNull($result);
$request->shouldNotHaveBeenCalled();
}
/** @test */
public function validateUrlWithTitleReturnsNullWhenAutoResolutionIsDisabledAndValidationIsEnabled(): void
{
$request = $this->httpClient->request(RequestMethodInterface::METHOD_HEAD, Argument::cetera())->willReturn(
$this->respWithTitle(),
);
$this->httpClient->expects($this->once())->method('request')->with(
RequestMethodInterface::METHOD_HEAD,
$this->anything(),
$this->anything(),
)->willReturn($this->respWithTitle());
$result = $this->urlValidator()->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', true);
self::assertNull($result);
$request->shouldHaveBeenCalledOnce();
}
/** @test */
public function validateUrlWithTitleResolvesTitleWhenAutoResolutionIsEnabled(): void
{
$request = $this->httpClient->request(RequestMethodInterface::METHOD_GET, Argument::cetera())->willReturn(
$this->respWithTitle(),
);
$this->httpClient->expects($this->once())->method('request')->with(
RequestMethodInterface::METHOD_GET,
$this->anything(),
$this->anything(),
)->willReturn($this->respWithTitle());
$result = $this->urlValidator(true)->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', true);
self::assertEquals('Resolved "title"', $result);
$request->shouldHaveBeenCalledOnce();
}
/** @test */
public function validateUrlWithTitleReturnsNullWhenAutoResolutionIsEnabledAndReturnedContentTypeIsInvalid(): void
{
$request = $this->httpClient->request(RequestMethodInterface::METHOD_GET, Argument::cetera())->willReturn(
new Response('php://memory', 200, ['Content-Type' => 'application/octet-stream']),
);
$this->httpClient->expects($this->once())->method('request')->with(
RequestMethodInterface::METHOD_GET,
$this->anything(),
$this->anything(),
)->willReturn(new Response('php://memory', 200, ['Content-Type' => 'application/octet-stream']));
$result = $this->urlValidator(true)->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', true);
self::assertNull($result);
$request->shouldHaveBeenCalledOnce();
}
/** @test */
public function validateUrlWithTitleReturnsNullWhenAutoResolutionIsEnabledAndBodyDoesNotContainTitle(): void
{
$request = $this->httpClient->request(RequestMethodInterface::METHOD_GET, Argument::cetera())->willReturn(
$this->httpClient->expects($this->once())->method('request')->with(
RequestMethodInterface::METHOD_GET,
$this->anything(),
$this->anything(),
)->willReturn(
new Response($this->createStreamWithContent('<body>No title</body>'), 200, ['Content-Type' => 'text/html']),
);
$result = $this->urlValidator(true)->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', true);
self::assertNull($result);
$request->shouldHaveBeenCalledOnce();
}
private function respWithTitle(): Response
@ -165,11 +159,17 @@ class UrlValidatorTest extends TestCase
return $body;
}
public function urlValidator(bool $autoResolveTitles = false): UrlValidator
private function clientException(): ClientException
{
return new UrlValidator(
$this->httpClient->reveal(),
new UrlShortenerOptions(autoResolveTitles: $autoResolveTitles),
return new ClientException(
'',
new Request(RequestMethodInterface::METHOD_GET, ''),
new Response(),
);
}
public function urlValidator(bool $autoResolveTitles = false): UrlValidator
{
return new UrlValidator($this->httpClient, new UrlShortenerOptions(autoResolveTitles: $autoResolveTitles));
}
}

View file

@ -7,11 +7,8 @@ namespace ShlinkioTest\Shlink\Core\Visit\Geolocation;
use Doctrine\ORM\EntityManager;
use Exception;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
@ -32,19 +29,17 @@ use function sprintf;
class VisitLocatorTest extends TestCase
{
use ProphecyTrait;
private VisitLocator $visitService;
private ObjectProphecy $em;
private ObjectProphecy $repo;
private MockObject $em;
private MockObject $repo;
protected function setUp(): void
{
$this->em = $this->prophesize(EntityManager::class);
$this->repo = $this->prophesize(VisitRepositoryInterface::class);
$this->em->getRepository(Visit::class)->willReturn($this->repo->reveal());
$this->em = $this->createMock(EntityManager::class);
$this->repo = $this->createMock(VisitRepositoryInterface::class);
$this->em->method('getRepository')->with(Visit::class)->willReturn($this->repo);
$this->visitService = new VisitLocator($this->em->reveal());
$this->visitService = new VisitLocator($this->em);
}
/**
@ -61,14 +56,13 @@ class VisitLocatorTest extends TestCase
Visit::forValidShortUrl(ShortUrl::withLongUrl(sprintf('short_code_%s', $i)), Visitor::emptyInstance()),
);
$findVisits = $this->mockRepoMethod($expectedRepoMethodName)->willReturn($unlocatedVisits);
$this->repo->expects($this->once())->method($expectedRepoMethodName)->willReturn($unlocatedVisits);
$persist = $this->em->persist(Argument::type(Visit::class))->will(function (): void {
});
$flush = $this->em->flush()->will(function (): void {
});
$clear = $this->em->clear()->will(function (): void {
});
$this->em->expects($this->exactly(count($unlocatedVisits)))->method('persist')->with(
$this->isInstanceOf(Visit::class),
);
$this->em->expects($this->exactly((int) floor(count($unlocatedVisits) / 200) + 1))->method('flush');
$this->em->expects($this->exactly((int) floor(count($unlocatedVisits) / 200) + 1))->method('clear');
$this->visitService->{$serviceMethodName}(new class implements VisitGeolocationHelperInterface {
public function geolocateVisit(Visit $visit): Location
@ -84,11 +78,6 @@ class VisitLocatorTest extends TestCase
Assert::assertInstanceOf(Visit::class, array_shift($args));
}
});
$findVisits->shouldHaveBeenCalledOnce();
$persist->shouldHaveBeenCalledTimes(count($unlocatedVisits));
$flush->shouldHaveBeenCalledTimes(floor(count($unlocatedVisits) / 200) + 1);
$clear->shouldHaveBeenCalledTimes(floor(count($unlocatedVisits) / 200) + 1);
}
public function provideMethodNames(): iterable
@ -111,14 +100,13 @@ class VisitLocatorTest extends TestCase
Visit::forValidShortUrl(ShortUrl::withLongUrl('foo'), Visitor::emptyInstance()),
];
$findVisits = $this->mockRepoMethod($expectedRepoMethodName)->willReturn($unlocatedVisits);
$this->repo->expects($this->once())->method($expectedRepoMethodName)->willReturn($unlocatedVisits);
$persist = $this->em->persist(Argument::type(Visit::class))->will(function (): void {
});
$flush = $this->em->flush()->will(function (): void {
});
$clear = $this->em->clear()->will(function (): void {
});
$this->em->expects($this->exactly($isNonLocatableAddress ? 1 : 0))->method('persist')->with(
$this->isInstanceOf(Visit::class),
);
$this->em->expects($this->once())->method('flush');
$this->em->expects($this->once())->method('clear');
$this->visitService->{$serviceMethodName}(
new class ($isNonLocatableAddress) implements VisitGeolocationHelperInterface {
@ -138,11 +126,6 @@ class VisitLocatorTest extends TestCase
}
},
);
$findVisits->shouldHaveBeenCalledOnce();
$persist->shouldHaveBeenCalledTimes($isNonLocatableAddress ? 1 : 0);
$flush->shouldHaveBeenCalledOnce();
$clear->shouldHaveBeenCalledOnce();
}
public function provideIsNonLocatableAddress(): iterable
@ -162,9 +145,4 @@ class VisitLocatorTest extends TestCase
yield 'locateAllVisits - locatable address' => ['locateAllVisits', 'findAllVisits', false];
yield 'locateAllVisits - non-locatable address' => ['locateAllVisits', 'findAllVisits', true];
}
private function mockRepoMethod(string $methodName): MethodProphecy
{
return (new MethodProphecy($this->repo, $methodName, new Argument\ArgumentsWildcard([])));
}
}

View file

@ -4,10 +4,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Visit\Geolocation;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Util\IpAddress;
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
@ -18,15 +16,13 @@ use Shlinkio\Shlink\IpGeolocation\Resolver\IpLocationResolverInterface;
class VisitToLocationHelperTest extends TestCase
{
use ProphecyTrait;
private VisitToLocationHelper $helper;
private ObjectProphecy $ipLocationResolver;
private MockObject $ipLocationResolver;
protected function setUp(): void
{
$this->ipLocationResolver = $this->prophesize(IpLocationResolverInterface::class);
$this->helper = new VisitToLocationHelper($this->ipLocationResolver->reveal());
$this->ipLocationResolver = $this->createMock(IpLocationResolverInterface::class);
$this->helper = new VisitToLocationHelper($this->ipLocationResolver);
}
/**
@ -38,7 +34,7 @@ class VisitToLocationHelperTest extends TestCase
IpCannotBeLocatedException $expectedException,
): void {
$this->expectExceptionObject($expectedException);
$this->ipLocationResolver->resolveIpLocation(Argument::cetera())->shouldNotBeCalled();
$this->ipLocationResolver->expects($this->never())->method('resolveIpLocation');
$this->helper->resolveVisitLocation($visit);
}
@ -58,8 +54,7 @@ class VisitToLocationHelperTest extends TestCase
$e = new WrongIpException('');
$this->expectExceptionObject(IpCannotBeLocatedException::forError($e));
$this->ipLocationResolver->resolveIpLocation(Argument::cetera())->willThrow($e)
->shouldBeCalledOnce();
$this->ipLocationResolver->expects($this->once())->method('resolveIpLocation')->willThrowException($e);
$this->helper->resolveVisitLocation(Visit::forBasePath(new Visitor('foo', 'bar', '1.2.3.4', '')));
}

View file

@ -4,9 +4,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Visit\Paginator\Adapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\Visitor;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
@ -18,34 +17,31 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class NonOrphanVisitsPaginatorAdapterTest extends TestCase
{
use ProphecyTrait;
private NonOrphanVisitsPaginatorAdapter $adapter;
private ObjectProphecy $repo;
private MockObject $repo;
private VisitsParams $params;
private ApiKey $apiKey;
protected function setUp(): void
{
$this->repo = $this->prophesize(VisitRepositoryInterface::class);
$this->repo = $this->createMock(VisitRepositoryInterface::class);
$this->params = VisitsParams::fromRawData([]);
$this->apiKey = ApiKey::create();
$this->adapter = new NonOrphanVisitsPaginatorAdapter($this->repo->reveal(), $this->params, $this->apiKey);
$this->adapter = new NonOrphanVisitsPaginatorAdapter($this->repo, $this->params, $this->apiKey);
}
/** @test */
public function countDelegatesToRepository(): void
{
$expectedCount = 5;
$repoCount = $this->repo->countNonOrphanVisits(
$this->repo->expects($this->once())->method('countNonOrphanVisits')->with(
new VisitsCountFiltering($this->params->dateRange, $this->params->excludeBots, $this->apiKey),
)->willReturn($expectedCount);
$result = $this->adapter->getNbResults();
self::assertEquals($expectedCount, $result);
$repoCount->shouldHaveBeenCalledOnce();
}
/**
@ -56,7 +52,7 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase
{
$visitor = Visitor::emptyInstance();
$list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)];
$repoFind = $this->repo->findNonOrphanVisits(new VisitsListFiltering(
$this->repo->expects($this->once())->method('findNonOrphanVisits')->with(new VisitsListFiltering(
$this->params->dateRange,
$this->params->excludeBots,
$this->apiKey,
@ -67,7 +63,6 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase
$result = $this->adapter->getSlice($offset, $limit);
self::assertEquals($list, $result);
$repoFind->shouldHaveBeenCalledOnce();
}
public function provideLimitAndOffset(): iterable

View file

@ -4,9 +4,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Visit\Paginator\Adapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\Visitor;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
@ -17,31 +16,28 @@ use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
class OrphanVisitsPaginatorAdapterTest extends TestCase
{
use ProphecyTrait;
private OrphanVisitsPaginatorAdapter $adapter;
private ObjectProphecy $repo;
private MockObject $repo;
private VisitsParams $params;
protected function setUp(): void
{
$this->repo = $this->prophesize(VisitRepositoryInterface::class);
$this->repo = $this->createMock(VisitRepositoryInterface::class);
$this->params = VisitsParams::fromRawData([]);
$this->adapter = new OrphanVisitsPaginatorAdapter($this->repo->reveal(), $this->params);
$this->adapter = new OrphanVisitsPaginatorAdapter($this->repo, $this->params);
}
/** @test */
public function countDelegatesToRepository(): void
{
$expectedCount = 5;
$repoCount = $this->repo->countOrphanVisits(
$this->repo->expects($this->once())->method('countOrphanVisits')->with(
new VisitsCountFiltering($this->params->dateRange),
)->willReturn($expectedCount);
$result = $this->adapter->getNbResults();
self::assertEquals($expectedCount, $result);
$repoCount->shouldHaveBeenCalledOnce();
}
/**
@ -52,14 +48,13 @@ class OrphanVisitsPaginatorAdapterTest extends TestCase
{
$visitor = Visitor::emptyInstance();
$list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)];
$repoFind = $this->repo->findOrphanVisits(
$this->repo->expects($this->once())->method('findOrphanVisits')->with(
new VisitsListFiltering($this->params->dateRange, $this->params->excludeBots, null, $limit, $offset),
)->willReturn($list);
$result = $this->adapter->getSlice($offset, $limit);
self::assertEquals($list, $result);
$repoFind->shouldHaveBeenCalledOnce();
}
public function provideLimitAndOffset(): iterable

View file

@ -4,9 +4,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Visit\Paginator\Adapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
@ -18,13 +17,11 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ShortUrlVisitsPaginatorAdapterTest extends TestCase
{
use ProphecyTrait;
private ObjectProphecy $repo;
private MockObject $repo;
protected function setUp(): void
{
$this->repo = $this->prophesize(VisitRepositoryInterface::class);
$this->repo = $this->createMock(VisitRepositoryInterface::class);
}
/** @test */
@ -34,7 +31,7 @@ class ShortUrlVisitsPaginatorAdapterTest extends TestCase
$limit = 1;
$offset = 5;
$adapter = $this->createAdapter(null);
$findVisits = $this->repo->findVisitsByShortCode(
$this->repo->expects($this->exactly($count))->method('findVisitsByShortCode')->with(
ShortUrlIdentifier::fromShortCodeAndDomain(''),
new VisitsListFiltering(DateRange::allTime(), false, null, $limit, $offset),
)->willReturn([]);
@ -42,8 +39,6 @@ class ShortUrlVisitsPaginatorAdapterTest extends TestCase
for ($i = 0; $i < $count; $i++) {
$adapter->getSlice($offset, $limit);
}
$findVisits->shouldHaveBeenCalledTimes($count);
}
/** @test */
@ -52,7 +47,7 @@ class ShortUrlVisitsPaginatorAdapterTest extends TestCase
$count = 3;
$apiKey = ApiKey::create();
$adapter = $this->createAdapter($apiKey);
$countVisits = $this->repo->countVisitsByShortCode(
$this->repo->expects($this->once())->method('countVisitsByShortCode')->with(
ShortUrlIdentifier::fromShortCodeAndDomain(''),
new VisitsCountFiltering(DateRange::allTime(), false, $apiKey),
)->willReturn(3);
@ -60,14 +55,12 @@ class ShortUrlVisitsPaginatorAdapterTest extends TestCase
for ($i = 0; $i < $count; $i++) {
$adapter->getNbResults();
}
$countVisits->shouldHaveBeenCalledOnce();
}
private function createAdapter(?ApiKey $apiKey): ShortUrlVisitsPaginatorAdapter
{
return new ShortUrlVisitsPaginatorAdapter(
$this->repo->reveal(),
$this->repo,
ShortUrlIdentifier::fromShortCodeAndDomain(''),
VisitsParams::fromRawData([]),
$apiKey,

View file

@ -4,9 +4,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Visit\Paginator\Adapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\TagVisitsPaginatorAdapter;
@ -17,13 +16,11 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class VisitsForTagPaginatorAdapterTest extends TestCase
{
use ProphecyTrait;
private ObjectProphecy $repo;
private MockObject $repo;
protected function setUp(): void
{
$this->repo = $this->prophesize(VisitRepositoryInterface::class);
$this->repo = $this->createMock(VisitRepositoryInterface::class);
}
/** @test */
@ -33,7 +30,7 @@ class VisitsForTagPaginatorAdapterTest extends TestCase
$limit = 1;
$offset = 5;
$adapter = $this->createAdapter(null);
$findVisits = $this->repo->findVisitsByTag(
$this->repo->expects($this->exactly($count))->method('findVisitsByTag')->with(
'foo',
new VisitsListFiltering(DateRange::allTime(), false, null, $limit, $offset),
)->willReturn([]);
@ -41,8 +38,6 @@ class VisitsForTagPaginatorAdapterTest extends TestCase
for ($i = 0; $i < $count; $i++) {
$adapter->getSlice($offset, $limit);
}
$findVisits->shouldHaveBeenCalledTimes($count);
}
/** @test */
@ -51,7 +46,7 @@ class VisitsForTagPaginatorAdapterTest extends TestCase
$count = 3;
$apiKey = ApiKey::create();
$adapter = $this->createAdapter($apiKey);
$countVisits = $this->repo->countVisitsByTag(
$this->repo->expects($this->once())->method('countVisitsByTag')->with(
'foo',
new VisitsCountFiltering(DateRange::allTime(), false, $apiKey),
)->willReturn(3);
@ -59,17 +54,10 @@ class VisitsForTagPaginatorAdapterTest extends TestCase
for ($i = 0; $i < $count; $i++) {
$adapter->getNbResults();
}
$countVisits->shouldHaveBeenCalledOnce();
}
private function createAdapter(?ApiKey $apiKey): TagVisitsPaginatorAdapter
{
return new TagVisitsPaginatorAdapter(
$this->repo->reveal(),
'foo',
VisitsParams::fromRawData([]),
$apiKey,
);
return new TagVisitsPaginatorAdapter($this->repo, 'foo', VisitsParams::fromRawData([]), $apiKey);
}
}

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Core\Visit;
use Fig\Http\Message\RequestMethodInterface;
use Laminas\Diactoros\ServerRequestFactory;
use Mezzio\Router\Middleware\ImplicitHeadMiddleware;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Middleware\IpAddressMiddlewareFactory;
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
@ -22,31 +20,28 @@ use Shlinkio\Shlink\Core\Visit\VisitsTrackerInterface;
class RequestTrackerTest extends TestCase
{
use ProphecyTrait;
private const LONG_URL = 'https://domain.com/foo/bar?some=thing';
private RequestTracker $requestTracker;
private ObjectProphecy $visitsTracker;
private ObjectProphecy $notFoundType;
private MockObject $notFoundType;
private MockObject $visitsTracker;
private ServerRequestInterface $request;
protected function setUp(): void
{
$this->notFoundType = $this->prophesize(NotFoundType::class);
$this->visitsTracker = $this->prophesize(VisitsTrackerInterface::class);
$this->visitsTracker = $this->createMock(VisitsTrackerInterface::class);
$this->requestTracker = new RequestTracker(
$this->visitsTracker->reveal(),
$this->visitsTracker,
new TrackingOptions(
disableTrackParam: 'foobar',
disableTrackingFrom: ['80.90.100.110', '192.168.10.0/24', '1.2.*.*'],
),
);
$this->notFoundType = $this->createMock(NotFoundType::class);
$this->request = ServerRequestFactory::fromGlobals()->withAttribute(
NotFoundType::class,
$this->notFoundType->reveal(),
$this->notFoundType,
);
}
@ -56,11 +51,10 @@ class RequestTrackerTest extends TestCase
*/
public function trackingIsDisabledWhenRequestDoesNotMeetConditions(ServerRequestInterface $request): void
{
$this->visitsTracker->expects($this->never())->method('track');
$shortUrl = ShortUrl::withLongUrl(self::LONG_URL);
$this->requestTracker->trackIfApplicable($shortUrl, $request);
$this->visitsTracker->track(Argument::cetera())->shouldNotHaveBeenCalled();
}
public function provideNonTrackingRequests(): iterable
@ -91,61 +85,57 @@ class RequestTrackerTest extends TestCase
public function trackingHappensOverShortUrlsWhenRequestMeetsConditions(): void
{
$shortUrl = ShortUrl::withLongUrl(self::LONG_URL);
$this->visitsTracker->expects($this->once())->method('track')->with(
$shortUrl,
$this->isInstanceOf(Visitor::class),
);
$this->requestTracker->trackIfApplicable($shortUrl, $this->request);
$this->visitsTracker->track($shortUrl, Argument::type(Visitor::class))->shouldHaveBeenCalledOnce();
}
/** @test */
public function baseUrlErrorIsTracked(): void
{
$isBaseUrl = $this->notFoundType->isBaseUrl()->willReturn(true);
$isRegularNotFound = $this->notFoundType->isRegularNotFound()->willReturn(false);
$isInvalidShortUrl = $this->notFoundType->isInvalidShortUrl()->willReturn(false);
$this->notFoundType->expects($this->once())->method('isBaseUrl')->willReturn(true);
$this->notFoundType->expects($this->never())->method('isRegularNotFound');
$this->notFoundType->expects($this->never())->method('isInvalidShortUrl');
$this->visitsTracker->expects($this->once())->method('trackBaseUrlVisit')->with(
$this->isInstanceOf(Visitor::class),
);
$this->visitsTracker->expects($this->never())->method('trackRegularNotFoundVisit');
$this->visitsTracker->expects($this->never())->method('trackInvalidShortUrlVisit');
$this->requestTracker->trackNotFoundIfApplicable($this->request);
$isBaseUrl->shouldHaveBeenCalledOnce();
$isRegularNotFound->shouldNotHaveBeenCalled();
$isInvalidShortUrl->shouldNotHaveBeenCalled();
$this->visitsTracker->trackBaseUrlVisit(Argument::type(Visitor::class))->shouldHaveBeenCalledOnce();
$this->visitsTracker->trackRegularNotFoundVisit(Argument::type(Visitor::class))->shouldNotHaveBeenCalled();
$this->visitsTracker->trackInvalidShortUrlVisit(Argument::type(Visitor::class))->shouldNotHaveBeenCalled();
}
/** @test */
public function regularNotFoundErrorIsTracked(): void
{
$isBaseUrl = $this->notFoundType->isBaseUrl()->willReturn(false);
$isRegularNotFound = $this->notFoundType->isRegularNotFound()->willReturn(true);
$isInvalidShortUrl = $this->notFoundType->isInvalidShortUrl()->willReturn(false);
$this->notFoundType->expects($this->once())->method('isBaseUrl')->willReturn(false);
$this->notFoundType->expects($this->once())->method('isRegularNotFound')->willReturn(true);
$this->notFoundType->expects($this->never())->method('isInvalidShortUrl');
$this->visitsTracker->expects($this->never())->method('trackBaseUrlVisit');
$this->visitsTracker->expects($this->once())->method('trackRegularNotFoundVisit')->with(
$this->isInstanceOf(Visitor::class),
);
$this->visitsTracker->expects($this->never())->method('trackInvalidShortUrlVisit');
$this->requestTracker->trackNotFoundIfApplicable($this->request);
$isBaseUrl->shouldHaveBeenCalledOnce();
$isRegularNotFound->shouldHaveBeenCalledOnce();
$isInvalidShortUrl->shouldNotHaveBeenCalled();
$this->visitsTracker->trackBaseUrlVisit(Argument::type(Visitor::class))->shouldNotHaveBeenCalled();
$this->visitsTracker->trackRegularNotFoundVisit(Argument::type(Visitor::class))->shouldHaveBeenCalledOnce();
$this->visitsTracker->trackInvalidShortUrlVisit(Argument::type(Visitor::class))->shouldNotHaveBeenCalled();
}
/** @test */
public function invalidShortUrlErrorIsTracked(): void
{
$isBaseUrl = $this->notFoundType->isBaseUrl()->willReturn(false);
$isRegularNotFound = $this->notFoundType->isRegularNotFound()->willReturn(false);
$isInvalidShortUrl = $this->notFoundType->isInvalidShortUrl()->willReturn(true);
$this->notFoundType->expects($this->once())->method('isBaseUrl')->willReturn(false);
$this->notFoundType->expects($this->once())->method('isRegularNotFound')->willReturn(false);
$this->notFoundType->expects($this->once())->method('isInvalidShortUrl')->willReturn(true);
$this->visitsTracker->expects($this->never())->method('trackBaseUrlVisit');
$this->visitsTracker->expects($this->never())->method('trackRegularNotFoundVisit');
$this->visitsTracker->expects($this->once())->method('trackInvalidShortUrlVisit')->with(
$this->isInstanceOf(Visitor::class),
);
$this->requestTracker->trackNotFoundIfApplicable($this->request);
$isBaseUrl->shouldHaveBeenCalledOnce();
$isRegularNotFound->shouldHaveBeenCalledOnce();
$isInvalidShortUrl->shouldHaveBeenCalledOnce();
$this->visitsTracker->trackBaseUrlVisit(Argument::type(Visitor::class))->shouldNotHaveBeenCalled();
$this->visitsTracker->trackRegularNotFoundVisit(Argument::type(Visitor::class))->shouldNotHaveBeenCalled();
$this->visitsTracker->trackInvalidShortUrlVisit(Argument::type(Visitor::class))->shouldHaveBeenCalledOnce();
}
/**
@ -154,10 +144,10 @@ class RequestTrackerTest extends TestCase
*/
public function notFoundIsNotTrackedIfRequestDoesNotMeetConditions(ServerRequestInterface $request): void
{
$this->requestTracker->trackNotFoundIfApplicable($request);
$this->visitsTracker->expects($this->never())->method('trackBaseUrlVisit');
$this->visitsTracker->expects($this->never())->method('trackRegularNotFoundVisit');
$this->visitsTracker->expects($this->never())->method('trackInvalidShortUrlVisit');
$this->visitsTracker->trackBaseUrlVisit(Argument::cetera())->shouldNotHaveBeenCalled();
$this->visitsTracker->trackRegularNotFoundVisit(Argument::cetera())->shouldNotHaveBeenCalled();
$this->visitsTracker->trackInvalidShortUrlVisit(Argument::cetera())->shouldNotHaveBeenCalled();
$this->requestTracker->trackNotFoundIfApplicable($request);
}
}

View file

@ -6,10 +6,8 @@ namespace ShlinkioTest\Shlink\Core\Visit;
use Doctrine\ORM\EntityManagerInterface;
use Laminas\Stdlib\ArrayUtils;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepository;
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
@ -38,15 +36,14 @@ use function range;
class VisitsStatsHelperTest extends TestCase
{
use ApiKeyHelpersTrait;
use ProphecyTrait;
private VisitsStatsHelper $helper;
private ObjectProphecy $em;
private MockObject $em;
protected function setUp(): void
{
$this->em = $this->prophesize(EntityManagerInterface::class);
$this->helper = new VisitsStatsHelper($this->em->reveal());
$this->em = $this->createMock(EntityManagerInterface::class);
$this->helper = new VisitsStatsHelper($this->em);
}
/**
@ -55,19 +52,18 @@ class VisitsStatsHelperTest extends TestCase
*/
public function returnsExpectedVisitsStats(int $expectedCount): void
{
$repo = $this->prophesize(VisitRepository::class);
$count = $repo->countNonOrphanVisits(new VisitsCountFiltering())->willReturn($expectedCount * 3);
$countOrphan = $repo->countOrphanVisits(Argument::type(VisitsCountFiltering::class))->willReturn(
$expectedCount,
$repo = $this->createMock(VisitRepository::class);
$repo->expects($this->once())->method('countNonOrphanVisits')->with(new VisitsCountFiltering())->willReturn(
$expectedCount * 3,
);
$getRepo = $this->em->getRepository(Visit::class)->willReturn($repo->reveal());
$repo->expects($this->once())->method('countOrphanVisits')->with(
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn($expectedCount);
$this->em->expects($this->once())->method('getRepository')->with(Visit::class)->willReturn($repo);
$stats = $this->helper->getVisitsStats();
self::assertEquals(new VisitsStats($expectedCount * 3, $expectedCount), $stats);
$count->shouldHaveBeenCalledOnce();
$countOrphan->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledOnce();
}
public function provideCounts(): iterable
@ -85,22 +81,28 @@ class VisitsStatsHelperTest extends TestCase
$identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode);
$spec = $apiKey?->spec();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$count = $repo->shortCodeIsInUse($identifier, $spec)->willReturn(
true,
);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce();
$repo = $this->createMock(ShortUrlRepositoryInterface::class);
$repo->expects($this->once())->method('shortCodeIsInUse')->with($identifier, $spec)->willReturn(true);
$list = map(range(0, 1), fn () => Visit::forValidShortUrl(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByShortCode($identifier, Argument::type(VisitsListFiltering::class))->willReturn($list);
$repo2->countVisitsByShortCode($identifier, Argument::type(VisitsCountFiltering::class))->willReturn(1);
$this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledOnce();
$repo2 = $this->createMock(VisitRepository::class);
$repo2->method('findVisitsByShortCode')->with(
$identifier,
$this->isInstanceOf(VisitsListFiltering::class),
)->willReturn($list);
$repo2->method('countVisitsByShortCode')->with(
$identifier,
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn(1);
$this->em->expects($this->exactly(2))->method('getRepository')->willReturnMap([
[ShortUrl::class, $repo],
[Visit::class, $repo2],
]);
$paginator = $this->helper->visitsForShortUrl($identifier, new VisitsParams(), $apiKey);
self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults()));
$count->shouldHaveBeenCalledOnce();
}
/** @test */
@ -109,14 +111,11 @@ class VisitsStatsHelperTest extends TestCase
$shortCode = '123ABC';
$identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode);
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$count = $repo->shortCodeIsInUse($identifier, null)->willReturn(
false,
);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce();
$repo = $this->createMock(ShortUrlRepositoryInterface::class);
$repo->expects($this->once())->method('shortCodeIsInUse')->with($identifier, null)->willReturn(false);
$this->em->expects($this->once())->method('getRepository')->with(ShortUrl::class)->willReturn($repo);
$this->expectException(ShortUrlNotFoundException::class);
$count->shouldBeCalledOnce();
$this->helper->visitsForShortUrl($identifier, new VisitsParams());
}
@ -126,13 +125,11 @@ class VisitsStatsHelperTest extends TestCase
{
$tag = 'foo';
$apiKey = ApiKey::create();
$repo = $this->prophesize(TagRepository::class);
$tagExists = $repo->tagExists($tag, $apiKey)->willReturn(false);
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
$repo = $this->createMock(TagRepository::class);
$repo->expects($this->once())->method('tagExists')->with($tag, $apiKey)->willReturn(false);
$this->em->expects($this->once())->method('getRepository')->with(Tag::class)->willReturn($repo);
$this->expectException(TagNotFoundException::class);
$tagExists->shouldBeCalledOnce();
$getRepo->shouldBeCalledOnce();
$this->helper->visitsForTag($tag, new VisitsParams(), $apiKey);
}
@ -144,21 +141,24 @@ class VisitsStatsHelperTest extends TestCase
public function visitsForTagAreReturnedAsExpected(?ApiKey $apiKey): void
{
$tag = 'foo';
$repo = $this->prophesize(TagRepository::class);
$tagExists = $repo->tagExists($tag, $apiKey)->willReturn(true);
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
$repo = $this->createMock(TagRepository::class);
$repo->expects($this->once())->method('tagExists')->with($tag, $apiKey)->willReturn(true);
$list = map(range(0, 1), fn () => Visit::forValidShortUrl(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByTag($tag, Argument::type(VisitsListFiltering::class))->willReturn($list);
$repo2->countVisitsByTag($tag, Argument::type(VisitsCountFiltering::class))->willReturn(1);
$this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledOnce();
$repo2 = $this->createMock(VisitRepository::class);
$repo2->method('findVisitsByTag')->with($tag, $this->isInstanceOf(VisitsListFiltering::class))->willReturn(
$list,
);
$repo2->method('countVisitsByTag')->with($tag, $this->isInstanceOf(VisitsCountFiltering::class))->willReturn(1);
$this->em->expects($this->exactly(2))->method('getRepository')->willReturnMap([
[Tag::class, $repo],
[Visit::class, $repo2],
]);
$paginator = $this->helper->visitsForTag($tag, new VisitsParams(), $apiKey);
self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults()));
$tagExists->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledOnce();
}
/** @test */
@ -166,13 +166,11 @@ class VisitsStatsHelperTest extends TestCase
{
$domain = 'foo.com';
$apiKey = ApiKey::create();
$repo = $this->prophesize(DomainRepository::class);
$domainExists = $repo->domainExists($domain, $apiKey)->willReturn(false);
$getRepo = $this->em->getRepository(Domain::class)->willReturn($repo->reveal());
$repo = $this->createMock(DomainRepository::class);
$repo->expects($this->once())->method('domainExists')->with($domain, $apiKey)->willReturn(false);
$this->em->expects($this->once())->method('getRepository')->with(Domain::class)->willReturn($repo);
$this->expectException(DomainNotFoundException::class);
$domainExists->shouldBeCalledOnce();
$getRepo->shouldBeCalledOnce();
$this->helper->visitsForDomain($domain, new VisitsParams(), $apiKey);
}
@ -184,21 +182,28 @@ class VisitsStatsHelperTest extends TestCase
public function visitsForNonDefaultDomainAreReturnedAsExpected(?ApiKey $apiKey): void
{
$domain = 'foo.com';
$repo = $this->prophesize(DomainRepository::class);
$domainExists = $repo->domainExists($domain, $apiKey)->willReturn(true);
$getRepo = $this->em->getRepository(Domain::class)->willReturn($repo->reveal());
$repo = $this->createMock(DomainRepository::class);
$repo->expects($this->once())->method('domainExists')->with($domain, $apiKey)->willReturn(true);
$list = map(range(0, 1), fn () => Visit::forValidShortUrl(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByDomain($domain, Argument::type(VisitsListFiltering::class))->willReturn($list);
$repo2->countVisitsByDomain($domain, Argument::type(VisitsCountFiltering::class))->willReturn(1);
$this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledOnce();
$repo2 = $this->createMock(VisitRepository::class);
$repo2->method('findVisitsByDomain')->with(
$domain,
$this->isInstanceOf(VisitsListFiltering::class),
)->willReturn($list);
$repo2->method('countVisitsByDomain')->with(
$domain,
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn(1);
$this->em->expects($this->exactly(2))->method('getRepository')->willReturnMap([
[Domain::class, $repo],
[Visit::class, $repo2],
]);
$paginator = $this->helper->visitsForDomain($domain, new VisitsParams(), $apiKey);
self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults()));
$domainExists->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledOnce();
}
/**
@ -207,56 +212,63 @@ class VisitsStatsHelperTest extends TestCase
*/
public function visitsForDefaultDomainAreReturnedAsExpected(?ApiKey $apiKey): void
{
$repo = $this->prophesize(DomainRepository::class);
$domainExists = $repo->domainExists(Argument::cetera());
$getRepo = $this->em->getRepository(Domain::class)->willReturn($repo->reveal());
$repo = $this->createMock(DomainRepository::class);
$repo->expects($this->never())->method('domainExists');
$list = map(range(0, 1), fn () => Visit::forValidShortUrl(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByDomain('DEFAULT', Argument::type(VisitsListFiltering::class))->willReturn($list);
$repo2->countVisitsByDomain('DEFAULT', Argument::type(VisitsCountFiltering::class))->willReturn(1);
$this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledOnce();
$repo2 = $this->createMock(VisitRepository::class);
$repo2->method('findVisitsByDomain')->with(
'DEFAULT',
$this->isInstanceOf(VisitsListFiltering::class),
)->willReturn($list);
$repo2->method('countVisitsByDomain')->with(
'DEFAULT',
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn(1);
$this->em->expects($this->exactly(2))->method('getRepository')->willReturnMap([
[Domain::class, $repo],
[Visit::class, $repo2],
]);
$paginator = $this->helper->visitsForDomain('DEFAULT', new VisitsParams(), $apiKey);
self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults()));
$domainExists->shouldNotHaveBeenCalled();
$getRepo->shouldHaveBeenCalledOnce();
}
/** @test */
public function orphanVisitsAreReturnedAsExpected(): void
{
$list = map(range(0, 3), fn () => Visit::forBasePath(Visitor::emptyInstance()));
$repo = $this->prophesize(VisitRepository::class);
$countVisits = $repo->countOrphanVisits(Argument::type(VisitsCountFiltering::class))->willReturn(count($list));
$listVisits = $repo->findOrphanVisits(Argument::type(VisitsListFiltering::class))->willReturn($list);
$getRepo = $this->em->getRepository(Visit::class)->willReturn($repo->reveal());
$repo = $this->createMock(VisitRepository::class);
$repo->expects($this->once())->method('countOrphanVisits')->with(
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn(count($list));
$repo->expects($this->once())->method('findOrphanVisits')->with(
$this->isInstanceOf(VisitsListFiltering::class),
)->willReturn($list);
$this->em->expects($this->once())->method('getRepository')->with(Visit::class)->willReturn($repo);
$paginator = $this->helper->orphanVisits(new VisitsParams());
self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults()));
$listVisits->shouldHaveBeenCalledOnce();
$countVisits->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledOnce();
}
/** @test */
public function nonOrphanVisitsAreReturnedAsExpected(): void
{
$list = map(range(0, 3), fn () => Visit::forValidShortUrl(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo = $this->prophesize(VisitRepository::class);
$countVisits = $repo->countNonOrphanVisits(Argument::type(VisitsCountFiltering::class))->willReturn(
count($list),
);
$listVisits = $repo->findNonOrphanVisits(Argument::type(VisitsListFiltering::class))->willReturn($list);
$getRepo = $this->em->getRepository(Visit::class)->willReturn($repo->reveal());
$repo = $this->createMock(VisitRepository::class);
$repo->expects($this->once())->method('countNonOrphanVisits')->with(
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn(count($list));
$repo->expects($this->once())->method('findNonOrphanVisits')->with(
$this->isInstanceOf(VisitsListFiltering::class),
)->willReturn($list);
$this->em->expects($this->once())->method('getRepository')->with(Visit::class)->willReturn($repo);
$paginator = $this->helper->nonOrphanVisits(new VisitsParams());
self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults()));
$listVisits->shouldHaveBeenCalledOnce();
$countVisits->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledOnce();
}
}

View file

@ -5,10 +5,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Visit;
use Doctrine\ORM\EntityManager;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\EventDispatcher\EventDispatcherInterface;
use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited;
use Shlinkio\Shlink\Core\Options\TrackingOptions;
@ -19,16 +17,13 @@ use Shlinkio\Shlink\Core\Visit\VisitsTracker;
class VisitsTrackerTest extends TestCase
{
use ProphecyTrait;
private VisitsTracker $visitsTracker;
private ObjectProphecy $em;
private ObjectProphecy $eventDispatcher;
private MockObject $em;
private MockObject $eventDispatcher;
protected function setUp(): void
{
$this->em = $this->prophesize(EntityManager::class);
$this->eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
$this->em = $this->createMock(EntityManager::class);
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
}
/**
@ -37,14 +32,15 @@ class VisitsTrackerTest extends TestCase
*/
public function trackPersistsVisitAndDispatchesEvent(string $method, array $args): void
{
$persist = $this->em->persist(Argument::that(fn (Visit $visit) => $visit->setId('1')))->will(function (): void {
});
$this->em->expects($this->once())->method('persist')->with(
$this->callback(fn (Visit $visit) => $visit->setId('1') !== null),
);
$this->em->expects($this->once())->method('flush');
$this->eventDispatcher->expects($this->once())->method('dispatch')->with(
$this->isInstanceOf(UrlVisited::class),
);
$this->visitsTracker()->{$method}(...$args);
$persist->shouldHaveBeenCalledOnce();
$this->em->flush()->shouldHaveBeenCalledOnce();
$this->eventDispatcher->dispatch(Argument::type(UrlVisited::class))->shouldHaveBeenCalled();
}
/**
@ -53,11 +49,11 @@ class VisitsTrackerTest extends TestCase
*/
public function trackingIsSkippedCompletelyWhenDisabledFromOptions(string $method, array $args): void
{
$this->visitsTracker(new TrackingOptions(disableTracking: true))->{$method}(...$args);
$this->em->expects($this->never())->method('persist');
$this->em->expects($this->never())->method('flush');
$this->eventDispatcher->expects($this->never())->method('dispatch');
$this->eventDispatcher->dispatch(Argument::cetera())->shouldNotHaveBeenCalled();
$this->em->persist(Argument::cetera())->shouldNotHaveBeenCalled();
$this->em->flush()->shouldNotHaveBeenCalled();
$this->visitsTracker(new TrackingOptions(disableTracking: true))->{$method}(...$args);
}
public function provideTrackingMethodNames(): iterable
@ -74,11 +70,11 @@ class VisitsTrackerTest extends TestCase
*/
public function orphanVisitsAreNotTrackedWhenDisabled(string $method): void
{
$this->visitsTracker(new TrackingOptions(trackOrphanVisits: false))->{$method}(Visitor::emptyInstance());
$this->em->expects($this->never())->method('persist');
$this->em->expects($this->never())->method('flush');
$this->eventDispatcher->expects($this->never())->method('dispatch');
$this->eventDispatcher->dispatch(Argument::cetera())->shouldNotHaveBeenCalled();
$this->em->persist(Argument::cetera())->shouldNotHaveBeenCalled();
$this->em->flush()->shouldNotHaveBeenCalled();
$this->visitsTracker(new TrackingOptions(trackOrphanVisits: false))->{$method}(Visitor::emptyInstance());
}
public function provideOrphanTrackingMethodNames(): iterable
@ -90,10 +86,6 @@ class VisitsTrackerTest extends TestCase
private function visitsTracker(?TrackingOptions $options = null): VisitsTracker
{
return new VisitsTracker(
$this->em->reveal(),
$this->eventDispatcher->reveal(),
$options ?? new TrackingOptions(),
);
return new VisitsTracker($this->em, $this->eventDispatcher, $options ?? new TrackingOptions());
}
}

View file

@ -6,10 +6,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Domain;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Config\NotFoundRedirects;
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
@ -22,15 +20,13 @@ use function array_key_exists;
class DomainRedirectsActionTest extends TestCase
{
use ProphecyTrait;
private DomainRedirectsAction $action;
private ObjectProphecy $domainService;
private MockObject $domainService;
protected function setUp(): void
{
$this->domainService = $this->prophesize(DomainServiceInterface::class);
$this->action = new DomainRedirectsAction($this->domainService->reveal());
$this->domainService = $this->createMock(DomainServiceInterface::class);
$this->action = new DomainRedirectsAction($this->domainService);
}
/**
@ -42,8 +38,8 @@ class DomainRedirectsActionTest extends TestCase
$request = ServerRequestFactory::fromGlobals()->withParsedBody($body);
$this->expectException(ValidationException::class);
$this->domainService->getOrCreate(Argument::cetera())->shouldNotBeCalled();
$this->domainService->configureNotFoundRedirects(Argument::cetera())->shouldNotBeCalled();
$this->domainService->expects($this->never())->method('getOrCreate');
$this->domainService->expects($this->never())->method('configureNotFoundRedirects');
$this->action->handle($request);
}
@ -70,19 +66,19 @@ class DomainRedirectsActionTest extends TestCase
$request = ServerRequestFactory::fromGlobals()->withParsedBody($redirects)
->withAttribute(ApiKey::class, $apiKey);
$getOrCreate = $this->domainService->getOrCreate($authority)->willReturn($domain);
$configureNotFoundRedirects = $this->domainService->configureNotFoundRedirects(
$this->domainService->expects($this->once())->method('getOrCreate')->with($authority)->willReturn($domain);
$this->domainService->expects($this->once())->method('configureNotFoundRedirects')->with(
$authority,
NotFoundRedirects::withRedirects(
array_key_exists(DomainRedirectsInputFilter::BASE_URL_REDIRECT, $redirects)
? $redirects[DomainRedirectsInputFilter::BASE_URL_REDIRECT]
: $domain?->baseUrlRedirect(),
: $domain->baseUrlRedirect(),
array_key_exists(DomainRedirectsInputFilter::REGULAR_404_REDIRECT, $redirects)
? $redirects[DomainRedirectsInputFilter::REGULAR_404_REDIRECT]
: $domain?->regular404Redirect(),
: $domain->regular404Redirect(),
array_key_exists(DomainRedirectsInputFilter::INVALID_SHORT_URL_REDIRECT, $redirects)
? $redirects[DomainRedirectsInputFilter::INVALID_SHORT_URL_REDIRECT]
: $domain?->invalidShortUrlRedirect(),
: $domain->invalidShortUrlRedirect(),
),
$apiKey,
);
@ -93,8 +89,6 @@ class DomainRedirectsActionTest extends TestCase
$payload = $response->getPayload();
self::assertEquals($expectedResult, $payload->jsonSerialize());
$getOrCreate->shouldHaveBeenCalledOnce();
$configureNotFoundRedirects->shouldHaveBeenCalledOnce();
}
public function provideDomainsAndRedirects(): iterable

View file

@ -6,9 +6,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Domain;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Config\NotFoundRedirects;
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
@ -19,17 +18,15 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ListDomainsActionTest extends TestCase
{
use ProphecyTrait;
private ListDomainsAction $action;
private ObjectProphecy $domainService;
private MockObject $domainService;
private NotFoundRedirectOptions $options;
protected function setUp(): void
{
$this->domainService = $this->prophesize(DomainServiceInterface::class);
$this->domainService = $this->createMock(DomainServiceInterface::class);
$this->options = new NotFoundRedirectOptions();
$this->action = new ListDomainsAction($this->domainService->reveal(), $this->options);
$this->action = new ListDomainsAction($this->domainService, $this->options);
}
/** @test */
@ -40,7 +37,7 @@ class ListDomainsActionTest extends TestCase
DomainItem::forDefaultDomain('bar.com', new NotFoundRedirectOptions()),
DomainItem::forNonDefaultDomain(Domain::withAuthority('baz.com')),
];
$listDomains = $this->domainService->listDomains($apiKey)->willReturn($domains);
$this->domainService->expects($this->once())->method('listDomains')->with($apiKey)->willReturn($domains);
/** @var JsonResponse $resp */
$resp = $this->action->handle(ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $apiKey));
@ -52,6 +49,5 @@ class ListDomainsActionTest extends TestCase
'defaultRedirects' => NotFoundRedirects::fromConfig($this->options),
],
], $payload);
$listDomains->shouldHaveBeenCalledOnce();
}
}

View file

@ -11,37 +11,34 @@ use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Options\AppOptions;
use Shlinkio\Shlink\Rest\Action\HealthAction;
class HealthActionTest extends TestCase
{
use ProphecyTrait;
private HealthAction $action;
private ObjectProphecy $conn;
private MockObject $conn;
protected function setUp(): void
{
$this->conn = $this->prophesize(Connection::class);
$this->conn->executeQuery(Argument::cetera())->willReturn($this->prophesize(Result::class)->reveal());
$dbPlatform = $this->prophesize(AbstractPlatform::class);
$dbPlatform->getDummySelectSQL()->willReturn('');
$this->conn->getDatabasePlatform()->willReturn($dbPlatform->reveal());
$this->conn = $this->createMock(Connection::class);
$dbPlatform = $this->createMock(AbstractPlatform::class);
$dbPlatform->method('getDummySelectSQL')->willReturn('');
$this->conn->method('getDatabasePlatform')->willReturn($dbPlatform);
$em = $this->prophesize(EntityManagerInterface::class);
$em->getConnection()->willReturn($this->conn->reveal());
$em = $this->createMock(EntityManagerInterface::class);
$em->method('getConnection')->willReturn($this->conn);
$this->action = new HealthAction($em->reveal(), new AppOptions(version: '1.2.3'));
$this->action = new HealthAction($em, new AppOptions(version: '1.2.3'));
}
/** @test */
public function passResponseIsReturnedWhenDummyQuerySucceeds(): void
{
$this->conn->expects($this->once())->method('executeQuery')->willReturn($this->createMock(Result::class));
/** @var JsonResponse $resp */
$resp = $this->action->handle(new ServerRequest());
$payload = $resp->getPayload();
@ -54,13 +51,12 @@ class HealthActionTest extends TestCase
'project' => 'https://github.com/shlinkio/shlink',
], $payload['links']);
self::assertEquals('application/health+json', $resp->getHeaderLine('Content-type'));
$this->conn->executeQuery(Argument::cetera())->shouldHaveBeenCalledOnce();
}
/** @test */
public function failResponseIsReturnedWhenDummyQueryThrowsException(): void
{
$executeQuery = $this->conn->executeQuery(Argument::cetera())->willThrow(Exception::class);
$this->conn->expects($this->once())->method('executeQuery')->willThrowException(new Exception());
/** @var JsonResponse $resp */
$resp = $this->action->handle(new ServerRequest());
@ -74,6 +70,5 @@ class HealthActionTest extends TestCase
'project' => 'https://github.com/shlinkio/shlink',
], $payload['links']);
self::assertEquals('application/health+json', $resp->getHeaderLine('Content-type'));
$executeQuery->shouldHaveBeenCalledOnce();
}
}

View file

@ -7,23 +7,19 @@ namespace ShlinkioTest\Shlink\Rest\Action;
use Cake\Chronos\Chronos;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Mercure\JwtProviderInterface;
use Shlinkio\Shlink\Rest\Action\MercureInfoAction;
use Shlinkio\Shlink\Rest\Exception\MercureException;
class MercureInfoActionTest extends TestCase
{
use ProphecyTrait;
private ObjectProphecy $provider;
private MockObject $provider;
protected function setUp(): void
{
$this->provider = $this->prophesize(JwtProviderInterface::class);
$this->provider = $this->createMock(JwtProviderInterface::class);
}
/**
@ -32,12 +28,11 @@ class MercureInfoActionTest extends TestCase
*/
public function throwsExceptionWhenConfigDoesNotHavePublicHost(array $mercureConfig): void
{
$buildToken = $this->provider->buildSubscriptionToken(Argument::any())->willReturn('abc.123');
$this->provider->expects($this->never())->method('buildSubscriptionToken');
$action = new MercureInfoAction($this->provider->reveal(), $mercureConfig);
$action = new MercureInfoAction($this->provider, $mercureConfig);
$this->expectException(MercureException::class);
$buildToken->shouldNotBeCalled();
$action->handle(ServerRequestFactory::fromGlobals());
}
@ -60,9 +55,9 @@ class MercureInfoActionTest extends TestCase
*/
public function returnsExpectedInfoWhenEverythingIsOk(?int $days): void
{
$buildToken = $this->provider->buildSubscriptionToken(Argument::any())->willReturn('abc.123');
$this->provider->expects($this->once())->method('buildSubscriptionToken')->willReturn('abc.123');
$action = new MercureInfoAction($this->provider->reveal(), [
$action = new MercureInfoAction($this->provider, [
'public_hub_url' => 'http://foobar.com',
'jwt_days_duration' => $days,
]);
@ -79,7 +74,6 @@ class MercureInfoActionTest extends TestCase
Chronos::now()->addDays($days ?? 1)->startOfDay(),
Chronos::parse($payload['jwtExpiration'])->startOfDay(),
);
$buildToken->shouldHaveBeenCalledOnce();
}
public function provideDays(): iterable

View file

@ -8,10 +8,8 @@ use Cake\Chronos\Chronos;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
@ -23,23 +21,16 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class CreateShortUrlActionTest extends TestCase
{
use ProphecyTrait;
private CreateShortUrlAction $action;
private ObjectProphecy $urlShortener;
private ObjectProphecy $transformer;
private MockObject $urlShortener;
private MockObject $transformer;
protected function setUp(): void
{
$this->urlShortener = $this->prophesize(UrlShortener::class);
$this->transformer = $this->prophesize(DataTransformerInterface::class);
$this->transformer->transform(Argument::type(ShortUrl::class))->willReturn([]);
$this->urlShortener = $this->createMock(UrlShortener::class);
$this->transformer = $this->createMock(DataTransformerInterface::class);
$this->action = new CreateShortUrlAction(
$this->urlShortener->reveal(),
$this->transformer->reveal(),
new UrlShortenerOptions(),
);
$this->action = new CreateShortUrlAction($this->urlShortener, $this->transformer, new UrlShortenerOptions());
}
/** @test */
@ -58,8 +49,12 @@ class CreateShortUrlActionTest extends TestCase
];
$expectedMeta['apiKey'] = $apiKey;
$shorten = $this->urlShortener->shorten(ShortUrlCreation::fromRawData($expectedMeta))->willReturn($shortUrl);
$transform = $this->transformer->transform($shortUrl)->willReturn(['shortUrl' => 'stringified_short_url']);
$this->urlShortener->expects($this->once())->method('shorten')->with(
ShortUrlCreation::fromRawData($expectedMeta),
)->willReturn($shortUrl);
$this->transformer->expects($this->once())->method('transform')->with($shortUrl)->willReturn(
['shortUrl' => 'stringified_short_url'],
);
$request = ServerRequestFactory::fromGlobals()->withParsedBody($body)->withAttribute(ApiKey::class, $apiKey);
@ -69,8 +64,6 @@ class CreateShortUrlActionTest extends TestCase
self::assertEquals(200, $response->getStatusCode());
self::assertEquals('stringified_short_url', $payload['shortUrl']);
$shorten->shouldHaveBeenCalledOnce();
$transform->shouldHaveBeenCalledOnce();
}
/**
@ -79,8 +72,8 @@ class CreateShortUrlActionTest extends TestCase
*/
public function anInvalidDomainReturnsError(string $domain): void
{
$shortUrl = ShortUrl::createEmpty();
$urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl);
$this->urlShortener->expects($this->never())->method('shorten');
$this->transformer->expects($this->never())->method('transform');
$request = (new ServerRequest())->withParsedBody([
'longUrl' => 'http://www.domain.com/foo/bar',
@ -88,7 +81,6 @@ class CreateShortUrlActionTest extends TestCase
])->withAttribute(ApiKey::class, ApiKey::create());
$this->expectException(ValidationException::class);
$urlToShortCode->shouldNotBeCalled();
$this->action->handle($request);
}

View file

@ -5,39 +5,31 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\ShortUrl\DeleteShortUrlServiceInterface;
use Shlinkio\Shlink\Rest\Action\ShortUrl\DeleteShortUrlAction;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class DeleteShortUrlActionTest extends TestCase
{
use ProphecyTrait;
private DeleteShortUrlAction $action;
private ObjectProphecy $service;
private MockObject $service;
protected function setUp(): void
{
$this->service = $this->prophesize(DeleteShortUrlServiceInterface::class);
$this->action = new DeleteShortUrlAction($this->service->reveal());
$this->service = $this->createMock(DeleteShortUrlServiceInterface::class);
$this->action = new DeleteShortUrlAction($this->service);
}
/** @test */
public function emptyResponseIsReturnedIfProperlyDeleted(): void
{
$apiKey = ApiKey::create();
$deleteByShortCode = $this->service->deleteByShortCode(Argument::any(), false, $apiKey)->will(
function (): void {
},
);
$this->service->expects($this->once())->method('deleteByShortCode');
$resp = $this->action->handle(ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $apiKey));
self::assertEquals(204, $resp->getStatusCode());
$deleteByShortCode->shouldHaveBeenCalledOnce();
}
}

View file

@ -5,10 +5,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
@ -19,15 +17,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class EditShortUrlActionTest extends TestCase
{
use ProphecyTrait;
private EditShortUrlAction $action;
private ObjectProphecy $shortUrlService;
private MockObject $shortUrlService;
protected function setUp(): void
{
$this->shortUrlService = $this->prophesize(ShortUrlServiceInterface::class);
$this->action = new EditShortUrlAction($this->shortUrlService->reveal(), new ShortUrlDataTransformer(
$this->shortUrlService = $this->createMock(ShortUrlServiceInterface::class);
$this->action = new EditShortUrlAction($this->shortUrlService, new ShortUrlDataTransformer(
new ShortUrlStringifier([]),
));
}
@ -38,6 +34,7 @@ class EditShortUrlActionTest extends TestCase
$request = (new ServerRequest())->withParsedBody([
'maxVisits' => 'invalid',
]);
$this->shortUrlService->expects($this->never())->method('updateShortUrl');
$this->expectException(ValidationException::class);
@ -52,13 +49,10 @@ class EditShortUrlActionTest extends TestCase
->withParsedBody([
'maxVisits' => 5,
]);
$updateMeta = $this->shortUrlService->updateShortUrl(Argument::cetera())->willReturn(
ShortUrl::createEmpty(),
);
$this->shortUrlService->expects($this->once())->method('updateShortUrl')->willReturn(ShortUrl::createEmpty());
$resp = $this->action->handle($request);
self::assertEquals(200, $resp->getStatusCode());
$updateMeta->shouldHaveBeenCalled();
}
}

View file

@ -8,9 +8,8 @@ use Cake\Chronos\Chronos;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams;
@ -21,16 +20,14 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ListShortUrlsActionTest extends TestCase
{
use ProphecyTrait;
private ListShortUrlsAction $action;
private ObjectProphecy $service;
private MockObject $service;
protected function setUp(): void
{
$this->service = $this->prophesize(ShortUrlService::class);
$this->service = $this->createMock(ShortUrlService::class);
$this->action = new ListShortUrlsAction($this->service->reveal(), new ShortUrlDataTransformer(
$this->action = new ListShortUrlsAction($this->service, new ShortUrlDataTransformer(
new ShortUrlStringifier([
'hostname' => 'doma.in',
'schema' => 'https',
@ -54,7 +51,7 @@ class ListShortUrlsActionTest extends TestCase
$apiKey = ApiKey::create();
$request = ServerRequestFactory::fromGlobals()->withQueryParams($query)
->withAttribute(ApiKey::class, $apiKey);
$listShortUrls = $this->service->listShortUrls(ShortUrlsParams::fromRawData([
$this->service->expects($this->once())->method('listShortUrls')->with(ShortUrlsParams::fromRawData([
'page' => $expectedPage,
'searchTerm' => $expectedSearchTerm,
'tags' => $expectedTags,
@ -71,7 +68,6 @@ class ListShortUrlsActionTest extends TestCase
self::assertArrayHasKey('data', $payload['shortUrls']);
self::assertEquals([], $payload['shortUrls']['data']);
self::assertEquals(200, $response->getStatusCode());
$listShortUrls->shouldHaveBeenCalledOnce();
}
public function provideFilteringData(): iterable

View file

@ -5,9 +5,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
@ -18,15 +17,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ResolveShortUrlActionTest extends TestCase
{
use ProphecyTrait;
private ResolveShortUrlAction $action;
private ObjectProphecy $urlResolver;
private MockObject $urlResolver;
protected function setUp(): void
{
$this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class);
$this->action = new ResolveShortUrlAction($this->urlResolver->reveal(), new ShortUrlDataTransformer(
$this->urlResolver = $this->createMock(ShortUrlResolverInterface::class);
$this->action = new ResolveShortUrlAction($this->urlResolver, new ShortUrlDataTransformer(
new ShortUrlStringifier([]),
));
}
@ -36,11 +33,10 @@ class ResolveShortUrlActionTest extends TestCase
{
$shortCode = 'abc123';
$apiKey = ApiKey::create();
$this->urlResolver->resolveShortUrl(
$this->urlResolver->expects($this->once())->method('resolveShortUrl')->with(
ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
$apiKey,
)->willReturn(ShortUrl::withLongUrl('http://domain.com/foo/bar'))
->shouldBeCalledOnce();
)->willReturn(ShortUrl::withLongUrl('http://domain.com/foo/bar'));
$request = (new ServerRequest())->withAttribute('shortCode', $shortCode)->withAttribute(ApiKey::class, $apiKey);
$response = $this->action->handle($request);

View file

@ -5,10 +5,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
@ -19,21 +17,18 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class SingleStepCreateShortUrlActionTest extends TestCase
{
use ProphecyTrait;
private SingleStepCreateShortUrlAction $action;
private ObjectProphecy $urlShortener;
private ObjectProphecy $transformer;
private MockObject $urlShortener;
protected function setUp(): void
{
$this->urlShortener = $this->prophesize(UrlShortenerInterface::class);
$this->transformer = $this->prophesize(DataTransformerInterface::class);
$this->transformer->transform(Argument::type(ShortUrl::class))->willReturn([]);
$this->urlShortener = $this->createMock(UrlShortenerInterface::class);
$transformer = $this->createMock(DataTransformerInterface::class);
$transformer->method('transform')->willReturn([]);
$this->action = new SingleStepCreateShortUrlAction(
$this->urlShortener->reveal(),
$this->transformer->reveal(),
$this->urlShortener,
$transformer,
new UrlShortenerOptions(),
);
}
@ -46,13 +41,12 @@ class SingleStepCreateShortUrlActionTest extends TestCase
$request = (new ServerRequest())->withQueryParams([
'longUrl' => 'http://foobar.com',
])->withAttribute(ApiKey::class, $apiKey);
$generateShortCode = $this->urlShortener->shorten(
$this->urlShortener->expects($this->once())->method('shorten')->with(
ShortUrlCreation::fromRawData(['apiKey' => $apiKey, 'longUrl' => 'http://foobar.com']),
)->willReturn(ShortUrl::createEmpty());
$resp = $this->action->handle($request);
self::assertEquals(200, $resp->getStatusCode());
$generateShortCode->shouldHaveBeenCalled();
}
}

View file

@ -5,25 +5,21 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
use Shlinkio\Shlink\Rest\Action\Tag\DeleteTagsAction;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class DeleteTagsActionTest extends TestCase
{
use ProphecyTrait;
private DeleteTagsAction $action;
private ObjectProphecy $tagService;
private MockObject $tagService;
protected function setUp(): void
{
$this->tagService = $this->prophesize(TagServiceInterface::class);
$this->action = new DeleteTagsAction($this->tagService->reveal());
$this->tagService = $this->createMock(TagServiceInterface::class);
$this->action = new DeleteTagsAction($this->tagService);
}
/**
@ -35,12 +31,14 @@ class DeleteTagsActionTest extends TestCase
$request = (new ServerRequest())
->withQueryParams(['tags' => $tags])
->withAttribute(ApiKey::class, ApiKey::create());
$deleteTags = $this->tagService->deleteTags($tags ?: [], Argument::type(ApiKey::class));
$this->tagService->expects($this->once())->method('deleteTags')->with(
$tags ?? [],
$this->isInstanceOf(ApiKey::class),
);
$response = $this->action->handle($request);
self::assertEquals(204, $response->getStatusCode());
$deleteTags->shouldHaveBeenCalled();
}
public function provideTags(): iterable

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
@ -23,15 +21,13 @@ use function count;
class ListTagsActionTest extends TestCase
{
use ProphecyTrait;
private ListTagsAction $action;
private ObjectProphecy $tagService;
private MockObject $tagService;
protected function setUp(): void
{
$this->tagService = $this->prophesize(TagServiceInterface::class);
$this->action = new ListTagsAction($this->tagService->reveal());
$this->tagService = $this->createMock(TagServiceInterface::class);
$this->action = new ListTagsAction($this->tagService);
}
/**
@ -42,9 +38,10 @@ class ListTagsActionTest extends TestCase
{
$tags = [new Tag('foo'), new Tag('bar')];
$tagsCount = count($tags);
$listTags = $this->tagService->listTags(Argument::any(), Argument::type(ApiKey::class))->willReturn(
new Paginator(new ArrayAdapter($tags)),
);
$this->tagService->expects($this->once())->method('listTags')->with(
$this->anything(),
$this->isInstanceOf(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter($tags)));
/** @var JsonResponse $resp */
$resp = $this->action->handle($this->requestWithApiKey()->withQueryParams($query));
@ -62,7 +59,6 @@ class ListTagsActionTest extends TestCase
],
],
], $payload);
$listTags->shouldHaveBeenCalled();
}
public function provideNoStatsQueries(): iterable
@ -80,9 +76,10 @@ class ListTagsActionTest extends TestCase
new TagInfo('bar', 3, 10),
];
$itemsCount = count($stats);
$tagsInfo = $this->tagService->tagsInfo(Argument::any(), Argument::type(ApiKey::class))->willReturn(
new Paginator(new ArrayAdapter($stats)),
);
$this->tagService->expects($this->once())->method('tagsInfo')->with(
$this->anything(),
$this->isInstanceOf(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter($stats)));
$req = $this->requestWithApiKey()->withQueryParams(['withStats' => 'true']);
/** @var JsonResponse $resp */
@ -102,7 +99,6 @@ class ListTagsActionTest extends TestCase
],
],
], $payload);
$tagsInfo->shouldHaveBeenCalled();
}
private function requestWithApiKey(): ServerRequestInterface

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
@ -22,15 +20,13 @@ use function count;
class TagsStatsActionTest extends TestCase
{
use ProphecyTrait;
private TagsStatsAction $action;
private ObjectProphecy $tagService;
private MockObject $tagService;
protected function setUp(): void
{
$this->tagService = $this->prophesize(TagServiceInterface::class);
$this->action = new TagsStatsAction($this->tagService->reveal());
$this->tagService = $this->createMock(TagServiceInterface::class);
$this->action = new TagsStatsAction($this->tagService);
}
/** @test */
@ -41,9 +37,10 @@ class TagsStatsActionTest extends TestCase
new TagInfo('bar', 3, 10),
];
$itemsCount = count($stats);
$tagsInfo = $this->tagService->tagsInfo(Argument::any(), Argument::type(ApiKey::class))->willReturn(
new Paginator(new ArrayAdapter($stats)),
);
$this->tagService->expects($this->once())->method('tagsInfo')->with(
$this->anything(),
$this->isInstanceOf(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter($stats)));
$req = $this->requestWithApiKey()->withQueryParams(['withStats' => 'true']);
/** @var JsonResponse $resp */
@ -62,7 +59,6 @@ class TagsStatsActionTest extends TestCase
],
],
], $payload);
$tagsInfo->shouldHaveBeenCalled();
}
private function requestWithApiKey(): ServerRequestInterface

View file

@ -5,10 +5,8 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
@ -19,15 +17,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class UpdateTagActionTest extends TestCase
{
use ProphecyTrait;
private UpdateTagAction $action;
private ObjectProphecy $tagService;
private MockObject $tagService;
protected function setUp(): void
{
$this->tagService = $this->prophesize(TagServiceInterface::class);
$this->action = new UpdateTagAction($this->tagService->reveal());
$this->tagService = $this->createMock(TagServiceInterface::class);
$this->action = new UpdateTagAction($this->tagService);
}
/**
@ -57,15 +53,14 @@ class UpdateTagActionTest extends TestCase
'oldName' => 'foo',
'newName' => 'bar',
]);
$rename = $this->tagService->renameTag(
$this->tagService->expects($this->once())->method('renameTag')->with(
TagRenaming::fromNames('foo', 'bar'),
Argument::type(ApiKey::class),
$this->isInstanceOf(ApiKey::class),
)->willReturn(new Tag('bar'));
$resp = $this->action->handle($request);
self::assertEquals(204, $resp->getStatusCode());
$rename->shouldHaveBeenCalled();
}
private function requestWithApiKey(): ServerRequestInterface

View file

@ -6,10 +6,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
@ -18,15 +16,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class DomainVisitsActionTest extends TestCase
{
use ProphecyTrait;
private DomainVisitsAction $action;
private ObjectProphecy $visitsHelper;
private MockObject $visitsHelper;
protected function setUp(): void
{
$this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class);
$this->action = new DomainVisitsAction($this->visitsHelper->reveal(), 'the_default.com');
$this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class);
$this->action = new DomainVisitsAction($this->visitsHelper, 'the_default.com');
}
/**
@ -36,9 +32,9 @@ class DomainVisitsActionTest extends TestCase
public function providingCorrectDomainReturnsVisits(string $providedDomain, string $expectedDomain): void
{
$apiKey = ApiKey::create();
$getVisits = $this->visitsHelper->visitsForDomain(
$this->visitsHelper->expects($this->once())->method('visitsForDomain')->with(
$expectedDomain,
Argument::type(VisitsParams::class),
$this->isInstanceOf(VisitsParams::class),
$apiKey,
)->willReturn(new Paginator(new ArrayAdapter([])));
@ -48,7 +44,6 @@ class DomainVisitsActionTest extends TestCase
);
self::assertEquals(200, $response->getStatusCode());
$getVisits->shouldHaveBeenCalledOnce();
}
public function provideDomainAuthorities(): iterable

View file

@ -6,9 +6,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Visit\Model\VisitsStats;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
use Shlinkio\Shlink\Rest\Action\Visit\GlobalVisitsAction;
@ -16,15 +15,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class GlobalVisitsActionTest extends TestCase
{
use ProphecyTrait;
private GlobalVisitsAction $action;
private ObjectProphecy $helper;
private MockObject $helper;
protected function setUp(): void
{
$this->helper = $this->prophesize(VisitsStatsHelperInterface::class);
$this->action = new GlobalVisitsAction($this->helper->reveal());
$this->helper = $this->createMock(VisitsStatsHelperInterface::class);
$this->action = new GlobalVisitsAction($this->helper);
}
/** @test */
@ -32,13 +29,12 @@ class GlobalVisitsActionTest extends TestCase
{
$apiKey = ApiKey::create();
$stats = new VisitsStats(5, 3);
$getStats = $this->helper->getVisitsStats($apiKey)->willReturn($stats);
$this->helper->expects($this->once())->method('getVisitsStats')->with($apiKey)->willReturn($stats);
/** @var JsonResponse $resp */
$resp = $this->action->handle(ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $apiKey));
$payload = $resp->getPayload();
self::assertEquals($payload, ['visits' => $stats]);
$getStats->shouldHaveBeenCalledOnce();
}
}

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
@ -19,24 +17,23 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class NonOrphanVisitsActionTest extends TestCase
{
use ProphecyTrait;
private NonOrphanVisitsAction $action;
private ObjectProphecy $visitsHelper;
private MockObject $visitsHelper;
protected function setUp(): void
{
$this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class);
$this->action = new NonOrphanVisitsAction($this->visitsHelper->reveal());
$this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class);
$this->action = new NonOrphanVisitsAction($this->visitsHelper);
}
/** @test */
public function requestIsHandled(): void
{
$apiKey = ApiKey::create();
$getVisits = $this->visitsHelper->nonOrphanVisits(Argument::type(VisitsParams::class), $apiKey)->willReturn(
new Paginator(new ArrayAdapter([])),
);
$this->visitsHelper->expects($this->once())->method('nonOrphanVisits')->with(
$this->isInstanceOf(VisitsParams::class),
$apiKey,
)->willReturn(new Paginator(new ArrayAdapter([])));
/** @var JsonResponse $response */
$response = $this->action->handle(ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $apiKey));
@ -44,6 +41,5 @@ class NonOrphanVisitsActionTest extends TestCase
self::assertEquals(200, $response->getStatusCode());
self::assertArrayHasKey('visits', $payload);
$getVisits->shouldHaveBeenCalledOnce();
}
}

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
@ -23,18 +21,16 @@ use function count;
class OrphanVisitsActionTest extends TestCase
{
use ProphecyTrait;
private OrphanVisitsAction $action;
private ObjectProphecy $visitsHelper;
private ObjectProphecy $orphanVisitTransformer;
private MockObject $visitsHelper;
private MockObject $orphanVisitTransformer;
protected function setUp(): void
{
$this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class);
$this->orphanVisitTransformer = $this->prophesize(DataTransformerInterface::class);
$this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class);
$this->orphanVisitTransformer = $this->createMock(DataTransformerInterface::class);
$this->action = new OrphanVisitsAction($this->visitsHelper->reveal(), $this->orphanVisitTransformer->reveal());
$this->action = new OrphanVisitsAction($this->visitsHelper, $this->orphanVisitTransformer);
}
/** @test */
@ -42,11 +38,13 @@ class OrphanVisitsActionTest extends TestCase
{
$visitor = Visitor::emptyInstance();
$visits = [Visit::forInvalidShortUrl($visitor), Visit::forRegularNotFound($visitor)];
$orphanVisits = $this->visitsHelper->orphanVisits(Argument::type(VisitsParams::class))->willReturn(
new Paginator(new ArrayAdapter($visits)),
);
$this->visitsHelper->expects($this->once())->method('orphanVisits')->with(
$this->isInstanceOf(VisitsParams::class),
)->willReturn(new Paginator(new ArrayAdapter($visits)));
$visitsAmount = count($visits);
$transform = $this->orphanVisitTransformer->transform(Argument::type(Visit::class))->willReturn([]);
$this->orphanVisitTransformer->expects($this->exactly($visitsAmount))->method('transform')->with(
$this->isInstanceOf(Visit::class),
)->willReturn([]);
/** @var JsonResponse $response */
$response = $this->action->handle(ServerRequestFactory::fromGlobals());
@ -54,7 +52,5 @@ class OrphanVisitsActionTest extends TestCase
self::assertCount($visitsAmount, $payload['visits']['data']);
self::assertEquals(200, $response->getStatusCode());
$orphanVisits->shouldHaveBeenCalledOnce();
$transform->shouldHaveBeenCalledTimes($visitsAmount);
}
}

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Visit;
use Cake\Chronos\Chronos;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Common\Util\DateRange;
@ -22,27 +20,24 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ShortUrlVisitsActionTest extends TestCase
{
use ProphecyTrait;
private ShortUrlVisitsAction $action;
private ObjectProphecy $visitsHelper;
private MockObject $visitsHelper;
protected function setUp(): void
{
$this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class);
$this->action = new ShortUrlVisitsAction($this->visitsHelper->reveal());
$this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class);
$this->action = new ShortUrlVisitsAction($this->visitsHelper);
}
/** @test */
public function providingCorrectShortCodeReturnsVisits(): void
{
$shortCode = 'abc123';
$this->visitsHelper->visitsForShortUrl(
$this->visitsHelper->expects($this->once())->method('visitsForShortUrl')->with(
ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
Argument::type(VisitsParams::class),
Argument::type(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter([])))
->shouldBeCalledOnce();
$this->isInstanceOf(VisitsParams::class),
$this->isInstanceOf(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter([])));
$response = $this->action->handle($this->requestWithApiKey()->withAttribute('shortCode', $shortCode));
self::assertEquals(200, $response->getStatusCode());
@ -52,13 +47,15 @@ class ShortUrlVisitsActionTest extends TestCase
public function paramsAreReadFromQuery(): void
{
$shortCode = 'abc123';
$this->visitsHelper->visitsForShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), new VisitsParams(
DateRange::until(Chronos::parse('2016-01-01 00:00:00')),
3,
10,
), Argument::type(ApiKey::class))
->willReturn(new Paginator(new ArrayAdapter([])))
->shouldBeCalledOnce();
$this->visitsHelper->expects($this->once())->method('visitsForShortUrl')->with(
ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
new VisitsParams(
DateRange::until(Chronos::parse('2016-01-01 00:00:00')),
3,
10,
),
$this->isInstanceOf(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter([])));
$response = $this->action->handle(
$this->requestWithApiKey()->withAttribute('shortCode', $shortCode)

View file

@ -6,10 +6,8 @@ namespace ShlinkioTest\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\ServerRequestFactory;
use Pagerfanta\Adapter\ArrayAdapter;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
@ -18,15 +16,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class TagVisitsActionTest extends TestCase
{
use ProphecyTrait;
private TagVisitsAction $action;
private ObjectProphecy $visitsHelper;
private MockObject $visitsHelper;
protected function setUp(): void
{
$this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class);
$this->action = new TagVisitsAction($this->visitsHelper->reveal());
$this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class);
$this->action = new TagVisitsAction($this->visitsHelper);
}
/** @test */
@ -34,15 +30,16 @@ class TagVisitsActionTest extends TestCase
{
$tag = 'foo';
$apiKey = ApiKey::create();
$getVisits = $this->visitsHelper->visitsForTag($tag, Argument::type(VisitsParams::class), $apiKey)->willReturn(
new Paginator(new ArrayAdapter([])),
);
$this->visitsHelper->expects($this->once())->method('visitsForTag')->with(
$tag,
$this->isInstanceOf(VisitsParams::class),
$apiKey,
)->willReturn(new Paginator(new ArrayAdapter([])));
$response = $this->action->handle(
ServerRequestFactory::fromGlobals()->withAttribute('tag', $tag)->withAttribute(ApiKey::class, $apiKey),
);
self::assertEquals(200, $response->getStatusCode());
$getVisits->shouldHaveBeenCalledOnce();
}
}

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\ApiKey;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Mezzio\Application;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Container\ContainerInterface;
use Shlinkio\Shlink\Rest\ApiKey\InitialApiKeyDelegator;
use Shlinkio\Shlink\Rest\ApiKey\Repository\ApiKeyRepositoryInterface;
@ -18,15 +16,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class InitialApiKeyDelegatorTest extends TestCase
{
use ProphecyTrait;
private InitialApiKeyDelegator $delegator;
private ObjectProphecy $container;
private MockObject $container;
protected function setUp(): void
{
$this->delegator = new InitialApiKeyDelegator();
$this->container = $this->prophesize(ContainerInterface::class);
$this->container = $this->createMock(ContainerInterface::class);
}
/**
@ -35,21 +31,21 @@ class InitialApiKeyDelegatorTest extends TestCase
*/
public function apiKeyIsInitializedWhenAppropriate(array $config, int $expectedCalls): void
{
$app = $this->prophesize(Application::class)->reveal();
$apiKeyRepo = $this->prophesize(ApiKeyRepositoryInterface::class);
$em = $this->prophesize(EntityManagerInterface::class);
$app = $this->createMock(Application::class);
$apiKeyRepo = $this->createMock(ApiKeyRepositoryInterface::class);
$apiKeyRepo->expects($this->exactly($expectedCalls))->method('createInitialApiKey');
$em = $this->createMock(EntityManagerInterface::class);
$em->expects($this->exactly($expectedCalls))->method('getRepository')->with(ApiKey::class)->willReturn(
$apiKeyRepo,
);
$this->container->expects($this->exactly($expectedCalls + 1))->method('get')->willReturnMap([
['config', $config],
[EntityManager::class, $em],
]);
$getConfig = $this->container->get('config')->willReturn($config);
$getRepo = $em->getRepository(ApiKey::class)->willReturn($apiKeyRepo->reveal());
$getEm = $this->container->get(EntityManager::class)->willReturn($em->reveal());
$result = ($this->delegator)($this->container->reveal(), '', fn () => $app);
$result = ($this->delegator)($this->container, '', fn () => $app);
self::assertSame($result, $app);
$getConfig->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledTimes($expectedCalls);
$getEm->shouldHaveBeenCalledTimes($expectedCalls);
$apiKeyRepo->createInitialApiKey(Argument::any())->shouldHaveBeenCalledTimes($expectedCalls);
}
public function provideConfigs(): iterable

View file

@ -10,10 +10,8 @@ use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\ServerRequestFactory;
use Mezzio\Router\Route;
use Mezzio\Router\RouteResult;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
@ -29,21 +27,19 @@ use function Laminas\Stratigility\middleware;
class AuthenticationMiddlewareTest extends TestCase
{
use ProphecyTrait;
private AuthenticationMiddleware $middleware;
private ObjectProphecy $apiKeyService;
private ObjectProphecy $handler;
private MockObject $apiKeyService;
private MockObject $handler;
protected function setUp(): void
{
$this->apiKeyService = $this->prophesize(ApiKeyServiceInterface::class);
$this->apiKeyService = $this->createMock(ApiKeyServiceInterface::class);
$this->middleware = new AuthenticationMiddleware(
$this->apiKeyService->reveal(),
$this->apiKeyService,
[HealthAction::class],
['with_query_api_key'],
);
$this->handler = $this->prophesize(RequestHandlerInterface::class);
$this->handler = $this->createMock(RequestHandlerInterface::class);
}
/**
@ -52,13 +48,10 @@ class AuthenticationMiddlewareTest extends TestCase
*/
public function someSituationsFallbackToNextMiddleware(ServerRequestInterface $request): void
{
$handle = $this->handler->handle($request)->willReturn(new Response());
$checkApiKey = $this->apiKeyService->check(Argument::any());
$this->handler->expects($this->once())->method('handle')->with($request)->willReturn(new Response());
$this->apiKeyService->expects($this->never())->method('check');
$this->middleware->process($request, $this->handler->reveal());
$handle->shouldHaveBeenCalledOnce();
$checkApiKey->shouldNotHaveBeenCalled();
$this->middleware->process($request, $this->handler);
}
public function provideRequestsWithoutAuth(): iterable
@ -90,12 +83,12 @@ class AuthenticationMiddlewareTest extends TestCase
ServerRequestInterface $request,
string $expectedMessage,
): void {
$this->apiKeyService->check(Argument::any())->shouldNotBeCalled();
$this->handler->handle($request)->shouldNotBeCalled();
$this->apiKeyService->expects($this->never())->method('check');
$this->handler->expects($this->never())->method('handle');
$this->expectException(MissingAuthenticationException::class);
$this->expectExceptionMessage($expectedMessage);
$this->middleware->process($request, $this->handler->reveal());
$this->middleware->process($request, $this->handler);
}
public function provideRequestsWithoutApiKey(): iterable
@ -127,12 +120,14 @@ class AuthenticationMiddlewareTest extends TestCase
)
->withHeader('X-Api-Key', $apiKey);
$this->apiKeyService->check($apiKey)->willReturn(new ApiKeyCheckResult())->shouldBeCalledOnce();
$this->handler->handle($request)->shouldNotBeCalled();
$this->apiKeyService->expects($this->once())->method('check')->with($apiKey)->willReturn(
new ApiKeyCheckResult(),
);
$this->handler->expects($this->never())->method('handle');
$this->expectException(VerifyAuthenticationException::class);
$this->expectExceptionMessage('Provided API key does not exist or is invalid');
$this->middleware->process($request, $this->handler->reveal());
$this->middleware->process($request, $this->handler);
}
/** @test */
@ -147,13 +142,14 @@ class AuthenticationMiddlewareTest extends TestCase
)
->withHeader('X-Api-Key', $key);
$handle = $this->handler->handle($request->withAttribute(ApiKey::class, $apiKey))->willReturn(new Response());
$checkApiKey = $this->apiKeyService->check($key)->willReturn(new ApiKeyCheckResult($apiKey));
$this->handler->expects($this->once())->method('handle')->with(
$request->withAttribute(ApiKey::class, $apiKey),
)->willReturn(new Response());
$this->apiKeyService->expects($this->once())->method('check')->with($key)->willReturn(
new ApiKeyCheckResult($apiKey),
);
$this->middleware->process($request, $this->handler->reveal());
$handle->shouldHaveBeenCalledOnce();
$checkApiKey->shouldHaveBeenCalledOnce();
$this->middleware->process($request, $this->handler);
}
private function getDummyMiddleware(): MiddlewareInterface

View file

@ -7,20 +7,14 @@ namespace ShlinkioTest\Shlink\Rest\Middleware;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\Stream;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ProphecyInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Rest\Middleware\BodyParserMiddleware;
use function array_shift;
class BodyParserMiddlewareTest extends TestCase
{
use ProphecyTrait;
private BodyParserMiddleware $middleware;
protected function setUp(): void
@ -34,11 +28,11 @@ class BodyParserMiddlewareTest extends TestCase
*/
public function requestsFromOtherMethodsJustFallbackToNextMiddleware(string $method): void
{
$request = $this->prophesize(ServerRequestInterface::class);
$request->getMethod()->willReturn($method);
$request->getParsedBody()->willReturn([]);
$request = $this->createMock(ServerRequestInterface::class);
$request->method('getMethod')->willReturn($method);
$request->method('getParsedBody')->willReturn([]);
self::assertHandlingRequestJustFallsBackToNext($request);
$this->assertHandlingRequestJustFallsBackToNext($request);
}
public function provideIgnoredRequestMethods(): iterable
@ -51,25 +45,21 @@ class BodyParserMiddlewareTest extends TestCase
/** @test */
public function requestsWithNonEmptyBodyJustFallbackToNextMiddleware(): void
{
$request = $this->prophesize(ServerRequestInterface::class);
$request->getMethod()->willReturn('POST');
$request->getParsedBody()->willReturn(['foo' => 'bar']);
$request = $this->createMock(ServerRequestInterface::class);
$request->method('getMethod')->willReturn('POST');
$request->method('getParsedBody')->willReturn(['foo' => 'bar']);
self::assertHandlingRequestJustFallsBackToNext($request);
$this->assertHandlingRequestJustFallsBackToNext($request);
}
private function assertHandlingRequestJustFallsBackToNext(ProphecyInterface $requestMock): void
private function assertHandlingRequestJustFallsBackToNext(MockObject & ServerRequestInterface $request): void
{
$getContentType = $requestMock->getHeaderLine('Content-type')->willReturn('');
$request = $requestMock->reveal();
$request->expects($this->never())->method('getHeaderLine');
$nextHandler = $this->prophesize(RequestHandlerInterface::class);
$handle = $nextHandler->handle($request)->willReturn(new Response());
$nextHandler = $this->createMock(RequestHandlerInterface::class);
$nextHandler->expects($this->once())->method('handle')->with($request)->willReturn(new Response());
$this->middleware->process($request, $nextHandler->reveal());
$handle->shouldHaveBeenCalledOnce();
$getContentType->shouldNotHaveBeenCalled();
$this->middleware->process($request, $nextHandler);
}
/** @test */
@ -80,12 +70,11 @@ class BodyParserMiddlewareTest extends TestCase
$body->write('{"foo": "bar", "bar": ["one", 5]}');
$request = (new ServerRequest())->withMethod('PUT')
->withBody($body);
$delegate = $this->prophesize(RequestHandlerInterface::class);
$process = $delegate->handle(Argument::type(ServerRequestInterface::class))->will(
function (array $args) use ($test) {
/** @var ServerRequestInterface $req */
$req = array_shift($args);
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->expects($this->once())->method('handle')->with(
$this->isInstanceOf(ServerRequestInterface::class),
)->willReturnCallback(
function (ServerRequestInterface $req) use ($test) {
$test->assertEquals([
'foo' => 'bar',
'bar' => ['one', 5],
@ -95,8 +84,6 @@ class BodyParserMiddlewareTest extends TestCase
},
);
$this->middleware->process($request, $delegate->reveal());
$process->shouldHaveBeenCalledOnce();
$this->middleware->process($request, $handler);
}
}

View file

@ -6,33 +6,29 @@ namespace ShlinkioTest\Shlink\Rest\Middleware;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Rest\Middleware\CrossDomainMiddleware;
class CrossDomainMiddlewareTest extends TestCase
{
use ProphecyTrait;
private CrossDomainMiddleware $middleware;
private ObjectProphecy $handler;
private MockObject $handler;
protected function setUp(): void
{
$this->middleware = new CrossDomainMiddleware(['max_age' => 1000]);
$this->handler = $this->prophesize(RequestHandlerInterface::class);
$this->handler = $this->createMock(RequestHandlerInterface::class);
}
/** @test */
public function nonCrossDomainRequestsAreNotAffected(): void
{
$originalResponse = (new Response())->withStatus(404);
$this->handler->handle(Argument::any())->willReturn($originalResponse)->shouldBeCalledOnce();
$this->handler->expects($this->once())->method('handle')->willReturn($originalResponse);
$response = $this->middleware->process(new ServerRequest(), $this->handler->reveal());
$response = $this->middleware->process(new ServerRequest(), $this->handler);
$headers = $response->getHeaders();
self::assertSame($originalResponse, $response);
@ -47,12 +43,9 @@ class CrossDomainMiddlewareTest extends TestCase
public function anyRequestIncludesTheAllowAccessHeader(): void
{
$originalResponse = new Response();
$this->handler->handle(Argument::any())->willReturn($originalResponse)->shouldBeCalledOnce();
$this->handler->expects($this->once())->method('handle')->willReturn($originalResponse);
$response = $this->middleware->process(
(new ServerRequest())->withHeader('Origin', 'local'),
$this->handler->reveal(),
);
$response = $this->middleware->process((new ServerRequest())->withHeader('Origin', 'local'), $this->handler);
self::assertNotSame($originalResponse, $response);
$headers = $response->getHeaders();
@ -71,9 +64,9 @@ class CrossDomainMiddlewareTest extends TestCase
->withMethod('OPTIONS')
->withHeader('Origin', 'local')
->withHeader('Access-Control-Request-Headers', 'foo, bar, baz');
$this->handler->handle(Argument::any())->willReturn($originalResponse)->shouldBeCalledOnce();
$this->handler->expects($this->once())->method('handle')->willReturn($originalResponse);
$response = $this->middleware->process($request, $this->handler->reveal());
$response = $this->middleware->process($request, $this->handler);
self::assertNotSame($originalResponse, $response);
$headers = $response->getHeaders();
@ -99,9 +92,9 @@ class CrossDomainMiddlewareTest extends TestCase
}
$request = (new ServerRequest())->withHeader('Origin', 'local')
->withMethod('OPTIONS');
$this->handler->handle(Argument::any())->willReturn($originalResponse)->shouldBeCalledOnce();
$this->handler->expects($this->once())->method('handle')->willReturn($originalResponse);
$response = $this->middleware->process($request, $this->handler->reveal());
$response = $this->middleware->process($request, $this->handler);
self::assertEquals($response->getHeaderLine('Access-Control-Allow-Methods'), $expectedAllowedMethods);
self::assertEquals(204, $response->getStatusCode());
@ -126,9 +119,9 @@ class CrossDomainMiddlewareTest extends TestCase
$originalResponse = (new Response())->withStatus($status);
$request = (new ServerRequest())->withMethod($method)
->withHeader('Origin', 'local');
$this->handler->handle(Argument::any())->willReturn($originalResponse)->shouldBeCalledOnce();
$this->handler->expects($this->once())->method('handle')->willReturn($originalResponse);
$response = $this->middleware->process($request, $this->handler->reveal());
$response = $this->middleware->process($request, $this->handler);
self::assertEquals($expectedStatus, $response->getStatusCode());
}

View file

@ -6,7 +6,6 @@ namespace ShlinkioTest\Shlink\Rest\Middleware\ErrorHandler;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\Exception\ValidationException;
@ -16,8 +15,6 @@ use Throwable;
class BackwardsCompatibleProblemDetailsHandlerTest extends TestCase
{
use ProphecyTrait;
private BackwardsCompatibleProblemDetailsHandler $handler;
protected function setUp(): void
@ -34,13 +31,12 @@ class BackwardsCompatibleProblemDetailsHandlerTest extends TestCase
Throwable $thrownException,
string $expectedException,
): void {
$handler = $this->prophesize(RequestHandlerInterface::class);
$handle = $handler->handle($request)->willThrow($thrownException);
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->expects($this->once())->method('handle')->with($request)->willThrowException($thrownException);
$this->expectException($expectedException);
$handle->shouldBeCalledOnce();
$this->handler->process($request, $handler->reveal());
$this->handler->process($request, $handler);
}
public function provideExceptions(): iterable

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Middleware\Mercure;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequestFactory;
use Mezzio\ProblemDetails\ProblemDetailsResponseFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Rest\Exception\MercureException;
@ -18,45 +16,40 @@ use Shlinkio\Shlink\Rest\Middleware\Mercure\NotConfiguredMercureErrorHandler;
class NotConfiguredMercureErrorHandlerTest extends TestCase
{
use ProphecyTrait;
private NotConfiguredMercureErrorHandler $middleware;
private ObjectProphecy $respFactory;
private ObjectProphecy $logger;
private ObjectProphecy $handler;
private MockObject $respFactory;
private MockObject $logger;
private MockObject $handler;
protected function setUp(): void
{
$this->respFactory = $this->prophesize(ProblemDetailsResponseFactory::class);
$this->logger = $this->prophesize(LoggerInterface::class);
$this->middleware = new NotConfiguredMercureErrorHandler($this->respFactory->reveal(), $this->logger->reveal());
$this->handler = $this->prophesize(RequestHandlerInterface::class);
$this->respFactory = $this->createMock(ProblemDetailsResponseFactory::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->middleware = new NotConfiguredMercureErrorHandler($this->respFactory, $this->logger);
$this->handler = $this->createMock(RequestHandlerInterface::class);
}
/** @test */
public function requestHandlerIsInvokedWhenNotErrorOccurs(): void
{
$req = ServerRequestFactory::fromGlobals();
$handle = $this->handler->handle($req)->willReturn(new Response());
$this->handler->expects($this->once())->method('handle')->with($req)->willReturn(new Response());
$this->respFactory->expects($this->never())->method('createResponseFromThrowable');
$this->logger->expects($this->never())->method('warning');
$this->middleware->process($req, $this->handler->reveal());
$handle->shouldHaveBeenCalledOnce();
$this->logger->warning(Argument::cetera())->shouldNotHaveBeenCalled();
$this->respFactory->createResponseFromThrowable(Argument::cetera())->shouldNotHaveBeenCalled();
$this->middleware->process($req, $this->handler);
}
/** @test */
public function exceptionIsParsedToResponse(): void
{
$req = ServerRequestFactory::fromGlobals();
$handle = $this->handler->handle($req)->willThrow(MercureException::mercureNotConfigured());
$createResp = $this->respFactory->createResponseFromThrowable(Argument::cetera())->willReturn(new Response());
$this->handler->expects($this->once())->method('handle')->with($req)->willThrowException(
MercureException::mercureNotConfigured(),
);
$this->respFactory->expects($this->once())->method('createResponseFromThrowable')->willReturn(new Response());
$this->logger->expects($this->once())->method('warning');
$this->middleware->process($req, $this->handler->reveal());
$handle->shouldHaveBeenCalledOnce();
$createResp->shouldHaveBeenCalledOnce();
$this->logger->warning(Argument::cetera())->shouldHaveBeenCalledOnce();
$this->middleware->process($req, $this->handler);
}
}

View file

@ -7,34 +7,32 @@ namespace ShlinkioTest\Shlink\Rest\Middleware\ShortUrl;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Rest\Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware;
class CreateShortUrlContentNegotiationMiddlewareTest extends TestCase
{
use ProphecyTrait;
private CreateShortUrlContentNegotiationMiddleware $middleware;
private ObjectProphecy $requestHandler;
private MockObject $requestHandler;
protected function setUp(): void
{
$this->middleware = new CreateShortUrlContentNegotiationMiddleware();
$this->requestHandler = $this->prophesize(RequestHandlerInterface::class);
$this->requestHandler = $this->createMock(RequestHandlerInterface::class);
}
/** @test */
public function whenNoJsonResponseIsReturnedNoFurtherOperationsArePerformed(): void
{
$expectedResp = new Response();
$this->requestHandler->handle(Argument::type(ServerRequestInterface::class))->willReturn($expectedResp);
$this->requestHandler->method('handle')->with($this->isInstanceOf(ServerRequestInterface::class))->willReturn(
$expectedResp,
);
$resp = $this->middleware->process(new ServerRequest(), $this->requestHandler->reveal());
$resp = $this->middleware->process(new ServerRequest(), $this->requestHandler);
self::assertSame($expectedResp, $resp);
}
@ -51,14 +49,13 @@ class CreateShortUrlContentNegotiationMiddlewareTest extends TestCase
$request = $request->withHeader('Accept', $accept);
}
$handle = $this->requestHandler->handle(Argument::type(ServerRequestInterface::class))->willReturn(
new JsonResponse(['shortUrl' => 'http://doma.in/foo']),
);
$this->requestHandler->expects($this->once())->method('handle')->with(
$this->isInstanceOf(ServerRequestInterface::class),
)->willReturn(new JsonResponse(['shortUrl' => 'http://doma.in/foo']));
$response = $this->middleware->process($request, $this->requestHandler->reveal());
$response = $this->middleware->process($request, $this->requestHandler);
self::assertEquals($expectedContentType, $response->getHeaderLine('Content-type'));
$handle->shouldHaveBeenCalled();
}
public function provideData(): iterable
@ -82,14 +79,13 @@ class CreateShortUrlContentNegotiationMiddlewareTest extends TestCase
{
$request = (new ServerRequest())->withQueryParams(['format' => 'txt']);
$handle = $this->requestHandler->handle(Argument::type(ServerRequestInterface::class))->willReturn(
new JsonResponse($json),
);
$this->requestHandler->expects($this->once())->method('handle')->with(
$this->isInstanceOf(ServerRequestInterface::class),
)->willReturn(new JsonResponse($json));
$response = $this->middleware->process($request, $this->requestHandler->reveal());
$response = $this->middleware->process($request, $this->requestHandler);
self::assertEquals($expectedBody, (string) $response->getBody());
$handle->shouldHaveBeenCalled();
}
public function provideTextBodies(): iterable

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Middleware\ShortUrl;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter;
@ -18,14 +16,12 @@ use Shlinkio\Shlink\Rest\Middleware\ShortUrl\DefaultShortCodesLengthMiddleware;
class DefaultShortCodesLengthMiddlewareTest extends TestCase
{
use ProphecyTrait;
private DefaultShortCodesLengthMiddleware $middleware;
private ObjectProphecy $handler;
private MockObject $handler;
protected function setUp(): void
{
$this->handler = $this->prophesize(RequestHandlerInterface::class);
$this->handler = $this->createMock(RequestHandlerInterface::class);
$this->middleware = new DefaultShortCodesLengthMiddleware(8);
}
@ -36,17 +32,17 @@ class DefaultShortCodesLengthMiddlewareTest extends TestCase
public function defaultValueIsInjectedInBodyWhenNotProvided(array $body, int $expectedLength): void
{
$request = ServerRequestFactory::fromGlobals()->withParsedBody($body);
$handle = $this->handler->handle(Argument::that(function (ServerRequestInterface $req) use ($expectedLength) {
$parsedBody = $req->getParsedBody();
Assert::assertArrayHasKey(ShortUrlInputFilter::SHORT_CODE_LENGTH, $parsedBody);
Assert::assertEquals($expectedLength, $parsedBody[ShortUrlInputFilter::SHORT_CODE_LENGTH]);
$this->handler->expects($this->once())->method('handle')->with($this->callback(
function (ServerRequestInterface $req) use ($expectedLength) {
$parsedBody = $req->getParsedBody();
Assert::assertArrayHasKey(ShortUrlInputFilter::SHORT_CODE_LENGTH, $parsedBody);
Assert::assertEquals($expectedLength, $parsedBody[ShortUrlInputFilter::SHORT_CODE_LENGTH]);
return $req;
}))->willReturn(new Response());
return true;
},
))->willReturn(new Response());
$this->middleware->process($request, $this->handler->reveal());
$handle->shouldHaveBeenCalledOnce();
$this->middleware->process($request, $this->handler);
}
public function provideBodies(): iterable

View file

@ -7,24 +7,20 @@ namespace ShlinkioTest\Shlink\Rest\Middleware\ShortUrl;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Rest\Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware;
class DropDefaultDomainFromRequestMiddlewareTest extends TestCase
{
use ProphecyTrait;
private DropDefaultDomainFromRequestMiddleware $middleware;
private ObjectProphecy $next;
private MockObject $next;
protected function setUp(): void
{
$this->next = $this->prophesize(RequestHandlerInterface::class);
$this->next = $this->createMock(RequestHandlerInterface::class);
$this->middleware = new DropDefaultDomainFromRequestMiddleware('doma.in');
}
@ -36,15 +32,15 @@ class DropDefaultDomainFromRequestMiddlewareTest extends TestCase
{
$req = ServerRequestFactory::fromGlobals()->withQueryParams($providedPayload)->withParsedBody($providedPayload);
$handle = $this->next->handle(Argument::that(function (ServerRequestInterface $request) use ($expectedPayload) {
Assert::assertEquals($expectedPayload, $request->getQueryParams());
Assert::assertEquals($expectedPayload, $request->getParsedBody());
return $request;
}))->willReturn(new Response());
$this->next->expects($this->once())->method('handle')->with($this->callback(
function (ServerRequestInterface $request) use ($expectedPayload) {
Assert::assertEquals($expectedPayload, $request->getQueryParams());
Assert::assertEquals($expectedPayload, $request->getParsedBody());
return true;
},
))->willReturn(new Response());
$this->middleware->process($req, $this->next->reveal());
$handle->shouldHaveBeenCalledOnce();
$this->middleware->process($req, $this->next);
}
public function provideQueryParams(): iterable

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Middleware\ShortUrl;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
@ -22,20 +20,18 @@ use Shlinkio\Shlink\Rest\Middleware\ShortUrl\OverrideDomainMiddleware;
class OverrideDomainMiddlewareTest extends TestCase
{
use ProphecyTrait;
private OverrideDomainMiddleware $middleware;
private ObjectProphecy $domainService;
private ObjectProphecy $apiKey;
private ObjectProphecy $handler;
private MockObject $domainService;
private MockObject $apiKey;
private MockObject $handler;
protected function setUp(): void
{
$this->apiKey = $this->prophesize(ApiKey::class);
$this->handler = $this->prophesize(RequestHandlerInterface::class);
$this->apiKey = $this->createMock(ApiKey::class);
$this->handler = $this->createMock(RequestHandlerInterface::class);
$this->domainService = $this->prophesize(DomainServiceInterface::class);
$this->middleware = new OverrideDomainMiddleware($this->domainService->reveal());
$this->domainService = $this->createMock(DomainServiceInterface::class);
$this->middleware = new OverrideDomainMiddleware($this->domainService);
}
/** @test */
@ -43,16 +39,13 @@ class OverrideDomainMiddlewareTest extends TestCase
{
$request = $this->requestWithApiKey();
$response = new Response();
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(false);
$handle = $this->handler->handle($request)->willReturn($response);
$getDomain = $this->domainService->getDomain(Argument::cetera());
$this->apiKey->expects($this->once())->method('hasRole')->with(Role::DOMAIN_SPECIFIC)->willReturn(false);
$this->handler->expects($this->once())->method('handle')->with($request)->willReturn($response);
$this->domainService->expects($this->never())->method('getDomain');
$result = $this->middleware->process($request, $this->handler->reveal());
$result = $this->middleware->process($request, $this->handler);
self::assertSame($response, $result);
$hasRole->shouldHaveBeenCalledOnce();
$handle->shouldHaveBeenCalledOnce();
$getDomain->shouldNotHaveBeenCalled();
}
/**
@ -62,22 +55,19 @@ class OverrideDomainMiddlewareTest extends TestCase
public function overwritesRequestBodyWhenMethodIsPost(Domain $domain, array $body, array $expectedBody): void
{
$request = $this->requestWithApiKey()->withMethod('POST')->withParsedBody($body);
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(true);
$getRoleMeta = $this->apiKey->getRoleMeta(Role::DOMAIN_SPECIFIC)->willReturn(['domain_id' => '123']);
$getDomain = $this->domainService->getDomain('123')->willReturn($domain);
$handle = $this->handler->handle(Argument::that(
$this->apiKey->expects($this->once())->method('hasRole')->with(Role::DOMAIN_SPECIFIC)->willReturn(true);
$this->apiKey->expects($this->once())->method('getRoleMeta')->with(Role::DOMAIN_SPECIFIC)->willReturn(
['domain_id' => '123'],
);
$this->domainService->expects($this->once())->method('getDomain')->with('123')->willReturn($domain);
$this->handler->expects($this->once())->method('handle')->with($this->callback(
function (ServerRequestInterface $req) use ($expectedBody): bool {
Assert::assertEquals($req->getParsedBody(), $expectedBody);
return true;
},
))->willReturn(new Response());
$this->middleware->process($request, $this->handler->reveal());
$hasRole->shouldHaveBeenCalledOnce();
$getRoleMeta->shouldHaveBeenCalledOnce();
$getDomain->shouldHaveBeenCalledOnce();
$handle->shouldHaveBeenCalledOnce();
$this->middleware->process($request, $this->handler);
}
public function provideBodies(): iterable
@ -112,22 +102,19 @@ class OverrideDomainMiddlewareTest extends TestCase
{
$domain = Domain::withAuthority('something.com');
$request = $this->requestWithApiKey()->withMethod($method);
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(true);
$getRoleMeta = $this->apiKey->getRoleMeta(Role::DOMAIN_SPECIFIC)->willReturn(['domain_id' => '123']);
$getDomain = $this->domainService->getDomain('123')->willReturn($domain);
$handle = $this->handler->handle(Argument::that(
$this->apiKey->expects($this->once())->method('hasRole')->with(Role::DOMAIN_SPECIFIC)->willReturn(true);
$this->apiKey->expects($this->once())->method('getRoleMeta')->with(Role::DOMAIN_SPECIFIC)->willReturn(
['domain_id' => '123'],
);
$this->domainService->expects($this->once())->method('getDomain')->with('123')->willReturn($domain);
$this->handler->expects($this->once())->method('handle')->with($this->callback(
function (ServerRequestInterface $req): bool {
Assert::assertEquals($req->getAttribute(ShortUrlInputFilter::DOMAIN), 'something.com');
return true;
},
))->willReturn(new Response());
$this->middleware->process($request, $this->handler->reveal());
$hasRole->shouldHaveBeenCalledOnce();
$getRoleMeta->shouldHaveBeenCalledOnce();
$getDomain->shouldHaveBeenCalledOnce();
$handle->shouldHaveBeenCalledOnce();
$this->middleware->process($request, $this->handler);
}
public function provideMethods(): iterable
@ -140,6 +127,6 @@ class OverrideDomainMiddlewareTest extends TestCase
private function requestWithApiKey(): ServerRequestInterface
{
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $this->apiKey->reveal());
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $this->apiKey);
}
}

View file

@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\Rest\Service;
use Cake\Chronos\Chronos;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
@ -20,15 +18,15 @@ use Shlinkio\Shlink\Rest\Service\ApiKeyService;
class ApiKeyServiceTest extends TestCase
{
use ProphecyTrait;
private ApiKeyService $service;
private ObjectProphecy $em;
private MockObject $em;
private MockObject $repo;
protected function setUp(): void
{
$this->em = $this->prophesize(EntityManager::class);
$this->service = new ApiKeyService($this->em->reveal());
$this->em = $this->createMock(EntityManager::class);
$this->repo = $this->createMock(EntityRepository::class);
$this->service = new ApiKeyService($this->em);
}
/**
@ -38,8 +36,8 @@ class ApiKeyServiceTest extends TestCase
*/
public function apiKeyIsProperlyCreated(?Chronos $date, ?string $name, array $roles): void
{
$this->em->flush()->shouldBeCalledOnce();
$this->em->persist(Argument::type(ApiKey::class))->shouldBeCalledOnce();
$this->em->expects($this->once())->method('flush');
$this->em->expects($this->once())->method('persist')->with($this->isInstanceOf(ApiKey::class));
$key = $this->service->create($date, $name, ...$roles);
@ -69,10 +67,8 @@ class ApiKeyServiceTest extends TestCase
*/
public function checkReturnsFalseForInvalidApiKeys(?ApiKey $invalidKey): void
{
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn($invalidKey)
->shouldBeCalledOnce();
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
$this->repo->expects($this->once())->method('findOneBy')->with(['key' => '12345'])->willReturn($invalidKey);
$this->em->method('getRepository')->with(ApiKey::class)->willReturn($this->repo);
$result = $this->service->check('12345');
@ -92,10 +88,8 @@ class ApiKeyServiceTest extends TestCase
{
$apiKey = ApiKey::create();
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn($apiKey)
->shouldBeCalledOnce();
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
$this->repo->expects($this->once())->method('findOneBy')->with(['key' => '12345'])->willReturn($apiKey);
$this->em->method('getRepository')->with(ApiKey::class)->willReturn($this->repo);
$result = $this->service->check('12345');
@ -106,10 +100,8 @@ class ApiKeyServiceTest extends TestCase
/** @test */
public function disableThrowsExceptionWhenNoApiKeyIsFound(): void
{
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn(null)
->shouldBeCalledOnce();
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
$this->repo->expects($this->once())->method('findOneBy')->with(['key' => '12345'])->willReturn(null);
$this->em->method('getRepository')->with(ApiKey::class)->willReturn($this->repo);
$this->expectException(InvalidArgumentException::class);
@ -120,12 +112,9 @@ class ApiKeyServiceTest extends TestCase
public function disableReturnsDisabledApiKeyWhenFound(): void
{
$key = ApiKey::create();
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn($key)
->shouldBeCalledOnce();
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
$this->em->flush()->shouldBeCalledOnce();
$this->repo->expects($this->once())->method('findOneBy')->with(['key' => '12345'])->willReturn($key);
$this->em->method('getRepository')->with(ApiKey::class)->willReturn($this->repo);
$this->em->expects($this->once())->method('flush');
self::assertTrue($key->isEnabled());
$returnedKey = $this->service->disable('12345');
@ -138,10 +127,8 @@ class ApiKeyServiceTest extends TestCase
{
$expectedApiKeys = [ApiKey::create(), ApiKey::create(), ApiKey::create()];
$repo = $this->prophesize(EntityRepository::class);
$repo->findBy([])->willReturn($expectedApiKeys)
->shouldBeCalledOnce();
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
$this->repo->expects($this->once())->method('findBy')->with([])->willReturn($expectedApiKeys);
$this->em->method('getRepository')->with(ApiKey::class)->willReturn($this->repo);
$result = $this->service->listKeys();
@ -153,10 +140,8 @@ class ApiKeyServiceTest extends TestCase
{
$expectedApiKeys = [ApiKey::create(), ApiKey::create(), ApiKey::create()];
$repo = $this->prophesize(EntityRepository::class);
$repo->findBy(['enabled' => true])->willReturn($expectedApiKeys)
->shouldBeCalledOnce();
$this->em->getRepository(ApiKey::class)->willReturn($repo->reveal());
$this->repo->expects($this->once())->method('findBy')->with(['enabled' => true])->willReturn($expectedApiKeys);
$this->em->method('getRepository')->with(ApiKey::class)->willReturn($this->repo);
$result = $this->service->listKeys(true);