From 399c56a0978eadd1f6e714af5ad180accc2db7cf Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 12 Apr 2023 18:30:02 +0200 Subject: [PATCH] Print warning when trying to create short URL from CLI on openswoole in verbose mode --- .../ShortUrl/CreateShortUrlCommand.php | 5 +++ .../ShortUrl/CreateShortUrlCommandTest.php | 41 ++++++++++++++++++- .../ShortUrl/Model/UrlShorteningResult.php | 2 +- .../Core/test/ShortUrl/UrlShortenerTest.php | 24 +++++++++-- 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php index fb5453cf..bb332d82 100644 --- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php @@ -176,6 +176,11 @@ class CreateShortUrlCommand extends Command ShortUrlInputFilter::FORWARD_QUERY => !$input->getOption('no-forward-query'), ], $this->options)); + $result->onEventDispatchingError(static fn () => $io->isVerbose() && $io->warning( + 'Short URL properly created, but the real-time updates cannot be notified when generating the ' + . 'short URL from the command line. Migrate to roadrunner in order to bypass this limitation.', + )); + $io->writeln([ sprintf('Processed long URL: %s', $longUrl), sprintf('Generated short URL: %s', $this->stringifier->stringify($result->shortUrl)), diff --git a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php index f8b0e2d3..28e3ec21 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl; +use Laminas\ServiceManager\Exception\ServiceNotFoundException; use PHPUnit\Framework\Assert; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; @@ -20,6 +21,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\UrlShorteningResult; use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface; use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; class CreateShortUrlCommandTest extends TestCase @@ -62,11 +64,12 @@ class CreateShortUrlCommandTest extends TestCase $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', '--max-visits' => '3', - ]); + ], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); $output = $this->commandTester->getDisplay(); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); self::assertStringContainsString('stringified_short_url', $output); + self::assertStringNotContainsString('but the real-time updates cannot be notified', $output); } #[Test] @@ -170,4 +173,40 @@ class CreateShortUrlCommandTest extends TestCase yield 'no flags' => [[], null]; yield 'validate-url' => [['--validate-url' => true], true]; } + + /** + * @param callable(string $output): void $assert + */ + #[Test, DataProvider('provideDispatchBehavior')] + public function warningIsPrintedInVerboseModeWhenDispatchErrors(int $verbosity, callable $assert): void + { + $shortUrl = ShortUrl::createFake(); + $this->urlShortener->expects($this->once())->method('shorten')->withAnyParameters()->willReturn( + UrlShorteningResult::withErrorOnEventDispatching($shortUrl, new ServiceNotFoundException()), + ); + $this->stringifier->method('stringify')->willReturn('stringified_short_url'); + + $this->commandTester->execute(['longUrl' => 'http://domain.com/foo/bar'], ['verbosity' => $verbosity]); + $output = $this->commandTester->getDisplay(); + + $assert($output); + } + + public static function provideDispatchBehavior(): iterable + { + $containsAssertion = static fn (string $output) => self::assertStringContainsString( + 'but the real-time updates cannot be notified', + $output, + ); + $doesNotContainAssertion = static fn (string $output) => self::assertStringNotContainsString( + 'but the real-time updates cannot be notified', + $output, + ); + + yield 'quiet' => [OutputInterface::VERBOSITY_QUIET, $doesNotContainAssertion]; + yield 'normal' => [OutputInterface::VERBOSITY_NORMAL, $doesNotContainAssertion]; + yield 'verbose' => [OutputInterface::VERBOSITY_VERBOSE, $containsAssertion]; + yield 'very verbose' => [OutputInterface::VERBOSITY_VERY_VERBOSE, $containsAssertion]; + yield 'debug' => [OutputInterface::VERBOSITY_DEBUG, $containsAssertion]; + } } diff --git a/module/Core/src/ShortUrl/Model/UrlShorteningResult.php b/module/Core/src/ShortUrl/Model/UrlShorteningResult.php index c6a95739..b9d4f993 100644 --- a/module/Core/src/ShortUrl/Model/UrlShorteningResult.php +++ b/module/Core/src/ShortUrl/Model/UrlShorteningResult.php @@ -16,7 +16,7 @@ final class UrlShorteningResult } /** - * @param callable(Throwable $errorOnEventDispatching): void $handler + * @param callable(Throwable $errorOnEventDispatching): mixed $handler */ public function onEventDispatchingError(callable $handler): void { diff --git a/module/Core/test/ShortUrl/UrlShortenerTest.php b/module/Core/test/ShortUrl/UrlShortenerTest.php index 247e6185..cf1691ac 100644 --- a/module/Core/test/ShortUrl/UrlShortenerTest.php +++ b/module/Core/test/ShortUrl/UrlShortenerTest.php @@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Core\ShortUrl; use Cake\Chronos\Chronos; use Doctrine\ORM\EntityManager; +use Laminas\ServiceManager\Exception\ServiceNotFoundException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; @@ -26,6 +27,7 @@ class UrlShortenerTest extends TestCase private MockObject & EntityManager $em; private MockObject & ShortUrlTitleResolutionHelperInterface $titleResolutionHelper; private MockObject & ShortCodeUniquenessHelperInterface $shortCodeHelper; + private MockObject & EventDispatcherInterface $dispatcher; protected function setUp(): void { @@ -39,17 +41,19 @@ class UrlShortenerTest extends TestCase fn (callable $callback) => $callback(), ); + $this->dispatcher = $this->createMock(EventDispatcherInterface::class); + $this->urlShortener = new UrlShortener( $this->titleResolutionHelper, $this->em, new SimpleShortUrlRelationResolver(), $this->shortCodeHelper, - $this->createMock(EventDispatcherInterface::class), + $this->dispatcher, ); } - #[Test] - public function urlIsProperlyShortened(): void + #[Test, DataProvider('provideDispatchBehavior')] + public function urlIsProperlyShortened(bool $expectDispatchError, callable $dispatchBehavior): void { $longUrl = 'http://foobar.com/12345/hello?foo=bar'; $meta = ShortUrlCreation::fromRawData(['longUrl' => $longUrl]); @@ -57,10 +61,24 @@ class UrlShortenerTest extends TestCase $meta, )->willReturnArgument(0); $this->shortCodeHelper->method('ensureShortCodeUniqueness')->willReturn(true); + $this->dispatcher->expects($this->once())->method('dispatch')->willReturnCallback($dispatchBehavior); $result = $this->urlShortener->shorten($meta); + $thereIsError = false; + $result->onEventDispatchingError(function () use (&$thereIsError) { + $thereIsError = true; + }); self::assertEquals($longUrl, $result->shortUrl->getLongUrl()); + self::assertEquals($expectDispatchError, $thereIsError); + } + + public static function provideDispatchBehavior(): iterable + { + yield 'no dispatch error' => [false, static function (): void {}]; + yield 'dispatch error' => [true, static function (): void { + throw new ServiceNotFoundException(); + }]; } #[Test]