From df1884357429514330556b6e333b816492411332 Mon Sep 17 00:00:00 2001 From: Vincent Hagen Date: Mon, 19 Sep 2022 00:31:12 +0200 Subject: [PATCH] Add tests for email verification --- .../Requests/SendVerificationEmailRequest.php | 7 +- app/Models/User.php | 12 +++ app/Services/Users/LaravelUserRegistrator.php | 5 +- database/factories/UserFactory.php | 9 ++- .../Api/UserEmailVerifyControllerTest.php | 75 +++++++++++++++++++ tests/Integration/RegistrationTest.php | 27 ++++++- .../Users/LaravelUserEmailVerifierTest.php | 70 +++++++++++++++++ .../Users/LaravelUserRegistratorTest.php | 59 +++++++++++++++ 8 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 tests/Integration/Http/Controllers/Api/UserEmailVerifyControllerTest.php create mode 100644 tests/Integration/Services/Users/LaravelUserEmailVerifierTest.php create mode 100644 tests/Integration/Services/Users/LaravelUserRegistratorTest.php diff --git a/app/Http/Requests/SendVerificationEmailRequest.php b/app/Http/Requests/SendVerificationEmailRequest.php index a850c3b..edb0749 100644 --- a/app/Http/Requests/SendVerificationEmailRequest.php +++ b/app/Http/Requests/SendVerificationEmailRequest.php @@ -33,6 +33,11 @@ public function rules(): array public function findUser(): User { - return $this->userRepository->findByEmail($this->validated('email')); + return $this->userRepository->findByEmail($this->email()); + } + + public function email(): string + { + return isset($this->validator) ? $this->validated('email') : $this->get('email'); } } diff --git a/app/Models/User.php b/app/Models/User.php index 1b70768..b571251 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,6 +4,8 @@ namespace App\Models; +use App\Domain\Users\Entities\User as UserVO; +use App\Domain\Users\ValueObjects\OriginUrl; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -74,4 +76,14 @@ public function equipment() { return $this->hasOne(Equipment::class); } + + public function toValueObject(): UserVO + { + return new UserVO( + id: $this->id, + name: $this->name, + email: $this->email, + origin: OriginUrl::fromString($this->origin), + ); + } } diff --git a/app/Services/Users/LaravelUserRegistrator.php b/app/Services/Users/LaravelUserRegistrator.php index f8d6b3f..afe33f9 100644 --- a/app/Services/Users/LaravelUserRegistrator.php +++ b/app/Services/Users/LaravelUserRegistrator.php @@ -15,15 +15,16 @@ final class LaravelUserRegistrator implements UserRegistrator { public function register(RegisterUser $registerUser): User { + $origin = OriginUrl::fromString($registerUser->origin); $user = UserModel::create([ 'name' => $registerUser->name, 'email' => $registerUser->email, 'password' => $registerUser->password, - 'origin' => $registerUser->origin, + 'origin' => $origin->toString(), ]); event(new Registered($user)); - return new User($user->id, $user->name, $user->email, OriginUrl::fromString($user->origin)); + return new User($user->id, $user->name, $user->email, $origin); } } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index d28fdce..5c3f3fa 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -27,10 +27,15 @@ public function definition() public function filled(): self { - $this->has( + return $this->has( Dive::factory()->filled()->count($this->faker->numberBetween(1, 50)) ); + } - return $this; + public function notVerified(): self + { + return $this->state([ + 'email_verified_at' => null, + ]); } } diff --git a/tests/Integration/Http/Controllers/Api/UserEmailVerifyControllerTest.php b/tests/Integration/Http/Controllers/Api/UserEmailVerifyControllerTest.php new file mode 100644 index 0000000..59785c8 --- /dev/null +++ b/tests/Integration/Http/Controllers/Api/UserEmailVerifyControllerTest.php @@ -0,0 +1,75 @@ +userRepository = $this->createMock(UserRepository::class); + $this->emailVerifier = $this->createMock(UserEmailVerifier::class); + $this->subject = new UserEmailVerifyController($this->emailVerifier); + } + + public function testItVerifies(): void + { + /** @var User $model */ + $model = User::factory()->notVerified()->createOne(); + $user = $model->toValueObject(); + + $this->userRepository + ->expects($this->once()) + ->method('findById') + ->with(null) + ->willReturn($user); + + $this->emailVerifier + ->expects($this->once()) + ->method('verify') + ->with($this->equalTo($user)); + + $this->subject + ->verifyEmail(new EmailVerificationRequest($this->userRepository)) + ->isRedirect($user->getOrigin()->withMessage('account.verified')->toString()); + } + + public function testResendSends(): void + { + /** @var User $model */ + $model = User::factory()->notVerified()->createOne(); + + $this->userRepository + ->expects($this->once()) + ->method('findByEmail') + ->with($model->email) + ->willReturn($model->toValueObject()); + + $this->emailVerifier + ->expects($this->once()) + ->method('resend') + ->with($this->equalTo($model->toValueObject())); + + $this->subject->sendVerificationEmail( + new SendVerificationEmailRequest($this->userRepository, [], [ 'email' => $model->email ]) + ); + } +} diff --git a/tests/Integration/RegistrationTest.php b/tests/Integration/RegistrationTest.php index 8482ab8..3b2cc31 100644 --- a/tests/Integration/RegistrationTest.php +++ b/tests/Integration/RegistrationTest.php @@ -5,9 +5,11 @@ namespace Tests\Integration; use App\Models\User; +use Illuminate\Auth\Notifications\VerifyEmail; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Notification; use Tests\TestCase; final class RegistrationTest extends TestCase @@ -71,8 +73,10 @@ public function testFailsOnDuplicatedUser(): void ]); } - public function testSuccessfullRegistration(): void + public function testSuccessfulRegistrationCreatesUser(): void { + Mail::fake(); + $data = [ 'email' => $this->faker->email, 'name' => $this->faker->name, @@ -90,4 +94,25 @@ public function testSuccessfullRegistration(): void $data ); } + + public function testSuccessfulRegistrationSentsNotification(): void + { + Notification::fake(); + + $data = [ + 'email' => $this->faker->email, + 'name' => $this->faker->name, + ]; + + $this->json( + 'post', + self::REGISTRATION_URL, + array_merge($data, ['password' => $this->faker->password]) + ) + ->assertStatus(201); + + $user = User::query()->where('email', '=', $data['email'])->get(); + + Notification::assertSentTo($user, VerifyEmail::class); + } } diff --git a/tests/Integration/Services/Users/LaravelUserEmailVerifierTest.php b/tests/Integration/Services/Users/LaravelUserEmailVerifierTest.php new file mode 100644 index 0000000..23f3b73 --- /dev/null +++ b/tests/Integration/Services/Users/LaravelUserEmailVerifierTest.php @@ -0,0 +1,70 @@ +subject = new LaravelUserEmailVerifier(); + } + + public function testVerifyThrowsOnVerifiedUser(): void + { + /** @var UserModel $user */ + $user = UserModel::factory()->createOne(); + + $this->expectException(AlreadyVerified::class); + + $this->subject->verify($user->toValueObject()); + } + + public function testVerifyUser(): void + { + /** @var UserModel $user */ + $user = UserModel::factory()->notVerified()->createOne(); + + $this->subject->verify($user->toValueObject()); + + $user->refresh(); + self::assertTrue($user->hasVerifiedEmail()); + } + + public function testResendThrowsOnVerifiedUser(): void + { + /** @var UserModel $user */ + $user = UserModel::factory()->createOne(); + + $this->expectException(AlreadyVerified::class); + + $this->subject->resend($user->toValueObject()); + } + + public function testResendMails(): void + { + Notification::fake(); + + /** @var UserModel $user */ + $user = UserModel::factory()->notVerified()->createOne(); + + $this->subject->resend($user->toValueObject()); + + Notification::assertSentTo($user, VerifyEmail::class); + } +} diff --git a/tests/Integration/Services/Users/LaravelUserRegistratorTest.php b/tests/Integration/Services/Users/LaravelUserRegistratorTest.php new file mode 100644 index 0000000..daa6599 --- /dev/null +++ b/tests/Integration/Services/Users/LaravelUserRegistratorTest.php @@ -0,0 +1,59 @@ +subject = new LaravelUserRegistrator(); + } + + public function testRegisterCreatesANewUser(): void + { + $registerUser = new RegisterUser( + name: ':test:', + email: 'user@example.com', + password: ':password:', + origin: 'https://origin.com', + ); + + $this->subject->register($registerUser); + + $this->assertDatabaseHas('users', [ + 'name' => $registerUser->name, + 'email' => $registerUser->email, + 'origin' => $registerUser->origin, + 'email_verified_at' => null, + ]); + } + + public function testRegisterEmitEvent(): void + { + Event::fake(); + + $registerUser = new RegisterUser( + name: ':test:', + email: 'user@example.com', + password: ':password:', + origin: 'https://origin.com', + ); + + $this->subject->register($registerUser); + + Event::assertDispatched(Registered::class); + } +}