test ConfirmUserEmail use case

This commit is contained in:
Yisroel Baum 2026-05-06 22:07:08 +03:00
parent 2890781a56
commit 60308988f7
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9

View file

@ -0,0 +1,182 @@
<?php
namespace Tests\Unit\User\UseCases;
use App\Email\EmailConfirmationToken\CreateEmailConfirmationTokenDto;
use App\Exceptions\BadRequestException;
use App\Shared\ValueObject\EmailAddress;
use App\User\CreateUserDto;
use App\User\UseCases\ConfirmUserEmail\ConfirmUserEmail;
use App\User\UseCases\ConfirmUserEmail\ConfirmUserEmailRequest;
use App\User\User;
use DateTimeImmutable;
use DateTimeZone;
use DomainException;
use Tests\Fakes\FakeClock;
use Tests\Fakes\FakeEmailConfirmationTokenRepository;
use Tests\Fakes\FakePasswordHasher;
use Tests\Fakes\FakeUserRepository;
use Tests\TestCase;
class ConfirmUserEmailTest extends TestCase
{
private FakeUserRepository $userRepo;
private FakeEmailConfirmationTokenRepository $tokenRepo;
private FakePasswordHasher $hasher;
private FakeClock $clock;
private ConfirmUserEmail $useCase;
private DateTimeImmutable $now;
protected function setUp(): void
{
$this->now = new DateTimeImmutable(
'2026-05-06T12:00:00',
new DateTimeZone('UTC'),
);
$this->userRepo = new FakeUserRepository;
$this->tokenRepo = new FakeEmailConfirmationTokenRepository(
$this->userRepo,
);
$this->hasher = new FakePasswordHasher;
$this->clock = new FakeClock($this->now);
$this->useCase = new ConfirmUserEmail(
$this->userRepo,
$this->tokenRepo,
$this->hasher,
$this->clock,
);
}
private function seedUserAndToken(DateTimeImmutable $availableTo): User
{
$user = $this->userRepo->create(new CreateUserDto(
email: new EmailAddress('user@example.com'),
displayName: 'user',
passwordHash: '',
isAdmin: false,
emailConfirmedAt: null,
));
$this->tokenRepo->create(new CreateEmailConfirmationTokenDto(
user: $user,
availableTo: $availableTo,
));
return $user;
}
private function tokenStringForUser(User $user): string
{
$token = $this->tokenRepo->findByUser($user);
$this->assertNotNull($token);
return $token->getToken();
}
public function test_null_token_throws_bad_request(): void
{
$this->expectException(BadRequestException::class);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: null,
password: 'longenoughpassword',
));
}
public function test_null_password_throws_bad_request(): void
{
$this->expectException(BadRequestException::class);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: 'sometoken',
password: null,
));
}
public function test_short_password_throws_bad_request(): void
{
$this->expectException(BadRequestException::class);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: 'sometoken',
password: 'short',
));
}
public function test_unknown_token_throws_domain_exception(): void
{
$this->expectException(DomainException::class);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: 'no-such-token',
password: 'longenoughpassword',
));
}
public function test_expired_token_throws_domain_exception(): void
{
$user = $this->seedUserAndToken(
$this->now->modify('-1 minute'),
);
$tokenStr = $this->tokenStringForUser($user);
$this->expectException(DomainException::class);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: $tokenStr,
password: 'longenoughpassword',
));
}
public function test_valid_confirmation_sets_password_hash(): void
{
$user = $this->seedUserAndToken(
$this->now->modify('+1 day'),
);
$tokenStr = $this->tokenStringForUser($user);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: $tokenStr,
password: 'longenoughpassword',
));
$reloaded = $this->userRepo->find($user->getId());
$this->assertNotNull($reloaded);
$this->assertSame(
$this->hasher->hash('longenoughpassword'),
$reloaded->getPasswordHash(),
);
}
public function test_valid_confirmation_marks_email_confirmed(): void
{
$user = $this->seedUserAndToken(
$this->now->modify('+1 day'),
);
$tokenStr = $this->tokenStringForUser($user);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: $tokenStr,
password: 'longenoughpassword',
));
$reloaded = $this->userRepo->find($user->getId());
$this->assertNotNull($reloaded);
$this->assertTrue($reloaded->isEmailConfirmed());
$this->assertEquals($this->now, $reloaded->getEmailConfirmedAt());
}
public function test_valid_confirmation_consumes_token(): void
{
$user = $this->seedUserAndToken(
$this->now->modify('+1 day'),
);
$tokenStr = $this->tokenStringForUser($user);
$this->useCase->execute(new ConfirmUserEmailRequest(
token: $tokenStr,
password: 'longenoughpassword',
));
$this->assertNull($this->tokenRepo->findByToken($tokenStr));
}
}