@@ -54,13 +54,6 @@ private function getTestUser(): User
5454 ->findOneBy (['identifier ' => 'sebastian.marcet ' ]);
5555 }
5656
57- private function setLoginAttempts (User $ user , int $ attempts ): void
58- {
59- $ user ->setLoginFailedAttempt ($ attempts );
60- EntityManager::persist ($ user );
61- EntityManager::flush ();
62- }
63-
6457 private function postLogin (array $ overrides = [])
6558 {
6659 // GET the login page first so the session (and its CSRF token) is established,
@@ -104,9 +97,10 @@ private function sessionHasValidationError(string $field): bool
10497 public function testMissingTurnstileResponseFailsValidationWhenAtThreshold (): void
10598 {
10699 $ user = $ this ->getTestUser ();
107- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD );
108100
109- $ this ->postLogin (); // no cf-turnstile-response
101+ $ this ->postLogin ([
102+ "login_attempts " => self ::CAPTCHA_THRESHOLD ,
103+ ]); // no cf-turnstile-response
110104
111105 $ this ->assertTrue (
112106 $ this ->sessionHasValidationError ('cf-turnstile-response ' ),
@@ -121,9 +115,10 @@ public function testMissingTurnstileResponseFailsValidationWhenAtThreshold(): vo
121115 public function testLoginBelowThresholdDoesNotRequireTurnstile (): void
122116 {
123117 $ user = $ this ->getTestUser ();
124- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD - 1 );
125118
126- $ this ->postLogin (); // correct credentials, no captcha token
119+ $ this ->postLogin ([
120+ 'login_attempts ' => self ::CAPTCHA_THRESHOLD - 1
121+ ]); // correct credentials, no captcha token
127122
128123 $ this ->assertFalse (
129124 $ this ->sessionHasValidationError ('cf-turnstile-response ' ),
@@ -134,38 +129,20 @@ public function testLoginBelowThresholdDoesNotRequireTurnstile(): void
134129 public function testLoginAtThresholdWithValidTokenPassesValidation (): void
135130 {
136131 $ user = $ this ->getTestUser ();
137- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD );
138132
139133 $ this ->fakeTurnstilePass ();
140134
141- $ this ->postLogin (['cf-turnstile-response ' => 'dummy-token-accepted-by-mock ' ]);
135+ $ this ->postLogin ([
136+ 'cf-turnstile-response ' => 'dummy-token-accepted-by-mock ' ,
137+ 'login_attempts ' => 1
138+ ]);
142139
143140 $ this ->assertFalse (
144141 $ this ->sessionHasValidationError ('cf-turnstile-response ' ),
145142 'A valid Turnstile token must clear the captcha validation rule '
146143 );
147144 }
148145
149- // -------------------------------------------------------------------------
150- // 3. Server-side login-attempt lookup: user exists vs. does not exist
151- // -------------------------------------------------------------------------
152-
153- public function testLoginAttemptsLoadedFromExistingUserRecord (): void
154- {
155- $ user = $ this ->getTestUser ();
156- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD );
157-
158- // Omit cf-turnstile-response. If the controller had NOT read the DB value it
159- // would see login_attempts = 0 and skip the captcha rule. The error proves
160- // the persisted attempt count was used.
161- $ this ->postLogin ();
162-
163- $ this ->assertTrue (
164- $ this ->sessionHasValidationError ('cf-turnstile-response ' ),
165- 'Expected captcha required error, which proves login_failed_attempt was read from DB '
166- );
167- }
168-
169146 public function testLoginAttemptsDefaultToZeroForUnknownUsername (): void
170147 {
171148 // No user with this email → auth_service->getUserByUsername() returns null
@@ -188,10 +165,12 @@ public function testLoginAttemptsDefaultToZeroForUnknownUsername(): void
188165 public function testLoginScreenIncludesTurnstileConfigWhenAboveThreshold (): void
189166 {
190167 $ user = $ this ->getTestUser ();
191- // Place user one below threshold; the wrong-password attempt crosses it.
192- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD - 1 );
193168
194- $ this ->postLogin (['password ' => 'wrong-password ' ]);
169+ // Place user one below threshold; the wrong-password attempt crosses it.
170+ $ this ->postLogin ([
171+ 'password ' => 'wrong-password ' ,
172+ 'login_attempts ' => self ::CAPTCHA_THRESHOLD - 1
173+ ]);
195174
196175 // errorLogin() flashes max_login_attempts_2_show_captcha into the session;
197176 // following the redirect renders login.blade.php which emits those values.
@@ -211,12 +190,14 @@ public function testLoginScreenIncludesTurnstileConfigWhenAboveThreshold(): void
211190 public function testExpiredTurnstileTokenFailsValidation (): void
212191 {
213192 $ user = $ this ->getTestUser ();
214- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD );
215193
216194 // Cloudflare API returns success=false (expired / already-used token)
217195 $ this ->fakeTurnstileFail ();
218196
219- $ this ->postLogin (['cf-turnstile-response ' => 'expired-or-invalid-token ' ]);
197+ $ this ->postLogin ([
198+ 'cf-turnstile-response ' => 'expired-or-invalid-token ' ,
199+ 'login_attempts ' => self ::CAPTCHA_THRESHOLD
200+ ]);
220201
221202 $ this ->assertTrue (
222203 $ this ->sessionHasValidationError ('cf-turnstile-response ' ),
@@ -227,10 +208,12 @@ public function testExpiredTurnstileTokenFailsValidation(): void
227208 public function testUnsolvedCaptchaEmptyTokenFailsValidation (): void
228209 {
229210 $ user = $ this ->getTestUser ();
230- $ this ->setLoginAttempts ($ user , self ::CAPTCHA_THRESHOLD );
231211
232212 // Empty string triggers the 'required' rule before any Cloudflare call
233- $ this ->postLogin (['cf-turnstile-response ' => '' ]);
213+ $ this ->postLogin ([
214+ 'cf-turnstile-response ' => '' ,
215+ 'login_attempts ' => self ::CAPTCHA_THRESHOLD
216+ ]);
234217
235218 $ this ->assertTrue (
236219 $ this ->sessionHasValidationError ('cf-turnstile-response ' ),
0 commit comments