Refactored short URL creation so that the long URL is part of the ShortUrlMeta

This commit is contained in:
Alejandro Celaya
2021-01-30 14:18:44 +01:00
parent 56a2253535
commit 07b12fac3c
45 changed files with 343 additions and 306 deletions

View File

@@ -145,7 +145,8 @@ class GenerateShortUrlCommand extends BaseCommand
$doValidateUrl = $this->doValidateUrl($input); $doValidateUrl = $this->doValidateUrl($input);
try { try {
$shortUrl = $this->urlShortener->shorten($longUrl, $tags, ShortUrlMeta::fromRawData([ $shortUrl = $this->urlShortener->shorten($tags, ShortUrlMeta::fromRawData([
ShortUrlMetaInputFilter::LONG_URL => $longUrl,
ShortUrlMetaInputFilter::VALID_SINCE => $this->getOptionWithDeprecatedFallback($input, 'valid-since'), ShortUrlMetaInputFilter::VALID_SINCE => $this->getOptionWithDeprecatedFallback($input, 'valid-since'),
ShortUrlMetaInputFilter::VALID_UNTIL => $this->getOptionWithDeprecatedFallback($input, 'valid-until'), ShortUrlMetaInputFilter::VALID_UNTIL => $this->getOptionWithDeprecatedFallback($input, 'valid-until'),
ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug, ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug,

View File

@@ -43,7 +43,7 @@ class GenerateShortUrlCommandTest extends TestCase
/** @test */ /** @test */
public function properShortCodeIsCreatedIfLongUrlIsCorrect(): void public function properShortCodeIsCreatedIfLongUrlIsCorrect(): void
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl); $urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl);
$this->commandTester->execute([ $this->commandTester->execute([
@@ -89,9 +89,8 @@ class GenerateShortUrlCommandTest extends TestCase
/** @test */ /** @test */
public function properlyProcessesProvidedTags(): void public function properlyProcessesProvidedTags(): void
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$urlToShortCode = $this->urlShortener->shorten( $urlToShortCode = $this->urlShortener->shorten(
Argument::type('string'),
Argument::that(function (array $tags) { Argument::that(function (array $tags) {
Assert::assertEquals(['foo', 'bar', 'baz', 'boo', 'zar'], $tags); Assert::assertEquals(['foo', 'bar', 'baz', 'boo', 'zar'], $tags);
return $tags; return $tags;
@@ -116,9 +115,8 @@ class GenerateShortUrlCommandTest extends TestCase
*/ */
public function urlValidationHasExpectedValueBasedOnProvidedTags(array $options, ?bool $expectedValidateUrl): void public function urlValidationHasExpectedValueBasedOnProvidedTags(array $options, ?bool $expectedValidateUrl): void
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$urlToShortCode = $this->urlShortener->shorten( $urlToShortCode = $this->urlShortener->shorten(
Argument::type('string'),
Argument::type('array'), Argument::type('array'),
Argument::that(function (ShortUrlMeta $meta) use ($expectedValidateUrl) { Argument::that(function (ShortUrlMeta $meta) use ($expectedValidateUrl) {
Assert::assertEquals($expectedValidateUrl, $meta->doValidateUrl()); Assert::assertEquals($expectedValidateUrl, $meta->doValidateUrl());

View File

@@ -103,7 +103,7 @@ class GetVisitsCommandTest extends TestCase
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->visitsTracker->info(new ShortUrlIdentifier($shortCode), Argument::any())->willReturn( $this->visitsTracker->info(new ShortUrlIdentifier($shortCode), Argument::any())->willReturn(
new Paginator(new ArrayAdapter([ new Paginator(new ArrayAdapter([
(new Visit(new ShortUrl(''), new Visitor('bar', 'foo', '')))->locate( (new Visit(ShortUrl::createEmpty(), new Visitor('bar', 'foo', '')))->locate(
new VisitLocation(new Location('', 'Spain', '', '', 0, 0, '')), new VisitLocation(new Location('', 'Spain', '', '', 0, 0, '')),
), ),
])), ])),

View File

@@ -42,7 +42,7 @@ class ListShortUrlsCommandTest extends TestCase
// The paginator will return more than one page // The paginator will return more than one page
$data = []; $data = [];
for ($i = 0; $i < 50; $i++) { for ($i = 0; $i < 50; $i++) {
$data[] = new ShortUrl('url_' . $i); $data[] = ShortUrl::withLongUrl('url_' . $i);
} }
$this->shortUrlService->listShortUrls(Argument::cetera()) $this->shortUrlService->listShortUrls(Argument::cetera())
@@ -64,7 +64,7 @@ class ListShortUrlsCommandTest extends TestCase
// The paginator will return more than one page // The paginator will return more than one page
$data = []; $data = [];
for ($i = 0; $i < 30; $i++) { for ($i = 0; $i < 30; $i++) {
$data[] = new ShortUrl('url_' . $i); $data[] = ShortUrl::withLongUrl('url_' . $i);
} }
$this->shortUrlService->listShortUrls(ShortUrlsParams::emptyInstance()) $this->shortUrlService->listShortUrls(ShortUrlsParams::emptyInstance())

View File

@@ -41,7 +41,7 @@ class ResolveUrlCommandTest extends TestCase
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$expectedUrl = 'http://domain.com/foo/bar'; $expectedUrl = 'http://domain.com/foo/bar';
$shortUrl = new ShortUrl($expectedUrl); $shortUrl = ShortUrl::withLongUrl($expectedUrl);
$this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode))->willReturn($shortUrl) $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode))->willReturn($shortUrl)
->shouldBeCalledOnce(); ->shouldBeCalledOnce();

View File

@@ -77,7 +77,7 @@ class LocateVisitsCommandTest extends TestCase
bool $expectWarningPrint, bool $expectWarningPrint,
array $args array $args
): void { ): void {
$visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4')); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('', '', '1.2.3.4'));
$location = new VisitLocation(Location::emptyInstance()); $location = new VisitLocation(Location::emptyInstance());
$mockMethodBehavior = $this->invokeHelperMethods($visit, $location); $mockMethodBehavior = $this->invokeHelperMethods($visit, $location);
@@ -121,7 +121,7 @@ class LocateVisitsCommandTest extends TestCase
*/ */
public function localhostAndEmptyAddressesAreIgnored(?string $address, string $message): void public function localhostAndEmptyAddressesAreIgnored(?string $address, string $message): void
{ {
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $address)); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('', '', $address));
$location = new VisitLocation(Location::emptyInstance()); $location = new VisitLocation(Location::emptyInstance());
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will( $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(
@@ -154,7 +154,7 @@ class LocateVisitsCommandTest extends TestCase
/** @test */ /** @test */
public function errorWhileLocatingIpIsDisplayed(): void public function errorWhileLocatingIpIsDisplayed(): void
{ {
$visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4')); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('', '', '1.2.3.4'));
$location = new VisitLocation(Location::emptyInstance()); $location = new VisitLocation(Location::emptyInstance());
$locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will( $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(

View File

@@ -40,26 +40,37 @@ class ShortUrl extends AbstractEntity
private ?string $importOriginalShortCode = null; private ?string $importOriginalShortCode = null;
private ?ApiKey $authorApiKey = null; private ?ApiKey $authorApiKey = null;
public function __construct( public static function createEmpty(): self
string $longUrl, {
?ShortUrlMeta $meta = null, return self::fromMeta(ShortUrlMeta::createEmpty());
}
public static function withLongUrl(string $longUrl): self
{
return self::fromMeta(ShortUrlMeta::fromRawData([ShortUrlMetaInputFilter::LONG_URL => $longUrl]));
}
public static function fromMeta(
ShortUrlMeta $meta,
?ShortUrlRelationResolverInterface $relationResolver = null ?ShortUrlRelationResolverInterface $relationResolver = null
) { ): self {
$meta = $meta ?? ShortUrlMeta::createEmpty(); $instance = new self();
$relationResolver = $relationResolver ?? new SimpleShortUrlRelationResolver(); $relationResolver = $relationResolver ?? new SimpleShortUrlRelationResolver();
$this->longUrl = $longUrl; $instance->longUrl = $meta->getLongUrl();
$this->dateCreated = Chronos::now(); $instance->dateCreated = Chronos::now();
$this->visits = new ArrayCollection(); $instance->visits = new ArrayCollection();
$this->tags = new ArrayCollection(); $instance->tags = new ArrayCollection();
$this->validSince = $meta->getValidSince(); $instance->validSince = $meta->getValidSince();
$this->validUntil = $meta->getValidUntil(); $instance->validUntil = $meta->getValidUntil();
$this->maxVisits = $meta->getMaxVisits(); $instance->maxVisits = $meta->getMaxVisits();
$this->customSlugWasProvided = $meta->hasCustomSlug(); $instance->customSlugWasProvided = $meta->hasCustomSlug();
$this->shortCodeLength = $meta->getShortCodeLength(); $instance->shortCodeLength = $meta->getShortCodeLength();
$this->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode($this->shortCodeLength); $instance->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode($instance->shortCodeLength);
$this->domain = $relationResolver->resolveDomain($meta->getDomain()); $instance->domain = $relationResolver->resolveDomain($meta->getDomain());
$this->authorApiKey = $meta->getApiKey(); $instance->authorApiKey = $meta->getApiKey();
return $instance;
} }
public static function fromImport( public static function fromImport(
@@ -68,6 +79,7 @@ class ShortUrl extends AbstractEntity
?ShortUrlRelationResolverInterface $relationResolver = null ?ShortUrlRelationResolverInterface $relationResolver = null
): self { ): self {
$meta = [ $meta = [
ShortUrlMetaInputFilter::LONG_URL => $url->longUrl(),
ShortUrlMetaInputFilter::DOMAIN => $url->domain(), ShortUrlMetaInputFilter::DOMAIN => $url->domain(),
ShortUrlMetaInputFilter::VALIDATE_URL => false, ShortUrlMetaInputFilter::VALIDATE_URL => false,
]; ];
@@ -75,7 +87,7 @@ class ShortUrl extends AbstractEntity
$meta[ShortUrlMetaInputFilter::CUSTOM_SLUG] = $url->shortCode(); $meta[ShortUrlMetaInputFilter::CUSTOM_SLUG] = $url->shortCode();
} }
$instance = new self($url->longUrl(), ShortUrlMeta::fromRawData($meta), $relationResolver); $instance = self::fromMeta(ShortUrlMeta::fromRawData($meta), $relationResolver);
$instance->importSource = $url->source(); $instance->importSource = $url->source();
$instance->importOriginalShortCode = $url->shortCode(); $instance->importOriginalShortCode = $url->shortCode();
$instance->dateCreated = Chronos::instance($url->createdAt()); $instance->dateCreated = Chronos::instance($url->createdAt());

View File

@@ -6,22 +6,15 @@ namespace Shlinkio\Shlink\Core\Model;
final class CreateShortUrlData final class CreateShortUrlData
{ {
private string $longUrl;
private array $tags; private array $tags;
private ShortUrlMeta $meta; private ShortUrlMeta $meta;
public function __construct(string $longUrl, array $tags = [], ?ShortUrlMeta $meta = null) public function __construct(array $tags = [], ?ShortUrlMeta $meta = null)
{ {
$this->longUrl = $longUrl;
$this->tags = $tags; $this->tags = $tags;
$this->meta = $meta ?? ShortUrlMeta::createEmpty(); $this->meta = $meta ?? ShortUrlMeta::createEmpty();
} }
public function getLongUrl(): string
{
return $this->longUrl;
}
/** /**
* @return string[] * @return string[]
*/ */

View File

@@ -17,6 +17,7 @@ use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH;
final class ShortUrlMeta final class ShortUrlMeta
{ {
private string $longUrl;
private ?Chronos $validSince = null; private ?Chronos $validSince = null;
private ?Chronos $validUntil = null; private ?Chronos $validUntil = null;
private ?string $customSlug = null; private ?string $customSlug = null;
@@ -34,7 +35,10 @@ final class ShortUrlMeta
public static function createEmpty(): self public static function createEmpty(): self
{ {
return new self(); $meta = new self();
$meta->longUrl = '';
return $meta;
} }
/** /**
@@ -52,11 +56,12 @@ final class ShortUrlMeta
*/ */
private function validateAndInit(array $data): void private function validateAndInit(array $data): void
{ {
$inputFilter = new ShortUrlMetaInputFilter($data); $inputFilter = new ShortUrlMetaInputFilter($data, true);
if (! $inputFilter->isValid()) { if (! $inputFilter->isValid()) {
throw ValidationException::fromInputFilter($inputFilter); throw ValidationException::fromInputFilter($inputFilter);
} }
$this->longUrl = $inputFilter->getValue(ShortUrlMetaInputFilter::LONG_URL);
$this->validSince = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_SINCE)); $this->validSince = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_SINCE));
$this->validUntil = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_UNTIL)); $this->validUntil = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_UNTIL));
$this->customSlug = $inputFilter->getValue(ShortUrlMetaInputFilter::CUSTOM_SLUG); $this->customSlug = $inputFilter->getValue(ShortUrlMetaInputFilter::CUSTOM_SLUG);
@@ -71,6 +76,11 @@ final class ShortUrlMeta
$this->apiKey = $inputFilter->getValue(ShortUrlMetaInputFilter::API_KEY); $this->apiKey = $inputFilter->getValue(ShortUrlMetaInputFilter::API_KEY);
} }
public function getLongUrl(): string
{
return $this->longUrl;
}
public function getValidSince(): ?Chronos public function getValidSince(): ?Chronos
{ {
return $this->validSince; return $this->validSince;

View File

@@ -201,14 +201,14 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
return $qb; return $qb;
} }
public function findOneMatching(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl public function findOneMatching(array $tags, ShortUrlMeta $meta): ?ShortUrl
{ {
$qb = $this->getEntityManager()->createQueryBuilder(); $qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('s') $qb->select('s')
->from(ShortUrl::class, 's') ->from(ShortUrl::class, 's')
->where($qb->expr()->eq('s.longUrl', ':longUrl')) ->where($qb->expr()->eq('s.longUrl', ':longUrl'))
->setParameter('longUrl', $url) ->setParameter('longUrl', $meta->getLongUrl())
->setMaxResults(1) ->setMaxResults(1)
->orderBy('s.id'); ->orderBy('s.id');

View File

@@ -38,7 +38,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificat
public function shortCodeIsInUse(string $slug, ?string $domain, ?Specification $spec = null): bool; public function shortCodeIsInUse(string $slug, ?string $domain, ?Specification $spec = null): bool;
public function findOneMatching(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl; public function findOneMatching(array $tags, ShortUrlMeta $meta): ?ShortUrl;
public function importedUrlExists(ImportedShlinkUrl $url): bool; public function importedUrlExists(ImportedShlinkUrl $url): bool;
} }

View File

@@ -43,18 +43,18 @@ class UrlShortener implements UrlShortenerInterface
* @throws InvalidUrlException * @throws InvalidUrlException
* @throws Throwable * @throws Throwable
*/ */
public function shorten(string $url, array $tags, ShortUrlMeta $meta): ShortUrl public function shorten(array $tags, ShortUrlMeta $meta): ShortUrl
{ {
// First, check if a short URL exists for all provided params // First, check if a short URL exists for all provided params
$existingShortUrl = $this->findExistingShortUrlIfExists($url, $tags, $meta); $existingShortUrl = $this->findExistingShortUrlIfExists($tags, $meta);
if ($existingShortUrl !== null) { if ($existingShortUrl !== null) {
return $existingShortUrl; return $existingShortUrl;
} }
$this->urlValidator->validateUrl($url, $meta->doValidateUrl()); $this->urlValidator->validateUrl($meta->getLongUrl(), $meta->doValidateUrl());
return $this->em->transactional(function () use ($url, $tags, $meta) { return $this->em->transactional(function () use ($tags, $meta) {
$shortUrl = new ShortUrl($url, $meta, $this->relationResolver); $shortUrl = ShortUrl::fromMeta($meta, $this->relationResolver);
$shortUrl->setTags($this->tagNamesToEntities($this->em, $tags)); $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags));
$this->verifyShortCodeUniqueness($meta, $shortUrl); $this->verifyShortCodeUniqueness($meta, $shortUrl);
@@ -64,7 +64,7 @@ class UrlShortener implements UrlShortenerInterface
}); });
} }
private function findExistingShortUrlIfExists(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl private function findExistingShortUrlIfExists(array $tags, ShortUrlMeta $meta): ?ShortUrl
{ {
if (! $meta->findIfExists()) { if (! $meta->findIfExists()) {
return null; return null;
@@ -72,7 +72,7 @@ class UrlShortener implements UrlShortenerInterface
/** @var ShortUrlRepositoryInterface $repo */ /** @var ShortUrlRepositoryInterface $repo */
$repo = $this->em->getRepository(ShortUrl::class); $repo = $this->em->getRepository(ShortUrl::class);
return $repo->findOneMatching($url, $tags, $meta); return $repo->findOneMatching($tags, $meta);
} }
private function verifyShortCodeUniqueness(ShortUrlMeta $meta, ShortUrl $shortUrlToBeCreated): void private function verifyShortCodeUniqueness(ShortUrlMeta $meta, ShortUrl $shortUrlToBeCreated): void

View File

@@ -16,5 +16,5 @@ interface UrlShortenerInterface
* @throws NonUniqueSlugException * @throws NonUniqueSlugException
* @throws InvalidUrlException * @throws InvalidUrlException
*/ */
public function shorten(string $url, array $tags, ShortUrlMeta $meta): ShortUrl; public function shorten(array $tags, ShortUrlMeta $meta): ShortUrl;
} }

View File

@@ -31,15 +31,26 @@ class ShortUrlMetaInputFilter extends InputFilter
public const VALIDATE_URL = 'validateUrl'; public const VALIDATE_URL = 'validateUrl';
public const API_KEY = 'apiKey'; public const API_KEY = 'apiKey';
public function __construct(array $data) private bool $requireLongUrl;
public function __construct(array $data, bool $requireLongUrl = false)
{ {
$this->requireLongUrl = $requireLongUrl;
$this->initialize(); $this->initialize();
$this->setData($data); $this->setData($data);
} }
private function initialize(): void private function initialize(): void
{ {
$this->add($this->createInput(self::LONG_URL, false)); $longUrlInput = $this->createInput(self::LONG_URL, $this->requireLongUrl);
$longUrlInput->getValidatorChain()->attach(new Validator\NotEmpty([
Validator\NotEmpty::OBJECT,
Validator\NotEmpty::SPACE,
Validator\NotEmpty::NULL,
Validator\NotEmpty::EMPTY_ARRAY,
Validator\NotEmpty::BOOLEAN,
]));
$this->add($longUrlInput);
$validSince = $this->createInput(self::VALID_SINCE, false); $validSince = $this->createInput(self::VALID_SINCE, false);
$validSince->getValidatorChain()->attach(new Validator\Date(['format' => DateTime::ATOM])); $validSince->getValidatorChain()->attach(new Validator\Date(['format' => DateTime::ATOM]));

View File

@@ -88,9 +88,8 @@ class DomainRepositoryTest extends DatabaseTestCase
private function createShortUrl(Domain $domain, ?ApiKey $apiKey = null): ShortUrl private function createShortUrl(Domain $domain, ?ApiKey $apiKey = null): ShortUrl
{ {
return new ShortUrl( return ShortUrl::fromMeta(
'foo', ShortUrlMeta::fromRawData(['domain' => $domain->getAuthority(), 'apiKey' => $apiKey, 'longUrl' => 'foo']),
ShortUrlMeta::fromRawData(['domain' => $domain->getAuthority(), 'apiKey' => $apiKey]),
new class ($domain) implements ShortUrlRelationResolverInterface { new class ($domain) implements ShortUrlRelationResolverInterface {
private Domain $domain; private Domain $domain;

View File

@@ -39,16 +39,16 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
/** @test */ /** @test */
public function findOneWithDomainFallbackReturnsProperData(): void public function findOneWithDomainFallbackReturnsProperData(): void
{ {
$regularOne = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'foo'])); $regularOne = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['customSlug' => 'foo', 'longUrl' => 'foo']));
$this->getEntityManager()->persist($regularOne); $this->getEntityManager()->persist($regularOne);
$withDomain = new ShortUrl('foo', ShortUrlMeta::fromRawData( $withDomain = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['domain' => 'example.com', 'customSlug' => 'domain-short-code'], ['domain' => 'example.com', 'customSlug' => 'domain-short-code', 'longUrl' => 'foo'],
)); ));
$this->getEntityManager()->persist($withDomain); $this->getEntityManager()->persist($withDomain);
$withDomainDuplicatingRegular = new ShortUrl('foo_with_domain', ShortUrlMeta::fromRawData( $withDomainDuplicatingRegular = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['domain' => 'doma.in', 'customSlug' => 'foo'], ['domain' => 'doma.in', 'customSlug' => 'foo', 'longUrl' => 'foo_with_domain'],
)); ));
$this->getEntityManager()->persist($withDomainDuplicatingRegular); $this->getEntityManager()->persist($withDomainDuplicatingRegular);
@@ -80,7 +80,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
{ {
$count = 5; $count = 5;
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$this->getEntityManager()->persist(new ShortUrl((string) $i)); $this->getEntityManager()->persist(ShortUrl::withLongUrl((string) $i));
} }
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
@@ -93,17 +93,17 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$tag = new Tag('bar'); $tag = new Tag('bar');
$this->getEntityManager()->persist($tag); $this->getEntityManager()->persist($tag);
$foo = new ShortUrl('foo'); $foo = ShortUrl::withLongUrl('foo');
$foo->setTags(new ArrayCollection([$tag])); $foo->setTags(new ArrayCollection([$tag]));
$this->getEntityManager()->persist($foo); $this->getEntityManager()->persist($foo);
$bar = new ShortUrl('bar'); $bar = ShortUrl::withLongUrl('bar');
$visit = new Visit($bar, Visitor::emptyInstance()); $visit = new Visit($bar, Visitor::emptyInstance());
$this->getEntityManager()->persist($visit); $this->getEntityManager()->persist($visit);
$bar->setVisits(new ArrayCollection([$visit])); $bar->setVisits(new ArrayCollection([$visit]));
$this->getEntityManager()->persist($bar); $this->getEntityManager()->persist($bar);
$foo2 = new ShortUrl('foo_2'); $foo2 = ShortUrl::withLongUrl('foo_2');
$ref = new ReflectionObject($foo2); $ref = new ReflectionObject($foo2);
$dateProp = $ref->getProperty('dateCreated'); $dateProp = $ref->getProperty('dateCreated');
$dateProp->setAccessible(true); $dateProp->setAccessible(true);
@@ -151,7 +151,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
{ {
$urls = ['a', 'z', 'c', 'b']; $urls = ['a', 'z', 'c', 'b'];
foreach ($urls as $url) { foreach ($urls as $url) {
$this->getEntityManager()->persist(new ShortUrl($url)); $this->getEntityManager()->persist(ShortUrl::withLongUrl($url));
} }
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
@@ -170,12 +170,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
/** @test */ /** @test */
public function shortCodeIsInUseLooksForShortUrlInProperSetOfTables(): void public function shortCodeIsInUseLooksForShortUrlInProperSetOfTables(): void
{ {
$shortUrlWithoutDomain = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'my-cool-slug'])); $shortUrlWithoutDomain = ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['customSlug' => 'my-cool-slug', 'longUrl' => 'foo']),
);
$this->getEntityManager()->persist($shortUrlWithoutDomain); $this->getEntityManager()->persist($shortUrlWithoutDomain);
$shortUrlWithDomain = new ShortUrl( $shortUrlWithDomain = ShortUrl::fromMeta(
'foo', ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug', 'longUrl' => 'foo']),
ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug']),
); );
$this->getEntityManager()->persist($shortUrlWithDomain); $this->getEntityManager()->persist($shortUrlWithDomain);
@@ -192,12 +193,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
/** @test */ /** @test */
public function findOneLooksForShortUrlInProperSetOfTables(): void public function findOneLooksForShortUrlInProperSetOfTables(): void
{ {
$shortUrlWithoutDomain = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'my-cool-slug'])); $shortUrlWithoutDomain = ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['customSlug' => 'my-cool-slug', 'longUrl' => 'foo']),
);
$this->getEntityManager()->persist($shortUrlWithoutDomain); $this->getEntityManager()->persist($shortUrlWithoutDomain);
$shortUrlWithDomain = new ShortUrl( $shortUrlWithDomain = ShortUrl::fromMeta(
'foo', ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug', 'longUrl' => 'foo']),
ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug']),
); );
$this->getEntityManager()->persist($shortUrlWithDomain); $this->getEntityManager()->persist($shortUrlWithDomain);
@@ -214,12 +216,16 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
/** @test */ /** @test */
public function findOneMatchingReturnsNullForNonExistingShortUrls(): void public function findOneMatchingReturnsNullForNonExistingShortUrls(): void
{ {
self::assertNull($this->repo->findOneMatching('', [], ShortUrlMeta::createEmpty())); self::assertNull($this->repo->findOneMatching([], ShortUrlMeta::createEmpty()));
self::assertNull($this->repo->findOneMatching('foobar', [], ShortUrlMeta::createEmpty())); self::assertNull($this->repo->findOneMatching([], ShortUrlMeta::fromRawData(['longUrl' => 'foobar'])));
self::assertNull($this->repo->findOneMatching('foobar', ['foo', 'bar'], ShortUrlMeta::createEmpty())); self::assertNull($this->repo->findOneMatching(
self::assertNull($this->repo->findOneMatching('foobar', ['foo', 'bar'], ShortUrlMeta::fromRawData([ ['foo', 'bar'],
ShortUrlMeta::fromRawData(['longUrl' => 'foobar']),
));
self::assertNull($this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => Chronos::parse('2020-03-05 20:18:30'), 'validSince' => Chronos::parse('2020-03-05 20:18:30'),
'customSlug' => 'this_slug_does_not_exist', 'customSlug' => 'this_slug_does_not_exist',
'longUrl' => 'foobar',
]))); ])));
} }
@@ -229,56 +235,65 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$start = Chronos::parse('2020-03-05 20:18:30'); $start = Chronos::parse('2020-03-05 20:18:30');
$end = Chronos::parse('2021-03-05 20:18:30'); $end = Chronos::parse('2021-03-05 20:18:30');
$shortUrl = new ShortUrl('foo', ShortUrlMeta::fromRawData(['validSince' => $start])); $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['validSince' => $start, 'longUrl' => 'foo']));
$shortUrl->setTags($this->tagNamesToEntities($this->getEntityManager(), ['foo', 'bar'])); $shortUrl->setTags($this->tagNamesToEntities($this->getEntityManager(), ['foo', 'bar']));
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
$shortUrl2 = new ShortUrl('bar', ShortUrlMeta::fromRawData(['validUntil' => $end])); $shortUrl2 = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['validUntil' => $end, 'longUrl' => 'bar']));
$this->getEntityManager()->persist($shortUrl2); $this->getEntityManager()->persist($shortUrl2);
$shortUrl3 = new ShortUrl('baz', ShortUrlMeta::fromRawData(['validSince' => $start, 'validUntil' => $end])); $shortUrl3 = ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['validSince' => $start, 'validUntil' => $end, 'longUrl' => 'baz']),
);
$this->getEntityManager()->persist($shortUrl3); $this->getEntityManager()->persist($shortUrl3);
$shortUrl4 = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'custom', 'validUntil' => $end])); $shortUrl4 = ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['customSlug' => 'custom', 'validUntil' => $end, 'longUrl' => 'foo']),
);
$this->getEntityManager()->persist($shortUrl4); $this->getEntityManager()->persist($shortUrl4);
$shortUrl5 = new ShortUrl('foo', ShortUrlMeta::fromRawData(['maxVisits' => 3])); $shortUrl5 = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['maxVisits' => 3, 'longUrl' => 'foo']));
$this->getEntityManager()->persist($shortUrl5); $this->getEntityManager()->persist($shortUrl5);
$shortUrl6 = new ShortUrl('foo', ShortUrlMeta::fromRawData(['domain' => 'doma.in'])); $shortUrl6 = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'longUrl' => 'foo']));
$this->getEntityManager()->persist($shortUrl6); $this->getEntityManager()->persist($shortUrl6);
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
self::assertSame( self::assertSame(
$shortUrl, $shortUrl,
$this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData(['validSince' => $start])), $this->repo->findOneMatching(
['foo', 'bar'],
ShortUrlMeta::fromRawData(['validSince' => $start, 'longUrl' => 'foo']),
),
); );
self::assertSame( self::assertSame(
$shortUrl2, $shortUrl2,
$this->repo->findOneMatching('bar', [], ShortUrlMeta::fromRawData(['validUntil' => $end])), $this->repo->findOneMatching([], ShortUrlMeta::fromRawData(['validUntil' => $end, 'longUrl' => 'bar'])),
); );
self::assertSame( self::assertSame(
$shortUrl3, $shortUrl3,
$this->repo->findOneMatching('baz', [], ShortUrlMeta::fromRawData([ $this->repo->findOneMatching([], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'validUntil' => $end, 'validUntil' => $end,
'longUrl' => 'baz',
])), ])),
); );
self::assertSame( self::assertSame(
$shortUrl4, $shortUrl4,
$this->repo->findOneMatching('foo', [], ShortUrlMeta::fromRawData([ $this->repo->findOneMatching([], ShortUrlMeta::fromRawData([
'customSlug' => 'custom', 'customSlug' => 'custom',
'validUntil' => $end, 'validUntil' => $end,
'longUrl' => 'foo',
])), ])),
); );
self::assertSame( self::assertSame(
$shortUrl5, $shortUrl5,
$this->repo->findOneMatching('foo', [], ShortUrlMeta::fromRawData(['maxVisits' => 3])), $this->repo->findOneMatching([], ShortUrlMeta::fromRawData(['maxVisits' => 3, 'longUrl' => 'foo'])),
); );
self::assertSame( self::assertSame(
$shortUrl6, $shortUrl6,
$this->repo->findOneMatching('foo', [], ShortUrlMeta::fromRawData(['domain' => 'doma.in'])), $this->repo->findOneMatching([], ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'longUrl' => 'foo'])),
); );
} }
@@ -286,25 +301,25 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
public function findOneMatchingReturnsOldestOneWhenThereAreMultipleMatches(): void public function findOneMatchingReturnsOldestOneWhenThereAreMultipleMatches(): void
{ {
$start = Chronos::parse('2020-03-05 20:18:30'); $start = Chronos::parse('2020-03-05 20:18:30');
$meta = ['validSince' => $start, 'maxVisits' => 50]; $meta = ShortUrlMeta::fromRawData(['validSince' => $start, 'maxVisits' => 50, 'longUrl' => 'foo']);
$tags = ['foo', 'bar']; $tags = ['foo', 'bar'];
$tagEntities = $this->tagNamesToEntities($this->getEntityManager(), $tags); $tagEntities = $this->tagNamesToEntities($this->getEntityManager(), $tags);
$shortUrl1 = new ShortUrl('foo', ShortUrlMeta::fromRawData($meta)); $shortUrl1 = ShortUrl::fromMeta($meta);
$shortUrl1->setTags($tagEntities); $shortUrl1->setTags($tagEntities);
$this->getEntityManager()->persist($shortUrl1); $this->getEntityManager()->persist($shortUrl1);
$shortUrl2 = new ShortUrl('foo', ShortUrlMeta::fromRawData($meta)); $shortUrl2 = ShortUrl::fromMeta($meta);
$shortUrl2->setTags($tagEntities); $shortUrl2->setTags($tagEntities);
$this->getEntityManager()->persist($shortUrl2); $this->getEntityManager()->persist($shortUrl2);
$shortUrl3 = new ShortUrl('foo', ShortUrlMeta::fromRawData($meta)); $shortUrl3 = ShortUrl::fromMeta($meta);
$shortUrl3->setTags($tagEntities); $shortUrl3->setTags($tagEntities);
$this->getEntityManager()->persist($shortUrl3); $this->getEntityManager()->persist($shortUrl3);
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
$result = $this->repo->findOneMatching('foo', $tags, ShortUrlMeta::fromRawData($meta)); $result = $this->repo->findOneMatching($tags, $meta);
self::assertSame($shortUrl1, $result); self::assertSame($shortUrl1, $result);
self::assertNotSame($shortUrl2, $result); self::assertNotSame($shortUrl2, $result);
@@ -332,8 +347,8 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$rightDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($rightDomain)); $rightDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($rightDomain));
$this->getEntityManager()->persist($rightDomainApiKey); $this->getEntityManager()->persist($rightDomainApiKey);
$shortUrl = new ShortUrl('foo', ShortUrlMeta::fromRawData( $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['validSince' => $start, 'apiKey' => $apiKey, 'domain' => $rightDomain->getAuthority()], ['validSince' => $start, 'apiKey' => $apiKey, 'domain' => $rightDomain->getAuthority(), 'longUrl' => 'foo'],
), new PersistenceShortUrlRelationResolver($this->getEntityManager())); ), new PersistenceShortUrlRelationResolver($this->getEntityManager()));
$shortUrl->setTags($this->tagNamesToEntities($this->getEntityManager(), ['foo', 'bar'])); $shortUrl->setTags($this->tagNamesToEntities($this->getEntityManager(), ['foo', 'bar']));
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
@@ -342,45 +357,54 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
self::assertSame( self::assertSame(
$shortUrl, $shortUrl,
$this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData(['validSince' => $start])), $this->repo->findOneMatching(
['foo', 'bar'],
ShortUrlMeta::fromRawData(['validSince' => $start, 'longUrl' => 'foo']),
),
); );
self::assertSame($shortUrl, $this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData([ self::assertSame($shortUrl, $this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'apiKey' => $apiKey, 'apiKey' => $apiKey,
'longUrl' => 'foo',
]))); ])));
self::assertNull($this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData([ self::assertNull($this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'apiKey' => $otherApiKey, 'apiKey' => $otherApiKey,
'longUrl' => 'foo',
]))); ])));
self::assertSame( self::assertSame(
$shortUrl, $shortUrl,
$this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData([ $this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'domain' => $rightDomain->getAuthority(), 'domain' => $rightDomain->getAuthority(),
'longUrl' => 'foo',
])), ])),
); );
self::assertSame( self::assertSame(
$shortUrl, $shortUrl,
$this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData([ $this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'domain' => $rightDomain->getAuthority(), 'domain' => $rightDomain->getAuthority(),
'apiKey' => $rightDomainApiKey, 'apiKey' => $rightDomainApiKey,
'longUrl' => 'foo',
])), ])),
); );
self::assertSame( self::assertSame(
$shortUrl, $shortUrl,
$this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData([ $this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'domain' => $rightDomain->getAuthority(), 'domain' => $rightDomain->getAuthority(),
'apiKey' => $apiKey, 'apiKey' => $apiKey,
'longUrl' => 'foo',
])), ])),
); );
self::assertNull( self::assertNull(
$this->repo->findOneMatching('foo', ['foo', 'bar'], ShortUrlMeta::fromRawData([ $this->repo->findOneMatching(['foo', 'bar'], ShortUrlMeta::fromRawData([
'validSince' => $start, 'validSince' => $start,
'domain' => $rightDomain->getAuthority(), 'domain' => $rightDomain->getAuthority(),
'apiKey' => $wrongDomainApiKey, 'apiKey' => $wrongDomainApiKey,
'longUrl' => 'foo',
])), ])),
); );
} }

View File

@@ -62,14 +62,14 @@ class TagRepositoryTest extends DatabaseTestCase
[$firstUrlTags] = array_chunk($tags, 3); [$firstUrlTags] = array_chunk($tags, 3);
$secondUrlTags = [$tags[0]]; $secondUrlTags = [$tags[0]];
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$shortUrl->setTags(new ArrayCollection($firstUrlTags)); $shortUrl->setTags(new ArrayCollection($firstUrlTags));
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
$this->getEntityManager()->persist(new Visit($shortUrl, Visitor::emptyInstance())); $this->getEntityManager()->persist(new Visit($shortUrl, Visitor::emptyInstance()));
$this->getEntityManager()->persist(new Visit($shortUrl, Visitor::emptyInstance())); $this->getEntityManager()->persist(new Visit($shortUrl, Visitor::emptyInstance()));
$this->getEntityManager()->persist(new Visit($shortUrl, Visitor::emptyInstance())); $this->getEntityManager()->persist(new Visit($shortUrl, Visitor::emptyInstance()));
$shortUrl2 = new ShortUrl(''); $shortUrl2 = ShortUrl::createEmpty();
$shortUrl2->setTags(new ArrayCollection($secondUrlTags)); $shortUrl2->setTags(new ArrayCollection($secondUrlTags));
$this->getEntityManager()->persist($shortUrl2); $this->getEntityManager()->persist($shortUrl2);
$this->getEntityManager()->persist(new Visit($shortUrl2, Visitor::emptyInstance())); $this->getEntityManager()->persist(new Visit($shortUrl2, Visitor::emptyInstance()));
@@ -119,13 +119,12 @@ class TagRepositoryTest extends DatabaseTestCase
[$firstUrlTags, $secondUrlTags] = array_chunk($tags, 3); [$firstUrlTags, $secondUrlTags] = array_chunk($tags, 3);
$shortUrl = new ShortUrl('', ShortUrlMeta::fromRawData(['apiKey' => $authorApiKey])); $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['apiKey' => $authorApiKey, 'longUrl' => '']));
$shortUrl->setTags(new ArrayCollection($firstUrlTags)); $shortUrl->setTags(new ArrayCollection($firstUrlTags));
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
$shortUrl2 = new ShortUrl( $shortUrl2 = ShortUrl::fromMeta(
'', ShortUrlMeta::fromRawData(['domain' => $domain->getAuthority(), 'longUrl' => '']),
ShortUrlMeta::fromRawData(['domain' => $domain->getAuthority()]),
new PersistenceShortUrlRelationResolver($this->getEntityManager()), new PersistenceShortUrlRelationResolver($this->getEntityManager()),
); );
$shortUrl2->setTags(new ArrayCollection($secondUrlTags)); $shortUrl2->setTags(new ArrayCollection($secondUrlTags));

View File

@@ -40,7 +40,7 @@ class VisitRepositoryTest extends DatabaseTestCase
*/ */
public function findVisitsReturnsProperVisits(int $blockSize): void public function findVisitsReturnsProperVisits(int $blockSize): void
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
$countIterable = function (iterable $results): int { $countIterable = function (iterable $results): int {
$resultsCount = 0; $resultsCount = 0;
@@ -190,9 +190,8 @@ class VisitRepositoryTest extends DatabaseTestCase
$apiKey1 = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls()); $apiKey1 = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$this->getEntityManager()->persist($apiKey1); $this->getEntityManager()->persist($apiKey1);
$shortUrl = new ShortUrl( $shortUrl = ShortUrl::fromMeta(
'', ShortUrlMeta::fromRawData(['apiKey' => $apiKey1, 'domain' => $domain->getAuthority(), 'longUrl' => '']),
ShortUrlMeta::fromRawData(['apiKey' => $apiKey1, 'domain' => $domain->getAuthority()]),
new PersistenceShortUrlRelationResolver($this->getEntityManager()), new PersistenceShortUrlRelationResolver($this->getEntityManager()),
); );
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
@@ -200,13 +199,12 @@ class VisitRepositoryTest extends DatabaseTestCase
$apiKey2 = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls()); $apiKey2 = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$this->getEntityManager()->persist($apiKey2); $this->getEntityManager()->persist($apiKey2);
$shortUrl2 = new ShortUrl('', ShortUrlMeta::fromRawData(['apiKey' => $apiKey2])); $shortUrl2 = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['apiKey' => $apiKey2, 'longUrl' => '']));
$this->getEntityManager()->persist($shortUrl2); $this->getEntityManager()->persist($shortUrl2);
$this->createVisitsForShortUrl($shortUrl2, 5); $this->createVisitsForShortUrl($shortUrl2, 5);
$shortUrl3 = new ShortUrl( $shortUrl3 = ShortUrl::fromMeta(
'', ShortUrlMeta::fromRawData(['apiKey' => $apiKey2, 'domain' => $domain->getAuthority(), 'longUrl' => '']),
ShortUrlMeta::fromRawData(['apiKey' => $apiKey2, 'domain' => $domain->getAuthority()]),
new PersistenceShortUrlRelationResolver($this->getEntityManager()), new PersistenceShortUrlRelationResolver($this->getEntityManager()),
); );
$this->getEntityManager()->persist($shortUrl3); $this->getEntityManager()->persist($shortUrl3);
@@ -225,7 +223,7 @@ class VisitRepositoryTest extends DatabaseTestCase
private function createShortUrlsAndVisits(bool $withDomain = true): array private function createShortUrlsAndVisits(bool $withDomain = true): array
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$domain = 'example.com'; $domain = 'example.com';
$shortCode = $shortUrl->getShortCode(); $shortCode = $shortUrl->getShortCode();
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
@@ -233,9 +231,10 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->createVisitsForShortUrl($shortUrl); $this->createVisitsForShortUrl($shortUrl);
if ($withDomain) { if ($withDomain) {
$shortUrlWithDomain = new ShortUrl('', ShortUrlMeta::fromRawData([ $shortUrlWithDomain = ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
'customSlug' => $shortCode, 'customSlug' => $shortCode,
'domain' => $domain, 'domain' => $domain,
'longUrl' => '',
])); ]));
$this->getEntityManager()->persist($shortUrlWithDomain); $this->getEntityManager()->persist($shortUrlWithDomain);
$this->createVisitsForShortUrl($shortUrlWithDomain, 3); $this->createVisitsForShortUrl($shortUrlWithDomain, 3);

View File

@@ -43,7 +43,7 @@ class PixelActionTest extends TestCase
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))->willReturn( $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))->willReturn(
new ShortUrl('http://domain.com/foo/bar'), ShortUrl::withLongUrl('http://domain.com/foo/bar'),
)->shouldBeCalledOnce(); )->shouldBeCalledOnce();
$this->visitTracker->track(Argument::cetera())->shouldBeCalledOnce(); $this->visitTracker->track(Argument::cetera())->shouldBeCalledOnce();

View File

@@ -60,7 +60,7 @@ class QrCodeActionTest extends TestCase
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, '')) $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))
->willReturn(new ShortUrl('')) ->willReturn(ShortUrl::createEmpty())
->shouldBeCalledOnce(); ->shouldBeCalledOnce();
$delegate = $this->prophesize(RequestHandlerInterface::class); $delegate = $this->prophesize(RequestHandlerInterface::class);
@@ -83,7 +83,9 @@ class QrCodeActionTest extends TestCase
string $expectedContentType string $expectedContentType
): void { ): void {
$code = 'abc123'; $code = 'abc123';
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(new ShortUrl('')); $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(
ShortUrl::createEmpty(),
);
$delegate = $this->prophesize(RequestHandlerInterface::class); $delegate = $this->prophesize(RequestHandlerInterface::class);
$req = (new ServerRequest())->withAttribute('shortCode', $code)->withQueryParams($query); $req = (new ServerRequest())->withAttribute('shortCode', $code)->withQueryParams($query);
@@ -107,7 +109,9 @@ class QrCodeActionTest extends TestCase
public function imageIsReturnedWithExpectedSize(ServerRequestInterface $req, int $expectedSize): void public function imageIsReturnedWithExpectedSize(ServerRequestInterface $req, int $expectedSize): void
{ {
$code = 'abc123'; $code = 'abc123';
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(new ShortUrl('')); $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(
ShortUrl::createEmpty(),
);
$delegate = $this->prophesize(RequestHandlerInterface::class); $delegate = $this->prophesize(RequestHandlerInterface::class);
$resp = $this->action->process($req->withAttribute('shortCode', $code), $delegate->reveal()); $resp = $this->action->process($req->withAttribute('shortCode', $code), $delegate->reveal());

View File

@@ -54,7 +54,7 @@ class RedirectActionTest extends TestCase
public function redirectionIsPerformedToLongUrl(string $expectedUrl, array $query): void public function redirectionIsPerformedToLongUrl(string $expectedUrl, array $query): void
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$shortUrl = new ShortUrl('http://domain.com/foo/bar?some=thing'); $shortUrl = ShortUrl::withLongUrl('http://domain.com/foo/bar?some=thing');
$shortCodeToUrl = $this->urlResolver->resolveEnabledShortUrl( $shortCodeToUrl = $this->urlResolver->resolveEnabledShortUrl(
new ShortUrlIdentifier($shortCode, ''), new ShortUrlIdentifier($shortCode, ''),
)->willReturn($shortUrl); )->willReturn($shortUrl);
@@ -104,7 +104,7 @@ class RedirectActionTest extends TestCase
public function trackingIsDisabledWhenRequestIsForwardedFromHead(): void public function trackingIsDisabledWhenRequestIsForwardedFromHead(): void
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$shortUrl = new ShortUrl('http://domain.com/foo/bar?some=thing'); $shortUrl = ShortUrl::withLongUrl('http://domain.com/foo/bar?some=thing');
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))->willReturn($shortUrl); $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))->willReturn($shortUrl);
$track = $this->visitTracker->track(Argument::cetera())->will(function (): void { $track = $this->visitTracker->track(Argument::cetera())->will(function (): void {
}); });

View File

@@ -37,11 +37,11 @@ class ShortUrlTest extends TestCase
public function provideInvalidShortUrls(): iterable public function provideInvalidShortUrls(): iterable
{ {
yield 'with custom slug' => [ yield 'with custom slug' => [
new ShortUrl('', ShortUrlMeta::fromRawData(['customSlug' => 'custom-slug'])), ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => ''])),
'The short code cannot be regenerated on ShortUrls where a custom slug was provided.', 'The short code cannot be regenerated on ShortUrls where a custom slug was provided.',
]; ];
yield 'already persisted' => [ yield 'already persisted' => [
(new ShortUrl(''))->setId('1'), ShortUrl::createEmpty()->setId('1'),
'The short code can be regenerated only on new ShortUrls which have not been persisted yet.', 'The short code can be regenerated only on new ShortUrls which have not been persisted yet.',
]; ];
} }
@@ -62,7 +62,7 @@ class ShortUrlTest extends TestCase
public function provideValidShortUrls(): iterable public function provideValidShortUrls(): iterable
{ {
yield 'no custom slug' => [new ShortUrl('')]; yield 'no custom slug' => [ShortUrl::createEmpty()];
yield 'imported with custom slug' => [ yield 'imported with custom slug' => [
ShortUrl::fromImport(new ImportedShlinkUrl('', '', [], Chronos::now(), null, 'custom-slug'), true), ShortUrl::fromImport(new ImportedShlinkUrl('', '', [], Chronos::now(), null, 'custom-slug'), true),
]; ];
@@ -74,8 +74,8 @@ class ShortUrlTest extends TestCase
*/ */
public function shortCodesHaveExpectedLength(?int $length, int $expectedLength): void public function shortCodesHaveExpectedLength(?int $length, int $expectedLength): void
{ {
$shortUrl = new ShortUrl('', ShortUrlMeta::fromRawData( $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
[ShortUrlMetaInputFilter::SHORT_CODE_LENGTH => $length], [ShortUrlMetaInputFilter::SHORT_CODE_LENGTH => $length, 'longUrl' => ''],
)); ));
self::assertEquals($expectedLength, strlen($shortUrl->getShortCode())); self::assertEquals($expectedLength, strlen($shortUrl->getShortCode()));

View File

@@ -19,7 +19,7 @@ class VisitTest extends TestCase
*/ */
public function isProperlyJsonSerialized(?Chronos $date): void public function isProperlyJsonSerialized(?Chronos $date): void
{ {
$visit = new Visit(new ShortUrl(''), new Visitor('Chrome', 'some site', '1.2.3.4'), true, $date); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('Chrome', 'some site', '1.2.3.4'), true, $date);
self::assertEquals([ self::assertEquals([
'referer' => 'some site', 'referer' => 'some site',
@@ -41,7 +41,7 @@ class VisitTest extends TestCase
*/ */
public function addressIsAnonymizedWhenRequested(bool $anonymize, ?string $address, ?string $expectedAddress): void public function addressIsAnonymizedWhenRequested(bool $anonymize, ?string $address, ?string $expectedAddress): void
{ {
$visit = new Visit(new ShortUrl(''), new Visitor('Chrome', 'some site', $address), $anonymize); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('Chrome', 'some site', $address), $anonymize);
self::assertEquals($expectedAddress, $visit->getRemoteAddr()); self::assertEquals($expectedAddress, $visit->getRemoteAddr());
} }

View File

@@ -78,7 +78,7 @@ class LocateShortUrlVisitTest extends TestCase
{ {
$event = new ShortUrlVisited('123'); $event = new ShortUrlVisited('123');
$findVisit = $this->em->find(Visit::class, '123')->willReturn( $findVisit = $this->em->find(Visit::class, '123')->willReturn(
new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4')), new Visit(ShortUrl::createEmpty(), new Visitor('', '', '1.2.3.4')),
); );
$resolveLocation = $this->ipLocationResolver->resolveIpLocation(Argument::cetera())->willThrow( $resolveLocation = $this->ipLocationResolver->resolveIpLocation(Argument::cetera())->willThrow(
WrongIpException::class, WrongIpException::class,
@@ -125,7 +125,7 @@ class LocateShortUrlVisitTest extends TestCase
public function provideNonLocatableVisits(): iterable public function provideNonLocatableVisits(): iterable
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
yield 'null IP' => [new Visit($shortUrl, new Visitor('', '', null))]; yield 'null IP' => [new Visit($shortUrl, new Visitor('', '', null))];
yield 'empty IP' => [new Visit($shortUrl, new Visitor('', '', ''))]; yield 'empty IP' => [new Visit($shortUrl, new Visitor('', '', ''))];
@@ -139,7 +139,7 @@ class LocateShortUrlVisitTest extends TestCase
public function locatableVisitsResolveToLocation(string $anonymizedIpAddress, ?string $originalIpAddress): void public function locatableVisitsResolveToLocation(string $anonymizedIpAddress, ?string $originalIpAddress): void
{ {
$ipAddr = $originalIpAddress ?? $anonymizedIpAddress; $ipAddr = $originalIpAddress ?? $anonymizedIpAddress;
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $ipAddr)); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('', '', $ipAddr));
$location = new Location('', '', '', '', 0.0, 0.0, ''); $location = new Location('', '', '', '', 0.0, 0.0, '');
$event = new ShortUrlVisited('123', $originalIpAddress); $event = new ShortUrlVisited('123', $originalIpAddress);
@@ -171,7 +171,7 @@ class LocateShortUrlVisitTest extends TestCase
{ {
$e = GeolocationDbUpdateFailedException::create(true); $e = GeolocationDbUpdateFailedException::create(true);
$ipAddr = '1.2.3.0'; $ipAddr = '1.2.3.0';
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $ipAddr)); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('', '', $ipAddr));
$location = new Location('', '', '', '', 0.0, 0.0, ''); $location = new Location('', '', '', '', 0.0, 0.0, '');
$event = new ShortUrlVisited('123'); $event = new ShortUrlVisited('123');
@@ -202,7 +202,7 @@ class LocateShortUrlVisitTest extends TestCase
{ {
$e = GeolocationDbUpdateFailedException::create(false); $e = GeolocationDbUpdateFailedException::create(false);
$ipAddr = '1.2.3.0'; $ipAddr = '1.2.3.0';
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $ipAddr)); $visit = new Visit(ShortUrl::createEmpty(), new Visitor('', '', $ipAddr));
$location = new Location('', '', '', '', 0.0, 0.0, ''); $location = new Location('', '', '', '', 0.0, 0.0, '');
$event = new ShortUrlVisited('123'); $event = new ShortUrlVisited('123');

View File

@@ -77,7 +77,7 @@ class NotifyVisitToMercureTest extends TestCase
public function notificationsAreSentWhenVisitIsFound(): void public function notificationsAreSentWhenVisitIsFound(): void
{ {
$visitId = '123'; $visitId = '123';
$visit = new Visit(new ShortUrl(''), Visitor::emptyInstance()); $visit = new Visit(ShortUrl::createEmpty(), Visitor::emptyInstance());
$update = new Update('', ''); $update = new Update('', '');
$findVisit = $this->em->find(Visit::class, $visitId)->willReturn($visit); $findVisit = $this->em->find(Visit::class, $visitId)->willReturn($visit);
@@ -101,7 +101,7 @@ class NotifyVisitToMercureTest extends TestCase
public function debugIsLoggedWhenExceptionIsThrown(): void public function debugIsLoggedWhenExceptionIsThrown(): void
{ {
$visitId = '123'; $visitId = '123';
$visit = new Visit(new ShortUrl(''), Visitor::emptyInstance()); $visit = new Visit(ShortUrl::createEmpty(), Visitor::emptyInstance());
$update = new Update('', ''); $update = new Update('', '');
$e = new RuntimeException('Error'); $e = new RuntimeException('Error');

View File

@@ -79,7 +79,9 @@ class NotifyVisitToWebHooksTest extends TestCase
$webhooks = ['foo', 'invalid', 'bar', 'baz']; $webhooks = ['foo', 'invalid', 'bar', 'baz'];
$invalidWebhooks = ['invalid', 'baz']; $invalidWebhooks = ['invalid', 'baz'];
$find = $this->em->find(Visit::class, '1')->willReturn(new Visit(new ShortUrl(''), Visitor::emptyInstance())); $find = $this->em->find(Visit::class, '1')->willReturn(
new Visit(ShortUrl::createEmpty(), Visitor::emptyInstance()),
);
$requestAsync = $this->httpClient->requestAsync( $requestAsync = $this->httpClient->requestAsync(
RequestMethodInterface::METHOD_POST, RequestMethodInterface::METHOD_POST,
Argument::type('string'), Argument::type('string'),

View File

@@ -28,7 +28,7 @@ class MercureUpdatesGeneratorTest extends TestCase
*/ */
public function visitIsProperlySerializedIntoUpdate(string $method, string $expectedTopic): void public function visitIsProperlySerializedIntoUpdate(string $method, string $expectedTopic): void
{ {
$shortUrl = new ShortUrl('', ShortUrlMeta::fromRawData(['customSlug' => 'foo'])); $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['customSlug' => 'foo', 'longUrl' => '']));
$visit = new Visit($shortUrl, Visitor::emptyInstance()); $visit = new Visit($shortUrl, Visitor::emptyInstance());
$update = $this->generator->{$method}($visit); $update = $this->generator->{$method}($visit);

View File

@@ -56,6 +56,9 @@ class ShortUrlMetaTest extends TestCase
yield [[ yield [[
ShortUrlMetaInputFilter::CUSTOM_SLUG => ' ', ShortUrlMetaInputFilter::CUSTOM_SLUG => ' ',
]]; ]];
yield [[
ShortUrlMetaInputFilter::LONG_URL => [],
]];
} }
/** /**
@@ -64,9 +67,11 @@ class ShortUrlMetaTest extends TestCase
*/ */
public function properlyCreatedInstanceReturnsValues(string $customSlug, string $expectedSlug): void public function properlyCreatedInstanceReturnsValues(string $customSlug, string $expectedSlug): void
{ {
$meta = ShortUrlMeta::fromRawData( $meta = ShortUrlMeta::fromRawData([
['validSince' => Chronos::parse('2015-01-01')->toAtomString(), 'customSlug' => $customSlug], 'validSince' => Chronos::parse('2015-01-01')->toAtomString(),
); 'customSlug' => $customSlug,
'longUrl' => '',
]);
self::assertTrue($meta->hasValidSince()); self::assertTrue($meta->hasValidSince());
self::assertEquals(Chronos::parse('2015-01-01'), $meta->getValidSince()); self::assertEquals(Chronos::parse('2015-01-01'), $meta->getValidSince());

View File

@@ -33,8 +33,8 @@ class DeleteShortUrlServiceTest extends TestCase
public function setUp(): void public function setUp(): void
{ {
$shortUrl = (new ShortUrl(''))->setVisits(new ArrayCollection( $shortUrl = ShortUrl::createEmpty()->setVisits(new ArrayCollection(
map(range(0, 10), fn () => new Visit(new ShortUrl(''), Visitor::emptyInstance())), map(range(0, 10), fn () => new Visit(ShortUrl::createEmpty(), Visitor::emptyInstance())),
)); ));
$this->shortCode = $shortUrl->getShortCode(); $this->shortCode = $shortUrl->getShortCode();

View File

@@ -44,7 +44,7 @@ class ShortUrlResolverTest extends TestCase
*/ */
public function shortCodeIsProperlyParsed(?ApiKey $apiKey): void public function shortCodeIsProperlyParsed(?ApiKey $apiKey): void
{ {
$shortUrl = new ShortUrl('expected_url'); $shortUrl = ShortUrl::withLongUrl('expected_url');
$shortCode = $shortUrl->getShortCode(); $shortCode = $shortUrl->getShortCode();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
@@ -80,7 +80,7 @@ class ShortUrlResolverTest extends TestCase
/** @test */ /** @test */
public function shortCodeToEnabledShortUrlProperlyParsesShortCode(): void public function shortCodeToEnabledShortUrlProperlyParsesShortCode(): void
{ {
$shortUrl = new ShortUrl('expected_url'); $shortUrl = ShortUrl::withLongUrl('expected_url');
$shortCode = $shortUrl->getShortCode(); $shortCode = $shortUrl->getShortCode();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
@@ -118,7 +118,7 @@ class ShortUrlResolverTest extends TestCase
$now = Chronos::now(); $now = Chronos::now();
yield 'maxVisits reached' => [(function () { yield 'maxVisits reached' => [(function () {
$shortUrl = new ShortUrl('', ShortUrlMeta::fromRawData(['maxVisits' => 3])); $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['maxVisits' => 3, 'longUrl' => '']));
$shortUrl->setVisits(new ArrayCollection(map( $shortUrl->setVisits(new ArrayCollection(map(
range(0, 4), range(0, 4),
fn () => new Visit($shortUrl, Visitor::emptyInstance()), fn () => new Visit($shortUrl, Visitor::emptyInstance()),
@@ -126,16 +126,17 @@ class ShortUrlResolverTest extends TestCase
return $shortUrl; return $shortUrl;
})()]; })()];
yield 'future validSince' => [new ShortUrl('', ShortUrlMeta::fromRawData([ yield 'future validSince' => [ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
'validSince' => $now->addMonth()->toAtomString(), ['validSince' => $now->addMonth()->toAtomString(), 'longUrl' => ''],
]))]; ))];
yield 'past validUntil' => [new ShortUrl('', ShortUrlMeta::fromRawData([ yield 'past validUntil' => [ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
'validUntil' => $now->subMonth()->toAtomString(), ['validUntil' => $now->subMonth()->toAtomString(), 'longUrl' => ''],
]))]; ))];
yield 'mixed' => [(function () use ($now) { yield 'mixed' => [(function () use ($now) {
$shortUrl = new ShortUrl('', ShortUrlMeta::fromRawData([ $shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
'maxVisits' => 3, 'maxVisits' => 3,
'validUntil' => $now->subMonth()->toAtomString(), 'validUntil' => $now->subMonth()->toAtomString(),
'longUrl' => '',
])); ]));
$shortUrl->setVisits(new ArrayCollection(map( $shortUrl->setVisits(new ArrayCollection(map(
range(0, 4), range(0, 4),

View File

@@ -58,10 +58,10 @@ class ShortUrlServiceTest extends TestCase
public function listedUrlsAreReturnedFromEntityManager(?ApiKey $apiKey): void public function listedUrlsAreReturnedFromEntityManager(?ApiKey $apiKey): void
{ {
$list = [ $list = [
new ShortUrl(''), ShortUrl::createEmpty(),
new ShortUrl(''), ShortUrl::createEmpty(),
new ShortUrl(''), ShortUrl::createEmpty(),
new ShortUrl(''), ShortUrl::createEmpty(),
]; ];
$repo = $this->prophesize(ShortUrlRepository::class); $repo = $this->prophesize(ShortUrlRepository::class);
@@ -106,7 +106,7 @@ class ShortUrlServiceTest extends TestCase
?ApiKey $apiKey ?ApiKey $apiKey
): void { ): void {
$originalLongUrl = 'originalLongUrl'; $originalLongUrl = 'originalLongUrl';
$shortUrl = new ShortUrl($originalLongUrl); $shortUrl = ShortUrl::withLongUrl($originalLongUrl);
$findShortUrl = $this->urlResolver->resolveShortUrl( $findShortUrl = $this->urlResolver->resolveShortUrl(
new ShortUrlIdentifier('abc123'), new ShortUrlIdentifier('abc123'),

View File

@@ -69,9 +69,8 @@ class UrlShortenerTest extends TestCase
public function urlIsProperlyShortened(): void public function urlIsProperlyShortened(): void
{ {
$shortUrl = $this->urlShortener->shorten( $shortUrl = $this->urlShortener->shorten(
'http://foobar.com/12345/hello?foo=bar',
[], [],
ShortUrlMeta::createEmpty(), ShortUrlMeta::fromRawData(['longUrl' => 'http://foobar.com/12345/hello?foo=bar']),
); );
self::assertEquals('http://foobar.com/12345/hello?foo=bar', $shortUrl->getLongUrl()); self::assertEquals('http://foobar.com/12345/hello?foo=bar', $shortUrl->getLongUrl());
@@ -85,28 +84,22 @@ class UrlShortenerTest extends TestCase
$ensureUniqueness->shouldBeCalledOnce(); $ensureUniqueness->shouldBeCalledOnce();
$this->expectException(NonUniqueSlugException::class); $this->expectException(NonUniqueSlugException::class);
$this->urlShortener->shorten( $this->urlShortener->shorten([], ShortUrlMeta::fromRawData(
'http://foobar.com/12345/hello?foo=bar', ['customSlug' => 'custom-slug', 'longUrl' => 'http://foobar.com/12345/hello?foo=bar'],
[], ));
ShortUrlMeta::fromRawData(['customSlug' => 'custom-slug']),
);
} }
/** /**
* @test * @test
* @dataProvider provideExistingShortUrls * @dataProvider provideExistingShortUrls
*/ */
public function existingShortUrlIsReturnedWhenRequested( public function existingShortUrlIsReturnedWhenRequested(array $tags, ShortUrlMeta $meta, ShortUrl $expected): void
string $url, {
array $tags,
ShortUrlMeta $meta,
ShortUrl $expected
): void {
$repo = $this->prophesize(ShortUrlRepository::class); $repo = $this->prophesize(ShortUrlRepository::class);
$findExisting = $repo->findOneMatching(Argument::cetera())->willReturn($expected); $findExisting = $repo->findOneMatching(Argument::cetera())->willReturn($expected);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$result = $this->urlShortener->shorten($url, $tags, $meta); $result = $this->urlShortener->shorten($tags, $meta);
$findExisting->shouldHaveBeenCalledOnce(); $findExisting->shouldHaveBeenCalledOnce();
$getRepo->shouldHaveBeenCalledOnce(); $getRepo->shouldHaveBeenCalledOnce();
@@ -119,52 +112,58 @@ class UrlShortenerTest extends TestCase
{ {
$url = 'http://foo.com'; $url = 'http://foo.com';
yield [$url, [], ShortUrlMeta::fromRawData(['findIfExists' => true]), new ShortUrl($url)]; yield [[], ShortUrlMeta::fromRawData(['findIfExists' => true, 'longUrl' => $url]), ShortUrl::withLongUrl(
yield [$url, [], ShortUrlMeta::fromRawData(
['findIfExists' => true, 'customSlug' => 'foo'],
), new ShortUrl($url)];
yield [
$url, $url,
)];
yield [[], ShortUrlMeta::fromRawData(
['findIfExists' => true, 'customSlug' => 'foo', 'longUrl' => $url],
), ShortUrl::withLongUrl($url)];
yield [
['foo', 'bar'], ['foo', 'bar'],
ShortUrlMeta::fromRawData(['findIfExists' => true]), ShortUrlMeta::fromRawData(['findIfExists' => true, 'longUrl' => $url]),
(new ShortUrl($url))->setTags(new ArrayCollection([new Tag('bar'), new Tag('foo')])), ShortUrl::withLongUrl($url)->setTags(new ArrayCollection([new Tag('bar'), new Tag('foo')])),
]; ];
yield [ yield [
$url,
[], [],
ShortUrlMeta::fromRawData(['findIfExists' => true, 'maxVisits' => 3]), ShortUrlMeta::fromRawData(['findIfExists' => true, 'maxVisits' => 3, 'longUrl' => $url]),
new ShortUrl($url, ShortUrlMeta::fromRawData(['maxVisits' => 3])), ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['maxVisits' => 3, 'longUrl' => $url])),
]; ];
yield [ yield [
$url,
[], [],
ShortUrlMeta::fromRawData(['findIfExists' => true, 'validSince' => Chronos::parse('2017-01-01')]), ShortUrlMeta::fromRawData(
new ShortUrl($url, ShortUrlMeta::fromRawData(['validSince' => Chronos::parse('2017-01-01')])), ['findIfExists' => true, 'validSince' => Chronos::parse('2017-01-01'), 'longUrl' => $url],
),
ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['validSince' => Chronos::parse('2017-01-01'), 'longUrl' => $url]),
),
]; ];
yield [ yield [
$url,
[], [],
ShortUrlMeta::fromRawData(['findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01')]), ShortUrlMeta::fromRawData(
new ShortUrl($url, ShortUrlMeta::fromRawData(['validUntil' => Chronos::parse('2017-01-01')])), ['findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01'), 'longUrl' => $url],
),
ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['validUntil' => Chronos::parse('2017-01-01'), 'longUrl' => $url]),
),
]; ];
yield [ yield [
$url,
[], [],
ShortUrlMeta::fromRawData(['findIfExists' => true, 'domain' => 'example.com']), ShortUrlMeta::fromRawData(['findIfExists' => true, 'domain' => 'example.com', 'longUrl' => $url]),
new ShortUrl($url, ShortUrlMeta::fromRawData(['domain' => 'example.com'])), ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['domain' => 'example.com', 'longUrl' => $url])),
]; ];
yield [ yield [
$url,
['baz', 'foo', 'bar'], ['baz', 'foo', 'bar'],
ShortUrlMeta::fromRawData([ ShortUrlMeta::fromRawData([
'findIfExists' => true, 'findIfExists' => true,
'validUntil' => Chronos::parse('2017-01-01'), 'validUntil' => Chronos::parse('2017-01-01'),
'maxVisits' => 4, 'maxVisits' => 4,
'longUrl' => $url,
]), ]),
(new ShortUrl($url, ShortUrlMeta::fromRawData([ ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
'validUntil' => Chronos::parse('2017-01-01'), 'validUntil' => Chronos::parse('2017-01-01'),
'maxVisits' => 4, 'maxVisits' => 4,
])))->setTags(new ArrayCollection([new Tag('foo'), new Tag('bar'), new Tag('baz')])), 'longUrl' => $url,
]))->setTags(new ArrayCollection([new Tag('foo'), new Tag('bar'), new Tag('baz')])),
]; ];
} }
} }

View File

@@ -56,7 +56,7 @@ class VisitsTrackerTest extends TestCase
$this->em->persist(Argument::that(fn (Visit $visit) => $visit->setId('1')))->shouldBeCalledOnce(); $this->em->persist(Argument::that(fn (Visit $visit) => $visit->setId('1')))->shouldBeCalledOnce();
$this->em->flush()->shouldBeCalledOnce(); $this->em->flush()->shouldBeCalledOnce();
$this->visitsTracker->track(new ShortUrl($shortCode), Visitor::emptyInstance()); $this->visitsTracker->track(ShortUrl::withLongUrl($shortCode), Visitor::emptyInstance());
$this->eventDispatcher->dispatch(Argument::type(ShortUrlVisited::class))->shouldHaveBeenCalled(); $this->eventDispatcher->dispatch(Argument::type(ShortUrlVisited::class))->shouldHaveBeenCalled();
} }
@@ -73,7 +73,7 @@ class VisitsTrackerTest extends TestCase
$count = $repo->shortCodeIsInUse($shortCode, null, $spec)->willReturn(true); $count = $repo->shortCodeIsInUse($shortCode, null, $spec)->willReturn(true);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce(); $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce();
$list = map(range(0, 1), fn () => new Visit(new ShortUrl(''), Visitor::emptyInstance())); $list = map(range(0, 1), fn () => new Visit(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo2 = $this->prophesize(VisitRepository::class); $repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByShortCode($shortCode, null, Argument::type(DateRange::class), 1, 0, $spec)->willReturn( $repo2->findVisitsByShortCode($shortCode, null, Argument::type(DateRange::class), 1, 0, $spec)->willReturn(
$list, $list,
@@ -129,7 +129,7 @@ class VisitsTrackerTest extends TestCase
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal()); $getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
$spec = $apiKey === null ? null : $apiKey->spec(); $spec = $apiKey === null ? null : $apiKey->spec();
$list = map(range(0, 1), fn () => new Visit(new ShortUrl(''), Visitor::emptyInstance())); $list = map(range(0, 1), fn () => new Visit(ShortUrl::createEmpty(), Visitor::emptyInstance()));
$repo2 = $this->prophesize(VisitRepository::class); $repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByTag($tag, Argument::type(DateRange::class), 1, 0, $spec)->willReturn($list); $repo2->findVisitsByTag($tag, Argument::type(DateRange::class), 1, 0, $spec)->willReturn($list);
$repo2->countVisitsByTag($tag, Argument::type(DateRange::class), $spec)->willReturn(1); $repo2->countVisitsByTag($tag, Argument::type(DateRange::class), $spec)->willReturn(1);

View File

@@ -37,18 +37,23 @@ class ShortUrlDataTransformerTest extends TestCase
$maxVisits = random_int(1, 1000); $maxVisits = random_int(1, 1000);
$now = Chronos::now(); $now = Chronos::now();
yield 'no metadata' => [new ShortUrl('', ShortUrlMeta::createEmpty()), [ yield 'no metadata' => [ShortUrl::createEmpty(), [
'validSince' => null, 'validSince' => null,
'validUntil' => null, 'validUntil' => null,
'maxVisits' => null, 'maxVisits' => null,
]]; ]];
yield 'max visits only' => [new ShortUrl('', ShortUrlMeta::fromRawData(['maxVisits' => $maxVisits])), [ yield 'max visits only' => [ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
'maxVisits' => $maxVisits,
'longUrl' => '',
])), [
'validSince' => null, 'validSince' => null,
'validUntil' => null, 'validUntil' => null,
'maxVisits' => $maxVisits, 'maxVisits' => $maxVisits,
]]; ]];
yield 'max visits and valid since' => [ yield 'max visits and valid since' => [
new ShortUrl('', ShortUrlMeta::fromRawData(['validSince' => $now, 'maxVisits' => $maxVisits])), ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['validSince' => $now, 'maxVisits' => $maxVisits, 'longUrl' => ''],
)),
[ [
'validSince' => $now->toAtomString(), 'validSince' => $now->toAtomString(),
'validUntil' => null, 'validUntil' => null,
@@ -56,8 +61,8 @@ class ShortUrlDataTransformerTest extends TestCase
], ],
]; ];
yield 'both dates' => [ yield 'both dates' => [
new ShortUrl('', ShortUrlMeta::fromRawData( ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['validSince' => $now, 'validUntil' => $now->subDays(10)], ['validSince' => $now, 'validUntil' => $now->subDays(10), 'longUrl' => ''],
)), )),
[ [
'validSince' => $now->toAtomString(), 'validSince' => $now->toAtomString(),
@@ -66,8 +71,8 @@ class ShortUrlDataTransformerTest extends TestCase
], ],
]; ];
yield 'everything' => [ yield 'everything' => [
new ShortUrl('', ShortUrlMeta::fromRawData( ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['validSince' => $now, 'validUntil' => $now->subDays(5), 'maxVisits' => $maxVisits], ['validSince' => $now, 'validUntil' => $now->subDays(5), 'maxVisits' => $maxVisits, 'longUrl' => ''],
)), )),
[ [
'validSince' => $now->toAtomString(), 'validSince' => $now->toAtomString(),

View File

@@ -57,7 +57,7 @@ class VisitLocatorTest extends TestCase
): void { ): void {
$unlocatedVisits = map( $unlocatedVisits = map(
range(1, 200), range(1, 200),
fn (int $i) => new Visit(new ShortUrl(sprintf('short_code_%s', $i)), Visitor::emptyInstance()), fn (int $i) => new Visit(ShortUrl::withLongUrl(sprintf('short_code_%s', $i)), Visitor::emptyInstance()),
); );
$findVisits = $this->mockRepoMethod($expectedRepoMethodName)->willReturn($unlocatedVisits); $findVisits = $this->mockRepoMethod($expectedRepoMethodName)->willReturn($unlocatedVisits);
@@ -107,7 +107,7 @@ class VisitLocatorTest extends TestCase
bool $isNonLocatableAddress bool $isNonLocatableAddress
): void { ): void {
$unlocatedVisits = [ $unlocatedVisits = [
new Visit(new ShortUrl('foo'), Visitor::emptyInstance()), new Visit(ShortUrl::withLongUrl('foo'), Visitor::emptyInstance()),
]; ];
$findVisits = $this->mockRepoMethod($expectedRepoMethodName)->willReturn($unlocatedVisits); $findVisits = $this->mockRepoMethod($expectedRepoMethodName)->willReturn($unlocatedVisits);

View File

@@ -27,11 +27,10 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
public function handle(Request $request): Response public function handle(Request $request): Response
{ {
$shortUrlData = $this->buildShortUrlData($request); $shortUrlData = $this->buildShortUrlData($request);
$longUrl = $shortUrlData->getLongUrl();
$tags = $shortUrlData->getTags(); $tags = $shortUrlData->getTags();
$shortUrlMeta = $shortUrlData->getMeta(); $shortUrlMeta = $shortUrlData->getMeta();
$shortUrl = $this->urlShortener->shorten($longUrl, $tags, $shortUrlMeta); $shortUrl = $this->urlShortener->shorten($tags, $shortUrlMeta);
$transformer = new ShortUrlDataTransformer($this->domainConfig); $transformer = new ShortUrlDataTransformer($this->domainConfig);
return new JsonResponse($transformer->transform($shortUrl)); return new JsonResponse($transformer->transform($shortUrl));

View File

@@ -22,15 +22,9 @@ class CreateShortUrlAction extends AbstractCreateShortUrlAction
protected function buildShortUrlData(Request $request): CreateShortUrlData protected function buildShortUrlData(Request $request): CreateShortUrlData
{ {
$payload = (array) $request->getParsedBody(); $payload = (array) $request->getParsedBody();
if (! isset($payload['longUrl'])) {
throw ValidationException::fromArray([
'longUrl' => 'A URL was not provided',
]);
}
$payload[ShortUrlMetaInputFilter::API_KEY] = AuthenticationMiddleware::apiKeyFromRequest($request); $payload[ShortUrlMetaInputFilter::API_KEY] = AuthenticationMiddleware::apiKeyFromRequest($request);
$meta = ShortUrlMeta::fromRawData($payload); $meta = ShortUrlMeta::fromRawData($payload);
return new CreateShortUrlData($payload['longUrl'], (array) ($payload['tags'] ?? []), $meta); return new CreateShortUrlData((array) ($payload['tags'] ?? []), $meta);
} }
} }

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\ShortUrl; namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\CreateShortUrlData; use Shlinkio\Shlink\Core\Model\CreateShortUrlData;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter; use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter;
@@ -20,15 +19,10 @@ class SingleStepCreateShortUrlAction extends AbstractCreateShortUrlAction
{ {
$query = $request->getQueryParams(); $query = $request->getQueryParams();
$longUrl = $query['longUrl'] ?? null; $longUrl = $query['longUrl'] ?? null;
if ($longUrl === null) {
throw ValidationException::fromArray([
'longUrl' => 'A URL was not provided',
]);
}
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request); $apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
return new CreateShortUrlData($longUrl, [], ShortUrlMeta::fromRawData([
return new CreateShortUrlData([], ShortUrlMeta::fromRawData([
ShortUrlMetaInputFilter::LONG_URL => $longUrl,
ShortUrlMetaInputFilter::API_KEY => $apiKey, ShortUrlMetaInputFilter::API_KEY => $apiKey,
// This will usually be null, unless this API key enforces one specific domain // This will usually be null, unless this API key enforces one specific domain
ShortUrlMetaInputFilter::DOMAIN => $request->getAttribute(ShortUrlMetaInputFilter::DOMAIN), ShortUrlMetaInputFilter::DOMAIN => $request->getAttribute(ShortUrlMetaInputFilter::DOMAIN),

View File

@@ -212,10 +212,12 @@ class CreateShortUrlTest extends ApiTestCase
yield ['http://téstb.shlink.io']; // Redirects to http://tést.shlink.io yield ['http://téstb.shlink.io']; // Redirects to http://tést.shlink.io
} }
/** @test */ /**
public function failsToCreateShortUrlWithInvalidLongUrl(): void * @test
* @dataProvider provideInvalidUrls
*/
public function failsToCreateShortUrlWithInvalidLongUrl(string $url): void
{ {
$url = 'https://this-has-to-be-invalid.com';
$expectedDetail = sprintf('Provided URL %s is invalid. Try with a different one.', $url); $expectedDetail = sprintf('Provided URL %s is invalid. Try with a different one.', $url);
[$statusCode, $payload] = $this->createShortUrl(['longUrl' => $url]); [$statusCode, $payload] = $this->createShortUrl(['longUrl' => $url]);
@@ -228,6 +230,25 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals($url, $payload['url']); self::assertEquals($url, $payload['url']);
} }
public function provideInvalidUrls(): iterable
{
yield 'empty URL' => [''];
yield 'non-reachable URL' => ['https://this-has-to-be-invalid.com'];
}
/** @test */
public function failsToCreateShortUrlWithoutLongUrl(): void
{
$resp = $this->callApiWithKey(self::METHOD_POST, '/short-urls', [RequestOptions::JSON => []]);
$payload = $this->getJsonResponsePayload($resp);
self::assertEquals(self::STATUS_BAD_REQUEST, $resp->getStatusCode());
self::assertEquals(self::STATUS_BAD_REQUEST, $payload['status']);
self::assertEquals('INVALID_ARGUMENT', $payload['type']);
self::assertEquals('Provided data is not valid', $payload['detail']);
self::assertEquals('Invalid data', $payload['title']);
}
/** @test */ /** @test */
public function defaultDomainIsDroppedIfProvided(): void public function defaultDomainIsDroppedIfProvided(): void
{ {

View File

@@ -27,44 +27,46 @@ class ShortUrlsFixture extends AbstractFixture implements DependentFixtureInterf
$authorApiKey = $this->getReference('author_api_key'); $authorApiKey = $this->getReference('author_api_key');
$abcShortUrl = $this->setShortUrlDate( $abcShortUrl = $this->setShortUrlDate(
new ShortUrl('https://shlink.io', ShortUrlMeta::fromRawData( ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['customSlug' => 'abc123', 'apiKey' => $authorApiKey], ['customSlug' => 'abc123', 'apiKey' => $authorApiKey, 'longUrl' => 'https://shlink.io'],
)), )),
'2018-05-01', '2018-05-01',
); );
$manager->persist($abcShortUrl); $manager->persist($abcShortUrl);
$defShortUrl = $this->setShortUrlDate(new ShortUrl( $defShortUrl = $this->setShortUrlDate(ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
'https://blog.alejandrocelaya.com/2017/12/09/acmailer-7-0-the-most-important-release-in-a-long-time/', 'validSince' => Chronos::parse('2020-05-01'),
ShortUrlMeta::fromRawData( 'customSlug' => 'def456',
['validSince' => Chronos::parse('2020-05-01'), 'customSlug' => 'def456', 'apiKey' => $authorApiKey], 'apiKey' => $authorApiKey,
), 'longUrl' =>
), '2019-01-01 00:00:10'); 'https://blog.alejandrocelaya.com/2017/12/09/acmailer-7-0-the-most-important-release-in-a-long-time/',
])), '2019-01-01 00:00:10');
$manager->persist($defShortUrl); $manager->persist($defShortUrl);
$customShortUrl = $this->setShortUrlDate(new ShortUrl( $customShortUrl = $this->setShortUrlDate(ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
'https://shlink.io', ['customSlug' => 'custom', 'maxVisits' => 2, 'apiKey' => $authorApiKey, 'longUrl' => 'https://shlink.io'],
ShortUrlMeta::fromRawData(['customSlug' => 'custom', 'maxVisits' => 2, 'apiKey' => $authorApiKey]), )), '2019-01-01 00:00:20');
), '2019-01-01 00:00:20');
$manager->persist($customShortUrl); $manager->persist($customShortUrl);
$ghiShortUrl = $this->setShortUrlDate( $ghiShortUrl = $this->setShortUrlDate(
new ShortUrl('https://shlink.io/documentation/', ShortUrlMeta::fromRawData(['customSlug' => 'ghi789'])), ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
['customSlug' => 'ghi789', 'longUrl' => 'https://shlink.io/documentation/'],
)),
'2018-05-01', '2018-05-01',
); );
$manager->persist($ghiShortUrl); $manager->persist($ghiShortUrl);
$withDomainDuplicatingShortCode = $this->setShortUrlDate(new ShortUrl( $withDomainDuplicatingShortCode = $this->setShortUrlDate(ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
'https://blog.alejandrocelaya.com/2019/04/27/considerations-to-properly-use-open-source-software-projects/', 'domain' => 'example.com',
ShortUrlMeta::fromRawData(['domain' => 'example.com', 'customSlug' => 'ghi789']), 'customSlug' => 'ghi789',
new PersistenceShortUrlRelationResolver($manager), 'longUrl' => 'https://blog.alejandrocelaya.com/2019/04/27/considerations-to-properly-use-open-'
), '2019-01-01 00:00:30'); . 'source-software-projects/',
]), new PersistenceShortUrlRelationResolver($manager)), '2019-01-01 00:00:30');
$manager->persist($withDomainDuplicatingShortCode); $manager->persist($withDomainDuplicatingShortCode);
$withDomainAndSlugShortUrl = $this->setShortUrlDate(new ShortUrl( $withDomainAndSlugShortUrl = $this->setShortUrlDate(ShortUrl::fromMeta(ShortUrlMeta::fromRawData(
'https://google.com', ['domain' => 'some-domain.com', 'customSlug' => 'custom-with-domain', 'longUrl' => 'https://google.com'],
ShortUrlMeta::fromRawData(['domain' => 'some-domain.com', 'customSlug' => 'custom-with-domain']), )), '2018-10-20');
), '2018-10-20');
$manager->persist($withDomainAndSlugShortUrl); $manager->persist($withDomainAndSlugShortUrl);
$manager->flush(); $manager->flush();

View File

@@ -39,24 +39,22 @@ class CreateShortUrlActionTest extends TestCase
} }
/** @test */ /** @test */
public function missingLongUrlParamReturnsError(): void public function properShortcodeConversionReturnsData(): void
{
$this->expectException(ValidationException::class);
$this->action->handle(new ServerRequest());
}
/**
* @test
* @dataProvider provideRequestBodies
*/
public function properShortcodeConversionReturnsData(array $body, array $expectedMeta): void
{ {
$apiKey = new ApiKey(); $apiKey = new ApiKey();
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$expectedMeta = $body = [
'longUrl' => 'http://www.domain.com/foo/bar',
'validSince' => Chronos::now()->toAtomString(),
'validUntil' => Chronos::now()->toAtomString(),
'customSlug' => 'foo-bar-baz',
'maxVisits' => 50,
'findIfExists' => true,
'domain' => 'my-domain.com',
];
$expectedMeta['apiKey'] = $apiKey; $expectedMeta['apiKey'] = $apiKey;
$shorten = $this->urlShortener->shorten( $shorten = $this->urlShortener->shorten(
Argument::type('string'),
Argument::type('array'), Argument::type('array'),
ShortUrlMeta::fromRawData($expectedMeta), ShortUrlMeta::fromRawData($expectedMeta),
)->willReturn($shortUrl); )->willReturn($shortUrl);
@@ -70,29 +68,13 @@ class CreateShortUrlActionTest extends TestCase
$shorten->shouldHaveBeenCalledOnce(); $shorten->shouldHaveBeenCalledOnce();
} }
public function provideRequestBodies(): iterable
{
$fullMeta = [
'longUrl' => 'http://www.domain.com/foo/bar',
'validSince' => Chronos::now()->toAtomString(),
'validUntil' => Chronos::now()->toAtomString(),
'customSlug' => 'foo-bar-baz',
'maxVisits' => 50,
'findIfExists' => true,
'domain' => 'my-domain.com',
];
yield 'no data' => [['longUrl' => 'http://www.domain.com/foo/bar'], []];
yield 'all data' => [$fullMeta, $fullMeta];
}
/** /**
* @test * @test
* @dataProvider provideInvalidDomains * @dataProvider provideInvalidDomains
*/ */
public function anInvalidDomainReturnsError(string $domain): void public function anInvalidDomainReturnsError(string $domain): void
{ {
$shortUrl = new ShortUrl(''); $shortUrl = ShortUrl::createEmpty();
$urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl); $urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl);
$request = (new ServerRequest())->withParsedBody([ $request = (new ServerRequest())->withParsedBody([

View File

@@ -49,7 +49,7 @@ class EditShortUrlActionTest extends TestCase
'maxVisits' => 5, 'maxVisits' => 5,
]); ]);
$updateMeta = $this->shortUrlService->updateMetadataByShortCode(Argument::cetera())->willReturn( $updateMeta = $this->shortUrlService->updateMetadataByShortCode(Argument::cetera())->willReturn(
new ShortUrl(''), ShortUrl::createEmpty(),
); );
$resp = $this->action->handle($request); $resp = $this->action->handle($request);

View File

@@ -45,7 +45,7 @@ class EditShortUrlTagsActionTest extends TestCase
new ShortUrlIdentifier($shortCode), new ShortUrlIdentifier($shortCode),
[], [],
Argument::type(ApiKey::class), Argument::type(ApiKey::class),
)->willReturn(new ShortUrl('')) )->willReturn(ShortUrl::createEmpty())
->shouldBeCalledOnce(); ->shouldBeCalledOnce();
$response = $this->action->handle( $response = $this->action->handle(

View File

@@ -35,7 +35,7 @@ class ResolveShortUrlActionTest extends TestCase
$shortCode = 'abc123'; $shortCode = 'abc123';
$apiKey = new ApiKey(); $apiKey = new ApiKey();
$this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode), $apiKey)->willReturn( $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode), $apiKey)->willReturn(
new ShortUrl('http://domain.com/foo/bar'), ShortUrl::withLongUrl('http://domain.com/foo/bar'),
)->shouldBeCalledOnce(); )->shouldBeCalledOnce();
$request = (new ServerRequest())->withAttribute('shortCode', $shortCode)->withAttribute(ApiKey::class, $apiKey); $request = (new ServerRequest())->withAttribute('shortCode', $shortCode)->withAttribute(ApiKey::class, $apiKey);

View File

@@ -5,13 +5,10 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl; namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl;
use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
use Shlinkio\Shlink\Rest\Action\ShortUrl\SingleStepCreateShortUrlAction; use Shlinkio\Shlink\Rest\Action\ShortUrl\SingleStepCreateShortUrlAction;
@@ -38,16 +35,6 @@ class SingleStepCreateShortUrlActionTest extends TestCase
); );
} }
/** @test */
public function errorResponseIsReturnedIfNoUrlIsProvided(): void
{
$request = new ServerRequest();
$this->expectException(ValidationException::class);
$this->action->handle($request);
}
/** @test */ /** @test */
public function properDataIsPassedWhenGeneratingShortCode(): void public function properDataIsPassedWhenGeneratingShortCode(): void
{ {
@@ -57,13 +44,9 @@ class SingleStepCreateShortUrlActionTest extends TestCase
'longUrl' => 'http://foobar.com', 'longUrl' => 'http://foobar.com',
])->withAttribute(ApiKey::class, $apiKey); ])->withAttribute(ApiKey::class, $apiKey);
$generateShortCode = $this->urlShortener->shorten( $generateShortCode = $this->urlShortener->shorten(
Argument::that(function (string $argument): bool {
Assert::assertEquals('http://foobar.com', $argument);
return true;
}),
[], [],
ShortUrlMeta::fromRawData(['apiKey' => $apiKey]), ShortUrlMeta::fromRawData(['apiKey' => $apiKey, 'longUrl' => 'http://foobar.com']),
)->willReturn(new ShortUrl('')); )->willReturn(ShortUrl::createEmpty());
$resp = $this->action->handle($request); $resp = $this->action->handle($request);