userRepository = $this->createMock(UserRepositoryInterface::class); $this->sessionManager = $this->createMock(SessionManagerInterface::class); $this->loginAttemptRepository = $this->createMock(LoginAttemptRepositoryInterface::class); $this->service = new AuthService( $this->userRepository, $this->sessionManager, $this->loginAttemptRepository, ); } // ── authenticate ─────────────────────────────────────────────── /** * authenticate() doit retourner l'utilisateur si les identifiants sont corrects. */ public function testAuthenticateValidCredentials(): void { $password = 'motdepasse1'; $user = $this->makeUser('alice', 'alice@example.com', $password); $this->userRepository->method('findByUsername')->with('alice')->willReturn($user); $result = $this->service->authenticate('alice', $password); $this->assertSame($user, $result); } /** * authenticate() doit normaliser le nom d'utilisateur en minuscules. */ public function testAuthenticateNormalizesUsername(): void { $password = 'motdepasse1'; $user = $this->makeUser('alice', 'alice@example.com', $password); $this->userRepository->method('findByUsername')->with('alice')->willReturn($user); $result = $this->service->authenticate('ALICE', $password); $this->assertNotNull($result); } /** * authenticate() doit retourner null si l'utilisateur est introuvable. */ public function testAuthenticateUnknownUser(): void { $this->userRepository->method('findByUsername')->willReturn(null); $result = $this->service->authenticate('inconnu', 'motdepasse1'); $this->assertNull($result); } /** * authenticate() doit retourner null si le mot de passe est incorrect. */ public function testAuthenticateWrongPassword(): void { $user = $this->makeUser('alice', 'alice@example.com', 'bonmotdepasse'); $this->userRepository->method('findByUsername')->willReturn($user); $result = $this->service->authenticate('alice', 'mauvaismdp'); $this->assertNull($result); } // ── changePassword ───────────────────────────────────────────── /** * changePassword() doit mettre à jour le hash si les données sont valides. */ public function testChangePasswordWithValidData(): void { $password = 'ancienmdp1'; $user = $this->makeUser('alice', 'alice@example.com', $password, 1); $this->userRepository->method('findById')->with(1)->willReturn($user); $this->userRepository->expects($this->once())->method('updatePassword')->with(1); $this->service->changePassword(1, $password, 'nouveaumdp1'); } /** * changePassword() doit lever une exception si le mot de passe actuel est incorrect. */ public function testChangePasswordWrongCurrentPassword(): void { $user = $this->makeUser('alice', 'alice@example.com', 'bonmotdepasse', 1); $this->userRepository->method('findById')->willReturn($user); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessageMatches('/actuel incorrect/'); $this->service->changePassword(1, 'mauvaismdp', 'nouveaumdp1'); } /** * changePassword() avec exactement 8 caractères doit réussir (frontière basse). */ public function testChangePasswordMinimumLengthNewPassword(): void { $password = 'ancienmdp1'; $user = $this->makeUser('alice', 'alice@example.com', $password, 1); $this->userRepository->method('findById')->willReturn($user); $this->userRepository->expects($this->once())->method('updatePassword'); // Ne doit pas lever d'exception $this->service->changePassword(1, $password, '12345678'); $this->addToAssertionCount(1); } /** * changePassword() doit lever WeakPasswordException si le nouveau mot de passe est trop court. */ public function testChangePasswordTooShortNewPasswordThrowsWeakPasswordException(): void { $password = 'ancienmdp1'; $user = $this->makeUser('alice', 'alice@example.com', $password, 1); $this->userRepository->method('findById')->willReturn($user); $this->expectException(WeakPasswordException::class); $this->service->changePassword(1, $password, '1234567'); } /** * changePassword() doit lever NotFoundException si l'utilisateur est introuvable. */ public function testChangePasswordUnknownUserThrowsNotFoundException(): void { $this->userRepository->method('findById')->willReturn(null); $this->expectException(NotFoundException::class); $this->service->changePassword(99, 'ancienmdp1', 'nouveaumdp1'); } // ── login / logout / isLoggedIn ──────────────────────────────── /** * login() doit appeler SessionManager::setUser() avec les bonnes données. */ public function testLoginCallsSetUser(): void { $user = $this->makeUser('alice', 'alice@example.com', 'secret', 7, User::ROLE_ADMIN); $this->sessionManager->expects($this->once()) ->method('setUser') ->with(7, 'alice', User::ROLE_ADMIN); $this->service->login($user); } /** * logout() doit appeler SessionManager::destroy(). */ public function testLogoutCallsDestroy(): void { $this->sessionManager->expects($this->once())->method('destroy'); $this->service->logout(); } /** * isLoggedIn() doit déléguer à SessionManager::isAuthenticated(). */ public function testIsLoggedInDelegatesToSessionManager(): void { $this->sessionManager->method('isAuthenticated')->willReturn(true); $this->assertTrue($this->service->isLoggedIn()); } // ── Helpers ──────────────────────────────────────────────────── /** * Crée un utilisateur de test avec un hash bcrypt du mot de passe fourni. * * @param string $username Nom d'utilisateur * @param string $email Adresse e-mail * @param string $password Mot de passe en clair (haché en bcrypt) * @param int $id Identifiant (défaut : 1) * @param string $role Rôle (défaut : 'user') */ private function makeUser( string $username, string $email, string $password = 'motdepasse1', int $id = 1, string $role = User::ROLE_USER, ): User { return new User( $id, $username, $email, password_hash($password, PASSWORD_BCRYPT), $role, ); } }