1- using exercise . wwwapi . DTOs ;
2- using exercise . wwwapi . DTOs . Login ;
1+ using exercise . wwwapi . DTOs . Login ;
32using exercise . wwwapi . DTOs . Register ;
4- using exercise . wwwapi . Models ;
5- using Microsoft . AspNetCore . Http ;
63using Microsoft . AspNetCore . Mvc . Testing ;
74using System . Net ;
85using System . Text ;
129
1310namespace exercise . tests . IntegrationTests
1411{
12+ /// <summary>
13+ /// Integration tests exercising the user management endpoints end-to-end via the API surface.
14+ /// </summary>
1515 [ TestFixture ]
1616 public class UserTests
1717 {
@@ -33,15 +33,22 @@ public void TearDown()
3333 _factory . Dispose ( ) ;
3434 }
3535
36- // ad test cases for approved usernames, emails
37- [ Test , TestCaseSource ( typeof ( UserTestCases ) , nameof ( UserTestCases . ValidRegisterCases ) ) ]
36+ /// <summary>
37+ /// Confirms that valid registration payloads yield an HTTP 201 Created response.
38+ /// </summary>
39+ /// <remarks>Test cases come from <see cref="UserTestCases.ValidRegisterCases"/> and are uniquified at runtime to avoid clashes.</remarks>
40+ /// <param name="username">The base username supplied by the data source.</param>
41+ /// <param name="email">The base email supplied by the data source.</param>
42+ /// <param name="password">The password candidate to register with.</param>
43+ [ Test , TestCaseSource ( typeof ( UserTestCases ) , nameof ( UserTestCases . ValidRegisterCases ) ) ]
3844 public async Task Register_success ( string username , string email , string password )
3945 {
4046 var uniqueId = DateTime . UtcNow . ToString ( "yyMMddHHmmssffff" ) ;
4147
4248 string uniqueUsername = username . Length > 0 ? username + uniqueId : "" ;
4349
44- RegisterRequestDTO body = new RegisterRequestDTO {
50+ RegisterRequestDTO body = new RegisterRequestDTO
51+ {
4552 email = $ "{ uniqueId } { email } ",
4653 password = password
4754 } ;
@@ -64,6 +71,13 @@ public async Task Register_success(string username, string email, string passwor
6471 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . Created ) ) ;
6572 }
6673
74+ /// <summary>
75+ /// Verifies the registration endpoint rejects malformed payloads with non-Created responses.
76+ /// </summary>
77+ /// <remarks>Inputs are provided by <see cref="UserTestCases.InvalidRegisterCases"/>.</remarks>
78+ /// <param name="username">The attempted username for registration.</param>
79+ /// <param name="email">The attempted email for registration.</param>
80+ /// <param name="password">The password candidate under test.</param>
6781 [ Test , TestCaseSource ( typeof ( UserTestCases ) , nameof ( UserTestCases . InvalidRegisterCases ) ) ]
6882 public async Task Register_Failure ( string username , string email , string password )
6983 {
@@ -77,7 +91,7 @@ public async Task Register_Failure(string username, string email, string passwor
7791 email = $ "{ uniqueId } { email } ",
7892 password = password
7993 } ;
80-
94+
8195 var json = JsonSerializer . Serialize ( body ) ;
8296 var requestBody = new StringContent ( json , Encoding . UTF8 , "application/json" ) ;
8397
@@ -98,6 +112,12 @@ public async Task Register_Failure(string username, string email, string passwor
98112 }
99113
100114
115+ /// <summary>
116+ /// Ensures valid credentials receive an HTTP 200 response and a token payload.
117+ /// </summary>
118+ /// <remarks>Credentials are sourced from <see cref="UserTestCases.ValidLoginCases"/>.</remarks>
119+ /// <param name="email">The email address to authenticate with.</param>
120+ /// <param name="password">The password paired with the supplied email.</param>
101121 [ Test , TestCaseSource ( typeof ( UserTestCases ) , nameof ( UserTestCases . ValidLoginCases ) ) ]
102122 public async Task Login_success ( string email , string password )
103123 {
@@ -129,6 +149,12 @@ public async Task Login_success(string email, string password)
129149 Assert . That ( message [ "data" ] [ "token" ] , Is . Not . Null ) ;
130150 }
131151
152+ /// <summary>
153+ /// Asserts that invalid login attempts fail with the expected HTTP status and message.
154+ /// </summary>
155+ /// <remarks>Negative credential combinations come from <see cref="UserTestCases.InvalidLoginCases"/>.</remarks>
156+ /// <param name="email">The incorrect email supplied to the login endpoint.</param>
157+ /// <param name="password">The incorrect password paired with the email.</param>
132158 [ Test , TestCaseSource ( typeof ( UserTestCases ) , nameof ( UserTestCases . InvalidLoginCases ) ) ]
133159 public async Task Login_failure ( string email , string password )
134160 {
@@ -163,6 +189,9 @@ public async Task Login_failure(string email, string password)
163189
164190 }
165191
192+ /// <summary>
193+ /// Validates that a PATCH with complete, valid fields updates an existing user successfully.
194+ /// </summary>
166195 [ Test ]
167196 public async Task UpdateUserSuccess ( )
168197 {
@@ -183,20 +212,26 @@ public async Task UpdateUserSuccess()
183212 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . OK ) ) ;
184213 }
185214
215+ /// <summary>
216+ /// Ensures PATCH requests with no mutable fields return a bad request response.
217+ /// </summary>
186218 [ Test ]
187- public async Task UpdateUserNoContent ( )
219+ public async Task UpdateUserNullFieldsOnly ( )
188220 {
189- var fieldsToUpdate = new Dictionary < string , object ? > { } ;
221+ var fieldsToUpdate = new Dictionary < string , object ? > { } ;
190222
191223 var json = JsonSerializer . Serialize ( fieldsToUpdate ) ;
192224 var content = new StringContent ( json , Encoding . UTF8 , "application/json" ) ;
193225
194226 int userId = 1 ;
195227 var response = await _client . PatchAsync ( $ "/users/{ userId } ", content ) ;
196228
197- Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . NoContent ) ) ;
229+ Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
198230 }
199231
232+ /// <summary>
233+ /// Confirms the API rejects usernames that violate allowed formatting rules.
234+ /// </summary>
200235 [ Test ]
201236 public async Task UpdateUserInvalidUsername ( )
202237 {
@@ -214,6 +249,9 @@ public async Task UpdateUserInvalidUsername()
214249 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
215250 }
216251
252+ /// <summary>
253+ /// Confirms GitHub username updates honor the same validation rules as the standalone validator.
254+ /// </summary>
217255 [ Test ]
218256 public async Task UpdateUserInvalidGitHubUsername ( )
219257 {
@@ -231,6 +269,9 @@ public async Task UpdateUserInvalidGitHubUsername()
231269 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
232270 }
233271
272+ /// <summary>
273+ /// Verifies email updates are blocked when the address fails format validation.
274+ /// </summary>
234275 [ Test ]
235276 public async Task UpdateUserInvalidEmail ( )
236277 {
@@ -248,6 +289,9 @@ public async Task UpdateUserInvalidEmail()
248289 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
249290 }
250291
292+ /// <summary>
293+ /// Confirms passwords that do not meet the complexity requirements are rejected on update.
294+ /// </summary>
251295 [ Test ]
252296 public async Task UpdateUserInvalidPassword ( )
253297 {
@@ -265,6 +309,9 @@ public async Task UpdateUserInvalidPassword()
265309 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
266310 }
267311
312+ /// <summary>
313+ /// Ensures role updates outside the defined enum range produce a bad request.
314+ /// </summary>
268315 [ Test ]
269316 public async Task UpdateUserInvalidRole ( )
270317 {
@@ -282,6 +329,9 @@ public async Task UpdateUserInvalidRole()
282329 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
283330 }
284331
332+ /// <summary>
333+ /// Verifies the API prevents changing a username to one that is already in use.
334+ /// </summary>
285335 [ Test ]
286336 public async Task UpdateUserUsernameExists ( )
287337 {
@@ -299,6 +349,9 @@ public async Task UpdateUserUsernameExists()
299349 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
300350 }
301351
352+ /// <summary>
353+ /// Verifies the API prevents reusing an existing GitHub username value.
354+ /// </summary>
302355 [ Test ]
303356 public async Task UpdateUserGitHubUsernameExists ( )
304357 {
@@ -316,6 +369,9 @@ public async Task UpdateUserGitHubUsernameExists()
316369 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
317370 }
318371
372+ /// <summary>
373+ /// Verifies the API prevents reusing an existing email address value.
374+ /// </summary>
319375 [ Test ]
320376 public async Task UpdateUserEmailExists ( )
321377 {
@@ -333,6 +389,11 @@ public async Task UpdateUserEmailExists()
333389 Assert . That ( response . StatusCode , Is . EqualTo ( HttpStatusCode . BadRequest ) ) ;
334390 }
335391
392+ /// <summary>
393+ /// Ensures retrieving users by id returns 200 for known users and 404 for missing ones.
394+ /// </summary>
395+ /// <param name="id">The user id sent to the endpoint.</param>
396+ /// <param name="responseStatus">The expected HTTP status code.</param>
336397 [ TestCase ( "1" , HttpStatusCode . OK ) ]
337398 [ TestCase ( "10000000" , HttpStatusCode . NotFound ) ]
338399 public async Task GetUserByIdTest ( string id , HttpStatusCode responseStatus )
0 commit comments