From 1311819a27dbe44892078f1bd06613e63092ad62 Mon Sep 17 00:00:00 2001 From: Johan Reitan Date: Mon, 22 Sep 2025 08:59:28 +0200 Subject: [PATCH 1/3] failing tests, but working endpoints --- api.tests/Notes/GetNotesTests.cs | 8 +-- api.tests/UserEndpointTests/GetUserTests.cs | 6 +- .../UserEndpointTests/RegistrationTests.cs | 6 +- .../DTOs/GetObjects/UsersSuccessDTO.cs | 2 +- .../DTOs/Notes/NotesResponseDTO.cs | 2 +- .../DTOs/UpdateUser/UpdateUserRequestDTO.cs | 1 - exercise.wwwapi/DTOs/Users/PostUserDTO.cs | 1 - exercise.wwwapi/Endpoints/NoteEndpoints.cs | 18 +++--- exercise.wwwapi/Endpoints/UserEndpoints.cs | 59 +++++++++++++++---- 9 files changed, 69 insertions(+), 34 deletions(-) diff --git a/api.tests/Notes/GetNotesTests.cs b/api.tests/Notes/GetNotesTests.cs index 3c01df7..864faa8 100644 --- a/api.tests/Notes/GetNotesTests.cs +++ b/api.tests/Notes/GetNotesTests.cs @@ -68,7 +68,7 @@ public async Task GetNoteByIdSuccess() Assert.That(noteResult.Status, Is.EqualTo("success")); Assert.That(noteResult.Data, Is.Not.Null); Assert.That(noteResult.Data.Id, Is.EqualTo(noteId)); - Assert.That(noteResult.Data.Title, Is.EqualTo("Title Note 1")); + Assert.That(noteResult.Data.Title, Is.EqualTo("Name Note 1")); Assert.That(noteResult.Data.Content, Is.EqualTo("note1note1 note1 note1 content")); } @@ -88,7 +88,7 @@ public async Task TeacherGetNotesOnAStudentSuccess() { await AuthenticateAsTeacherAsync(); - var userId = 2; // student user id to get notes for + var userId = 1; // student user id to get notes for var getNotesResponse = await _client.GetAsync($"/users/{userId}/notes"); Assert.That(getNotesResponse.IsSuccessStatusCode, Is.True); @@ -101,7 +101,7 @@ public async Task TeacherGetNotesOnAStudentSuccess() Assert.That(notesResult.Status, Is.EqualTo("success")); Assert.That(notesResult.Data, Is.Not.Null); Assert.That(notesResult.Data.Notes, Is.Not.Empty); - Assert.That(notesResult.Data.Notes.Count, Is.EqualTo(1)); + Assert.That(notesResult.Data.Notes.Count, Is.EqualTo(4)); } [Test] @@ -109,7 +109,7 @@ public async Task TeacherGetNotesOnStudentWithNoNotesSuccess() { await AuthenticateAsTeacherAsync(); - var userId = 5; // student user id to get notes for but user has no notes + var userId = 3; // student user id to get notes for but user has no notes var getNotesResponse = await _client.GetAsync($"/users/{userId}/notes"); Assert.That(getNotesResponse.IsSuccessStatusCode, Is.True); diff --git a/api.tests/UserEndpointTests/GetUserTests.cs b/api.tests/UserEndpointTests/GetUserTests.cs index d47b931..41f5151 100644 --- a/api.tests/UserEndpointTests/GetUserTests.cs +++ b/api.tests/UserEndpointTests/GetUserTests.cs @@ -74,7 +74,7 @@ public async Task GetFilteredUsersByFirstNameTest() [Test] public async Task GetFilteredUsersByLastNameTest() { - var getUsersResponse = await _client.GetAsync($"users?searchTerm=Jackson"); + var getUsersResponse = await _client.GetAsync($"users?searchTerm=jordan"); var jsonResponse = await getUsersResponse.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(jsonResponse); @@ -94,7 +94,7 @@ public async Task GetFilteredUsersTestFails() [Test] public async Task GetFilteredUsersByFullNameTest() { - var getUsersResponse = await _client.GetAsync($"users?searchTerm=Michael%20Jackson"); + var getUsersResponse = await _client.GetAsync($"users?searchTerm=Michael%20Jordan"); var jsonResponse = await getUsersResponse.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(jsonResponse); @@ -108,7 +108,7 @@ public async Task GetFilteredUsersByLetterTest() var jsonResponse = await getUsersResponse.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(jsonResponse); - Assert.That(result.Data.Users.Count, Is.EqualTo(3)); + Assert.That(result.Data.Users.Count, Is.EqualTo(2)); } } \ No newline at end of file diff --git a/api.tests/UserEndpointTests/RegistrationTests.cs b/api.tests/UserEndpointTests/RegistrationTests.cs index 52cf686..b85d23f 100644 --- a/api.tests/UserEndpointTests/RegistrationTests.cs +++ b/api.tests/UserEndpointTests/RegistrationTests.cs @@ -23,9 +23,9 @@ public void TearDown() [Test] public async Task RegisterUserExistsTest() { - const string email = "test1@test1"; - const string password = "Test1test1%"; - const string username = "TestTestTest"; + const string email = "test2@test2"; + const string password = "Test2test2%"; + const string username = "test2@test2"; var newUser = new RegisterRequestDTO { diff --git a/exercise.wwwapi/DTOs/GetObjects/UsersSuccessDTO.cs b/exercise.wwwapi/DTOs/GetObjects/UsersSuccessDTO.cs index c3f71b8..4a9a632 100644 --- a/exercise.wwwapi/DTOs/GetObjects/UsersSuccessDTO.cs +++ b/exercise.wwwapi/DTOs/GetObjects/UsersSuccessDTO.cs @@ -6,5 +6,5 @@ namespace exercise.wwwapi.DTOs.GetUsers; public class UsersSuccessDTO { [JsonPropertyName("users")] - public List Users { get; set; } = []; + public List Users { get; set; } = new List(); } \ No newline at end of file diff --git a/exercise.wwwapi/DTOs/Notes/NotesResponseDTO.cs b/exercise.wwwapi/DTOs/Notes/NotesResponseDTO.cs index aaf3e2a..87d77ca 100644 --- a/exercise.wwwapi/DTOs/Notes/NotesResponseDTO.cs +++ b/exercise.wwwapi/DTOs/Notes/NotesResponseDTO.cs @@ -2,6 +2,6 @@ { public class NotesResponseDTO { - public List Notes { get; set; } + public List Notes { get; set; } = new List(); } } diff --git a/exercise.wwwapi/DTOs/UpdateUser/UpdateUserRequestDTO.cs b/exercise.wwwapi/DTOs/UpdateUser/UpdateUserRequestDTO.cs index ddab366..5f0fa73 100644 --- a/exercise.wwwapi/DTOs/UpdateUser/UpdateUserRequestDTO.cs +++ b/exercise.wwwapi/DTOs/UpdateUser/UpdateUserRequestDTO.cs @@ -17,7 +17,6 @@ public class UpdateUserRequestDTO [JsonPropertyName("lastName")] public string? LastName { get; set; } - [JsonPropertyName("bio")] public string? Bio { get; set; } [JsonPropertyName("github")] diff --git a/exercise.wwwapi/DTOs/Users/PostUserDTO.cs b/exercise.wwwapi/DTOs/Users/PostUserDTO.cs index 127e291..b055266 100644 --- a/exercise.wwwapi/DTOs/Users/PostUserDTO.cs +++ b/exercise.wwwapi/DTOs/Users/PostUserDTO.cs @@ -6,7 +6,6 @@ namespace exercise.wwwapi.DTOs.Users { public class PostUserDTO { - public int Id { get; set; } public string Username { get; set; } public string Email { get; set; } public string Password { get; set; } diff --git a/exercise.wwwapi/Endpoints/NoteEndpoints.cs b/exercise.wwwapi/Endpoints/NoteEndpoints.cs index 7788af7..2029d75 100644 --- a/exercise.wwwapi/Endpoints/NoteEndpoints.cs +++ b/exercise.wwwapi/Endpoints/NoteEndpoints.cs @@ -66,18 +66,18 @@ private static async Task GetAllNotesForUser(IRepository userRepo { noteResponse.Add(NoteFactory.GetNoteDTO(note)); } + */ - var response = new ResponseDTO + var responseObject = new ResponseDTO { Status = "success", Data = new NotesResponseDTO { - Notes = noteResponse + Notes = result.ToList() } }; - */ - return TypedResults.Ok(result); + return TypedResults.Ok(responseObject); } [ProducesResponseType(StatusCodes.Status200OK)] @@ -154,16 +154,16 @@ private static async Task GetNoteById(IRepository noteRepository, { return TypedResults.NotFound(); } - /* - var response = new ResponseDTO + + var responseObject = new ResponseDTO { Status = "success", - Data = NoteFactory.GetNoteDTO(note) + Data = NoteFactory.GetNoteDTO(response) }; - */ + var result = new GetNoteDTO(response); - return TypedResults.Ok(result); + return TypedResults.Ok(responseObject); } [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/exercise.wwwapi/Endpoints/UserEndpoints.cs b/exercise.wwwapi/Endpoints/UserEndpoints.cs index e1defab..6cd8de5 100644 --- a/exercise.wwwapi/Endpoints/UserEndpoints.cs +++ b/exercise.wwwapi/Endpoints/UserEndpoints.cs @@ -49,23 +49,49 @@ private static async Task GetUsers(IRepository userRepository, st if (!string.IsNullOrWhiteSpace(searchTerm)) { + var terms = searchTerm + .Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(t => t.ToLowerInvariant()) + .ToArray(); + results = results.Where(u => - $"{u.FirstName} {u.LastName}".Contains(searchTerm, StringComparison.OrdinalIgnoreCase)) - .ToList(); + { + var first = u.FirstName?.ToLowerInvariant() ?? ""; + var last = u.LastName?.ToLowerInvariant() ?? ""; + var full = (first + " " + last).Trim(); + + // All search terms must be present in either first, last, or full name (order-insensitive) + return terms.All(term => + first.Contains(term) || + last.Contains(term) || + full.Contains(term) + ); + }).ToList(); } var userRole = claimPrincipal.Role(); var authorizedAsTeacher = AuthorizeTeacher(claimPrincipal); - + var userData = new UsersSuccessDTO + { + Users = results.Select(user => authorizedAsTeacher + ? UserFactory.GetUserDTO(user, PrivilegeLevel.Teacher) + : UserFactory.GetUserDTO(user, PrivilegeLevel.Student)) + .ToList() + }; + + var response = new ResponseDTO + { + Status = "success", + Data = userData + }; - var response = results.Select(u => new UserDTO(u)).ToList(); return TypedResults.Ok(response); } [ProducesResponseType(StatusCodes.Status200OK)] - private static async Task GetUsersByCohort(IRepository repository, int course_id, ClaimsPrincipal claimsPrincipal) + private static async Task GetUsersByCohort(IRepository repository, int cohort_id, ClaimsPrincipal claimsPrincipal) { - var response = await repository.GetByIdWithIncludes(a => a.Include(p => p.CohortCourses).ThenInclude(b => b.UserCCs).ThenInclude(a => a.User).ThenInclude(u => u.Notes), course_id); + var response = await repository.GetByIdWithIncludes(a => a.Include(p => p.CohortCourses).ThenInclude(b => b.UserCCs).ThenInclude(a => a.User).ThenInclude(u => u.Notes), cohort_id); var results = response.CohortCourses.SelectMany(a => a.UserCCs).Select(a => a.User).ToList(); var dto_results = results.Select(a => new UserDTO(a)); @@ -102,13 +128,23 @@ private static async Task Register(PostUserDTO request, IRepository { Status = "fail", Data = failureDto }; + var failResponse = new ResponseDTO { Status = "conflict", Data = failureDto }; return Results.BadRequest(failResponse); } - + var response = await userRepository.GetWithIncludes(x => x.Where(u => u.Email == request.Email)); // uses where-statement to filter data before fetching + if (response.Count == 1) + { + return Results.Conflict(new Payload + { + Status = "fail", + Data = "User already exists" + }); + } + + + - var passwordHash = BCrypt.Net.BCrypt.HashPassword(request.Password); @@ -130,7 +166,7 @@ private static async Task Register(PostUserDTO request, IRepository + var responseObject = new ResponseDTO { Status = "success", Data = new RegisterSuccessDTO @@ -149,8 +185,9 @@ private static async Task Register(PostUserDTO request, IRepository Date: Mon, 22 Sep 2025 09:05:30 +0200 Subject: [PATCH 2/3] Update UserEndpoints.cs --- exercise.wwwapi/Endpoints/UserEndpoints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercise.wwwapi/Endpoints/UserEndpoints.cs b/exercise.wwwapi/Endpoints/UserEndpoints.cs index 6cd8de5..a23e69f 100644 --- a/exercise.wwwapi/Endpoints/UserEndpoints.cs +++ b/exercise.wwwapi/Endpoints/UserEndpoints.cs @@ -56,7 +56,7 @@ private static async Task GetUsers(IRepository userRepository, st results = results.Where(u => { - var first = u.FirstName?.ToLowerInvariant() ?? ""; + var first = u.FirstName?.ToLowerInvariant() ?? ""; //if teacher loads students, also load notes for students. var last = u.LastName?.ToLowerInvariant() ?? ""; var full = (first + " " + last).Trim(); From a39bcc9d9df01839ef21f5acac8a564151f21b41 Mon Sep 17 00:00:00 2001 From: johanreitan Date: Mon, 22 Sep 2025 09:06:15 +0200 Subject: [PATCH 3/3] Improve user filtering in UserEndpoints Refactor user filtering logic to clarify intent. --- exercise.wwwapi/Endpoints/UserEndpoints.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercise.wwwapi/Endpoints/UserEndpoints.cs b/exercise.wwwapi/Endpoints/UserEndpoints.cs index a23e69f..8009be1 100644 --- a/exercise.wwwapi/Endpoints/UserEndpoints.cs +++ b/exercise.wwwapi/Endpoints/UserEndpoints.cs @@ -56,7 +56,7 @@ private static async Task GetUsers(IRepository userRepository, st results = results.Where(u => { - var first = u.FirstName?.ToLowerInvariant() ?? ""; //if teacher loads students, also load notes for students. + var first = u.FirstName?.ToLowerInvariant() ?? ""; var last = u.LastName?.ToLowerInvariant() ?? ""; var full = (first + " " + last).Trim(); @@ -74,7 +74,7 @@ private static async Task GetUsers(IRepository userRepository, st var userData = new UsersSuccessDTO { Users = results.Select(user => authorizedAsTeacher - ? UserFactory.GetUserDTO(user, PrivilegeLevel.Teacher) + ? UserFactory.GetUserDTO(user, PrivilegeLevel.Teacher) //if teacher loads students, also load notes for students. : UserFactory.GetUserDTO(user, PrivilegeLevel.Student)) .ToList() };