@@ -92,5 +92,236 @@ public async Task RegisterAsync_ShouldThrow_WhenUsernameExists()
9292 // Assert
9393 await act . Should ( ) . ThrowAsync < DuplicateUsernameException > ( ) ;
9494 }
95+
96+ [ Fact ]
97+ public async Task RegisterAsync_ShouldThrow_WhenEmailExists ( )
98+ {
99+ // Arrange
100+ var existingUser = new User
101+ {
102+ Id = Guid . NewGuid ( ) ,
103+ Username = "existinguser" ,
104+ Email = "test@example.com" ,
105+ PasswordHash = "hash"
106+ } ;
107+ _context . Users . Add ( existingUser ) ;
108+ await _context . SaveChangesAsync ( ) ;
109+
110+ var dto = new RegisterDto
111+ {
112+ Username = "newuser" ,
113+ Email = "test@example.com" ,
114+ Password = "Password123!"
115+ } ;
116+
117+ // Act
118+ Func < Task > act = async ( ) => await _authService . RegisterAsync ( dto ) ;
119+
120+ // Assert
121+ await act . Should ( ) . ThrowAsync < DuplicateEmailException > ( ) ;
122+ }
123+
124+ [ Fact ]
125+ public async Task LoginAsync_ShouldReturnTokens_WhenCredentialsValid ( )
126+ {
127+ // Arrange
128+ var registerDto = new RegisterDto
129+ {
130+ Username = "loginuser" ,
131+ Email = "login@example.com" ,
132+ Password = "Password123!"
133+ } ;
134+ await _authService . RegisterAsync ( registerDto ) ;
135+
136+ var loginDto = new LoginDto
137+ {
138+ Username = "loginuser" ,
139+ Password = "Password123!"
140+ } ;
141+
142+ // Act
143+ var result = await _authService . LoginAsync ( loginDto ) ;
144+
145+ // Assert
146+ result . Should ( ) . NotBeNull ( ) ;
147+ result . Token . Should ( ) . NotBeNullOrEmpty ( ) ;
148+ result . RefreshToken . Should ( ) . NotBeNullOrEmpty ( ) ;
149+ }
150+
151+ [ Fact ]
152+ public async Task LoginAsync_ShouldThrow_WhenUserNotFound ( )
153+ {
154+ // Arrange
155+ var loginDto = new LoginDto
156+ {
157+ Username = "nonexistent" ,
158+ Password = "Password123!"
159+ } ;
160+
161+ // Act
162+ Func < Task > act = async ( ) => await _authService . LoginAsync ( loginDto ) ;
163+
164+ // Assert
165+ await act . Should ( ) . ThrowAsync < InvalidCredentialsException > ( ) ;
166+ }
167+
168+ [ Fact ]
169+ public async Task LoginAsync_ShouldThrow_WhenPasswordInvalid ( )
170+ {
171+ // Arrange
172+ var registerDto = new RegisterDto
173+ {
174+ Username = "testuser2" ,
175+ Email = "test2@example.com" ,
176+ Password = "Password123!"
177+ } ;
178+ await _authService . RegisterAsync ( registerDto ) ;
179+
180+ var loginDto = new LoginDto
181+ {
182+ Username = "testuser2" ,
183+ Password = "WrongPassword!"
184+ } ;
185+
186+ // Act
187+ Func < Task > act = async ( ) => await _authService . LoginAsync ( loginDto ) ;
188+
189+ // Assert
190+ await act . Should ( ) . ThrowAsync < InvalidCredentialsException > ( ) ;
191+ }
192+
193+ [ Fact ]
194+ public async Task LoginAsync_ShouldLockAccount_After5FailedAttempts ( )
195+ {
196+ // Arrange
197+ var registerDto = new RegisterDto
198+ {
199+ Username = "lockuser" ,
200+ Email = "lock@example.com" ,
201+ Password = "Password123!"
202+ } ;
203+ await _authService . RegisterAsync ( registerDto ) ;
204+
205+ var loginDto = new LoginDto
206+ {
207+ Username = "lockuser" ,
208+ Password = "WrongPassword!"
209+ } ;
210+
211+ // Act - Make 5 failed attempts
212+ for ( int i = 0 ; i < 5 ; i ++ )
213+ {
214+ try { await _authService . LoginAsync ( loginDto ) ; } catch { }
215+ }
216+
217+ // Try a 6th time - should throw AccountLockedException
218+ Func < Task > act = async ( ) => await _authService . LoginAsync ( loginDto ) ;
219+
220+ // Assert
221+ await act . Should ( ) . ThrowAsync < AccountLockedException > ( ) ;
222+ }
223+
224+ [ Fact ]
225+ public async Task LoginAsync_ShouldResetFailedAttempts_OnSuccessfulLogin ( )
226+ {
227+ // Arrange
228+ var registerDto = new RegisterDto
229+ {
230+ Username = "resetuser" ,
231+ Email = "reset@example.com" ,
232+ Password = "Password123!"
233+ } ;
234+ await _authService . RegisterAsync ( registerDto ) ;
235+
236+ var wrongLoginDto = new LoginDto
237+ {
238+ Username = "resetuser" ,
239+ Password = "WrongPassword!"
240+ } ;
241+
242+ // Make a few failed attempts (but not enough to lock)
243+ for ( int i = 0 ; i < 3 ; i ++ )
244+ {
245+ try { await _authService . LoginAsync ( wrongLoginDto ) ; } catch { }
246+ }
247+
248+ // Now login successfully
249+ var correctLoginDto = new LoginDto
250+ {
251+ Username = "resetuser" ,
252+ Password = "Password123!"
253+ } ;
254+
255+ // Act
256+ var result = await _authService . LoginAsync ( correctLoginDto ) ;
257+
258+ // Assert
259+ result . Should ( ) . NotBeNull ( ) ;
260+ var user = await _context . Users . FirstAsync ( u => u . Username == "resetuser" ) ;
261+ user . FailedLoginAttempts . Should ( ) . Be ( 0 ) ;
262+ }
263+
264+ [ Fact ]
265+ public async Task RefreshTokenAsync_ShouldReturnNewTokens_WhenRefreshTokenValid ( )
266+ {
267+ // Arrange
268+ var registerDto = new RegisterDto
269+ {
270+ Username = "refreshuser" ,
271+ Email = "refresh@example.com" ,
272+ Password = "Password123!"
273+ } ;
274+ var authResponse = await _authService . RegisterAsync ( registerDto ) ;
275+
276+ // Act
277+ var result = await _authService . RefreshTokenAsync ( authResponse . Token , authResponse . RefreshToken ) ;
278+
279+ // Assert
280+ result . Should ( ) . NotBeNull ( ) ;
281+ result . Token . Should ( ) . NotBeNullOrEmpty ( ) ;
282+ result . RefreshToken . Should ( ) . NotBeNullOrEmpty ( ) ;
283+ }
284+
285+ [ Fact ]
286+ public async Task RefreshTokenAsync_ShouldThrow_WhenRefreshTokenInvalid ( )
287+ {
288+ // Arrange
289+ var registerDto = new RegisterDto
290+ {
291+ Username = "invalidrefresh" ,
292+ Email = "invalidrefresh@example.com" ,
293+ Password = "Password123!"
294+ } ;
295+ var authResponse = await _authService . RegisterAsync ( registerDto ) ;
296+
297+ // Act
298+ Func < Task > act = async ( ) => await _authService . RefreshTokenAsync ( authResponse . Token , "invalid-refresh-token" ) ;
299+
300+ // Assert
301+ await act . Should ( ) . ThrowAsync < InvalidRefreshTokenException > ( ) ;
302+ }
303+
304+ [ Fact ]
305+ public async Task RefreshTokenAsync_ShouldThrow_WhenRefreshTokenExpired ( )
306+ {
307+ // Arrange
308+ var user = new User
309+ {
310+ Id = Guid . NewGuid ( ) ,
311+ Username = "expireduser" ,
312+ Email = "expired@example.com" ,
313+ PasswordHash = BCrypt . Net . BCrypt . HashPassword ( "Password123!" ) ,
314+ RefreshToken = "expired-token" ,
315+ RefreshTokenExpiryTime = DateTime . UtcNow . AddDays ( - 1 ) // Expired yesterday
316+ } ;
317+ _context . Users . Add ( user ) ;
318+ await _context . SaveChangesAsync ( ) ;
319+
320+ // Act
321+ Func < Task > act = async ( ) => await _authService . RefreshTokenAsync ( "some-token" , "expired-token" ) ;
322+
323+ // Assert
324+ await act . Should ( ) . ThrowAsync < InvalidRefreshTokenException > ( ) ;
325+ }
95326 }
96327}
0 commit comments