mirror of
https://github.com/shlinkio/shlink.git
synced 2025-01-01 03:37:08 -06:00
Merge pull request #359 from acelaya/feature/memory-leak
Feature/memory leak
This commit is contained in:
commit
08bd4f131c
@ -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(
|
||||
|
@ -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';
|
||||
|
@ -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()
|
||||
|
@ -25,9 +25,7 @@ class GenerateCharsetCommandTest extends TestCase
|
||||
$this->commandTester = new CommandTester($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function charactersAreGeneratedFromDefault()
|
||||
{
|
||||
$prefix = 'Character set: ';
|
||||
|
@ -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 = [
|
||||
|
@ -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(
|
||||
|
@ -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();
|
||||
|
@ -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()))
|
||||
|
@ -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';
|
||||
|
@ -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([
|
||||
|
@ -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 () {
|
||||
});
|
||||
|
||||
|
@ -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'),
|
||||
|
@ -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([
|
||||
|
@ -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([]);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -16,9 +16,7 @@ class ConfigProviderTest extends TestCase
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function confiIsProperlyReturned()
|
||||
{
|
||||
$config = ($this->configProvider)();
|
||||
|
@ -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([
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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'] ?? '')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,5 +10,5 @@ interface IpLocationResolverInterface
|
||||
/**
|
||||
* @throws WrongIpException
|
||||
*/
|
||||
public function resolveIpLocation(string $ipAddress): array;
|
||||
public function resolveIpLocation(string $ipAddress): Model\Location;
|
||||
}
|
||||
|
80
module/Common/src/IpGeolocation/Model/Location.php
Normal file
80
module/Common/src/IpGeolocation/Model/Location.php
Normal 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;
|
||||
}
|
||||
}
|
@ -16,9 +16,7 @@ class ConfigProviderTest extends TestCase
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function configIsReturned()
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
|
@ -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());
|
||||
|
@ -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');
|
||||
|
@ -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 = [
|
||||
|
@ -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);
|
||||
|
@ -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(), '');
|
||||
|
@ -18,9 +18,7 @@ class EntityManagerFactoryTest extends TestCase
|
||||
$this->factory = new EntityManagerFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function serviceIsCreated()
|
||||
{
|
||||
$sm = new ServiceManager(['services' => [
|
||||
|
@ -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 */
|
||||
|
@ -18,9 +18,7 @@ class TranslatorFactoryTest extends TestCase
|
||||
$this->factory = new TranslatorFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function serviceIsCreated()
|
||||
{
|
||||
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||
|
@ -18,9 +18,7 @@ class ImageBuilderFactoryTest extends TestCase
|
||||
$this->factory = new ImageBuilderFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function serviceIsCreated()
|
||||
{
|
||||
$instance = $this->factory->__invoke(new ServiceManager(), '');
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 () {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,7 @@ class CloseDbConnectionMiddlewareTest extends TestCase
|
||||
$this->middleware = new CloseDbConnectionMiddleware($this->em->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function connectionIsClosedWhenMiddlewareIsProcessed()
|
||||
{
|
||||
$req = new ServerRequest();
|
||||
|
@ -18,9 +18,7 @@ class IpAddressMiddlewareFactoryTest extends TestCase
|
||||
$this->factory = new IpAddressMiddlewareFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function returnedInstanceIsProperlyConfigured()
|
||||
{
|
||||
$instance = $this->factory->__invoke(new ServiceManager(), '');
|
||||
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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'));
|
||||
|
@ -9,9 +9,7 @@ use Shlinkio\Shlink\Common\Response\QrCodeResponse;
|
||||
|
||||
class QrCodeResponseTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function providedQrCoideIsSetAsBody()
|
||||
{
|
||||
$qrCode = new QrCode('Hello');
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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}/';
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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'];
|
||||
|
@ -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'];
|
||||
|
@ -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);
|
||||
|
@ -37,9 +37,7 @@ class PixelActionTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function imageIsReturned()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
|
@ -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(
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -16,9 +16,7 @@ class ConfigProviderTest extends TestCase
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function properConfigIsReturned()
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
|
@ -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');
|
||||
|
@ -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());
|
||||
|
@ -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)];
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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)];
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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('');
|
||||
|
@ -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');
|
||||
|
@ -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';
|
||||
|
@ -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');
|
||||
});
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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([
|
||||
|
@ -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');
|
||||
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -21,9 +21,7 @@ class HealthActionFactoryTest extends TestCase
|
||||
$this->factory = new Action\HealthActionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function serviceIsCreatedExtractingConnectionFromEntityManager()
|
||||
{
|
||||
$em = $this->prophesize(EntityManager::class);
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -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')
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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([
|
||||
|
@ -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' => [[]];
|
||||
}
|
||||
}
|
||||
|
@ -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' => [[]];
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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';
|
||||
|
@ -18,9 +18,7 @@ class AuthenticationPluginManagerFactoryTest extends TestCase
|
||||
$this->factory = new AuthenticationPluginManagerFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
/** @test */
|
||||
public function serviceIsProperlyCreated()
|
||||
{
|
||||
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user