From 38b2bac49286d807c264430f0a1660159ad309eb Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Thu, 4 Apr 2024 14:28:28 +0000 Subject: [PATCH 01/30] feat: PostCRUD --- .../domain/post/api/PostController.java | 47 +++++++++ .../domain/post/application/PostService.java | 35 +++++++ .../domain/post/dao/PostRepository.java | 10 ++ .../domain/post/domain/Post.java | 41 ++++++++ .../domain/post/domain/PostReply.java | 27 ++++++ .../domain/post/dto/PostDto.java | 19 ++++ .../domain/post/dto/PostReplyDto.java | 17 ++++ .../domain/user/domain/User.java | 21 ++++ .../domain/post/api/PostControllerTest.java | 96 +++++++++++++++++++ 9 files changed, 313 insertions(+) create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java create mode 100644 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java new file mode 100644 index 0000000..5a5f78a --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java @@ -0,0 +1,47 @@ +package com.ssafy.springbootapi.domain.post.api; + +import com.ssafy.springbootapi.domain.post.application.PostService; +import com.ssafy.springbootapi.domain.post.domain.Post; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +@Tag(name = "Post", description = "Post API 입니다.") +@RestController +@RequestMapping("/api/v1/posts") +public class PostController { + private final PostService postService; + + @Autowired + public PostController(PostService postService) { + this.postService = postService; + } + + @GetMapping + public ResponseEntity> getAllPosts() { + List posts = postService.getAllPosts(); + return new ResponseEntity<>(posts, HttpStatus.OK); + } + + @GetMapping("/{id}") + public ResponseEntity getPostById(@PathVariable Long id) { + return postService.getPostById(id) + .map(post -> new ResponseEntity<>(post, HttpStatus.OK)) + .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + + @PostMapping + public ResponseEntity createPost(@RequestBody Post post) { + Post createdPost = postService.savePost(post); + return new ResponseEntity<>(createdPost, HttpStatus.CREATED); + } + + @DeleteMapping("/{id}") + public ResponseEntity deletePost(@PathVariable Long id) { + postService.deletePost(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java new file mode 100644 index 0000000..cc2507f --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java @@ -0,0 +1,35 @@ +package com.ssafy.springbootapi.domain.post.application; + +import com.ssafy.springbootapi.domain.post.dao.PostRepository; +import com.ssafy.springbootapi.domain.post.domain.Post; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class PostService { + private final PostRepository postRepository; + + @Autowired + public PostService(PostRepository postRepository) { + this.postRepository = postRepository; + } + + public List getAllPosts() { + return postRepository.findAll(); + } + + public Optional getPostById(Long id) { + return postRepository.findById(id); + } + + public Post savePost(Post post) { + return postRepository.save(post); + } + + public void deletePost(Long id) { + postRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java new file mode 100644 index 0000000..31d7f6f --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java @@ -0,0 +1,10 @@ +package com.ssafy.springbootapi.domain.post.dao; + +import com.ssafy.springbootapi.domain.post.domain.Post; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PostRepository extends JpaRepository { + // 추가적인 쿼리 메서드가 필요하다면 여기에 추가할 수 있습니다. +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java new file mode 100644 index 0000000..ebef985 --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java @@ -0,0 +1,41 @@ +package com.ssafy.springbootapi.domain.post.domain; + + +import com.ssafy.springbootapi.domain.user.domain.User; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@Getter @Setter +public class Post { + @Id + @GeneratedValue + private Long id; + + private String contents; + + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + private Long likes; + + private Long dislikes; + + private Long views; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + public Post(long l, String firstPost, LocalDateTime now, LocalDateTime now1, long l1, long l2, long l3, User user) { + } + + + public Post() { + + } +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java new file mode 100644 index 0000000..762c70d --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java @@ -0,0 +1,27 @@ +package com.ssafy.springbootapi.domain.post.domain; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@Getter @Setter +public class PostReply { + @Id + @GeneratedValue + private Long id; + + private String contents; + + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + private Long likes; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java new file mode 100644 index 0000000..9b8cdc9 --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java @@ -0,0 +1,19 @@ +package com.ssafy.springbootapi.domain.post.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.sql.Date; + +@Getter @Setter +public class PostDto { + private Long id; + private String contents; + private Date createdAt; + private Date updatedAt; + private Long likes; + private Long dislikes; + private Long views; + private Long userId; + +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java new file mode 100644 index 0000000..4cd13d4 --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java @@ -0,0 +1,17 @@ +package com.ssafy.springbootapi.domain.post.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.sql.Date; + + +@Getter @Setter +public class PostReplyDto { + private Long id; + private String contents; + private Date createdAt; + private Date updatedAt; + private Long likes; + private Long postId; +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java new file mode 100644 index 0000000..9dad15c --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java @@ -0,0 +1,21 @@ +package com.ssafy.springbootapi.domain.user.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Getter +@Setter +public class User { + @Id + @GeneratedValue + private Long id; + + private String username; + + + // 생성자, getter, setter 등은 생략 +} diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java new file mode 100644 index 0000000..3ecacb1 --- /dev/null +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java @@ -0,0 +1,96 @@ +package com.ssafy.springbootapi.domain.post.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ssafy.springbootapi.domain.post.application.PostService; +import com.ssafy.springbootapi.domain.post.domain.Post; + +import com.ssafy.springbootapi.domain.user.domain.User; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Optional; + +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; + +@ExtendWith(MockitoExtension.class) +class PostControllerTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Mock + private PostService postService; + + @InjectMocks + private PostController postController; + + @Test + void getAllPosts() throws Exception { + when(postService.getAllPosts()).thenReturn(Arrays.asList( + new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), + new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) + )); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); + + mockMvc.perform(MockMvcRequestBuilders.get("/posts")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(Arrays.asList( + new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), + new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) + )))); + } + + @Test + void getPostById() throws Exception { + Long postId = 1L; + Optional post = Optional.of(new Post(postId, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User())); + when(postService.getPostById(postId)).thenReturn(post); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); + + mockMvc.perform(MockMvcRequestBuilders.get("/posts/{id}", postId)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); + } + + @Test + void createPost() throws Exception { + Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); + + when(postService.savePost(ArgumentMatchers.any(Post.class))).thenReturn(post); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController) + .build(); + + mockMvc.perform(MockMvcRequestBuilders.post("/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(post))) // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환 + .andExpect(MockMvcResultMatchers.status().isCreated()) + .andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(post))); // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환하여 비교 + } + + + @Test + void deletePost() throws Exception { + Long postId = 1L; + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); + + mockMvc.perform(MockMvcRequestBuilders.delete("/posts/{id}", postId)) + .andExpect(MockMvcResultMatchers.status().isNoContent()); + + verify(postService, times(1)).deletePost(postId); + } +} From e764e2ea4b749cafbc581670a196602912f84c70 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Sun, 7 Apr 2024 07:15:49 +0000 Subject: [PATCH 02/30] refactor: fix testcode --- .../springbootapi/domain/post/dao/PostRepository.java | 2 +- .../springbootapi/domain/post/api/PostControllerTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java index 31d7f6f..5db9e47 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java @@ -6,5 +6,5 @@ @Repository public interface PostRepository extends JpaRepository { - // 추가적인 쿼리 메서드가 필요하다면 여기에 추가할 수 있습니다. + } diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java index 3ecacb1..9be7c66 100644 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java @@ -44,7 +44,7 @@ void getAllPosts() throws Exception { MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); - mockMvc.perform(MockMvcRequestBuilders.get("/posts")) + mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(Arrays.asList( new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), @@ -60,7 +60,7 @@ void getPostById() throws Exception { MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); - mockMvc.perform(MockMvcRequestBuilders.get("/posts/{id}", postId)) + mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); } @@ -74,7 +74,7 @@ void createPost() throws Exception { MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController) .build(); - mockMvc.perform(MockMvcRequestBuilders.post("/posts") + mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") .contentType(MediaType.APPLICATION_JSON) .content(new ObjectMapper().writeValueAsString(post))) // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환 .andExpect(MockMvcResultMatchers.status().isCreated()) @@ -88,7 +88,7 @@ void deletePost() throws Exception { MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); - mockMvc.perform(MockMvcRequestBuilders.delete("/posts/{id}", postId)) + mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) .andExpect(MockMvcResultMatchers.status().isNoContent()); verify(postService, times(1)).deletePost(postId); From a020322e2b18991845228b69d88212aacc779b87 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Sun, 7 Apr 2024 12:32:32 +0000 Subject: [PATCH 03/30] feat: PostController updatePost --- .../ssafy/springbootapi/domain/post/api/PostController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java index 5a5f78a..f7c4023 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java @@ -44,4 +44,10 @@ public ResponseEntity deletePost(@PathVariable Long id) { postService.deletePost(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + + @PutMapping("/{id}") + public ResponseEntity updatePost(@PathVariable Long id, @RequestBody Post postDetails) { + Post updatedPost = postService.updatePost(id, postDetails); + return new ResponseEntity<>(updatedPost, HttpStatus.OK); + } } From ead06464c0f25ccd6a386f6c74ee1efd57b4acac Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Sun, 7 Apr 2024 12:33:15 +0000 Subject: [PATCH 04/30] feat: PostService updatePost --- .../domain/post/application/PostService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java index cc2507f..4227631 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java @@ -32,4 +32,14 @@ public Post savePost(Post post) { public void deletePost(Long id) { postRepository.deleteById(id); } + + public Post updatePost(Long id, Post postDetails) { + Post post = postRepository.findById(id) + .orElseThrow(() -> new RuntimeException()); + + post.setContents(postDetails.getContents()); + + return postRepository.save(post); + } + } \ No newline at end of file From 8d2f19de70b1ee6f1d8cc821acbf756d441f6cfe Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Sun, 7 Apr 2024 12:34:19 +0000 Subject: [PATCH 05/30] Test: updatePost --- .../domain/post/api/PostControllerTest.java | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java index 9be7c66..27fef20 100644 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java @@ -21,9 +21,14 @@ import java.util.Arrays; import java.util.Optional; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) class PostControllerTest { @@ -37,6 +42,8 @@ class PostControllerTest { @Test void getAllPosts() throws Exception { + + //given when(postService.getAllPosts()).thenReturn(Arrays.asList( new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) @@ -44,8 +51,10 @@ void getAllPosts() throws Exception { MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); + //when mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) - .andExpect(MockMvcResultMatchers.status().isOk()) + //then + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(Arrays.asList( new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) @@ -54,43 +63,72 @@ void getAllPosts() throws Exception { @Test void getPostById() throws Exception { + + // given Long postId = 1L; Optional post = Optional.of(new Post(postId, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User())); when(postService.getPostById(postId)).thenReturn(post); MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); + //when mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) - .andExpect(MockMvcResultMatchers.status().isOk()) + //then + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); } @Test void createPost() throws Exception { + //given Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); - when(postService.savePost(ArgumentMatchers.any(Post.class))).thenReturn(post); + when(postService.savePost(any(Post.class))).thenReturn(post); MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController) .build(); + //when mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") .contentType(MediaType.APPLICATION_JSON) .content(new ObjectMapper().writeValueAsString(post))) // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환 - .andExpect(MockMvcResultMatchers.status().isCreated()) + //then + .andExpect(status().isCreated()) .andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(post))); // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환하여 비교 } @Test void deletePost() throws Exception { + //given Long postId = 1L; MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); - + //when mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) - .andExpect(MockMvcResultMatchers.status().isNoContent()); + //then + .andExpect(status().isNoContent()); - verify(postService, times(1)).deletePost(postId); + verify(postService, times(1)).deletePost(postId); //postService의 deletePost 메소드가 정확히 한 번 호출되었는지 검증 + } + + @Test + public void updatePostTest() throws Exception { + //given + Post post = new Post(); + post.setId(1L); + post.setContents("Updated content"); + + given(postService.updatePost(any(Long.class), any(Post.class))).willReturn(post); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); + + //when + mockMvc.perform(put("/api/v1/posts/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(post))) + //then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.contents").value("Updated content")); } } From 754fe553272e9265a3c7504765939f45a4c3c0db Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Wed, 10 Apr 2024 15:27:38 +0000 Subject: [PATCH 06/30] =?UTF-8?q?:sparkles:=20[Add]=20GlobalExceptionHandl?= =?UTF-8?q?er=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/error/GlobalExceptionHandler.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/global/error/GlobalExceptionHandler.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/global/error/GlobalExceptionHandler.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/global/error/GlobalExceptionHandler.java new file mode 100644 index 0000000..fc3f7b0 --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/global/error/GlobalExceptionHandler.java @@ -0,0 +1,24 @@ +package com.ssafy.springbootapi.global.error; + +import jakarta.persistence.EntityNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity handleRuntimeException(RuntimeException ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("서버 내부 오류가 발생했습니다."); + } + + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity handleEntityNotFoundException(EntityNotFoundException ex) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("존재하지 않습니다."); + } + + + +} From f14c0d312a3138c1400c94f874ebf969b932d3b7 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Wed, 10 Apr 2024 15:33:09 +0000 Subject: [PATCH 07/30] =?UTF-8?q?:white=5Fcheck=5Fmark:=20[Test]=20PostSer?= =?UTF-8?q?viceTest=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/dto/{PostDto.java => Post.java} | 2 +- .../dto/{PostReplyDto.java => PostReply.java} | 2 +- .../post/application/PostServiceTest.java | 140 ++++++++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) rename spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/{PostDto.java => Post.java} (93%) rename spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/{PostReplyDto.java => PostReply.java} (91%) create mode 100644 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java similarity index 93% rename from spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java rename to spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java index 9b8cdc9..7f335b0 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostDto.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java @@ -6,7 +6,7 @@ import java.sql.Date; @Getter @Setter -public class PostDto { +public class Post { private Long id; private String contents; private Date createdAt; diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java similarity index 91% rename from spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java rename to spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java index 4cd13d4..0794aea 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReplyDto.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java @@ -7,7 +7,7 @@ @Getter @Setter -public class PostReplyDto { +public class PostReply { private Long id; private String contents; private Date createdAt; diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java new file mode 100644 index 0000000..effb38d --- /dev/null +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java @@ -0,0 +1,140 @@ +package com.ssafy.springbootapi.domain.post.application; + +import com.ssafy.springbootapi.domain.post.dao.PostRepository; +import com.ssafy.springbootapi.domain.post.domain.Post; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.Arrays; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@SpringBootTest +class PostServiceTest { + + @Autowired + private PostService postService; + + @MockBean + private PostRepository postRepository; + + private Post post; + + @BeforeEach + void setUp() { + post = new Post(); + post.setId(1L); + post.setContents("Test Contents"); + } + @BeforeEach + void clean() { + postRepository.deleteAll(); + } + + @DisplayName("게시글 전부 조회 성공 테스트") + @Test + void 게시글전부조회성공테스트() { + //given when + when(postRepository.findAll()).thenReturn(Arrays.asList(post)); + //then + assertThat(postService.getAllPosts()).hasSize(1); + } + + @DisplayName("게시글 전부 조회 예외 테스트") + @Test + void 게시글전부조회예외테스트() { + when(postRepository.findAll()).thenThrow(new RuntimeException()); + assertThrows(RuntimeException.class, () -> postService.getAllPosts()); + } + + @DisplayName("게시글 아이디로 조회 성공 테스트") + @Test + void 게시글아이디조회테스트() { + //given when + when(postRepository.findById(1L)).thenReturn(Optional.of(post)); + //then + assertThat(postService.getPostById(1L)).isNotEmpty(); + } + @DisplayName("게시글 아이디로 조회 예외 테스트") + @Test + void 게시글아이디조회예외테스트() { + when(postRepository.findById(1L)).thenReturn(Optional.empty()); + assertThat(postService.getPostById(1L)).isEmpty(); + } + + @DisplayName("게시글 등록 성공 테스트") + @Test + void 게시글등록테스트() { + //given when + when(postRepository.save(any(Post.class))).thenReturn(post); + Post savedPost = postService.savePost(new Post()); + //then + assertThat(savedPost.getContents()).isEqualTo("Test Contents"); + } + @DisplayName("게시글 등록 예외 테스트") + @Test + void 게시글등록예외테스트() { + when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); + assertThrows(RuntimeException.class, () -> postService.savePost(new Post())); + } + + + + @DisplayName("게시글 삭제 성공 테스트") + @Test + void 게시글삭제테스트() { + //given + doNothing().when(postRepository).deleteById(1L); + //when + postService.deletePost(1L); + //then + verify(postRepository, times(1)).deleteById(1L); + } + @DisplayName("게시글 삭제 예외 테스트") + @Test + void 게시글삭제예외테스트() { + doThrow(new RuntimeException()).when(postRepository).deleteById(1L); + assertThrows(RuntimeException.class, () -> postService.deletePost(1L)); + } + + + @DisplayName("게시글 수정 성공 테스트") + @Test + void 게시글수정테스트() { + //given + when(postRepository.findById(1L)).thenReturn(Optional.of(post)); + when(postRepository.save(any(Post.class))).thenReturn(post); + Post updatedPost = new Post(); + //when + updatedPost.setContents("Updated Contents"); + Post result = postService.updatePost(1L, updatedPost); + //then + assertThat(result.getContents()).isEqualTo("Updated Contents"); + } + @DisplayName("게시글 수정 존재안함 테스트") + @Test + void 게시글수정존재안함테스트() { + when(postRepository.findById(1L)).thenReturn(Optional.empty()); + Post updatedPost = new Post(); + updatedPost.setContents("Updated Contents"); + assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); + } + @DisplayName("게시글 수정 예외 테스트") + @Test + void 게시글수정예외테스트() { + when(postRepository.findById(1L)).thenReturn(Optional.of(post)); + when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); + Post updatedPost = new Post(); + updatedPost.setContents("Updated Contents"); + assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); + } + +} \ No newline at end of file From 2e75a22c627192e141d4fb9a5efbd4056f051b87 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Wed, 10 Apr 2024 15:34:53 +0000 Subject: [PATCH 08/30] =?UTF-8?q?:white=5Fcheck=5Fmark:=20[Test]=20PostCon?= =?UTF-8?q?trollerTest=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/api/PostControllerTest.java | 109 +++++++++++++++--- 1 file changed, 92 insertions(+), 17 deletions(-) diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java index 27fef20..bd0574c 100644 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java @@ -5,6 +5,10 @@ import com.ssafy.springbootapi.domain.post.domain.Post; import com.ssafy.springbootapi.domain.user.domain.User; +import com.ssafy.springbootapi.global.error.GlobalExceptionHandler; +import jakarta.persistence.EntityNotFoundException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; @@ -16,6 +20,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.ExceptionHandler; import java.time.LocalDateTime; import java.util.Arrays; @@ -23,9 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -40,8 +43,17 @@ class PostControllerTest { @InjectMocks private PostController postController; + private MockMvc mockMvc; + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(postController) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + } + + @DisplayName("게시글 전부 조회 성공 테스트") @Test - void getAllPosts() throws Exception { + void 게시글전부조회성공테스트() throws Exception { //given when(postService.getAllPosts()).thenReturn(Arrays.asList( @@ -49,7 +61,6 @@ void getAllPosts() throws Exception { new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) )); - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); //when mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) @@ -60,16 +71,28 @@ void getAllPosts() throws Exception { new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) )))); } + @DisplayName("게시글 전부 조회 실패 테스트") + @Test + void 게시글전부조회실패테스트() throws Exception { + //given + when(postService.getAllPosts()).thenThrow(new RuntimeException()); + + //when, then + mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) + .andExpect(status().isInternalServerError()); + } + + + @DisplayName("게시글 아이디로 조회 성공 테스트") @Test - void getPostById() throws Exception { + void 게시글아이디조회성공테스트() throws Exception { // given Long postId = 1L; Optional post = Optional.of(new Post(postId, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User())); when(postService.getPostById(postId)).thenReturn(post); - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); //when mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) @@ -77,17 +100,27 @@ void getPostById() throws Exception { .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); } + @DisplayName("게시글 아이디로 조회 실패 테스트") + @Test + void 게시글아이디조회실패테스트() throws Exception { + // given + Long postId = 1L; + when(postService.getPostById(postId)).thenThrow(new EntityNotFoundException()); + //when, then + mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) + .andExpect(status().isNotFound()); + } + + + @DisplayName("게시글 생성 성공 테스트") @Test - void createPost() throws Exception { + void 게시글생성성공테스트() throws Exception { //given Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); when(postService.savePost(any(Post.class))).thenReturn(post); - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController) - .build(); - //when mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") .contentType(MediaType.APPLICATION_JSON) @@ -96,14 +129,28 @@ void createPost() throws Exception { .andExpect(status().isCreated()) .andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(post))); // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환하여 비교 } + @DisplayName("게시글 생성 실패 테스트") + @Test + void 게시글생성실패테스트() throws Exception { + //given + Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); + when(postService.savePost(any(Post.class))).thenThrow(new RuntimeException()); + //when, then + mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(post))) + .andExpect(status().isInternalServerError()); + } + + + + @DisplayName("게시글 삭제 성공 테스트") @Test - void deletePost() throws Exception { + void 게시글삭제성공테스트() throws Exception { //given Long postId = 1L; - - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); //when mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) //then @@ -111,9 +158,22 @@ void deletePost() throws Exception { verify(postService, times(1)).deletePost(postId); //postService의 deletePost 메소드가 정확히 한 번 호출되었는지 검증 } + @DisplayName("게시글 삭제 실패 테스트") + @Test + void 게시글삭제실패테스트() throws Exception { + //given + Long postId = 1L; + doThrow(new RuntimeException()).when(postService).deletePost(postId); + + //when, then + mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) + .andExpect(status().isInternalServerError()); + } + + @DisplayName("게시글 수정 성공 테스트") @Test - public void updatePostTest() throws Exception { + public void 게시글수정성공테스트() throws Exception { //given Post post = new Post(); post.setId(1L); @@ -121,8 +181,6 @@ public void updatePostTest() throws Exception { given(postService.updatePost(any(Long.class), any(Post.class))).willReturn(post); - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(postController).build(); - //when mockMvc.perform(put("/api/v1/posts/{id}", 1L) .contentType(MediaType.APPLICATION_JSON) @@ -131,4 +189,21 @@ public void updatePostTest() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.contents").value("Updated content")); } + @DisplayName("게시글 수정 실패 테스트") + @Test + public void 게시글수정실패테스트() throws Exception { + //given + Post post = new Post(); + post.setId(1L); + post.setContents("Updated content"); + + given(postService.updatePost(any(Long.class), any(Post.class))).willThrow(new RuntimeException()); + + //when, then + mockMvc.perform(put("/api/v1/posts/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(post))) + .andExpect(status().isInternalServerError()); + } + } From f5ef79bb156b396aab2384d22d9f0b800cb7fde5 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 10:39:32 +0000 Subject: [PATCH 09/30] :recycle: Refactor: Post --- .../domain/post/domain/Post.java | 31 +++++++++++++------ .../domain/post/dto/AddPostRequest.java | 4 +++ .../springbootapi/domain/post/dto/Post.java | 19 ------------ .../domain/post/dto/PostReply.java | 17 ---------- 4 files changed, 26 insertions(+), 45 deletions(-) mode change 100644 => 100755 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java delete mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java delete mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java old mode 100644 new mode 100755 index ebef985..03de2b3 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java @@ -3,39 +3,52 @@ import com.ssafy.springbootapi.domain.user.domain.User; import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import java.time.LocalDateTime; @Entity -@Getter @Setter +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Post { @Id @GeneratedValue + @Column(name = "id", updatable = false) private Long id; - private String contents; + @Column(name = "title", nullable = false) + private String title; + @Column(name = "content", nullable = false) + private String content; + + @CreatedDate + @Column(name = "created_at") private LocalDateTime createdAt; + @LastModifiedDate + @Column(name = "updated_at") private LocalDateTime updatedAt; + @Column(name = "likes", nullable = false) private Long likes; + @Column(name = "dislikes", nullable = false) private Long dislikes; + @Column(name = "views", nullable = false) private Long views; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - public Post(long l, String firstPost, LocalDateTime now, LocalDateTime now1, long l1, long l2, long l3, User user) { + @Builder + public Post(String title, String content) { + this.title = title; + this.content = content; } - - public Post() { - - } } diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java new file mode 100644 index 0000000..a0d24f3 --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java @@ -0,0 +1,4 @@ +package com.ssafy.springbootapi.domain.post.dto; + +public class AddPostRequest { +} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java deleted file mode 100644 index 7f335b0..0000000 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/Post.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ssafy.springbootapi.domain.post.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.sql.Date; - -@Getter @Setter -public class Post { - private Long id; - private String contents; - private Date createdAt; - private Date updatedAt; - private Long likes; - private Long dislikes; - private Long views; - private Long userId; - -} diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java deleted file mode 100644 index 0794aea..0000000 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostReply.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ssafy.springbootapi.domain.post.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.sql.Date; - - -@Getter @Setter -public class PostReply { - private Long id; - private String contents; - private Date createdAt; - private Date updatedAt; - private Long likes; - private Long postId; -} From 585be82279b830539de77f11beb976b38ff87b5a Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 10:42:51 +0000 Subject: [PATCH 10/30] :recycle: Refactor: PostReply --- .../springbootapi/domain/post/domain/PostReply.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) mode change 100644 => 100755 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java old mode 100644 new mode 100755 index 762c70d..7ea0a08 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java @@ -3,22 +3,31 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import java.time.LocalDateTime; @Entity -@Getter @Setter +@Getter public class PostReply { @Id @GeneratedValue + @Column(name = "id", updatable = false) private Long id; - private String contents; + @Column(name = "content", nullable = false) + private String content; + @CreatedDate + @Column(name = "created_at") private LocalDateTime createdAt; + @LastModifiedDate + @Column(name = "updated_at") private LocalDateTime updatedAt; + @Column(name = "likes", nullable = false) private Long likes; @ManyToOne(fetch = FetchType.LAZY) From f90269dcaf7bc4b1f1a83e2e8286188e3f592abc Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 10:46:20 +0000 Subject: [PATCH 11/30] :sparkles: Add: PostResponse --- .../domain/post/dto/PostResponse.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostResponse.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostResponse.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostResponse.java new file mode 100644 index 0000000..ad1e73d --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/PostResponse.java @@ -0,0 +1,15 @@ +package com.ssafy.springbootapi.domain.post.dto; + +import com.ssafy.springbootapi.domain.post.domain.Post; +import lombok.Getter; + +@Getter +public class PostResponse { + private final String title; + private final String content; + + public PostResponse(Post post) { + this.title = post.getTitle(); + this.content = post.getContent(); + } +} From d6efc0ff163c643d5e277222e0cb298bb9436c9c Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 10:46:46 +0000 Subject: [PATCH 12/30] :sparkles: Add: AddPostRequest --- .../domain/post/dto/AddPostRequest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java index a0d24f3..7da76c3 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java @@ -1,4 +1,23 @@ package com.ssafy.springbootapi.domain.post.dto; +import com.ssafy.springbootapi.domain.post.domain.Post; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +@NoArgsConstructor +@AllArgsConstructor +@Getter public class AddPostRequest { + private String title; + + private String content; + + public Post toEntity() { + return Post.builder() + .title(title) + .content(content) + .build(); + } } From fce47eef1978cf9f56f974de7a80a537ba741715 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 10:48:52 +0000 Subject: [PATCH 13/30] :sparkles: Add: UpdatePostRequest --- .../domain/post/dto/UpdatePostRequest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/UpdatePostRequest.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/UpdatePostRequest.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/UpdatePostRequest.java new file mode 100644 index 0000000..a6eae07 --- /dev/null +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/UpdatePostRequest.java @@ -0,0 +1,13 @@ +package com.ssafy.springbootapi.domain.post.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class UpdatePostRequest { + private String title; + private String content; +} From 2253c2aa81b2815912bca955fa4235407c4bb43d Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 10:58:13 +0000 Subject: [PATCH 14/30] :sparkles: Add: PostService --- .../domain/post/application/PostService.java | 32 ++++++++++--------- .../domain/post/domain/Post.java | 5 +++ 2 files changed, 22 insertions(+), 15 deletions(-) mode change 100644 => 100755 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java old mode 100644 new mode 100755 index 4227631..f733606 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java @@ -2,44 +2,46 @@ import com.ssafy.springbootapi.domain.post.dao.PostRepository; import com.ssafy.springbootapi.domain.post.domain.Post; +import com.ssafy.springbootapi.domain.post.dto.AddPostRequest; +import com.ssafy.springbootapi.domain.post.dto.UpdatePostRequest; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; +@RequiredArgsConstructor @Service public class PostService { private final PostRepository postRepository; - @Autowired - public PostService(PostRepository postRepository) { - this.postRepository = postRepository; + public Post save(AddPostRequest request) { + return postRepository.save(request.toEntity()); } - public List getAllPosts() { + public List findAll(){ return postRepository.findAll(); } - public Optional getPostById(Long id) { - return postRepository.findById(id); + public Post findById(long id) { + return postRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("not found : " + id)); } - public Post savePost(Post post) { - return postRepository.save(post); - } - - public void deletePost(Long id) { + public void delete(long id) { postRepository.deleteById(id); } - public Post updatePost(Long id, Post postDetails) { + @Transactional + public Post update(long id, UpdatePostRequest request) { Post post = postRepository.findById(id) - .orElseThrow(() -> new RuntimeException()); + .orElseThrow(() -> new IllegalArgumentException("not found : " + id)); - post.setContents(postDetails.getContents()); + post.update(request.getTitle(), request.getContent()); - return postRepository.save(post); + return post; } } \ No newline at end of file diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java index 03de2b3..f447e9d 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java @@ -51,4 +51,9 @@ public Post(String title, String content) { this.content = content; } + public void update(String title, String content) { + this.title = title; + this.content = content; + } + } From 681cee9eac4768737a6d442d39319cd325d45fc8 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 11:05:05 +0000 Subject: [PATCH 15/30] :sparkles: Add: PostController --- .../domain/post/api/PostController.java | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) mode change 100644 => 100755 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java old mode 100644 new mode 100755 index f7c4023..d8e94d0 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java @@ -2,7 +2,11 @@ import com.ssafy.springbootapi.domain.post.application.PostService; import com.ssafy.springbootapi.domain.post.domain.Post; +import com.ssafy.springbootapi.domain.post.dto.AddPostRequest; +import com.ssafy.springbootapi.domain.post.dto.PostResponse; +import com.ssafy.springbootapi.domain.post.dto.UpdatePostRequest; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -10,44 +14,52 @@ import java.util.List; @Tag(name = "Post", description = "Post API 입니다.") +@RequiredArgsConstructor @RestController @RequestMapping("/api/v1/posts") public class PostController { + private final PostService postService; - - @Autowired - public PostController(PostService postService) { - this.postService = postService; + + @PostMapping + public ResponseEntity addPost(@RequestBody AddPostRequest request) { + Post savedPost = postService.save(request); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(savedPost); } @GetMapping - public ResponseEntity> getAllPosts() { - List posts = postService.getAllPosts(); - return new ResponseEntity<>(posts, HttpStatus.OK); + public ResponseEntity> findAllPosts() { + List posts = postService.findAll() + .stream() + .map(PostResponse::new) + .toList(); + + return ResponseEntity.ok() + .body(posts); } @GetMapping("/{id}") - public ResponseEntity getPostById(@PathVariable Long id) { - return postService.getPostById(id) - .map(post -> new ResponseEntity<>(post, HttpStatus.OK)) - .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); - } + public ResponseEntity findPost(@PathVariable Long id) { + Post post = postService.findById(id); - @PostMapping - public ResponseEntity createPost(@RequestBody Post post) { - Post createdPost = postService.savePost(post); - return new ResponseEntity<>(createdPost, HttpStatus.CREATED); + return ResponseEntity.ok() + .body(new PostResponse(post)); } @DeleteMapping("/{id}") public ResponseEntity deletePost(@PathVariable Long id) { - postService.deletePost(id); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + postService.delete(id); + return ResponseEntity.ok() + .build(); } @PutMapping("/{id}") - public ResponseEntity updatePost(@PathVariable Long id, @RequestBody Post postDetails) { - Post updatedPost = postService.updatePost(id, postDetails); - return new ResponseEntity<>(updatedPost, HttpStatus.OK); + public ResponseEntity updatePost(@PathVariable Long id, @RequestBody UpdatePostRequest request) { + Post updatedPost = postService.update(id, request); + + return ResponseEntity.ok() + .body(updatedPost); } } From 5a97f4761326aa7d68b79273753908339f46044d Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:41:47 +0000 Subject: [PATCH 16/30] :sparkles: Add: Post --- .../domain/post/domain/Post.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java index f447e9d..25e714c 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/Post.java @@ -4,24 +4,27 @@ import com.ssafy.springbootapi.domain.user.domain.User; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.ColumnDefault; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import java.time.LocalDateTime; +@Builder @Entity @Getter +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Post { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", updatable = false) private Long id; @Column(name = "title", nullable = false) private String title; - @Column(name = "content", nullable = false) + @Column(name = "contents", nullable = false) private String content; @CreatedDate @@ -32,25 +35,22 @@ public class Post { @Column(name = "updated_at") private LocalDateTime updatedAt; - @Column(name = "likes", nullable = false) - private Long likes; + @Builder.Default + @Column(name = "likes") + private Long likes=0L; - @Column(name = "dislikes", nullable = false) - private Long dislikes; + @Builder.Default + @Column(name = "dislikes") + private Long dislikes=0L; - @Column(name = "views", nullable = false) - private Long views; + @Builder.Default + @Column(name = "views") + private Long views=0L; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - @Builder - public Post(String title, String content) { - this.title = title; - this.content = content; - } - public void update(String title, String content) { this.title = title; this.content = content; From f5f5f809dc5370722f6164755a39ade457bddca0 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:42:09 +0000 Subject: [PATCH 17/30] :sparkles: Add: PostApiControllerTest --- .../post/api/PostApiControllerTest.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java new file mode 100644 index 0000000..0f1ba49 --- /dev/null +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java @@ -0,0 +1,79 @@ +package com.ssafy.springbootapi.domain.post.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ssafy.springbootapi.domain.post.dao.PostRepository; +import com.ssafy.springbootapi.domain.post.domain.Post; +import com.ssafy.springbootapi.domain.post.dto.AddPostRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +@SpringBootTest +@AutoConfigureMockMvc +public class PostApiControllerTest { + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @Autowired + private WebApplicationContext context; + + @Autowired + PostRepository postRepository; + + @BeforeEach + public void mockMvcSetUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(context) + .build(); + postRepository.deleteAll(); + } + + @DisplayName("게시글 생성 성공 테스트") + @Test + public void 게시글생성성공테스트() throws Exception { + // given + final String url = "/api/v1/posts"; + final String title = "title"; + final String content = "content"; + final AddPostRequest userRequest = new AddPostRequest(title, content); + + final String requestBody = objectMapper.writeValueAsString(userRequest); + + // when + ResultActions result = mockMvc.perform(post(url) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(requestBody)); + + // then + result.andExpect(status().isCreated()); + + List posts = postRepository.findAll(); + + assertThat(posts.size()).isEqualTo(1); + assertThat(posts.get(0).getTitle()).isEqualTo(title); + assertThat(posts.get(0).getContent()).isEqualTo(content); + } + + +} From 9e1da2eedc782ee10ddbcb98d81b6486e60e07f6 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:42:13 +0000 Subject: [PATCH 18/30] :sparkles: Add: PostApiController --- .../domain/post/api/PostController.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java index d8e94d0..7c6a169 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java @@ -13,15 +13,14 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@Tag(name = "Post", description = "Post API 입니다.") + @RequiredArgsConstructor @RestController -@RequestMapping("/api/v1/posts") public class PostController { private final PostService postService; - @PostMapping + @PostMapping("/api/v1/posts") public ResponseEntity addPost(@RequestBody AddPostRequest request) { Post savedPost = postService.save(request); @@ -29,7 +28,7 @@ public ResponseEntity addPost(@RequestBody AddPostRequest request) { .body(savedPost); } - @GetMapping + @GetMapping("/api/v1/posts") public ResponseEntity> findAllPosts() { List posts = postService.findAll() .stream() @@ -40,7 +39,7 @@ public ResponseEntity> findAllPosts() { .body(posts); } - @GetMapping("/{id}") + @GetMapping("/api/v1/posts/{id}") public ResponseEntity findPost(@PathVariable Long id) { Post post = postService.findById(id); @@ -48,14 +47,14 @@ public ResponseEntity findPost(@PathVariable Long id) { .body(new PostResponse(post)); } - @DeleteMapping("/{id}") + @DeleteMapping("/api/v1/posts/{id}") public ResponseEntity deletePost(@PathVariable Long id) { postService.delete(id); return ResponseEntity.ok() .build(); } - @PutMapping("/{id}") + @PutMapping("/api/v1/posts/{id}") public ResponseEntity updatePost(@PathVariable Long id, @RequestBody UpdatePostRequest request) { Post updatedPost = postService.update(id, request); From 056b3b749a14ed53219af23d22074a0f5e931d70 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:42:22 +0000 Subject: [PATCH 19/30] :sparkles: Add: PostApiControllerTest --- .../domain/post/api/PostControllerTest.java | 418 +++++++++--------- 1 file changed, 209 insertions(+), 209 deletions(-) mode change 100644 => 100755 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java old mode 100644 new mode 100755 index bd0574c..b3d566b --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java @@ -1,209 +1,209 @@ -package com.ssafy.springbootapi.domain.post.api; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ssafy.springbootapi.domain.post.application.PostService; -import com.ssafy.springbootapi.domain.post.domain.Post; - -import com.ssafy.springbootapi.domain.user.domain.User; -import com.ssafy.springbootapi.global.error.GlobalExceptionHandler; -import jakarta.persistence.EntityNotFoundException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentMatchers; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.bind.annotation.ExceptionHandler; - -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Optional; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@ExtendWith(MockitoExtension.class) -class PostControllerTest { - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Mock - private PostService postService; - - @InjectMocks - private PostController postController; - - private MockMvc mockMvc; - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(postController) - .setControllerAdvice(new GlobalExceptionHandler()) - .build(); - } - - @DisplayName("게시글 전부 조회 성공 테스트") - @Test - void 게시글전부조회성공테스트() throws Exception { - - //given - when(postService.getAllPosts()).thenReturn(Arrays.asList( - new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), - new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) - )); - - - //when - mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) - //then - .andExpect(status().isOk()) - .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(Arrays.asList( - new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), - new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) - )))); - } - @DisplayName("게시글 전부 조회 실패 테스트") - @Test - void 게시글전부조회실패테스트() throws Exception { - //given - when(postService.getAllPosts()).thenThrow(new RuntimeException()); - - //when, then - mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) - .andExpect(status().isInternalServerError()); - - } - - - @DisplayName("게시글 아이디로 조회 성공 테스트") - @Test - void 게시글아이디조회성공테스트() throws Exception { - - // given - Long postId = 1L; - Optional post = Optional.of(new Post(postId, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User())); - when(postService.getPostById(postId)).thenReturn(post); - - - //when - mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) - //then - .andExpect(status().isOk()) - .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); - } - @DisplayName("게시글 아이디로 조회 실패 테스트") - @Test - void 게시글아이디조회실패테스트() throws Exception { - // given - Long postId = 1L; - when(postService.getPostById(postId)).thenThrow(new EntityNotFoundException()); - - //when, then - mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) - .andExpect(status().isNotFound()); - } - - - @DisplayName("게시글 생성 성공 테스트") - @Test - void 게시글생성성공테스트() throws Exception { - //given - Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); - - when(postService.savePost(any(Post.class))).thenReturn(post); - - //when - mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") - .contentType(MediaType.APPLICATION_JSON) - .content(new ObjectMapper().writeValueAsString(post))) // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환 - //then - .andExpect(status().isCreated()) - .andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(post))); // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환하여 비교 - } - @DisplayName("게시글 생성 실패 테스트") - @Test - void 게시글생성실패테스트() throws Exception { - //given - Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); - - when(postService.savePost(any(Post.class))).thenThrow(new RuntimeException()); - - //when, then - mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") - .contentType(MediaType.APPLICATION_JSON) - .content(new ObjectMapper().writeValueAsString(post))) - .andExpect(status().isInternalServerError()); - } - - - - @DisplayName("게시글 삭제 성공 테스트") - @Test - void 게시글삭제성공테스트() throws Exception { - //given - Long postId = 1L; - //when - mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) - //then - .andExpect(status().isNoContent()); - - verify(postService, times(1)).deletePost(postId); //postService의 deletePost 메소드가 정확히 한 번 호출되었는지 검증 - } - @DisplayName("게시글 삭제 실패 테스트") - @Test - void 게시글삭제실패테스트() throws Exception { - //given - Long postId = 1L; - doThrow(new RuntimeException()).when(postService).deletePost(postId); - - //when, then - mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) - .andExpect(status().isInternalServerError()); - } - - - @DisplayName("게시글 수정 성공 테스트") - @Test - public void 게시글수정성공테스트() throws Exception { - //given - Post post = new Post(); - post.setId(1L); - post.setContents("Updated content"); - - given(postService.updatePost(any(Long.class), any(Post.class))).willReturn(post); - - //when - mockMvc.perform(put("/api/v1/posts/{id}", 1L) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(post))) - //then - .andExpect(status().isOk()) - .andExpect(jsonPath("$.contents").value("Updated content")); - } - @DisplayName("게시글 수정 실패 테스트") - @Test - public void 게시글수정실패테스트() throws Exception { - //given - Post post = new Post(); - post.setId(1L); - post.setContents("Updated content"); - - given(postService.updatePost(any(Long.class), any(Post.class))).willThrow(new RuntimeException()); - - //when, then - mockMvc.perform(put("/api/v1/posts/{id}", 1L) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(post))) - .andExpect(status().isInternalServerError()); - } - -} +//package com.ssafy.springbootapi.domain.post.api; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.ssafy.springbootapi.domain.post.application.PostService; +//import com.ssafy.springbootapi.domain.post.domain.Post; +// +//import com.ssafy.springbootapi.domain.user.domain.User; +//import com.ssafy.springbootapi.global.error.GlobalExceptionHandler; +//import jakarta.persistence.EntityNotFoundException; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.DisplayName; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.ArgumentMatchers; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.junit.jupiter.MockitoExtension; +//import org.springframework.http.MediaType; +//import org.springframework.test.web.servlet.MockMvc; +//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +//import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +//import org.springframework.test.web.servlet.setup.MockMvcBuilders; +//import org.springframework.web.bind.annotation.ExceptionHandler; +// +//import java.time.LocalDateTime; +//import java.util.Arrays; +//import java.util.Optional; +// +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.BDDMockito.given; +//import static org.mockito.Mockito.*; +//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +// +//@ExtendWith(MockitoExtension.class) +//class PostControllerTest { +// private final ObjectMapper objectMapper = new ObjectMapper(); +// +// @Mock +// private PostService postService; +// +// @InjectMocks +// private PostController postController; +// +// private MockMvc mockMvc; +// @BeforeEach +// void setUp() { +// mockMvc = MockMvcBuilders.standaloneSetup(postController) +// .setControllerAdvice(new GlobalExceptionHandler()) +// .build(); +// } +// +// @DisplayName("게시글 전부 조회 성공 테스트") +// @Test +// void 게시글전부조회성공테스트() throws Exception { +// +// //given +// when(postService.getAllPosts()).thenReturn(Arrays.asList( +// new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), +// new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) +// )); +// +// +// //when +// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) +// //then +// .andExpect(status().isOk()) +// .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(Arrays.asList( +// new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), +// new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) +// )))); +// } +// @DisplayName("게시글 전부 조회 실패 테스트") +// @Test +// void 게시글전부조회실패테스트() throws Exception { +// //given +// when(postService.getAllPosts()).thenThrow(new RuntimeException()); +// +// //when, then +// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) +// .andExpect(status().isInternalServerError()); +// +// } +// +// +// @DisplayName("게시글 아이디로 조회 성공 테스트") +// @Test +// void 게시글아이디조회성공테스트() throws Exception { +// +// // given +// Long postId = 1L; +// Optional post = Optional.of(new Post(postId, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User())); +// when(postService.getPostById(postId)).thenReturn(post); +// +// +// //when +// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) +// //then +// .andExpect(status().isOk()) +// .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); +// } +// @DisplayName("게시글 아이디로 조회 실패 테스트") +// @Test +// void 게시글아이디조회실패테스트() throws Exception { +// // given +// Long postId = 1L; +// when(postService.getPostById(postId)).thenThrow(new EntityNotFoundException()); +// +// //when, then +// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) +// .andExpect(status().isNotFound()); +// } +// +// +// @DisplayName("게시글 생성 성공 테스트") +// @Test +// void 게시글생성성공테스트() throws Exception { +// //given +// Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); +// +// when(postService.savePost(any(Post.class))).thenReturn(post); +// +// //when +// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") +// .contentType(MediaType.APPLICATION_JSON) +// .content(new ObjectMapper().writeValueAsString(post))) // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환 +// //then +// .andExpect(status().isCreated()) +// .andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(post))); // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환하여 비교 +// } +// @DisplayName("게시글 생성 실패 테스트") +// @Test +// void 게시글생성실패테스트() throws Exception { +// //given +// Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); +// +// when(postService.savePost(any(Post.class))).thenThrow(new RuntimeException()); +// +// //when, then +// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") +// .contentType(MediaType.APPLICATION_JSON) +// .content(new ObjectMapper().writeValueAsString(post))) +// .andExpect(status().isInternalServerError()); +// } +// +// +// +// @DisplayName("게시글 삭제 성공 테스트") +// @Test +// void 게시글삭제성공테스트() throws Exception { +// //given +// Long postId = 1L; +// //when +// mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) +// //then +// .andExpect(status().isNoContent()); +// +// verify(postService, times(1)).deletePost(postId); //postService의 deletePost 메소드가 정확히 한 번 호출되었는지 검증 +// } +// @DisplayName("게시글 삭제 실패 테스트") +// @Test +// void 게시글삭제실패테스트() throws Exception { +// //given +// Long postId = 1L; +// doThrow(new RuntimeException()).when(postService).deletePost(postId); +// +// //when, then +// mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) +// .andExpect(status().isInternalServerError()); +// } +// +// +// @DisplayName("게시글 수정 성공 테스트") +// @Test +// public void 게시글수정성공테스트() throws Exception { +// //given +// Post post = new Post(); +// post.setId(1L); +// post.setContents("Updated content"); +// +// given(postService.updatePost(any(Long.class), any(Post.class))).willReturn(post); +// +// //when +// mockMvc.perform(put("/api/v1/posts/{id}", 1L) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(post))) +// //then +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.contents").value("Updated content")); +// } +// @DisplayName("게시글 수정 실패 테스트") +// @Test +// public void 게시글수정실패테스트() throws Exception { +// //given +// Post post = new Post(); +// post.setId(1L); +// post.setContents("Updated content"); +// +// given(postService.updatePost(any(Long.class), any(Post.class))).willThrow(new RuntimeException()); +// +// //when, then +// mockMvc.perform(put("/api/v1/posts/{id}", 1L) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(post))) +// .andExpect(status().isInternalServerError()); +// } +// +//} From 9dac3a81d73f79cfd5969f35782f4272c38a93a3 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:42:29 +0000 Subject: [PATCH 20/30] :sparkles: Add: PostReply --- .../com/ssafy/springbootapi/domain/post/domain/PostReply.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java index 7ea0a08..bed3c59 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/domain/PostReply.java @@ -12,11 +12,11 @@ @Getter public class PostReply { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", updatable = false) private Long id; - @Column(name = "content", nullable = false) + @Column(name = "contents", nullable = false) private String content; @CreatedDate From 47d0df2dcb9b15715ed3d53c732f6a88a6ae0117 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:42:37 +0000 Subject: [PATCH 21/30] :sparkles: Add: PostRepository --- .../com/ssafy/springbootapi/domain/post/dao/PostRepository.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java old mode 100644 new mode 100755 From aaf1eafc1042b14b832ef54958234de57f1e6ff3 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 15 Apr 2024 12:42:46 +0000 Subject: [PATCH 22/30] :sparkles: Add: PostServiceTest.java --- .../post/application/PostServiceTest.java | 280 +++++++++--------- 1 file changed, 140 insertions(+), 140 deletions(-) mode change 100644 => 100755 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java old mode 100644 new mode 100755 index effb38d..333177e --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java @@ -1,140 +1,140 @@ -package com.ssafy.springbootapi.domain.post.application; - -import com.ssafy.springbootapi.domain.post.dao.PostRepository; -import com.ssafy.springbootapi.domain.post.domain.Post; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; - -import java.util.Arrays; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -@SpringBootTest -class PostServiceTest { - - @Autowired - private PostService postService; - - @MockBean - private PostRepository postRepository; - - private Post post; - - @BeforeEach - void setUp() { - post = new Post(); - post.setId(1L); - post.setContents("Test Contents"); - } - @BeforeEach - void clean() { - postRepository.deleteAll(); - } - - @DisplayName("게시글 전부 조회 성공 테스트") - @Test - void 게시글전부조회성공테스트() { - //given when - when(postRepository.findAll()).thenReturn(Arrays.asList(post)); - //then - assertThat(postService.getAllPosts()).hasSize(1); - } - - @DisplayName("게시글 전부 조회 예외 테스트") - @Test - void 게시글전부조회예외테스트() { - when(postRepository.findAll()).thenThrow(new RuntimeException()); - assertThrows(RuntimeException.class, () -> postService.getAllPosts()); - } - - @DisplayName("게시글 아이디로 조회 성공 테스트") - @Test - void 게시글아이디조회테스트() { - //given when - when(postRepository.findById(1L)).thenReturn(Optional.of(post)); - //then - assertThat(postService.getPostById(1L)).isNotEmpty(); - } - @DisplayName("게시글 아이디로 조회 예외 테스트") - @Test - void 게시글아이디조회예외테스트() { - when(postRepository.findById(1L)).thenReturn(Optional.empty()); - assertThat(postService.getPostById(1L)).isEmpty(); - } - - @DisplayName("게시글 등록 성공 테스트") - @Test - void 게시글등록테스트() { - //given when - when(postRepository.save(any(Post.class))).thenReturn(post); - Post savedPost = postService.savePost(new Post()); - //then - assertThat(savedPost.getContents()).isEqualTo("Test Contents"); - } - @DisplayName("게시글 등록 예외 테스트") - @Test - void 게시글등록예외테스트() { - when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); - assertThrows(RuntimeException.class, () -> postService.savePost(new Post())); - } - - - - @DisplayName("게시글 삭제 성공 테스트") - @Test - void 게시글삭제테스트() { - //given - doNothing().when(postRepository).deleteById(1L); - //when - postService.deletePost(1L); - //then - verify(postRepository, times(1)).deleteById(1L); - } - @DisplayName("게시글 삭제 예외 테스트") - @Test - void 게시글삭제예외테스트() { - doThrow(new RuntimeException()).when(postRepository).deleteById(1L); - assertThrows(RuntimeException.class, () -> postService.deletePost(1L)); - } - - - @DisplayName("게시글 수정 성공 테스트") - @Test - void 게시글수정테스트() { - //given - when(postRepository.findById(1L)).thenReturn(Optional.of(post)); - when(postRepository.save(any(Post.class))).thenReturn(post); - Post updatedPost = new Post(); - //when - updatedPost.setContents("Updated Contents"); - Post result = postService.updatePost(1L, updatedPost); - //then - assertThat(result.getContents()).isEqualTo("Updated Contents"); - } - @DisplayName("게시글 수정 존재안함 테스트") - @Test - void 게시글수정존재안함테스트() { - when(postRepository.findById(1L)).thenReturn(Optional.empty()); - Post updatedPost = new Post(); - updatedPost.setContents("Updated Contents"); - assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); - } - @DisplayName("게시글 수정 예외 테스트") - @Test - void 게시글수정예외테스트() { - when(postRepository.findById(1L)).thenReturn(Optional.of(post)); - when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); - Post updatedPost = new Post(); - updatedPost.setContents("Updated Contents"); - assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); - } - -} \ No newline at end of file +//package com.ssafy.springbootapi.domain.post.application; +// +//import com.ssafy.springbootapi.domain.post.dao.PostRepository; +//import com.ssafy.springbootapi.domain.post.domain.Post; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.DisplayName; +//import org.junit.jupiter.api.Test; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.boot.test.mock.mockito.MockBean; +// +//import java.util.Arrays; +//import java.util.Optional; +// +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.junit.jupiter.api.Assertions.*; +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.Mockito.*; +// +//@SpringBootTest +//class PostServiceTest { +// +// @Autowired +// private PostService postService; +// +// @MockBean +// private PostRepository postRepository; +// +// private Post post; +// +// @BeforeEach +// void setUp() { +// post = new Post(); +// post.setId(1L); +// post.setContents("Test Contents"); +// } +// @BeforeEach +// void clean() { +// postRepository.deleteAll(); +// } +// +// @DisplayName("게시글 전부 조회 성공 테스트") +// @Test +// void 게시글전부조회성공테스트() { +// //given when +// when(postRepository.findAll()).thenReturn(Arrays.asList(post)); +// //then +// assertThat(postService.getAllPosts()).hasSize(1); +// } +// +// @DisplayName("게시글 전부 조회 예외 테스트") +// @Test +// void 게시글전부조회예외테스트() { +// when(postRepository.findAll()).thenThrow(new RuntimeException()); +// assertThrows(RuntimeException.class, () -> postService.getAllPosts()); +// } +// +// @DisplayName("게시글 아이디로 조회 성공 테스트") +// @Test +// void 게시글아이디조회테스트() { +// //given when +// when(postRepository.findById(1L)).thenReturn(Optional.of(post)); +// //then +// assertThat(postService.getPostById(1L)).isNotEmpty(); +// } +// @DisplayName("게시글 아이디로 조회 예외 테스트") +// @Test +// void 게시글아이디조회예외테스트() { +// when(postRepository.findById(1L)).thenReturn(Optional.empty()); +// assertThat(postService.getPostById(1L)).isEmpty(); +// } +// +// @DisplayName("게시글 등록 성공 테스트") +// @Test +// void 게시글등록테스트() { +// //given when +// when(postRepository.save(any(Post.class))).thenReturn(post); +// Post savedPost = postService.savePost(new Post()); +// //then +// assertThat(savedPost.getContents()).isEqualTo("Test Contents"); +// } +// @DisplayName("게시글 등록 예외 테스트") +// @Test +// void 게시글등록예외테스트() { +// when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); +// assertThrows(RuntimeException.class, () -> postService.savePost(new Post())); +// } +// +// +// +// @DisplayName("게시글 삭제 성공 테스트") +// @Test +// void 게시글삭제테스트() { +// //given +// doNothing().when(postRepository).deleteById(1L); +// //when +// postService.deletePost(1L); +// //then +// verify(postRepository, times(1)).deleteById(1L); +// } +// @DisplayName("게시글 삭제 예외 테스트") +// @Test +// void 게시글삭제예외테스트() { +// doThrow(new RuntimeException()).when(postRepository).deleteById(1L); +// assertThrows(RuntimeException.class, () -> postService.deletePost(1L)); +// } +// +// +// @DisplayName("게시글 수정 성공 테스트") +// @Test +// void 게시글수정테스트() { +// //given +// when(postRepository.findById(1L)).thenReturn(Optional.of(post)); +// when(postRepository.save(any(Post.class))).thenReturn(post); +// Post updatedPost = new Post(); +// //when +// updatedPost.setContents("Updated Contents"); +// Post result = postService.updatePost(1L, updatedPost); +// //then +// assertThat(result.getContents()).isEqualTo("Updated Contents"); +// } +// @DisplayName("게시글 수정 존재안함 테스트") +// @Test +// void 게시글수정존재안함테스트() { +// when(postRepository.findById(1L)).thenReturn(Optional.empty()); +// Post updatedPost = new Post(); +// updatedPost.setContents("Updated Contents"); +// assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); +// } +// @DisplayName("게시글 수정 예외 테스트") +// @Test +// void 게시글수정예외테스트() { +// when(postRepository.findById(1L)).thenReturn(Optional.of(post)); +// when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); +// Post updatedPost = new Post(); +// updatedPost.setContents("Updated Contents"); +// assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); +// } +// +//} \ No newline at end of file From 1cfe8cf46d4cf88b0c6a26cfab579ce515b2ed2b Mon Sep 17 00:00:00 2001 From: chanmin97 <92173657+chanmin97@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:38:07 +0900 Subject: [PATCH 23/30] :fire: Delete : PostServiceTest --- .../post/application/PostServiceTest.java | 140 ------------------ 1 file changed, 140 deletions(-) delete mode 100755 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java deleted file mode 100755 index 333177e..0000000 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/application/PostServiceTest.java +++ /dev/null @@ -1,140 +0,0 @@ -//package com.ssafy.springbootapi.domain.post.application; -// -//import com.ssafy.springbootapi.domain.post.dao.PostRepository; -//import com.ssafy.springbootapi.domain.post.domain.Post; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.DisplayName; -//import org.junit.jupiter.api.Test; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.test.context.SpringBootTest; -//import org.springframework.boot.test.mock.mockito.MockBean; -// -//import java.util.Arrays; -//import java.util.Optional; -// -//import static org.assertj.core.api.Assertions.assertThat; -//import static org.junit.jupiter.api.Assertions.*; -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.Mockito.*; -// -//@SpringBootTest -//class PostServiceTest { -// -// @Autowired -// private PostService postService; -// -// @MockBean -// private PostRepository postRepository; -// -// private Post post; -// -// @BeforeEach -// void setUp() { -// post = new Post(); -// post.setId(1L); -// post.setContents("Test Contents"); -// } -// @BeforeEach -// void clean() { -// postRepository.deleteAll(); -// } -// -// @DisplayName("게시글 전부 조회 성공 테스트") -// @Test -// void 게시글전부조회성공테스트() { -// //given when -// when(postRepository.findAll()).thenReturn(Arrays.asList(post)); -// //then -// assertThat(postService.getAllPosts()).hasSize(1); -// } -// -// @DisplayName("게시글 전부 조회 예외 테스트") -// @Test -// void 게시글전부조회예외테스트() { -// when(postRepository.findAll()).thenThrow(new RuntimeException()); -// assertThrows(RuntimeException.class, () -> postService.getAllPosts()); -// } -// -// @DisplayName("게시글 아이디로 조회 성공 테스트") -// @Test -// void 게시글아이디조회테스트() { -// //given when -// when(postRepository.findById(1L)).thenReturn(Optional.of(post)); -// //then -// assertThat(postService.getPostById(1L)).isNotEmpty(); -// } -// @DisplayName("게시글 아이디로 조회 예외 테스트") -// @Test -// void 게시글아이디조회예외테스트() { -// when(postRepository.findById(1L)).thenReturn(Optional.empty()); -// assertThat(postService.getPostById(1L)).isEmpty(); -// } -// -// @DisplayName("게시글 등록 성공 테스트") -// @Test -// void 게시글등록테스트() { -// //given when -// when(postRepository.save(any(Post.class))).thenReturn(post); -// Post savedPost = postService.savePost(new Post()); -// //then -// assertThat(savedPost.getContents()).isEqualTo("Test Contents"); -// } -// @DisplayName("게시글 등록 예외 테스트") -// @Test -// void 게시글등록예외테스트() { -// when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); -// assertThrows(RuntimeException.class, () -> postService.savePost(new Post())); -// } -// -// -// -// @DisplayName("게시글 삭제 성공 테스트") -// @Test -// void 게시글삭제테스트() { -// //given -// doNothing().when(postRepository).deleteById(1L); -// //when -// postService.deletePost(1L); -// //then -// verify(postRepository, times(1)).deleteById(1L); -// } -// @DisplayName("게시글 삭제 예외 테스트") -// @Test -// void 게시글삭제예외테스트() { -// doThrow(new RuntimeException()).when(postRepository).deleteById(1L); -// assertThrows(RuntimeException.class, () -> postService.deletePost(1L)); -// } -// -// -// @DisplayName("게시글 수정 성공 테스트") -// @Test -// void 게시글수정테스트() { -// //given -// when(postRepository.findById(1L)).thenReturn(Optional.of(post)); -// when(postRepository.save(any(Post.class))).thenReturn(post); -// Post updatedPost = new Post(); -// //when -// updatedPost.setContents("Updated Contents"); -// Post result = postService.updatePost(1L, updatedPost); -// //then -// assertThat(result.getContents()).isEqualTo("Updated Contents"); -// } -// @DisplayName("게시글 수정 존재안함 테스트") -// @Test -// void 게시글수정존재안함테스트() { -// when(postRepository.findById(1L)).thenReturn(Optional.empty()); -// Post updatedPost = new Post(); -// updatedPost.setContents("Updated Contents"); -// assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); -// } -// @DisplayName("게시글 수정 예외 테스트") -// @Test -// void 게시글수정예외테스트() { -// when(postRepository.findById(1L)).thenReturn(Optional.of(post)); -// when(postRepository.save(any(Post.class))).thenThrow(new RuntimeException()); -// Post updatedPost = new Post(); -// updatedPost.setContents("Updated Contents"); -// assertThrows(RuntimeException.class, () -> postService.updatePost(1L, updatedPost)); -// } -// -//} \ No newline at end of file From 35f76a8d47bdd36813b0f68745f8b416c6d7356d Mon Sep 17 00:00:00 2001 From: chanmin97 <92173657+chanmin97@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:38:45 +0900 Subject: [PATCH 24/30] :fire: Delete: PostControllerTest --- .../domain/post/api/PostControllerTest.java | 209 ------------------ 1 file changed, 209 deletions(-) delete mode 100755 spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java deleted file mode 100755 index b3d566b..0000000 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostControllerTest.java +++ /dev/null @@ -1,209 +0,0 @@ -//package com.ssafy.springbootapi.domain.post.api; -// -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.ssafy.springbootapi.domain.post.application.PostService; -//import com.ssafy.springbootapi.domain.post.domain.Post; -// -//import com.ssafy.springbootapi.domain.user.domain.User; -//import com.ssafy.springbootapi.global.error.GlobalExceptionHandler; -//import jakarta.persistence.EntityNotFoundException; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.DisplayName; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.extension.ExtendWith; -//import org.mockito.ArgumentMatchers; -//import org.mockito.InjectMocks; -//import org.mockito.Mock; -//import org.mockito.junit.jupiter.MockitoExtension; -//import org.springframework.http.MediaType; -//import org.springframework.test.web.servlet.MockMvc; -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -//import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -//import org.springframework.test.web.servlet.setup.MockMvcBuilders; -//import org.springframework.web.bind.annotation.ExceptionHandler; -// -//import java.time.LocalDateTime; -//import java.util.Arrays; -//import java.util.Optional; -// -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.BDDMockito.given; -//import static org.mockito.Mockito.*; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -// -//@ExtendWith(MockitoExtension.class) -//class PostControllerTest { -// private final ObjectMapper objectMapper = new ObjectMapper(); -// -// @Mock -// private PostService postService; -// -// @InjectMocks -// private PostController postController; -// -// private MockMvc mockMvc; -// @BeforeEach -// void setUp() { -// mockMvc = MockMvcBuilders.standaloneSetup(postController) -// .setControllerAdvice(new GlobalExceptionHandler()) -// .build(); -// } -// -// @DisplayName("게시글 전부 조회 성공 테스트") -// @Test -// void 게시글전부조회성공테스트() throws Exception { -// -// //given -// when(postService.getAllPosts()).thenReturn(Arrays.asList( -// new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), -// new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) -// )); -// -// -// //when -// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) -// //then -// .andExpect(status().isOk()) -// .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(Arrays.asList( -// new Post(1L, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()), -// new Post(2L, "Second post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()) -// )))); -// } -// @DisplayName("게시글 전부 조회 실패 테스트") -// @Test -// void 게시글전부조회실패테스트() throws Exception { -// //given -// when(postService.getAllPosts()).thenThrow(new RuntimeException()); -// -// //when, then -// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts")) -// .andExpect(status().isInternalServerError()); -// -// } -// -// -// @DisplayName("게시글 아이디로 조회 성공 테스트") -// @Test -// void 게시글아이디조회성공테스트() throws Exception { -// -// // given -// Long postId = 1L; -// Optional post = Optional.of(new Post(postId, "First post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User())); -// when(postService.getPostById(postId)).thenReturn(post); -// -// -// //when -// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) -// //then -// .andExpect(status().isOk()) -// .andExpect(MockMvcResultMatchers.content().json(objectMapper.writeValueAsString(post.get()))); -// } -// @DisplayName("게시글 아이디로 조회 실패 테스트") -// @Test -// void 게시글아이디조회실패테스트() throws Exception { -// // given -// Long postId = 1L; -// when(postService.getPostById(postId)).thenThrow(new EntityNotFoundException()); -// -// //when, then -// mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/posts/{id}", postId)) -// .andExpect(status().isNotFound()); -// } -// -// -// @DisplayName("게시글 생성 성공 테스트") -// @Test -// void 게시글생성성공테스트() throws Exception { -// //given -// Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); -// -// when(postService.savePost(any(Post.class))).thenReturn(post); -// -// //when -// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") -// .contentType(MediaType.APPLICATION_JSON) -// .content(new ObjectMapper().writeValueAsString(post))) // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환 -// //then -// .andExpect(status().isCreated()) -// .andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(post))); // ObjectMapper를 사용하여 객체를 JSON 형식으로 변환하여 비교 -// } -// @DisplayName("게시글 생성 실패 테스트") -// @Test -// void 게시글생성실패테스트() throws Exception { -// //given -// Post post = new Post(1L, "New post", LocalDateTime.now(), LocalDateTime.now(), 0L, 0L, 0L, new User()); -// -// when(postService.savePost(any(Post.class))).thenThrow(new RuntimeException()); -// -// //when, then -// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/posts") -// .contentType(MediaType.APPLICATION_JSON) -// .content(new ObjectMapper().writeValueAsString(post))) -// .andExpect(status().isInternalServerError()); -// } -// -// -// -// @DisplayName("게시글 삭제 성공 테스트") -// @Test -// void 게시글삭제성공테스트() throws Exception { -// //given -// Long postId = 1L; -// //when -// mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) -// //then -// .andExpect(status().isNoContent()); -// -// verify(postService, times(1)).deletePost(postId); //postService의 deletePost 메소드가 정확히 한 번 호출되었는지 검증 -// } -// @DisplayName("게시글 삭제 실패 테스트") -// @Test -// void 게시글삭제실패테스트() throws Exception { -// //given -// Long postId = 1L; -// doThrow(new RuntimeException()).when(postService).deletePost(postId); -// -// //when, then -// mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1/posts/{id}", postId)) -// .andExpect(status().isInternalServerError()); -// } -// -// -// @DisplayName("게시글 수정 성공 테스트") -// @Test -// public void 게시글수정성공테스트() throws Exception { -// //given -// Post post = new Post(); -// post.setId(1L); -// post.setContents("Updated content"); -// -// given(postService.updatePost(any(Long.class), any(Post.class))).willReturn(post); -// -// //when -// mockMvc.perform(put("/api/v1/posts/{id}", 1L) -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(post))) -// //then -// .andExpect(status().isOk()) -// .andExpect(jsonPath("$.contents").value("Updated content")); -// } -// @DisplayName("게시글 수정 실패 테스트") -// @Test -// public void 게시글수정실패테스트() throws Exception { -// //given -// Post post = new Post(); -// post.setId(1L); -// post.setContents("Updated content"); -// -// given(postService.updatePost(any(Long.class), any(Post.class))).willThrow(new RuntimeException()); -// -// //when, then -// mockMvc.perform(put("/api/v1/posts/{id}", 1L) -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(post))) -// .andExpect(status().isInternalServerError()); -// } -// -//} From 7d30459d77bddb1e77c76e4f2846a4cf09f1830c Mon Sep 17 00:00:00 2001 From: chanmin97 <92173657+chanmin97@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:39:11 +0900 Subject: [PATCH 25/30] :fire: Delete: User --- .../domain/user/domain/User.java | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java deleted file mode 100644 index 9dad15c..0000000 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/user/domain/User.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ssafy.springbootapi.domain.user.domain; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import lombok.Getter; -import lombok.Setter; - -@Entity -@Getter -@Setter -public class User { - @Id - @GeneratedValue - private Long id; - - private String username; - - - // 생성자, getter, setter 등은 생략 -} From f8f28c50b280de8bb6f8dc2268e8e5137512959a Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Tue, 16 Apr 2024 13:22:07 +0000 Subject: [PATCH 26/30] :white_check_mark: Test: PostApiControllerTest --- .../src/main/resources/application.yml | 2 +- .../post/api/PostApiControllerTest.java | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/spring-boot-api/src/main/resources/application.yml b/spring-boot-api/src/main/resources/application.yml index 48f4a90..408e372 100644 --- a/spring-boot-api/src/main/resources/application.yml +++ b/spring-boot-api/src/main/resources/application.yml @@ -10,7 +10,7 @@ spring: database: mysql database-platform: org.hibernate.dialect.MySQL8Dialect hibernate: - ddl-auto: none + ddl-auto: create generate-ddl: false show-sql: true properties: diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java index 0f1ba49..56f7803 100644 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java @@ -4,6 +4,7 @@ import com.ssafy.springbootapi.domain.post.dao.PostRepository; import com.ssafy.springbootapi.domain.post.domain.Post; import com.ssafy.springbootapi.domain.post.dto.AddPostRequest; +import com.ssafy.springbootapi.domain.post.dto.UpdatePostRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -75,5 +76,106 @@ public void mockMvcSetUp() { assertThat(posts.get(0).getContent()).isEqualTo(content); } + @DisplayName("게시글 전부 조회 성공 테스트") + @Test + public void 게시글전부조회성공테스트() throws Exception { + //given + final String url = "/api/v1/posts"; + final String title = "title"; + final String content = "content"; + + postRepository.save(Post.builder() + .title(title) + .content(content) + .build()); + + //when + final ResultActions resultActions = mockMvc.perform(get(url) + .contentType(MediaType.APPLICATION_JSON)); + + //then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].content").value(content)) + .andExpect(jsonPath("$[0].title").value(title)); + } + + @DisplayName("게시글 아이디 조회 성공 테스트") + @Test + public void 게시글아이디조회성공테스트() throws Exception { + //given + final String url = "/api/v1/posts/{id}"; + final String title = "title"; + final String content = "content"; + + Post savedPost = postRepository.save(Post.builder() + .title(title) + .content(content) + .build()); + + //when + final ResultActions resultActions = mockMvc.perform(get(url, savedPost.getId())); + + //then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").value(content)) + .andExpect(jsonPath("$.title").value(title)); + + } + + @DisplayName("게시글 삭제 성공 테스트") + @Test + public void 게시글삭제성공테스트() throws Exception{ + // given + final String url = "/api/v1/posts/{id}"; + final String title = "title"; + final String content = "content"; + + Post savedPost = postRepository.save(Post.builder() + .title(title) + .content(content) + .build()); + + //when + mockMvc.perform(delete(url, savedPost.getId())) + .andExpect(status().isOk()); + + //then + List posts = postRepository.findAll(); + + assertThat(posts).isEmpty(); + } + + @DisplayName("게시글 수정 성공 테스트") + @Test + public void 게시글수정성공테스트() throws Exception { + // given + final String url = "/api/v1/posts/{id}"; + final String title = "title"; + final String content = "content"; + + Post savedPost = postRepository.save(Post.builder() + .title(title) + .content(content) + .build()); + + final String newTitle = "new title"; + final String newContent = "new content"; + + UpdatePostRequest request = new UpdatePostRequest(newTitle, newContent); + + //when + ResultActions result = mockMvc.perform(put(url, savedPost.getId()) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))); + + // then + result.andExpect(status().isOk()); + Post post = postRepository.findById(savedPost.getId()).get(); + + assertThat(post.getTitle()).isEqualTo(newTitle); + assertThat(post.getContent()).isEqualTo(newContent); + } } From c416a97720cb40d8737cabcd54e67a16e5cd2c00 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Mon, 22 Apr 2024 15:01:31 +0000 Subject: [PATCH 27/30] =?UTF-8?q?:white=5Fcheck=5Fmark:=20Test:=20PostApiC?= =?UTF-8?q?ontrollerTest=EC=83=9D=EC=84=B1=EC=98=88=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-boot-api/build.gradle | 1 + .../domain/post/api/PostController.java | 4 ++- .../domain/post/dto/AddPostRequest.java | 6 +++++ .../post/api/PostApiControllerTest.java | 26 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/spring-boot-api/build.gradle b/spring-boot-api/build.gradle index a0bd52a..354f021 100755 --- a/spring-boot-api/build.gradle +++ b/spring-boot-api/build.gradle @@ -24,6 +24,7 @@ dependencies { runtimeOnly 'com.mysql:mysql-connector-j' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-validation' // for lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java index 7c6a169..8b03295 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java @@ -6,10 +6,12 @@ import com.ssafy.springbootapi.domain.post.dto.PostResponse; import com.ssafy.springbootapi.domain.post.dto.UpdatePostRequest; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -21,7 +23,7 @@ public class PostController { private final PostService postService; @PostMapping("/api/v1/posts") - public ResponseEntity addPost(@RequestBody AddPostRequest request) { + public ResponseEntity addPost(@Validated @RequestBody AddPostRequest request) { Post savedPost = postService.save(request); return ResponseEntity.status(HttpStatus.CREATED) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java index 7da76c3..cba21e2 100644 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dto/AddPostRequest.java @@ -1,6 +1,8 @@ package com.ssafy.springbootapi.domain.post.dto; import com.ssafy.springbootapi.domain.post.domain.Post; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,8 +12,12 @@ @AllArgsConstructor @Getter public class AddPostRequest { + @NotEmpty + @NotNull private String title; + @NotEmpty + @NotNull private String content; public Post toEntity() { diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java index 56f7803..7b118f0 100644 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java @@ -76,6 +76,32 @@ public void mockMvcSetUp() { assertThat(posts.get(0).getContent()).isEqualTo(content); } + @DisplayName("게시글 생성 실패 테스트 - 유효하지 않은 입력 값") + @Test + public void 게시글생성실패테스트_유효하지않은입력값() throws Exception { + // given + final String url = "/api/v1/posts"; + final String invalidTitle = ""; // 유효하지 않은 제목 + final String invalidContent = ""; // 유효하지 않은 내용 + final AddPostRequest userRequestWithInvalidData = new AddPostRequest(invalidTitle, invalidContent); + + final String requestBody = objectMapper.writeValueAsString(userRequestWithInvalidData); + + // when + ResultActions result = mockMvc.perform(post(url) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(requestBody)); + + // then + result.andExpect(status().isBadRequest()); // 400 Bad Request 상태 코드 예상 + + List posts = postRepository.findAll(); + + // 게시글이 생성되지 않았으므로, 저장된 게시글이 없어야 함 + assertThat(posts.size()).isEqualTo(0); + } + + @DisplayName("게시글 전부 조회 성공 테스트") @Test public void 게시글전부조회성공테스트() throws Exception { From 90bf54b9ccd83178dffd4c35c6dccc29090c01e9 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Sat, 4 May 2024 10:37:31 +0000 Subject: [PATCH 28/30] =?UTF-8?q?:sparkles:=20Feat=20:=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/api/PostController.java | 26 ++++++++++++++----- .../domain/post/application/PostService.java | 11 ++++++++ .../domain/post/dao/PostRepository.java | 4 ++- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java index b62354a..4398eaa 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/api/PostController.java @@ -10,6 +10,8 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; @@ -33,18 +35,28 @@ public ResponseEntity addPost(@Validated @RequestBody AddPostRequest reque .body(savedPost); } - @Operation(summary = "게시글 리스트 불러오기") - @GetMapping("/api/v1/posts") - public ResponseEntity> findAllPosts() { - List posts = postService.findAll() - .stream() - .map(PostResponse::new) - .toList(); + + /** 스웨거에 넣을때 + * { + * "page": 0, //페이지번호 + * "size": 3, //한페이지당 몇개 표시할지 + * "sort": [ + * "id,asc" // id 오름차순 + * ] + * } + * @param pageable + * @return + */ + @Operation(summary = "게시글 리스트 불러오기 페이지버전") + @GetMapping("/api/v1/posts") + public ResponseEntity> findAllPosts(Pageable pageable) { + Page posts = postService.findAll(pageable); return ResponseEntity.ok() .body(posts); } + @Operation(summary = "게시글 아이디로 조회") @GetMapping("/api/v1/posts/{id}") public ResponseEntity findPost(@PathVariable Long id) { diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java index f733606..5712103 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java @@ -3,10 +3,13 @@ import com.ssafy.springbootapi.domain.post.dao.PostRepository; import com.ssafy.springbootapi.domain.post.domain.Post; import com.ssafy.springbootapi.domain.post.dto.AddPostRequest; +import com.ssafy.springbootapi.domain.post.dto.PostResponse; import com.ssafy.springbootapi.domain.post.dto.UpdatePostRequest; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @@ -44,4 +47,12 @@ public Post update(long id, UpdatePostRequest request) { return post; } + + //페이징 기능 + public Page findAll(Pageable pageable) { + Page posts = postRepository.findAll(pageable); + return posts.map(PostResponse::new); // 여기서 post를 PostReponse객체로 전부 변환 + //이렇게 해주면 Post가 외부에 노출되는 것을 방지하고, 필요한 데이터만을 담은 DTO(PostResponse)를 클라이언트에 반환하게됨 + } + } \ No newline at end of file diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java index 5db9e47..19bceaf 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/dao/PostRepository.java @@ -1,10 +1,12 @@ package com.ssafy.springbootapi.domain.post.dao; import com.ssafy.springbootapi.domain.post.domain.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface PostRepository extends JpaRepository { - + Page findAll(Pageable pageable); //페이징 기능기준 } From cba91dd0703dd779f7f26603059a7bf7e5d3e511 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Sat, 4 May 2024 10:39:06 +0000 Subject: [PATCH 29/30] =?UTF-8?q?:white=5Fcheck=5Fmark:=20Test=20:=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/api/PostApiControllerTest.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java index 7b118f0..bd88736 100644 --- a/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java +++ b/spring-boot-api/src/test/java/com/ssafy/springbootapi/domain/post/api/PostApiControllerTest.java @@ -101,12 +101,11 @@ public void mockMvcSetUp() { assertThat(posts.size()).isEqualTo(0); } - - @DisplayName("게시글 전부 조회 성공 테스트") + @DisplayName("게시글 페이징 조회 성공 테스트") @Test - public void 게시글전부조회성공테스트() throws Exception { + public void 게시글페이징조회성공테스트() throws Exception { //given - final String url = "/api/v1/posts"; + final String url = "/api/v1/posts?page=0&size=10"; final String title = "title"; final String content = "content"; @@ -122,8 +121,8 @@ public void mockMvcSetUp() { //then resultActions .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].content").value(content)) - .andExpect(jsonPath("$[0].title").value(title)); + .andExpect(jsonPath("$.content[0].content").value(content)) + .andExpect(jsonPath("$.content[0].title").value(title)); } @DisplayName("게시글 아이디 조회 성공 테스트") From 9def7a0c151f8e81cc926140ae9c058df6e25428 Mon Sep 17 00:00:00 2001 From: chanmin97 Date: Wed, 8 May 2024 09:03:22 +0900 Subject: [PATCH 30/30] =?UTF-8?q?Comment:=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A3=BC=EC=84=9D=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/application/PostService.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java index 5712103..0811a1b 100755 --- a/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java +++ b/spring-boot-api/src/main/java/com/ssafy/springbootapi/domain/post/application/PostService.java @@ -20,23 +20,48 @@ public class PostService { private final PostRepository postRepository; + /** + * 게시글 등록 + * @param request + * @return + */ public Post save(AddPostRequest request) { return postRepository.save(request.toEntity()); } + /** + * 게시글 리스트 불러오기 + * @return + */ public List findAll(){ return postRepository.findAll(); } + /** + * id로 게시글 찾기 + * @param id 찾을 게시글 id + * @return + */ public Post findById(long id) { return postRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("not found : " + id)); } + + /** + * 게시글 삭제 + * @param id 삭제할 게시글 id + */ public void delete(long id) { postRepository.deleteById(id); } + /** + * 게시글 수정 + * @param id 수정할 게시글 id + * @param request + * @return + */ @Transactional public Post update(long id, UpdatePostRequest request) { Post post = postRepository.findById(id) @@ -48,6 +73,11 @@ public Post update(long id, UpdatePostRequest request) { } + /** + * 페이지 나눠서 리스트 불러오기 + * @param pageable + * @return + */ //페이징 기능 public Page findAll(Pageable pageable) { Page posts = postRepository.findAll(pageable);