diff --git a/exercise.tests/IntegrationTests/BaseIntegrationTest.cs b/exercise.tests/IntegrationTests/BaseIntegrationTest.cs new file mode 100644 index 0000000..ed38d6b --- /dev/null +++ b/exercise.tests/IntegrationTests/BaseIntegrationTest.cs @@ -0,0 +1,141 @@ +using exercise.wwwapi.DTOs.Login; +using Microsoft.AspNetCore.Mvc.Testing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; + +namespace exercise.tests.IntegrationTests +{ + public static class HttpResponseExtensions + { + public static async Task ReadJsonAsync(this HttpResponseMessage response) + { + var contentString = await response.Content.ReadAsStringAsync(); + return string.IsNullOrWhiteSpace(contentString) + ? null + : JsonNode.Parse(contentString); + } + } + public abstract class BaseIntegrationTest + { + protected WebApplicationFactory _factory; + protected HttpClient _client; + + [SetUp] + public void BaseSetup() + { + _factory = new WebApplicationFactory(); + _client = _factory.CreateClient(); + } + + [TearDown] + public void BaseTearDown() + { + _client?.Dispose(); + _factory?.Dispose(); + } + + + // IF CHANGES DONE TO THE SEEDER, RECHECK THESE USERS + // SQL QUERY TO CHECK IF USER HAS ATLEAST ONE POST AND ATLEAST ONE COMMENT + /* + SELECT + u.id AS user_id, + u.email, + u.role, + ARRAY_AGG(DISTINCT p.id) AS post_ids, + ARRAY_AGG(DISTINCT c.id) AS comment_ids + FROM users u + JOIN posts p ON u.id = p.userid + JOIN comments c ON u.id = c.userid + WHERE u.role = 'student' + GROUP BY u.id, u.email, u.role, u.passwordhash; + */ + protected const string TeacherEmail = "anna.gruber160@example.com"; // has post id 34,35 and comment id 37 + protected const string TeacherPassword = "Neidintulling!l33t"; + protected const int TeacherPostID = 34; + protected const int TeacherCommentID = 37; + + protected const string StudentEmail1 = "jan.larsen9@example.com"; //id 232, has post id 57, 58 and comment id 2 + protected const string StudentPassword1 = "SuperHash!4"; + protected const int StudentPostID1 = 57; + protected const int StudentCommentID1 = 2; + + protected const string StudentEmail2 = "timian.saar85@example.com"; //id 85, has post id 36 and comment id 3 + protected const string StudentPassword2 = "Neidintulling!l33t"; + protected const int StudentPostID2 = 36; + protected const int StudentCommentID2 = 3; + + + protected async Task LoginAndGetToken(string email, string password, bool success = true) + { + var loginBody = new LoginRequestDTO { email = email, password = password }; + var loginRequestBody = new StringContent( + JsonSerializer.Serialize(loginBody), + Encoding.UTF8, + "application/json" + ); + + var loginResponse = await _client.PostAsync("/login", loginRequestBody); + //loginResponse.EnsureSuccessStatusCode(); + + var loginContent = await loginResponse.Content.ReadAsStringAsync(); + var loginMessage = JsonNode.Parse(loginContent); + string? token = loginMessage?["data"]?["token"]?.GetValue(); + + Assert.That(token, success ? Is.Not.Null : Is.Null); + + return token; + } + + // Helper methods + protected async Task SendAuthenticatedRequestAsync( + HttpMethod method, + string endpoint, + string token, + T? body = default + ) + { + var request = new HttpRequestMessage(method, endpoint); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + + if (body != null) + { + var json = JsonSerializer.Serialize(body); + request.Content = new StringContent(json, Encoding.UTF8, "application/json"); + } + + return await _client.SendAsync(request); + } + + protected async Task SendAuthenticatedGetAsync(string endpoint, string token) + { + return await SendAuthenticatedRequestAsync(HttpMethod.Get, endpoint, token); + } + + protected async Task SendAuthenticatedPostAsync(string endpoint, string token, T body) + { + return await SendAuthenticatedRequestAsync(HttpMethod.Post, endpoint, token, body); + } + protected async Task SendAuthenticatedPatchAsync(string endpoint, string token, T body) + { + return await SendAuthenticatedRequestAsync(HttpMethod.Patch, endpoint, token, body); + } + + protected async Task SendAuthenticatedPutAsync(string endpoint, string token, T body) + { + return await SendAuthenticatedRequestAsync(HttpMethod.Put, endpoint, token, body); + } + + protected async Task SendAuthenticatedDeleteAsync(string endpoint, string token) + { + return await SendAuthenticatedRequestAsync(HttpMethod.Delete, endpoint, token); + } + + } +} diff --git a/exercise.tests/IntegrationTests/EndpointStatusTest.cs b/exercise.tests/IntegrationTests/EndpointStatusTest.cs index 022d3b2..f022909 100644 --- a/exercise.tests/IntegrationTests/EndpointStatusTest.cs +++ b/exercise.tests/IntegrationTests/EndpointStatusTest.cs @@ -10,52 +10,25 @@ namespace exercise.tests.IntegrationTests /// Integration smoke tests that ensure unexpected endpoints return consistent error responses. /// [TestFixture] - public class EndpointStatusTest + public class EndpointStatusTest : BaseIntegrationTest { - private WebApplicationFactory _factory; - private HttpClient _client; - - [SetUp] - public void SetUp() - { - // Arrange - _factory = new WebApplicationFactory(); - _client = _factory.CreateClient(); - } - - [TearDown] - public void TearDown() - { - _client.Dispose(); - _factory.Dispose(); - } - /// /// Verifies POST requests to unknown routes return a 404 with the canonical payload. /// [Test] public async Task PostCheckInvalidEndpoint() { + var body = new { - email = $"myemailVery@gmail.com", - password = "someR21!password" + email = TeacherEmail, + password = TeacherPassword }; - var json = JsonSerializer.Serialize(body); - var requestBody = new StringContent(json, Encoding.UTF8, "application/json"); - - // Act - var response = await _client.PostAsync("/okokoNONEVER", requestBody); - var contentString = await response.Content.ReadAsStringAsync(); + string token = await LoginAndGetToken(TeacherEmail, TeacherPassword); + HttpResponseMessage response = await SendAuthenticatedPostAsync("/NOthisWouldNEVERHAPPENItskindaweird", token, body); - JsonNode? message = null; - if (!string.IsNullOrWhiteSpace(contentString)) - { - message = JsonNode.Parse(contentString); - } - - var messageText = message?["message"]?.GetValue(); + var message = await response.ReadJsonAsync(); Console.WriteLine("Message: " + message); // Assert @@ -69,19 +42,13 @@ public async Task PostCheckInvalidEndpoint() [Test] public async Task GetCheckInvalidEndpoint() { - + string token = await LoginAndGetToken(TeacherEmail, TeacherPassword); // Act - var response = await _client.GetAsync("/okokoNONEVER"); + HttpResponseMessage response = await SendAuthenticatedGetAsync("/NOthisWouldNEVERHAPPENItskindaweird", token); - var contentString = await response.Content.ReadAsStringAsync(); - JsonNode? message = null; - if (!string.IsNullOrWhiteSpace(contentString)) - { - message = JsonNode.Parse(contentString); - } + var message = await response.ReadJsonAsync(); - var messageText = message?["message"]?.GetValue(); Console.WriteLine("Message: " + message); // Assert diff --git a/exercise.tests/IntegrationTests/PostTests.cs b/exercise.tests/IntegrationTests/PostTests.cs index 30d6f6c..48e7e8f 100644 --- a/exercise.tests/IntegrationTests/PostTests.cs +++ b/exercise.tests/IntegrationTests/PostTests.cs @@ -1,6 +1,7 @@ using exercise.wwwapi.DTOs.Login; using exercise.wwwapi.DTOs.Posts; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using System.Net; using System.Net.Http.Headers; using System.Text; @@ -13,106 +14,8 @@ namespace exercise.tests.IntegrationTests /// Integration tests covering the lifecycle of posts via the public API endpoints. /// [TestFixture] - public class PostTests + public class PostTests : BaseIntegrationTest { - private WebApplicationFactory _factory; - private HttpClient _client; - - [SetUp] - public void SetUp() - { - // Arrange - _factory = new WebApplicationFactory(); - _client = _factory.CreateClient(); - } - - [TearDown] - public void TearDown() - { - _client.Dispose(); - _factory.Dispose(); - } - - private const string TeacherEmail = "anna.gruber160@example.com"; // has post id 34,35 and comment id 37 - private const string TeacherPassword = "Neidintulling!l33t"; - private const int TeacherPostID = 34; - private const int TeacherCommentID = 37; - - private const string StudentEmail1 = "jan.larsen9@example.com"; //id 232, has post id 57, 58 and comment id 2 - private const string StudentPassword1 = "SuperHash!4"; - private const int StudentPostID1 = 57; - private const int StudentCommentID1 = 2; - - private const string StudentEmail2 = "timian.saar85@example.com"; //id 85, has post id 36 and comment id 3 - private const string StudentPassword2 = "Neidintulling!l33t"; - private const int StudentPostID2 = 36; - private const int StudentCommentID2 = 3; - - private async Task LoginAndGetToken(string email, string password, bool success = true) - { - var loginBody = new LoginRequestDTO { email = email, password = password }; - var loginRequestBody = new StringContent( - JsonSerializer.Serialize(loginBody), - Encoding.UTF8, - "application/json" - ); - - var loginResponse = await _client.PostAsync("/login", loginRequestBody); - //loginResponse.EnsureSuccessStatusCode(); - - var loginContent = await loginResponse.Content.ReadAsStringAsync(); - var loginMessage = JsonNode.Parse(loginContent); - string? token = loginMessage?["data"]?["token"]?.GetValue(); - - Assert.That(token, success ? Is.Not.Null : Is.Null); - - return token; - } - - // Helper methods - private async Task SendAuthenticatedRequestAsync( - HttpMethod method, - string endpoint, - string token, - T body = default - ) - { - var request = new HttpRequestMessage(method, endpoint); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); - - if (body != null) - { - var json = JsonSerializer.Serialize(body); - request.Content = new StringContent(json, Encoding.UTF8, "application/json"); - } - - return await _client.SendAsync(request); - } - - private async Task SendAuthenticatedGetAsync(string endpoint, string token) - { - return await SendAuthenticatedRequestAsync(HttpMethod.Get, endpoint, token); - } - - private async Task SendAuthenticatedPostAsync(string endpoint, string token, T body) - { - return await SendAuthenticatedRequestAsync(HttpMethod.Post, endpoint, token, body); - } - private async Task SendAuthenticatedPatchAsync(string endpoint, string token, T body) - { - return await SendAuthenticatedRequestAsync(HttpMethod.Patch, endpoint, token, body); - } - - private async Task SendAuthenticatedPutAsync(string endpoint, string token, T body) - { - return await SendAuthenticatedRequestAsync(HttpMethod.Put, endpoint, token, body); - } - - private async Task SendAuthenticatedDeleteAsync(string endpoint, string token) - { - return await SendAuthenticatedRequestAsync(HttpMethod.Delete, endpoint, token); - } - [Test] public async Task GetAllPosts() { @@ -120,50 +23,60 @@ public async Task GetAllPosts() string token = await LoginAndGetToken("oyvind.perez1@example.com", "SuperHash!4"); //act HttpResponseMessage response = await SendAuthenticatedGetAsync($"/posts", token); + JsonNode? message = await response.ReadJsonAsync(); - var contentString = await response.Content.ReadAsStringAsync(); - var message = string.IsNullOrWhiteSpace(contentString) ? null : JsonNode.Parse(contentString); - - // Assert status - Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); - - Assert.That(message, Is.Not.Null); - Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Success")); - + using (Assert.EnterMultipleScope()) + { + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + Assert.That(message, Is.Not.Null); + Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Success")); + } var data = message?["data"]?.AsArray(); Assert.That(data, Is.Not.Null); - Assert.That(data!.Count, Is.GreaterThan(0)); + Assert.That(data?.Count, Is.GreaterThan(0)); + Console.WriteLine("Message: " + message); // Check first post for structure - var post = data!.First(); - Assert.That(post["id"]?.GetValue(), Is.GreaterThan(0)); - Assert.That(post["content"]?.GetValue(), Is.Not.Null); - Assert.That(post["numLikes"]?.GetValue(), Is.Not.Null); - Assert.That(post["createdAt"], Is.Not.Null); + var post = data?.First(); + using (Assert.EnterMultipleScope()) + { + Assert.That(post?["id"]?.GetValue(), Is.GreaterThan(0)); + Assert.That(post["content"]?.GetValue(), Is.Not.Null); + Assert.That(post["numLikes"]?.GetValue(), Is.Not.Null); + Assert.That(post["createdAt"], Is.Not.Null); + } // Check nested user var user = post["user"]; Assert.That(user, Is.Not.Null); - Assert.That(user!["id"]?.GetValue(), Is.GreaterThan(0)); - Assert.That(user["firstName"]?.GetValue(), Is.Not.Null); - Assert.That(user["lastName"]?.GetValue(), Is.Not.Null); - Assert.That(user["photo"], Is.Not.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(user?["id"]?.GetValue(), Is.GreaterThan(0)); + Assert.That(user["firstName"]?.GetValue(), Is.Not.Null); + Assert.That(user["lastName"]?.GetValue(), Is.Not.Null); + Assert.That(user["photo"], Is.Not.Null); + } // Check nested comments var comments = post["comments"]?.AsArray(); Assert.That(comments, Is.Not.Null); // Check comments - if (comments!.Count > 0) + if (comments?.Count > 0) { - var comment = comments!.First(); - Assert.That(comment["content"]?.GetValue(), Is.Not.Null); - Assert.That(comment["id"]?.GetValue(), Is.GreaterThan(0)); + var comment = comments?.First(); + using (Assert.EnterMultipleScope()) + { + Assert.That(comment?["content"]?.GetValue(), Is.Not.Null); + Assert.That(comment?["id"]?.GetValue(), Is.GreaterThan(0)); + } } } + + [TestCase(TeacherEmail, TeacherPassword)] [TestCase(StudentEmail1, StudentPassword1)] public async Task SuccessfulCreatePostStatus(string userEmail, string password) @@ -173,9 +86,8 @@ public async Task SuccessfulCreatePostStatus(string userEmail, string password) //Act HttpResponseMessage response = await SendAuthenticatedPostAsync("/posts", token, body); + JsonNode? message = await response.ReadJsonAsync(); - var contentString = await response.Content.ReadAsStringAsync(); - var message = string.IsNullOrWhiteSpace(contentString) ? null : JsonNode.Parse(contentString); Console.WriteLine("Message: " + message); // Assert @@ -191,9 +103,7 @@ public async Task SuccessfulCreatePostMessage(string userEmail, string password) //Act HttpResponseMessage response = await SendAuthenticatedPostAsync("/posts", token, body); - - var contentString = await response.Content.ReadAsStringAsync(); - var message = string.IsNullOrWhiteSpace(contentString) ? null : JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); Console.WriteLine("Message: " + message); // Assert @@ -204,21 +114,25 @@ public async Task SuccessfulCreatePostMessage(string userEmail, string password) var data = message["data"]; Assert.That(data, Is.Not.Null); - Assert.That(data?["id"]?.GetValue(), Is.GreaterThan(0)); - Assert.That(data["content"]?.GetValue(), Is.EqualTo("Some content")); - Assert.That(data["numLikes"]?.GetValue(), Is.EqualTo(0)); - Assert.That(data["comments"]?.AsArray().Count, Is.EqualTo(0)); + using (Assert.EnterMultipleScope()) + { + Assert.That(data?["id"]?.GetValue(), Is.GreaterThan(0)); + Assert.That(data["content"]?.GetValue(), Is.EqualTo("Some content")); + Assert.That(data["numLikes"]?.GetValue(), Is.EqualTo(0)); + Assert.That(data["comments"]?.AsArray().Count, Is.EqualTo(0)); + } // Check User object inside data var user = data["user"]; Assert.That(user, Is.Not.Null); - //Assert.That(user!["id"]?.GetValue(), Is.EqualTo(userid)); - Assert.That(user["firstName"]?.GetValue(), Is.Not.Null); - Assert.That(user["lastName"]?.GetValue(), Is.Not.Null); - Assert.That(user["photo"]?.GetValue(), Is.Not.Null); - - Assert.That(message["timestamp"], Is.Not.Null); - Assert.That(data["createdAt"], Is.Not.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(user["firstName"]?.GetValue(), Is.Not.Null); + Assert.That(user["lastName"]?.GetValue(), Is.Not.Null); + Assert.That(user["photo"]?.GetValue(), Is.Not.Null); + Assert.That(message["timestamp"], Is.Not.Null); + Assert.That(data["createdAt"], Is.Not.Null); + } } [TestCase(TeacherEmail, TeacherPassword, "", HttpStatusCode.BadRequest)] @@ -232,9 +146,8 @@ public async Task FailedCreatePostStatus(string userEmail, string password, stri //Act HttpResponseMessage response = await SendAuthenticatedPostAsync("/posts", token, body); + JsonNode? message = await response.ReadJsonAsync(); - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); Console.WriteLine($"Message: {message}| "); // Assert Assert.That(response.StatusCode, Is.EqualTo(expected)); @@ -251,16 +164,18 @@ public async Task FailedCreatePostMessage(string userEmail, string password, str //Act HttpResponseMessage response = await SendAuthenticatedPostAsync("/posts", token, body); + JsonNode? message = await response.ReadJsonAsync(); - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); Console.WriteLine($"Message: {message}| "); // Assert JSON structure Assert.That(message, Is.Not.Null); - Assert.That(message?["message"]?.GetValue(), Is.EqualTo(expectedmessage)); - Assert.That(message["data"], Is.Null); - Assert.That(message["timestamp"], Is.Not.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(message?["message"]?.GetValue(), Is.EqualTo(expectedmessage)); + Assert.That(message["data"], Is.Null); + Assert.That(message["timestamp"], Is.Not.Null); + } } [TestCase(TeacherEmail, TeacherPassword)] @@ -274,24 +189,23 @@ public async Task DeletePostById_SuccessAndNotFound(string userEmail, string pas //ACt HttpResponseMessage createResponse = await SendAuthenticatedPostAsync($"/posts", token, body); + JsonNode? createMessage = await createResponse.ReadJsonAsync(); - var createContent = await createResponse.Content.ReadAsStringAsync(); - var createMessage = JsonNode.Parse(createContent); + int? createdPostId = createMessage?["data"]?["id"]?.GetValue(); - int createdPostId = createMessage!["data"]!["id"]!.GetValue(); + Assert.That(createdPostId, Is.Not.Null); // Act, delete the post HttpResponseMessage deleteResponse = await SendAuthenticatedDeleteAsync($"/posts/{createdPostId}", token); - - var deleteContent = await deleteResponse.Content.ReadAsStringAsync(); + JsonNode? deleteContent = await deleteResponse.ReadJsonAsync(); // Assert delete success Assert.That(deleteResponse.StatusCode, Is.EqualTo(HttpStatusCode.OK)); // Act again, try deleting same post (should now be gone) HttpResponseMessage deleteAgainResponse = await SendAuthenticatedDeleteAsync($"/posts/{createdPostId}", token); - var deleteAgainContent = await deleteAgainResponse.Content.ReadAsStringAsync(); - var deleteAgainMessage = JsonNode.Parse(deleteAgainContent); + JsonNode? deleteAgainMessage = await deleteAgainResponse.ReadJsonAsync(); + // Assert not found Assert.That(deleteAgainResponse.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); @@ -320,11 +234,8 @@ public async Task UpdatePostById(string userEmail, string password, int postId, // Arrange UpdatePostDTO body = new UpdatePostDTO { Content = newContent }; HttpResponseMessage response = await SendAuthenticatedPatchAsync($"/posts/{postId}", token, body); + JsonNode? message = await response.ReadJsonAsync(); - var contentString = await response.Content.ReadAsStringAsync(); - - JsonNode? message = null; - if (!string.IsNullOrWhiteSpace(contentString)) message = JsonNode.Parse(contentString); Console.WriteLine("Message: " + message); @@ -335,24 +246,36 @@ public async Task UpdatePostById(string userEmail, string password, int postId, { var data = message?["data"]; Assert.That(data, Is.Not.Null); - Assert.That(data?["id"]?.GetValue(), Is.EqualTo(postId)); - Assert.That(data?["content"]?.GetValue(), Is.EqualTo(newContent)); - Assert.That(data?["updatedAt"], Is.Not.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(data?["id"]?.GetValue(), Is.EqualTo(postId)); + Assert.That(data?["content"]?.GetValue(), Is.EqualTo(newContent)); + Assert.That(data?["updatedAt"], Is.Not.Null); + } } else if (expected == HttpStatusCode.NotFound) { - Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Post not found")); - Assert.That(message?["data"], Is.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Post not found")); + Assert.That(message?["data"], Is.Null); + } } else if (expected == HttpStatusCode.BadRequest) { - Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Content cannot be empty")); - Assert.That(message?["data"], Is.Null); + Assert.Multiple(() => + { + Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Content cannot be empty")); + Assert.That(message?["data"], Is.Null); + }); } else if (expected == HttpStatusCode.Forbidden) { - Assert.That(message?["message"]?.GetValue(), Is.EqualTo("You are not authorized to edit this post.")); - Assert.That(message?["data"], Is.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(message?["message"]?.GetValue(), Is.EqualTo("You are not authorized to edit this post.")); + Assert.That(message?["data"], Is.Null); + } } } @@ -367,36 +290,35 @@ public async Task AddCommentToPost_Success(string userEmail, string password) // Act HttpResponseMessage response = await SendAuthenticatedPostAsync("/posts", token, body); + JsonNode? message = await response.ReadJsonAsync(); - var message = JsonNode.Parse(await response.Content.ReadAsStringAsync()); - - int createdPostId = message!["data"]!["id"]!.GetValue(); + int? createdPostId = message?["data"]?["id"]?.GetValue(); Console.WriteLine($"createdPostId:{createdPostId}."); + Assert.That(createdPostId, Is.Not.Null); + // Arrange CreatePostCommentDTO commentbody = new CreatePostCommentDTO { Content = "Some new comment" }; - var commentjson = JsonSerializer.Serialize(commentbody); - var requestComment = new StringContent(commentjson, Encoding.UTF8, "application/json"); - - var commentrequest = new HttpRequestMessage(HttpMethod.Post, $"/posts/{createdPostId}/comments") - { - Content = requestComment - }; - commentrequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act - var commentresponse = await _client.SendAsync(commentrequest); + HttpResponseMessage commentresponse = await SendAuthenticatedPostAsync($"/posts/{createdPostId}/comments", token, commentbody); + JsonNode? commentmessage = await commentresponse.ReadJsonAsync(); - var commentmessage = JsonNode.Parse(await commentresponse.Content.ReadAsStringAsync()); Console.WriteLine(commentmessage); - // Assert - Assert.That(commentresponse.StatusCode, Is.EqualTo(HttpStatusCode.Created)); - Assert.That(commentmessage?["message"]?.GetValue(), Is.EqualTo("Success")); + Assert.Multiple(() => + { + // Assert + Assert.That(commentresponse.StatusCode, Is.EqualTo(HttpStatusCode.Created)); + Assert.That(commentmessage?["message"]?.GetValue(), Is.EqualTo("Success")); + }); var data = commentmessage?["data"]; Assert.That(data, Is.Not.Null); - Assert.That(data?["id"]?.GetValue(), Is.GreaterThan(0)); - Assert.That(data?["content"]?.GetValue(), Is.EqualTo(commentbody.Content)); + using (Assert.EnterMultipleScope()) + { + Assert.That(data?["id"]?.GetValue(), Is.GreaterThan(0)); + Assert.That(data?["content"]?.GetValue(), Is.EqualTo(commentbody.Content)); + } var user = data?["user"]; Assert.That(user, Is.Not.Null); @@ -415,13 +337,16 @@ public async Task AddCommentToPost_Failure(int postId, string content, HttpStatu HttpResponseMessage response = await SendAuthenticatedPostAsync($"/posts/{postId}/comments", token, newComment); // Act - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); + Console.WriteLine(message); - // Assert - Assert.That(response.StatusCode, Is.EqualTo(expectedStatus)); - Assert.That(message?["message"]?.GetValue(), Is.EqualTo(expectedMessage)); + using (Assert.EnterMultipleScope()) + { + // Assert + Assert.That(response.StatusCode, Is.EqualTo(expectedStatus)); + Assert.That(message?["message"]?.GetValue(), Is.EqualTo(expectedMessage)); + } } @@ -436,11 +361,10 @@ public async Task GetCommentsForPost_Success() int postId = 5; // Act - HttpResponseMessage response = await SendAuthenticatedGetAsync($"/posts/{postId}/comments", token); + HttpResponseMessage response = await SendAuthenticatedGetAsync($"/posts/{postId}/comments", token); // Assert - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); Console.WriteLine(message); @@ -475,24 +399,31 @@ public async Task EditComment_WithVariousInputs(string userEmail, string passwor HttpResponseMessage postRes = await SendAuthenticatedPostAsync("/posts", token, postDTO); Assert.That(postRes.StatusCode, Is.EqualTo(HttpStatusCode.Created)); - var createContent = await postRes.Content.ReadAsStringAsync(); - var createMessage = JsonNode.Parse(createContent); + JsonNode? createMessage = await postRes.ReadJsonAsync(); + + + int? createdPostId = createMessage?["data"]?["id"]?.GetValue(); - int createdPostId = createMessage!["data"]!["id"]!.GetValue(); + Assert.That(createdPostId, Is.Not.Null); // Arrange: Create comment HttpResponseMessage comCreationRes = await SendAuthenticatedPostAsync($"/posts/{createdPostId}/comments", token, postDTO); + Assert.That(comCreationRes.StatusCode, Is.EqualTo(HttpStatusCode.Created)); - var comCreationContent = await comCreationRes.Content.ReadAsStringAsync(); - var comCreationMessage = JsonNode.Parse(comCreationContent); - int createdCommentId = comCreationMessage!["data"]!["id"]!.GetValue(); + JsonNode? comCreationMessage = await comCreationRes.ReadJsonAsync(); + + int? createdCommentId = comCreationMessage?["data"]?["id"]?.GetValue(); Console.WriteLine($"Created comment id: {createdCommentId}|"); + Assert.That(createdCommentId, Is.Not.Null); + + CreatePostCommentDTO updateDTO = new () { Content = newContent }; // Act HttpResponseMessage response = await SendAuthenticatedPatchAsync($"/comments/{createdCommentId}", token, updateDTO); - var message = JsonNode.Parse(await response.Content.ReadAsStringAsync()); + JsonNode? message = await response.ReadJsonAsync(); + Console.WriteLine(message); // Assert @@ -500,11 +431,13 @@ public async Task EditComment_WithVariousInputs(string userEmail, string passwor if (expectedStatus == HttpStatusCode.OK) { - var updateContentString = await response.Content.ReadAsStringAsync(); - var updateMessage = JsonNode.Parse(updateContentString); + JsonNode? updateMessage = await response.ReadJsonAsync(); var data = updateMessage?["data"]; - Assert.That(data?["content"]?.GetValue(), Is.EqualTo(newContent)); - Assert.That(data?["updatedAt"], Is.Not.Null); + using (Assert.EnterMultipleScope()) + { + Assert.That(data?["content"]?.GetValue(), Is.EqualTo(newContent)); + Assert.That(data?["updatedAt"], Is.Not.Null); + } } } @@ -543,19 +476,25 @@ public async Task DeleteComment_SuccessAndThenNotFound() // Act 1: Create the posts HttpResponseMessage postres = await SendAuthenticatedPostAsync("/posts", studentToken, postDTO); - var postMessage = JsonNode.Parse(await postres.Content.ReadAsStringAsync()); - int postId = postMessage!["data"]!["id"]!.GetValue(); + JsonNode? postMessage = await postres.ReadJsonAsync(); + int? postId = postMessage?["data"]?["id"]?.GetValue(); HttpResponseMessage comres1 = await SendAuthenticatedPostAsync($"/posts/{postId}/comments", studentToken, postDTO); - var comMessage1 = JsonNode.Parse(await comres1.Content.ReadAsStringAsync()); - int commentId1 = comMessage1!["data"]!["id"]!.GetValue(); + JsonNode? comMessage1 = await comres1.ReadJsonAsync(); + int? commentId1 = comMessage1?["data"]?["id"]?.GetValue(); HttpResponseMessage comres2 = await SendAuthenticatedPostAsync($"/posts/{postId}/comments", studentToken, postDTO); - var comMessage2 = JsonNode.Parse(await comres2.Content.ReadAsStringAsync()); - int commentId2 = comMessage2!["data"]!["id"]!.GetValue(); + JsonNode? comMessage2 = await comres2.ReadJsonAsync(); + int? commentId2 = comMessage2?["data"]?["id"]?.GetValue(); Console.WriteLine($"PostId: {postId} | CommentId1: {commentId1} | CommentId2: {commentId2} |"); + using (Assert.EnterMultipleScope()) + { + Assert.That(postId, Is.Not.Null); + Assert.That(commentId1, Is.Not.Null); + Assert.That(commentId2, Is.Not.Null); + } // Act 2: Student deletes a post and tries to delete the same HttpResponseMessage studDel1 = await SendAuthenticatedDeleteAsync($"/comments/{commentId1}", studentToken); @@ -584,9 +523,7 @@ public async Task GetPostsByUser_Success() HttpResponseMessage response = await SendAuthenticatedGetAsync($"/posts/user/{userId}", token); // Assert - - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); Console.WriteLine(message); // Assert status Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); @@ -595,10 +532,10 @@ public async Task GetPostsByUser_Success() Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Success")); var data = message?["data"]?.AsArray(); Assert.That(data, Is.Not.Null); - Assert.That(data!.Count, Is.GreaterThan(0), "Expected user 1 to have at least one post."); + Assert.That(data?.Count, Is.GreaterThan(0), "Expected user 1 to have at least one post."); // Assert that ALL posts in the response belong to the correct user - foreach (var post in data!) + foreach (var post in data) { Assert.That(post?["user"]?["id"]?.GetValue(), Is.EqualTo(userId)); } @@ -621,8 +558,7 @@ public async Task GetPostsByUser_NotFound() //assert - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); // Assert Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); @@ -644,8 +580,7 @@ public async Task GetCommentsByUser_Success() HttpResponseMessage response = await SendAuthenticatedGetAsync($"/comments/user/{userId}", token); // Assert - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); Console.WriteLine(message); // Assert status @@ -654,10 +589,10 @@ public async Task GetCommentsByUser_Success() Assert.That(message?["message"]?.GetValue(), Is.EqualTo("Success")); var data = message?["data"]?.AsArray(); Assert.That(data, Is.Not.Null); - Assert.That(data!.Count, Is.GreaterThan(0), "Expected user 1 to have at least one comment."); + Assert.That(data?.Count, Is.GreaterThan(0), "Expected user 1 to have at least one comment."); // Assert that ALL comments in the response belong to the correct user - foreach (var comment in data!) + foreach (var comment in data) { Assert.That(comment?["user"]?["id"]?.GetValue(), Is.EqualTo(userId)); } @@ -679,8 +614,7 @@ public async Task GetCommentsByUser_NotFound() // Assert - var contentString = await response.Content.ReadAsStringAsync(); - var message = JsonNode.Parse(contentString); + JsonNode? message = await response.ReadJsonAsync(); // Assert Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); diff --git a/exercise.wwwapi/Endpoints/PostEndpoints.cs b/exercise.wwwapi/Endpoints/PostEndpoints.cs index 90b2c6b..63401ae 100644 --- a/exercise.wwwapi/Endpoints/PostEndpoints.cs +++ b/exercise.wwwapi/Endpoints/PostEndpoints.cs @@ -48,9 +48,9 @@ CreatePostDTO request ) { int? userid = user.UserRealId(); + if ( userid == null ) return Results.NotFound(new ResponseDTO { Message = "UserId missing" }); User? dbUser = userservice.GetById(userid); - if (user == null) - return Results.NotFound(new ResponseDTO { Message = "Invalid userID" }); + if (dbUser == null) return Results.NotFound(new ResponseDTO { Message = "Invalid userID" }); if (string.IsNullOrWhiteSpace(request.Content)) return Results.BadRequest(new ResponseDTO { Message = "Content cannot be empty" }); @@ -170,7 +170,9 @@ private static IResult AddCommentToPost( } // Check if user exists - var dbUser = userService.GetById(user.UserRealId()); + int? userid = user.UserRealId(); + if (userid == null) return Results.NotFound(new ResponseDTO { Message = "UserId missing" }); + var dbUser = userService.GetById(userid); if (dbUser == null) { return TypedResults.NotFound(new ResponseDTO { Message = "User not found." }); diff --git a/exercise.wwwapi/Repository/IRepository.cs b/exercise.wwwapi/Repository/IRepository.cs index 4521d08..7f7baf5 100644 --- a/exercise.wwwapi/Repository/IRepository.cs +++ b/exercise.wwwapi/Repository/IRepository.cs @@ -12,7 +12,7 @@ public interface IRepository where T : class IEnumerable GetAllFiltered(Expression> filter); IEnumerable GetWithIncludes(Func, IQueryable> includeQuery); T? GetById(int id, Func, IQueryable> includeQuery); - T GetById(object id); + T? GetById(object? id); void Insert(T obj); void Update(T obj); void Delete(object id); diff --git a/exercise.wwwapi/Repository/Repository.cs b/exercise.wwwapi/Repository/Repository.cs index fc83ca6..a58cdf3 100644 --- a/exercise.wwwapi/Repository/Repository.cs +++ b/exercise.wwwapi/Repository/Repository.cs @@ -45,7 +45,7 @@ public IEnumerable GetWithIncludes(Func, IQueryable> include IQueryable query = includeQuery(_table); return query.ToList(); } - public T GetById(object id) + public T? GetById(object? id) { return _table.Find(id); }