From 11f2823a308da5a207878c0b2faf9ef0afec65d5 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 6 May 2026 22:08:25 +0300 Subject: [PATCH] test SignupUser two-step confirm flow --- .../Unit/User/UseCases/SignupUserTest.php | 107 ++++++++---------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/backend/tests/Unit/User/UseCases/SignupUserTest.php b/backend/tests/Unit/User/UseCases/SignupUserTest.php index 9babb7e..501eaf2 100644 --- a/backend/tests/Unit/User/UseCases/SignupUserTest.php +++ b/backend/tests/Unit/User/UseCases/SignupUserTest.php @@ -8,8 +8,13 @@ use App\User\CreateUserDto; use App\User\UseCases\SignupUser\SignupUser; use App\User\UseCases\SignupUser\SignupUserRequest; use App\User\User; +use DateTimeImmutable; +use DateTimeZone; use DomainException; -use Tests\Fakes\FakePasswordHasher; +use Tests\Fakes\FakeClock; +use Tests\Fakes\FakeEmailConfirmationTokenRepository; +use Tests\Fakes\FakeEmailer; +use Tests\Fakes\FakeEmailFactory; use Tests\Fakes\FakeUserRepository; use Tests\TestCase; @@ -17,17 +22,38 @@ class SignupUserTest extends TestCase { private FakeUserRepository $userRepo; - private FakePasswordHasher $hasher; + private FakeEmailConfirmationTokenRepository $tokenRepo; + + private FakeEmailer $emailer; + + private FakeEmailFactory $emailFactory; + + private FakeClock $clock; + + private DateTimeImmutable $now; private SignupUser $useCase; protected function setUp(): void { + $this->now = new DateTimeImmutable( + '2026-05-06T12:00:00', + new DateTimeZone('UTC'), + ); $this->userRepo = new FakeUserRepository; - $this->hasher = new FakePasswordHasher; + $this->tokenRepo = new FakeEmailConfirmationTokenRepository( + $this->userRepo, + ); + $this->emailer = new FakeEmailer; + $this->emailFactory = new FakeEmailFactory; + $this->clock = new FakeClock($this->now); $this->useCase = new SignupUser( $this->userRepo, - $this->hasher, + $this->tokenRepo, + $this->emailer, + $this->emailFactory, + $this->clock, + 'noreply@tide.test', ); } @@ -37,7 +63,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: null, displayName: 'alice', - password: 'longenoughpassword', )); } @@ -47,7 +72,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: '', displayName: 'alice', - password: 'longenoughpassword', )); } @@ -57,7 +81,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: 'not-an-email', displayName: 'alice', - password: 'longenoughpassword', )); } @@ -67,7 +90,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: 'user@example.com', displayName: null, - password: 'longenoughpassword', )); } @@ -77,7 +99,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: 'user@example.com', displayName: 'ab', - password: 'longenoughpassword', )); } @@ -87,27 +108,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: 'user@example.com', displayName: 'Has Spaces', - password: 'longenoughpassword', - )); - } - - public function test_null_password_throws_bad_request(): void - { - $this->expectException(BadRequestException::class); - $this->useCase->execute(new SignupUserRequest( - email: 'user@example.com', - displayName: 'alice', - password: null, - )); - } - - public function test_short_password_throws_bad_request(): void - { - $this->expectException(BadRequestException::class); - $this->useCase->execute(new SignupUserRequest( - email: 'user@example.com', - displayName: 'alice', - password: 'short', )); } @@ -116,7 +116,7 @@ class SignupUserTest extends TestCase $this->userRepo->create(new CreateUserDto( email: new EmailAddress('user@example.com'), displayName: 'first', - passwordHash: $this->hasher->hash('original-password'), + passwordHash: '', isAdmin: false, emailConfirmedAt: null, )); @@ -125,7 +125,6 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: 'user@example.com', displayName: 'second', - password: 'second-attempt-password', )); } @@ -134,7 +133,7 @@ class SignupUserTest extends TestCase $this->userRepo->create(new CreateUserDto( email: new EmailAddress('first@example.com'), displayName: 'taken', - passwordHash: $this->hasher->hash('original-password'), + passwordHash: '', isAdmin: false, emailConfirmedAt: null, )); @@ -143,55 +142,48 @@ class SignupUserTest extends TestCase $this->useCase->execute(new SignupUserRequest( email: 'second@example.com', displayName: 'taken', - password: 'second-attempt-password', )); } - public function test_valid_signup_returns_user_with_hashed_password(): void + public function test_valid_signup_creates_unconfirmed_user(): void { $created = $this->useCase->execute(new SignupUserRequest( email: 'new@example.com', displayName: 'newuser', - password: 'longenoughpassword', )); $this->assertInstanceOf(User::class, $created); $this->assertSame('new@example.com', $created->getEmail()->value()); $this->assertSame('newuser', $created->getDisplayName()); - $this->assertSame( - $this->hasher->hash('longenoughpassword'), - $created->getPasswordHash(), - ); + $this->assertSame('', $created->getPasswordHash()); $this->assertFalse($created->isAdmin()); $this->assertFalse($created->isEmailConfirmed()); } - public function test_created_user_is_findable_by_email(): void + public function test_valid_signup_creates_confirmation_token(): void { $created = $this->useCase->execute(new SignupUserRequest( - email: 'lookup@example.com', - displayName: 'lookup', - password: 'longenoughpassword', + email: 'new@example.com', + displayName: 'newuser', )); - $found = $this->userRepo->findByEmail( - new EmailAddress('lookup@example.com') - ); - $this->assertNotNull($found); - $this->assertSame($created->getId(), $found->getId()); + $token = $this->tokenRepo->findByUser($created); + $this->assertNotNull($token); + $this->assertGreaterThan($this->now, $token->getAvailableTo()); } - public function test_created_user_is_findable_by_display_name(): void + public function test_valid_signup_sends_confirmation_email(): void { - $created = $this->useCase->execute(new SignupUserRequest( - email: 'lookup@example.com', - displayName: 'lookupbyname', - password: 'longenoughpassword', + $this->useCase->execute(new SignupUserRequest( + email: 'new@example.com', + displayName: 'newuser', )); - $found = $this->userRepo->findByDisplayName('lookupbyname'); - $this->assertNotNull($found); - $this->assertSame($created->getId(), $found->getId()); + $this->assertSame(1, $this->emailer->getNumberOfEmailsSent()); + $sent = $this->emailer->getSentEmails()[0]; + $this->assertSame('noreply@tide.test', $sent['from']); + $this->assertSame('new@example.com', $sent['to']); + $this->assertStringContainsString('confirm:', $sent['body']); } public function test_signup_normalizes_email_domain(): void @@ -199,7 +191,6 @@ class SignupUserTest extends TestCase $created = $this->useCase->execute(new SignupUserRequest( email: 'Mixed@CASE.com', displayName: 'mixed', - password: 'longenoughpassword', )); $this->assertSame('Mixed@case.com', $created->getEmail()->value());