Merge pull request #359 from acelaya/feature/memory-leak

Feature/memory leak
This commit is contained in:
Alejandro Celaya 2019-02-20 18:09:00 +01:00 committed by GitHub
commit 08bd4f131c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
113 changed files with 691 additions and 1182 deletions

View File

@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\CLI\Command\Visit;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\IpGeolocation\IpLocationResolverInterface;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Common\Util\IpAddress;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
@ -61,7 +62,7 @@ class ProcessVisitsCommand extends Command
}
try {
$this->visitService->locateVisits(
$this->visitService->locateUnlocatedVisits(
[$this, 'getGeolocationDataForVisit'],
function (VisitLocation $location) use ($output) {
$output->writeln(sprintf(' [<info>Address located at "%s"</info>]', $location->getCountryName()));
@ -75,7 +76,7 @@ class ProcessVisitsCommand extends Command
}
}
public function getGeolocationDataForVisit(Visit $visit): array
public function getGeolocationDataForVisit(Visit $visit): Location
{
if (! $visit->hasRemoteAddr()) {
$this->output->writeln(

View File

@ -27,9 +27,7 @@ class DisableKeyCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function providedApiKeyIsDisabled()
{
$apiKey = 'abcd1234';
@ -44,9 +42,7 @@ class DisableKeyCommandTest extends TestCase
$this->assertStringContainsString('API key "abcd1234" properly disabled', $output);
}
/**
* @test
*/
/** @test */
public function errorIsReturnedIfServiceThrowsException()
{
$apiKey = 'abcd1234';

View File

@ -29,9 +29,7 @@ class GenerateKeyCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function noExpirationDateIsDefinedIfNotProvided()
{
$create = $this->apiKeyService->create(null)->willReturn(new ApiKey());
@ -45,9 +43,7 @@ class GenerateKeyCommandTest extends TestCase
$create->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function expirationDateIsDefinedIfProvided()
{
$this->apiKeyService->create(Argument::type(Chronos::class))->shouldBeCalledOnce()

View File

@ -25,9 +25,7 @@ class GenerateCharsetCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function charactersAreGeneratedFromDefault()
{
$prefix = 'Character set: ';

View File

@ -39,9 +39,7 @@ class GeneratePreviewCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function previewsForEveryUrlAreGenerated()
{
$paginator = $this->createPaginator([
@ -69,9 +67,7 @@ class GeneratePreviewCommandTest extends TestCase
$generatePreview3->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function exceptionWillOutputError()
{
$items = [

View File

@ -34,9 +34,7 @@ class GenerateShortUrlCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function properShortCodeIsCreatedIfLongUrlIsCorrect()
{
$urlToShortCode = $this->urlShortener->urlToShortCode(Argument::cetera())->willReturn(
@ -54,9 +52,7 @@ class GenerateShortUrlCommandTest extends TestCase
$urlToShortCode->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function exceptionWhileParsingLongUrlOutputsError()
{
$this->urlShortener->urlToShortCode(Argument::cetera())->willThrow(new InvalidUrlException())
@ -73,9 +69,7 @@ class GenerateShortUrlCommandTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function properlyProcessesProvidedTags()
{
$urlToShortCode = $this->urlShortener->urlToShortCode(

View File

@ -8,6 +8,7 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\ShortUrl\GetVisitsCommand;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
@ -36,9 +37,7 @@ class GetVisitsCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function noDateFlagsTriesToListWithoutDateRange()
{
$shortCode = 'abc123';
@ -52,9 +51,7 @@ class GetVisitsCommandTest extends TestCase
]);
}
/**
* @test
*/
/** @test */
public function providingDateFlagsTheListGetsFiltered()
{
$shortCode = 'abc123';
@ -75,16 +72,14 @@ class GetVisitsCommandTest extends TestCase
]);
}
/**
* @test
*/
public function outputIsProperlyGenerated()
/** @test */
public function outputIsProperlyGenerated(): void
{
$shortCode = 'abc123';
$this->visitsTracker->info($shortCode, Argument::any())->willReturn(
new Paginator(new ArrayAdapter([
(new Visit(new ShortUrl(''), new Visitor('bar', 'foo', '')))->locate(
new VisitLocation(['country_name' => 'Spain'])
new VisitLocation(new Location('', 'Spain', '', '', 0, 0, ''))
),
]))
)->shouldBeCalledOnce();

View File

@ -30,9 +30,7 @@ class ListShortUrlsCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function noInputCallsListJustOnce()
{
$this->shortUrlService->listShortUrls(1, null, [], null)->willReturn(new Paginator(new ArrayAdapter()))
@ -42,9 +40,7 @@ class ListShortUrlsCommandTest extends TestCase
$this->commandTester->execute(['command' => 'shortcode:list']);
}
/**
* @test
*/
/** @test */
public function loadingMorePagesCallsListMoreTimes()
{
// The paginator will return more than one page
@ -66,9 +62,7 @@ class ListShortUrlsCommandTest extends TestCase
$this->assertStringContainsString('Continue with page 4?', $output);
}
/**
* @test
*/
/** @test */
public function havingMorePagesButAnsweringNoCallsListJustOnce()
{
// The paginator will return more than one page
@ -93,9 +87,7 @@ class ListShortUrlsCommandTest extends TestCase
$this->assertStringNotContainsString('Continue with page 3?', $output);
}
/**
* @test
*/
/** @test */
public function passingPageWillMakeListStartOnThatPage()
{
$page = 5;
@ -109,9 +101,7 @@ class ListShortUrlsCommandTest extends TestCase
]);
}
/**
* @test
*/
/** @test */
public function ifTagsFlagIsProvidedTagsColumnIsIncluded()
{
$this->shortUrlService->listShortUrls(1, null, [], null)->willReturn(new Paginator(new ArrayAdapter()))

View File

@ -31,9 +31,7 @@ class ResolveUrlCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function correctShortCodeResolvesUrl()
{
$shortCode = 'abc123';
@ -50,9 +48,7 @@ class ResolveUrlCommandTest extends TestCase
$this->assertEquals('Long URL: ' . $expectedUrl . PHP_EOL, $output);
}
/**
* @test
*/
/** @test */
public function incorrectShortCodeOutputsErrorMessage()
{
$shortCode = 'abc123';
@ -67,9 +63,7 @@ class ResolveUrlCommandTest extends TestCase
$this->assertStringContainsString('Provided short code "' . $shortCode . '" could not be found.', $output);
}
/**
* @test
*/
/** @test */
public function wrongShortCodeFormatOutputsErrorMessage()
{
$shortCode = 'abc123';

View File

@ -5,7 +5,6 @@ namespace ShlinkioTest\Shlink\CLI\Command\Tag;
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Tag\CreateTagCommand;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
@ -30,9 +29,7 @@ class CreateTagCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function errorIsReturnedWhenNoTagsAreProvided()
{
$this->commandTester->execute([]);
@ -41,13 +38,10 @@ class CreateTagCommandTest extends TestCase
$this->assertStringContainsString('You have to provide at least one tag name', $output);
}
/**
* @test
*/
/** @test */
public function serviceIsInvokedOnSuccess()
{
$tagNames = ['foo', 'bar'];
/** @var MethodProphecy $createTags */
$createTags = $this->tagService->createTags($tagNames)->willReturn(new ArrayCollection());
$this->commandTester->execute([

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Tag\DeleteTagsCommand;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
@ -31,9 +30,7 @@ class DeleteTagsCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function errorIsReturnedWhenNoTagsAreProvided()
{
$this->commandTester->execute([]);
@ -42,13 +39,10 @@ class DeleteTagsCommandTest extends TestCase
$this->assertStringContainsString('You have to provide at least one tag name', $output);
}
/**
* @test
*/
/** @test */
public function serviceIsInvokedOnSuccess()
{
$tagNames = ['foo', 'bar'];
/** @var MethodProphecy $deleteTags */
$deleteTags = $this->tagService->deleteTags($tagNames)->will(function () {
});

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Tag\ListTagsCommand;
use Shlinkio\Shlink\Core\Entity\Tag;
@ -32,12 +31,9 @@ class ListTagsCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function noTagsPrintsEmptyMessage()
{
/** @var MethodProphecy $listTags */
$listTags = $this->tagService->listTags()->willReturn([]);
$this->commandTester->execute([]);
@ -47,12 +43,9 @@ class ListTagsCommandTest extends TestCase
$listTags->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function listOfTagsIsPrinted()
{
/** @var MethodProphecy $listTags */
$listTags = $this->tagService->listTags()->willReturn([
new Tag('foo'),
new Tag('bar'),

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Tag\RenameTagCommand;
use Shlinkio\Shlink\Core\Entity\Tag;
@ -33,14 +32,11 @@ class RenameTagCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function errorIsPrintedIfExceptionIsThrown()
{
$oldName = 'foo';
$newName = 'bar';
/** @var MethodProphecy $renameTag */
$renameTag = $this->tagService->renameTag($oldName, $newName)->willThrow(EntityDoesNotExistException::class);
$this->commandTester->execute([
@ -53,14 +49,11 @@ class RenameTagCommandTest extends TestCase
$renameTag->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function successIsPrintedIfNoErrorOccurs()
{
$oldName = 'foo';
$newName = 'bar';
/** @var MethodProphecy $renameTag */
$renameTag = $this->tagService->renameTag($oldName, $newName)->willReturn(new Tag($newName));
$this->commandTester->execute([

View File

@ -9,18 +9,17 @@ use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Visit\ProcessVisitsCommand;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\IpGeolocation\IpApiLocationResolver;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Common\Util\IpAddress;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Service\VisitService;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Lock;
use Throwable;
use function array_shift;
use function sprintf;
@ -60,15 +59,13 @@ class ProcessVisitsCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
public function allPendingVisitsAreProcessed()
/** @test */
public function allPendingVisitsAreProcessed(): void
{
$visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4'));
$location = new VisitLocation([]);
$location = new VisitLocation(Location::emptyInstance());
$locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
function (array $args) use ($visit, $location) {
$firstCallback = array_shift($args);
$firstCallback($visit);
@ -77,7 +74,9 @@ class ProcessVisitsCommandTest extends TestCase
$secondCallback($location, $visit);
}
);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn([]);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn(
Location::emptyInstance()
);
$this->commandTester->execute([
'command' => 'visit:process',
@ -93,12 +92,12 @@ class ProcessVisitsCommandTest extends TestCase
* @test
* @dataProvider provideIgnoredAddresses
*/
public function localhostAndEmptyAddressesAreIgnored(?string $address, string $message)
public function localhostAndEmptyAddressesAreIgnored(?string $address, string $message): void
{
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $address));
$location = new VisitLocation([]);
$location = new VisitLocation(Location::emptyInstance());
$locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
function (array $args) use ($visit, $location) {
$firstCallback = array_shift($args);
$firstCallback($visit);
@ -107,41 +106,34 @@ class ProcessVisitsCommandTest extends TestCase
$secondCallback($location, $visit);
}
);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn([]);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn(
Location::emptyInstance()
);
try {
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
} catch (Throwable $e) {
$output = $this->commandTester->getDisplay();
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
$this->assertInstanceOf(IpCannotBeLocatedException::class, $e);
$this->assertStringContainsString($message, $output);
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldNotHaveBeenCalled();
}
$output = $this->commandTester->getDisplay();
$this->assertStringContainsString($message, $output);
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldNotHaveBeenCalled();
}
public function provideIgnoredAddresses(): array
public function provideIgnoredAddresses(): iterable
{
return [
['', 'Ignored visit with no IP address'],
[null, 'Ignored visit with no IP address'],
[IpAddress::LOCALHOST, 'Ignored localhost address'],
];
yield 'with empty address' => ['', 'Ignored visit with no IP address'];
yield 'with null address' => [null, 'Ignored visit with no IP address'];
yield 'with localhost address' => [IpAddress::LOCALHOST, 'Ignored localhost address'];
}
/**
* @test
*/
public function errorWhileLocatingIpIsDisplayed()
/** @test */
public function errorWhileLocatingIpIsDisplayed(): void
{
$visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4'));
$location = new VisitLocation([]);
$location = new VisitLocation(Location::emptyInstance());
$locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
function (array $args) use ($visit, $location) {
$firstCallback = array_shift($args);
$firstCallback($visit);
@ -152,29 +144,23 @@ class ProcessVisitsCommandTest extends TestCase
);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willThrow(WrongIpException::class);
try {
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
} catch (Throwable $e) {
$output = $this->commandTester->getDisplay();
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
$this->assertInstanceOf(IpCannotBeLocatedException::class, $e);
$output = $this->commandTester->getDisplay();
$this->assertStringContainsString('An error occurred while locating IP. Skipped', $output);
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldHaveBeenCalledOnce();
}
$this->assertStringContainsString('An error occurred while locating IP. Skipped', $output);
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function noActionIsPerformedIfLockIsAcquired()
{
$this->lock->acquire()->willReturn(false);
$locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(function () {
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(function () {
});
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn([]);

View File

@ -30,9 +30,7 @@ class UpdateDbCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
*/
/** @test */
public function successMessageIsPrintedIfEverythingWorks()
{
$download = $this->dbUpdater->downloadFreshCopy(Argument::type('callable'))->will(function () {
@ -45,9 +43,7 @@ class UpdateDbCommandTest extends TestCase
$download->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function errorMessageIsPrintedIfAnExceptionIsThrown()
{
$download = $this->dbUpdater->downloadFreshCopy(Argument::type('callable'))->willThrow(RuntimeException::class);

View File

@ -16,9 +16,7 @@ class ConfigProviderTest extends TestCase
$this->configProvider = new ConfigProvider();
}
/**
* @test
*/
/** @test */
public function confiIsProperlyReturned()
{
$config = ($this->configProvider)();

View File

@ -23,18 +23,14 @@ class ApplicationFactoryTest extends TestCase
$this->factory = new ApplicationFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreated()
{
$instance = ($this->factory)($this->createServiceManager(), '');
$this->assertInstanceOf(Application::class, $instance);
}
/**
* @test
*/
/** @test */
public function allCommandsWhichAreServicesAreAdded()
{
$sm = $this->createServiceManager([

View File

@ -18,7 +18,7 @@ class ChainIpLocationResolver implements IpLocationResolverInterface
/**
* @throws WrongIpException
*/
public function resolveIpLocation(string $ipAddress): array
public function resolveIpLocation(string $ipAddress): Model\Location
{
$error = null;

View File

@ -10,16 +10,8 @@ class EmptyIpLocationResolver implements IpLocationResolverInterface
/**
* @throws WrongIpException
*/
public function resolveIpLocation(string $ipAddress): array
public function resolveIpLocation(string $ipAddress): Model\Location
{
return [
'country_code' => '',
'country_name' => '',
'region_name' => '',
'city' => '',
'latitude' => '',
'longitude' => '',
'time_zone' => '',
];
return Model\Location::emptyInstance();
}
}

View File

@ -24,7 +24,7 @@ class GeoLite2LocationResolver implements IpLocationResolverInterface
/**
* @throws WrongIpException
*/
public function resolveIpLocation(string $ipAddress): array
public function resolveIpLocation(string $ipAddress): Model\Location
{
try {
$city = $this->geoLiteDbReader->city($ipAddress);
@ -36,19 +36,19 @@ class GeoLite2LocationResolver implements IpLocationResolverInterface
}
}
private function mapFields(City $city): array
private function mapFields(City $city): Model\Location
{
/** @var Subdivision $region */
$region = first($city->subdivisions);
return [
'country_code' => $city->country->isoCode ?? '',
'country_name' => $city->country->name ?? '',
'region_name' => $region->name ?? '',
'city' => $city->city->name ?? '',
'latitude' => $city->location->latitude ?? '',
'longitude' => $city->location->longitude ?? '',
'time_zone' => $city->location->timeZone ?? '',
];
return new Model\Location(
$city->country->isoCode ?? '',
$city->country->name ?? '',
$region->name ?? '',
$city->city->name ?? '',
(float) ($city->location->latitude ?? ''),
(float) ($city->location->longitude ?? ''),
$city->location->timeZone ?? ''
);
}
}

View File

@ -25,7 +25,7 @@ class IpApiLocationResolver implements IpLocationResolverInterface
/**
* @throws WrongIpException
*/
public function resolveIpLocation(string $ipAddress): array
public function resolveIpLocation(string $ipAddress): Model\Location
{
try {
$response = $this->httpClient->get(sprintf(self::SERVICE_PATTERN, $ipAddress));
@ -37,16 +37,16 @@ class IpApiLocationResolver implements IpLocationResolverInterface
}
}
private function mapFields(array $entry): array
private function mapFields(array $entry): Model\Location
{
return [
'country_code' => $entry['countryCode'] ?? '',
'country_name' => $entry['country'] ?? '',
'region_name' => $entry['regionName'] ?? '',
'city' => $entry['city'] ?? '',
'latitude' => $entry['lat'] ?? '',
'longitude' => $entry['lon'] ?? '',
'time_zone' => $entry['timezone'] ?? '',
];
return new Model\Location(
(string) ($entry['countryCode'] ?? ''),
(string) ($entry['country'] ?? ''),
(string) ($entry['regionName'] ?? ''),
(string) ($entry['city'] ?? ''),
(float) ($entry['lat'] ?? 0.0),
(float) ($entry['lon'] ?? 0.0),
(string) ($entry['timezone'] ?? '')
);
}
}

View File

@ -10,5 +10,5 @@ interface IpLocationResolverInterface
/**
* @throws WrongIpException
*/
public function resolveIpLocation(string $ipAddress): array;
public function resolveIpLocation(string $ipAddress): Model\Location;
}

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Common\IpGeolocation\Model;
final class Location
{
/** @var string */
private $countryCode;
/** @var string */
private $countryName;
/** @var string */
private $regionName;
/** @var string */
private $city;
/** @var float */
private $latitude;
/** @var float */
private $longitude;
/** @var string */
private $timeZone;
public function __construct(
string $countryCode,
string $countryName,
string $regionName,
string $city,
float $latitude,
float $longitude,
string $timeZone
) {
$this->countryCode = $countryCode;
$this->countryName = $countryName;
$this->regionName = $regionName;
$this->city = $city;
$this->latitude = $latitude;
$this->longitude = $longitude;
$this->timeZone = $timeZone;
}
public static function emptyInstance(): self
{
return new self('', '', '', '', 0.0, 0.0, '');
}
public function countryCode(): string
{
return $this->countryCode;
}
public function countryName(): string
{
return $this->countryName;
}
public function regionName(): string
{
return $this->regionName;
}
public function city(): string
{
return $this->city;
}
public function latitude(): float
{
return $this->latitude;
}
public function longitude(): float
{
return $this->longitude;
}
public function timeZone(): string
{
return $this->timeZone;
}
}

View File

@ -16,9 +16,7 @@ class ConfigProviderTest extends TestCase
$this->configProvider = new ConfigProvider();
}
/**
* @test
*/
/** @test */
public function configIsReturned()
{
$config = $this->configProvider->__invoke();

View File

@ -25,9 +25,7 @@ class ShlinkTableTest extends TestCase
$this->shlinkTable = new ShlinkTable($this->baseTable->reveal());
}
/**
* @test
*/
/** @test */
public function renderMakesTableToBeRenderedWithProvidedInfo()
{
$headers = [];
@ -54,9 +52,7 @@ class ShlinkTableTest extends TestCase
$render->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function newTableIsCreatedForFactoryMethod()
{
$instance = ShlinkTable::fromOutput($this->prophesize(OutputInterface::class)->reveal());

View File

@ -9,9 +9,7 @@ use Shlinkio\Shlink\Common\Exception\WrongIpException;
class WrongIpExceptionTest extends TestCase
{
/**
* @test
*/
/** @test */
public function fromIpAddressProperlyCreatesExceptionWithoutPrev()
{
$e = WrongIpException::fromIpAddress('1.2.3.4');
@ -20,9 +18,7 @@ class WrongIpExceptionTest extends TestCase
$this->assertEquals(0, $e->getCode());
$this->assertNull($e->getPrevious());
}
/**
* @test
*/
/** @test */
public function fromIpAddressProperlyCreatesExceptionWithPrev()
{
$prev = new Exception('Previous error');

View File

@ -32,9 +32,7 @@ class CacheFactoryTest extends TestCase
putenv('APP_ENV');
}
/**
* @test
*/
/** @test */
public function productionReturnsApcAdapter()
{
putenv('APP_ENV=pro');
@ -42,9 +40,7 @@ class CacheFactoryTest extends TestCase
$this->assertInstanceOf(ApcuCache::class, $instance);
}
/**
* @test
*/
/** @test */
public function developmentReturnsArrayAdapter()
{
putenv('APP_ENV=dev');
@ -52,9 +48,7 @@ class CacheFactoryTest extends TestCase
$this->assertInstanceOf(ArrayCache::class, $instance);
}
/**
* @test
*/
/** @test */
public function adapterDefinedInConfigIgnoresEnvironment()
{
putenv('APP_ENV=pro');
@ -62,9 +56,7 @@ class CacheFactoryTest extends TestCase
$this->assertInstanceOf(ArrayCache::class, $instance);
}
/**
* @test
*/
/** @test */
public function invalidAdapterDefinedInConfigFallbacksToEnvironment()
{
putenv('APP_ENV=pro');
@ -72,9 +64,7 @@ class CacheFactoryTest extends TestCase
$this->assertInstanceOf(ApcuCache::class, $instance);
}
/**
* @test
*/
/** @test */
public function filesystemCacheAdaptersReadDirOption()
{
$dir = realpath(sys_get_temp_dir());
@ -84,9 +74,7 @@ class CacheFactoryTest extends TestCase
$this->assertEquals($dir, $instance->getDirectory());
}
/**
* @test
*/
/** @test */
public function memcachedCacheAdaptersReadServersOption()
{
$servers = [

View File

@ -20,30 +20,23 @@ class DottedAccessConfigAbstractFactoryTest extends TestCase
}
/**
* @param string $serviceName
* @param bool $canCreate
*
* @test
* @dataProvider provideDotNames
*/
public function canCreateOnlyServicesWithDot(string $serviceName, bool $canCreate)
public function canCreateOnlyServicesWithDot(string $serviceName, bool $canCreate): void
{
$this->assertEquals($canCreate, $this->factory->canCreate(new ServiceManager(), $serviceName));
}
public function provideDotNames(): array
public function provideDotNames(): iterable
{
return [
['foo.bar', true],
['config.something', true],
['config_something', false],
['foo', false],
];
yield 'with a valid service' => ['foo.bar', true];
yield 'with another valid service' => ['config.something', true];
yield 'with an invalid service' => ['config_something', false];
yield 'with another invalid service' => ['foo', false];
}
/**
* @test
*/
/** @test */
public function throwsExceptionWhenFirstPartOfTheServiceIsNotRegistered()
{
$this->expectException(ServiceNotCreatedException::class);
@ -54,9 +47,7 @@ class DottedAccessConfigAbstractFactoryTest extends TestCase
$this->factory->__invoke(new ServiceManager(), 'foo.bar');
}
/**
* @test
*/
/** @test */
public function dottedNotationIsRecursivelyResolvedUntilLastValueIsFoundAndReturned()
{
$expected = 'this is the result';
@ -70,9 +61,7 @@ class DottedAccessConfigAbstractFactoryTest extends TestCase
$this->assertEquals($expected, $result);
}
/**
* @test
*/
/** @test */
public function exceptionIsThrownIfAnyStepCannotBeResolved()
{
$this->expectException(InvalidArgumentException::class);

View File

@ -20,18 +20,14 @@ class EmptyResponseImplicitOptionsMiddlewareFactoryTest extends TestCase
$this->factory = new EmptyResponseImplicitOptionsMiddlewareFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreated()
{
$instance = $this->factory->__invoke(new ServiceManager(), '');
$this->assertInstanceOf(ImplicitOptionsMiddleware::class, $instance);
}
/**
* @test
*/
/** @test */
public function responsePrototypeIsEmptyResponse()
{
$instance = $this->factory->__invoke(new ServiceManager(), '');

View File

@ -18,9 +18,7 @@ class EntityManagerFactoryTest extends TestCase
$this->factory = new EntityManagerFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreated()
{
$sm = new ServiceManager(['services' => [

View File

@ -19,9 +19,7 @@ class LoggerFactoryTest extends TestCase
$this->factory = new LoggerFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreated()
{
/** @var Logger $instance */
@ -30,9 +28,7 @@ class LoggerFactoryTest extends TestCase
$this->assertEquals('Logger', $instance->getName());
}
/**
* @test
*/
/** @test */
public function nameIsSetFromOptions()
{
/** @var Logger $instance */
@ -41,9 +37,7 @@ class LoggerFactoryTest extends TestCase
$this->assertEquals('Foo', $instance->getName());
}
/**
* @test
*/
/** @test */
public function serviceNameOverwritesOptionsLoggerName()
{
/** @var Logger $instance */

View File

@ -18,9 +18,7 @@ class TranslatorFactoryTest extends TestCase
$this->factory = new TranslatorFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreated()
{
$instance = $this->factory->__invoke(new ServiceManager(['services' => [

View File

@ -18,9 +18,7 @@ class ImageBuilderFactoryTest extends TestCase
$this->factory = new ImageBuilderFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreated()
{
$instance = $this->factory->__invoke(new ServiceManager(), '');

View File

@ -19,9 +19,7 @@ class ImageFactoryTest extends TestCase
$this->factory = new ImageFactory();
}
/**
* @test
*/
/** @test */
public function noPageIsSetWhenOptionsAreNotProvided()
{
/** @var Image $image */
@ -36,9 +34,7 @@ class ImageFactoryTest extends TestCase
$this->assertNull($page->getValue($image));
}
/**
* @test
*/
/** @test */
public function aPageIsSetWhenOptionsIncludeTheUrl()
{
$expectedPage = 'foo/bar.html';

View File

@ -8,6 +8,7 @@ use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\IpGeolocation\ChainIpLocationResolver;
use Shlinkio\Shlink\Common\IpGeolocation\IpLocationResolverInterface;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
class ChainIpLocationResolverTest extends TestCase
{
@ -29,9 +30,7 @@ class ChainIpLocationResolverTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function throwsExceptionWhenNoInnerResolverCanHandleTheResolution()
{
$ipAddress = '1.2.3.4';
@ -46,14 +45,12 @@ class ChainIpLocationResolverTest extends TestCase
$this->resolver->resolveIpLocation($ipAddress);
}
/**
* @test
*/
public function returnsResultOfFirstInnerResolver()
/** @test */
public function returnsResultOfFirstInnerResolver(): void
{
$ipAddress = '1.2.3.4';
$firstResolve = $this->firstInnerResolver->resolveIpLocation($ipAddress)->willReturn([]);
$firstResolve = $this->firstInnerResolver->resolveIpLocation($ipAddress)->willReturn(Location::emptyInstance());
$secondResolve = $this->secondInnerResolver->resolveIpLocation($ipAddress)->willThrow(WrongIpException::class);
$this->resolver->resolveIpLocation($ipAddress);
@ -62,15 +59,15 @@ class ChainIpLocationResolverTest extends TestCase
$secondResolve->shouldNotHaveBeenCalled();
}
/**
* @test
*/
public function returnsResultOfSecondInnerResolver()
/** @test */
public function returnsResultOfSecondInnerResolver(): void
{
$ipAddress = '1.2.3.4';
$firstResolve = $this->firstInnerResolver->resolveIpLocation($ipAddress)->willThrow(WrongIpException::class);
$secondResolve = $this->secondInnerResolver->resolveIpLocation($ipAddress)->willReturn([]);
$secondResolve = $this->secondInnerResolver->resolveIpLocation($ipAddress)->willReturn(
Location::emptyInstance()
);
$this->resolver->resolveIpLocation($ipAddress);

View File

@ -5,6 +5,7 @@ namespace ShlinkioTest\Shlink\Common\IpGeolocation;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\IpGeolocation\EmptyIpLocationResolver;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
use function Functional\map;
use function range;
@ -13,16 +14,6 @@ class EmptyIpLocationResolverTest extends TestCase
{
use StringUtilsTrait;
private const EMPTY_RESP = [
'country_code' => '',
'country_name' => '',
'region_name' => '',
'city' => '',
'latitude' => '',
'longitude' => '',
'time_zone' => '',
];
/** @var EmptyIpLocationResolver */
private $resolver;
@ -35,15 +26,15 @@ class EmptyIpLocationResolverTest extends TestCase
* @test
* @dataProvider provideEmptyResponses
*/
public function alwaysReturnsAnEmptyResponse(array $expected, string $ipAddress)
public function alwaysReturnsAnEmptyLocation(string $ipAddress): void
{
$this->assertEquals($expected, $this->resolver->resolveIpLocation($ipAddress));
$this->assertEquals(Location::emptyInstance(), $this->resolver->resolveIpLocation($ipAddress));
}
public function provideEmptyResponses(): array
{
return map(range(0, 5), function () {
return [self::EMPTY_RESP, $this->generateRandomString(10)];
return [$this->generateRandomString(15)];
});
}
}

View File

@ -39,10 +39,8 @@ class DbUpdaterTest extends TestCase
$this->dbUpdater = new DbUpdater($this->httpClient->reveal(), $this->filesystem->reveal(), $this->options);
}
/**
* @test
*/
public function anExceptionIsThrownIfFreshDbCannotBeDownloaded()
/** @test */
public function anExceptionIsThrownIfFreshDbCannotBeDownloaded(): void
{
$request = $this->httpClient->request(Argument::cetera())->willThrow(ClientException::class);
@ -56,10 +54,8 @@ class DbUpdaterTest extends TestCase
$this->dbUpdater->downloadFreshCopy();
}
/**
* @test
*/
public function anExceptionIsThrownIfFreshDbCannotBeExtracted()
/** @test */
public function anExceptionIsThrownIfFreshDbCannotBeExtracted(): void
{
$this->options->tempDir = '__invalid__';
@ -79,7 +75,7 @@ class DbUpdaterTest extends TestCase
* @test
* @dataProvider provideFilesystemExceptions
*/
public function anExceptionIsThrownIfFreshDbCannotBeCopiedToDestination(string $e)
public function anExceptionIsThrownIfFreshDbCannotBeCopiedToDestination(string $e): void
{
$request = $this->httpClient->request(Argument::cetera())->willReturn(new Response());
$copy = $this->filesystem->copy(Argument::cetera())->willThrow($e);
@ -93,18 +89,14 @@ class DbUpdaterTest extends TestCase
$this->dbUpdater->downloadFreshCopy();
}
public function provideFilesystemExceptions(): array
public function provideFilesystemExceptions(): iterable
{
return [
[FilesystemException\FileNotFoundException::class],
[FilesystemException\IOException::class],
];
yield 'file not found' => [FilesystemException\FileNotFoundException::class];
yield 'IO error' => [FilesystemException\IOException::class];
}
/**
* @test
*/
public function noExceptionsAreThrownIfEverythingWorksFine()
/** @test */
public function noExceptionsAreThrownIfEverythingWorksFine(): void
{
$request = $this->httpClient->request(Argument::cetera())->willReturn(new Response());
$copy = $this->filesystem->copy(Argument::cetera())->will(function () {

View File

@ -11,6 +11,7 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\IpGeolocation\GeoLite2LocationResolver;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
class GeoLite2LocationResolverTest extends TestCase
{
@ -29,7 +30,7 @@ class GeoLite2LocationResolverTest extends TestCase
* @test
* @dataProvider provideReaderExceptions
*/
public function exceptionIsThrownIfReaderThrowsException(string $e, string $message)
public function exceptionIsThrownIfReaderThrowsException(string $e, string $message): void
{
$ipAddress = '1.2.3.4';
@ -43,18 +44,14 @@ class GeoLite2LocationResolverTest extends TestCase
$this->resolver->resolveIpLocation($ipAddress);
}
public function provideReaderExceptions(): array
public function provideReaderExceptions(): iterable
{
return [
[AddressNotFoundException::class, 'Provided IP "1.2.3.4" is invalid'],
[InvalidDatabaseException::class, 'Provided GeoLite2 db file is invalid'],
];
yield 'invalid IP address' => [AddressNotFoundException::class, 'Provided IP "1.2.3.4" is invalid'];
yield 'invalid geolite DB' => [InvalidDatabaseException::class, 'Provided GeoLite2 db file is invalid'];
}
/**
* @test
*/
public function resolvedCityIsProperlyMapped()
/** @test */
public function resolvedCityIsProperlyMapped(): void
{
$ipAddress = '1.2.3.4';
$city = new City([]);
@ -63,15 +60,7 @@ class GeoLite2LocationResolverTest extends TestCase
$result = $this->resolver->resolveIpLocation($ipAddress);
$this->assertEquals([
'country_code' => '',
'country_name' => '',
'region_name' => '',
'city' => '',
'latitude' => '',
'longitude' => '',
'time_zone' => '',
], $result);
$this->assertEquals(Location::emptyInstance(), $result);
$cityMethod->shouldHaveBeenCalledOnce();
}
}

View File

@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\IpGeolocation\IpApiLocationResolver;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use function json_encode;
class IpApiLocationResolverTest extends TestCase
@ -25,25 +26,15 @@ class IpApiLocationResolverTest extends TestCase
$this->ipResolver = new IpApiLocationResolver($this->client->reveal());
}
/**
* @test
*/
public function correctIpReturnsDecodedInfo()
/** @test */
public function correctIpReturnsDecodedInfo(): void
{
$actual = [
'countryCode' => 'bar',
'lat' => 5,
'lon' => 10,
];
$expected = [
'country_code' => 'bar',
'country_name' => '',
'region_name' => '',
'city' => '',
'latitude' => 5,
'longitude' => 10,
'time_zone' => '',
];
$expected = new Location('bar', '', '', '', 5, 10, '');
$response = new Response();
$response->getBody()->write(json_encode($actual));
$response->getBody()->rewind();
@ -54,7 +45,7 @@ class IpApiLocationResolverTest extends TestCase
}
/** @test */
public function guzzleExceptionThrowsShlinkException()
public function guzzleExceptionThrowsShlinkException(): void
{
$this->client->get('http://ip-api.com/json/1.2.3.4')->willThrow(new TransferException())
->shouldBeCalledOnce();

View File

@ -5,10 +5,15 @@ namespace ShlinkioTest\Shlink\Common\Logger\Processor;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\Logger\Processor\ExceptionWithNewLineProcessor;
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
use const PHP_EOL;
use function Functional\map;
use function range;
class ExceptionWithNewLineProcessorTest extends TestCase
{
use StringUtilsTrait;
/** @var ExceptionWithNewLineProcessor */
private $processor;
@ -21,44 +26,40 @@ class ExceptionWithNewLineProcessorTest extends TestCase
* @test
* @dataProvider provideNoPlaceholderRecords
*/
public function keepsRecordAsIsWhenNoPlaceholderExists(array $record)
public function keepsRecordAsIsWhenNoPlaceholderExists(array $record): void
{
$this->assertSame($record, ($this->processor)($record));
}
public function provideNoPlaceholderRecords(): array
public function provideNoPlaceholderRecords(): iterable
{
return [
[['message' => 'Hello World']],
[['message' => 'Shlink']],
[['message' => 'Foo bar']],
];
return map(range(1, 5), function () {
return [['message' => $this->generateRandomString()]];
});
}
/**
* @test
* @dataProvider providePlaceholderRecords
*/
public function properlyReplacesExceptionPlaceholderAddingNewLine(array $record, array $expected)
public function properlyReplacesExceptionPlaceholderAddingNewLine(array $record, array $expected): void
{
$this->assertEquals($expected, ($this->processor)($record));
}
public function providePlaceholderRecords(): array
public function providePlaceholderRecords(): iterable
{
return [
[
['message' => 'Hello World with placeholder {e}'],
['message' => 'Hello World with placeholder ' . PHP_EOL . '{e}'],
],
[
['message' => '{e} Shlink'],
['message' => PHP_EOL . '{e} Shlink'],
],
[
['message' => 'Foo {e} bar'],
['message' => 'Foo ' . PHP_EOL . '{e} bar'],
],
yield [
['message' => 'Hello World with placeholder {e}'],
['message' => 'Hello World with placeholder ' . PHP_EOL . '{e}'],
];
yield [
['message' => '{e} Shlink'],
['message' => PHP_EOL . '{e} Shlink'],
];
yield [
['message' => 'Foo {e} bar'],
['message' => 'Foo ' . PHP_EOL . '{e} bar'],
];
}
}

View File

@ -29,9 +29,7 @@ class CloseDbConnectionMiddlewareTest extends TestCase
$this->middleware = new CloseDbConnectionMiddleware($this->em->reveal());
}
/**
* @test
*/
/** @test */
public function connectionIsClosedWhenMiddlewareIsProcessed()
{
$req = new ServerRequest();

View File

@ -18,9 +18,7 @@ class IpAddressMiddlewareFactoryTest extends TestCase
$this->factory = new IpAddressMiddlewareFactory();
}
/**
* @test
*/
/** @test */
public function returnedInstanceIsProperlyConfigured()
{
$instance = $this->factory->__invoke(new ServiceManager(), '');

View File

@ -22,20 +22,16 @@ class LocaleMiddlewareTest extends TestCase
$this->middleware = new LocaleMiddleware($this->translator);
}
/**
* @test
*/
public function whenNoHeaderIsPresentLocaleIsNotChanged()
/** @test */
public function whenNoHeaderIsPresentLocaleIsNotChanged(): void
{
$this->assertEquals('ru', $this->translator->getLocale());
$this->middleware->process(new ServerRequest(), TestUtils::createReqHandlerMock()->reveal());
$this->assertEquals('ru', $this->translator->getLocale());
}
/**
* @test
*/
public function whenTheHeaderIsPresentLocaleIsChanged()
/** @test */
public function whenTheHeaderIsPresentLocaleIsChanged(): void
{
$this->assertEquals('ru', $this->translator->getLocale());
$request = (new ServerRequest())->withHeader('Accept-Language', 'es');
@ -47,7 +43,7 @@ class LocaleMiddlewareTest extends TestCase
* @test
* @dataProvider provideLanguages
*/
public function localeGetsNormalized(string $lang, string $expected)
public function localeGetsNormalized(string $lang, string $expected): void
{
$handler = TestUtils::createReqHandlerMock();
@ -58,12 +54,10 @@ class LocaleMiddlewareTest extends TestCase
$this->assertEquals($expected, $this->translator->getLocale());
}
public function provideLanguages(): array
public function provideLanguages(): iterable
{
return [
['ru', 'ru'],
['es_ES', 'es'],
['en-US', 'en'],
];
yield 'language only' => ['ru', 'ru'];
yield 'country and language with underscore' => ['es_ES', 'es'];
yield 'country and language with dash' => ['en-US', 'en'];
}
}

View File

@ -21,18 +21,14 @@ class PaginableRepositoryAdapterTest extends TestCase
$this->adapter = new PaginableRepositoryAdapter($this->repo->reveal(), 'search', ['foo', 'bar'], 'order');
}
/**
* @test
*/
/** @test */
public function getItemsFallbacksToFindList()
{
$this->repo->findList(10, 5, 'search', ['foo', 'bar'], 'order')->shouldBeCalledOnce();
$this->adapter->getItems(5, 10);
}
/**
* @test
*/
/** @test */
public function countFallbacksToCountList()
{
$this->repo->countList('search', ['foo', 'bar'])->shouldBeCalledOnce();

View File

@ -16,9 +16,7 @@ class PixelResponseTest extends TestCase
$this->resp = new PixelResponse();
}
/**
* @test
*/
/** @test */
public function responseHasGifTypeAndIsNotEmpty()
{
$this->assertEquals('image/gif', $this->resp->getHeaderLine('Content-Type'));

View File

@ -9,9 +9,7 @@ use Shlinkio\Shlink\Common\Response\QrCodeResponse;
class QrCodeResponseTest extends TestCase
{
/**
* @test
*/
/** @test */
public function providedQrCoideIsSetAsBody()
{
$qrCode = new QrCode('Hello');

View File

@ -38,9 +38,7 @@ class PreviewGeneratorTest extends TestCase
]), $this->filesystem->reveal(), 'dir');
}
/**
* @test
*/
/** @test */
public function alreadyProcessedElementsAreNotProcessed()
{
$url = 'http://foo.com';
@ -50,9 +48,7 @@ class PreviewGeneratorTest extends TestCase
$this->assertEquals(sprintf('dir/preview_%s.png', urlencode($url)), $this->generator->generatePreview($url));
}
/**
* @test
*/
/** @test */
public function nonProcessedElementsAreProcessed()
{
$url = 'http://foo.com';

View File

@ -19,9 +19,7 @@ class TranslatorExtensionTest extends TestCase
$this->extension = new TranslatorExtension($this->prophesize(Translator::class)->reveal());
}
/**
* @test
*/
/** @test */
public function properFunctionsAreReturned()
{
$engine = $this->prophesize(Engine::class);

View File

@ -28,10 +28,8 @@ class ChronosDateTimeTypeTest extends TestCase
$this->type = Type::getType(ChronosDateTimeType::CHRONOS_DATETIME);
}
/**
* @test
*/
public function nameIsReturned()
/** @test */
public function nameIsReturned(): void
{
$this->assertEquals(ChronosDateTimeType::CHRONOS_DATETIME, $this->type->getName());
}
@ -40,7 +38,7 @@ class ChronosDateTimeTypeTest extends TestCase
* @test
* @dataProvider provideValues
*/
public function valueIsConverted(?string $value, ?string $expected)
public function valueIsConverted(?string $value, ?string $expected): void
{
$platform = $this->prophesize(AbstractPlatform::class);
$platform->getDateTimeFormatString()->willReturn('Y-m-d H:i:s');
@ -54,20 +52,18 @@ class ChronosDateTimeTypeTest extends TestCase
}
}
public function provideValues(): array
public function provideValues(): iterable
{
return [
[null, null],
['now', Chronos::class],
['2017-01-01', Chronos::class],
];
yield 'null date' => [null, null];
yield 'human friendly date' => ['now', Chronos::class];
yield 'numeric date' => ['2017-01-01', Chronos::class];
}
/**
* @test
* @dataProvider providePhpValues
*/
public function valueIsConvertedToDatabaseFormat(?DateTimeInterface $value, ?string $expected)
public function valueIsConvertedToDatabaseFormat(?DateTimeInterface $value, ?string $expected): void
{
$platform = $this->prophesize(AbstractPlatform::class);
$platform->getDateTimeFormatString()->willReturn('Y-m-d');
@ -75,20 +71,16 @@ class ChronosDateTimeTypeTest extends TestCase
$this->assertEquals($expected, $this->type->convertToDatabaseValue($value, $platform->reveal()));
}
public function providePhpValues(): array
public function providePhpValues(): iterable
{
return [
[null, null],
[new DateTimeImmutable('2017-01-01'), '2017-01-01'],
[Chronos::parse('2017-02-01'), '2017-02-01'],
[new DateTime('2017-03-01'), '2017-03-01'],
];
yield 'null date' => [null, null];
yield 'DateTimeImmutable date' => [new DateTimeImmutable('2017-01-01'), '2017-01-01'];
yield 'Chronos date' => [Chronos::parse('2017-02-01'), '2017-02-01'];
yield 'DateTime date' => [new DateTime('2017-03-01'), '2017-03-01'];
}
/**
* @test
*/
public function exceptionIsThrownIfInvalidValueIsParsedToDatabase()
/** @test */
public function exceptionIsThrownIfInvalidValueIsParsedToDatabase(): void
{
$this->expectException(ConversionException::class);
$this->type->convertToDatabaseValue(new stdClass(), $this->prophesize(AbstractPlatform::class)->reveal());

View File

@ -9,9 +9,7 @@ use Shlinkio\Shlink\Common\Util\DateRange;
class DateRangeTest extends TestCase
{
/**
* @test
*/
/** @test */
public function defaultConstructorSetDatesToNull()
{
$range = new DateRange();
@ -20,9 +18,7 @@ class DateRangeTest extends TestCase
$this->assertTrue($range->isEmpty());
}
/**
* @test
*/
/** @test */
public function providedDatesAreSet()
{
$startDate = Chronos::now();
@ -37,19 +33,20 @@ class DateRangeTest extends TestCase
* @test
* @dataProvider provideDates
*/
public function isConsideredEmptyOnlyIfNoneOfTheDatesIsSet(?Chronos $startDate, ?Chronos $endDate, bool $isEmpty)
{
public function isConsideredEmptyOnlyIfNoneOfTheDatesIsSet(
?Chronos $startDate,
?Chronos $endDate,
bool $isEmpty
): void {
$range = new DateRange($startDate, $endDate);
$this->assertEquals($isEmpty, $range->isEmpty());
}
public function provideDates(): array
public function provideDates(): iterable
{
return [
[null, null, true],
[null, Chronos::now(), false],
[Chronos::now(), null, false],
[Chronos::now(), Chronos::now(), false],
];
yield 'both are null' => [null, null, true];
yield 'start is null' => [null, Chronos::now(), false];
yield 'end is null' => [Chronos::now(), null, false];
yield 'none are null' => [Chronos::now(), Chronos::now(), false];
}
}

View File

@ -5,6 +5,8 @@ namespace ShlinkioTest\Shlink\Common\Util;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
use function Functional\map;
use function range;
use function strlen;
class StringUtilsTraitTest extends TestCase
@ -15,23 +17,19 @@ class StringUtilsTraitTest extends TestCase
* @test
* @dataProvider provideLengths
*/
public function generateRandomStringGeneratesStringOfProvidedLength(int $length)
public function generateRandomStringGeneratesStringOfProvidedLength(int $length): void
{
$this->assertEquals($length, strlen($this->generateRandomString($length)));
}
public function provideLengths(): array
{
return [
[1],
[10],
[15],
];
return map(range(10, 50, 5), function (int $i) {
return [$i];
});
}
/**
* @test
*/
/** @test */
public function generatesUuidV4()
{
$uuidPattern = '/[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}/';

View File

@ -82,7 +82,7 @@ class Visit extends AbstractEntity implements JsonSerializable
{
return [
'referer' => $this->referer,
'date' => isset($this->date) ? $this->date->toAtomString() : null,
'date' => $this->date !== null ? $this->date->toAtomString() : null,
'userAgent' => $this->userAgent,
'visitLocation' => $this->visitLocation,

View File

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Entity;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Core\Visit\Model\VisitLocationInterface;
use function array_key_exists;
class VisitLocation extends AbstractEntity implements VisitLocationInterface
{
@ -24,9 +24,9 @@ class VisitLocation extends AbstractEntity implements VisitLocationInterface
/** @var string */
private $timezone;
public function __construct(array $locationInfo)
public function __construct(Location $location)
{
$this->exchangeArray($locationInfo);
$this->exchangeLocationInfo($location);
}
public function getCountryName(): string
@ -49,32 +49,15 @@ class VisitLocation extends AbstractEntity implements VisitLocationInterface
return $this->cityName ?? '';
}
/**
* Exchange internal values from provided array
*/
private function exchangeArray(array $array): void
private function exchangeLocationInfo(Location $info): void
{
if (array_key_exists('country_code', $array)) {
$this->countryCode = (string) $array['country_code'];
}
if (array_key_exists('country_name', $array)) {
$this->countryName = (string) $array['country_name'];
}
if (array_key_exists('region_name', $array)) {
$this->regionName = (string) $array['region_name'];
}
if (array_key_exists('city', $array)) {
$this->cityName = (string) $array['city'];
}
if (array_key_exists('latitude', $array)) {
$this->latitude = (string) $array['latitude'];
}
if (array_key_exists('longitude', $array)) {
$this->longitude = (string) $array['longitude'];
}
if (array_key_exists('time_zone', $array)) {
$this->timezone = (string) $array['time_zone'];
}
$this->countryCode = $info->countryCode();
$this->countryName = $info->countryName();
$this->regionName = $info->regionName();
$this->cityName = $info->city();
$this->latitude = (string) $info->latitude();
$this->longitude = (string) $info->longitude();
$this->timezone = $info->timeZone();
}
public function jsonSerialize(): array

View File

@ -3,12 +3,12 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Exception;
use Exception;
use Throwable;
use function sprintf;
class InvalidShortCodeException extends RuntimeException
{
public static function fromCharset(string $shortCode, string $charSet, Exception $previous = null): self
public static function fromCharset(string $shortCode, string $charSet, ?Throwable $previous = null): self
{
$code = $previous !== null ? $previous->getCode() : -1;
return new static(

View File

@ -8,7 +8,7 @@ use function sprintf;
class InvalidUrlException extends RuntimeException
{
public static function fromUrl(string $url, Throwable $previous = null)
public static function fromUrl(string $url, Throwable $previous = null): self
{
$code = $previous !== null ? $previous->getCode() : -1;
return new static(sprintf('Provided URL "%s" is not an existing and valid URL', $url), $code, $previous);

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
@ -19,7 +20,7 @@ class VisitService implements VisitServiceInterface
$this->em = $em;
}
public function locateVisits(callable $getGeolocationData, ?callable $locatedVisit = null): void
public function locateUnlocatedVisits(callable $geolocateVisit, ?callable $notifyVisitWithLocation = null): void
{
/** @var VisitRepository $repo */
$repo = $this->em->getRepository(Visit::class);
@ -27,26 +28,27 @@ class VisitService implements VisitServiceInterface
foreach ($results as [$visit]) {
try {
$locationData = $getGeolocationData($visit);
/** @var Location $location */
$location = $geolocateVisit($visit);
} catch (IpCannotBeLocatedException $e) {
// Skip if the visit's IP could not be located
continue;
}
$location = new VisitLocation($locationData);
$this->locateVisit($visit, $location, $locatedVisit);
$location = new VisitLocation($location);
$this->locateVisit($visit, $location, $notifyVisitWithLocation);
}
}
private function locateVisit(Visit $visit, VisitLocation $location, ?callable $locatedVisit): void
private function locateVisit(Visit $visit, VisitLocation $location, ?callable $notifyVisitWithLocation): void
{
$visit->locate($location);
$this->em->persist($visit);
$this->em->flush();
if ($locatedVisit !== null) {
$locatedVisit($location, $visit);
if ($notifyVisitWithLocation !== null) {
$notifyVisitWithLocation($location, $visit);
}
$this->em->clear();

View File

@ -5,5 +5,5 @@ namespace Shlinkio\Shlink\Core\Service;
interface VisitServiceInterface
{
public function locateVisits(callable $getGeolocationData, ?callable $locatedVisit = null): void;
public function locateUnlocatedVisits(callable $geolocateVisit, ?callable $notifyVisitWithLocation = null): void;
}

View File

@ -30,9 +30,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->repo = $this->getEntityManager()->getRepository(ShortUrl::class);
}
/**
* @test
*/
/** @test */
public function findOneByShortCodeReturnsProperData()
{
$foo = new ShortUrl('foo');
@ -62,9 +60,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->assertNull($this->repo->findOneByShortCode($baz->getShortCode()));
}
/**
* @test
*/
/** @test */
public function countListReturnsProperNumberOfResults()
{
$count = 5;
@ -78,9 +74,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->assertEquals($count, $this->repo->countList());
}
/**
* @test
*/
/** @test */
public function findListProperlyFiltersByTagAndSearchTerm()
{
$tag = new Tag('bar');
@ -125,9 +119,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->assertSame($bar, $result[0]);
}
/**
* @test
*/
/** @test */
public function findListProperlyMapsFieldNamesToColumnNamesWhenOrdering()
{
$urls = ['a', 'z', 'c', 'b'];

View File

@ -21,17 +21,13 @@ class TagRepositoryTest extends DatabaseTestCase
$this->repo = $this->getEntityManager()->getRepository(Tag::class);
}
/**
* @test
*/
/** @test */
public function deleteByNameDoesNothingWhenEmptyListIsProvided()
{
$this->assertEquals(0, $this->repo->deleteByName([]));
}
/**
* @test
*/
/** @test */
public function allTagsWhichMatchNameAreDeleted()
{
$names = ['foo', 'bar', 'baz'];

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Repository;
use Cake\Chronos\Chronos;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
@ -29,10 +30,8 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->repo = $this->getEntityManager()->getRepository(Visit::class);
}
/**
* @test
*/
public function findUnlocatedVisitsReturnsProperVisits()
/** @test */
public function findUnlocatedVisitsReturnsProperVisits(): void
{
$shortUrl = new ShortUrl('');
$this->getEntityManager()->persist($shortUrl);
@ -41,7 +40,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$visit = new Visit($shortUrl, Visitor::emptyInstance());
if ($i % 2 === 0) {
$location = new VisitLocation([]);
$location = new VisitLocation(Location::emptyInstance());
$this->getEntityManager()->persist($location);
$visit->locate($location);
}
@ -59,10 +58,8 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->assertEquals(3, $resultsCount);
}
/**
* @test
*/
public function findVisitsByShortCodeReturnsProperData()
/** @test */
public function findVisitsByShortCodeReturnsProperData(): void
{
$shortUrl = new ShortUrl('');
$this->getEntityManager()->persist($shortUrl);
@ -86,10 +83,8 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->assertCount(2, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, 5, 4));
}
/**
* @test
*/
public function countVisitsByShortCodeReturnsProperData()
/** @test */
public function countVisitsByShortCodeReturnsProperData(): void
{
$shortUrl = new ShortUrl('');
$this->getEntityManager()->persist($shortUrl);

View File

@ -37,9 +37,7 @@ class PixelActionTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function imageIsReturned()
{
$shortCode = 'abc123';

View File

@ -6,7 +6,6 @@ namespace ShlinkioTest\Shlink\Core\Action;
use finfo;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Common\Service\PreviewGenerator;
@ -37,9 +36,7 @@ class PreviewActionTest extends TestCase
$this->action = new PreviewAction($this->previewGenerator->reveal(), $this->urlShortener->reveal());
}
/**
* @test
*/
/** @test */
public function invalidShortCodeFallsBackToNextMiddleware()
{
$shortCode = 'abc123';
@ -52,9 +49,7 @@ class PreviewActionTest extends TestCase
$this->action->process((new ServerRequest())->withAttribute('shortCode', $shortCode), $delegate->reveal());
}
/**
* @test
*/
/** @test */
public function correctShortCodeReturnsImageResponse()
{
$shortCode = 'abc123';
@ -73,16 +68,13 @@ class PreviewActionTest extends TestCase
$this->assertEquals((new finfo(FILEINFO_MIME))->file($path), $resp->getHeaderLine('Content-type'));
}
/**
* @test
*/
/** @test */
public function invalidShortCodeExceptionFallsBackToNextMiddleware()
{
$shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(InvalidShortCodeException::class)
->shouldBeCalledOnce();
$delegate = $this->prophesize(RequestHandlerInterface::class);
/** @var MethodProphecy $process */
$process = $delegate->handle(Argument::any())->willReturn(new Response());
$this->action->process(

View File

@ -5,7 +5,6 @@ namespace ShlinkioTest\Shlink\Core\Action;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Common\Response\QrCodeResponse;
@ -35,9 +34,7 @@ class QrCodeActionTest extends TestCase
$this->action = new QrCodeAction($router->reveal(), $this->urlShortener->reveal());
}
/**
* @test
*/
/** @test */
public function aNotFoundShortCodeWillDelegateIntoNextMiddleware()
{
$shortCode = 'abc123';
@ -51,16 +48,13 @@ class QrCodeActionTest extends TestCase
$process->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function anInvalidShortCodeWillReturnNotFoundResponse()
{
$shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(InvalidShortCodeException::class)
->shouldBeCalledOnce();
$delegate = $this->prophesize(RequestHandlerInterface::class);
/** @var MethodProphecy $process */
$process = $delegate->handle(Argument::any())->willReturn(new Response());
$this->action->process((new ServerRequest())->withAttribute('shortCode', $shortCode), $delegate->reveal());
@ -68,9 +62,7 @@ class QrCodeActionTest extends TestCase
$process->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function aCorrectRequestReturnsTheQrCodeResponse()
{
$shortCode = 'abc123';

View File

@ -42,9 +42,7 @@ class RedirectActionTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function redirectionIsPerformedToLongUrl()
{
$shortCode = 'abc123';
@ -63,9 +61,7 @@ class RedirectActionTest extends TestCase
$this->assertEquals($expectedUrl, $response->getHeaderLine('Location'));
}
/**
* @test
*/
/** @test */
public function nextMiddlewareIsInvokedIfLongUrlIsNotFound()
{
$shortCode = 'abc123';
@ -82,9 +78,7 @@ class RedirectActionTest extends TestCase
$handle->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function redirectToCustomUrlIsReturnedIfConfiguredSoAndShortUrlIsNotFound()
{
$shortCode = 'abc123';
@ -107,9 +101,7 @@ class RedirectActionTest extends TestCase
$handle->shouldNotHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function visitIsNotTrackedIfDisableParamIsProvided()
{
$shortCode = 'abc123';

View File

@ -16,9 +16,7 @@ class ConfigProviderTest extends TestCase
$this->configProvider = new ConfigProvider();
}
/**
* @test
*/
/** @test */
public function properConfigIsReturned()
{
$config = $this->configProvider->__invoke();

View File

@ -8,9 +8,7 @@ use Shlinkio\Shlink\Core\Entity\Tag;
class TagTest extends TestCase
{
/**
* @test
*/
/** @test */
public function jsonSerializationOfTagsReturnsItsStringRepresentation()
{
$tag = new Tag('This is my name');

View File

@ -4,20 +4,15 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Entity;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
class VisitLocationTest extends TestCase
{
/**
* @test
*/
public function valuesFoundWhenExchangingArrayAreCastToString()
/** @test */
public function valuesFoundWhenExchangingArrayAreCastToString(): void
{
$payload = [
'latitude' => 1000.7,
'longitude' => -2000.4,
];
$payload = new Location('', '', '', '', 1000.7, -2000.4, '');
$location = new VisitLocation($payload);
$this->assertSame('1000.7', $location->getLatitude());

View File

@ -15,7 +15,7 @@ class VisitTest extends TestCase
* @test
* @dataProvider provideDates
*/
public function isProperlyJsonSerialized(?Chronos $date)
public function isProperlyJsonSerialized(?Chronos $date): void
{
$visit = new Visit(new ShortUrl(''), new Visitor('Chrome', 'some site', '1.2.3.4'), $date);
@ -30,11 +30,9 @@ class VisitTest extends TestCase
], $visit->jsonSerialize());
}
public function provideDates(): array
public function provideDates(): iterable
{
return [
[null],
[Chronos::now()->subDays(10)],
];
yield 'null date' => [null];
yield 'not null date' => [Chronos::now()->subDays(10)];
}
}

View File

@ -4,61 +4,56 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Exception;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException;
use function Functional\map;
use function range;
use function sprintf;
class DeleteShortUrlExceptionTest extends TestCase
{
use StringUtilsTrait;
/**
* @test
* @dataProvider provideMessages
* @dataProvider provideThresholds
*/
public function fromVisitsThresholdGeneratesMessageProperly(
int $threshold,
string $shortCode,
string $expectedMessage
) {
): void {
$e = DeleteShortUrlException::fromVisitsThreshold($threshold, $shortCode);
$this->assertEquals($threshold, $e->getVisitsThreshold());
$this->assertEquals($expectedMessage, $e->getMessage());
$this->assertEquals(0, $e->getCode());
}
public function provideMessages(): array
{
return [
[
50,
'abc123',
'Impossible to delete short URL with short code "abc123" since it has more than "50" visits.',
],
[
33,
'def456',
'Impossible to delete short URL with short code "def456" since it has more than "33" visits.',
],
[
5713,
'foobar',
'Impossible to delete short URL with short code "foobar" since it has more than "5713" visits.',
],
];
$this->assertNull($e->getPrevious());
}
/**
* @test
* @dataProvider provideThresholds
*/
public function visitsThresholdIsProperlyReturned(int $threshold)
public function visitsThresholdIsProperlyReturned(int $threshold): void
{
$e = new DeleteShortUrlException($threshold);
$this->assertEquals($threshold, $e->getVisitsThreshold());
$this->assertEquals('', $e->getMessage());
$this->assertEquals(0, $e->getCode());
$this->assertNull($e->getPrevious());
}
public function provideThresholds(): array
{
return map(range(5, 50, 5), function (int $number) {
return [$number];
$shortCode = $this->generateRandomString(6);
return [$number, $shortCode, sprintf(
'Impossible to delete short URL with short code "%s" since it has more than "%s" visits.',
$shortCode,
$number
)];
});
}
}

View File

@ -14,7 +14,7 @@ class InvalidShortCodeExceptionTest extends TestCase
* @test
* @dataProvider providePrevious
*/
public function properlyCreatesExceptionFromCharset(?Throwable $prev)
public function properlyCreatesExceptionFromCharset(?Throwable $prev): void
{
$e = InvalidShortCodeException::fromCharset('abc123', 'def456', $prev);
@ -23,18 +23,14 @@ class InvalidShortCodeExceptionTest extends TestCase
$this->assertEquals($prev, $e->getPrevious());
}
public function providePrevious(): array
public function providePrevious(): iterable
{
return [
[null],
[new Exception('Previos error', 10)],
];
yield 'null previous' => [null];
yield 'instance previous' => [new Exception('Previous error', 10)];
}
/**
* @test
*/
public function properlyCreatesExceptionFromNotFoundShortCode()
/** @test */
public function properlyCreatesExceptionFromNotFoundShortCode(): void
{
$e = InvalidShortCodeException::fromNotFoundShortCode('abc123');

View File

@ -14,7 +14,7 @@ class InvalidUrlExceptionTest extends TestCase
* @test
* @dataProvider providePrevious
*/
public function properlyCreatesExceptionFromUrl(?Throwable $prev)
public function properlyCreatesExceptionFromUrl(?Throwable $prev): void
{
$e = InvalidUrlException::fromUrl('http://the_url.com', $prev);
@ -23,11 +23,9 @@ class InvalidUrlExceptionTest extends TestCase
$this->assertEquals($prev, $e->getPrevious());
}
public function providePrevious(): array
public function providePrevious(): iterable
{
return [
[null],
[new Exception('Previos error', 10)],
];
yield 'null previous' => [null];
yield 'instance previous' => [new Exception('Previous error', 10)];
}
}

View File

@ -26,9 +26,7 @@ class QrCodeCacheMiddlewareTest extends TestCase
$this->middleware = new QrCodeCacheMiddleware($this->cache);
}
/**
* @test
*/
/** @test */
public function noCachedPathFallsBackToNextMiddleware()
{
$delegate = $this->prophesize(RequestHandlerInterface::class);
@ -39,9 +37,7 @@ class QrCodeCacheMiddlewareTest extends TestCase
$this->assertTrue($this->cache->contains('/foo/bar'));
}
/**
* @test
*/
/** @test */
public function cachedPathReturnsCacheContent()
{
$isCalled = false;

View File

@ -16,32 +16,28 @@ class ShortUrlMetaTest extends TestCase
* @test
* @dataProvider provideInvalidData
*/
public function exceptionIsThrownIfProvidedDataIsInvalid(array $data)
public function exceptionIsThrownIfProvidedDataIsInvalid(array $data): void
{
$this->expectException(ValidationException::class);
ShortUrlMeta::createFromRawData($data);
}
public function provideInvalidData(): array
public function provideInvalidData(): iterable
{
return [
[[
ShortUrlMetaInputFilter::VALID_SINCE => '',
ShortUrlMetaInputFilter::VALID_UNTIL => '',
ShortUrlMetaInputFilter::CUSTOM_SLUG => 'foobar',
ShortUrlMetaInputFilter::MAX_VISITS => 'invalid',
]],
[[
ShortUrlMetaInputFilter::VALID_SINCE => '2017',
ShortUrlMetaInputFilter::MAX_VISITS => 5,
]],
];
yield [[
ShortUrlMetaInputFilter::VALID_SINCE => '',
ShortUrlMetaInputFilter::VALID_UNTIL => '',
ShortUrlMetaInputFilter::CUSTOM_SLUG => 'foobar',
ShortUrlMetaInputFilter::MAX_VISITS => 'invalid',
]];
yield [[
ShortUrlMetaInputFilter::VALID_SINCE => '2017',
ShortUrlMetaInputFilter::MAX_VISITS => 5,
]];
}
/**
* @test
*/
public function properlyCreatedInstanceReturnsValues()
/** @test */
public function properlyCreatedInstanceReturnsValues(): void
{
$meta = ShortUrlMeta::createFromParams(Chronos::parse('2015-01-01')->toAtomString(), null, 'foobar');

View File

@ -5,7 +5,6 @@ namespace ShlinkioTest\Shlink\Core\Response;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Response\NotFoundHandler;
use Zend\Diactoros\Response;
@ -26,17 +25,12 @@ class NotFoundHandlerTest extends TestCase
}
/**
* @param string $expectedResponse
* @param string $accept
* @param int $renderCalls
*
* @test
* @dataProvider provideResponses
*/
public function properResponseTypeIsReturned(string $expectedResponse, string $accept, int $renderCalls)
public function properResponseTypeIsReturned(string $expectedResponse, string $accept, int $renderCalls): void
{
$request = (new ServerRequest())->withHeader('Accept', $accept);
/** @var MethodProphecy $render */
$render = $this->renderer->render(Argument::cetera())->willReturn('');
$resp = $this->delegate->handle($request);
@ -45,13 +39,11 @@ class NotFoundHandlerTest extends TestCase
$render->shouldHaveBeenCalledTimes($renderCalls);
}
public function provideResponses(): array
public function provideResponses(): iterable
{
return [
[Response\JsonResponse::class, 'application/json', 0],
[Response\JsonResponse::class, 'text/json', 0],
[Response\JsonResponse::class, 'application/x-json', 0],
[Response\HtmlResponse::class, 'text/html', 1],
];
yield 'application/json' => [Response\JsonResponse::class, 'application/json', 0];
yield 'text/json' => [Response\JsonResponse::class, 'text/json', 0];
yield 'application/x-json' => [Response\JsonResponse::class, 'application/x-json', 0];
yield 'text/html' => [Response\HtmlResponse::class, 'text/html', 1];
}
}

View File

@ -39,9 +39,7 @@ class DeleteShortUrlServiceTest extends TestCase
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
}
/**
* @test
*/
/** @test */
public function deleteByShortCodeThrowsExceptionWhenThresholdIsReached()
{
$service = $this->createService();
@ -54,9 +52,7 @@ class DeleteShortUrlServiceTest extends TestCase
$service->deleteByShortCode('abc123');
}
/**
* @test
*/
/** @test */
public function deleteByShortCodeDeletesUrlWhenThresholdIsReachedButExplicitlyIgnored()
{
$service = $this->createService();
@ -70,9 +66,7 @@ class DeleteShortUrlServiceTest extends TestCase
$flush->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function deleteByShortCodeDeletesUrlWhenThresholdIsReachedButCheckIsDisabled()
{
$service = $this->createService(false);
@ -86,9 +80,7 @@ class DeleteShortUrlServiceTest extends TestCase
$flush->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function deleteByShortCodeDeletesUrlWhenThresholdIsNotReached()
{
$service = $this->createService(true, 100);

View File

@ -32,9 +32,7 @@ class ShortUrlServiceTest extends TestCase
$this->service = new ShortUrlService($this->em->reveal());
}
/**
* @test
*/
/** @test */
public function listedUrlsAreReturnedFromEntityManager()
{
$list = [
@ -53,9 +51,7 @@ class ShortUrlServiceTest extends TestCase
$this->assertEquals(4, $list->getCurrentItemCount());
}
/**
* @test
*/
/** @test */
public function exceptionIsThrownWhenSettingTagsOnInvalidShortcode()
{
$shortCode = 'abc123';
@ -68,9 +64,7 @@ class ShortUrlServiceTest extends TestCase
$this->service->setTagsByShortCode($shortCode);
}
/**
* @test
*/
/** @test */
public function providedTagsAreGetFromRepoAndSetToTheShortUrl()
{
$shortUrl = $this->prophesize(ShortUrl::class);
@ -89,9 +83,7 @@ class ShortUrlServiceTest extends TestCase
$this->service->setTagsByShortCode($shortCode, ['foo', 'bar']);
}
/**
* @test
*/
/** @test */
public function updateMetadataByShortCodeUpdatesProvidedData()
{
$shortUrl = new ShortUrl('');

View File

@ -7,7 +7,6 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
@ -27,17 +26,13 @@ class TagServiceTest extends TestCase
$this->service = new TagService($this->em->reveal());
}
/**
* @test
*/
/** @test */
public function listTagsDelegatesOnRepository()
{
$expected = [new Tag('foo'), new Tag('bar')];
$repo = $this->prophesize(EntityRepository::class);
/** @var MethodProphecy $find */
$find = $repo->findBy(Argument::cetera())->willReturn($expected);
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
$result = $this->service->listTags();
@ -47,15 +42,11 @@ class TagServiceTest extends TestCase
$getRepo->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function deleteTagsDelegatesOnRepository()
{
$repo = $this->prophesize(TagRepository::class);
/** @var MethodProphecy $delete */
$delete = $repo->deleteByName(['foo', 'bar'])->willReturn(4);
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
$this->service->deleteTags(['foo', 'bar']);
@ -64,19 +55,13 @@ class TagServiceTest extends TestCase
$getRepo->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function createTagsPersistsEntities()
{
$repo = $this->prophesize(TagRepository::class);
/** @var MethodProphecy $find */
$find = $repo->findOneBy(Argument::cetera())->willReturn(new Tag('foo'));
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
/** @var MethodProphecy $persist */
$persist = $this->em->persist(Argument::type(Tag::class))->willReturn(null);
/** @var MethodProphecy $flush */
$flush = $this->em->flush()->willReturn(null);
$result = $this->service->createTags(['foo', 'bar']);
@ -88,15 +73,11 @@ class TagServiceTest extends TestCase
$flush->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function renameInvalidTagThrowsException()
{
$repo = $this->prophesize(TagRepository::class);
/** @var MethodProphecy $find */
$find = $repo->findOneBy(Argument::cetera())->willReturn(null);
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
$find->shouldBeCalled();
@ -106,19 +87,14 @@ class TagServiceTest extends TestCase
$this->service->renameTag('foo', 'bar');
}
/**
* @test
*/
/** @test */
public function renameValidTagChangesItsName()
{
$expected = new Tag('foo');
$repo = $this->prophesize(TagRepository::class);
/** @var MethodProphecy $find */
$find = $repo->findOneBy(Argument::cetera())->willReturn($expected);
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
/** @var MethodProphecy $flush */
$flush = $this->em->flush($expected)->willReturn(null);
$tag = $this->service->renameTag('foo', 'bar');

View File

@ -13,7 +13,6 @@ use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7\Request;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Tag;
@ -60,7 +59,7 @@ class UrlShortenerTest extends TestCase
$this->setUrlShortener(false);
}
public function setUrlShortener(bool $urlValidationEnabled): void
private function setUrlShortener(bool $urlValidationEnabled): void
{
$this->urlShortener = new UrlShortener(
$this->httpClient->reveal(),
@ -69,9 +68,7 @@ class UrlShortenerTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function urlIsProperlyShortened(): void
{
// 10 -> 0Q1Y
@ -119,15 +116,12 @@ class UrlShortenerTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function exceptionIsThrownWhenNonUniqueSlugIsProvided(): void
{
$repo = $this->prophesize(ShortUrlRepository::class);
$countBySlug = $repo->count(['shortCode' => 'custom-slug'])->willReturn(1);
$repo->findOneBy(Argument::cetera())->willReturn(null);
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$countBySlug->shouldBeCalledOnce();
@ -143,7 +137,7 @@ class UrlShortenerTest extends TestCase
/**
* @test
* @dataProvider provideExsitingShortUrls
* @dataProvider provideExistingShortUrls
*/
public function existingShortUrlIsReturnedWhenRequested(
string $url,
@ -162,58 +156,54 @@ class UrlShortenerTest extends TestCase
$getRepo->shouldHaveBeenCalledOnce();
}
public function provideExsitingShortUrls(): array
public function provideExistingShortUrls(): iterable
{
$url = 'http://foo.com';
return [
[$url, [], ShortUrlMeta::createFromRawData(['findIfExists' => true]), new ShortUrl($url)],
[$url, [], ShortUrlMeta::createFromRawData(
['findIfExists' => true, 'customSlug' => 'foo']
), new ShortUrl($url)],
[
$url,
['foo', 'bar'],
ShortUrlMeta::createFromRawData(['findIfExists' => true]),
(new ShortUrl($url))->setTags(new ArrayCollection([new Tag('bar'), new Tag('foo')])),
],
[
$url,
[],
ShortUrlMeta::createFromRawData(['findIfExists' => true, 'maxVisits' => 3]),
new ShortUrl($url, ShortUrlMeta::createFromRawData(['maxVisits' => 3])),
],
[
$url,
[],
ShortUrlMeta::createFromRawData(['findIfExists' => true, 'validSince' => Chronos::parse('2017-01-01')]),
new ShortUrl($url, ShortUrlMeta::createFromRawData(['validSince' => Chronos::parse('2017-01-01')])),
],
[
$url,
[],
ShortUrlMeta::createFromRawData(['findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01')]),
new ShortUrl($url, ShortUrlMeta::createFromRawData(['validUntil' => Chronos::parse('2017-01-01')])),
],
[
$url,
['baz', 'foo', 'bar'],
ShortUrlMeta::createFromRawData([
'findIfExists' => true,
'validUntil' => Chronos::parse('2017-01-01'),
'maxVisits' => 4,
]),
(new ShortUrl($url, ShortUrlMeta::createFromRawData([
'validUntil' => Chronos::parse('2017-01-01'),
'maxVisits' => 4,
])))->setTags(new ArrayCollection([new Tag('foo'), new Tag('bar'), new Tag('baz')])),
],
yield [$url, [], ShortUrlMeta::createFromRawData(['findIfExists' => true]), new ShortUrl($url)];
yield [$url, [], ShortUrlMeta::createFromRawData(
['findIfExists' => true, 'customSlug' => 'foo']
), new ShortUrl($url)];
yield [
$url,
['foo', 'bar'],
ShortUrlMeta::createFromRawData(['findIfExists' => true]),
(new ShortUrl($url))->setTags(new ArrayCollection([new Tag('bar'), new Tag('foo')])),
];
yield [
$url,
[],
ShortUrlMeta::createFromRawData(['findIfExists' => true, 'maxVisits' => 3]),
new ShortUrl($url, ShortUrlMeta::createFromRawData(['maxVisits' => 3])),
];
yield [
$url,
[],
ShortUrlMeta::createFromRawData(['findIfExists' => true, 'validSince' => Chronos::parse('2017-01-01')]),
new ShortUrl($url, ShortUrlMeta::createFromRawData(['validSince' => Chronos::parse('2017-01-01')])),
];
yield [
$url,
[],
ShortUrlMeta::createFromRawData(['findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01')]),
new ShortUrl($url, ShortUrlMeta::createFromRawData(['validUntil' => Chronos::parse('2017-01-01')])),
];
yield [
$url,
['baz', 'foo', 'bar'],
ShortUrlMeta::createFromRawData([
'findIfExists' => true,
'validUntil' => Chronos::parse('2017-01-01'),
'maxVisits' => 4,
]),
(new ShortUrl($url, ShortUrlMeta::createFromRawData([
'validUntil' => Chronos::parse('2017-01-01'),
'maxVisits' => 4,
])))->setTags(new ArrayCollection([new Tag('foo'), new Tag('bar'), new Tag('baz')])),
];
}
/**
* @test
*/
/** @test */
public function shortCodeIsProperlyParsed(): void
{
$shortCode = '12C1c';

View File

@ -7,6 +7,7 @@ use Doctrine\ORM\EntityManager;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
@ -31,10 +32,8 @@ class VisitServiceTest extends TestCase
$this->visitService = new VisitService($this->em->reveal());
}
/**
* @test
*/
public function locateVisitsIteratesAndLocatesUnlocatedVisits()
/** @test */
public function locateVisitsIteratesAndLocatesUnlocatedVisits(): void
{
$unlocatedVisits = [
[new Visit(new ShortUrl('foo'), Visitor::emptyInstance())],
@ -52,8 +51,8 @@ class VisitServiceTest extends TestCase
$clear = $this->em->clear()->will(function () {
});
$this->visitService->locateVisits(function () {
return [];
$this->visitService->locateUnlocatedVisits(function () {
return Location::emptyInstance();
}, function () {
$args = func_get_args();
@ -68,9 +67,7 @@ class VisitServiceTest extends TestCase
$clear->shouldHaveBeenCalledTimes(count($unlocatedVisits));
}
/**
* @test
*/
/** @test */
public function visitsWhichCannotBeLocatedAreIgnored()
{
$unlocatedVisits = [
@ -88,7 +85,7 @@ class VisitServiceTest extends TestCase
$clear = $this->em->clear()->will(function () {
});
$this->visitService->locateVisits(function () {
$this->visitService->locateUnlocatedVisits(function () {
throw new IpCannotBeLocatedException('Cannot be located');
});

View File

@ -31,9 +31,7 @@ class VisitsTrackerTest extends TestCase
$this->visitsTracker = new VisitsTracker($this->em->reveal());
}
/**
* @test
*/
/** @test */
public function trackPersistsVisit()
{
$shortCode = '123ABC';
@ -47,9 +45,7 @@ class VisitsTrackerTest extends TestCase
$this->visitsTracker->track($shortCode, Visitor::emptyInstance());
}
/**
* @test
*/
/** @test */
public function trackedIpAddressGetsObfuscated()
{
$shortCode = '123ABC';
@ -67,9 +63,7 @@ class VisitsTrackerTest extends TestCase
$this->visitsTracker->track($shortCode, new Visitor('', '', '4.3.2.1'));
}
/**
* @test
*/
/** @test */
public function infoReturnsVisistForCertainShortCode()
{
$shortCode = '123ABC';

View File

@ -6,12 +6,12 @@ namespace ShlinkioApiTest\Shlink\Rest\Action;
use Cake\Chronos\Chronos;
use GuzzleHttp\RequestOptions;
use ShlinkioTest\Shlink\Common\ApiTest\ApiTestCase;
use function Functional\map;
use function range;
class CreateShortUrlActionTest extends ApiTestCase
{
/**
* @test
*/
/** @test */
public function createsNewShortUrlWhenOnlyLongUrlIsProvided(): void
{
$expectedKeys = ['shortCode', 'shortUrl', 'longUrl', 'dateCreated', 'visitsCount', 'tags'];
@ -23,9 +23,7 @@ class CreateShortUrlActionTest extends ApiTestCase
}
}
/**
* @test
*/
/** @test */
public function createsNewShortUrlWithCustomSlug(): void
{
[$statusCode, $payload] = $this->createShortUrl(['customSlug' => 'my cool slug']);
@ -34,9 +32,7 @@ class CreateShortUrlActionTest extends ApiTestCase
$this->assertEquals('my-cool-slug', $payload['shortCode']);
}
/**
* @test
*/
/** @test */
public function createsNewShortUrlWithTags(): void
{
[$statusCode, $payload] = $this->createShortUrl(['tags' => ['foo', 'bar', 'baz']]);
@ -65,16 +61,12 @@ class CreateShortUrlActionTest extends ApiTestCase
public function provideMaxVisits(): array
{
return [
[1],
[5],
[3],
];
return map(range(1, 20), function (int $i) {
return [$i];
});
}
/**
* @test
*/
/** @test */
public function createsShortUrlWithValidSince(): void
{
[$statusCode, ['shortCode' => $shortCode]] = $this->createShortUrl([
@ -88,9 +80,7 @@ class CreateShortUrlActionTest extends ApiTestCase
$this->assertEquals(self::STATUS_NOT_FOUND, $lastResp->getStatusCode());
}
/**
* @test
*/
/** @test */
public function createsShortUrlWithValidUntil(): void
{
[$statusCode, ['shortCode' => $shortCode]] = $this->createShortUrl([
@ -110,7 +100,6 @@ class CreateShortUrlActionTest extends ApiTestCase
*/
public function returnsAnExistingShortUrlWhenRequested(array $body): void
{
[$firstStatusCode, ['shortCode' => $firstShortCode]] = $this->createShortUrl($body);
$body['findIfExists'] = true;
@ -121,26 +110,22 @@ class CreateShortUrlActionTest extends ApiTestCase
$this->assertEquals($firstShortCode, $secondShortCode);
}
public function provideMatchingBodies(): array
public function provideMatchingBodies(): iterable
{
$longUrl = 'https://www.alejandrocelaya.com';
return [
'only long URL' => [['longUrl' => $longUrl]],
'long URL and tags' => [['longUrl' => $longUrl, 'tags' => ['boo', 'far']]],
'long URL custom slug' => [['longUrl' => $longUrl, 'customSlug' => 'my cool slug']],
'several params' => [[
'longUrl' => $longUrl,
'tags' => ['boo', 'far'],
'validSince' => Chronos::now()->toAtomString(),
'maxVisits' => 7,
]],
];
yield 'only long URL' => [['longUrl' => $longUrl]];
yield 'long URL and tags' => [['longUrl' => $longUrl, 'tags' => ['boo', 'far']]];
yield 'long URL and custom slug' => [['longUrl' => $longUrl, 'customSlug' => 'my cool slug']];
yield 'several params' => [[
'longUrl' => $longUrl,
'tags' => ['boo', 'far'],
'validSince' => Chronos::now()->toAtomString(),
'maxVisits' => 7,
]];
}
/**
* @test
*/
/** @test */
public function returnsErrorWhenRequestingReturnExistingButCustomSlugIsInUse(): void
{
$longUrl = 'https://www.alejandrocelaya.com';
@ -156,9 +141,7 @@ class CreateShortUrlActionTest extends ApiTestCase
$this->assertEquals(self::STATUS_BAD_REQUEST, $secondStatusCode);
}
/**
* @test
*/
/** @test */
public function createsNewShortUrlIfRequestedToFindButThereIsNoMatch(): void
{
[$firstStatusCode, ['shortCode' => $firstShortCode]] = $this->createShortUrl([

View File

@ -7,9 +7,7 @@ use ShlinkioTest\Shlink\Common\ApiTest\ApiTestCase;
class ListShortUrlsTest extends ApiTestCase
{
/**
* @test
*/
/** @test */
public function shortUrlsAreProperlyListed()
{
$resp = $this->callApiWithKey(self::METHOD_GET, '/short-urls');

View File

@ -12,10 +12,8 @@ use function sprintf;
class AuthenticationTest extends ApiTestCase
{
/**
* @test
*/
public function authorizationErrorIsReturnedIfNoApiKeyIsSent()
/** @test */
public function authorizationErrorIsReturnedIfNoApiKeyIsSent(): void
{
$resp = $this->callApi(self::METHOD_GET, '/short-codes');
['error' => $error, 'message' => $message] = $this->getJsonResponsePayload($resp);
@ -35,7 +33,7 @@ class AuthenticationTest extends ApiTestCase
* @test
* @dataProvider provideInvalidApiKeys
*/
public function apiKeyErrorIsReturnedWhenProvidedApiKeyIsInvalid(string $apiKey)
public function apiKeyErrorIsReturnedWhenProvidedApiKeyIsInvalid(string $apiKey): void
{
$resp = $this->callApi(self::METHOD_GET, '/short-codes', [
'headers' => [
@ -49,12 +47,10 @@ class AuthenticationTest extends ApiTestCase
$this->assertEquals('Provided API key does not exist or is invalid.', $message);
}
public function provideInvalidApiKeys(): array
public function provideInvalidApiKeys(): iterable
{
return [
'key which does not exist' => ['invalid'],
'key which is expired' => ['expired_api_key'],
'key which is disabled' => ['disabled_api_key'],
];
yield 'key which does not exist' => ['invalid'];
yield 'key which is expired' => ['expired_api_key'];
yield 'key which is disabled' => ['disabled_api_key'];
}
}

View File

@ -31,18 +31,14 @@ class AuthenticateActionTest extends TestCase
$this->action = new AuthenticateAction($this->apiKeyService->reveal(), $this->jwtService->reveal());
}
/**
* @test
*/
/** @test */
public function notProvidingAuthDataReturnsError()
{
$resp = $this->action->handle(new ServerRequest());
$this->assertEquals(400, $resp->getStatusCode());
}
/**
* @test
*/
/** @test */
public function properApiKeyReturnsTokenInResponse()
{
$this->apiKeyService->getByKey('foo')->willReturn((new ApiKey())->setId('5'))
@ -58,9 +54,7 @@ class AuthenticateActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), '"token"') > 0);
}
/**
* @test
*/
/** @test */
public function invalidApiKeyReturnsErrorResponse()
{
$this->apiKeyService->getByKey('foo')->willReturn((new ApiKey())->disable())

View File

@ -21,9 +21,7 @@ class HealthActionFactoryTest extends TestCase
$this->factory = new Action\HealthActionFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsCreatedExtractingConnectionFromEntityManager()
{
$em = $this->prophesize(EntityManager::class);

View File

@ -25,9 +25,7 @@ class HealthActionTest extends TestCase
$this->action = new HealthAction($this->conn->reveal(), new AppOptions(['version' => '1.2.3']));
}
/**
* @test
*/
/** @test */
public function passResponseIsReturnedWhenConnectionSucceeds()
{
$ping = $this->conn->ping()->willReturn(true);
@ -47,9 +45,7 @@ class HealthActionTest extends TestCase
$ping->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function failResponseIsReturnedWhenConnectionFails()
{
$ping = $this->conn->ping()->willReturn(false);
@ -69,9 +65,7 @@ class HealthActionTest extends TestCase
$ping->shouldHaveBeenCalledOnce();
}
/**
* @test
*/
/** @test */
public function failResponseIsReturnedWhenConnectionThrowsException()
{
$ping = $this->conn->ping()->willThrow(Exception::class);

View File

@ -34,18 +34,14 @@ class CreateShortUrlActionTest extends TestCase
]);
}
/**
* @test
*/
/** @test */
public function missingLongUrlParamReturnsError()
{
$response = $this->action->handle(new ServerRequest());
$this->assertEquals(400, $response->getStatusCode());
}
/**
* @test
*/
/** @test */
public function properShortcodeConversionReturnsData()
{
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())
@ -62,9 +58,7 @@ class CreateShortUrlActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), 'http://foo.com/abc123') > 0);
}
/**
* @test
*/
/** @test */
public function anInvalidUrlReturnsError()
{
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())
@ -79,9 +73,7 @@ class CreateShortUrlActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_URL_ERROR) > 0);
}
/**
* @test
*/
/** @test */
public function nonUniqueSlugReturnsError()
{
$this->urlShortener->urlToShortCode(
@ -100,9 +92,7 @@ class CreateShortUrlActionTest extends TestCase
$this->assertStringContainsString(RestUtils::INVALID_SLUG_ERROR, (string) $response->getBody());
}
/**
* @test
*/
/** @test */
public function aGenericExceptionWillReturnError()
{
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())

View File

@ -27,10 +27,8 @@ class DeleteShortUrlActionTest extends TestCase
$this->action = new DeleteShortUrlAction($this->service->reveal());
}
/**
* @test
*/
public function emptyResponseIsReturnedIfProperlyDeleted()
/** @test */
public function emptyResponseIsReturnedIfProperlyDeleted(): void
{
$deleteByShortCode = $this->service->deleteByShortCode(Argument::any())->will(function () {
});
@ -45,7 +43,7 @@ class DeleteShortUrlActionTest extends TestCase
* @test
* @dataProvider provideExceptions
*/
public function returnsErrorResponseInCaseOfException(Throwable $e, string $error, int $statusCode)
public function returnsErrorResponseInCaseOfException(Throwable $e, string $error, int $statusCode): void
{
$deleteByShortCode = $this->service->deleteByShortCode(Argument::any())->willThrow($e);
@ -58,11 +56,13 @@ class DeleteShortUrlActionTest extends TestCase
$deleteByShortCode->shouldHaveBeenCalledOnce();
}
public function provideExceptions(): array
public function provideExceptions(): iterable
{
return [
[new Exception\InvalidShortCodeException(), RestUtils::INVALID_SHORTCODE_ERROR, 404],
[new Exception\DeleteShortUrlException(5), RestUtils::INVALID_SHORTCODE_DELETION_ERROR, 400],
yield 'not found' => [new Exception\InvalidShortCodeException(), RestUtils::INVALID_SHORTCODE_ERROR, 404];
yield 'bad request' => [
new Exception\DeleteShortUrlException(5),
RestUtils::INVALID_SHORTCODE_DELETION_ERROR,
400,
];
}
}

View File

@ -27,9 +27,7 @@ class EditShortUrlActionTest extends TestCase
$this->action = new EditShortUrlAction($this->shortUrlService->reveal());
}
/**
* @test
*/
/** @test */
public function invalidDataReturnsError()
{
$request = (new ServerRequest())->withParsedBody([
@ -45,9 +43,7 @@ class EditShortUrlActionTest extends TestCase
$this->assertEquals('Provided data is invalid.', $payload['message']);
}
/**
* @test
*/
/** @test */
public function incorrectShortCodeReturnsError()
{
$request = (new ServerRequest())->withAttribute('shortCode', 'abc123')
@ -68,9 +64,7 @@ class EditShortUrlActionTest extends TestCase
$updateMeta->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function correctShortCodeReturnsSuccess()
{
$request = (new ServerRequest())->withAttribute('shortCode', 'abc123')

View File

@ -24,18 +24,14 @@ class EditShortUrlTagsActionTest extends TestCase
$this->action = new EditShortUrlTagsAction($this->shortUrlService->reveal());
}
/**
* @test
*/
/** @test */
public function notProvidingTagsReturnsError()
{
$response = $this->action->handle((new ServerRequest())->withAttribute('shortCode', 'abc123'));
$this->assertEquals(400, $response->getStatusCode());
}
/**
* @test
*/
/** @test */
public function anInvalidShortCodeReturnsNotFound()
{
$shortCode = 'abc123';
@ -49,9 +45,7 @@ class EditShortUrlTagsActionTest extends TestCase
$this->assertEquals(404, $response->getStatusCode());
}
/**
* @test
*/
/** @test */
public function tagsListIsReturnedIfCorrectShortCodeIsProvided()
{
$shortCode = 'abc123';

View File

@ -28,9 +28,7 @@ class ListShortUrlsActionTest extends TestCase
]);
}
/**
* @test
*/
/** @test */
public function properListReturnsSuccessResponse()
{
$page = 3;
@ -43,9 +41,7 @@ class ListShortUrlsActionTest extends TestCase
$this->assertEquals(200, $response->getStatusCode());
}
/**
* @test
*/
/** @test */
public function anExceptionsReturnsErrorResponse()
{
$page = 3;

View File

@ -28,9 +28,7 @@ class ResolveShortUrlActionTest extends TestCase
$this->action = new ResolveShortUrlAction($this->urlShortener->reveal(), []);
}
/**
* @test
*/
/** @test */
public function incorrectShortCodeReturnsError()
{
$shortCode = 'abc123';
@ -43,9 +41,7 @@ class ResolveShortUrlActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_ARGUMENT_ERROR) > 0);
}
/**
* @test
*/
/** @test */
public function correctShortCodeReturnsSuccess()
{
$shortCode = 'abc123';
@ -59,9 +55,7 @@ class ResolveShortUrlActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), 'http://domain.com/foo/bar') > 0);
}
/**
* @test
*/
/** @test */
public function invalidShortCodeExceptionReturnsError()
{
$shortCode = 'abc123';
@ -74,9 +68,7 @@ class ResolveShortUrlActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_SHORTCODE_ERROR) > 0);
}
/**
* @test
*/
/** @test */
public function unexpectedExceptionWillReturnError()
{
$shortCode = 'abc123';

View File

@ -40,9 +40,7 @@ class SingleStepCreateShortUrlActionTest extends TestCase
);
}
/**
* @test
*/
/** @test */
public function errorResponseIsReturnedIfInvalidApiKeyIsProvided()
{
$request = (new ServerRequest())->withQueryParams(['apiKey' => 'abc123']);
@ -58,9 +56,7 @@ class SingleStepCreateShortUrlActionTest extends TestCase
$findApiKey->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function errorResponseIsReturnedIfNoUrlIsProvided()
{
$request = (new ServerRequest())->withQueryParams(['apiKey' => 'abc123']);
@ -76,9 +72,7 @@ class SingleStepCreateShortUrlActionTest extends TestCase
$findApiKey->shouldHaveBeenCalled();
}
/**
* @test
*/
/** @test */
public function properDataIsPassedWhenGeneratingShortCode()
{
$request = (new ServerRequest())->withQueryParams([

View File

@ -5,7 +5,6 @@ namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
use Shlinkio\Shlink\Rest\Action\Tag\CreateTagsAction;
@ -27,12 +26,10 @@ class CreateTagsActionTest extends TestCase
/**
* @test
* @dataProvider provideTags
* @param array|null $tags
*/
public function processDelegatesIntoService($tags)
public function processDelegatesIntoService(?array $tags): void
{
$request = (new ServerRequest())->withParsedBody(['tags' => $tags]);
/** @var MethodProphecy $deleteTags */
$deleteTags = $this->tagService->createTags($tags ?: [])->willReturn(new ArrayCollection());
$response = $this->action->handle($request);
@ -41,13 +38,11 @@ class CreateTagsActionTest extends TestCase
$deleteTags->shouldHaveBeenCalled();
}
public function provideTags()
public function provideTags(): iterable
{
return [
[['foo', 'bar', 'baz']],
[['some', 'thing']],
[null],
[[]],
];
yield 'three tags' => [['foo', 'bar', 'baz']];
yield 'two tags' => [['some', 'thing']];
yield 'null tags' => [null];
yield 'empty tags' => [[]];
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
use Shlinkio\Shlink\Rest\Action\Tag\DeleteTagsAction;
@ -26,12 +25,10 @@ class DeleteTagsActionTest extends TestCase
/**
* @test
* @dataProvider provideTags
* @param array|null $tags
*/
public function processDelegatesIntoService($tags)
public function processDelegatesIntoService(?array $tags): void
{
$request = (new ServerRequest())->withQueryParams(['tags' => $tags]);
/** @var MethodProphecy $deleteTags */
$deleteTags = $this->tagService->deleteTags($tags ?: []);
$response = $this->action->handle($request);
@ -40,13 +37,11 @@ class DeleteTagsActionTest extends TestCase
$deleteTags->shouldHaveBeenCalled();
}
public function provideTags()
public function provideTags(): iterable
{
return [
[['foo', 'bar', 'baz']],
[['some', 'thing']],
[null],
[[]],
];
yield 'three tags' => [['foo', 'bar', 'baz']];
yield 'two tags' => [['some', 'thing']];
yield 'null tags' => [null];
yield 'empty tags' => [[]];
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
@ -25,12 +24,9 @@ class ListTagsActionTest extends TestCase
$this->action = new ListTagsAction($this->tagService->reveal());
}
/**
* @test
*/
/** @test */
public function returnsDataFromService()
{
/** @var MethodProphecy $listTags */
$listTags = $this->tagService->listTags()->willReturn([new Tag('foo'), new Tag('bar')]);
$resp = $this->action->handle(new ServerRequest());

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\Tag;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
@ -28,9 +27,8 @@ class UpdateTagActionTest extends TestCase
/**
* @test
* @dataProvider provideParams
* @param array $bodyParams
*/
public function whenInvalidParamsAreProvidedAnErrorIsReturned(array $bodyParams)
public function whenInvalidParamsAreProvidedAnErrorIsReturned(array $bodyParams): void
{
$request = (new ServerRequest())->withParsedBody($bodyParams);
$resp = $this->action->handle($request);
@ -38,25 +36,20 @@ class UpdateTagActionTest extends TestCase
$this->assertEquals(400, $resp->getStatusCode());
}
public function provideParams()
public function provideParams(): iterable
{
return [
[['oldName' => 'foo']],
[['newName' => 'foo']],
[[]],
];
yield 'old name only' => [['oldName' => 'foo']];
yield 'new name only' => [['newName' => 'foo']];
yield 'no params' => [[]];
}
/**
* @test
*/
public function requestingInvalidTagReturnsError()
/** @test */
public function requestingInvalidTagReturnsError(): void
{
$request = (new ServerRequest())->withParsedBody([
'oldName' => 'foo',
'newName' => 'bar',
]);
/** @var MethodProphecy $rename */
$rename = $this->tagService->renameTag('foo', 'bar')->willThrow(EntityDoesNotExistException::class);
$resp = $this->action->handle($request);
@ -65,16 +58,13 @@ class UpdateTagActionTest extends TestCase
$rename->shouldHaveBeenCalled();
}
/**
* @test
*/
public function correctInvocationRenamesTag()
/** @test */
public function correctInvocationRenamesTag(): void
{
$request = (new ServerRequest())->withParsedBody([
'oldName' => 'foo',
'newName' => 'bar',
]);
/** @var MethodProphecy $rename */
$rename = $this->tagService->renameTag('foo', 'bar')->willReturn(new Tag('bar'));
$resp = $this->action->handle($request);

View File

@ -29,9 +29,7 @@ class GetVisitsActionTest extends TestCase
$this->action = new GetVisitsAction($this->visitsTracker->reveal());
}
/**
* @test
*/
/** @test */
public function providingCorrectShortCodeReturnsVisits()
{
$shortCode = 'abc123';
@ -43,9 +41,7 @@ class GetVisitsActionTest extends TestCase
$this->assertEquals(200, $response->getStatusCode());
}
/**
* @test
*/
/** @test */
public function providingInvalidShortCodeReturnsError()
{
$shortCode = 'abc123';
@ -57,9 +53,7 @@ class GetVisitsActionTest extends TestCase
$this->assertEquals(404, $response->getStatusCode());
}
/**
* @test
*/
/** @test */
public function paramsAreReadFromQuery()
{
$shortCode = 'abc123';

View File

@ -18,9 +18,7 @@ class AuthenticationPluginManagerFactoryTest extends TestCase
$this->factory = new AuthenticationPluginManagerFactory();
}
/**
* @test
*/
/** @test */
public function serviceIsProperlyCreated()
{
$instance = $this->factory->__invoke(new ServiceManager(['services' => [

View File

@ -25,9 +25,7 @@ class JWTServiceTest extends TestCase
]));
}
/**
* @test
*/
/** @test */
public function tokenIsProperlyCreated()
{
$id = '34';
@ -40,9 +38,7 @@ class JWTServiceTest extends TestCase
$this->assertEquals('ShlinkTest:v10000.3.1', $payload['iss']);
}
/**
* @test
*/
/** @test */
public function refreshIncreasesExpiration()
{
$originalLifetime = 10;
@ -55,25 +51,19 @@ class JWTServiceTest extends TestCase
$this->assertGreaterThan($originalPayload['exp'], $newPayload['exp']);
}
/**
* @test
*/
/** @test */
public function verifyReturnsTrueWhenTheTokenIsCorrect()
{
$this->assertTrue($this->service->verify(JWT::encode([], 'foo')));
}
/**
* @test
*/
/** @test */
public function verifyReturnsFalseWhenTheTokenIsCorrect()
{
$this->assertFalse($this->service->verify('invalidToken'));
}
/**
* @test
*/
/** @test */
public function getPayloadWorksWithCorrectTokens()
{
$originalPayload = [

Some files were not shown because too many files have changed in this diff Show More