mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Fix ImportedLinksProcessorTest
This commit is contained in:
@@ -14,6 +14,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkRedirectRule;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkVisit;
|
||||
|
||||
use function count;
|
||||
use function Shlinkio\Shlink\Core\ArrayUtils\map;
|
||||
use function Shlinkio\Shlink\Core\normalizeDate;
|
||||
use function sprintf;
|
||||
@@ -69,6 +70,10 @@ final readonly class ShortUrlImporting
|
||||
EntityManagerInterface $em,
|
||||
ShortUrlRedirectRuleServiceInterface $redirectRuleService,
|
||||
): void {
|
||||
if ($this->isNew && count($rules) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$shortUrl = $this->resolveShortUrl($em);
|
||||
$redirectRules = map(
|
||||
$rules,
|
||||
|
||||
@@ -24,7 +24,7 @@ use function trim;
|
||||
class RedirectCondition extends AbstractEntity implements JsonSerializable
|
||||
{
|
||||
private function __construct(
|
||||
private readonly RedirectConditionType $type,
|
||||
public readonly RedirectConditionType $type,
|
||||
private readonly string $matchValue,
|
||||
private readonly string|null $matchKey = null,
|
||||
) {
|
||||
|
||||
@@ -291,7 +291,7 @@ class GeolocationDbUpdaterTest extends TestCase
|
||||
private function geolocationDbUpdater(TrackingOptions|null $options = null): GeolocationDbUpdater
|
||||
{
|
||||
$locker = $this->createMock(Lock\LockFactory::class);
|
||||
$locker->method('createLock')->with($this->isType('string'))->willReturn($this->lock);
|
||||
$locker->method('createLock')->with($this->isString())->willReturn($this->lock);
|
||||
|
||||
return new GeolocationDbUpdater($this->dbUpdater, $locker, $options ?? new TrackingOptions(), $this->em, 3);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RuntimeException;
|
||||
use Shlinkio\Shlink\Core\Importer\ImportedLinksProcessor;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Model\RedirectConditionType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleServiceInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortCodeUniquenessHelperInterface;
|
||||
@@ -24,6 +27,8 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\Visitor;
|
||||
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepository;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkOrphanVisit;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkRedirectCondition;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkRedirectRule;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkVisit;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportResult;
|
||||
@@ -71,10 +76,31 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
#[Test]
|
||||
public function newUrlsWithNoErrorsAreAllPersisted(): void
|
||||
{
|
||||
$now = Chronos::now();
|
||||
$urls = [
|
||||
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),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://foo', [], $now, null, 'foo', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://bar', [], $now, null, 'bar', 'foo'),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'https://baz', [], $now, null, 'baz', null, redirectRules: [
|
||||
new ImportedShlinkRedirectRule(
|
||||
longUrl: 'https://example.com/android',
|
||||
conditions: [
|
||||
new ImportedShlinkRedirectCondition(
|
||||
RedirectConditionType::DEVICE->value,
|
||||
DeviceType::ANDROID->value,
|
||||
),
|
||||
],
|
||||
),
|
||||
new ImportedShlinkRedirectRule(
|
||||
longUrl: 'https://example.com/spain',
|
||||
conditions: [
|
||||
new ImportedShlinkRedirectCondition(
|
||||
RedirectConditionType::GEOLOCATION_COUNTRY_CODE->value,
|
||||
'ES',
|
||||
),
|
||||
new ImportedShlinkRedirectCondition(RedirectConditionType::LANGUAGE->value, 'es-ES'),
|
||||
],
|
||||
),
|
||||
]),
|
||||
];
|
||||
$expectedCalls = count($urls);
|
||||
|
||||
@@ -86,7 +112,19 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
$this->em->expects($this->exactly($expectedCalls))->method('persist')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
);
|
||||
$this->io->expects($this->exactly($expectedCalls))->method('text')->with($this->isType('string'));
|
||||
$this->io->expects($this->exactly($expectedCalls))->method('text')->with($this->isString());
|
||||
$this->redirectRuleService->expects($this->once())->method('saveRulesForShortUrl')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
$this->callback(function (array $rules): bool {
|
||||
Assert::assertCount(2, $rules);
|
||||
Assert::assertInstanceOf(ShortUrlRedirectRule::class, $rules[0]);
|
||||
Assert::assertInstanceOf(ShortUrlRedirectRule::class, $rules[1]);
|
||||
Assert::assertCount(1, $rules[0]->mapConditions(fn ($c) => $c));
|
||||
Assert::assertCount(2, $rules[1]->mapConditions(fn ($c) => $c));
|
||||
|
||||
return true;
|
||||
}),
|
||||
);
|
||||
|
||||
$this->processor->process($this->io, ImportResult::withShortUrls($urls), $this->buildParams());
|
||||
}
|
||||
@@ -243,7 +281,8 @@ class ImportedLinksProcessorTest extends TestCase
|
||||
if (!$originalShortUrl->getId()) {
|
||||
$this->em->expects($this->never())->method('find');
|
||||
} else {
|
||||
$this->em->expects($this->exactly(2))->method('find')->willReturn($foundShortUrl);
|
||||
// 3 times: Initial short URL checking, before creating redirect rules, before creating visits
|
||||
$this->em->expects($this->exactly(3))->method('find')->willReturn($foundShortUrl);
|
||||
}
|
||||
$this->em->expects($this->once())->method('persist')->willReturnCallback(
|
||||
static fn (Visit $visit) => Assert::assertSame(
|
||||
|
||||
@@ -9,6 +9,8 @@ use PHPUnit\Framework\Attributes\TestWith;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Model\RedirectConditionType;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkRedirectCondition;
|
||||
use Shlinkio\Shlink\IpGeolocation\Model\Location;
|
||||
|
||||
use const Shlinkio\Shlink\IP_ADDRESS_REQUEST_ATTRIBUTE;
|
||||
@@ -133,4 +135,22 @@ class RedirectConditionTest extends TestCase
|
||||
yield 'matching location' => [new Location(city: 'Madrid'), 'Madrid', true];
|
||||
yield 'matching case-insensitive' => [new Location(city: 'Los Angeles'), 'los angeles', true];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[TestWith(['invalid', null])]
|
||||
#[TestWith([RedirectConditionType::DEVICE->value, RedirectConditionType::DEVICE])]
|
||||
#[TestWith([RedirectConditionType::LANGUAGE->value, RedirectConditionType::LANGUAGE])]
|
||||
#[TestWith([RedirectConditionType::QUERY_PARAM->value, RedirectConditionType::QUERY_PARAM])]
|
||||
#[TestWith([RedirectConditionType::IP_ADDRESS->value, RedirectConditionType::IP_ADDRESS])]
|
||||
#[TestWith(
|
||||
[RedirectConditionType::GEOLOCATION_COUNTRY_CODE->value, RedirectConditionType::GEOLOCATION_COUNTRY_CODE],
|
||||
)]
|
||||
#[TestWith([RedirectConditionType::GEOLOCATION_CITY_NAME->value, RedirectConditionType::GEOLOCATION_CITY_NAME])]
|
||||
public function canBeCreatedFromImport(string $type, RedirectConditionType|null $expectedType): void
|
||||
{
|
||||
$condition = RedirectCondition::fromImport(
|
||||
new ImportedShlinkRedirectCondition($type, DeviceType::ANDROID->value, ''),
|
||||
);
|
||||
self::assertEquals($expectedType, $condition?->type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase
|
||||
|
||||
$tagRepo = $this->createMock(TagRepository::class);
|
||||
$tagRepo->expects($this->exactly($expectedLookedOutTags))->method('findOneBy')->with(
|
||||
$this->isType('array'),
|
||||
$this->isArray(),
|
||||
)->willReturnCallback(function (array $criteria): Tag|null {
|
||||
['name' => $name] = $criteria;
|
||||
return $name === 'foo' ? new Tag($name) : null;
|
||||
@@ -115,7 +115,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase
|
||||
public function newDomainsAreMemoizedUntilStateIsCleared(): void
|
||||
{
|
||||
$repo = $this->createMock(DomainRepository::class);
|
||||
$repo->expects($this->exactly(3))->method('findOneBy')->with($this->isType('array'))->willReturn(null);
|
||||
$repo->expects($this->exactly(3))->method('findOneBy')->with($this->isArray())->willReturn(null);
|
||||
$this->em->method('getRepository')->with(Domain::class)->willReturn($repo);
|
||||
|
||||
$authority = 'foo.com';
|
||||
@@ -134,7 +134,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase
|
||||
public function newTagsAreMemoizedUntilStateIsCleared(): void
|
||||
{
|
||||
$tagRepo = $this->createMock(TagRepository::class);
|
||||
$tagRepo->expects($this->exactly(6))->method('findOneBy')->with($this->isType('array'))->willReturn(null);
|
||||
$tagRepo->expects($this->exactly(6))->method('findOneBy')->with($this->isArray())->willReturn(null);
|
||||
$this->em->method('getRepository')->with(Tag::class)->willReturn($tagRepo);
|
||||
|
||||
$tags = ['foo', 'bar'];
|
||||
|
||||
@@ -38,7 +38,7 @@ class UrlShortenerTest extends TestCase
|
||||
// FIXME Should use the interface, but it doe snot define wrapInTransaction explicitly
|
||||
$this->em = $this->createMock(EntityManager::class);
|
||||
$this->em->method('persist')->willReturnCallback(fn (ShortUrl $shortUrl) => $shortUrl->setId('10'));
|
||||
$this->em->method('wrapInTransaction')->with($this->isType('callable'))->willReturnCallback(
|
||||
$this->em->method('wrapInTransaction')->with($this->isCallable())->willReturnCallback(
|
||||
fn (callable $callback) => $callback(),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user