Add API tests to cover usage of orphan visits restricted keys

This commit is contained in:
Alejandro Celaya
2023-05-31 09:22:40 +02:00
parent eaba5edf7f
commit be26dd58c3
8 changed files with 52 additions and 16 deletions

View File

@@ -32,7 +32,7 @@ class RoleResolver implements RoleResolverInterface
$roleDefinitions[] = $this->resolveRoleForAuthority($domainAuthority); $roleDefinitions[] = $this->resolveRoleForAuthority($domainAuthority);
} }
if ($noOrphanVisits) { if ($noOrphanVisits) {
$roleDefinitions[] = RoleDefinition::forOrphanVisitsExcluded(); $roleDefinitions[] = RoleDefinition::forNoOrphanVisits();
} }
return $roleDefinitions; return $roleDefinitions;

View File

@@ -262,7 +262,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
$noOrphanVisitsApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forOrphanVisitsExcluded())); $noOrphanVisitsApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forNoOrphanVisits()));
$this->getEntityManager()->persist($noOrphanVisitsApiKey); $this->getEntityManager()->persist($noOrphanVisitsApiKey);
$apiKey1 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls())); $apiKey1 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
@@ -330,7 +330,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist($shortUrl);
$this->createVisitsForShortUrl($shortUrl, 7); $this->createVisitsForShortUrl($shortUrl, 7);
$noOrphanVisitsApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forOrphanVisitsExcluded())); $noOrphanVisitsApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forNoOrphanVisits()));
$this->getEntityManager()->persist($noOrphanVisitsApiKey); $this->getEntityManager()->persist($noOrphanVisitsApiKey);
$botsCount = 3; $botsCount = 3;

View File

@@ -48,7 +48,7 @@ class VisitsDeleterTest extends TestCase
$this->repo->expects($this->never())->method('deleteOrphanVisits'); $this->repo->expects($this->never())->method('deleteOrphanVisits');
$result = $this->visitsDeleter->deleteOrphanVisits( $result = $this->visitsDeleter->deleteOrphanVisits(
ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forOrphanVisitsExcluded())), ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forNoOrphanVisits())),
); );
self::assertEquals(0, $result->affectedItems); self::assertEquals(0, $result->affectedItems);

View File

@@ -26,7 +26,7 @@ final class RoleDefinition
); );
} }
public static function forOrphanVisitsExcluded(): self public static function forNoOrphanVisits(): self
{ {
return new self(Role::NO_ORPHAN_VISITS, []); return new self(Role::NO_ORPHAN_VISITS, []);
} }

View File

@@ -10,7 +10,7 @@ use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase;
class DeleteOrphanVisitsTest extends ApiTestCase class DeleteOrphanVisitsTest extends ApiTestCase
{ {
#[Test] #[Test]
public function deletesVisitsForShortUrlWithoutAffectingTheRest(): void public function deletesOrphanVisitsWithoutAffectingTheRest(): void
{ {
self::assertEquals(7, $this->getTotalVisits()); self::assertEquals(7, $this->getTotalVisits());
self::assertEquals(3, $this->getOrphanVisits()); self::assertEquals(3, $this->getOrphanVisits());
@@ -24,6 +24,21 @@ class DeleteOrphanVisitsTest extends ApiTestCase
self::assertEquals(0, $this->getOrphanVisits()); self::assertEquals(0, $this->getOrphanVisits());
} }
#[Test]
public function doesNotDeleteOrphanVisitsForRestrictedApiKey(): void
{
self::assertEquals(7, $this->getTotalVisits());
self::assertEquals(3, $this->getOrphanVisits());
$resp = $this->callApiWithKey(self::METHOD_DELETE, '/visits/orphan', apiKey: 'no_orphans_api_key');
$payload = $this->getJsonResponsePayload($resp);
self::assertEquals(200, $resp->getStatusCode());
self::assertEquals(0, $payload['deletedVisits']);
self::assertEquals(7, $this->getTotalVisits()); // This verifies that regular visits have not been affected
self::assertEquals(3, $this->getOrphanVisits()); // This verifies that all orphan visits still exist
}
private function getTotalVisits(): int private function getTotalVisits(): int
{ {
$resp = $this->callApiWithKey(self::METHOD_GET, '/visits/non-orphan'); $resp = $this->callApiWithKey(self::METHOD_GET, '/visits/non-orphan');

View File

@@ -11,7 +11,7 @@ use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase;
class GlobalVisitsTest extends ApiTestCase class GlobalVisitsTest extends ApiTestCase
{ {
#[Test, DataProvider('provideApiKeys')] #[Test, DataProvider('provideApiKeys')]
public function returnsExpectedVisitsStats(string $apiKey, int $expectedVisits): void public function returnsExpectedVisitsStats(string $apiKey, int $expectedVisits, int $expectedOrphanVisits): void
{ {
$resp = $this->callApiWithKey(self::METHOD_GET, '/visits', [], $apiKey); $resp = $this->callApiWithKey(self::METHOD_GET, '/visits', [], $apiKey);
$payload = $this->getJsonResponsePayload($resp); $payload = $this->getJsonResponsePayload($resp);
@@ -20,13 +20,14 @@ class GlobalVisitsTest extends ApiTestCase
self::assertArrayHasKey('visitsCount', $payload['visits']); self::assertArrayHasKey('visitsCount', $payload['visits']);
self::assertArrayHasKey('orphanVisitsCount', $payload['visits']); self::assertArrayHasKey('orphanVisitsCount', $payload['visits']);
self::assertEquals($expectedVisits, $payload['visits']['visitsCount']); self::assertEquals($expectedVisits, $payload['visits']['visitsCount']);
self::assertEquals(3, $payload['visits']['orphanVisitsCount']); self::assertEquals($expectedOrphanVisits, $payload['visits']['orphanVisitsCount']);
} }
public static function provideApiKeys(): iterable public static function provideApiKeys(): iterable
{ {
yield 'admin API key' => ['valid_api_key', 7]; yield 'admin API key' => ['valid_api_key', 7, 3];
yield 'domain API key' => ['domain_api_key', 0]; yield 'domain API key' => ['domain_api_key', 0, 3];
yield 'author API key' => ['author_api_key', 5]; yield 'author API key' => ['author_api_key', 5, 3];
yield 'no orphans API key' => ['no_orphans_api_key', 7, 0];
} }
} }

View File

@@ -69,4 +69,16 @@ class OrphanVisitsTest extends ApiTestCase
[self::REGULAR_NOT_FOUND], [self::REGULAR_NOT_FOUND],
]; ];
} }
#[Test]
public function noVisitsAreReturnedForRestrictedApiKey(): void
{
$resp = $this->callApiWithKey(self::METHOD_GET, '/visits/orphan', apiKey: 'no_orphans_api_key');
$payload = $this->getJsonResponsePayload($resp);
$visits = $payload['visits']['data'] ?? null;
self::assertIsArray($visits);
self::assertEmpty($visits);
self::assertEquals(0, $payload['visits']['pagination']['totalItems'] ?? Paginator::ALL_ITEMS);
}
} }

View File

@@ -23,21 +23,29 @@ class ApiKeyFixture extends AbstractFixture implements DependentFixtureInterface
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void
{ {
$manager->persist($this->buildApiKey('valid_api_key', true)); $manager->persist($this->buildApiKey('valid_api_key', enabled: true));
$manager->persist($this->buildApiKey('disabled_api_key', false)); $manager->persist($this->buildApiKey('disabled_api_key', enabled: false));
$manager->persist($this->buildApiKey('expired_api_key', true, Chronos::now()->subDay()->startOfDay())); $manager->persist($this->buildApiKey(
'expired_api_key',
enabled: true,
expiresAt: Chronos::now()->subDay()->startOfDay(),
));
$authorApiKey = $this->buildApiKey('author_api_key', true); $authorApiKey = $this->buildApiKey('author_api_key', enabled: true);
$authorApiKey->registerRole(RoleDefinition::forAuthoredShortUrls()); $authorApiKey->registerRole(RoleDefinition::forAuthoredShortUrls());
$manager->persist($authorApiKey); $manager->persist($authorApiKey);
$this->addReference('author_api_key', $authorApiKey); $this->addReference('author_api_key', $authorApiKey);
/** @var Domain $exampleDomain */ /** @var Domain $exampleDomain */
$exampleDomain = $this->getReference('example_domain'); $exampleDomain = $this->getReference('example_domain');
$domainApiKey = $this->buildApiKey('domain_api_key', true); $domainApiKey = $this->buildApiKey('domain_api_key', enabled: true);
$domainApiKey->registerRole(RoleDefinition::forDomain($exampleDomain)); $domainApiKey->registerRole(RoleDefinition::forDomain($exampleDomain));
$manager->persist($domainApiKey); $manager->persist($domainApiKey);
$authorApiKey = $this->buildApiKey('no_orphans_api_key', enabled: true);
$authorApiKey->registerRole(RoleDefinition::forNoOrphanVisits());
$manager->persist($authorApiKey);
$manager->flush(); $manager->flush();
} }