Merge pull request #252 from acelaya/feature/redirect-not-found

Feature/redirect not found
This commit is contained in:
Alejandro Celaya 2018-11-04 12:19:03 +01:00 committed by GitHub
commit d9d4c8a70c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 236 additions and 125 deletions

View File

@ -26,11 +26,11 @@ install:
script: script:
- mkdir build - mkdir build
- composer ci - composer check
after_success: after_success:
- rm -f build/clover.xml - rm -f build/clover.xml
- vendor/bin/phpcov merge build --clover build/clover.xml - phpdbg -qrr vendor/bin/phpcov merge build --clover build/clover.xml
- wget https://scrutinizer-ci.com/ocular.phar - wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover build/clover.xml - php ocular.phar code-coverage:upload --format=php-clover build/clover.xml

View File

@ -8,7 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
#### Added #### Added
* *Nothing* * [#236](https://github.com/shlinkio/shlink/issues/236) Added option to define a redirection to a custom URL when a user hits an invalid short URL.
It only affects URLs matched as "short URL" where the short code is invalid, not any 404 that happens in the app. For example, a request to the path `/foo/bar` will keep returning a 404.
This new option will be asked by the installer both for new shlink installations and for any previous shlink version which is updated.
#### Changed #### Changed

View File

@ -88,17 +88,15 @@
}, },
"scripts": { "scripts": {
"check": [ "check": [
"@cs",
"@stan",
"@test",
"@infect"
],
"ci": [
"@cs", "@cs",
"@stan", "@stan",
"@test:ci", "@test:ci",
"@infect:ci" "@infect:ci"
], ],
"ci": [
"echo \"This command is DEPRECATED. Use check instead\"",
"@check"
],
"cs": "phpcs", "cs": "phpcs",
"cs:fix": "phpcbf", "cs:fix": "phpcbf",

View File

@ -13,6 +13,10 @@ return [
], ],
'shortcode_chars' => env('SHORTCODE_CHARS', UrlShortener::DEFAULT_CHARS), 'shortcode_chars' => env('SHORTCODE_CHARS', UrlShortener::DEFAULT_CHARS),
'validate_url' => true, 'validate_url' => true,
'not_found_short_url' => [
'enable_redirection' => false,
'redirect_to' => null,
],
], ],
]; ];

View File

@ -16,8 +16,9 @@ return [
'dependencies' => [ 'dependencies' => [
'factories' => [ 'factories' => [
Options\AppOptions::class => Options\AppOptionsFactory::class, Options\AppOptions::class => ConfigAbstractFactory::class,
Options\DeleteShortUrlsOptions::class => Options\DeleteShortUrlsOptionsFactory::class, Options\DeleteShortUrlsOptions::class => ConfigAbstractFactory::class,
Options\NotFoundShortUrlOptions::class => ConfigAbstractFactory::class,
NotFoundHandler::class => ConfigAbstractFactory::class, NotFoundHandler::class => ConfigAbstractFactory::class,
// Services // Services
@ -40,6 +41,10 @@ return [
ConfigAbstractFactory::class => [ ConfigAbstractFactory::class => [
NotFoundHandler::class => [TemplateRendererInterface::class], NotFoundHandler::class => [TemplateRendererInterface::class],
Options\AppOptions::class => ['config.app_options'],
Options\DeleteShortUrlsOptions::class => ['config.delete_short_urls'],
Options\NotFoundShortUrlOptions::class => ['config.url_shortener.not_found_short_url'],
// Services // Services
Service\UrlShortener::class => [ Service\UrlShortener::class => [
'httpClient', 'httpClient',
@ -58,6 +63,7 @@ return [
Service\UrlShortener::class, Service\UrlShortener::class,
Service\VisitsTracker::class, Service\VisitsTracker::class,
Options\AppOptions::class, Options\AppOptions::class,
Options\NotFoundShortUrlOptions::class,
'Logger_Shlink', 'Logger_Shlink',
], ],
Action\PixelAction::class => [ Action\PixelAction::class => [

View File

@ -9,7 +9,6 @@ use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger; use Psr\Log\NullLogger;
use Shlinkio\Shlink\Core\Action\Util\ErrorResponseBuilderTrait;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException; use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException; use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Model\Visitor;
@ -20,8 +19,6 @@ use function array_key_exists;
abstract class AbstractTrackingAction implements MiddlewareInterface abstract class AbstractTrackingAction implements MiddlewareInterface
{ {
use ErrorResponseBuilderTrait;
/** /**
* @var UrlShortenerInterface * @var UrlShortenerInterface
*/ */
@ -74,12 +71,17 @@ abstract class AbstractTrackingAction implements MiddlewareInterface
$this->visitTracker->track($shortCode, Visitor::fromRequest($request)); $this->visitTracker->track($shortCode, Visitor::fromRequest($request));
} }
return $this->createResp($url->getLongUrl()); return $this->createSuccessResp($url->getLongUrl());
} catch (InvalidShortCodeException | EntityDoesNotExistException $e) { } catch (InvalidShortCodeException | EntityDoesNotExistException $e) {
$this->logger->warning('An error occurred while tracking short code. {e}', ['e' => $e]); $this->logger->warning('An error occurred while tracking short code. {e}', ['e' => $e]);
return $this->buildErrorResponse($request, $handler); return $this->createErrorResp($request, $handler);
} }
} }
abstract protected function createResp(string $longUrl): ResponseInterface; abstract protected function createSuccessResp(string $longUrl): ResponseInterface;
abstract protected function createErrorResp(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface;
} }

View File

@ -4,12 +4,21 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Action; namespace Shlinkio\Shlink\Core\Action;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Common\Response\PixelResponse; use Shlinkio\Shlink\Common\Response\PixelResponse;
class PixelAction extends AbstractTrackingAction class PixelAction extends AbstractTrackingAction
{ {
protected function createResp(string $longUrl): ResponseInterface protected function createSuccessResp(string $longUrl): ResponseInterface
{ {
return new PixelResponse(); return new PixelResponse();
} }
protected function createErrorResp(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
return new PixelResponse();
}
} }

View File

@ -4,14 +4,50 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Action; namespace Shlinkio\Shlink\Core\Action;
use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\Action\Util\ErrorResponseBuilderTrait;
use Shlinkio\Shlink\Core\Options;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Zend\Diactoros\Response\RedirectResponse; use Zend\Diactoros\Response\RedirectResponse;
class RedirectAction extends AbstractTrackingAction class RedirectAction extends AbstractTrackingAction
{ {
protected function createResp(string $longUrl): Response use ErrorResponseBuilderTrait;
/**
* @var Options\NotFoundShortUrlOptions
*/
private $notFoundOptions;
public function __construct(
UrlShortenerInterface $urlShortener,
VisitsTrackerInterface $visitTracker,
Options\AppOptions $appOptions,
Options\NotFoundShortUrlOptions $notFoundOptions,
LoggerInterface $logger = null
) {
parent::__construct($urlShortener, $visitTracker, $appOptions, $logger);
$this->notFoundOptions = $notFoundOptions;
}
protected function createSuccessResp(string $longUrl): Response
{ {
// Return a redirect response to the long URL. // Return a redirect response to the long URL.
// Use a temporary redirect to make sure browsers always hit the server for analytics purposes // Use a temporary redirect to make sure browsers always hit the server for analytics purposes
return new RedirectResponse($longUrl); return new RedirectResponse($longUrl);
} }
protected function createErrorResp(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): Response {
if ($this->notFoundOptions->isRedirectionEnabled()) {
return new RedirectResponse($this->notFoundOptions->getRedirectTo());
}
return $this->buildErrorResponse($request, $handler);
}
} }

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Options;
use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;
class AppOptionsFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
* @return object
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$config = $container->has('config') ? $container->get('config') : [];
return new AppOptions($config['app_options'] ?? []);
}
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Options;
use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;
class DeleteShortUrlsOptionsFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
* @return object
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$config = $container->has('config') ? $container->get('config') : [];
return new DeleteShortUrlsOptions($config['delete_short_urls'] ?? []);
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Options;
use Zend\Stdlib\AbstractOptions;
class NotFoundShortUrlOptions extends AbstractOptions
{
/**
* @var bool
*/
private $enableRedirection = false;
/**
* @var string|null
*/
private $redirectTo;
public function isRedirectionEnabled(): bool
{
return $this->enableRedirection;
}
protected function setEnableRedirection(bool $enableRedirection = true): self
{
$this->enableRedirection = $enableRedirection;
return $this;
}
public function getRedirectTo(): string
{
return $this->redirectTo ?? '';
}
protected function setRedirectTo(?string $redirectTo): self
{
$this->redirectTo = $redirectTo;
return $this;
}
}

View File

@ -5,13 +5,12 @@ namespace ShlinkioTest\Shlink\Core\Action;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Prophecy\Argument; use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Action\RedirectAction;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException; use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Options\AppOptions; use Shlinkio\Shlink\Core\Options;
use Shlinkio\Shlink\Core\Service\UrlShortener; use Shlinkio\Shlink\Core\Service\UrlShortener;
use Shlinkio\Shlink\Core\Service\VisitsTracker; use Shlinkio\Shlink\Core\Service\VisitsTracker;
use ShlinkioTest\Shlink\Common\Util\TestUtils; use ShlinkioTest\Shlink\Common\Util\TestUtils;
@ -23,25 +22,31 @@ class RedirectActionTest extends TestCase
/** /**
* @var RedirectAction * @var RedirectAction
*/ */
protected $action; private $action;
/** /**
* @var ObjectProphecy * @var ObjectProphecy
*/ */
protected $urlShortener; private $urlShortener;
/** /**
* @var ObjectProphecy * @var ObjectProphecy
*/ */
protected $visitTracker; private $visitTracker;
/**
* @var Options\NotFoundShortUrlOptions
*/
private $notFoundOptions;
public function setUp() public function setUp()
{ {
$this->urlShortener = $this->prophesize(UrlShortener::class); $this->urlShortener = $this->prophesize(UrlShortener::class);
$this->visitTracker = $this->prophesize(VisitsTracker::class); $this->visitTracker = $this->prophesize(VisitsTracker::class);
$this->notFoundOptions = new Options\NotFoundShortUrlOptions();
$this->action = new RedirectAction( $this->action = new RedirectAction(
$this->urlShortener->reveal(), $this->urlShortener->reveal(),
$this->visitTracker->reveal(), $this->visitTracker->reveal(),
new AppOptions(['disableTrackParam' => 'foobar']) new Options\AppOptions(['disableTrackParam' => 'foobar']),
$this->notFoundOptions
); );
} }
@ -76,14 +81,38 @@ class RedirectActionTest extends TestCase
->shouldBeCalledTimes(1); ->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->shouldNotBeCalled(); $this->visitTracker->track(Argument::cetera())->shouldNotBeCalled();
$delegate = $this->prophesize(RequestHandlerInterface::class); $handler = $this->prophesize(RequestHandlerInterface::class);
/** @var MethodProphecy $process */ $handle = $handler->handle(Argument::any())->willReturn(new Response());
$process = $delegate->handle(Argument::any())->willReturn(new Response());
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode); $request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
$this->action->process($request, $delegate->reveal()); $this->action->process($request, $handler->reveal());
$process->shouldHaveBeenCalledTimes(1); $handle->shouldHaveBeenCalledTimes(1);
}
/**
* @test
*/
public function redirectToCustomUrlIsReturnedIfConfiguredSoAndShortUrlIsNotFound()
{
$shortCode = 'abc123';
$shortCodeToUrl = $this->urlShortener->shortCodeToUrl($shortCode)->willThrow(
EntityDoesNotExistException::class
);
$handler = $this->prophesize(RequestHandlerInterface::class);
$handle = $handler->handle(Argument::any())->willReturn(new Response());
$this->notFoundOptions->enableRedirection = true;
$this->notFoundOptions->redirectTo = 'https://shlink.io';
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
$resp = $this->action->process($request, $handler->reveal());
$this->assertEquals(302, $resp->getStatusCode());
$this->assertEquals('https://shlink.io', $resp->getHeaderLine('Location'));
$shortCodeToUrl->shouldHaveBeenCalledTimes(1);
$handle->shouldNotHaveBeenCalled();
} }
/** /**

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Options;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Options\AppOptions;
use Shlinkio\Shlink\Core\Options\AppOptionsFactory;
use Zend\ServiceManager\ServiceManager;
class AppOptionsFactoryTest extends TestCase
{
/**
* @var AppOptionsFactory
*/
protected $factory;
public function setUp()
{
$this->factory = new AppOptionsFactory();
}
/**
* @test
*/
public function serviceIsCreated()
{
$instance = $this->factory->__invoke(new ServiceManager([]), '');
$this->assertInstanceOf(AppOptions::class, $instance);
}
}

View File

@ -19,11 +19,15 @@ class UrlShortenerConfigCustomizer implements ConfigCustomizerInterface
public const HOSTNAME = 'HOSTNAME'; public const HOSTNAME = 'HOSTNAME';
public const CHARS = 'CHARS'; public const CHARS = 'CHARS';
public const VALIDATE_URL = 'VALIDATE_URL'; public const VALIDATE_URL = 'VALIDATE_URL';
public const ENABLE_NOT_FOUND_REDIRECTION = 'ENABLE_NOT_FOUND_REDIRECTION';
public const NOT_FOUND_REDIRECT_TO = 'NOT_FOUND_REDIRECT_TO';
private const EXPECTED_KEYS = [ private const EXPECTED_KEYS = [
self::SCHEMA, self::SCHEMA,
self::HOSTNAME, self::HOSTNAME,
self::CHARS, self::CHARS,
self::VALIDATE_URL, self::VALIDATE_URL,
self::ENABLE_NOT_FOUND_REDIRECTION,
self::NOT_FOUND_REDIRECT_TO,
]; ];
public function process(SymfonyStyle $io, CustomizableAppConfig $appConfig): void public function process(SymfonyStyle $io, CustomizableAppConfig $appConfig): void
@ -38,6 +42,11 @@ class UrlShortenerConfigCustomizer implements ConfigCustomizerInterface
$io->title('URL SHORTENER'); $io->title('URL SHORTENER');
foreach ($keysToAskFor as $key) { foreach ($keysToAskFor as $key) {
// Skip not found redirect URL when the user decided not to redirect
if ($key === self::NOT_FOUND_REDIRECT_TO && ! $urlShortener[self::ENABLE_NOT_FOUND_REDIRECTION]) {
continue;
}
$urlShortener[$key] = $this->ask($io, $key); $urlShortener[$key] = $this->ask($io, $key);
} }
$appConfig->setUrlShortener($urlShortener); $appConfig->setUrlShortener($urlShortener);
@ -60,6 +69,18 @@ class UrlShortenerConfigCustomizer implements ConfigCustomizerInterface
) ?: str_shuffle(UrlShortener::DEFAULT_CHARS); ) ?: str_shuffle(UrlShortener::DEFAULT_CHARS);
case self::VALIDATE_URL: case self::VALIDATE_URL:
return $io->confirm('Do you want to validate long urls by 200 HTTP status code on response'); return $io->confirm('Do you want to validate long urls by 200 HTTP status code on response');
case self::ENABLE_NOT_FOUND_REDIRECTION:
return $io->confirm(
'Do you want to enable a redirection to a custom URL when a user hits an invalid short URL? ' .
'(If not enabled, the user will see a default "404 not found" page)',
false
);
case self::NOT_FOUND_REDIRECT_TO:
return $this->askRequired(
$io,
'redirect URL',
'Custom URL to redirect to when a user hits an invalid short URL'
);
} }
return ''; return '';

View File

@ -146,6 +146,16 @@ final class CustomizableAppConfig implements ArraySerializableInterface
UrlShortenerConfigCustomizer::HOSTNAME => ['url_shortener', 'domain', 'hostname'], UrlShortenerConfigCustomizer::HOSTNAME => ['url_shortener', 'domain', 'hostname'],
UrlShortenerConfigCustomizer::CHARS => ['url_shortener', 'shortcode_chars'], UrlShortenerConfigCustomizer::CHARS => ['url_shortener', 'shortcode_chars'],
UrlShortenerConfigCustomizer::VALIDATE_URL => ['url_shortener', 'validate_url'], UrlShortenerConfigCustomizer::VALIDATE_URL => ['url_shortener', 'validate_url'],
UrlShortenerConfigCustomizer::ENABLE_NOT_FOUND_REDIRECTION => [
'url_shortener',
'not_found_short_url',
'enable_redirection',
],
UrlShortenerConfigCustomizer::NOT_FOUND_REDIRECT_TO => [
'url_shortener',
'not_found_short_url',
'redirect_to',
],
], $pathCollection)); ], $pathCollection));
} }
@ -191,6 +201,11 @@ final class CustomizableAppConfig implements ArraySerializableInterface
], ],
'shortcode_chars' => $this->urlShortener[UrlShortenerConfigCustomizer::CHARS] ?? '', 'shortcode_chars' => $this->urlShortener[UrlShortenerConfigCustomizer::CHARS] ?? '',
'validate_url' => $this->urlShortener[UrlShortenerConfigCustomizer::VALIDATE_URL] ?? true, 'validate_url' => $this->urlShortener[UrlShortenerConfigCustomizer::VALIDATE_URL] ?? true,
'not_found_short_url' => [
'enable_redirection' =>
$this->urlShortener[UrlShortenerConfigCustomizer::ENABLE_NOT_FOUND_REDIRECTION] ?? false,
'redirect_to' => $this->urlShortener[UrlShortenerConfigCustomizer::NOT_FOUND_REDIRECT_TO] ?? null,
],
], ],
]; ];

View File

@ -46,10 +46,12 @@ class UrlShortenerConfigCustomizerTest extends TestCase
'HOSTNAME' => 'asked', 'HOSTNAME' => 'asked',
'CHARS' => 'asked', 'CHARS' => 'asked',
'VALIDATE_URL' => true, 'VALIDATE_URL' => true,
'ENABLE_NOT_FOUND_REDIRECTION' => true,
'NOT_FOUND_REDIRECT_TO' => 'asked',
], $config->getUrlShortener()); ], $config->getUrlShortener());
$ask->shouldHaveBeenCalledTimes(2); $ask->shouldHaveBeenCalledTimes(3);
$choice->shouldHaveBeenCalledTimes(1); $choice->shouldHaveBeenCalledTimes(1);
$confirm->shouldHaveBeenCalledTimes(1); $confirm->shouldHaveBeenCalledTimes(2);
} }
/** /**
@ -64,6 +66,8 @@ class UrlShortenerConfigCustomizerTest extends TestCase
$config->setUrlShortener([ $config->setUrlShortener([
'SCHEMA' => 'foo', 'SCHEMA' => 'foo',
'HOSTNAME' => 'foo', 'HOSTNAME' => 'foo',
'ENABLE_NOT_FOUND_REDIRECTION' => true,
'NOT_FOUND_REDIRECT_TO' => 'foo',
]); ]);
$this->plugin->process($this->io->reveal(), $config); $this->plugin->process($this->io->reveal(), $config);
@ -73,6 +77,8 @@ class UrlShortenerConfigCustomizerTest extends TestCase
'HOSTNAME' => 'foo', 'HOSTNAME' => 'foo',
'CHARS' => 'asked', 'CHARS' => 'asked',
'VALIDATE_URL' => false, 'VALIDATE_URL' => false,
'ENABLE_NOT_FOUND_REDIRECTION' => true,
'NOT_FOUND_REDIRECT_TO' => 'foo',
], $config->getUrlShortener()); ], $config->getUrlShortener());
$choice->shouldNotHaveBeenCalled(); $choice->shouldNotHaveBeenCalled();
$ask->shouldHaveBeenCalledTimes(1); $ask->shouldHaveBeenCalledTimes(1);
@ -94,6 +100,8 @@ class UrlShortenerConfigCustomizerTest extends TestCase
'HOSTNAME' => 'foo', 'HOSTNAME' => 'foo',
'CHARS' => 'foo', 'CHARS' => 'foo',
'VALIDATE_URL' => true, 'VALIDATE_URL' => true,
'ENABLE_NOT_FOUND_REDIRECTION' => true,
'NOT_FOUND_REDIRECT_TO' => 'foo',
]); ]);
$this->plugin->process($this->io->reveal(), $config); $this->plugin->process($this->io->reveal(), $config);
@ -103,9 +111,41 @@ class UrlShortenerConfigCustomizerTest extends TestCase
'HOSTNAME' => 'foo', 'HOSTNAME' => 'foo',
'CHARS' => 'foo', 'CHARS' => 'foo',
'VALIDATE_URL' => true, 'VALIDATE_URL' => true,
'ENABLE_NOT_FOUND_REDIRECTION' => true,
'NOT_FOUND_REDIRECT_TO' => 'foo',
], $config->getUrlShortener()); ], $config->getUrlShortener());
$choice->shouldNotHaveBeenCalled(); $choice->shouldNotHaveBeenCalled();
$ask->shouldNotHaveBeenCalled(); $ask->shouldNotHaveBeenCalled();
$confirm->shouldNotHaveBeenCalled(); $confirm->shouldNotHaveBeenCalled();
} }
/**
* @test
*/
public function redirectUrlOptionIsNotAskedIfAnswerToPreviousQuestionIsNo()
{
$ask = $this->io->ask(Argument::cetera())->willReturn('asked');
$confirm = $this->io->confirm(Argument::cetera())->willReturn(false);
$config = new CustomizableAppConfig();
$config->setUrlShortener([
'SCHEMA' => 'foo',
'HOSTNAME' => 'foo',
'CHARS' => 'foo',
'VALIDATE_URL' => true,
]);
$this->plugin->process($this->io->reveal(), $config);
$this->assertTrue($config->hasUrlShortener());
$this->assertEquals([
'SCHEMA' => 'foo',
'HOSTNAME' => 'foo',
'CHARS' => 'foo',
'VALIDATE_URL' => true,
'ENABLE_NOT_FOUND_REDIRECTION' => false,
], $config->getUrlShortener());
$ask->shouldNotHaveBeenCalled();
$confirm->shouldHaveBeenCalledTimes(1);
}
} }