diff --git a/composer.json b/composer.json index 3159566c..f488c757 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "ocramius/proxy-manager": "~2.2.2", "phly/phly-event-dispatcher": "^1.0", "predis/predis": "^1.1", + "pugx/shortid-php": "^0.5", "shlinkio/shlink-common": "^2.0", "shlinkio/shlink-event-dispatcher": "^1.0", "shlinkio/shlink-installer": "^2.1", diff --git a/config/autoload/url-shortener.global.php b/config/autoload/url-shortener.global.php index 4bbd2c31..3d3689ea 100644 --- a/config/autoload/url-shortener.global.php +++ b/config/autoload/url-shortener.global.php @@ -2,8 +2,6 @@ declare(strict_types=1); -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; - use function Shlinkio\Shlink\Common\env; return [ @@ -13,7 +11,6 @@ return [ 'schema' => env('SHORTENED_URL_SCHEMA', 'http'), 'hostname' => env('SHORTENED_URL_HOSTNAME'), ], - 'shortcode_chars' => env('SHORTCODE_CHARS', UrlShortenerOptions::DEFAULT_CHARS), 'validate_url' => true, 'not_found_short_url' => [ 'enable_redirection' => false, diff --git a/module/CLI/src/Command/Config/GenerateCharsetCommand.php b/module/CLI/src/Command/Config/GenerateCharsetCommand.php index 924c45fc..a285c7f0 100644 --- a/module/CLI/src/Command/Config/GenerateCharsetCommand.php +++ b/module/CLI/src/Command/Config/GenerateCharsetCommand.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\Config; use Shlinkio\Shlink\CLI\Util\ExitCodes; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -18,6 +17,7 @@ use function str_shuffle; class GenerateCharsetCommand extends Command { public const NAME = 'config:generate-charset'; + private const DEFAULT_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; protected function configure(): void { @@ -26,14 +26,14 @@ class GenerateCharsetCommand extends Command ->setDescription(sprintf( '[DEPRECATED] Generates a character set sample just by shuffling the default one, "%s". ' . 'Then it can be set in the SHORTCODE_CHARS environment variable', - UrlShortenerOptions::DEFAULT_CHARS + self::DEFAULT_CHARS )) ->setHelp('This command is deprecated. Better leave shlink generate the charset.'); } protected function execute(InputInterface $input, OutputInterface $output): ?int { - $charSet = str_shuffle(UrlShortenerOptions::DEFAULT_CHARS); + $charSet = str_shuffle(self::DEFAULT_CHARS); (new SymfonyStyle($input, $output))->success(sprintf('Character set: "%s"', $charSet)); return ExitCodes::EXIT_SUCCESS; } diff --git a/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php index 0ac05688..d83bd042 100644 --- a/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php @@ -20,6 +20,11 @@ use Symfony\Component\Console\Tester\CommandTester; class GenerateShortUrlCommandTest extends TestCase { + private const DOMAIN_CONFIG = [ + 'schema' => 'http', + 'hostname' => 'foo.com', + ]; + /** @var CommandTester */ private $commandTester; /** @var ObjectProphecy */ @@ -28,10 +33,7 @@ class GenerateShortUrlCommandTest extends TestCase public function setUp(): void { $this->urlShortener = $this->prophesize(UrlShortener::class); - $command = new GenerateShortUrlCommand($this->urlShortener->reveal(), [ - 'schema' => 'http', - 'hostname' => 'foo.com', - ]); + $command = new GenerateShortUrlCommand($this->urlShortener->reveal(), self::DOMAIN_CONFIG); $app = new Application(); $app->add($command); $this->commandTester = new CommandTester($command); @@ -40,9 +42,8 @@ class GenerateShortUrlCommandTest extends TestCase /** @test */ public function properShortCodeIsCreatedIfLongUrlIsCorrect(): void { - $urlToShortCode = $this->urlShortener->urlToShortCode(Argument::cetera())->willReturn( - (new ShortUrl(''))->setShortCode('abc123') - ); + $shortUrl = new ShortUrl(''); + $urlToShortCode = $this->urlShortener->urlToShortCode(Argument::cetera())->willReturn($shortUrl); $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', @@ -51,7 +52,7 @@ class GenerateShortUrlCommandTest extends TestCase $output = $this->commandTester->getDisplay(); $this->assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); - $this->assertStringContainsString('http://foo.com/abc123', $output); + $this->assertStringContainsString($shortUrl->toString(self::DOMAIN_CONFIG), $output); $urlToShortCode->shouldHaveBeenCalledOnce(); } @@ -86,6 +87,7 @@ class GenerateShortUrlCommandTest extends TestCase /** @test */ public function properlyProcessesProvidedTags(): void { + $shortUrl = new ShortUrl(''); $urlToShortCode = $this->urlShortener->urlToShortCode( Argument::type(UriInterface::class), Argument::that(function (array $tags) { @@ -93,7 +95,7 @@ class GenerateShortUrlCommandTest extends TestCase return $tags; }), Argument::cetera() - )->willReturn((new ShortUrl(''))->setShortCode('abc123')); + )->willReturn($shortUrl); $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', @@ -102,7 +104,7 @@ class GenerateShortUrlCommandTest extends TestCase $output = $this->commandTester->getDisplay(); $this->assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); - $this->assertStringContainsString('http://foo.com/abc123', $output); + $this->assertStringContainsString($shortUrl->toString(self::DOMAIN_CONFIG), $output); $urlToShortCode->shouldHaveBeenCalledOnce(); } } diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php index f8ae2401..99e7c00a 100644 --- a/module/Core/src/Entity/ShortUrl.php +++ b/module/Core/src/Entity/ShortUrl.php @@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Core\Entity; use Cake\Chronos\Chronos; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use PUGX\Shortid\Factory as ShortIdFactory; use Shlinkio\Shlink\Common\Entity\AbstractEntity; use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface; use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver; @@ -20,6 +21,8 @@ use function Functional\invoke; class ShortUrl extends AbstractEntity { + private const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + /** @var string */ private $longUrl; /** @var string */ @@ -53,10 +56,15 @@ class ShortUrl extends AbstractEntity $this->validSince = $meta->getValidSince(); $this->validUntil = $meta->getValidUntil(); $this->maxVisits = $meta->getMaxVisits(); - $this->shortCode = $meta->getCustomSlug() ?? ''; // TODO logic to calculate short code should be passed somehow + $this->shortCode = $meta->getCustomSlug() ?? $this->generateShortCode(); $this->domain = ($domainResolver ?? new SimpleDomainResolver())->resolveDomain($meta->getDomain()); } + private function generateShortCode(): string + { + return (new ShortIdFactory())->generate(6, self::BASE62)->serialize(); + } + public function getLongUrl(): string { return $this->longUrl; @@ -67,13 +75,6 @@ class ShortUrl extends AbstractEntity return $this->shortCode; } - // TODO Short code is currently calculated based on the ID, so a setter is needed - public function setShortCode(string $shortCode): self - { - $this->shortCode = $shortCode; - return $this; - } - public function getDateCreated(): Chronos { return $this->dateCreated; diff --git a/module/Core/src/Options/UrlShortenerOptions.php b/module/Core/src/Options/UrlShortenerOptions.php index 4523bc00..6816ba6e 100644 --- a/module/Core/src/Options/UrlShortenerOptions.php +++ b/module/Core/src/Options/UrlShortenerOptions.php @@ -8,26 +8,12 @@ use Zend\Stdlib\AbstractOptions; class UrlShortenerOptions extends AbstractOptions { - public const DEFAULT_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - // phpcs:disable protected $__strictMode__ = false; // phpcs:enable - private $shortcodeChars = self::DEFAULT_CHARS; private $validateUrl = true; - public function getChars(): string - { - return $this->shortcodeChars; - } - - protected function setShortcodeChars(string $shortcodeChars): self - { - $this->shortcodeChars = empty($shortcodeChars) ? self::DEFAULT_CHARS : $shortcodeChars; - return $this; - } - public function isUrlValidationEnabled(): bool { return $this->validateUrl; diff --git a/module/Core/src/Service/UrlShortener.php b/module/Core/src/Service/UrlShortener.php index 47ea6056..510f6d41 100644 --- a/module/Core/src/Service/UrlShortener.php +++ b/module/Core/src/Service/UrlShortener.php @@ -24,17 +24,11 @@ use Shlinkio\Shlink\Core\Util\TagManagerTrait; use Throwable; use function array_reduce; -use function floor; -use function fmod; -use function preg_match; -use function strlen; class UrlShortener implements UrlShortenerInterface { use TagManagerTrait; - private const ID_INCREMENT = 200000; - /** @var ClientInterface */ private $httpClient; /** @var EntityManagerInterface */ @@ -77,16 +71,8 @@ class UrlShortener implements UrlShortenerInterface // First, create the short URL with an empty short code $shortUrl = new ShortUrl($url, $meta, new PersistenceDomainResolver($this->em)); - $this->em->persist($shortUrl); - $this->em->flush(); - - // Generate the short code and persist it if no custom slug was provided - if (! $meta->hasCustomSlug()) { - // TODO Somehow provide the logic to calculate the shortCode to avoid the need of a setter - $shortCode = $this->convertAutoincrementIdToShortCode((float) $shortUrl->getId()); - $shortUrl->setShortCode($shortCode); - } $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags)); + $this->em->persist($shortUrl); $this->em->flush(); $this->em->commit(); @@ -155,36 +141,12 @@ class UrlShortener implements UrlShortenerInterface } } - private function convertAutoincrementIdToShortCode(float $id): string - { - $id += self::ID_INCREMENT; // Increment the Id so that the generated shortcode is not too short - $chars = $this->options->getChars(); - - $length = strlen($chars); - $code = ''; - - while ($id > 0) { - // Determine the value of the next higher character in the short code and prepend it - $code = $chars[(int) fmod($id, $length)] . $code; - $id = floor($id / $length); - } - - return $chars[(int) $id] . $code; - } - /** * @throws InvalidShortCodeException * @throws EntityDoesNotExistException */ public function shortCodeToUrl(string $shortCode, ?string $domain = null): ShortUrl { - $chars = $this->options->getChars(); - - // Validate short code format - if (! preg_match('|[' . $chars . ']+|', $shortCode)) { - throw InvalidShortCodeException::fromCharset($shortCode, $chars); - } - /** @var ShortUrlRepository $shortUrlRepo */ $shortUrlRepo = $this->em->getRepository(ShortUrl::class); $shortUrl = $shortUrlRepo->findOneByShortCode($shortCode, $domain); diff --git a/module/Core/test-db/Repository/ShortUrlRepositoryTest.php b/module/Core/test-db/Repository/ShortUrlRepositoryTest.php index e6d511fa..29c9860b 100644 --- a/module/Core/test-db/Repository/ShortUrlRepositoryTest.php +++ b/module/Core/test-db/Repository/ShortUrlRepositoryTest.php @@ -37,37 +37,41 @@ class ShortUrlRepositoryTest extends DatabaseTestCase /** @test */ public function findOneByShortCodeReturnsProperData(): void { - $regularOne = new ShortUrl('foo'); - $regularOne->setShortCode('foo'); + $regularOne = new ShortUrl('foo', ShortUrlMeta::createFromParams(null, null, 'foo')); $this->getEntityManager()->persist($regularOne); - $notYetValid = new ShortUrl('bar', ShortUrlMeta::createFromParams(Chronos::now()->addMonth())); - $notYetValid->setShortCode('bar_very_long_text'); + $notYetValid = new ShortUrl( + 'bar', + ShortUrlMeta::createFromParams(Chronos::now()->addMonth(), null, 'bar_very_long_text') + ); $this->getEntityManager()->persist($notYetValid); - $expired = new ShortUrl('expired', ShortUrlMeta::createFromParams(null, Chronos::now()->subMonth())); - $expired->setShortCode('expired'); + $expired = new ShortUrl('expired', ShortUrlMeta::createFromParams(null, Chronos::now()->subMonth(), 'expired')); $this->getEntityManager()->persist($expired); - $allVisitsComplete = new ShortUrl('baz', ShortUrlMeta::createFromRawData(['maxVisits' => 3])); + $allVisitsComplete = new ShortUrl('baz', ShortUrlMeta::createFromRawData([ + 'maxVisits' => 3, + 'customSlug' => 'baz', + ])); $visits = []; for ($i = 0; $i < 3; $i++) { $visit = new Visit($allVisitsComplete, Visitor::emptyInstance()); $this->getEntityManager()->persist($visit); $visits[] = $visit; } - $allVisitsComplete->setShortCode('baz') - ->setVisits(new ArrayCollection($visits)); + $allVisitsComplete->setVisits(new ArrayCollection($visits)); $this->getEntityManager()->persist($allVisitsComplete); - $withDomain = new ShortUrl('foo', ShortUrlMeta::createFromRawData(['domain' => 'example.com'])); - $withDomain->setShortCode('domain-short-code'); + $withDomain = new ShortUrl('foo', ShortUrlMeta::createFromRawData([ + 'domain' => 'example.com', + 'customSlug' => 'domain-short-code', + ])); $this->getEntityManager()->persist($withDomain); $withDomainDuplicatingRegular = new ShortUrl('foo_with_domain', ShortUrlMeta::createFromRawData([ 'domain' => 'doma.in', + 'customSlug' => 'foo', ])); - $withDomainDuplicatingRegular->setShortCode('foo'); $this->getEntityManager()->persist($withDomainDuplicatingRegular); $this->getEntityManager()->flush(); @@ -96,9 +100,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase { $count = 5; for ($i = 0; $i < $count; $i++) { - $this->getEntityManager()->persist( - (new ShortUrl((string) $i))->setShortCode((string) $i) - ); + $this->getEntityManager()->persist(new ShortUrl((string) $i)); } $this->getEntityManager()->flush(); @@ -112,19 +114,16 @@ class ShortUrlRepositoryTest extends DatabaseTestCase $this->getEntityManager()->persist($tag); $foo = new ShortUrl('foo'); - $foo->setShortCode('foo') - ->setTags(new ArrayCollection([$tag])); + $foo->setTags(new ArrayCollection([$tag])); $this->getEntityManager()->persist($foo); $bar = new ShortUrl('bar'); $visit = new Visit($bar, Visitor::emptyInstance()); $this->getEntityManager()->persist($visit); - $bar->setShortCode('bar_very_long_text') - ->setVisits(new ArrayCollection([$visit])); + $bar->setVisits(new ArrayCollection([$visit])); $this->getEntityManager()->persist($bar); $foo2 = new ShortUrl('foo_2'); - $foo2->setShortCode('foo_2'); $this->getEntityManager()->persist($foo2); $this->getEntityManager()->flush(); @@ -155,9 +154,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase { $urls = ['a', 'z', 'c', 'b']; foreach ($urls as $url) { - $this->getEntityManager()->persist( - (new ShortUrl($url))->setShortCode($url) - ); + $this->getEntityManager()->persist(new ShortUrl($url)); } $this->getEntityManager()->flush(); @@ -174,13 +171,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase /** @test */ public function slugIsInUseLooksForShortUrlInProperSetOfTables(): void { - $shortUrlWithoutDomain = (new ShortUrl('foo'))->setShortCode('my-cool-slug'); + $shortUrlWithoutDomain = new ShortUrl('foo', ShortUrlMeta::createFromRawData(['customSlug' => 'my-cool-slug'])); $this->getEntityManager()->persist($shortUrlWithoutDomain); - $shortUrlWithDomain = (new ShortUrl( + $shortUrlWithDomain = new ShortUrl( 'foo', - ShortUrlMeta::createFromRawData(['domain' => 'doma.in']) - ))->setShortCode('another-slug'); + ShortUrlMeta::createFromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug']) + ); $this->getEntityManager()->persist($shortUrlWithDomain); $this->getEntityManager()->flush(); diff --git a/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php b/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php index f96b6f33..cbb99c57 100644 --- a/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php +++ b/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php @@ -19,20 +19,21 @@ use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlService; use function Functional\map; use function range; +use function sprintf; class DeleteShortUrlServiceTest extends TestCase { - /** @var DeleteShortUrlService */ - private $service; /** @var ObjectProphecy */ private $em; + /** @var string */ + private $shortCode; public function setUp(): void { - $shortUrl = (new ShortUrl(''))->setShortCode('abc123') - ->setVisits(new ArrayCollection(map(range(0, 10), function () { - return new Visit(new ShortUrl(''), Visitor::emptyInstance()); - }))); + $shortUrl = (new ShortUrl(''))->setVisits(new ArrayCollection(map(range(0, 10), function () { + return new Visit(new ShortUrl(''), Visitor::emptyInstance()); + }))); + $this->shortCode = $shortUrl->getShortCode(); $this->em = $this->prophesize(EntityManagerInterface::class); @@ -42,55 +43,56 @@ class DeleteShortUrlServiceTest extends TestCase } /** @test */ - public function deleteByShortCodeThrowsExceptionWhenThresholdIsReached() + public function deleteByShortCodeThrowsExceptionWhenThresholdIsReached(): void { $service = $this->createService(); $this->expectException(DeleteShortUrlException::class); - $this->expectExceptionMessage( - 'Impossible to delete short URL with short code "abc123" since it has more than "5" visits.' - ); + $this->expectExceptionMessage(sprintf( + 'Impossible to delete short URL with short code "%s" since it has more than "5" visits.', + $this->shortCode + )); - $service->deleteByShortCode('abc123'); + $service->deleteByShortCode($this->shortCode); } /** @test */ - public function deleteByShortCodeDeletesUrlWhenThresholdIsReachedButExplicitlyIgnored() + public function deleteByShortCodeDeletesUrlWhenThresholdIsReachedButExplicitlyIgnored(): void { $service = $this->createService(); $remove = $this->em->remove(Argument::type(ShortUrl::class))->willReturn(null); $flush = $this->em->flush()->willReturn(null); - $service->deleteByShortCode('abc123', true); + $service->deleteByShortCode($this->shortCode, true); $remove->shouldHaveBeenCalledOnce(); $flush->shouldHaveBeenCalledOnce(); } /** @test */ - public function deleteByShortCodeDeletesUrlWhenThresholdIsReachedButCheckIsDisabled() + public function deleteByShortCodeDeletesUrlWhenThresholdIsReachedButCheckIsDisabled(): void { $service = $this->createService(false); $remove = $this->em->remove(Argument::type(ShortUrl::class))->willReturn(null); $flush = $this->em->flush()->willReturn(null); - $service->deleteByShortCode('abc123'); + $service->deleteByShortCode($this->shortCode); $remove->shouldHaveBeenCalledOnce(); $flush->shouldHaveBeenCalledOnce(); } /** @test */ - public function deleteByShortCodeDeletesUrlWhenThresholdIsNotReached() + public function deleteByShortCodeDeletesUrlWhenThresholdIsNotReached(): void { $service = $this->createService(true, 100); $remove = $this->em->remove(Argument::type(ShortUrl::class))->willReturn(null); $flush = $this->em->flush()->willReturn(null); - $service->deleteByShortCode('abc123'); + $service->deleteByShortCode($this->shortCode); $remove->shouldHaveBeenCalledOnce(); $flush->shouldHaveBeenCalledOnce(); diff --git a/module/Core/test/Service/UrlShortenerTest.php b/module/Core/test/Service/UrlShortenerTest.php index 3e75e5b4..55f2452c 100644 --- a/module/Core/test/Service/UrlShortenerTest.php +++ b/module/Core/test/Service/UrlShortenerTest.php @@ -17,7 +17,6 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\Tag; -use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException; use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; use Shlinkio\Shlink\Core\Exception\RuntimeException; @@ -74,13 +73,13 @@ class UrlShortenerTest extends TestCase /** @test */ public function urlIsProperlyShortened(): void { - // 10 -> 0Q1Y $shortUrl = $this->urlShortener->urlToShortCode( new Uri('http://foobar.com/12345/hello?foo=bar'), [], ShortUrlMeta::createEmpty() ); - $this->assertEquals('0Q1Y', $shortUrl->getShortCode()); + + $this->assertEquals('http://foobar.com/12345/hello?foo=bar', $shortUrl->getLongUrl()); } /** @test */ @@ -243,9 +242,8 @@ class UrlShortenerTest extends TestCase /** @test */ public function shortCodeIsProperlyParsed(): void { - $shortCode = '12C1c'; $shortUrl = new ShortUrl('expected_url'); - $shortUrl->setShortCode($shortCode); + $shortCode = $shortUrl->getShortCode(); $repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo->findOneByShortCode($shortCode, null)->willReturn($shortUrl); @@ -254,11 +252,4 @@ class UrlShortenerTest extends TestCase $url = $this->urlShortener->shortCodeToUrl($shortCode); $this->assertSame($shortUrl, $url); } - - /** @test */ - public function invalidCharSetThrowsException(): void - { - $this->expectException(InvalidShortCodeException::class); - $this->urlShortener->shortCodeToUrl('&/('); - } } diff --git a/module/Rest/test-api/Action/CreateShortUrlActionTest.php b/module/Rest/test-api/Action/CreateShortUrlActionTest.php index ed6a66d8..c3f7a4e4 100644 --- a/module/Rest/test-api/Action/CreateShortUrlActionTest.php +++ b/module/Rest/test-api/Action/CreateShortUrlActionTest.php @@ -90,7 +90,7 @@ class CreateShortUrlActionTest extends ApiTestCase $this->assertEquals(self::STATUS_OK, $statusCode); - // Request to the short URL will return a 404 since ist' not valid yet + // Request to the short URL will return a 404 since it's not valid yet $lastResp = $this->callShortUrl($shortCode); $this->assertEquals(self::STATUS_NOT_FOUND, $lastResp->getStatusCode()); } diff --git a/module/Rest/test-api/Fixtures/ShortUrlsFixture.php b/module/Rest/test-api/Fixtures/ShortUrlsFixture.php index 4a9de19b..253b0032 100644 --- a/module/Rest/test-api/Fixtures/ShortUrlsFixture.php +++ b/module/Rest/test-api/Fixtures/ShortUrlsFixture.php @@ -20,13 +20,15 @@ class ShortUrlsFixture extends AbstractFixture */ public function load(ObjectManager $manager): void { - $abcShortUrl = $this->setShortUrlDate(new ShortUrl('https://shlink.io'))->setShortCode('abc123'); + $abcShortUrl = $this->setShortUrlDate( + new ShortUrl('https://shlink.io', ShortUrlMeta::createFromRawData(['customSlug' => 'abc123'])) + ); $manager->persist($abcShortUrl); $defShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://blog.alejandrocelaya.com/2017/12/09/acmailer-7-0-the-most-important-release-in-a-long-time/', - ShortUrlMeta::createFromParams(Chronos::parse('2020-05-01')) - ))->setShortCode('def456'); + ShortUrlMeta::createFromParams(Chronos::parse('2020-05-01'), null, 'def456') + )); $manager->persist($defShortUrl); $customShortUrl = $this->setShortUrlDate(new ShortUrl( @@ -37,14 +39,14 @@ class ShortUrlsFixture extends AbstractFixture $withDomainShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://blog.alejandrocelaya.com/2019/04/27/considerations-to-properly-use-open-source-software-projects/', - ShortUrlMeta::createFromRawData(['domain' => 'example.com']) - ))->setShortCode('ghi789'); + ShortUrlMeta::createFromRawData(['domain' => 'example.com', 'customSlug' => 'ghi789']) + )); $manager->persist($withDomainShortUrl); $withDomainAndSlugShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://google.com', - ShortUrlMeta::createFromRawData(['domain' => 'some-domain.com']) - ))->setShortCode('custom-with-domain'); + ShortUrlMeta::createFromRawData(['domain' => 'some-domain.com', 'customSlug' => 'custom-with-domain']) + )); $manager->persist($withDomainAndSlugShortUrl); $manager->flush(); diff --git a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php index 7175b794..8b00229e 100644 --- a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php @@ -22,6 +22,11 @@ use function strpos; class CreateShortUrlActionTest extends TestCase { + private const DOMAIN_CONFIG = [ + 'schema' => 'http', + 'hostname' => 'foo.com', + ]; + /** @var CreateShortUrlAction */ private $action; /** @var ObjectProphecy */ @@ -30,10 +35,7 @@ class CreateShortUrlActionTest extends TestCase public function setUp(): void { $this->urlShortener = $this->prophesize(UrlShortener::class); - $this->action = new CreateShortUrlAction($this->urlShortener->reveal(), [ - 'schema' => 'http', - 'hostname' => 'foo.com', - ]); + $this->action = new CreateShortUrlAction($this->urlShortener->reveal(), self::DOMAIN_CONFIG); } /** @test */ @@ -46,10 +48,9 @@ class CreateShortUrlActionTest extends TestCase /** @test */ public function properShortcodeConversionReturnsData(): void { + $shortUrl = new ShortUrl(''); $this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera()) - ->willReturn( - (new ShortUrl(''))->setShortCode('abc123') - ) + ->willReturn($shortUrl) ->shouldBeCalledOnce(); $request = (new ServerRequest())->withParsedBody([ @@ -57,7 +58,7 @@ class CreateShortUrlActionTest extends TestCase ]); $response = $this->action->handle($request); $this->assertEquals(200, $response->getStatusCode()); - $this->assertTrue(strpos($response->getBody()->getContents(), 'http://foo.com/abc123') > 0); + $this->assertTrue(strpos($response->getBody()->getContents(), $shortUrl->toString(self::DOMAIN_CONFIG)) > 0); } /** @test */