85 lines
2.8 KiB
PHP
85 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Entity;
|
|
|
|
use App\Entity\User;
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
|
|
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory;
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher;
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
use Symfony\Component\Uid\UuidV7;
|
|
|
|
final class UserPersistenceTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
private UserPasswordHasherInterface $hasher;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
// Build the hasher directly — avoids the chicken-and-egg where
|
|
// UserPasswordHasherInterface gets compiled away before User exists.
|
|
$factory = new PasswordHasherFactory([
|
|
User::class => new NativePasswordHasher(cost: 4),
|
|
]);
|
|
$this->hasher = new UserPasswordHasher($factory);
|
|
}
|
|
|
|
public function testItPersistsAUserWithUuidIdentifierAndHashedPassword(): void
|
|
{
|
|
$hasher = $this->hasher;
|
|
|
|
$user = new User();
|
|
$user->setEmail('alice@example.com');
|
|
$user->setPassword($hasher->hashPassword($user, 'secret123'));
|
|
|
|
$this->em->persist($user);
|
|
$this->em->flush();
|
|
|
|
$id = $user->getId();
|
|
self::assertInstanceOf(UuidV7::class, $id);
|
|
|
|
$this->em->clear();
|
|
|
|
$reloaded = $this->em->find(User::class, $id);
|
|
|
|
self::assertNotNull($reloaded, 'User must be retrievable from the database after an identity-map clear.');
|
|
self::assertInstanceOf(User::class, $reloaded);
|
|
self::assertSame('alice@example.com', $reloaded->getEmail());
|
|
self::assertTrue(
|
|
$hasher->isPasswordValid($reloaded, 'secret123'),
|
|
'Stored password hash must verify against the original plain-text password.',
|
|
);
|
|
self::assertStringNotContainsString(
|
|
'secret123',
|
|
$reloaded->getPassword(),
|
|
'Plain-text password must never be stored.',
|
|
);
|
|
}
|
|
|
|
public function testEmailMustBeUnique(): void
|
|
{
|
|
$hasher = $this->hasher;
|
|
|
|
$first = new User();
|
|
$first->setEmail('bob@example.com');
|
|
$first->setPassword($hasher->hashPassword($first, 'pass1'));
|
|
$this->em->persist($first);
|
|
$this->em->flush();
|
|
|
|
$this->em->clear();
|
|
|
|
$second = new User();
|
|
$second->setEmail('bob@example.com');
|
|
$second->setPassword($hasher->hashPassword($second, 'pass2'));
|
|
$this->em->persist($second);
|
|
|
|
$this->expectException(UniqueConstraintViolationException::class);
|
|
$this->em->flush();
|
|
}
|
|
}
|