use FakePasswordHasher in tests to eliminate bcrypt cost

Add a trivial prefix-based PasswordHasher fake and inject it into the
three test files that exercise CreateUser or AuthenticateUser. Drops
the full phpunit suite from ~7.4s to ~30ms (about 224x) without
losing coverage: the round-trip through hash/verify still validates
that CreateUser stores something other than the plaintext and that
AuthenticateUser only succeeds on a matching hash.

CreateUserTest is also refactored to use a setUp method, matching
the pattern already used in AuthenticateUserTest and AuthControllerTest.
This commit is contained in:
Yisroel Baum 2026-04-26 09:06:26 +03:00
parent 632085f5b6
commit bb6bd7cbb3
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
4 changed files with 76 additions and 39 deletions

View file

@ -10,22 +10,31 @@ use App\User\UseCases\CreateUser;
use App\User\UseCases\CreateUserRequest;
use App\User\User;
use PHPUnit\Framework\TestCase;
use Tests\Fakes\FakePasswordHasher;
use Tests\Fakes\FakeUserRepository;
class AuthenticateUserTest extends TestCase
{
private FakeUserRepository $userRepo;
private FakePasswordHasher $passwordHasher;
private AuthenticateUser $useCase;
public function setUp(): void
{
$this->userRepo = new FakeUserRepository();
$createUser = new CreateUser($this->userRepo);
$this->passwordHasher = new FakePasswordHasher();
$createUser = new CreateUser(
$this->userRepo,
$this->passwordHasher,
);
$createUser->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
));
$this->useCase = new AuthenticateUser($this->userRepo);
$this->useCase = new AuthenticateUser(
$this->userRepo,
$this->passwordHasher,
);
}
public function test_returns_user_on_valid_credentials(): void

View file

@ -6,67 +6,71 @@ use App\Exceptions\BadRequestException;
use App\User\User;
use App\User\UseCases\CreateUser;
use App\User\UseCases\CreateUserRequest;
use Tests\Fakes\FakePasswordHasher;
use Tests\Fakes\FakeUserRepository;
use PHPUnit\Framework\TestCase;
class CreateUserTest extends TestCase
{
private FakeUserRepository $userRepo;
private FakePasswordHasher $passwordHasher;
private CreateUser $useCase;
public function setUp(): void
{
$this->userRepo = new FakeUserRepository();
$this->passwordHasher = new FakePasswordHasher();
$this->useCase = new CreateUser(
$this->userRepo,
$this->passwordHasher,
);
}
public function test_create_user(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
));
$user = $userRepo->find(0);
$user = $this->userRepo->find(0);
$this->assertInstanceOf(User::class, $user);
$this->assertEquals('test@test.com', $user->getEmail());
}
public function test_throws_if_email_is_null(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('email is required');
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: null,
));
}
public function test_is_admin_defaults_to_false(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
));
$user = $userRepo->find(0);
$user = $this->userRepo->find(0);
$this->assertFalse($user->isAdmin());
}
public function test_is_admin_can_be_set_true(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
isAdmin: true,
));
$user = $userRepo->find(0);
$user = $this->userRepo->find(0);
$this->assertTrue($user->isAdmin());
}
public function test_throws_when_email_already_taken(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
));
@ -74,7 +78,7 @@ class CreateUserTest extends TestCase
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('email already taken');
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
));
@ -82,13 +86,10 @@ class CreateUserTest extends TestCase
public function test_throws_if_password_is_null(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('password is required');
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: null,
));
@ -96,15 +97,12 @@ class CreateUserTest extends TestCase
public function test_throws_if_password_too_short(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage(
'password must be at least 8 characters'
);
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'short',
));
@ -112,16 +110,17 @@ class CreateUserTest extends TestCase
public function test_stores_hashed_password(): void
{
$userRepo = new FakeUserRepository();
$useCase = new CreateUser($userRepo);
$useCase->execute(new CreateUserRequest(
$this->useCase->execute(new CreateUserRequest(
email: 'test@test.com',
password: 'password1',
));
$user = $userRepo->find(0);
$user = $this->userRepo->find(0);
$this->assertNotEquals('password1', $user->getPasswordHash());
$this->assertTrue(
password_verify('password1', $user->getPasswordHash())
$this->passwordHasher->verify(
'password1',
$user->getPasswordHash()
)
);
}
}