From bfba05c8634a739ffcb49de7be3204523331c6db Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 3 Feb 2021 11:53:08 +0100 Subject: [PATCH] Enhanced UrlValidatorTest --- module/Core/functions/functions.php | 1 + module/Core/src/Util/UrlValidator.php | 7 ++- module/Core/test/Util/UrlValidatorTest.php | 50 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php index 531f8038..f9a67e3d 100644 --- a/module/Core/functions/functions.php +++ b/module/Core/functions/functions.php @@ -26,6 +26,7 @@ const DEFAULT_REDIRECT_STATUS_CODE = StatusCodeInterface::STATUS_FOUND; const DEFAULT_REDIRECT_CACHE_LIFETIME = 30; const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory'; const CUSTOM_SLUGS_REGEXP = '/[^\pL\pN._~]/u'; // Any unicode letter or number, plus ".", "_" and "~" chars +const TITLE_TAG_VALUE = '/]*>(.*?)<\/title>/i'; // Matches the value inside an html title tag function generateRandomShortCode(int $length): string { diff --git a/module/Core/src/Util/UrlValidator.php b/module/Core/src/Util/UrlValidator.php index e1ae159b..8d05cbe6 100644 --- a/module/Core/src/Util/UrlValidator.php +++ b/module/Core/src/Util/UrlValidator.php @@ -13,6 +13,9 @@ use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use function preg_match; +use function trim; + +use const Shlinkio\Shlink\Core\TITLE_TAG_VALUE; class UrlValidator implements UrlValidatorInterface, RequestMethodInterface { @@ -51,8 +54,8 @@ class UrlValidator implements UrlValidatorInterface, RequestMethodInterface } $body = $response->getBody()->__toString(); - preg_match('/]*>(.*?)<\/title>/i', $body, $matches); - return $matches[1] ?? null; + preg_match(TITLE_TAG_VALUE, $body, $matches); + return isset($matches[1]) ? trim($matches[1]) : null; } private function validateUrlAndGetResponse(string $url, bool $throwOnError): ?ResponseInterface diff --git a/module/Core/test/Util/UrlValidatorTest.php b/module/Core/test/Util/UrlValidatorTest.php index fab1db1e..7c5f7c55 100644 --- a/module/Core/test/Util/UrlValidatorTest.php +++ b/module/Core/test/Util/UrlValidatorTest.php @@ -9,6 +9,7 @@ use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\ClientException; use GuzzleHttp\RequestOptions; use Laminas\Diactoros\Response; +use Laminas\Diactoros\Stream; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -76,10 +77,59 @@ class UrlValidatorTest extends TestCase $request->shouldNotHaveBeenCalled(); } + /** + * @test + * @dataProvider provideDisabledCombinations + */ + public function validateUrlWithTitleReturnsNullWhenRequestFailsAndValidationIsDisabled( + ?bool $doValidate, + bool $validateUrl + ): void { + $request = $this->httpClient->request(Argument::cetera())->willThrow(ClientException::class); + $this->options->validateUrl = $validateUrl; + + $result = $this->urlValidator->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', $doValidate); + + self::assertNull($result); + $request->shouldHaveBeenCalledOnce(); + } + public function provideDisabledCombinations(): iterable { yield 'config is disabled and no runtime option is provided' => [null, false]; yield 'config is enabled but runtime option is disabled' => [false, true]; yield 'both config and runtime option are disabled' => [false, false]; } + + /** @test */ + public function validateUrlWithTitleReturnsNullWhenAutoResolutionIsDisabled(): void + { + $request = $this->httpClient->request(Argument::cetera())->willReturn($this->respWithTitle()); + $this->options->autoResolveTitles = false; + + $result = $this->urlValidator->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', true); + + self::assertNull($result); + $request->shouldHaveBeenCalledOnce(); + } + + /** @test */ + public function validateUrlWithTitleResolvesTitleWhenAutoResolutionIsEnabled(): void + { + $request = $this->httpClient->request(Argument::cetera())->willReturn($this->respWithTitle()); + $this->options->autoResolveTitles = true; + + $result = $this->urlValidator->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', true); + + self::assertEquals('Resolved title', $result); + $request->shouldHaveBeenCalledOnce(); + } + + private function respWithTitle(): Response + { + $body = new Stream('php://temp', 'wr'); + $body->write(' Resolved title'); + + return new Response($body); + } }