Added test for DomainRedirectCommand

This commit is contained in:
Alejandro Celaya 2021-07-22 20:48:58 +02:00
parent 267d72a76c
commit 24a6a0c23f
20 changed files with 245 additions and 52 deletions

View File

@ -36,7 +36,7 @@ class RoleResolverTest extends TestCase
int $expectedDomainCalls,
): void {
$getDomain = $this->domainService->getOrCreate('example.com')->willReturn(
(new Domain('example.com'))->setId('1'),
Domain::withAuthority('example.com')->setId('1'),
);
$result = $this->resolver->determineRoles($input);
@ -47,7 +47,7 @@ class RoleResolverTest extends TestCase
public function provideRoles(): iterable
{
$domain = (new Domain('example.com'))->setId('1');
$domain = Domain::withAuthority('example.com')->setId('1');
$buildInput = function (array $definition): InputInterface {
$input = $this->prophesize(InputInterface::class);

View File

@ -76,11 +76,13 @@ class ListKeysCommandTest extends TestCase
[
$apiKey1 = ApiKey::create(),
$apiKey2 = $this->apiKeyWithRoles([RoleDefinition::forAuthoredShortUrls()]),
$apiKey3 = $this->apiKeyWithRoles([RoleDefinition::forDomain((new Domain('example.com'))->setId('1'))]),
$apiKey3 = $this->apiKeyWithRoles(
[RoleDefinition::forDomain(Domain::withAuthority('example.com')->setId('1'))],
),
$apiKey4 = ApiKey::create(),
$apiKey5 = $this->apiKeyWithRoles([
RoleDefinition::forAuthoredShortUrls(),
RoleDefinition::forDomain((new Domain('example.com'))->setId('1')),
RoleDefinition::forDomain(Domain::withAuthority('example.com')->setId('1')),
]),
$apiKey6 = ApiKey::create(),
],

View File

@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Domain;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Domain\DomainRedirectsCommand;
use Shlinkio\Shlink\Core\Config\NotFoundRedirects;
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions;
use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait;
use Symfony\Component\Console\Tester\CommandTester;
use function substr_count;
class DomainRedirectsCommandTest extends TestCase
{
use CliTestUtilsTrait;
private CommandTester $commandTester;
private ObjectProphecy $domainService;
public function setUp(): void
{
$this->domainService = $this->prophesize(DomainServiceInterface::class);
$this->commandTester = $this->testerForCommand(new DomainRedirectsCommand($this->domainService->reveal()));
}
/**
* @test
* @dataProvider provideDomains
*/
public function onlyPlainQuestionsAreAskedForNewDomainsAndDomainsWithNoRedirects(?Domain $domain): void
{
$domainAuthority = 'my-domain.com';
$findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain);
$configureRedirects = $this->domainService->configureNotFoundRedirects(
$domainAuthority,
new NotFoundRedirects('foo.com', null, 'baz.com'),
)->willReturn(Domain::withAuthority(''));
$this->commandTester->setInputs(['foo.com', '', 'baz.com']);
$this->commandTester->execute(['domain' => $domainAuthority]);
$output = $this->commandTester->getDisplay();
self::assertStringContainsString('[OK] "Not found" redirects properly set for "my-domain.com"', $output);
self::assertStringContainsString('URL to redirect to when a user hits this domain\'s base URL', $output);
self::assertStringContainsString(
'URL to redirect to when a user hits a not found URL other than an invalid short URL',
$output,
);
self::assertStringContainsString('URL to redirect to when a user hits an invalid short URL', $output);
self::assertEquals(3, substr_count($output, '(Leave empty for no redirect)'));
$findDomain->shouldHaveBeenCalledOnce();
$configureRedirects->shouldHaveBeenCalledOnce();
$this->domainService->listDomains()->shouldNotHaveBeenCalled();
}
public function provideDomains(): iterable
{
yield 'no domain' => [null];
yield 'domain without redirects' => [Domain::withAuthority('')];
}
/** @test */
public function offersNewOptionsForDomainsWithExistingRedirects(): void
{
$domainAuthority = 'example.com';
$domain = Domain::withAuthority($domainAuthority);
$domain->configureNotFoundRedirects(new NotFoundRedirects('foo.com', 'bar.com', 'baz.com'));
$findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain);
$configureRedirects = $this->domainService->configureNotFoundRedirects(
$domainAuthority,
new NotFoundRedirects(null, 'edited.com', 'baz.com'),
)->willReturn($domain);
$this->commandTester->setInputs(['2', '1', 'edited.com', '0']);
$this->commandTester->execute(['domain' => $domainAuthority]);
$output = $this->commandTester->getDisplay();
self::assertStringContainsString('[OK] "Not found" redirects properly set for "example.com"', $output);
self::assertStringContainsString('Keep current one: [bar.com]', $output);
self::assertStringContainsString('Keep current one: [baz.com]', $output);
self::assertStringContainsString('Keep current one: [baz.com]', $output);
self::assertStringNotContainsStringIgnoringCase('(Leave empty for no redirect)', $output);
self::assertEquals(3, substr_count($output, 'Set new redirect URL'));
self::assertEquals(3, substr_count($output, 'Remove redirect'));
$findDomain->shouldHaveBeenCalledOnce();
$configureRedirects->shouldHaveBeenCalledOnce();
$this->domainService->listDomains()->shouldNotHaveBeenCalled();
}
/** @test */
public function authorityIsRequestedWhenNotProvidedAndNoOtherDomainsExist(): void
{
$domainAuthority = 'example.com';
$domain = Domain::withAuthority($domainAuthority);
$listDomains = $this->domainService->listDomains()->willReturn([]);
$findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain);
$configureRedirects = $this->domainService->configureNotFoundRedirects(
$domainAuthority,
new NotFoundRedirects(),
)->willReturn($domain);
$this->commandTester->setInputs([$domainAuthority, '', '', '']);
$this->commandTester->execute([]);
$output = $this->commandTester->getDisplay();
self::assertStringContainsString('Domain authority for which you want to set specific redirects', $output);
$listDomains->shouldHaveBeenCalledOnce();
$findDomain->shouldHaveBeenCalledOnce();
$configureRedirects->shouldHaveBeenCalledOnce();
}
/** @test */
public function oneOfTheExistingDomainsCanBeSelected(): void
{
$domainAuthority = 'existing-two.com';
$domain = Domain::withAuthority($domainAuthority);
$listDomains = $this->domainService->listDomains()->willReturn([
DomainItem::forDefaultDomain('default-domain.com', new NotFoundRedirectOptions()),
DomainItem::forExistingDomain(Domain::withAuthority('existing-one.com')),
DomainItem::forExistingDomain(Domain::withAuthority($domainAuthority)),
]);
$findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain);
$configureRedirects = $this->domainService->configureNotFoundRedirects(
$domainAuthority,
new NotFoundRedirects(),
)->willReturn($domain);
$this->commandTester->setInputs(['1', '', '', '']);
$this->commandTester->execute([]);
$output = $this->commandTester->getDisplay();
self::assertStringNotContainsString('Domain authority for which you want to set specific redirects', $output);
self::assertStringNotContainsString('default-domain.com', $output);
self::assertStringContainsString('existing-one.com', $output);
self::assertStringContainsString($domainAuthority, $output);
$listDomains->shouldHaveBeenCalledOnce();
$findDomain->shouldHaveBeenCalledOnce();
$configureRedirects->shouldHaveBeenCalledOnce();
}
/** @test */
public function aNewDomainCanBeCreatedEvenIfOthersAlreadyExist(): void
{
$domainAuthority = 'new-domain.com';
$domain = Domain::withAuthority($domainAuthority);
$listDomains = $this->domainService->listDomains()->willReturn([
DomainItem::forDefaultDomain('default-domain.com', new NotFoundRedirectOptions()),
DomainItem::forExistingDomain(Domain::withAuthority('existing-one.com')),
DomainItem::forExistingDomain(Domain::withAuthority('existing-two.com')),
]);
$findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain);
$configureRedirects = $this->domainService->configureNotFoundRedirects(
$domainAuthority,
new NotFoundRedirects(),
)->willReturn($domain);
$this->commandTester->setInputs(['2', $domainAuthority, '', '', '']);
$this->commandTester->execute([]);
$output = $this->commandTester->getDisplay();
self::assertStringContainsString('Domain authority for which you want to set specific redirects', $output);
self::assertStringNotContainsString('default-domain.com', $output);
self::assertStringContainsString('existing-one.com', $output);
self::assertStringContainsString('existing-two.com', $output);
$listDomains->shouldHaveBeenCalledOnce();
$findDomain->shouldHaveBeenCalledOnce();
$configureRedirects->shouldHaveBeenCalledOnce();
}
}

View File

@ -35,7 +35,7 @@ class ListDomainsCommandTest extends TestCase
*/
public function allDomainsAreProperlyPrinted(array $input, string $expectedOutput): void
{
$bazDomain = new Domain('baz.com');
$bazDomain = Domain::withAuthority('baz.com');
$bazDomain->configureNotFoundRedirects(new NotFoundRedirects(
null,
'https://foo.com/baz-domain/regular',
@ -47,7 +47,7 @@ class ListDomainsCommandTest extends TestCase
'base_url' => 'https://foo.com/default/base',
'invalid_short_url' => 'https://foo.com/default/invalid',
])),
DomainItem::forExistingDomain(new Domain('bar.com')),
DomainItem::forExistingDomain(Domain::withAuthority('bar.com')),
DomainItem::forExistingDomain($bazDomain),
]);

View File

@ -67,7 +67,7 @@ class DomainService implements DomainServiceInterface
public function getOrCreate(string $authority): Domain
{
$domain = $this->findByAuthority($authority) ?? new Domain($authority);
$domain = $this->findByAuthority($authority) ?? Domain::withAuthority($authority);
$this->em->persist($domain);
$this->em->flush();

View File

@ -15,10 +15,15 @@ class Domain extends AbstractEntity implements JsonSerializable, NotFoundRedirec
private ?string $regular404Redirect = null;
private ?string $invalidShortUrlRedirect = null;
public function __construct(private string $authority)
private function __construct(private string $authority)
{
}
public static function withAuthority(string $authority): self
{
return new self($authority);
}
public function getAuthority(): string
{
return $this->authority;

View File

@ -41,7 +41,9 @@ class PersistenceShortUrlRelationResolver implements ShortUrlRelationResolverInt
private function memoizeNewDomain(string $domain): Domain
{
return $this->memoizedNewDomains[$domain] = $this->memoizedNewDomains[$domain] ?? new Domain($domain);
return $this->memoizedNewDomains[$domain] = $this->memoizedNewDomains[$domain] ?? Domain::withAuthority(
$domain,
);
}
/**

View File

@ -15,7 +15,7 @@ class SimpleShortUrlRelationResolver implements ShortUrlRelationResolverInterfac
{
public function resolveDomain(?string $domain): ?Domain
{
return $domain !== null ? new Domain($domain) : null;
return $domain !== null ? Domain::withAuthority($domain) : null;
}
/**

View File

@ -28,19 +28,19 @@ class DomainRepositoryTest extends DatabaseTestCase
/** @test */
public function findDomainsReturnsExpectedResult(): void
{
$fooDomain = new Domain('foo.com');
$fooDomain = Domain::withAuthority('foo.com');
$this->getEntityManager()->persist($fooDomain);
$this->getEntityManager()->persist($this->createShortUrl($fooDomain));
$barDomain = new Domain('bar.com');
$barDomain = Domain::withAuthority('bar.com');
$this->getEntityManager()->persist($barDomain);
$this->getEntityManager()->persist($this->createShortUrl($barDomain));
$bazDomain = new Domain('baz.com');
$bazDomain = Domain::withAuthority('baz.com');
$this->getEntityManager()->persist($bazDomain);
$this->getEntityManager()->persist($this->createShortUrl($bazDomain));
$detachedDomain = new Domain('detached.com');
$detachedDomain = Domain::withAuthority('detached.com');
$this->getEntityManager()->persist($detachedDomain);
$this->getEntityManager()->flush();
@ -59,15 +59,15 @@ class DomainRepositoryTest extends DatabaseTestCase
$authorAndDomainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($authorAndDomainApiKey);
$fooDomain = new Domain('foo.com');
$fooDomain = Domain::withAuthority('foo.com');
$this->getEntityManager()->persist($fooDomain);
$this->getEntityManager()->persist($this->createShortUrl($fooDomain, $authorApiKey));
$barDomain = new Domain('bar.com');
$barDomain = Domain::withAuthority('bar.com');
$this->getEntityManager()->persist($barDomain);
$this->getEntityManager()->persist($this->createShortUrl($barDomain, $authorAndDomainApiKey));
$bazDomain = new Domain('baz.com');
$bazDomain = Domain::withAuthority('baz.com');
$this->getEntityManager()->persist($bazDomain);
$this->getEntityManager()->persist($this->createShortUrl($bazDomain, $authorApiKey));

View File

@ -340,9 +340,9 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
{
$start = Chronos::parse('2020-03-05 20:18:30');
$wrongDomain = new Domain('wrong.com');
$wrongDomain = Domain::withAuthority('wrong.com');
$this->getEntityManager()->persist($wrongDomain);
$rightDomain = new Domain('right.com');
$rightDomain = Domain::withAuthority('right.com');
$this->getEntityManager()->persist($rightDomain);
$this->getEntityManager()->flush();

View File

@ -97,7 +97,7 @@ class TagRepositoryTest extends DatabaseTestCase
/** @test */
public function tagExistsReturnsExpectedResultBasedOnApiKey(): void
{
$domain = new Domain('foo.com');
$domain = Domain::withAuthority('foo.com');
$this->getEntityManager()->persist($domain);
$this->getEntityManager()->flush();

View File

@ -222,7 +222,7 @@ class VisitRepositoryTest extends DatabaseTestCase
/** @test */
public function countVisitsReturnsExpectedResultBasedOnApiKey(): void
{
$domain = new Domain('foo.com');
$domain = Domain::withAuthority('foo.com');
$this->getEntityManager()->persist($domain);
$this->getEntityManager()->flush();

View File

@ -55,52 +55,52 @@ class DomainServiceTest extends TestCase
$default = DomainItem::forDefaultDomain('default.com', new NotFoundRedirectOptions());
$adminApiKey = ApiKey::create();
$domainSpecificApiKey = ApiKey::fromMeta(
ApiKeyMeta::withRoles(RoleDefinition::forDomain((new Domain(''))->setId('123'))),
ApiKeyMeta::withRoles(RoleDefinition::forDomain(Domain::withAuthority('')->setId('123'))),
);
yield 'empty list without API key' => [[], [$default], null];
yield 'one item without API key' => [
[new Domain('bar.com')],
[$default, DomainItem::forExistingDomain(new Domain('bar.com'))],
[Domain::withAuthority('bar.com')],
[$default, DomainItem::forExistingDomain(Domain::withAuthority('bar.com'))],
null,
];
yield 'multiple items without API key' => [
[new Domain('foo.com'), new Domain('bar.com')],
[Domain::withAuthority('foo.com'), Domain::withAuthority('bar.com')],
[
$default,
DomainItem::forExistingDomain(new Domain('foo.com')),
DomainItem::forExistingDomain(new Domain('bar.com')),
DomainItem::forExistingDomain(Domain::withAuthority('foo.com')),
DomainItem::forExistingDomain(Domain::withAuthority('bar.com')),
],
null,
];
yield 'empty list with admin API key' => [[], [$default], $adminApiKey];
yield 'one item with admin API key' => [
[new Domain('bar.com')],
[$default, DomainItem::forExistingDomain(new Domain('bar.com'))],
[Domain::withAuthority('bar.com')],
[$default, DomainItem::forExistingDomain(Domain::withAuthority('bar.com'))],
$adminApiKey,
];
yield 'multiple items with admin API key' => [
[new Domain('foo.com'), new Domain('bar.com')],
[Domain::withAuthority('foo.com'), Domain::withAuthority('bar.com')],
[
$default,
DomainItem::forExistingDomain(new Domain('foo.com')),
DomainItem::forExistingDomain(new Domain('bar.com')),
DomainItem::forExistingDomain(Domain::withAuthority('foo.com')),
DomainItem::forExistingDomain(Domain::withAuthority('bar.com')),
],
$adminApiKey,
];
yield 'empty list with domain-specific API key' => [[], [], $domainSpecificApiKey];
yield 'one item with domain-specific API key' => [
[new Domain('bar.com')],
[DomainItem::forExistingDomain(new Domain('bar.com'))],
[Domain::withAuthority('bar.com')],
[DomainItem::forExistingDomain(Domain::withAuthority('bar.com'))],
$domainSpecificApiKey,
];
yield 'multiple items with domain-specific API key' => [
[new Domain('foo.com'), new Domain('bar.com')],
[Domain::withAuthority('foo.com'), Domain::withAuthority('bar.com')],
[
DomainItem::forExistingDomain(new Domain('foo.com')),
DomainItem::forExistingDomain(new Domain('bar.com')),
DomainItem::forExistingDomain(Domain::withAuthority('foo.com')),
DomainItem::forExistingDomain(Domain::withAuthority('bar.com')),
],
$domainSpecificApiKey,
];
@ -120,7 +120,7 @@ class DomainServiceTest extends TestCase
/** @test */
public function getDomainReturnsEntityWhenFound(): void
{
$domain = new Domain('');
$domain = Domain::withAuthority('');
$find = $this->em->find(Domain::class, '123')->willReturn($domain);
$result = $this->domainService->getDomain('123');
@ -185,6 +185,6 @@ class DomainServiceTest extends TestCase
public function provideFoundDomains(): iterable
{
yield 'domain not found' => [null];
yield 'domain found' => [new Domain('')];
yield 'domain found' => [Domain::withAuthority('')];
}
}

View File

@ -78,7 +78,7 @@ class NotFoundRedirectHandlerTest extends TestCase
}];
yield 'non-redirecting domain' => [function (ObjectProphecy $domainService, ObjectProphecy $resolver): void {
$domainService->findByAuthority(Argument::cetera())
->willReturn(new Domain(''))
->willReturn(Domain::withAuthority(''))
->shouldBeCalledOnce();
$resolver->resolveRedirectResponse(Argument::cetera())
->willReturn(null)
@ -109,7 +109,7 @@ class NotFoundRedirectHandlerTest extends TestCase
public function domainRedirectIsUsedIfFound(): void
{
$expectedResp = new Response();
$domain = new Domain('');
$domain = Domain::withAuthority('');
$findDomain = $this->domainService->findByAuthority(Argument::cetera())->willReturn($domain);
$resolveRedirect = $this->resolver->resolveRedirectResponse(

View File

@ -60,7 +60,7 @@ class ShortCodeHelperTest extends TestCase
public function provideDomains(): iterable
{
yield 'no domain' => [null, null];
yield 'domain' => [new Domain($authority = 'doma.in'), $authority];
yield 'domain' => [Domain::withAuthority($authority = 'doma.in'), $authority];
}
/** @test */

View File

@ -68,7 +68,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase
$authority = 'doma.in';
yield 'not found domain' => [null, $authority];
yield 'found domain' => [new Domain($authority), $authority];
yield 'found domain' => [Domain::withAuthority($authority), $authority];
}
/**

View File

@ -12,11 +12,11 @@ class DomainFixture extends AbstractFixture
{
public function load(ObjectManager $manager): void
{
$domain = new Domain('example.com');
$domain = Domain::withAuthority('example.com');
$manager->persist($domain);
$this->addReference('example_domain', $domain);
$manager->persist(new Domain('this_domain_is_detached.com'));
$manager->persist(Domain::withAuthority('this_domain_is_detached.com'));
$manager->flush();
}
}

View File

@ -35,7 +35,7 @@ class ListDomainsActionTest extends TestCase
$apiKey = ApiKey::create();
$domains = [
DomainItem::forDefaultDomain('bar.com', new NotFoundRedirectOptions()),
DomainItem::forExistingDomain(new Domain('baz.com')),
DomainItem::forExistingDomain(Domain::withAuthority('baz.com')),
];
$listDomains = $this->domainService->listDomains($apiKey)->willReturn($domains);

View File

@ -82,19 +82,23 @@ class OverrideDomainMiddlewareTest extends TestCase
public function provideBodies(): iterable
{
yield 'no domain provided' => [new Domain('foo.com'), [], [ShortUrlInputFilter::DOMAIN => 'foo.com']];
yield 'no domain provided' => [
Domain::withAuthority('foo.com'),
[],
[ShortUrlInputFilter::DOMAIN => 'foo.com'],
];
yield 'other domain provided' => [
new Domain('bar.com'),
Domain::withAuthority('bar.com'),
[ShortUrlInputFilter::DOMAIN => 'foo.com'],
[ShortUrlInputFilter::DOMAIN => 'bar.com'],
];
yield 'same domain provided' => [
new Domain('baz.com'),
Domain::withAuthority('baz.com'),
[ShortUrlInputFilter::DOMAIN => 'baz.com'],
[ShortUrlInputFilter::DOMAIN => 'baz.com'],
];
yield 'more body params' => [
new Domain('doma.in'),
Domain::withAuthority('doma.in'),
[ShortUrlInputFilter::DOMAIN => 'baz.com', 'something' => 'else', 'foo' => 123],
[ShortUrlInputFilter::DOMAIN => 'doma.in', 'something' => 'else', 'foo' => 123],
];
@ -106,7 +110,7 @@ class OverrideDomainMiddlewareTest extends TestCase
*/
public function setsRequestAttributeWhenMethodIsNotPost(string $method): void
{
$domain = new Domain('something.com');
$domain = Domain::withAuthority('something.com');
$request = $this->requestWithApiKey()->withMethod($method);
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(true);
$getRoleMeta = $this->apiKey->getRoleMeta(Role::DOMAIN_SPECIFIC)->willReturn(['domain_id' => '123']);

View File

@ -55,7 +55,7 @@ class ApiKeyServiceTest extends TestCase
yield 'no expiration date or name' => [null, null, []];
yield 'expiration date' => [Chronos::parse('2030-01-01'), null, []];
yield 'roles' => [null, null, [
RoleDefinition::forDomain((new Domain(''))->setId('123')),
RoleDefinition::forDomain(Domain::withAuthority('')->setId('123')),
RoleDefinition::forAuthoredShortUrls(),
]];
yield 'single name' => [null, 'Alice', []];