shlink/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php

186 lines
6.7 KiB
PHP
Raw Normal View History

2016-07-30 07:42:09 -05:00
<?php
2017-10-12 03:13:20 -05:00
declare(strict_types=1);
2016-08-15 04:01:55 -05:00
namespace ShlinkioTest\Shlink\CLI\Command\Visit;
2016-07-30 07:42:09 -05:00
2017-03-24 14:34:18 -05:00
use PHPUnit\Framework\TestCase;
2016-07-30 07:42:09 -05:00
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
2019-04-14 02:10:00 -05:00
use Shlinkio\Shlink\CLI\Command\Visit\LocateVisitsCommand;
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;
2016-07-30 07:42:09 -05:00
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\Visitor;
2016-07-30 07:42:09 -05:00
use Shlinkio\Shlink\Core\Service\VisitService;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
2016-07-30 07:42:09 -05:00
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Lock;
use function array_shift;
use function sprintf;
2016-07-30 07:42:09 -05:00
2019-04-14 02:10:00 -05:00
class LocateVisitsCommandTest extends TestCase
2016-07-30 07:42:09 -05:00
{
/** @var CommandTester */
2018-11-12 13:37:30 -06:00
private $commandTester;
/** @var ObjectProphecy */
2018-11-12 13:37:30 -06:00
private $visitService;
/** @var ObjectProphecy */
2018-11-12 13:37:30 -06:00
private $ipResolver;
/** @var ObjectProphecy */
private $locker;
/** @var ObjectProphecy */
private $lock;
2016-07-30 07:42:09 -05:00
2019-02-16 03:53:45 -06:00
public function setUp(): void
2016-07-30 07:42:09 -05:00
{
$this->visitService = $this->prophesize(VisitService::class);
$this->ipResolver = $this->prophesize(IpApiLocationResolver::class);
$this->locker = $this->prophesize(Lock\Factory::class);
$this->lock = $this->prophesize(Lock\LockInterface::class);
$this->lock->acquire()->willReturn(true);
$this->lock->release()->will(function () {
});
$this->locker->createLock(Argument::type('string'))->willReturn($this->lock->reveal());
2019-04-14 02:10:00 -05:00
$command = new LocateVisitsCommand(
2016-07-30 07:42:09 -05:00
$this->visitService->reveal(),
$this->ipResolver->reveal(),
2018-11-18 09:02:52 -06:00
$this->locker->reveal()
2016-07-30 07:42:09 -05:00
);
$app = new Application();
$app->add($command);
$this->commandTester = new CommandTester($command);
}
/** @test */
public function allPendingVisitsAreProcessed(): void
2016-07-30 07:42:09 -05:00
{
$visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4'));
$location = new VisitLocation(Location::emptyInstance());
2019-02-17 06:21:07 -06:00
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
function (array $args) use ($visit, $location) {
$firstCallback = array_shift($args);
$firstCallback($visit);
2016-07-30 07:42:09 -05:00
$secondCallback = array_shift($args);
$secondCallback($location, $visit);
}
);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn(
Location::emptyInstance()
);
2016-07-30 07:42:09 -05:00
$this->commandTester->execute([
'command' => 'visit:process',
]);
$output = $this->commandTester->getDisplay();
2019-02-16 03:53:45 -06:00
$this->assertStringContainsString('Processing IP 1.2.3.0', $output);
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldHaveBeenCalledOnce();
2016-07-30 07:42:09 -05:00
}
/**
* @test
* @dataProvider provideIgnoredAddresses
2016-07-30 07:42:09 -05:00
*/
public function localhostAndEmptyAddressesAreIgnored(?string $address, string $message): void
2016-07-30 07:42:09 -05:00
{
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $address));
$location = new VisitLocation(Location::emptyInstance());
2019-02-17 06:21:07 -06:00
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
function (array $args) use ($visit, $location) {
$firstCallback = array_shift($args);
$firstCallback($visit);
$secondCallback = array_shift($args);
$secondCallback($location, $visit);
}
);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn(
Location::emptyInstance()
);
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
$output = $this->commandTester->getDisplay();
$this->assertStringContainsString($message, $output);
if (empty($address)) {
$this->assertStringNotContainsString('Processing IP', $output);
} else {
$this->assertStringContainsString('Processing IP', $output);
}
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldNotHaveBeenCalled();
}
public function provideIgnoredAddresses(): iterable
{
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'];
}
2016-07-30 07:42:09 -05:00
/** @test */
public function errorWhileLocatingIpIsDisplayed(): void
{
$visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4'));
$location = new VisitLocation(Location::emptyInstance());
2016-07-30 07:42:09 -05:00
2019-02-17 06:21:07 -06:00
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
function (array $args) use ($visit, $location) {
$firstCallback = array_shift($args);
$firstCallback($visit);
$secondCallback = array_shift($args);
$secondCallback($location, $visit);
}
);
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willThrow(WrongIpException::class);
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
$output = $this->commandTester->getDisplay();
$this->assertStringContainsString('An error occurred while locating IP. Skipped', $output);
$locateVisits->shouldHaveBeenCalledOnce();
$resolveIpLocation->shouldHaveBeenCalledOnce();
2016-07-30 07:42:09 -05:00
}
2019-02-17 13:28:34 -06:00
/** @test */
public function noActionIsPerformedIfLockIsAcquired()
{
$this->lock->acquire()->willReturn(false);
2019-02-17 06:21:07 -06:00
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(function () {
});
$resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn([]);
$this->commandTester->execute([
'command' => 'visit:process',
], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
$output = $this->commandTester->getDisplay();
2019-02-16 03:53:45 -06:00
$this->assertStringContainsString(
2019-04-14 02:10:00 -05:00
sprintf('There is already an instance of the "%s" command', LocateVisitsCommand::NAME),
$output
);
$locateVisits->shouldNotHaveBeenCalled();
$resolveIpLocation->shouldNotHaveBeenCalled();
}
2016-07-30 07:42:09 -05:00
}