test: add some KernelTestCase

This commit is contained in:
Maël Gangloff
2025-10-14 17:40:48 +02:00
parent 39262814ce
commit 97ea1ab33a
10 changed files with 255 additions and 10 deletions

View File

@@ -21,6 +21,7 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class RegistrationController extends AbstractController
{
@@ -33,6 +34,7 @@ class RegistrationController extends AbstractController
private readonly SerializerInterface $serializer,
private readonly LoggerInterface $logger,
private readonly KernelInterface $kernel,
private readonly ValidatorInterface $validator,
) {
}
@@ -64,14 +66,16 @@ class RegistrationController extends AbstractController
}
$user = $this->serializer->deserialize($request->getContent(), User::class, 'json', ['groups' => 'user:register']);
if (null === $user->getEmail() || null === $user->getPassword()) {
throw new BadRequestHttpException('Bad request');
$violations = $this->validator->validate($user);
if ($violations->count() > 0) {
throw new BadRequestHttpException($violations->get(0));
}
$user->setPassword(
$userPasswordHasher->hashPassword(
$user,
$user->getPassword()
$user->getPlainPassword()
)
)->setCreatedAt(new \DateTimeImmutable());

View File

@@ -2,6 +2,7 @@
namespace App\Entity;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use App\Config\EventAction;
@@ -75,6 +76,11 @@ class Domain
*/
#[ORM\OneToMany(targetEntity: DomainEvent::class, mappedBy: 'domain', cascade: ['persist'], orphanRemoval: true)]
#[Groups(['domain:item', 'domain:list', 'watchlist:list'])]
#[ApiProperty(
openapiContext: [
'type' => 'array',
]
)]
private Collection $events;
/**

View File

@@ -2,6 +2,7 @@
namespace App\Entity;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\EntityRepository;
use Doctrine\Common\Collections\ArrayCollection;
@@ -77,7 +78,13 @@ class Entity
#[Groups(['entity:item', 'domain:item'])]
private Collection $events;
#[ORM\Column]
#[ORM\Column(type: 'json')]
#[ApiProperty(
openapiContext: [
'type' => 'array',
'items' => ['type' => 'array'],
]
)]
#[Groups(['entity:item', 'domain:item'])]
private array $jCard = [];

View File

@@ -15,6 +15,8 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Serializer\Attribute\SerializedName;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
@@ -46,6 +48,8 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column(length: 180)]
#[Groups(['user:list', 'user:register'])]
#[Assert\Email]
#[Assert\NotBlank]
private ?string $email = null;
/**
@@ -59,7 +63,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
* @var string|null The hashed password
*/
#[ORM\Column(nullable: true)]
#[Groups(['user:register'])]
private ?string $password = null;
/**
@@ -80,6 +83,10 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $verifiedAt = null;
#[Assert\PasswordStrength]
#[Assert\NotBlank]
#[SerializedName('password')]
#[Groups(['user:register'])]
private ?string $plainPassword = null;
public function __construct()

View File

@@ -36,7 +36,7 @@ final class UserFactory extends PersistentObjectFactory
protected function defaults(): array|callable
{
$createdAt = \DateTimeImmutable::createFromMutable(self::faker()->dateTime());
$plainPassword = self::faker()->password(8, 20);
$plainPassword = self::faker()->password(16, 20);
return [
'createdAt' => $createdAt,

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Tests\Controller;
use App\Entity\Connector;
use App\Factory\UserFactory;
use App\Tests\AbstractTest;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
final class ConnectorControllerTest extends AbstractTest
{
use ResetDatabase;
use Factories;
public function testGetConnectorCollection(): void
{
$testUser = UserFactory::createOne();
$client = ConnectorControllerTest::createClientWithCredentials(ConnectorControllerTest::getToken($testUser));
$response = $client->request('GET', '/api/connectors');
$this->assertResponseIsSuccessful();
$this->assertMatchesResourceCollectionJsonSchema(Connector::class);
$this->assertCount(0, $response->toArray()['hydra:member']);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Tests\Controller;
use App\Entity\Domain;
use App\Factory\UserFactory;
use App\Tests\AbstractTest;
use App\Tests\Service\RDAPServiceTest;
use PHPUnit\Framework\Attributes\DependsExternal;
use Zenstruck\Foundry\Test\Factories;
final class DomainRefreshControllerTest extends AbstractTest
{
use Factories;
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testRegisterDomain(): void
{
$testUser = UserFactory::createOne();
$client = DomainRefreshControllerTest::createClientWithCredentials(DomainRefreshControllerTest::getToken($testUser));
$client->request('GET', '/api/domains/example.com');
$this->assertResponseIsSuccessful();
$this->assertMatchesResourceItemJsonSchema(Domain::class);
}
}

View File

@@ -1,14 +1,15 @@
<?php
namespace App\Tests;
namespace App\Tests\Controller;
use App\Entity\User;
use App\Factory\UserFactory;
use App\Tests\AbstractTest;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
final class UserTest extends AbstractTest
final class MeControllerTest extends AbstractTest
{
use ResetDatabase;
use Factories;
@@ -19,9 +20,8 @@ final class UserTest extends AbstractTest
public function testGetMyProfile(): void
{
$testUser = UserFactory::createOne();
$client = UserTest::createClientWithCredentials(UserTest::getToken($testUser));
$client = MeControllerTest::createClientWithCredentials(MeControllerTest::getToken($testUser));
$client->loginUser($testUser);
$client->request('GET', '/api/me');
$this->assertResponseIsSuccessful();

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Tests\Controller;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Factory\UserFactory;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
final class RegistrationControllerTest extends ApiTestCase
{
use ResetDatabase;
use Factories;
protected static ContainerInterface $container;
protected static EntityManagerInterface $entityManager;
protected function setUp(): void
{
RegistrationControllerTest::$container = RegistrationControllerTest::getContainer();
RegistrationControllerTest::$entityManager = RegistrationControllerTest::$container->get(EntityManagerInterface::class);
}
public function testRegister(): void
{
$testUser = UserFactory::createOne();
RegistrationControllerTest::$entityManager->remove($testUser);
RegistrationControllerTest::$entityManager->flush();
$client = $this->createClient();
$client->request('POST', '/api/register', [
'json' => [
'email' => $testUser->getEmail(),
'password' => $testUser->getPlainPassword(),
],
]);
$this->assertResponseIsSuccessful();
$this->assertResponseStatusCodeSame(201);
}
public function testRegisterEmptyEmail(): void
{
$client = $this->createClient();
$client->request('POST', '/api/register', [
'json' => [
'email' => '',
'password' => 'MySuperPassword123',
],
]);
$this->assertResponseStatusCodeSame(400);
}
public function testRegisterEmptyPassword(): void
{
$client = $this->createClient();
$client->request('POST', '/api/register', [
'json' => [
'email' => 'test@domainwatchdog.eu',
'password' => '',
],
]);
$this->assertResponseStatusCodeSame(400);
}
public function testRegisterWeakPassword(): void
{
$client = $this->createClient();
$client->request('POST', '/api/register', [
'json' => [
'email' => 'test@domainwatchdog.eu',
'password' => '123',
],
]);
$this->assertResponseStatusCodeSame(400);
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace App\Tests\Controller;
use ApiPlatform\Symfony\Bundle\Test\Client;
use App\Entity\WatchList;
use App\Factory\UserFactory;
use App\Tests\AbstractTest;
use App\Tests\Service\RDAPServiceTest;
use PHPUnit\Framework\Attributes\DependsExternal;
use Zenstruck\Foundry\Test\Factories;
final class WatchlistControllerTest extends AbstractTest
{
use Factories;
public function testGetWatchlistCollection(): void
{
$client = $this->createUserAndWatchlist();
$response = $client->request('GET', '/api/watchlists');
$this->assertResponseIsSuccessful();
$this->assertMatchesResourceCollectionJsonSchema(WatchList::class);
$data = $response->toArray();
$this->assertArrayHasKey('hydra:member', $data);
$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')]
public function testGetTrackedDomains()
{
$client = $this->createUserAndWatchlist();
$response = $client->request('GET', '/api/tracked');
$this->assertResponseIsSuccessful();
$data = $response->toArray();
$this->assertArrayHasKey('hydra:member', $data);
$this->assertCount(1, $data['hydra:member']);
}
#[DependsExternal(RDAPServiceTest::class, 'testUpdateRdapServers')]
public function testGetWatchlistFeeds()
{
$client = $this->createUserAndWatchlist();
$response = $client->request('GET', '/api/watchlists');
$token = $response->toArray()['hydra:member'][0]['token'];
$client->request('GET', '/api/watchlists/'.$token.'/calendar');
$this->assertResponseIsSuccessful();
$this->assertResponseHeaderSame('content-type', 'text/calendar; charset=utf-8');
$client->request('GET', '/api/watchlists/'.$token.'/rss/events');
$this->assertResponseIsSuccessful();
$this->assertResponseHeaderSame('content-type', 'application/atom+xml; charset=utf-8');
$client->request('GET', '/api/watchlists/'.$token.'/rss/status');
$this->assertResponseIsSuccessful();
$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/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;
}
}