1414
1515use App \libs \OAuth2 \Repositories \IOAuth2OTPRepository ;
1616use Auth \AuthService ;
17+ use Auth \CustomAuthProvider ;
1718use Auth \Exceptions \AuthenticationException ;
1819use Auth \Repositories \IUserRepository ;
1920use Mockery ;
@@ -89,16 +90,9 @@ public function testValidCredentials_returnsUser_withoutEstablishingSession(): v
8990 $ username = 'jane.doe ' ;
9091 $ password = 'Str0ng!Pass ' ;
9192
92- $ this ->mock_user_repository
93- ->expects ($ this ->once ())
94- ->method ('getByEmailOrName ' )
95- ->with ($ username )
96- ->willReturn (null );
97-
9893 $ resolved_user = Mockery::mock ('Auth\User ' );
99- $ resolved_user ->shouldReceive ('canLogin ' )->andReturn (true );
10094
101- $ provider_mock = Mockery::mock (' Illuminate\Contracts\Auth\UserProvider ' );
95+ $ provider_mock = Mockery::mock (CustomAuthProvider::class );
10296 $ provider_mock ->shouldReceive ('retrieveByCredentials ' )
10397 ->once ()
10498 ->with (['username ' => $ username , 'password ' => $ password ])
@@ -122,13 +116,7 @@ public function testInvalidCredentials_throwsAuthenticationException(): void
122116 $ username = 'jane.doe ' ;
123117 $ password = 'wrong ' ;
124118
125- $ this ->mock_user_repository
126- ->expects ($ this ->once ())
127- ->method ('getByEmailOrName ' )
128- ->with ($ username )
129- ->willReturn (null );
130-
131- $ provider_mock = Mockery::mock ('Illuminate\Contracts\Auth\UserProvider ' );
119+ $ provider_mock = Mockery::mock (CustomAuthProvider::class);
132120 $ provider_mock ->shouldReceive ('retrieveByCredentials ' )
133121 ->once ()
134122 ->with (['username ' => $ username , 'password ' => $ password ])
@@ -143,110 +131,13 @@ public function testInvalidCredentials_throwsAuthenticationException(): void
143131 $ this ->service ->validateCredentials ($ username , $ password );
144132 }
145133
146- /**
147- * Provider returns a User whose canLogin() is false — must throw AuthenticationException.
148- * This guards against future providers or provider changes that bypass the internal canLogin()
149- * check inside CustomAuthProvider::retrieveByCredentials().
150- */
151- public function testProviderReturnsUserThatCannotLogin_throwsAuthenticationException (): void
152- {
153- $ username = 'jane.doe ' ;
154- $ password = 'Str0ng!Pass ' ;
155-
156- // Pre-check: user not found in repository, so the locked-account short-circuit is not taken.
157- $ this ->mock_user_repository
158- ->expects ($ this ->once ())
159- ->method ('getByEmailOrName ' )
160- ->with ($ username )
161- ->willReturn (null );
162-
163- // Provider returns a valid User instance, but canLogin() is false.
164- $ non_loginable_user = Mockery::mock ('Auth\User ' );
165- $ non_loginable_user ->shouldReceive ('canLogin ' )->andReturn (false );
166-
167- $ provider_mock = Mockery::mock ('Illuminate\Contracts\Auth\UserProvider ' );
168- $ provider_mock ->shouldReceive ('retrieveByCredentials ' )
169- ->once ()
170- ->with (['username ' => $ username , 'password ' => $ password ])
171- ->andReturn ($ non_loginable_user );
172-
173- $ this ->auth_mock ->shouldReceive ('getProvider ' )->once ()->andReturn ($ provider_mock );
174- $ this ->auth_mock ->shouldNotReceive ('login ' );
175- $ this ->auth_mock ->shouldNotReceive ('attempt ' );
176-
177- $ this ->expectException (AuthenticationException::class);
178-
179- $ this ->service ->validateCredentials ($ username , $ password );
180- }
181-
182- /**
183- * A user that exists but is inactive (locked) short-circuits the password check
184- * and throws AuthenticationException with a "is locked" message.
185- */
186- public function testLockedAccount_throwsAuthenticationException_withLockedMessage (): void
187- {
188- $ username = 'locked.user ' ;
189-
190- $ locked_user = Mockery::mock ('Auth\User ' );
191- $ locked_user ->shouldReceive ('isActive ' )->andReturn (false );
192-
193- $ this ->mock_user_repository
194- ->expects ($ this ->once ())
195- ->method ('getByEmailOrName ' )
196- ->with ($ username )
197- ->willReturn ($ locked_user );
198-
199- // Provider must NOT be consulted when the user is locked.
200- $ this ->auth_mock ->shouldNotReceive ('getProvider ' );
201- $ this ->auth_mock ->shouldNotReceive ('login ' );
202- $ this ->auth_mock ->shouldNotReceive ('attempt ' );
203-
204- $ this ->expectException (AuthenticationException::class);
205- $ this ->expectExceptionMessageMatches ('/is locked/i ' );
206-
207- $ this ->service ->validateCredentials ($ username , 'irrelevant ' );
208- }
209-
210- /**
211- * When the existing user is active, the locked-path is not taken:
212- * the provider is consulted and the resolved User is returned.
213- */
214- public function testActiveUser_doesNotTriggerLockedPath (): void
215- {
216- $ username = 'jane.doe ' ;
217- $ password = 'Str0ng!Pass ' ;
218-
219- $ active_user = Mockery::mock ('Auth\User ' );
220- $ active_user ->shouldReceive ('isActive ' )->andReturn (true );
221-
222- $ this ->mock_user_repository
223- ->expects ($ this ->once ())
224- ->method ('getByEmailOrName ' )
225- ->with ($ username )
226- ->willReturn ($ active_user );
227-
228- $ resolved_user = Mockery::mock ('Auth\User ' );
229- $ resolved_user ->shouldReceive ('canLogin ' )->andReturn (true );
230-
231- $ provider_mock = Mockery::mock ('Illuminate\Contracts\Auth\UserProvider ' );
232- $ provider_mock ->shouldReceive ('retrieveByCredentials ' )
233- ->once ()
234- ->andReturn ($ resolved_user );
235-
236- $ this ->auth_mock ->shouldReceive ('getProvider ' )->once ()->andReturn ($ provider_mock );
237- $ this ->auth_mock ->shouldNotReceive ('login ' );
238-
239- $ returned = $ this ->service ->validateCredentials ($ username , $ password );
240-
241- $ this ->assertSame ($ resolved_user , $ returned );
242- }
243-
244134 /**
245135 * loginUser(user, true) delegates to Auth::login with the remember flag set.
246136 */
247137 public function testLoginUser_callsAuthLogin_withRememberTrue (): void
248138 {
249139 $ user = Mockery::mock ('Auth\User ' );
140+ $ user ->shouldReceive ('canLogin ' )->andReturn (true );
250141
251142 $ this ->auth_mock
252143 ->shouldReceive ('login ' )
@@ -262,6 +153,7 @@ public function testLoginUser_callsAuthLogin_withRememberTrue(): void
262153 public function testLoginUser_callsAuthLogin_withRememberFalse (): void
263154 {
264155 $ user = Mockery::mock ('Auth\User ' );
156+ $ user ->shouldReceive ('canLogin ' )->andReturn (true );
265157
266158 $ this ->auth_mock
267159 ->shouldReceive ('login ' )
@@ -270,4 +162,21 @@ public function testLoginUser_callsAuthLogin_withRememberFalse(): void
270162
271163 $ this ->service ->loginUser ($ user , false );
272164 }
165+
166+ /**
167+ * loginUser(user, [true|false]) and isActive or canLogin false throws an Exception.
168+ */
169+ public function testLoginUser_throwsException_whenIsNotActive (): void
170+ {
171+ $ user = Mockery::mock ('Auth\User ' );
172+ $ user ->shouldReceive ('canLogin ' )->andReturn (false );
173+
174+ $ this ->auth_mock ->shouldNotReceive ('login ' );
175+
176+ $ this ->expectException (AuthenticationException::class);
177+ $ this ->expectExceptionMessageMatches ('/User is not active or cannot login\./ ' );
178+
179+ $ this ->service ->loginUser ($ user , true );
180+ }
181+
273182}
0 commit comments