mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-27 17:31:20 -06:00
Merge branch 'feature/12' into develop
This commit is contained in:
commit
ce00874dd1
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Shlinkio\Shlink\Common\Expressive\ContentBasedErrorHandler;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
|
||||
use Zend\Expressive\Container\WhoopsErrorHandlerFactory;
|
||||
|
||||
return [
|
||||
|
@ -1,8 +1,5 @@
|
||||
<?php
|
||||
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
|
||||
use Shlinkio\Shlink\Common\Expressive\ContentBasedErrorHandler;
|
||||
use Shlinkio\Shlink\Common\Expressive\ErrorHandlerManager;
|
||||
use Shlinkio\Shlink\Common\Expressive\ErrorHandlerManagerFactory;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
|
||||
use Zend\Expressive;
|
||||
use Zend\Expressive\Container;
|
||||
use Zend\Expressive\Helper;
|
||||
@ -25,8 +22,6 @@ return [
|
||||
Router\FastRouteRouter::class => InvokableFactory::class,
|
||||
|
||||
// View
|
||||
ContentBasedErrorHandler::class => AnnotatedFactory::class,
|
||||
ErrorHandlerManager::class => ErrorHandlerManagerFactory::class,
|
||||
Template\TemplateRendererInterface::class => Twig\TwigRendererFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
|
@ -52,7 +52,7 @@ class GenerateShortcodeCommand extends Command
|
||||
{
|
||||
$this->setName('shortcode:generate')
|
||||
->setDescription(
|
||||
$this->translator->translate('Generates a shortcode for provided URL and returns the short URL')
|
||||
$this->translator->translate('Generates a short code for provided URL and returns the short URL')
|
||||
)
|
||||
->addArgument('longUrl', InputArgument::REQUIRED, $this->translator->translate('The long URL to parse'));
|
||||
}
|
||||
@ -87,8 +87,8 @@ class GenerateShortcodeCommand extends Command
|
||||
return;
|
||||
}
|
||||
|
||||
$shortcode = $this->urlShortener->urlToShortCode(new Uri($longUrl));
|
||||
$shortUrl = (new Uri())->withPath($shortcode)
|
||||
$shortCode = $this->urlShortener->urlToShortCode(new Uri($longUrl));
|
||||
$shortUrl = (new Uri())->withPath($shortCode)
|
||||
->withScheme($this->domainConfig['schema'])
|
||||
->withHost($this->domainConfig['hostname']);
|
||||
|
||||
|
@ -25,7 +25,7 @@ class ApplicationFactory implements FactoryInterface
|
||||
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
|
||||
{
|
||||
$config = $container->get('config')['cli'];
|
||||
$app = new CliApp();
|
||||
$app = new CliApp('Shlink', '1.0.0');
|
||||
|
||||
$commands = isset($config['commands']) ? $config['commands'] : [];
|
||||
foreach ($commands as $command) {
|
||||
|
70
module/CLI/test/Command/GenerateShortcodeCommandTest.php
Normal file
70
module/CLI/test/Command/GenerateShortcodeCommandTest.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\CLI\Command;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\CLI\Command\GenerateShortcodeCommand;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class GenerateShortcodeCommandTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CommandTester
|
||||
*/
|
||||
protected $commandTester;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $urlShortener;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->urlShortener = $this->prophesize(UrlShortener::class);
|
||||
$command = new GenerateShortcodeCommand($this->urlShortener->reveal(), Translator::factory([]), [
|
||||
'schema' => 'http',
|
||||
'hostname' => 'foo.com'
|
||||
]);
|
||||
$app = new Application();
|
||||
$app->add($command);
|
||||
$this->commandTester = new CommandTester($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properShortCodeIsCreatedIfLongUrlIsCorrect()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::any())->willReturn('abc123')
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:generate',
|
||||
'longUrl' => 'http://domain.com/foo/bar'
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertTrue(strpos($output, 'http://foo.com/abc123') > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function exceptionWhileParsingLongUrlOutputsError()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::any())->willThrow(new InvalidUrlException())
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:generate',
|
||||
'longUrl' => 'http://domain.com/invalid'
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertTrue(
|
||||
strpos($output, 'Provided URL "http://domain.com/invalid" is invalid. Try with a different one.') === 0
|
||||
);
|
||||
}
|
||||
}
|
91
module/CLI/test/Command/GetVisitsCommandTest.php
Normal file
91
module/CLI/test/Command/GetVisitsCommandTest.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\CLI\Command;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\CLI\Command\GetVisitsCommand;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class GetVisitsCommandTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CommandTester
|
||||
*/
|
||||
protected $commandTester;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $visitsTracker;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->visitsTracker = $this->prophesize(VisitsTrackerInterface::class);
|
||||
$command = new GetVisitsCommand($this->visitsTracker->reveal(), Translator::factory([]));
|
||||
$app = new Application();
|
||||
$app->add($command);
|
||||
$this->commandTester = new CommandTester($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function noDateFlagsTriesToListWithoutDateRange()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->visitsTracker->info($shortCode, new DateRange(null, null))->willReturn([])
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:visits',
|
||||
'shortCode' => $shortCode,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function providingDateFlagsTheListGetsFiltered()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$startDate = '2016-01-01';
|
||||
$endDate = '2016-02-01';
|
||||
$this->visitsTracker->info($shortCode, new DateRange(new \DateTime($startDate), new \DateTime($endDate)))
|
||||
->willReturn([])
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:visits',
|
||||
'shortCode' => $shortCode,
|
||||
'--startDate' => $startDate,
|
||||
'--endDate' => $endDate,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function outputIsProperlyGenerated()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->visitsTracker->info($shortCode, Argument::any())->willReturn([
|
||||
(new Visit())->setReferer('foo')
|
||||
->setRemoteAddr('1.2.3.4')
|
||||
->setUserAgent('bar'),
|
||||
])->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:visits',
|
||||
'shortCode' => $shortCode,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertTrue(strpos($output, 'foo') > 0);
|
||||
$this->assertTrue(strpos($output, '1.2.3.4') > 0);
|
||||
$this->assertTrue(strpos($output, 'bar') > 0);
|
||||
}
|
||||
}
|
96
module/CLI/test/Command/ProcessVisitsCommandTest.php
Normal file
96
module/CLI/test/Command/ProcessVisitsCommandTest.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\CLI\Command;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\CLI\Command\ProcessVisitsCommand;
|
||||
use Shlinkio\Shlink\Common\Service\IpLocationResolver;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Service\VisitService;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class ProcessVisitsCommandTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CommandTester
|
||||
*/
|
||||
protected $commandTester;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $visitService;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $ipResolver;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->visitService = $this->prophesize(VisitService::class);
|
||||
$this->ipResolver = $this->prophesize(IpLocationResolver::class);
|
||||
$command = new ProcessVisitsCommand(
|
||||
$this->visitService->reveal(),
|
||||
$this->ipResolver->reveal(),
|
||||
Translator::factory([])
|
||||
);
|
||||
$app = new Application();
|
||||
$app->add($command);
|
||||
|
||||
$this->commandTester = new CommandTester($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function allReturnedVisitsIpsAreProcessed()
|
||||
{
|
||||
$visits = [
|
||||
(new Visit())->setRemoteAddr('1.2.3.4'),
|
||||
(new Visit())->setRemoteAddr('4.3.2.1'),
|
||||
(new Visit())->setRemoteAddr('12.34.56.78'),
|
||||
];
|
||||
$this->visitService->getUnlocatedVisits()->willReturn($visits)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->visitService->saveVisit(Argument::any())->shouldBeCalledTimes(count($visits));
|
||||
$this->ipResolver->resolveIpLocation(Argument::any())->willReturn([])
|
||||
->shouldBeCalledTimes(count($visits));
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'visit:process',
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertTrue(strpos($output, 'Processing IP 1.2.3.4') === 0);
|
||||
$this->assertTrue(strpos($output, 'Processing IP 4.3.2.1') > 0);
|
||||
$this->assertTrue(strpos($output, 'Processing IP 12.34.56.78') > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function localhostAddressIsIgnored()
|
||||
{
|
||||
$visits = [
|
||||
(new Visit())->setRemoteAddr('1.2.3.4'),
|
||||
(new Visit())->setRemoteAddr('4.3.2.1'),
|
||||
(new Visit())->setRemoteAddr('12.34.56.78'),
|
||||
(new Visit())->setRemoteAddr('127.0.0.1'),
|
||||
(new Visit())->setRemoteAddr('127.0.0.1'),
|
||||
];
|
||||
$this->visitService->getUnlocatedVisits()->willReturn($visits)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->visitService->saveVisit(Argument::any())->shouldBeCalledTimes(count($visits) - 2);
|
||||
$this->ipResolver->resolveIpLocation(Argument::any())->willReturn([])
|
||||
->shouldBeCalledTimes(count($visits) - 2);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'visit:process',
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertTrue(strpos($output, 'Ignored localhost address') > 0);
|
||||
}
|
||||
}
|
85
module/CLI/test/Command/ResolveUrlCommandTest.php
Normal file
85
module/CLI/test/Command/ResolveUrlCommandTest.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\CLI\Command;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\CLI\Command\ResolveUrlCommand;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class ResolveUrlCommandTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CommandTester
|
||||
*/
|
||||
protected $commandTester;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $urlShortener;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->urlShortener = $this->prophesize(UrlShortener::class);
|
||||
$command = new ResolveUrlCommand($this->urlShortener->reveal(), Translator::factory([]));
|
||||
$app = new Application();
|
||||
$app->add($command);
|
||||
|
||||
$this->commandTester = new CommandTester($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function correctShortCodeResolvesUrl()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$expectedUrl = 'http://domain.com/foo/bar';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($expectedUrl)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:parse',
|
||||
'shortCode' => $shortCode,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertEquals('Long URL: ' . $expectedUrl . PHP_EOL, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function incorrectShortCodeOutputsErrorMessage()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn(null)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:parse',
|
||||
'shortCode' => $shortCode,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertEquals('No URL found for short code "' . $shortCode . '"' . PHP_EOL, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function wrongShortCodeFormatOutputsErrorMessage()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(new InvalidShortCodeException())
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:parse',
|
||||
'shortCode' => $shortCode,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertEquals('Provided short code "' . $shortCode . '" has an invalid format.' . PHP_EOL, $output);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
use Shlinkio\Shlink\Common\Expressive\ContentBasedErrorHandler;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
|
||||
use Zend\Expressive\Container\TemplatedErrorHandlerFactory;
|
||||
use Zend\Stratigility\FinalHandler;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler;
|
||||
use Shlinkio\Shlink\Common\Factory\CacheFactory;
|
||||
use Shlinkio\Shlink\Common\Factory\EntityManagerFactory;
|
||||
use Shlinkio\Shlink\Common\Factory\TranslatorFactory;
|
||||
@ -22,6 +23,9 @@ return [
|
||||
Translator::class => TranslatorFactory::class,
|
||||
TranslatorExtension::class => AnnotatedFactory::class,
|
||||
LocaleMiddleware::class => AnnotatedFactory::class,
|
||||
|
||||
ErrorHandler\ContentBasedErrorHandler::class => AnnotatedFactory::class,
|
||||
ErrorHandler\ErrorHandlerManager::class => ErrorHandler\ErrorHandlerManagerFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
'em' => EntityManager::class,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Shlinkio\Shlink\Common\Expressive;
|
||||
namespace Shlinkio\Shlink\Common\ErrorHandler;
|
||||
|
||||
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
@ -49,6 +49,7 @@ class ContentBasedErrorHandler implements ErrorHandlerInterface
|
||||
*/
|
||||
protected function resolveErrorHandlerFromAcceptHeader(Request $request)
|
||||
{
|
||||
// Try to find an error handler for one of the accepted content types
|
||||
$accepts = $request->hasHeader('Accept') ? $request->getHeaderLine('Accept') : self::DEFAULT_CONTENT;
|
||||
$accepts = explode(',', $accepts);
|
||||
foreach ($accepts as $accept) {
|
||||
@ -59,8 +60,17 @@ class ContentBasedErrorHandler implements ErrorHandlerInterface
|
||||
return $this->errorHandlerManager->get($accept);
|
||||
}
|
||||
|
||||
// If it wasn't possible to find an error handler for accepted content type, use default one if registered
|
||||
if ($this->errorHandlerManager->has(self::DEFAULT_CONTENT)) {
|
||||
return $this->errorHandlerManager->get(self::DEFAULT_CONTENT);
|
||||
}
|
||||
|
||||
// It wasn't possible to find an error handler
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'It wasn\'t possible to find an error handler for '
|
||||
'It wasn\'t possible to find an error handler for ["%s"] content types. '
|
||||
. 'Make sure you have registered at least the default "%s" content type',
|
||||
implode('", "', $accepts),
|
||||
self::DEFAULT_CONTENT
|
||||
));
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Shlinkio\Shlink\Common\Expressive;
|
||||
namespace Shlinkio\Shlink\Common\ErrorHandler;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Shlinkio\Shlink\Common\Expressive;
|
||||
namespace Shlinkio\Shlink\Common\ErrorHandler;
|
||||
|
||||
use Zend\ServiceManager\AbstractPluginManager;
|
||||
use Zend\ServiceManager\Exception\InvalidServiceException;
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Shlinkio\Shlink\Common\Expressive;
|
||||
namespace Shlinkio\Shlink\Common\ErrorHandler;
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Interop\Container\Exception\ContainerException;
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Shlinkio\Shlink\Common\Expressive;
|
||||
namespace Shlinkio\Shlink\Common\ErrorHandler;
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
31
module/Common/test/ConfigProviderTest.php
Normal file
31
module/Common/test/ConfigProviderTest.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\ConfigProvider;
|
||||
|
||||
class ConfigProviderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ConfigProvider
|
||||
*/
|
||||
protected $configProvider;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function configIsReturned()
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
|
||||
$this->assertArrayHasKey('error_handler', $config);
|
||||
$this->assertArrayHasKey('middleware_pipeline', $config);
|
||||
$this->assertArrayHasKey('services', $config);
|
||||
$this->assertArrayHasKey('twig', $config);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\ErrorHandler;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManager;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
|
||||
class ContentBasedErrorHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ContentBasedErrorHandler
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->errorHandler = new ContentBasedErrorHandler(new ErrorHandlerManager(new ServiceManager(), [
|
||||
'factories' => [
|
||||
'text/html' => [$this, 'factory'],
|
||||
'application/json' => [$this, 'factory'],
|
||||
],
|
||||
]));
|
||||
}
|
||||
|
||||
public function factory($container, $name)
|
||||
{
|
||||
return function () use ($name) {
|
||||
return $name;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function correctAcceptHeaderValueInvokesErrorHandler()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept', 'foo/bar,application/json');
|
||||
$result = $this->errorHandler->__invoke($request, new Response());
|
||||
$this->assertEquals('application/json', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function defaultContentTypeIsUsedWhenNoAcceptHeaderisPresent()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals();
|
||||
$result = $this->errorHandler->__invoke($request, new Response());
|
||||
$this->assertEquals('text/html', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function defaultContentTypeIsUsedWhenAcceptedContentIsNotSupported()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept', 'foo/bar,text/xml');
|
||||
$result = $this->errorHandler->__invoke($request, new Response());
|
||||
$this->assertEquals('text/html', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Shlinkio\Shlink\Common\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function ifNoErrorHandlerIsFoundAnExceptionIsThrown()
|
||||
{
|
||||
$this->errorHandler = new ContentBasedErrorHandler(new ErrorHandlerManager(new ServiceManager(), []));
|
||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept', 'foo/bar,text/xml');
|
||||
$result = $this->errorHandler->__invoke($request, new Response());
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\ErrorHandler;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManager;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManagerFactory;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
|
||||
class ErrorHandlerManagerFactoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ErrorHandlerManagerFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->factory = new ErrorHandlerManagerFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function serviceIsCreated()
|
||||
{
|
||||
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||
'config' => [
|
||||
'error_handler' => [
|
||||
'plugins' => [],
|
||||
],
|
||||
],
|
||||
]]), '');
|
||||
$this->assertInstanceOf(ErrorHandlerManager::class, $instance);
|
||||
}
|
||||
}
|
45
module/Common/test/ErrorHandler/ErrorHandlerManagerTest.php
Normal file
45
module/Common/test/ErrorHandler/ErrorHandlerManagerTest.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\ErrorHandler;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManager;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
|
||||
class ErrorHandlerManagerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ErrorHandlerManager
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->pluginManager = new ErrorHandlerManager(new ServiceManager(), [
|
||||
'services' => [
|
||||
'foo' => function () {
|
||||
},
|
||||
],
|
||||
'invokables' => [
|
||||
'invalid' => \stdClass::class,
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function callablesAreReturned()
|
||||
{
|
||||
$instance = $this->pluginManager->get('foo');
|
||||
$this->assertInstanceOf(\Closure::class, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Zend\ServiceManager\Exception\InvalidServiceException
|
||||
*/
|
||||
public function nonCallablesThrowException()
|
||||
{
|
||||
$this->pluginManager->get('invalid');
|
||||
}
|
||||
}
|
31
module/Common/test/Factory/TranslatorFactoryTest.php
Normal file
31
module/Common/test/Factory/TranslatorFactoryTest.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\Factory;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\Factory\TranslatorFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
|
||||
class TranslatorFactoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var TranslatorFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->factory = new TranslatorFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function serviceIsCreated()
|
||||
{
|
||||
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||
'config' => [],
|
||||
]]), '');
|
||||
$this->assertInstanceOf(Translator::class, $instance);
|
||||
}
|
||||
}
|
71
module/Common/test/Middleware/LocaleMiddlewareTest.php
Normal file
71
module/Common/test/Middleware/LocaleMiddlewareTest.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\Middleware;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\Middleware\LocaleMiddleware;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class LocaleMiddlewareTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var LocaleMiddleware
|
||||
*/
|
||||
protected $middleware;
|
||||
/**
|
||||
* @var Translator
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->translator = Translator::factory(['locale' => 'ru']);
|
||||
$this->middleware = new LocaleMiddleware($this->translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function whenNoHeaderIsPresentLocaleIsNotChanged()
|
||||
{
|
||||
$this->assertEquals('ru', $this->translator->getLocale());
|
||||
$this->middleware->__invoke(ServerRequestFactory::fromGlobals(), new Response(), function ($req, $resp) {
|
||||
return $resp;
|
||||
});
|
||||
$this->assertEquals('ru', $this->translator->getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function whenTheHeaderIsPresentLocaleIsChanged()
|
||||
{
|
||||
$this->assertEquals('ru', $this->translator->getLocale());
|
||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'es');
|
||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) {
|
||||
return $resp;
|
||||
});
|
||||
$this->assertEquals('es', $this->translator->getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function localeGetsNormalized()
|
||||
{
|
||||
$this->assertEquals('ru', $this->translator->getLocale());
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'es_ES');
|
||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) {
|
||||
return $resp;
|
||||
});
|
||||
$this->assertEquals('es', $this->translator->getLocale());
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'en-US');
|
||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) {
|
||||
return $resp;
|
||||
});
|
||||
$this->assertEquals('en', $this->translator->getLocale());
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\Paginator;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
|
||||
use Shlinkio\Shlink\Common\Repository\PaginableRepositoryInterface;
|
||||
|
||||
class PaginableRepositoryAdapterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var PaginableRepositoryAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $repo;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->repo = $this->prophesize(PaginableRepositoryInterface::class);
|
||||
$this->adapter = new PaginableRepositoryAdapter($this->repo->reveal(), 'search', 'order');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function getItemsFallbacksToFindList()
|
||||
{
|
||||
$this->repo->findList(10, 5, 'search', 'order')->shouldBeCalledTimes(1);
|
||||
$this->adapter->getItems(5, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function countFallbacksToCountList()
|
||||
{
|
||||
$this->repo->countList('search')->shouldBeCalledTimes(1);
|
||||
$this->adapter->count();
|
||||
}
|
||||
}
|
56
module/Common/test/Service/IpLocationResolverTest.php
Normal file
56
module/Common/test/Service/IpLocationResolverTest.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\Service;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Common\Service\IpLocationResolver;
|
||||
|
||||
class IpLocationResolverTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var IpLocationResolver
|
||||
*/
|
||||
protected $ipResolver;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->client = $this->prophesize(Client::class);
|
||||
$this->ipResolver = new IpLocationResolver($this->client->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function correctIpReturnsDecodedInfo()
|
||||
{
|
||||
$expected = [
|
||||
'foo' => 'bar',
|
||||
'baz' => 'foo',
|
||||
];
|
||||
$response = new Response();
|
||||
$response->getBody()->write(json_encode($expected));
|
||||
$response->getBody()->rewind();
|
||||
|
||||
$this->client->get('http://freegeoip.net/json/1.2.3.4')->willReturn($response)
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->assertEquals($expected, $this->ipResolver->resolveIpLocation('1.2.3.4'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Shlinkio\Shlink\Common\Exception\WrongIpException
|
||||
*/
|
||||
public function guzzleExceptionThrowsShlinkException()
|
||||
{
|
||||
$this->client->get('http://freegeoip.net/json/1.2.3.4')->willThrow(new TransferException())
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->ipResolver->resolveIpLocation('1.2.3.4');
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\Twig\Extension;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Common\Twig\Extension\TranslatorExtension;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class TranslatorExtensionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var TranslatorExtension
|
||||
*/
|
||||
protected $extension;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->translator = $this->prophesize(Translator::class);
|
||||
$this->extension = new TranslatorExtension($this->translator->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function extensionNameIsClassName()
|
||||
{
|
||||
$this->assertEquals(TranslatorExtension::class, $this->extension->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properFunctionsAreReturned()
|
||||
{
|
||||
$funcs = $this->extension->getFunctions();
|
||||
$this->assertCount(2, $funcs);
|
||||
foreach ($funcs as $func) {
|
||||
$this->assertInstanceOf(\Twig_SimpleFunction::class, $func);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function translateFallbacksToTranslator()
|
||||
{
|
||||
$this->translator->translate('foo', 'default', null)->shouldBeCalledTimes(1);
|
||||
$this->extension->translate('foo');
|
||||
|
||||
$this->translator->translate('bar', 'baz', 'en')->shouldBeCalledTimes(1);
|
||||
$this->extension->translate('bar', 'baz', 'en');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function translatePluralFallbacksToTranslator()
|
||||
{
|
||||
$this->translator->translatePlural('foo', 'bar', 'baz', 'default', null)->shouldBeCalledTimes(1);
|
||||
$this->extension->translatePlural('foo', 'bar', 'baz');
|
||||
|
||||
$this->translator->translatePlural('foo', 'bar', 'baz', 'another', 'en')->shouldBeCalledTimes(1);
|
||||
$this->extension->translatePlural('foo', 'bar', 'baz', 'another', 'en');
|
||||
}
|
||||
}
|
32
module/Common/test/Util/DateRangeTest.php
Normal file
32
module/Common/test/Util/DateRangeTest.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Common\Util;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
|
||||
class DateRangeTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function defaultConstructorSetDatesToNull()
|
||||
{
|
||||
$range = new DateRange();
|
||||
$this->assertNull($range->getStartDate());
|
||||
$this->assertNull($range->getEndDate());
|
||||
$this->assertTrue($range->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function providedDatesAreSet()
|
||||
{
|
||||
$startDate = new \DateTime();
|
||||
$endDate = new \DateTime();
|
||||
$range = new DateRange($startDate, $endDate);
|
||||
$this->assertSame($startDate, $range->getStartDate());
|
||||
$this->assertSame($endDate, $range->getEndDate());
|
||||
$this->assertFalse($range->isEmpty());
|
||||
}
|
||||
}
|
@ -70,10 +70,10 @@ class RedirectAction implements MiddlewareInterface
|
||||
// If provided shortCode does not belong to a valid long URL, dispatch next middleware, which will trigger
|
||||
// a not-found error
|
||||
if (! isset($longUrl)) {
|
||||
return $out($request, $response->withStatus(404), 'Not found');
|
||||
return $this->notFoundResponse($request, $response, $out);
|
||||
}
|
||||
|
||||
// Track visit to this shortcode
|
||||
// Track visit to this short code
|
||||
$this->visitTracker->track($shortCode);
|
||||
|
||||
// Return a redirect response to the long URL.
|
||||
@ -81,7 +81,18 @@ class RedirectAction implements MiddlewareInterface
|
||||
return new RedirectResponse($longUrl);
|
||||
} catch (\Exception $e) {
|
||||
// In case of error, dispatch 404 error
|
||||
return $out($request, $response);
|
||||
return $this->notFoundResponse($request, $response, $out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param callable $out
|
||||
* @return Response
|
||||
*/
|
||||
protected function notFoundResponse(Request $request, Response $response, callable $out)
|
||||
{
|
||||
return $out($request, $response->withStatus(404), 'Not Found');
|
||||
}
|
||||
}
|
||||
|
104
module/Core/test/Action/RedirectActionTest.php
Normal file
104
module/Core/test/Action/RedirectActionTest.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Core\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Action\RedirectAction;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use Shlinkio\Shlink\Core\Service\VisitsTracker;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
|
||||
class RedirectActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var RedirectAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $urlShortener;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->urlShortener = $this->prophesize(UrlShortener::class);
|
||||
$visitTracker = $this->prophesize(VisitsTracker::class);
|
||||
$visitTracker->track(Argument::any());
|
||||
$this->action = new RedirectAction($this->urlShortener->reveal(), $visitTracker->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function redirectionIsPerformedToLongUrl()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$expectedUrl = 'http://domain.com/foo/bar';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($expectedUrl)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
|
||||
$this->assertInstanceOf(Response\RedirectResponse::class, $response);
|
||||
$this->assertEquals(302, $response->getStatusCode());
|
||||
$this->assertTrue($response->hasHeader('Location'));
|
||||
$this->assertEquals($expectedUrl, $response->getHeaderLine('Location'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function nextErrorMiddlewareIsInvokedIfLongUrlIsNotFound()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn(null)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$originalResponse = new Response();
|
||||
$test = $this;
|
||||
$this->action->__invoke($request, $originalResponse, function (
|
||||
ServerRequestInterface $req,
|
||||
ResponseInterface $resp,
|
||||
$error
|
||||
) use (
|
||||
$test,
|
||||
$request
|
||||
) {
|
||||
$test->assertSame($request, $req);
|
||||
$test->assertEquals(404, $resp->getStatusCode());
|
||||
$test->assertEquals('Not Found', $error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function nextErrorMiddlewareIsInvokedIfAnExceptionIsThrown()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(\Exception::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$originalResponse = new Response();
|
||||
$test = $this;
|
||||
$this->action->__invoke($request, $originalResponse, function (
|
||||
ServerRequestInterface $req,
|
||||
ResponseInterface $resp,
|
||||
$error
|
||||
) use (
|
||||
$test,
|
||||
$request
|
||||
) {
|
||||
$test->assertSame($request, $req);
|
||||
$test->assertEquals(404, $resp->getStatusCode());
|
||||
$test->assertEquals('Not Found', $error);
|
||||
});
|
||||
}
|
||||
}
|
32
module/Core/test/ConfigProviderTest.php
Normal file
32
module/Core/test/ConfigProviderTest.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Core;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Core\ConfigProvider;
|
||||
|
||||
class ConfigProviderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ConfigProvider
|
||||
*/
|
||||
protected $configProvider;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properConfigIsReturned()
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
|
||||
$this->assertArrayHasKey('routes', $config);
|
||||
$this->assertArrayHasKey('services', $config);
|
||||
$this->assertArrayHasKey('templates', $config);
|
||||
$this->assertArrayHasKey('translator', $config);
|
||||
$this->assertArrayHasKey('zend-expressive', $config);
|
||||
}
|
||||
}
|
49
module/Core/test/Service/VisitServiceTest.php
Normal file
49
module/Core/test/Service/VisitServiceTest.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Core\Service;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Repository\VisitRepository;
|
||||
use Shlinkio\Shlink\Core\Service\VisitService;
|
||||
|
||||
class VisitServiceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var VisitService
|
||||
*/
|
||||
protected $visitService;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->em = $this->prophesize(EntityManager::class);
|
||||
$this->visitService = new VisitService($this->em->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function saveVisitsPersistsProvidedVisit()
|
||||
{
|
||||
$visit = new Visit();
|
||||
$this->em->persist($visit)->shouldBeCalledTimes(1);
|
||||
$this->em->flush()->shouldBeCalledTimes(1);
|
||||
$this->visitService->saveVisit($visit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function getUnlocatedVisitsFallbacksToRepository()
|
||||
{
|
||||
$repo = $this->prophesize(VisitRepository::class);
|
||||
$repo->findUnlocatedVisits()->shouldBeCalledTimes(1);
|
||||
$this->em->getRepository(Visit::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
|
||||
$this->visitService->getUnlocatedVisits();
|
||||
}
|
||||
}
|
@ -5,11 +5,29 @@ use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Repository\VisitRepository;
|
||||
use Shlinkio\Shlink\Core\Service\VisitsTracker;
|
||||
|
||||
class VisitsTrackerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var VisitsTracker
|
||||
*/
|
||||
protected $visitsTracker;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->em = $this->prophesize(EntityManager::class);
|
||||
$this->visitsTracker = new VisitsTracker($this->em->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
@ -19,12 +37,32 @@ class VisitsTrackerTest extends TestCase
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['shortCode' => $shortCode])->willReturn(new ShortUrl());
|
||||
|
||||
$em = $this->prophesize(EntityManager::class);
|
||||
$em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
|
||||
$em->persist(Argument::any())->shouldBeCalledTimes(1);
|
||||
$em->flush()->shouldBeCalledTimes(1);
|
||||
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
|
||||
$this->em->persist(Argument::any())->shouldBeCalledTimes(1);
|
||||
$this->em->flush()->shouldBeCalledTimes(1);
|
||||
|
||||
$visitsTracker = new VisitsTracker($em->reveal());
|
||||
$visitsTracker->track($shortCode);
|
||||
$this->visitsTracker->track($shortCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function infoReturnsVisistForCertainShortCode()
|
||||
{
|
||||
$shortCode = '123ABC';
|
||||
$shortUrl = (new ShortUrl())->setOriginalUrl('http://domain.com/foo/bar');
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['shortCode' => $shortCode])->willReturn($shortUrl);
|
||||
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
|
||||
|
||||
$list = [
|
||||
new Visit(),
|
||||
new Visit(),
|
||||
];
|
||||
$repo2 = $this->prophesize(VisitRepository::class);
|
||||
$repo2->findVisitsByShortUrl($shortUrl, null)->willReturn($list);
|
||||
$this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledTimes(1);
|
||||
|
||||
$this->assertEquals($list, $this->visitsTracker->info($shortCode));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
use Shlinkio\Shlink\Rest\Expressive\JsonErrorHandler;
|
||||
use Shlinkio\Shlink\Rest\ErrorHandler\JsonErrorHandler;
|
||||
|
||||
return [
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace Shlinkio\Shlink\Rest\Expressive;
|
||||
namespace Shlinkio\Shlink\Rest\ErrorHandler;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Shlinkio\Shlink\Common\Expressive\ErrorHandlerInterface;
|
||||
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Expressive\Router\RouteResult;
|
||||
|
75
module/Rest/test/Action/AuthenticateActionTest.php
Normal file
75
module/Rest/test/Action/AuthenticateActionTest.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\RestToken;
|
||||
use Shlinkio\Shlink\Rest\Action\AuthenticateAction;
|
||||
use Shlinkio\Shlink\Rest\Exception\AuthenticationException;
|
||||
use Shlinkio\Shlink\Rest\Service\RestTokenService;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class AuthenticateActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var AuthenticateAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $tokenService;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->tokenService = $this->prophesize(RestTokenService::class);
|
||||
$this->action = new AuthenticateAction($this->tokenService->reveal(), Translator::factory([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notProvidingAuthDataReturnsError()
|
||||
{
|
||||
$resp = $this->action->__invoke(ServerRequestFactory::fromGlobals(), new Response());
|
||||
$this->assertEquals(400, $resp->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properCredentialsReturnTokenInResponse()
|
||||
{
|
||||
$this->tokenService->createToken('foo', 'bar')->willReturn(
|
||||
(new RestToken())->setToken('abc-ABC')
|
||||
)->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'username' => 'foo',
|
||||
'password' => 'bar',
|
||||
]);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$response->getBody()->rewind();
|
||||
$this->assertEquals(['token' => 'abc-ABC'], json_decode($response->getBody()->getContents(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function authenticationExceptionsReturnErrorResponse()
|
||||
{
|
||||
$this->tokenService->createToken('foo', 'bar')->willThrow(new AuthenticationException())
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'username' => 'foo',
|
||||
'password' => 'bar',
|
||||
]);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(401, $response->getStatusCode());
|
||||
}
|
||||
}
|
92
module/Rest/test/Action/CreateShortcodeActionTest.php
Normal file
92
module/Rest/test/Action/CreateShortcodeActionTest.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use Shlinkio\Shlink\Rest\Action\CreateShortcodeAction;
|
||||
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\Diactoros\Uri;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class CreateShortcodeActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CreateShortcodeAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $urlShortener;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->urlShortener = $this->prophesize(UrlShortener::class);
|
||||
$this->action = new CreateShortcodeAction($this->urlShortener->reveal(), Translator::factory([]), [
|
||||
'schema' => 'http',
|
||||
'hostname' => 'foo.com',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function missingLongUrlParamReturnsError()
|
||||
{
|
||||
$response = $this->action->__invoke(ServerRequestFactory::fromGlobals(), new Response());
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properShortcodeConversionReturnsData()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class))->willReturn('abc123')
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
]);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), 'http://foo.com/abc123') > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function anInvalidUrlReturnsError()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class))->willThrow(InvalidUrlException::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
]);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_URL_ERROR) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function aGenericExceptionWillReturnError()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class))->willThrow(\Exception::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
]);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::UNKNOWN_ERROR) > 0);
|
||||
}
|
||||
}
|
99
module/Rest/test/Action/GetVisitsActionTest.php
Normal file
99
module/Rest/test/Action/GetVisitsActionTest.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Service\VisitsTracker;
|
||||
use Shlinkio\Shlink\Rest\Action\GetVisitsAction;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class GetVisitsActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var GetVisitsAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $visitsTracker;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->visitsTracker = $this->prophesize(VisitsTracker::class);
|
||||
$this->action = new GetVisitsAction($this->visitsTracker->reveal(), Translator::factory([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function providingCorrectShortCodeReturnsVisits()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->visitsTracker->info($shortCode, Argument::type(DateRange::class))->willReturn([])
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function providingInvalidShortCodeReturnsError()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->visitsTracker->info($shortCode, Argument::type(DateRange::class))->willThrow(
|
||||
InvalidArgumentException::class
|
||||
)->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function unexpectedExceptionWillReturnError()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->visitsTracker->info($shortCode, Argument::type(DateRange::class))->willThrow(
|
||||
\Exception::class
|
||||
)->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function datesAreReadFromQuery()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->visitsTracker->info($shortCode, new DateRange(null, new \DateTime('2016-01-01 00:00:00')))
|
||||
->willReturn([])
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode)
|
||||
->withQueryParams(['endDate' => '2016-01-01 00:00:00']),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
66
module/Rest/test/Action/ListShortcodesActionTest.php
Normal file
66
module/Rest/test/Action/ListShortcodesActionTest.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlService;
|
||||
use Shlinkio\Shlink\Rest\Action\ListShortcodesAction;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
use Zend\Paginator\Adapter\ArrayAdapter;
|
||||
use Zend\Paginator\Paginator;
|
||||
|
||||
class ListShortcodesActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ListShortcodesAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->service = $this->prophesize(ShortUrlService::class);
|
||||
$this->action = new ListShortcodesAction($this->service->reveal(), Translator::factory([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properListReturnsSuccessResponse()
|
||||
{
|
||||
$page = 3;
|
||||
$this->service->listShortUrls($page)->willReturn(new Paginator(new ArrayAdapter()))
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withQueryParams([
|
||||
'page' => $page,
|
||||
]),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function anExceptionsReturnsErrorResponse()
|
||||
{
|
||||
$page = 3;
|
||||
$this->service->listShortUrls($page)->willThrow(\Exception::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withQueryParams([
|
||||
'page' => $page,
|
||||
]),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
}
|
||||
}
|
90
module/Rest/test/Action/ResolveUrlActionTest.php
Normal file
90
module/Rest/test/Action/ResolveUrlActionTest.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use Shlinkio\Shlink\Rest\Action\ResolveUrlAction;
|
||||
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class ResolveUrlActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ResolveUrlAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $urlShortener;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->urlShortener = $this->prophesize(UrlShortener::class);
|
||||
$this->action = new ResolveUrlAction($this->urlShortener->reveal(), Translator::factory([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function incorrectShortCodeReturnsError()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn(null)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_ARGUMENT_ERROR) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function correctShortCodeReturnsSuccess()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn('http://domain.com/foo/bar')
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), 'http://domain.com/foo/bar') > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function invalidShortCodeExceptionReturnsError()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(InvalidShortCodeException::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_SHORTCODE_ERROR) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function unexpectedExceptionWillReturnError()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(\Exception::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
|
||||
$response = $this->action->__invoke($request, new Response());
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::UNKNOWN_ERROR) > 0);
|
||||
}
|
||||
}
|
33
module/Rest/test/ConfigProviderTest.php
Normal file
33
module/Rest/test/ConfigProviderTest.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Rest\ConfigProvider;
|
||||
|
||||
class ConfigProviderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ConfigProvider
|
||||
*/
|
||||
protected $configProvider;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function properConfigIsReturned()
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
|
||||
$this->assertArrayHasKey('error_handler', $config);
|
||||
$this->assertArrayHasKey('middleware_pipeline', $config);
|
||||
$this->assertArrayHasKey('rest', $config);
|
||||
$this->assertArrayHasKey('routes', $config);
|
||||
$this->assertArrayHasKey('services', $config);
|
||||
$this->assertArrayHasKey('translator', $config);
|
||||
}
|
||||
}
|
79
module/Rest/test/ErrorHandler/JsonErrorHandlerTest.php
Normal file
79
module/Rest/test/ErrorHandler/JsonErrorHandlerTest.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\ErrorHandler;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Rest\ErrorHandler\JsonErrorHandler;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\Expressive\Router\RouteResult;
|
||||
|
||||
class JsonErrorHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var JsonErrorHandler
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->errorHandler = new JsonErrorHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function noMatchedRouteReturnsNotFoundResponse()
|
||||
{
|
||||
$response = $this->errorHandler->__invoke(ServerRequestFactory::fromGlobals(), new Response());
|
||||
$this->assertInstanceOf(Response\JsonResponse::class, $response);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function matchedRouteWithErrorReturnsMethodNotAllowedResponse()
|
||||
{
|
||||
$response = $this->errorHandler->__invoke(
|
||||
ServerRequestFactory::fromGlobals(),
|
||||
(new Response())->withStatus(405),
|
||||
405
|
||||
);
|
||||
$this->assertInstanceOf(Response\JsonResponse::class, $response);
|
||||
$this->assertEquals(405, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function responseWithErrorKeepsStatus()
|
||||
{
|
||||
$response = $this->errorHandler->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('foo', 'bar', [])
|
||||
),
|
||||
(new Response())->withStatus(401),
|
||||
401
|
||||
);
|
||||
$this->assertInstanceOf(Response\JsonResponse::class, $response);
|
||||
$this->assertEquals(401, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function responseWithoutErrorReturnsStatus500()
|
||||
{
|
||||
$response = $this->errorHandler->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('foo', 'bar', [])
|
||||
),
|
||||
(new Response())->withStatus(200),
|
||||
'Some error'
|
||||
);
|
||||
$this->assertInstanceOf(Response\JsonResponse::class, $response);
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Middleware;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\RestToken;
|
||||
use Shlinkio\Shlink\Rest\Middleware\CheckAuthenticationMiddleware;
|
||||
use Shlinkio\Shlink\Rest\Service\RestTokenService;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\Expressive\Router\RouteResult;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class CheckAuthenticationMiddlewareTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CheckAuthenticationMiddleware
|
||||
*/
|
||||
protected $middleware;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $tokenService;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->tokenService = $this->prophesize(RestTokenService::class);
|
||||
$this->middleware = new CheckAuthenticationMiddleware($this->tokenService->reveal(), Translator::factory([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function someWhitelistedSituationsFallbackToNextMiddleware()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals();
|
||||
$response = new Response();
|
||||
$isCalled = false;
|
||||
$this->assertFalse($isCalled);
|
||||
$this->middleware->__invoke($request, $response, function ($req, $resp) use (&$isCalled) {
|
||||
$isCalled = true;
|
||||
});
|
||||
$this->assertTrue($isCalled);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteFailure(['GET'])
|
||||
);
|
||||
$response = new Response();
|
||||
$isCalled = false;
|
||||
$this->assertFalse($isCalled);
|
||||
$this->middleware->__invoke($request, $response, function ($req, $resp) use (&$isCalled) {
|
||||
$isCalled = true;
|
||||
});
|
||||
$this->assertTrue($isCalled);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('rest-authenticate', 'foo', [])
|
||||
);
|
||||
$response = new Response();
|
||||
$isCalled = false;
|
||||
$this->assertFalse($isCalled);
|
||||
$this->middleware->__invoke($request, $response, function ($req, $resp) use (&$isCalled) {
|
||||
$isCalled = true;
|
||||
});
|
||||
$this->assertTrue($isCalled);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('bar', 'foo', [])
|
||||
)->withMethod('OPTIONS');
|
||||
$response = new Response();
|
||||
$isCalled = false;
|
||||
$this->assertFalse($isCalled);
|
||||
$this->middleware->__invoke($request, $response, function ($req, $resp) use (&$isCalled) {
|
||||
$isCalled = true;
|
||||
});
|
||||
$this->assertTrue($isCalled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function noHeaderReturnsError()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('bar', 'foo', [])
|
||||
);
|
||||
$response = $this->middleware->__invoke($request, new Response());
|
||||
$this->assertEquals(401, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function provideAnExpiredTokenReturnsError()
|
||||
{
|
||||
$authToken = 'ABC-abc';
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('bar', 'foo', [])
|
||||
)->withHeader(CheckAuthenticationMiddleware::AUTH_TOKEN_HEADER, $authToken);
|
||||
$this->tokenService->getByToken($authToken)->willReturn(
|
||||
(new RestToken())->setExpirationDate((new \DateTime())->sub(new \DateInterval('P1D')))
|
||||
)->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->middleware->__invoke($request, new Response());
|
||||
$this->assertEquals(401, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function provideCorrectTokenUpdatesExpirationAndFallbacksToNextMiddleware()
|
||||
{
|
||||
$authToken = 'ABC-abc';
|
||||
$restToken = (new RestToken())->setExpirationDate((new \DateTime())->add(new \DateInterval('P1D')));
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(
|
||||
RouteResult::class,
|
||||
RouteResult::fromRouteMatch('bar', 'foo', [])
|
||||
)->withHeader(CheckAuthenticationMiddleware::AUTH_TOKEN_HEADER, $authToken);
|
||||
$this->tokenService->getByToken($authToken)->willReturn($restToken)->shouldBeCalledTimes(1);
|
||||
$this->tokenService->updateExpiration($restToken)->shouldBeCalledTimes(1);
|
||||
|
||||
$isCalled = false;
|
||||
$this->assertFalse($isCalled);
|
||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) use (&$isCalled) {
|
||||
$isCalled = true;
|
||||
});
|
||||
$this->assertTrue($isCalled);
|
||||
}
|
||||
}
|
93
module/Rest/test/Service/RestTokenServiceTest.php
Normal file
93
module/Rest/test/Service/RestTokenServiceTest.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Service;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\RestToken;
|
||||
use Shlinkio\Shlink\Rest\Service\RestTokenService;
|
||||
|
||||
class RestTokenServiceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var RestTokenService
|
||||
*/
|
||||
protected $service;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->em = $this->prophesize(EntityManager::class);
|
||||
$this->service = new RestTokenService($this->em->reveal(), [
|
||||
'username' => 'foo',
|
||||
'password' => 'bar',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function tokenIsCreatedIfCredentialsAreCorrect()
|
||||
{
|
||||
$this->em->persist(Argument::type(RestToken::class))->shouldBeCalledTimes(1);
|
||||
$this->em->flush()->shouldBeCalledTimes(1);
|
||||
|
||||
$token = $this->service->createToken('foo', 'bar');
|
||||
$this->assertInstanceOf(RestToken::class, $token);
|
||||
$this->assertFalse($token->isExpired());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Shlinkio\Shlink\Rest\Exception\AuthenticationException
|
||||
*/
|
||||
public function exceptionIsThrownWhileCreatingTokenWithWrongCredentials()
|
||||
{
|
||||
$this->service->createToken('foo', 'wrong');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function restTokenIsReturnedFromTokenString()
|
||||
{
|
||||
$authToken = 'ABC-abc';
|
||||
$theToken = new RestToken();
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['token' => $authToken])->willReturn($theToken)->shouldBeCalledTimes(1);
|
||||
$this->em->getRepository(RestToken::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
|
||||
|
||||
$this->assertSame($theToken, $this->service->getByToken($authToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Shlinkio\Shlink\Common\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function exceptionIsThrownWhenRequestingWrongToken()
|
||||
{
|
||||
$authToken = 'ABC-abc';
|
||||
$repo = $this->prophesize(EntityRepository::class);
|
||||
$repo->findOneBy(['token' => $authToken])->willReturn(null)->shouldBeCalledTimes(1);
|
||||
$this->em->getRepository(RestToken::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
|
||||
|
||||
$this->service->getByToken($authToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function updateExpirationFlushesEntityManager()
|
||||
{
|
||||
$token = $this->prophesize(RestToken::class);
|
||||
$token->updateExpiration()->shouldBeCalledTimes(1);
|
||||
$this->em->flush()->shouldBeCalledTimes(1);
|
||||
|
||||
$this->service->updateExpiration($token->reveal());
|
||||
}
|
||||
}
|
40
module/Rest/test/Util/RestUtilsTest.php
Normal file
40
module/Rest/test/Util/RestUtilsTest.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Util;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
||||
use Shlinkio\Shlink\Common\Exception\WrongIpException;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||
use Shlinkio\Shlink\Rest\Exception\AuthenticationException;
|
||||
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||
|
||||
class RestUtilsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function correctCodeIsReturnedFromException()
|
||||
{
|
||||
$this->assertEquals(
|
||||
RestUtils::INVALID_SHORTCODE_ERROR,
|
||||
RestUtils::getRestErrorCodeFromException(new InvalidShortCodeException())
|
||||
);
|
||||
$this->assertEquals(
|
||||
RestUtils::INVALID_URL_ERROR,
|
||||
RestUtils::getRestErrorCodeFromException(new InvalidUrlException())
|
||||
);
|
||||
$this->assertEquals(
|
||||
RestUtils::INVALID_ARGUMENT_ERROR,
|
||||
RestUtils::getRestErrorCodeFromException(new InvalidArgumentException())
|
||||
);
|
||||
$this->assertEquals(
|
||||
RestUtils::INVALID_CREDENTIALS_ERROR,
|
||||
RestUtils::getRestErrorCodeFromException(new AuthenticationException())
|
||||
);
|
||||
$this->assertEquals(
|
||||
RestUtils::UNKNOWN_ERROR,
|
||||
RestUtils::getRestErrorCodeFromException(new WrongIpException())
|
||||
);
|
||||
}
|
||||
}
|
@ -20,6 +20,10 @@
|
||||
<directory suffix=".php">./module/Core/src</directory>
|
||||
<directory suffix=".php">./module/Rest/src</directory>
|
||||
<directory suffix=".php">./module/CLI/src</directory>
|
||||
|
||||
<exclude>
|
||||
<directory suffix=".php">./module/Core/src/Repository</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
|
Loading…
Reference in New Issue
Block a user