Skip to content

Commit 9c73cf1

Browse files
authored
Merge pull request #84 from boolean-uk/73-backend---create-endpoints-for-getpostputdelete-post
73 backend create endpoints for getpostputdelete post
2 parents f565fc0 + 904b31c commit 9c73cf1

File tree

9 files changed

+393
-6
lines changed

9 files changed

+393
-6
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
using exercise.wwwapi.DTOs.Posts;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc.Testing;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Text;
9+
using System.Text.Json;
10+
using System.Text.Json.Nodes;
11+
using System.Threading.Tasks;
12+
13+
namespace exercise.tests.IntegrationTests
14+
{
15+
[TestFixture]
16+
public class PostTests
17+
{
18+
private WebApplicationFactory<Program> _factory;
19+
private HttpClient _client;
20+
21+
[SetUp]
22+
public void SetUp()
23+
{
24+
// Arrange
25+
_factory = new WebApplicationFactory<Program>();
26+
_client = _factory.CreateClient();
27+
}
28+
29+
[TearDown]
30+
public void TearDown()
31+
{
32+
_client.Dispose();
33+
_factory.Dispose();
34+
}
35+
36+
[Test]
37+
public async Task SuccessfulCreatePostStatus()
38+
{
39+
int userid = 1;
40+
string content = "Some content";
41+
CreatePostDTO body = new CreatePostDTO
42+
{
43+
Userid = userid,
44+
Content = content
45+
};
46+
var json = JsonSerializer.Serialize(body);
47+
var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
48+
49+
// Act
50+
var response = await _client.PostAsync("/posts", requestBody);
51+
52+
var contentString = await response.Content.ReadAsStringAsync();
53+
54+
JsonNode? message = null;
55+
if (!string.IsNullOrWhiteSpace(contentString))
56+
{
57+
message = JsonNode.Parse(contentString);
58+
}
59+
60+
Console.WriteLine("Message: " + message);
61+
// Assert
62+
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Created));
63+
}
64+
65+
[Test]
66+
public async Task SuccessfulCreatePostMessage()
67+
{
68+
int userid = 1;
69+
string content = "Some content";
70+
CreatePostDTO body = new CreatePostDTO
71+
{
72+
Userid = userid,
73+
Content = content
74+
};
75+
var json = JsonSerializer.Serialize(body);
76+
var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
77+
78+
// Act
79+
var response = await _client.PostAsync("/posts", requestBody);
80+
81+
var contentString = await response.Content.ReadAsStringAsync();
82+
83+
JsonNode? message = null;
84+
if (!string.IsNullOrWhiteSpace(contentString))
85+
{
86+
message = JsonNode.Parse(contentString);
87+
}
88+
89+
Console.WriteLine("Message: " + message);
90+
// Assert
91+
92+
// Assert JSON structure
93+
Assert.That(message, Is.Not.Null);
94+
Assert.That(message?["message"]?.GetValue<string>(), Is.EqualTo("success"));
95+
96+
var data = message["data"];
97+
Assert.That(data, Is.Not.Null);
98+
Assert.That(data?["id"]?.GetValue<int>(), Is.GreaterThan(0));
99+
Assert.That(data["content"]?.GetValue<string>(), Is.EqualTo(content));
100+
Assert.That(data["numLikes"]?.GetValue<int>(), Is.EqualTo(0));
101+
Assert.That(data["comments"]?.AsArray().Count, Is.EqualTo(0));
102+
103+
// Check User object inside data
104+
var user = data["user"];
105+
Assert.That(user, Is.Not.Null);
106+
Assert.That(user!["id"]?.GetValue<int>(), Is.EqualTo(userid));
107+
Assert.That(user["firstName"]?.GetValue<string>(), Is.Not.Null);
108+
Assert.That(user["lastName"]?.GetValue<string>(), Is.Not.Null);
109+
Assert.That(user["photo"]?.GetValue<string>(), Is.Not.Null);
110+
111+
Assert.That(message["timestamp"], Is.Not.Null);
112+
Assert.That(data["createdAt"], Is.Not.Null);
113+
}
114+
115+
[TestCase(9999999, "somecontent", HttpStatusCode.NotFound)]
116+
[TestCase(5, "", HttpStatusCode.BadRequest)]
117+
[TestCase(5, " ", HttpStatusCode.BadRequest)]
118+
[TestCase(5, " ", HttpStatusCode.BadRequest)]
119+
public async Task FailedlCreatePostStatus(int userid, string content, HttpStatusCode expected)
120+
{
121+
CreatePostDTO body = new CreatePostDTO
122+
{
123+
Userid = userid,
124+
Content = content
125+
};
126+
var json = JsonSerializer.Serialize(body);
127+
var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
128+
129+
// Act
130+
var response = await _client.PostAsync("/posts", requestBody);
131+
132+
var contentString = await response.Content.ReadAsStringAsync();
133+
134+
JsonNode? message = null;
135+
if (!string.IsNullOrWhiteSpace(contentString))
136+
{
137+
message = JsonNode.Parse(contentString);
138+
}
139+
140+
Console.WriteLine("Message: " + message);
141+
// Assert
142+
Assert.That(response.StatusCode, Is.EqualTo(expected));
143+
}
144+
145+
[TestCase(9999999, "somecontent", "Invalid userID")]
146+
[TestCase(5, "", "Content cannot be empty")]
147+
[TestCase(5, " ", "Content cannot be empty")]
148+
[TestCase(5, " ", "Content cannot be empty")]
149+
public async Task FailedCreatePostMessage(int userid, string content, string expectedmessage)
150+
{
151+
CreatePostDTO body = new CreatePostDTO
152+
{
153+
Userid = userid,
154+
Content = content
155+
};
156+
var json = JsonSerializer.Serialize(body);
157+
var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
158+
159+
// Act
160+
var response = await _client.PostAsync("/posts", requestBody);
161+
162+
var contentString = await response.Content.ReadAsStringAsync();
163+
164+
JsonNode? message = null;
165+
if (!string.IsNullOrWhiteSpace(contentString))
166+
{
167+
message = JsonNode.Parse(contentString);
168+
}
169+
170+
Console.WriteLine("Message: " + message);
171+
172+
// Assert JSON structure
173+
Assert.That(message, Is.Not.Null);
174+
Assert.That(message?["message"]?.GetValue<string>(), Is.EqualTo(expectedmessage));
175+
Assert.That(message["data"], Is.Null);
176+
Assert.That(message["timestamp"], Is.Not.Null);
177+
}
178+
[Test]
179+
public async Task DeletePostById_SuccessAndNotFound()
180+
{
181+
// Arrange, create a post first
182+
var newPost = new CreatePostDTO
183+
{
184+
Userid = 1,
185+
Content = "Temp post to delete"
186+
};
187+
var json = JsonSerializer.Serialize(newPost);
188+
var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
189+
190+
var createResponse = await _client.PostAsync("/posts", requestBody);
191+
var createContent = await createResponse.Content.ReadAsStringAsync();
192+
var createMessage = JsonNode.Parse(createContent);
193+
194+
int createdPostId = createMessage!["data"]!["id"]!.GetValue<int>();
195+
196+
// Act, delete the post
197+
var deleteResponse = await _client.DeleteAsync($"/posts/{createdPostId}");
198+
var deleteContent = await deleteResponse.Content.ReadAsStringAsync();
199+
200+
// Assert delete success
201+
Assert.That(deleteResponse.StatusCode, Is.EqualTo(HttpStatusCode.OK));
202+
203+
// Act again, try deleting same post (should now be gone)
204+
var deleteAgainResponse = await _client.DeleteAsync($"/posts/{createdPostId}");
205+
var deleteAgainContent = await deleteAgainResponse.Content.ReadAsStringAsync();
206+
var deleteAgainMessage = JsonNode.Parse(deleteAgainContent);
207+
208+
// Assert not found
209+
Assert.That(deleteAgainResponse.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
210+
Assert.That(deleteAgainMessage?["message"]?.GetValue<string>(), Is.EqualTo("Post not found"));
211+
}
212+
213+
214+
215+
216+
[TestCase(5, "Updated content", HttpStatusCode.OK)]
217+
[TestCase(9999999, "Updated content", HttpStatusCode.NotFound)]
218+
[TestCase(5, "", HttpStatusCode.BadRequest)]
219+
public async Task UpdatePostById(int postId, string newContent, HttpStatusCode expected)
220+
{
221+
// Arrange
222+
UpdatePostDTO body = new UpdatePostDTO { Content = newContent };
223+
var json = JsonSerializer.Serialize(body);
224+
var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
225+
226+
// Act
227+
var response = await _client.PatchAsync($"/posts/{postId}", requestBody);
228+
var contentString = await response.Content.ReadAsStringAsync();
229+
230+
JsonNode? message = null;
231+
if (!string.IsNullOrWhiteSpace(contentString))
232+
{
233+
message = JsonNode.Parse(contentString);
234+
}
235+
236+
Console.WriteLine("Message: " + message);
237+
238+
// Assert status
239+
Assert.That(response.StatusCode, Is.EqualTo(expected));
240+
241+
if (expected == HttpStatusCode.OK)
242+
{
243+
var data = message?["data"];
244+
Assert.That(data, Is.Not.Null);
245+
Assert.That(data?["id"]?.GetValue<int>(), Is.EqualTo(postId));
246+
Assert.That(data?["content"]?.GetValue<string>(), Is.EqualTo(newContent));
247+
Assert.That(data?["updatedAt"], Is.Not.Null);
248+
}
249+
else if (expected == HttpStatusCode.NotFound)
250+
{
251+
Assert.That(message?["message"]?.GetValue<string>(), Is.EqualTo("Post not found"));
252+
Assert.That(message?["data"], Is.Null);
253+
}
254+
else if (expected == HttpStatusCode.BadRequest)
255+
{
256+
Assert.That(message?["message"]?.GetValue<string>(), Is.EqualTo("Content cannot be empty"));
257+
Assert.That(message?["data"], Is.Null);
258+
}
259+
}
260+
261+
262+
}
263+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using exercise.wwwapi.DTOs.GetUsers;
2+
using exercise.wwwapi.Models;
3+
using System.ComponentModel.DataAnnotations;
4+
using System.ComponentModel.DataAnnotations.Schema;
5+
6+
namespace exercise.wwwapi.DTOs.Posts
7+
{
8+
public class CreatePostDTO
9+
{
10+
public required int Userid { get; set; }
11+
public required string Content { get; set; }
12+
13+
}
14+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace exercise.wwwapi.DTOs.Posts
2+
{
3+
public class UpdatePostDTO
4+
{
5+
public string Content { get; set; } = string.Empty;
6+
}
7+
}
Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
using Microsoft.AspNetCore.Mvc;
1+
using AutoMapper;
2+
using exercise.wwwapi.Data;
3+
using exercise.wwwapi.DTOs;
4+
using exercise.wwwapi.DTOs.GetUsers;
5+
using exercise.wwwapi.DTOs.Posts;
6+
using exercise.wwwapi.Models;
7+
using exercise.wwwapi.Repository;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.EntityFrameworkCore;
210

311
namespace exercise.wwwapi.Endpoints
412
{
@@ -9,16 +17,90 @@ public static void ConfigurePostEndpoints(this WebApplication app)
917
var posts = app.MapGroup("posts");
1018
posts.MapPost("/", CreatePost).WithSummary("Create post");
1119
posts.MapGet("/", GetAllPosts).WithSummary("Get all posts");
20+
posts.MapPatch("/{id}", UpdatePost).WithSummary("Update a certain post");
21+
posts.MapDelete("/{id}", DeletePost).WithSummary("Remove a certain post");
1222
}
23+
1324
[ProducesResponseType(StatusCodes.Status200OK)]
14-
public static async Task<IResult> CreatePost()
25+
[ProducesResponseType(StatusCodes.Status404NotFound)]
26+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
27+
public static IResult CreatePost(IRepository<User> userservice, IRepository<Post> postservice, IMapper mapper, CreatePostDTO request)
1528
{
16-
return TypedResults.Ok();
29+
30+
User? user = userservice.GetById(request.Userid);
31+
if (user == null)
32+
return Results.NotFound(new ResponseDTO<Object>{Message = "Invalid userID"});
33+
34+
if (string.IsNullOrWhiteSpace(request.Content))
35+
return Results.BadRequest(new ResponseDTO<Object> { Message = "Content cannot be empty" });
36+
37+
Post post = new Post() { CreatedAt = DateTime.UtcNow, NumLikes = 0, UserId=request.Userid, Content=request.Content };
38+
39+
// is a try catch needed here?
40+
postservice.Insert(post);
41+
postservice.Save();
42+
43+
44+
UserBasicDTO userBasicDTO = mapper.Map<UserBasicDTO>(user);
45+
PostDTO postDTO = mapper.Map<PostDTO>(post);
46+
postDTO.User = userBasicDTO;
47+
48+
ResponseDTO<PostDTO> response = new ResponseDTO<PostDTO>
49+
{
50+
Message = "success",
51+
Data = postDTO
52+
};
53+
54+
return Results.Created($"/posts/{post.Id}", response);
55+
}
56+
[ProducesResponseType(StatusCodes.Status200OK)]
57+
public static IResult GetAllPosts(IRepository<Post> service, IMapper mapper)
58+
{
59+
IEnumerable<Post> results = service.GetWithIncludes(q => q.Include(p => p.User));
60+
IEnumerable<PostDTO> postDTOs = mapper.Map<IEnumerable<PostDTO>>(results);
61+
ResponseDTO<IEnumerable<PostDTO>> response = new ResponseDTO<IEnumerable<PostDTO>>()
62+
{
63+
Message = "success",
64+
Data = postDTOs
65+
};
66+
return TypedResults.Ok(response);
1767
}
68+
1869
[ProducesResponseType(StatusCodes.Status200OK)]
19-
public static async Task<IResult> GetAllPosts()
70+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
71+
public static IResult UpdatePost(IRepository<Post> service, IMapper mapper, int id, UpdatePostDTO request)
2072
{
21-
return TypedResults.Ok();
73+
if (string.IsNullOrWhiteSpace(request.Content)) return TypedResults.BadRequest(new ResponseDTO<object>{
74+
Message = "Content cannot be empty"
75+
});
76+
77+
Post? post = service.GetById(id, q=>q.Include(p => p.User));
78+
79+
if (post == null) return TypedResults.NotFound(new ResponseDTO<Object> { Message = "Post not found" });
80+
81+
post.Content = request.Content;
82+
post.UpdatedAt = DateTime.UtcNow;
83+
84+
service.Update(post);
85+
service.Save();
86+
87+
PostDTO postDTO = mapper.Map<PostDTO>(post);
88+
89+
90+
return TypedResults.Ok(new ResponseDTO<PostDTO> { Message = "Success", Data = postDTO });
91+
}
92+
93+
[ProducesResponseType(StatusCodes.Status200OK)]
94+
[ProducesResponseType(StatusCodes.Status404NotFound)]
95+
private static IResult DeletePost(IRepository<Post> service, int id)
96+
{
97+
Post? post = service.GetById(id, q => q.Include(p => p.User));
98+
if (post == null) return TypedResults.NotFound(new ResponseDTO<Object> { Message = "Post not found" });
99+
100+
service.Delete(id);
101+
service.Save();
102+
103+
return TypedResults.Ok(new ResponseDTO<PostDTO> { Message = "Success" });
22104
}
23105
}
24106
}

0 commit comments

Comments
 (0)