test email signup flow

This commit is contained in:
Yisroel Baum 2026-08-03 20:22:50 +03:00
parent a95903ed05
commit 9945163a2f
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
12 changed files with 631 additions and 0 deletions

View file

@ -0,0 +1,135 @@
<?php
namespace Tests\Unit\User\UseCases;
use App\Email\EmailConfirmationToken\UseCases\CreateEmailConfirmationToken;
use App\Exceptions\BadRequestException;
use App\Shared\ValueObject\EmailAddress;
use App\User\CreateUserDto;
use App\User\UseCases\SignupUser\SignupUser;
use App\User\UseCases\SignupUser\SignupUserRequest;
use DateTimeImmutable;
use DateTimeZone;
use DomainException;
use PHPUnit\Framework\TestCase;
use Tests\Fakes\FakeClock;
use Tests\Fakes\FakeEmailConfirmationTokenRepository;
use Tests\Fakes\FakeEmailer;
use Tests\Fakes\FakeEmailFactory;
use Tests\Fakes\FakeTokenGenerator;
use Tests\Fakes\FakeUserRepository;
class SignupUserTest extends TestCase
{
private FakeUserRepository $userRepository;
private FakeEmailConfirmationTokenRepository $tokenRepository;
private FakeEmailer $emailer;
private FakeEmailFactory $emailFactory;
private SignupUser $signupUser;
protected function setUp(): void
{
$this->userRepository = new FakeUserRepository;
$this->tokenRepository = new FakeEmailConfirmationTokenRepository;
$this->emailer = new FakeEmailer;
$this->emailFactory = new FakeEmailFactory;
$createToken = new CreateEmailConfirmationToken(
$this->tokenRepository,
new FakeClock(new DateTimeImmutable(
'2026-08-03T12:00:00',
new DateTimeZone('UTC'),
)),
new FakeTokenGenerator(['first-token', 'second-token']),
);
$this->signupUser = new SignupUser(
$this->userRepository,
$createToken,
$this->emailer,
$this->emailFactory,
);
}
public function test_it_creates_a_pending_user_token_and_email(): void
{
$this->signupUser->execute(new SignupUserRequest(
email: ' Founder@EXAMPLE.COM ',
));
$user = $this->userRepository->findByEmail(
new EmailAddress('Founder@example.com'),
);
$this->assertNotNull($user);
$this->assertNull($user->getPasswordHash());
$token = $this->tokenRepository->findByUser($user);
$this->assertNotNull($token);
$this->assertSame('first-token', $token->getToken());
$this->assertSame(
'2026-08-03T12:10:00+00:00',
$token->getAvailableTo()->format('c'),
);
$this->assertSame(1, $this->emailer->getSendCount());
$this->assertSame(
'Founder@example.com',
$this->emailer->getLastRecipient()?->value(),
);
$this->assertSame(
'Confirm your Attainly email',
$this->emailer->getLastSubject(),
);
$this->assertSame('first-token', $this->emailFactory->getLastToken());
}
public function test_it_replaces_the_token_for_a_pending_user(): void
{
$request = new SignupUserRequest(email: 'user@example.com');
$this->signupUser->execute($request);
$this->signupUser->execute($request);
$user = $this->userRepository->findByEmail(
new EmailAddress('user@example.com'),
);
$this->assertNotNull($user);
$this->assertSame(
'second-token',
$this->tokenRepository->findByUser($user)?->getToken(),
);
$this->assertSame(2, $this->emailer->getSendCount());
}
public function test_it_rejects_an_existing_confirmed_account(): void
{
$this->userRepository->create(new CreateUserDto(
email: new EmailAddress('user@example.com'),
passwordHash: 'hashed-password',
));
$this->expectException(DomainException::class);
$this->expectExceptionMessage('user@example.com already has an account');
$this->signupUser->execute(new SignupUserRequest(
email: 'user@example.com',
));
}
public function test_it_rejects_a_missing_email(): void
{
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('email is required');
$this->signupUser->execute(new SignupUserRequest(email: null));
}
public function test_it_rejects_an_invalid_email(): void
{
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('email must be valid');
$this->signupUser->execute(new SignupUserRequest(
email: 'not-an-email',
));
}
}