mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-22 23:23:42 -06:00
Merge pull request #1730 from acelaya-forks/feature/validate-uris
Feature/validate uris
This commit is contained in:
commit
b12cfaedf3
17
CHANGELOG.md
17
CHANGELOG.md
@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1715](https://github.com/shlinkio/shlink/issues/1715) Fix short URL creation/edition allowing long URLs without schema. Now a validation error is thrown.
|
||||
|
||||
|
||||
## [3.5.2] - 2023-02-16
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
@ -13,6 +13,7 @@ const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302; // Deprecated.
|
||||
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag
|
||||
const LOOSE_URI_MATCHER = '/(.+)\:\/\/(.+)/i'; // Matches anything starting with a schema.
|
||||
const DEFAULT_QR_CODE_SIZE = 300;
|
||||
const DEFAULT_QR_CODE_MARGIN = 0;
|
||||
const DEFAULT_QR_CODE_FORMAT = 'png';
|
||||
|
@ -49,7 +49,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
// The paginator will return more than one page
|
||||
$data = [];
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
$data[] = ShortUrl::withLongUrl('url_' . $i);
|
||||
$data[] = ShortUrl::withLongUrl('https://url_' . $i);
|
||||
}
|
||||
|
||||
$this->shortUrlService->expects($this->exactly(3))->method('listShortUrls')->withAnyParameters()
|
||||
@ -71,7 +71,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
// The paginator will return more than one page
|
||||
$data = [];
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
$data[] = ShortUrl::withLongUrl('url_' . $i);
|
||||
$data[] = ShortUrl::withLongUrl('https://url_' . $i);
|
||||
}
|
||||
|
||||
$this->shortUrlService->expects($this->once())->method('listShortUrls')->with(
|
||||
@ -114,7 +114,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
ShortUrlsParams::emptyInstance(),
|
||||
)->willReturn(new Paginator(new ArrayAdapter([
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo.com',
|
||||
'longUrl' => 'https://foo.com',
|
||||
'tags' => ['foo', 'bar', 'baz'],
|
||||
'apiKey' => $apiKey,
|
||||
])),
|
||||
|
@ -68,7 +68,7 @@ class ShortUrl extends AbstractEntity
|
||||
*/
|
||||
public static function createFake(): self
|
||||
{
|
||||
return self::withLongUrl('foo');
|
||||
return self::withLongUrl('https://foo');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,8 +12,11 @@ use Shlinkio\Shlink\Common\Validation;
|
||||
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
use function is_string;
|
||||
use function preg_match;
|
||||
use function substr;
|
||||
|
||||
use const Shlinkio\Shlink\LOOSE_URI_MATCHER;
|
||||
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
|
||||
|
||||
/**
|
||||
@ -59,27 +62,13 @@ class ShortUrlInputFilter extends InputFilter
|
||||
|
||||
private function initialize(bool $requireLongUrl, UrlShortenerOptions $options): void
|
||||
{
|
||||
$longUrlNotEmptyCommonOptions = [
|
||||
Validator\NotEmpty::OBJECT,
|
||||
Validator\NotEmpty::SPACE,
|
||||
Validator\NotEmpty::EMPTY_ARRAY,
|
||||
Validator\NotEmpty::BOOLEAN,
|
||||
Validator\NotEmpty::STRING,
|
||||
];
|
||||
|
||||
$longUrlInput = $this->createInput(self::LONG_URL, $requireLongUrl);
|
||||
$longUrlInput->getValidatorChain()->attach(new Validator\NotEmpty([
|
||||
...$longUrlNotEmptyCommonOptions,
|
||||
Validator\NotEmpty::NULL,
|
||||
]));
|
||||
$longUrlInput->getValidatorChain()->merge($this->longUrlValidators());
|
||||
$this->add($longUrlInput);
|
||||
|
||||
$deviceLongUrlsInput = $this->createInput(self::DEVICE_LONG_URLS, false);
|
||||
$deviceLongUrlsInput->getValidatorChain()->attach(
|
||||
new DeviceLongUrlsValidator(new Validator\NotEmpty([
|
||||
...$longUrlNotEmptyCommonOptions,
|
||||
...($requireLongUrl ? [Validator\NotEmpty::NULL] : []),
|
||||
])),
|
||||
new DeviceLongUrlsValidator($this->longUrlValidators(allowNull: ! $requireLongUrl)),
|
||||
);
|
||||
$this->add($deviceLongUrlsInput);
|
||||
|
||||
@ -129,4 +118,25 @@ class ShortUrlInputFilter extends InputFilter
|
||||
|
||||
$this->add($this->createBooleanInput(self::CRAWLABLE, false));
|
||||
}
|
||||
|
||||
private function longUrlValidators(bool $allowNull = false): Validator\ValidatorChain
|
||||
{
|
||||
$emptyModifiers = [
|
||||
Validator\NotEmpty::OBJECT,
|
||||
Validator\NotEmpty::SPACE,
|
||||
Validator\NotEmpty::EMPTY_ARRAY,
|
||||
Validator\NotEmpty::BOOLEAN,
|
||||
Validator\NotEmpty::STRING,
|
||||
];
|
||||
if (! $allowNull) {
|
||||
$emptyModifiers[] = Validator\NotEmpty::NULL;
|
||||
}
|
||||
|
||||
return (new Validator\ValidatorChain())
|
||||
->attach(new Validator\NotEmpty($emptyModifiers))
|
||||
->attach(new Validator\Callback(
|
||||
// Non-strings is always allowed. Other validators will take care of those
|
||||
static fn (mixed $value) => ! is_string($value) || preg_match(LOOSE_URI_MATCHER, $value) === 1,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ class DomainRepositoryTest extends DatabaseTestCase
|
||||
{
|
||||
return ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(
|
||||
['domain' => $domain->authority, 'apiKey' => $apiKey, 'longUrl' => 'foo'],
|
||||
['domain' => $domain->authority, 'apiKey' => $apiKey, 'longUrl' => 'https://foo'],
|
||||
),
|
||||
new class ($domain) implements ShortUrlRelationResolverInterface {
|
||||
public function __construct(private Domain $domain)
|
||||
|
@ -24,7 +24,7 @@ class CrawlableShortCodesQueryTest extends DatabaseTestCase
|
||||
public function invokingQueryReturnsExpectedResult(): void
|
||||
{
|
||||
$createShortUrl = fn (bool $crawlable) => ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['crawlable' => $crawlable, 'longUrl' => 'foo.com']),
|
||||
ShortUrlCreation::fromRawData(['crawlable' => $crawlable, 'longUrl' => 'https://foo.com']),
|
||||
);
|
||||
|
||||
$shortUrl1 = $createShortUrl(true);
|
||||
|
@ -43,7 +43,7 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
{
|
||||
$count = 5;
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$this->getEntityManager()->persist(ShortUrl::withLongUrl((string) $i));
|
||||
$this->getEntityManager()->persist(ShortUrl::withLongUrl('https://' . $i));
|
||||
}
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
@ -54,12 +54,12 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
public function findListProperlyFiltersResult(): void
|
||||
{
|
||||
$foo = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['longUrl' => 'foo', 'tags' => ['bar']]),
|
||||
ShortUrlCreation::fromRawData(['longUrl' => 'https://foo', 'tags' => ['bar']]),
|
||||
$this->relationResolver,
|
||||
);
|
||||
$this->getEntityManager()->persist($foo);
|
||||
|
||||
$bar = ShortUrl::withLongUrl('bar');
|
||||
$bar = ShortUrl::withLongUrl('https://bar');
|
||||
$visits = map(range(0, 5), function () use ($bar) {
|
||||
$visit = Visit::forValidShortUrl($bar, Visitor::botInstance());
|
||||
$this->getEntityManager()->persist($visit);
|
||||
@ -69,7 +69,7 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
$bar->setVisits(new ArrayCollection($visits));
|
||||
$this->getEntityManager()->persist($bar);
|
||||
|
||||
$foo2 = ShortUrl::withLongUrl('foo_2');
|
||||
$foo2 = ShortUrl::withLongUrl('https://foo_2');
|
||||
$visits2 = map(range(0, 3), function () use ($foo2) {
|
||||
$visit = Visit::forValidShortUrl($foo2, Visitor::emptyInstance());
|
||||
$this->getEntityManager()->persist($visit);
|
||||
@ -147,7 +147,7 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function findListProperlyMapsFieldNamesToColumnNamesWhenOrdering(): void
|
||||
{
|
||||
$urls = ['a', 'z', 'c', 'b'];
|
||||
$urls = ['https://a', 'https://z', 'https://c', 'https://b'];
|
||||
foreach ($urls as $url) {
|
||||
$this->getEntityManager()->persist(ShortUrl::withLongUrl($url));
|
||||
}
|
||||
@ -159,37 +159,37 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
);
|
||||
|
||||
self::assertCount(count($urls), $result);
|
||||
self::assertEquals('a', $result[0]->getLongUrl());
|
||||
self::assertEquals('b', $result[1]->getLongUrl());
|
||||
self::assertEquals('c', $result[2]->getLongUrl());
|
||||
self::assertEquals('z', $result[3]->getLongUrl());
|
||||
self::assertEquals('https://a', $result[0]->getLongUrl());
|
||||
self::assertEquals('https://b', $result[1]->getLongUrl());
|
||||
self::assertEquals('https://c', $result[2]->getLongUrl());
|
||||
self::assertEquals('https://z', $result[3]->getLongUrl());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findListReturnsOnlyThoseWithMatchingTags(): void
|
||||
{
|
||||
$shortUrl1 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo1',
|
||||
'longUrl' => 'https://foo1',
|
||||
'tags' => ['foo', 'bar'],
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl1);
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo2',
|
||||
'longUrl' => 'https://foo2',
|
||||
'tags' => ['foo', 'baz'],
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo3',
|
||||
'longUrl' => 'https://foo3',
|
||||
'tags' => ['foo'],
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
$shortUrl4 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo4',
|
||||
'longUrl' => 'https://foo4',
|
||||
'tags' => ['bar', 'baz'],
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl4);
|
||||
$shortUrl5 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo5',
|
||||
'longUrl' => 'https://foo5',
|
||||
'tags' => ['bar', 'baz'],
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl5);
|
||||
@ -278,17 +278,17 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
public function findListReturnsOnlyThoseWithMatchingDomains(): void
|
||||
{
|
||||
$shortUrl1 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo1',
|
||||
'longUrl' => 'https://foo1',
|
||||
'domain' => null,
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl1);
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo2',
|
||||
'longUrl' => 'https://foo2',
|
||||
'domain' => null,
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo3',
|
||||
'longUrl' => 'https://foo3',
|
||||
'domain' => 'another.com',
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
@ -314,22 +314,22 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
public function findListReturnsOnlyThoseWithoutExcludedUrls(): void
|
||||
{
|
||||
$shortUrl1 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo1',
|
||||
'longUrl' => 'https://foo1',
|
||||
'validUntil' => Chronos::now()->addDays(1)->toAtomString(),
|
||||
'maxVisits' => 100,
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl1);
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo2',
|
||||
'longUrl' => 'https://foo2',
|
||||
'validUntil' => Chronos::now()->subDays(1)->toAtomString(),
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo3',
|
||||
'longUrl' => 'https://foo3',
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
$shortUrl4 = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo4',
|
||||
'longUrl' => 'https://foo4',
|
||||
'maxVisits' => 3,
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl4);
|
||||
|
@ -34,16 +34,18 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function findOneWithDomainFallbackReturnsProperData(): void
|
||||
{
|
||||
$regularOne = ShortUrl::create(ShortUrlCreation::fromRawData(['customSlug' => 'Foo', 'longUrl' => 'foo']));
|
||||
$regularOne = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'Foo', 'longUrl' => 'https://foo']),
|
||||
);
|
||||
$this->getEntityManager()->persist($regularOne);
|
||||
|
||||
$withDomain = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['domain' => 'example.com', 'customSlug' => 'domain-short-code', 'longUrl' => 'foo'],
|
||||
['domain' => 'example.com', 'customSlug' => 'domain-short-code', 'longUrl' => 'https://foo'],
|
||||
));
|
||||
$this->getEntityManager()->persist($withDomain);
|
||||
|
||||
$withDomainDuplicatingRegular = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['domain' => 's.test', 'customSlug' => 'Foo', 'longUrl' => 'foo_with_domain'],
|
||||
['domain' => 's.test', 'customSlug' => 'Foo', 'longUrl' => 'https://foo_with_domain'],
|
||||
));
|
||||
$this->getEntityManager()->persist($withDomainDuplicatingRegular);
|
||||
|
||||
@ -102,13 +104,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
public function shortCodeIsInUseLooksForShortUrlInProperSetOfTables(): void
|
||||
{
|
||||
$shortUrlWithoutDomain = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'my-cool-slug', 'longUrl' => 'foo']),
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'my-cool-slug', 'longUrl' => 'https://foo']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrlWithoutDomain);
|
||||
|
||||
$shortUrlWithDomain = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['domain' => 's.test', 'customSlug' => 'another-slug', 'longUrl' => 'foo']),
|
||||
);
|
||||
$shortUrlWithDomain = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['domain' => 's.test', 'customSlug' => 'another-slug', 'longUrl' => 'https://foo'],
|
||||
));
|
||||
$this->getEntityManager()->persist($shortUrlWithDomain);
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
@ -131,13 +133,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
public function findOneLooksForShortUrlInProperSetOfTables(): void
|
||||
{
|
||||
$shortUrlWithoutDomain = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'my-cool-slug', 'longUrl' => 'foo']),
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'my-cool-slug', 'longUrl' => 'https://foo']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrlWithoutDomain);
|
||||
|
||||
$shortUrlWithDomain = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['domain' => 's.test', 'customSlug' => 'another-slug', 'longUrl' => 'foo']),
|
||||
);
|
||||
$shortUrlWithDomain = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['domain' => 's.test', 'customSlug' => 'another-slug', 'longUrl' => 'https://foo'],
|
||||
));
|
||||
$this->getEntityManager()->persist($shortUrlWithDomain);
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
@ -157,14 +159,14 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function findOneMatchingReturnsNullForNonExistingShortUrls(): void
|
||||
{
|
||||
self::assertNull($this->repo->findOneMatching(ShortUrlCreation::fromRawData(['longUrl' => 'foobar'])));
|
||||
self::assertNull($this->repo->findOneMatching(ShortUrlCreation::fromRawData(['longUrl' => 'https://foobar'])));
|
||||
self::assertNull($this->repo->findOneMatching(
|
||||
ShortUrlCreation::fromRawData(['longUrl' => 'foobar', 'tags' => ['foo', 'bar']]),
|
||||
ShortUrlCreation::fromRawData(['longUrl' => 'https://foobar', 'tags' => ['foo', 'bar']]),
|
||||
));
|
||||
self::assertNull($this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'validSince' => Chronos::parse('2020-03-05 20:18:30'),
|
||||
'customSlug' => 'this_slug_does_not_exist',
|
||||
'longUrl' => 'foobar',
|
||||
'longUrl' => 'https://foobar',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])));
|
||||
}
|
||||
@ -175,49 +177,54 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
$start = Chronos::parse('2020-03-05 20:18:30');
|
||||
$end = Chronos::parse('2021-03-05 20:18:30');
|
||||
|
||||
$shortUrl = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['validSince' => $start, 'longUrl' => 'foo', 'tags' => ['foo', 'bar']]),
|
||||
$this->relationResolver,
|
||||
);
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $start, 'longUrl' => 'https://foo', 'tags' => ['foo', 'bar']],
|
||||
), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['validUntil' => $end, 'longUrl' => 'bar']));
|
||||
$shortUrl2 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['validUntil' => $end, 'longUrl' => 'https://bar']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
|
||||
$shortUrl3 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['validSince' => $start, 'validUntil' => $end, 'longUrl' => 'baz']),
|
||||
ShortUrlCreation::fromRawData(['validSince' => $start, 'validUntil' => $end, 'longUrl' => 'https://baz']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
|
||||
$shortUrl4 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'custom', 'validUntil' => $end, 'longUrl' => 'foo']),
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'custom', 'validUntil' => $end, 'longUrl' => 'https://foo']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl4);
|
||||
|
||||
$shortUrl5 = ShortUrl::create(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'foo']));
|
||||
$shortUrl5 = ShortUrl::create(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'https://foo']));
|
||||
$this->getEntityManager()->persist($shortUrl5);
|
||||
|
||||
$shortUrl6 = ShortUrl::create(ShortUrlCreation::fromRawData(['domain' => 's.test', 'longUrl' => 'foo']));
|
||||
$shortUrl6 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['domain' => 's.test', 'longUrl' => 'https://foo']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl6);
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
self::assertSame(
|
||||
$shortUrl,
|
||||
$this->repo->findOneMatching(
|
||||
ShortUrlCreation::fromRawData(['validSince' => $start, 'longUrl' => 'foo', 'tags' => ['foo', 'bar']]),
|
||||
),
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $start, 'longUrl' => 'https://foo', 'tags' => ['foo', 'bar']],
|
||||
)),
|
||||
);
|
||||
self::assertSame(
|
||||
$shortUrl2,
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData(['validUntil' => $end, 'longUrl' => 'bar'])),
|
||||
$this->repo->findOneMatching(
|
||||
ShortUrlCreation::fromRawData(['validUntil' => $end, 'longUrl' => 'https://bar']),
|
||||
),
|
||||
);
|
||||
self::assertSame(
|
||||
$shortUrl3,
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'validSince' => $start,
|
||||
'validUntil' => $end,
|
||||
'longUrl' => 'baz',
|
||||
'longUrl' => 'https://baz',
|
||||
])),
|
||||
);
|
||||
self::assertSame(
|
||||
@ -225,16 +232,18 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => 'custom',
|
||||
'validUntil' => $end,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
])),
|
||||
);
|
||||
self::assertSame(
|
||||
$shortUrl5,
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'foo'])),
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'https://foo'])),
|
||||
);
|
||||
self::assertSame(
|
||||
$shortUrl6,
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData(['domain' => 's.test', 'longUrl' => 'foo'])),
|
||||
$this->repo->findOneMatching(
|
||||
ShortUrlCreation::fromRawData(['domain' => 's.test', 'longUrl' => 'https://foo']),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -244,7 +253,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
$start = Chronos::parse('2020-03-05 20:18:30');
|
||||
$tags = ['foo', 'bar'];
|
||||
$meta = ShortUrlCreation::fromRawData(
|
||||
['validSince' => $start, 'maxVisits' => 50, 'longUrl' => 'foo', 'tags' => $tags],
|
||||
['validSince' => $start, 'maxVisits' => 50, 'longUrl' => 'https://foo', 'tags' => $tags],
|
||||
);
|
||||
|
||||
$shortUrl1 = ShortUrl::create($meta, $this->relationResolver);
|
||||
@ -293,14 +302,14 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
'validSince' => $start,
|
||||
'apiKey' => $apiKey,
|
||||
'domain' => $rightDomain->authority,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
|
||||
$nonDomainShortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'apiKey' => $apiKey,
|
||||
'longUrl' => 'non-domain',
|
||||
'longUrl' => 'https://non-domain',
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($nonDomainShortUrl);
|
||||
|
||||
@ -308,26 +317,26 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
|
||||
self::assertSame(
|
||||
$shortUrl,
|
||||
$this->repo->findOneMatching(
|
||||
ShortUrlCreation::fromRawData(['validSince' => $start, 'longUrl' => 'foo', 'tags' => ['foo', 'bar']]),
|
||||
),
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $start, 'longUrl' => 'https://foo', 'tags' => ['foo', 'bar']],
|
||||
)),
|
||||
);
|
||||
self::assertSame($shortUrl, $this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'validSince' => $start,
|
||||
'apiKey' => $apiKey,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])));
|
||||
self::assertSame($shortUrl, $this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'validSince' => $start,
|
||||
'apiKey' => $adminApiKey,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])));
|
||||
self::assertNull($this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'validSince' => $start,
|
||||
'apiKey' => $otherApiKey,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])));
|
||||
|
||||
@ -336,7 +345,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'validSince' => $start,
|
||||
'domain' => $rightDomain->authority,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])),
|
||||
);
|
||||
@ -346,7 +355,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
'validSince' => $start,
|
||||
'domain' => $rightDomain->authority,
|
||||
'apiKey' => $rightDomainApiKey,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])),
|
||||
);
|
||||
@ -356,7 +365,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
'validSince' => $start,
|
||||
'domain' => $rightDomain->authority,
|
||||
'apiKey' => $apiKey,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])),
|
||||
);
|
||||
@ -365,7 +374,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
'validSince' => $start,
|
||||
'domain' => $rightDomain->authority,
|
||||
'apiKey' => $wrongDomainApiKey,
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'tags' => ['foo', 'bar'],
|
||||
])),
|
||||
);
|
||||
@ -374,20 +383,20 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
$nonDomainShortUrl,
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'apiKey' => $apiKey,
|
||||
'longUrl' => 'non-domain',
|
||||
'longUrl' => 'https://non-domain',
|
||||
])),
|
||||
);
|
||||
self::assertSame(
|
||||
$nonDomainShortUrl,
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'apiKey' => $adminApiKey,
|
||||
'longUrl' => 'non-domain',
|
||||
'longUrl' => 'https://non-domain',
|
||||
])),
|
||||
);
|
||||
self::assertNull(
|
||||
$this->repo->findOneMatching(ShortUrlCreation::fromRawData([
|
||||
'apiKey' => $otherApiKey,
|
||||
'longUrl' => 'non-domain',
|
||||
'longUrl' => 'https://non-domain',
|
||||
])),
|
||||
);
|
||||
}
|
||||
@ -396,7 +405,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
public function importedShortUrlsAreFoundWhenExpected(): void
|
||||
{
|
||||
$buildImported = static fn (string $shortCode, ?string $domain = null) =>
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'foo', [], Chronos::now(), $domain, $shortCode, null);
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://foo', [], Chronos::now(), $domain, $shortCode, null);
|
||||
|
||||
$shortUrlWithoutDomain = ShortUrl::fromImport($buildImported('my-cool-slug'), true);
|
||||
$this->getEntityManager()->persist($shortUrlWithoutDomain);
|
||||
|
@ -74,7 +74,7 @@ class TagRepositoryTest extends DatabaseTestCase
|
||||
[$firstUrlTags] = array_chunk($names, 3);
|
||||
$secondUrlTags = [$names[0]];
|
||||
$metaWithTags = static fn (array $tags, ?ApiKey $apiKey) => ShortUrlCreation::fromRawData(
|
||||
['longUrl' => 'longUrl', 'tags' => $tags, 'apiKey' => $apiKey],
|
||||
['longUrl' => 'https://longUrl', 'tags' => $tags, 'apiKey' => $apiKey],
|
||||
);
|
||||
|
||||
$shortUrl = ShortUrl::create($metaWithTags($firstUrlTags, $apiKey), $this->relationResolver);
|
||||
@ -240,15 +240,14 @@ class TagRepositoryTest extends DatabaseTestCase
|
||||
|
||||
[$firstUrlTags, $secondUrlTags] = array_chunk($names, 3);
|
||||
|
||||
$shortUrl = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['apiKey' => $authorApiKey, 'longUrl' => 'longUrl', 'tags' => $firstUrlTags]),
|
||||
$this->relationResolver,
|
||||
);
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['apiKey' => $authorApiKey, 'longUrl' => 'https://longUrl', 'tags' => $firstUrlTags],
|
||||
), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
|
||||
$shortUrl2 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(
|
||||
['domain' => $domain->authority, 'longUrl' => 'longUrl', 'tags' => $secondUrlTags],
|
||||
['domain' => $domain->authority, 'longUrl' => 'https://longUrl', 'tags' => $secondUrlTags],
|
||||
),
|
||||
$this->relationResolver,
|
||||
);
|
||||
|
@ -266,7 +266,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
$this->getEntityManager()->persist($apiKey1);
|
||||
$shortUrl = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(
|
||||
['apiKey' => $apiKey1, 'domain' => $domain->authority, 'longUrl' => 'longUrl'],
|
||||
['apiKey' => $apiKey1, 'domain' => $domain->authority, 'longUrl' => 'https://longUrl'],
|
||||
),
|
||||
$this->relationResolver,
|
||||
);
|
||||
@ -275,13 +275,15 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
|
||||
$apiKey2 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
|
||||
$this->getEntityManager()->persist($apiKey2);
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['apiKey' => $apiKey2, 'longUrl' => 'longUrl']));
|
||||
$shortUrl2 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['apiKey' => $apiKey2, 'longUrl' => 'https://longUrl']),
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$this->createVisitsForShortUrl($shortUrl2, 5);
|
||||
|
||||
$shortUrl3 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(
|
||||
['apiKey' => $apiKey2, 'domain' => $domain->authority, 'longUrl' => 'longUrl'],
|
||||
['apiKey' => $apiKey2, 'domain' => $domain->authority, 'longUrl' => 'https://longUrl'],
|
||||
),
|
||||
$this->relationResolver,
|
||||
);
|
||||
@ -320,7 +322,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function findOrphanVisitsReturnsExpectedResult(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'longUrl']));
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://longUrl']));
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, 7);
|
||||
|
||||
@ -369,7 +371,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function countOrphanVisitsReturnsExpectedResult(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'longUrl']));
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://longUrl']));
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, 7);
|
||||
|
||||
@ -406,15 +408,15 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function findNonOrphanVisitsReturnsExpectedResult(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => '1']));
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://1']));
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, 7);
|
||||
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => '2']));
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://2']));
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$this->createVisitsForShortUrl($shortUrl2, 4);
|
||||
|
||||
$shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => '3']));
|
||||
$shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://3']));
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
$this->createVisitsForShortUrl($shortUrl3, 10);
|
||||
|
||||
@ -473,7 +475,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
?ApiKey $apiKey = null,
|
||||
): array {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
ShortUrlInputFilter::LONG_URL => 'longUrl',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://longUrl',
|
||||
ShortUrlInputFilter::TAGS => $tags,
|
||||
ShortUrlInputFilter::API_KEY => $apiKey,
|
||||
]), $this->relationResolver);
|
||||
@ -487,7 +489,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
$shortUrlWithDomain = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => $shortCode,
|
||||
'domain' => $domain,
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
]));
|
||||
$this->getEntityManager()->persist($shortUrlWithDomain);
|
||||
$this->createVisitsForShortUrl($shortUrlWithDomain, 3);
|
||||
|
@ -58,7 +58,7 @@ class NotifyNewShortUrlToMercureTest extends TestCase
|
||||
#[Test]
|
||||
public function expectedNotificationIsPublished(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('longUrl');
|
||||
$shortUrl = ShortUrl::withLongUrl('https://longUrl');
|
||||
$update = Update::forTopicAndPayload('', []);
|
||||
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, '123')->willReturn($shortUrl);
|
||||
@ -75,7 +75,7 @@ class NotifyNewShortUrlToMercureTest extends TestCase
|
||||
#[Test]
|
||||
public function messageIsPrintedIfPublishingFails(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('longUrl');
|
||||
$shortUrl = ShortUrl::withLongUrl('https://longUrl');
|
||||
$update = Update::forTopicAndPayload('', []);
|
||||
$e = new Exception('Error');
|
||||
|
||||
|
@ -37,7 +37,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => 'foo',
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
'title' => $title,
|
||||
]));
|
||||
$visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance());
|
||||
@ -50,7 +50,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
||||
'shortUrl' => [
|
||||
'shortCode' => $shortUrl->getShortCode(),
|
||||
'shortUrl' => 'http:/' . $shortUrl->getShortCode(),
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
'deviceLongUrls' => $shortUrl->deviceLongUrls(),
|
||||
'dateCreated' => $shortUrl->getDateCreated()->toAtomString(),
|
||||
'visitsCount' => 0,
|
||||
@ -115,7 +115,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => 'foo',
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
'title' => 'The title',
|
||||
]));
|
||||
|
||||
@ -125,7 +125,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
||||
self::assertEquals(['shortUrl' => [
|
||||
'shortCode' => $shortUrl->getShortCode(),
|
||||
'shortUrl' => 'http:/' . $shortUrl->getShortCode(),
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
'deviceLongUrls' => $shortUrl->deviceLongUrls(),
|
||||
'dateCreated' => $shortUrl->getDateCreated()->toAtomString(),
|
||||
'visitsCount' => 0,
|
||||
|
@ -70,7 +70,7 @@ class NotifyNewShortUrlToRabbitMqTest extends TestCase
|
||||
$shortUrlId = '123';
|
||||
$update = Update::forTopicAndPayload(Topic::NEW_SHORT_URL->value, []);
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, $shortUrlId)->willReturn(
|
||||
ShortUrl::withLongUrl('longUrl'),
|
||||
ShortUrl::withLongUrl('https://longUrl'),
|
||||
);
|
||||
$this->updatesGenerator->expects($this->once())->method('newShortUrlUpdate')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
@ -87,7 +87,7 @@ class NotifyNewShortUrlToRabbitMqTest extends TestCase
|
||||
$shortUrlId = '123';
|
||||
$update = Update::forTopicAndPayload(Topic::NEW_SHORT_URL->value, []);
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, $shortUrlId)->willReturn(
|
||||
ShortUrl::withLongUrl('longUrl'),
|
||||
ShortUrl::withLongUrl('https://longUrl'),
|
||||
);
|
||||
$this->updatesGenerator->expects($this->once())->method('newShortUrlUpdate')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
|
@ -98,7 +98,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
|
||||
yield 'non-orphan visit' => [
|
||||
Visit::forValidShortUrl(
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo',
|
||||
'longUrl' => 'https://foo',
|
||||
'customSlug' => 'bar',
|
||||
])),
|
||||
$visitor,
|
||||
@ -152,7 +152,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
|
||||
{
|
||||
yield 'legacy non-orphan visit' => [
|
||||
true,
|
||||
$visit = Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()),
|
||||
$visit = Visit::forValidShortUrl(ShortUrl::withLongUrl('https://longUrl'), Visitor::emptyInstance()),
|
||||
noop(...),
|
||||
function (MockObject & PublishingHelperInterface $helper) use ($visit): void {
|
||||
$helper->method('publishUpdate')->with(self::callback(function (Update $update) use ($visit): bool {
|
||||
@ -183,7 +183,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
|
||||
];
|
||||
yield 'non-legacy non-orphan visit' => [
|
||||
false,
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()),
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl('https://longUrl'), Visitor::emptyInstance()),
|
||||
function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void {
|
||||
$update = Update::forTopicAndPayload('', []);
|
||||
$updatesGenerator->expects(self::never())->method('newOrphanVisitUpdate');
|
||||
|
@ -54,7 +54,7 @@ class NotifyNewShortUrlToRedisTest extends TestCase
|
||||
$shortUrlId = '123';
|
||||
$update = Update::forTopicAndPayload(Topic::NEW_SHORT_URL->value, []);
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, $shortUrlId)->willReturn(
|
||||
ShortUrl::withLongUrl('longUrl'),
|
||||
ShortUrl::withLongUrl('https://longUrl'),
|
||||
);
|
||||
$this->updatesGenerator->expects($this->once())->method('newShortUrlUpdate')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
|
@ -67,9 +67,9 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
public function newUrlsWithNoErrorsAreAllPersisted(): void
|
||||
{
|
||||
$urls = [
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'bar', [], Chronos::now(), null, 'bar', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz', [], Chronos::now(), null, 'baz', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://bar', [], Chronos::now(), null, 'bar', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz', [], Chronos::now(), null, 'baz', null),
|
||||
];
|
||||
$expectedCalls = count($urls);
|
||||
|
||||
@ -90,9 +90,9 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
public function newUrlsWithErrorsAreSkipped(): void
|
||||
{
|
||||
$urls = [
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'bar', [], Chronos::now(), null, 'bar', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz', [], Chronos::now(), null, 'baz', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://bar', [], Chronos::now(), null, 'bar', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz', [], Chronos::now(), null, 'baz', null),
|
||||
];
|
||||
|
||||
$this->em->method('getRepository')->with(ShortUrl::class)->willReturn($this->repo);
|
||||
@ -117,17 +117,19 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
public function alreadyImportedUrlsAreSkipped(): void
|
||||
{
|
||||
$urls = [
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'bar', [], Chronos::now(), null, 'bar', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz', [], Chronos::now(), null, 'baz', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz2', [], Chronos::now(), null, 'baz2', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz3', [], Chronos::now(), null, 'baz3', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://bar', [], Chronos::now(), null, 'bar', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz', [], Chronos::now(), null, 'baz', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz2', [], Chronos::now(), null, 'baz2', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz3', [], Chronos::now(), null, 'baz3', null),
|
||||
];
|
||||
|
||||
$this->em->method('getRepository')->with(ShortUrl::class)->willReturn($this->repo);
|
||||
$this->repo->expects($this->exactly(count($urls)))->method('findOneByImportedUrl')->willReturnCallback(
|
||||
fn (ImportedShlinkUrl $url): ?ShortUrl
|
||||
=> contains(['foo', 'baz2', 'baz3'], $url->longUrl) ? ShortUrl::fromImport($url, true) : null,
|
||||
fn (ImportedShlinkUrl $url): ?ShortUrl => contains(
|
||||
['https://foo', 'https://baz2', 'https://baz3'],
|
||||
$url->longUrl,
|
||||
) ? ShortUrl::fromImport($url, true) : null,
|
||||
);
|
||||
$this->shortCodeHelper->expects($this->exactly(2))->method('ensureShortCodeUniqueness')->willReturn(true);
|
||||
$this->em->expects($this->exactly(2))->method('persist')->with($this->isInstanceOf(ShortUrl::class));
|
||||
@ -143,11 +145,11 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
public function nonUniqueShortCodesAreAskedToUser(): void
|
||||
{
|
||||
$urls = [
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'bar', [], Chronos::now(), null, 'bar', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz', [], Chronos::now(), null, 'baz', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz2', [], Chronos::now(), null, 'baz2', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'baz3', [], Chronos::now(), null, 'baz3', 'bar'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://foo', [], Chronos::now(), null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://bar', [], Chronos::now(), null, 'bar', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz', [], Chronos::now(), null, 'baz', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz2', [], Chronos::now(), null, 'baz2', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz3', [], Chronos::now(), null, 'baz3', 'bar'),
|
||||
];
|
||||
|
||||
$this->em->method('getRepository')->with(ShortUrl::class)->willReturn($this->repo);
|
||||
@ -191,7 +193,7 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
{
|
||||
$now = Chronos::now();
|
||||
$createImportedUrl = static fn (array $visits) =>
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 's', [], $now, null, 's', null, $visits);
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://s', [], $now, null, 's', null, $visits);
|
||||
|
||||
yield 'new short URL' => [$createImportedUrl([
|
||||
new ImportedShlinkVisit('', '', $now, null),
|
||||
|
@ -43,7 +43,9 @@ class ShortUrlTest extends TestCase
|
||||
public static function provideInvalidShortUrls(): iterable
|
||||
{
|
||||
yield 'with custom slug' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => 'longUrl'])),
|
||||
ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => 'https://longUrl']),
|
||||
),
|
||||
'The short code cannot be regenerated on ShortUrls where a custom slug was provided.',
|
||||
];
|
||||
yield 'already persisted' => [
|
||||
@ -68,7 +70,7 @@ class ShortUrlTest extends TestCase
|
||||
{
|
||||
yield 'no custom slug' => [ShortUrl::createFake()];
|
||||
yield 'imported with custom slug' => [ShortUrl::fromImport(
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'longUrl', [], Chronos::now(), null, 'custom-slug', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://url', [], Chronos::now(), null, 'custom-slug', null),
|
||||
true,
|
||||
)];
|
||||
}
|
||||
@ -77,7 +79,7 @@ class ShortUrlTest extends TestCase
|
||||
public function shortCodesHaveExpectedLength(?int $length, int $expectedLength): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
[ShortUrlInputFilter::SHORT_CODE_LENGTH => $length, 'longUrl' => 'longUrl'],
|
||||
[ShortUrlInputFilter::SHORT_CODE_LENGTH => $length, 'longUrl' => 'https://longUrl'],
|
||||
));
|
||||
|
||||
self::assertEquals($expectedLength, strlen($shortUrl->getShortCode()));
|
||||
@ -92,30 +94,30 @@ class ShortUrlTest extends TestCase
|
||||
#[Test]
|
||||
public function deviceLongUrlsAreUpdated(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('foo');
|
||||
$shortUrl = ShortUrl::withLongUrl('https://foo');
|
||||
|
||||
$shortUrl->update(ShortUrlEdition::fromRawData([
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::ANDROID->value => 'android',
|
||||
DeviceType::IOS->value => 'ios',
|
||||
DeviceType::ANDROID->value => 'https://android',
|
||||
DeviceType::IOS->value => 'https://ios',
|
||||
],
|
||||
]));
|
||||
self::assertEquals([
|
||||
DeviceType::ANDROID->value => 'android',
|
||||
DeviceType::IOS->value => 'ios',
|
||||
DeviceType::ANDROID->value => 'https://android',
|
||||
DeviceType::IOS->value => 'https://ios',
|
||||
DeviceType::DESKTOP->value => null,
|
||||
], $shortUrl->deviceLongUrls());
|
||||
|
||||
$shortUrl->update(ShortUrlEdition::fromRawData([
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::ANDROID->value => null,
|
||||
DeviceType::DESKTOP->value => 'desktop',
|
||||
DeviceType::DESKTOP->value => 'https://desktop',
|
||||
],
|
||||
]));
|
||||
self::assertEquals([
|
||||
DeviceType::ANDROID->value => null,
|
||||
DeviceType::IOS->value => 'ios',
|
||||
DeviceType::DESKTOP->value => 'desktop',
|
||||
DeviceType::IOS->value => 'https://ios',
|
||||
DeviceType::DESKTOP->value => 'https://desktop',
|
||||
], $shortUrl->deviceLongUrls());
|
||||
|
||||
$shortUrl->update(ShortUrlEdition::fromRawData([
|
||||
@ -127,7 +129,7 @@ class ShortUrlTest extends TestCase
|
||||
self::assertEquals([
|
||||
DeviceType::ANDROID->value => null,
|
||||
DeviceType::IOS->value => null,
|
||||
DeviceType::DESKTOP->value => 'desktop',
|
||||
DeviceType::DESKTOP->value => 'https://desktop',
|
||||
], $shortUrl->deviceLongUrls());
|
||||
}
|
||||
|
||||
@ -137,7 +139,7 @@ class ShortUrlTest extends TestCase
|
||||
$range = range(1, 1000); // Use a "big" number to reduce false negatives
|
||||
$allFor = static fn (ShortUrlMode $mode): bool => every($range, static function () use ($mode): bool {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
[ShortUrlInputFilter::LONG_URL => 'foo'],
|
||||
[ShortUrlInputFilter::LONG_URL => 'https://foo'],
|
||||
new UrlShortenerOptions(mode: $mode),
|
||||
));
|
||||
$shortCode = $shortUrl->getShortCode();
|
||||
|
@ -29,7 +29,7 @@ class ShortUrlStringifierTest extends TestCase
|
||||
{
|
||||
$shortUrlWithShortCode = fn (string $shortCode, ?string $domain = null) => ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
'customSlug' => $shortCode,
|
||||
'domain' => $domain,
|
||||
]),
|
||||
|
@ -136,7 +136,7 @@ class ExtraPathRedirectMiddlewareTest extends TestCase
|
||||
$type->method('isInvalidShortUrl')->willReturn(true);
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(NotFoundType::class, $type)
|
||||
->withUri(new Uri('https://s.test/shortCode/bar/baz'));
|
||||
$shortUrl = ShortUrl::withLongUrl('longUrl');
|
||||
$shortUrl = ShortUrl::withLongUrl('https://longUrl');
|
||||
|
||||
$currentIteration = 1;
|
||||
$this->resolver->expects($this->exactly($expectedResolveCalls))->method('resolveEnabledShortUrl')->with(
|
||||
|
@ -33,37 +33,37 @@ class ShortUrlCreationTest extends TestCase
|
||||
{
|
||||
yield [[]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::VALID_SINCE => '',
|
||||
ShortUrlInputFilter::VALID_UNTIL => '',
|
||||
ShortUrlInputFilter::CUSTOM_SLUG => 'foobar',
|
||||
ShortUrlInputFilter::MAX_VISITS => 'invalid',
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::VALID_SINCE => '2017',
|
||||
ShortUrlInputFilter::MAX_VISITS => 5,
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::VALID_SINCE => new stdClass(),
|
||||
ShortUrlInputFilter::VALID_UNTIL => 'foo',
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::VALID_UNTIL => 500,
|
||||
ShortUrlInputFilter::DOMAIN => 4,
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::SHORT_CODE_LENGTH => 3,
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::CUSTOM_SLUG => '',
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::CUSTOM_SLUG => ' ',
|
||||
]];
|
||||
yield [[
|
||||
@ -73,33 +73,42 @@ class ShortUrlCreationTest extends TestCase
|
||||
ShortUrlInputFilter::LONG_URL => null,
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'missing_schema',
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
'invalid' => 'https://shlink.io',
|
||||
],
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::DESKTOP->value => '',
|
||||
],
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::DESKTOP->value => null,
|
||||
],
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::IOS->value => ' ',
|
||||
],
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'foo',
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::IOS->value => 'bar',
|
||||
DeviceType::ANDROID->value => 'missing_schema',
|
||||
],
|
||||
]];
|
||||
yield [[
|
||||
ShortUrlInputFilter::LONG_URL => 'https://foo',
|
||||
ShortUrlInputFilter::DEVICE_LONG_URLS => [
|
||||
DeviceType::IOS->value => 'https://bar',
|
||||
DeviceType::ANDROID->value => [],
|
||||
],
|
||||
]];
|
||||
@ -115,7 +124,7 @@ class ShortUrlCreationTest extends TestCase
|
||||
$creation = ShortUrlCreation::fromRawData([
|
||||
'validSince' => Chronos::parse('2015-01-01')->toAtomString(),
|
||||
'customSlug' => $customSlug,
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
], new UrlShortenerOptions(multiSegmentSlugsEnabled: $multiSegmentEnabled, mode: $shortUrlMode));
|
||||
|
||||
self::assertTrue($creation->hasValidSince());
|
||||
@ -161,7 +170,7 @@ class ShortUrlCreationTest extends TestCase
|
||||
{
|
||||
$creation = ShortUrlCreation::fromRawData([
|
||||
'title' => $title,
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
]);
|
||||
|
||||
self::assertEquals($expectedTitle, $creation->title);
|
||||
@ -184,7 +193,7 @@ class ShortUrlCreationTest extends TestCase
|
||||
{
|
||||
$creation = ShortUrlCreation::fromRawData([
|
||||
'domain' => $domain,
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
]);
|
||||
|
||||
self::assertSame($expectedDomain, $creation->domain);
|
||||
|
@ -31,23 +31,29 @@ class ShortUrlEditionTest extends TestCase
|
||||
yield 'null' => [null, [], []];
|
||||
yield 'empty' => [[], [], []];
|
||||
yield 'only new urls' => [[
|
||||
DeviceType::DESKTOP->value => 'foo',
|
||||
DeviceType::IOS->value => 'bar',
|
||||
DeviceType::DESKTOP->value => 'https://foo',
|
||||
DeviceType::IOS->value => 'https://bar',
|
||||
], [
|
||||
DeviceType::DESKTOP->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(DeviceType::DESKTOP->value, 'foo'),
|
||||
DeviceType::IOS->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(DeviceType::IOS->value, 'bar'),
|
||||
DeviceType::DESKTOP->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(
|
||||
DeviceType::DESKTOP->value,
|
||||
'https://foo',
|
||||
),
|
||||
DeviceType::IOS->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(DeviceType::IOS->value, 'https://bar'),
|
||||
], []];
|
||||
yield 'only urls to remove' => [[
|
||||
DeviceType::ANDROID->value => null,
|
||||
DeviceType::IOS->value => null,
|
||||
], [], [DeviceType::ANDROID, DeviceType::IOS]];
|
||||
yield 'both' => [[
|
||||
DeviceType::DESKTOP->value => 'bar',
|
||||
DeviceType::IOS->value => 'foo',
|
||||
DeviceType::DESKTOP->value => 'https://bar',
|
||||
DeviceType::IOS->value => 'https://foo',
|
||||
DeviceType::ANDROID->value => null,
|
||||
], [
|
||||
DeviceType::DESKTOP->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(DeviceType::DESKTOP->value, 'bar'),
|
||||
DeviceType::IOS->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(DeviceType::IOS->value, 'foo'),
|
||||
DeviceType::DESKTOP->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(
|
||||
DeviceType::DESKTOP->value,
|
||||
'https://bar',
|
||||
),
|
||||
DeviceType::IOS->value => DeviceLongUrlPair::fromRawTypeAndLongUrl(DeviceType::IOS->value, 'https://foo'),
|
||||
], [DeviceType::ANDROID]];
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class ShortUrlResolverTest extends TestCase
|
||||
#[Test, DataProvider('provideAdminApiKeys')]
|
||||
public function shortCodeIsProperlyParsed(?ApiKey $apiKey): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('expected_url');
|
||||
$shortUrl = ShortUrl::withLongUrl('https://expected_url');
|
||||
$shortCode = $shortUrl->getShortCode();
|
||||
$identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode);
|
||||
|
||||
@ -76,7 +76,7 @@ class ShortUrlResolverTest extends TestCase
|
||||
#[Test]
|
||||
public function shortCodeToEnabledShortUrlProperlyParsesShortCode(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('expected_url');
|
||||
$shortUrl = ShortUrl::withLongUrl('https://expected_url');
|
||||
$shortCode = $shortUrl->getShortCode();
|
||||
|
||||
$this->repo->expects($this->once())->method('findOneWithDomainFallback')->with(
|
||||
@ -111,7 +111,9 @@ class ShortUrlResolverTest extends TestCase
|
||||
$now = Chronos::now();
|
||||
|
||||
yield 'maxVisits reached' => [(function () {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'longUrl']));
|
||||
$shortUrl = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'https://longUrl']),
|
||||
);
|
||||
$shortUrl->setVisits(new ArrayCollection(map(
|
||||
range(0, 4),
|
||||
fn () => Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()),
|
||||
@ -120,16 +122,16 @@ class ShortUrlResolverTest extends TestCase
|
||||
return $shortUrl;
|
||||
})()];
|
||||
yield 'future validSince' => [ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now->addMonth()->toAtomString(), 'longUrl' => 'longUrl'],
|
||||
['validSince' => $now->addMonth()->toAtomString(), 'longUrl' => 'https://longUrl'],
|
||||
))];
|
||||
yield 'past validUntil' => [ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validUntil' => $now->subMonth()->toAtomString(), 'longUrl' => 'longUrl'],
|
||||
['validUntil' => $now->subMonth()->toAtomString(), 'longUrl' => 'https://longUrl'],
|
||||
))];
|
||||
yield 'mixed' => [(function () use ($now) {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'maxVisits' => 3,
|
||||
'validUntil' => $now->subMonth()->toAtomString(),
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
]));
|
||||
$shortUrl->setVisits(new ArrayCollection(map(
|
||||
range(0, 4),
|
||||
|
@ -57,7 +57,7 @@ class ShortUrlServiceTest extends TestCase
|
||||
ShortUrlEdition $shortUrlEdit,
|
||||
?ApiKey $apiKey,
|
||||
): void {
|
||||
$originalLongUrl = 'originalLongUrl';
|
||||
$originalLongUrl = 'https://originalLongUrl';
|
||||
$shortUrl = ShortUrl::withLongUrl($originalLongUrl);
|
||||
|
||||
$this->urlResolver->expects($this->once())->method('resolveShortUrl')->with(
|
||||
@ -103,16 +103,16 @@ class ShortUrlServiceTest extends TestCase
|
||||
yield 'long URL and API key' => [new InvokedCount(1), ShortUrlEdition::fromRawData([
|
||||
'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(),
|
||||
'maxVisits' => 10,
|
||||
'longUrl' => 'modifiedLongUrl',
|
||||
'longUrl' => 'https://modifiedLongUrl',
|
||||
]), ApiKey::create()];
|
||||
yield 'long URL with validation' => [new InvokedCount(1), ShortUrlEdition::fromRawData([
|
||||
'longUrl' => 'modifiedLongUrl',
|
||||
'longUrl' => 'https://modifiedLongUrl',
|
||||
'validateUrl' => true,
|
||||
]), null];
|
||||
yield 'device redirects' => [new InvokedCount(0), ShortUrlEdition::fromRawData([
|
||||
'deviceLongUrls' => [
|
||||
DeviceType::IOS->value => 'iosLongUrl',
|
||||
DeviceType::ANDROID->value => 'androidLongUrl',
|
||||
DeviceType::IOS->value => 'https://iosLongUrl',
|
||||
DeviceType::ANDROID->value => 'https://androidLongUrl',
|
||||
],
|
||||
]), null];
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
||||
]];
|
||||
yield 'max visits only' => [ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'maxVisits' => $maxVisits,
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
])), [
|
||||
'validSince' => null,
|
||||
'validUntil' => null,
|
||||
@ -52,7 +52,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
||||
]];
|
||||
yield 'max visits and valid since' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now, 'maxVisits' => $maxVisits, 'longUrl' => 'longUrl'],
|
||||
['validSince' => $now, 'maxVisits' => $maxVisits, 'longUrl' => 'https://longUrl'],
|
||||
)),
|
||||
[
|
||||
'validSince' => $now->toAtomString(),
|
||||
@ -62,7 +62,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
||||
];
|
||||
yield 'both dates' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now, 'validUntil' => $now->subDays(10), 'longUrl' => 'longUrl'],
|
||||
['validSince' => $now, 'validUntil' => $now->subDays(10), 'longUrl' => 'https://longUrl'],
|
||||
)),
|
||||
[
|
||||
'validSince' => $now->toAtomString(),
|
||||
@ -75,7 +75,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
||||
'validSince' => $now,
|
||||
'validUntil' => $now->subDays(5),
|
||||
'maxVisits' => $maxVisits,
|
||||
'longUrl' => 'longUrl',
|
||||
'longUrl' => 'https://longUrl',
|
||||
])),
|
||||
[
|
||||
'validSince' => $now->toAtomString(),
|
||||
|
@ -47,8 +47,10 @@ class VisitLocatorTest extends TestCase
|
||||
): void {
|
||||
$unlocatedVisits = map(
|
||||
range(1, 200),
|
||||
fn (int $i) =>
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl(sprintf('short_code_%s', $i)), Visitor::emptyInstance()),
|
||||
fn (int $i) => Visit::forValidShortUrl(
|
||||
ShortUrl::withLongUrl(sprintf('https://short_code_%s', $i)),
|
||||
Visitor::emptyInstance(),
|
||||
),
|
||||
);
|
||||
|
||||
$this->repo->expects($this->once())->method($expectedRepoMethodName)->willReturn($unlocatedVisits);
|
||||
@ -85,7 +87,7 @@ class VisitLocatorTest extends TestCase
|
||||
bool $isNonLocatableAddress,
|
||||
): void {
|
||||
$unlocatedVisits = [
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl('foo'), Visitor::emptyInstance()),
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl('https://foo'), Visitor::emptyInstance()),
|
||||
];
|
||||
|
||||
$this->repo->expects($this->once())->method($expectedRepoMethodName)->willReturn($unlocatedVisits);
|
||||
|
@ -268,6 +268,8 @@ class CreateShortUrlTest extends ApiTestCase
|
||||
yield 'missing long url v3' => [[], '3', 'https://shlink.io/api/error/invalid-data'];
|
||||
yield 'empty long url v2' => [['longUrl' => null], '2', 'INVALID_ARGUMENT'];
|
||||
yield 'empty long url v3' => [['longUrl' => ' '], '3', 'https://shlink.io/api/error/invalid-data'];
|
||||
yield 'missing url schema v2' => [['longUrl' => 'foo.com'], '2', 'INVALID_ARGUMENT'];
|
||||
yield 'missing url schema v3' => [['longUrl' => 'foo.com'], '3', 'https://shlink.io/api/error/invalid-data'];
|
||||
yield 'empty device long url v2' => [[
|
||||
'longUrl' => 'foo',
|
||||
'deviceLongUrls' => [
|
||||
|
@ -96,7 +96,7 @@ class EditShortUrlTest extends ApiTestCase
|
||||
public static function provideLongUrls(): iterable
|
||||
{
|
||||
yield 'valid URL' => ['https://shlink.io', self::STATUS_OK, null];
|
||||
yield 'invalid URL' => ['htt:foo', self::STATUS_BAD_REQUEST, 'INVALID_URL'];
|
||||
yield 'invalid URL' => ['http://foo', self::STATUS_BAD_REQUEST, 'INVALID_URL'];
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideInvalidUrls')]
|
||||
|
Loading…
Reference in New Issue
Block a user