view = $this->makeTwigMock(); $this->authService = $this->createMock(AuthServiceInterface::class); $this->flash = $this->createMock(FlashServiceInterface::class); $this->sessionManager = $this->createMock(SessionManagerInterface::class); $this->controller = new AccountController( $this->view, $this->authService, $this->flash, $this->sessionManager, ); } // ── showChangePassword ─────────────────────────────────────────── /** * showChangePassword() doit rendre le formulaire de changement de mot de passe. */ public function testShowChangePasswordRendersForm(): void { $this->view->expects($this->once()) ->method('render') ->with($this->anything(), 'pages/account/password-change.twig', $this->anything()) ->willReturnArgument(0); $res = $this->controller->showChangePassword($this->makeGet('/account/password'), $this->makeResponse()); $this->assertStatus($res, 200); } // ── changePassword ─────────────────────────────────────────────── /** * changePassword() doit rediriger avec une erreur si les mots de passe ne correspondent pas. */ public function testChangePasswordRedirectsWhenPasswordMismatch(): void { $this->flash->expects($this->once())->method('set') ->with('password_error', 'Les mots de passe ne correspondent pas'); $req = $this->makePost('/account/password', [ 'current_password' => 'oldpass', 'new_password' => 'newpass1', 'new_password_confirm' => 'newpass2', ]); $res = $this->controller->changePassword($req, $this->makeResponse()); $this->assertRedirectTo($res, '/account/password'); } /** * changePassword() ne doit pas appeler authService si les mots de passe ne correspondent pas. */ public function testChangePasswordDoesNotCallServiceOnMismatch(): void { $this->authService->expects($this->never())->method('changePassword'); $req = $this->makePost('/account/password', [ 'current_password' => 'old', 'new_password' => 'aaa', 'new_password_confirm' => 'bbb', ]); $this->controller->changePassword($req, $this->makeResponse()); } /** * changePassword() doit afficher une erreur si le nouveau mot de passe est trop court. */ public function testChangePasswordRedirectsOnWeakPassword(): void { $this->sessionManager->method('getUserId')->willReturn(1); $this->authService->method('changePassword')->willThrowException(new WeakPasswordException()); $this->flash->expects($this->once())->method('set') ->with('password_error', $this->stringContains('8 caractères')); $req = $this->makePost('/account/password', [ 'current_password' => 'old', 'new_password' => 'short', 'new_password_confirm' => 'short', ]); $res = $this->controller->changePassword($req, $this->makeResponse()); $this->assertRedirectTo($res, '/account/password'); } /** * changePassword() doit afficher une erreur si le mot de passe actuel est incorrect. */ public function testChangePasswordRedirectsOnWrongCurrentPassword(): void { $this->sessionManager->method('getUserId')->willReturn(1); $this->authService->method('changePassword') ->willThrowException(new \InvalidArgumentException('Mot de passe actuel incorrect')); $this->flash->expects($this->once())->method('set') ->with('password_error', 'Le mot de passe actuel est incorrect'); $req = $this->makePost('/account/password', [ 'current_password' => 'wrong', 'new_password' => 'newpassword', 'new_password_confirm' => 'newpassword', ]); $res = $this->controller->changePassword($req, $this->makeResponse()); $this->assertRedirectTo($res, '/account/password'); } /** * changePassword() doit afficher une erreur générique en cas d'exception inattendue. */ public function testChangePasswordRedirectsOnUnexpectedError(): void { $this->sessionManager->method('getUserId')->willReturn(1); $this->authService->method('changePassword') ->willThrowException(new \RuntimeException('DB error')); $this->flash->expects($this->once())->method('set') ->with('password_error', $this->stringContains('inattendue')); $req = $this->makePost('/account/password', [ 'current_password' => 'old', 'new_password' => 'newpassword', 'new_password_confirm' => 'newpassword', ]); $res = $this->controller->changePassword($req, $this->makeResponse()); $this->assertRedirectTo($res, '/account/password'); } /** * changePassword() doit afficher un message de succès et rediriger en cas de succès. */ public function testChangePasswordRedirectsWithSuccessFlash(): void { $this->sessionManager->method('getUserId')->willReturn(1); $this->flash->expects($this->once())->method('set') ->with('password_success', $this->stringContains('Mot de passe modifié')); $req = $this->makePost('/account/password', [ 'current_password' => 'oldpass123', 'new_password' => 'newpass123', 'new_password_confirm' => 'newpass123', ]); $res = $this->controller->changePassword($req, $this->makeResponse()); $this->assertRedirectTo($res, '/account/password'); } /** * changePassword() doit utiliser 0 comme userId de repli si la session est vide. */ public function testChangePasswordUsesZeroAsUserIdFallback(): void { $this->sessionManager->method('getUserId')->willReturn(null); $this->authService->expects($this->once()) ->method('changePassword') ->with(0, $this->anything(), $this->anything()); $req = $this->makePost('/account/password', [ 'current_password' => 'old', 'new_password' => 'newpassword', 'new_password_confirm' => 'newpassword', ]); $this->controller->changePassword($req, $this->makeResponse()); } }