test: add WatchlistUpdateProcessor tests

This commit is contained in:
Maël Gangloff 2025-10-15 18:55:39 +02:00
parent 7a3d64256e
commit a193338664
No known key found for this signature in database
GPG Key ID: 11FDC81C24A7F629
5 changed files with 92 additions and 37 deletions

View File

@ -3,3 +3,9 @@ KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st' APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999 SYMFONY_DEPRECATIONS_HELPER=999999
DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/postgres?serverVersion=16&charset=utf8" DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/postgres?serverVersion=16&charset=utf8"
# FEATURES
LIMITED_FEATURES=true
LIMIT_MAX_WATCHLIST=10
LIMIT_MAX_WATCHLIST_DOMAINS=10
LIMIT_MAX_WATCHLIST_WEBHOOKS=10

View File

@ -90,7 +90,7 @@ use Symfony\Component\Uid\Uuid;
), ),
new Put( new Put(
normalizationContext: ['groups' => 'watchlist:item'], normalizationContext: ['groups' => 'watchlist:item'],
denormalizationContext: ['groups' => ['watchlist:create', 'watchlist:token']], denormalizationContext: ['groups' => ['watchlist:update']],
security: 'object.user == user', security: 'object.user == user',
processor: WatchListUpdateProcessor::class, processor: WatchListUpdateProcessor::class,
), ),
@ -163,7 +163,7 @@ class WatchList
#[ORM\JoinTable(name: 'watch_lists_domains', #[ORM\JoinTable(name: 'watch_lists_domains',
joinColumns: [new ORM\JoinColumn(name: 'watch_list_token', referencedColumnName: 'token', onDelete: 'CASCADE')], joinColumns: [new ORM\JoinColumn(name: 'watch_list_token', referencedColumnName: 'token', onDelete: 'CASCADE')],
inverseJoinColumns: [new ORM\JoinColumn(name: 'domain_ldh_name', referencedColumnName: 'ldh_name', onDelete: 'CASCADE')])] inverseJoinColumns: [new ORM\JoinColumn(name: 'domain_ldh_name', referencedColumnName: 'ldh_name', onDelete: 'CASCADE')])]
#[Groups(['watchlist:create', 'watchlist:list', 'watchlist:item'])] #[Groups(['watchlist:create', 'watchlist:list', 'watchlist:item', 'watchlist:update'])]
private Collection $domains; private Collection $domains;
/** /**
@ -175,11 +175,11 @@ class WatchList
private Collection $watchListTriggers; private Collection $watchListTriggers;
#[ORM\ManyToOne(inversedBy: 'watchLists')] #[ORM\ManyToOne(inversedBy: 'watchLists')]
#[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create'])] #[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])]
private ?Connector $connector = null; private ?Connector $connector = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
#[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create'])] #[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])]
private ?string $name = null; private ?string $name = null;
#[ORM\Column] #[ORM\Column]
@ -188,7 +188,7 @@ class WatchList
#[SerializedName('dsn')] #[SerializedName('dsn')]
#[ORM\Column(type: Types::SIMPLE_ARRAY, nullable: true)] #[ORM\Column(type: Types::SIMPLE_ARRAY, nullable: true)]
#[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create'])] #[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])]
private ?array $webhookDsn = null; private ?array $webhookDsn = null;
public function __construct() public function __construct()

View File

@ -45,7 +45,7 @@ readonly class WatchListUpdateProcessor implements ProcessorInterface
{ {
/** @var User $user */ /** @var User $user */
$user = $this->security->getUser(); $user = $this->security->getUser();
$data->setUser($user); $data->setUser($user)->setToken($uriVariables['token'] ?? $data->getToken());
if ($this->parameterBag->get('limited_features')) { if ($this->parameterBag->get('limited_features')) {
if ($data->getDomains()->count() > (int) $this->parameterBag->get('limit_max_watchlist_domains')) { if ($data->getDomains()->count() > (int) $this->parameterBag->get('limit_max_watchlist_domains')) {

View File

@ -3,11 +3,10 @@
namespace App\Tests\Controller; namespace App\Tests\Controller;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use ApiPlatform\Symfony\Bundle\Test\Client;
use App\Entity\WatchList; use App\Entity\WatchList;
use App\Factory\UserFactory;
use App\Tests\AuthenticatedUserTrait; use App\Tests\AuthenticatedUserTrait;
use App\Tests\Service\RDAPServiceTest; use App\Tests\Service\RDAPServiceTest;
use App\Tests\State\WatchListUpdateProcessorTest;
use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\Attributes\DependsExternal;
use Zenstruck\Foundry\Test\Factories; use Zenstruck\Foundry\Test\Factories;
@ -19,7 +18,7 @@ final class WatchlistControllerTest extends ApiTestCase
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')] #[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testGetWatchlistCollection(): void public function testGetWatchlistCollection(): void
{ {
$client = $this->createUserAndWatchlist(); $client = WatchListUpdateProcessorTest::createUserAndWatchlist();
$response = $client->request('GET', '/api/watchlists'); $response = $client->request('GET', '/api/watchlists');
@ -31,20 +30,13 @@ final class WatchlistControllerTest extends ApiTestCase
$this->assertCount(1, $data['hydra:member']); $this->assertCount(1, $data['hydra:member']);
} }
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testCreateWatchlist(): void
{
$this->createUserAndWatchlist();
$this->assertResponseIsSuccessful();
$this->assertResponseStatusCodeSame(201);
$this->assertMatchesResourceItemJsonSchema(WatchList::class);
}
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')] #[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testGetTrackedDomains() public function testGetTrackedDomains()
{ {
$client = $this->createUserAndWatchlist(); $client = WatchListUpdateProcessorTest::createUserAndWatchlist();
$client->getContainer()->get('doctrine')->getManager()->clear();
sleep(2);
$response = $client->request('GET', '/api/tracked'); $response = $client->request('GET', '/api/tracked');
$this->assertResponseIsSuccessful(); $this->assertResponseIsSuccessful();
@ -56,7 +48,7 @@ final class WatchlistControllerTest extends ApiTestCase
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')] #[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testGetWatchlistFeeds() public function testGetWatchlistFeeds()
{ {
$client = $this->createUserAndWatchlist(); $client = WatchListUpdateProcessorTest::createUserAndWatchlist();
$response = $client->request('GET', '/api/watchlists'); $response = $client->request('GET', '/api/watchlists');
$token = $response->toArray()['hydra:member'][0]['token']; $token = $response->toArray()['hydra:member'][0]['token'];
@ -73,21 +65,4 @@ final class WatchlistControllerTest extends ApiTestCase
$this->assertResponseIsSuccessful(); $this->assertResponseIsSuccessful();
$this->assertResponseHeaderSame('content-type', 'application/atom+xml; charset=utf-8'); $this->assertResponseHeaderSame('content-type', 'application/atom+xml; charset=utf-8');
} }
private function createUserAndWatchlist(): Client
{
$client = self::createClientWithCredentials(self::getToken(UserFactory::createOne()));
$client->request('POST', '/api/watchlists', ['json' => [
'domains' => ['/api/domains/iana.org'],
'name' => 'My Watchlist',
'triggers' => [
['action' => 'email', 'event' => 'last changed'],
['action' => 'email', 'event' => 'transfer'],
['action' => 'email', 'event' => 'expiration'],
['action' => 'email', 'event' => 'deletion'],
],
]]);
return $client;
}
} }

View File

@ -0,0 +1,74 @@
<?php
namespace App\Tests\State;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use ApiPlatform\Symfony\Bundle\Test\Client;
use App\Entity\WatchList;
use App\Factory\UserFactory;
use App\Tests\AuthenticatedUserTrait;
use App\Tests\Service\RDAPServiceTest;
use PHPUnit\Framework\Attributes\DependsExternal;
use Zenstruck\Foundry\Test\Factories;
final class WatchListUpdateProcessorTest extends ApiTestCase
{
use Factories;
use AuthenticatedUserTrait;
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testCreateWatchlist(): void
{
self::createUserAndWatchlist();
$this->assertResponseIsSuccessful();
$this->assertResponseStatusCodeSame(201);
$this->assertMatchesResourceItemJsonSchema(WatchList::class);
}
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testCreateTwoWatchlistWithSameDomains(): void
{
$client = self::createClientWithCredentials(self::getToken(UserFactory::createOne()));
self::createUserAndWatchlist($client);
self::createUserAndWatchlist($client);
$this->assertResponseStatusCodeSame(403);
}
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testUpdateWatchlist(): void
{
$client = self::createUserAndWatchlist();
$response = $client->request('GET', '/api/watchlists');
$token = $response->toArray()['hydra:member'][0]['token'];
$response = $client->request('PUT', '/api/watchlists/'.$token, ['json' => [
'domains' => ['/api/domains/iana.org', '/api/domains/example.com'],
'name' => 'My modified Watchlist',
'triggers' => [
['action' => 'email', 'event' => 'last changed'],
],
]]);
$this->assertResponseIsSuccessful();
$this->assertMatchesResourceItemJsonSchema(WatchList::class);
$data = $response->toArray();
$this->assertCount(2, $data['domains']);
$this->assertCount(1, $data['triggers']);
}
public static function createUserAndWatchlist(?Client $client = null): Client
{
$client = $client ?? self::createClientWithCredentials(self::getToken(UserFactory::createOne()));
$client->request('POST', '/api/watchlists', ['json' => [
'domains' => ['/api/domains/example.com'],
'name' => 'My Watchlist',
'triggers' => [
['action' => 'email', 'event' => 'last changed'],
['action' => 'email', 'event' => 'transfer'],
['action' => 'email', 'event' => 'expiration'],
['action' => 'email', 'event' => 'deletion'],
],
]]);
return $client;
}
}