Created ExtraPathRedirectMiddleware test

This commit is contained in:
Alejandro Celaya 2021-07-15 19:37:09 +02:00
parent 20575a2b0f
commit eabaa94e06
2 changed files with 153 additions and 0 deletions

View file

@ -11,6 +11,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
Now, when calling the `GET /{shorCode}/qr-code` URL, you can pass the `errorCorrection` query param with values `L` for Low, `M` for Medium, `Q` for Quartile or `H` for High.
* [#1080](https://github.com/shlinkio/shlink/issues/1080) Added support to redirect to URLs as soon as the path starts with a valid short code, appending the rest of the path to the redirected long URL.
With this, if you have the `https://example.com/abc123` short URL redirecting to `https://www.twitter.com`, a visit to `https://example.com/abc123/shlinkio` will take you to `https://www.twitter.com/shlinkio`.
This behavior needs to be actively opted in, via installer config options or env vars.
### Changed
* *Nothing*

View file

@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\ShortUrl\Middleware;
use Laminas\Diactoros\Response\RedirectResponse;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\Diactoros\Uri;
use Mezzio\Router\Route;
use Mezzio\Router\RouteResult;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilderInterface;
use Shlinkio\Shlink\Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware;
use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface;
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
class ExtraPathRedirectMiddlewareTest extends TestCase
{
use ProphecyTrait;
private ExtraPathRedirectMiddleware $middleware;
private ObjectProphecy $resolver;
private ObjectProphecy $requestTracker;
private ObjectProphecy $redirectionBuilder;
private ObjectProphecy $redirectResponseHelper;
private UrlShortenerOptions $options;
private ObjectProphecy $handler;
protected function setUp(): void
{
$this->resolver = $this->prophesize(ShortUrlResolverInterface::class);
$this->requestTracker = $this->prophesize(RequestTrackerInterface::class);
$this->redirectionBuilder = $this->prophesize(ShortUrlRedirectionBuilderInterface::class);
$this->redirectResponseHelper = $this->prophesize(RedirectResponseHelperInterface::class);
$this->options = new UrlShortenerOptions(['append_extra_path' => true]);
$this->middleware = new ExtraPathRedirectMiddleware(
$this->resolver->reveal(),
$this->requestTracker->reveal(),
$this->redirectionBuilder->reveal(),
$this->redirectResponseHelper->reveal(),
$this->options,
);
$this->handler = $this->prophesize(RequestHandlerInterface::class);
$this->handler->handle(Argument::cetera())->willReturn(new RedirectResponse(''));
}
/**
* @test
* @dataProvider provideNonRedirectingRequests
*/
public function handlerIsCalledWhenConfigPreventsRedirectWithExtraPath(
bool $appendExtraPath,
ServerRequestInterface $request
): void {
$this->options->appendExtraPath = $appendExtraPath;
$this->middleware->process($request, $this->handler->reveal());
$this->resolver->resolveEnabledShortUrl(Argument::cetera())->shouldNotHaveBeenCalled();
$this->requestTracker->trackIfApplicable(Argument::cetera())->shouldNotHaveBeenCalled();
$this->redirectionBuilder->buildShortUrlRedirect(Argument::cetera())->shouldNotHaveBeenCalled();
$this->redirectResponseHelper->buildRedirectResponse(Argument::cetera())->shouldNotHaveBeenCalled();
}
public function provideNonRedirectingRequests(): iterable
{
$baseReq = ServerRequestFactory::fromGlobals();
$buildReq = static fn (?NotFoundType $type): ServerRequestInterface =>
$baseReq->withAttribute(NotFoundType::class, $type);
yield 'disabled option' => [false, $buildReq(NotFoundType::fromRequest($baseReq, '/foo/bar'))];
yield 'base_url error' => [true, $buildReq(NotFoundType::fromRequest($baseReq, ''))];
yield 'invalid_short_url error' => [
true,
$buildReq(NotFoundType::fromRequest($baseReq, ''))->withAttribute(
RouteResult::class,
RouteResult::fromRoute(new Route(
'',
$this->prophesize(MiddlewareInterface::class)->reveal(),
['GET'],
)),
),
];
yield 'no error type' => [true, $buildReq(null)];
}
/** @test */
public function handlerIsCalledWhenNoShortUrlIsFound(): void
{
$type = $this->prophesize(NotFoundType::class);
$type->isRegularNotFound()->willReturn(true);
$request = ServerRequestFactory::fromGlobals()->withAttribute(NotFoundType::class, $type->reveal())
->withUri(new Uri('/shortCode/bar/baz'));
$resolve = $this->resolver->resolveEnabledShortUrl(Argument::cetera())->willThrow(
ShortUrlNotFoundException::class,
);
$this->middleware->process($request, $this->handler->reveal());
$resolve->shouldHaveBeenCalledOnce();
$this->requestTracker->trackIfApplicable(Argument::cetera())->shouldNotHaveBeenCalled();
$this->redirectionBuilder->buildShortUrlRedirect(Argument::cetera())->shouldNotHaveBeenCalled();
$this->redirectResponseHelper->buildRedirectResponse(Argument::cetera())->shouldNotHaveBeenCalled();
}
/** @test */
public function visitIsTrackedAndRedirectIsReturnedWhenShortUrlIsFound(): void
{
$type = $this->prophesize(NotFoundType::class);
$type->isRegularNotFound()->willReturn(true);
$request = ServerRequestFactory::fromGlobals()->withAttribute(NotFoundType::class, $type->reveal())
->withUri(new Uri('https://doma.in/shortCode/bar/baz'));
$shortUrl = ShortUrl::withLongUrl('');
$identifier = ShortUrlIdentifier::fromShortCodeAndDomain('shortCode', 'doma.in');
$resolve = $this->resolver->resolveEnabledShortUrl($identifier)->willReturn($shortUrl);
$buildLongUrl = $this->redirectionBuilder->buildShortUrlRedirect($shortUrl, [], '/bar/baz')->willReturn(
'the_built_long_url',
);
$buildResp = $this->redirectResponseHelper->buildRedirectResponse('the_built_long_url')->willReturn(
new RedirectResponse(''),
);
$this->middleware->process($request, $this->handler->reveal());
$resolve->shouldHaveBeenCalledOnce();
$buildLongUrl->shouldHaveBeenCalledOnce();
$buildResp->shouldHaveBeenCalledOnce();
$this->requestTracker->trackIfApplicable($shortUrl, $request)->shouldHaveBeenCalledOnce();
}
}