mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Refactored short URL creation so that the long URL is part of the ShortUrlMeta
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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, '')),
|
||||||
),
|
),
|
||||||
])),
|
])),
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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[]
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
])),
|
])),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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 {
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
@@ -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')])),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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([
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user