diff --git a/src/main/java/cotato/bookitlist/post/controller/PostController.java b/src/main/java/cotato/bookitlist/post/controller/PostController.java index 69b3887..bb2024f 100644 --- a/src/main/java/cotato/bookitlist/post/controller/PostController.java +++ b/src/main/java/cotato/bookitlist/post/controller/PostController.java @@ -152,6 +152,15 @@ public ResponseEntity getRecommendPosts( return ResponseEntity.ok(postService.getRecommendPosts(type, start, details.getId())); } + @PatchMapping("/{post-id}/status") + public ResponseEntity togglePostStatus( + @PathVariable("post-id") Long postId, + @AuthenticationPrincipal AuthDetails details + ) { + postService.togglePostStatus(postId, details.getId()); + return ResponseEntity.ok().build(); + } + private void handlePostViewCount(HttpServletRequest request, HttpServletResponse response, Long postId) { Cookie[] cookies = request.getCookies(); Cookie postViewCookie = findCookie(cookies); diff --git a/src/main/java/cotato/bookitlist/post/domain/entity/Post.java b/src/main/java/cotato/bookitlist/post/domain/entity/Post.java index b6081d6..28ed601 100644 --- a/src/main/java/cotato/bookitlist/post/domain/entity/Post.java +++ b/src/main/java/cotato/bookitlist/post/domain/entity/Post.java @@ -95,4 +95,8 @@ public void validatePostLike(Member member) { throw new AccessDeniedException("본인의 게시글은 좋아요할 수 없습니다."); } } + + public void toggleStatus() { + status = (status == PostStatus.PRIVATE) ? PostStatus.PUBLIC : PostStatus.PRIVATE; + } } diff --git a/src/main/java/cotato/bookitlist/post/service/PostService.java b/src/main/java/cotato/bookitlist/post/service/PostService.java index 4c5c5b4..9b02137 100644 --- a/src/main/java/cotato/bookitlist/post/service/PostService.java +++ b/src/main/java/cotato/bookitlist/post/service/PostService.java @@ -128,4 +128,11 @@ public PostListResponse getNewPosts(int start, Long memberId) { return PostListResponse.from(postPage, memberId); } + + public void togglePostStatus(Long postId, Long memberId) { + Post post = postRepository.findByIdAndMemberId(postId, memberId) + .orElseThrow(() -> new EntityNotFoundException("게시글을 찾을 수 없습니다.")); + + post.toggleStatus(); + } } diff --git a/src/main/java/cotato/bookitlist/review/controller/ReviewController.java b/src/main/java/cotato/bookitlist/review/controller/ReviewController.java index 68b56fe..3fb16ff 100644 --- a/src/main/java/cotato/bookitlist/review/controller/ReviewController.java +++ b/src/main/java/cotato/bookitlist/review/controller/ReviewController.java @@ -157,6 +157,15 @@ public ResponseEntity getBestReviewOfBook( return ResponseEntity.ok(reviewService.getBestReviewOfBook(isbn13)); } + @PatchMapping("/{review-id}/status") + public ResponseEntity toggleReviewStatus( + @PathVariable("review-id") Long reviewId, + @AuthenticationPrincipal AuthDetails details + ) { + reviewService.toggleReviewStatus(reviewId, details.getId()); + return ResponseEntity.ok().build(); + } + private void handleReviewViewCount(HttpServletRequest request, HttpServletResponse response, Long reviewId) { Cookie[] cookies = request.getCookies(); Cookie reviewViewCookie = findCookie(cookies); diff --git a/src/main/java/cotato/bookitlist/review/domain/entity/Review.java b/src/main/java/cotato/bookitlist/review/domain/entity/Review.java index d22d36d..f97b5c3 100644 --- a/src/main/java/cotato/bookitlist/review/domain/entity/Review.java +++ b/src/main/java/cotato/bookitlist/review/domain/entity/Review.java @@ -87,4 +87,8 @@ public void validateReviewLike(Member member) { throw new AccessDeniedException("본인의 한줄요약은 좋아요할 수 없습니다."); } } + + public void toggleStatus() { + status = (status == ReviewStatus.PRIVATE) ? ReviewStatus.PUBLIC : ReviewStatus.PRIVATE; + } } diff --git a/src/main/java/cotato/bookitlist/review/service/ReviewService.java b/src/main/java/cotato/bookitlist/review/service/ReviewService.java index f97504e..418b31c 100644 --- a/src/main/java/cotato/bookitlist/review/service/ReviewService.java +++ b/src/main/java/cotato/bookitlist/review/service/ReviewService.java @@ -139,4 +139,11 @@ public ReviewSimpleResponse getBestReviewOfBook(String isbn13) { return reviewRepository.findBestReview(book) .orElseThrow(() -> new EntityNotFoundException("한줄요약을 찾을 수 없습니다.")); } + + public void toggleReviewStatus(Long reviewId, Long memberId) { + Review review = reviewRepository.findByIdAndMemberId(reviewId, memberId) + .orElseThrow(() -> new EntityNotFoundException("한줄요약을 찾을 수 없습니다.")); + + review.toggleStatus(); + } } diff --git a/src/test/java/cotato/bookitlist/fixture/ReviewFixture.java b/src/test/java/cotato/bookitlist/fixture/ReviewFixture.java index 6cb013b..5d21960 100644 --- a/src/test/java/cotato/bookitlist/fixture/ReviewFixture.java +++ b/src/test/java/cotato/bookitlist/fixture/ReviewFixture.java @@ -8,9 +8,13 @@ import static cotato.bookitlist.fixture.MemberFixture.createMember; public class ReviewFixture { - public static Review createReview(Long reviewId) { + public static Review createReview(Long reviewId, Long memberId) { Review review = Review.of(createMember(), createBook(), "content", ReviewStatus.PUBLIC); ReflectionTestUtils.setField(review, "id", reviewId); return review; } + + public static Review createReview(Long reviewId) { + return createReview(reviewId, 1L); + } } diff --git a/src/test/java/cotato/bookitlist/post/controller/PostControllerTest.java b/src/test/java/cotato/bookitlist/post/controller/PostControllerTest.java index 82e34bc..9d45ade 100644 --- a/src/test/java/cotato/bookitlist/post/controller/PostControllerTest.java +++ b/src/test/java/cotato/bookitlist/post/controller/PostControllerTest.java @@ -557,5 +557,45 @@ void givenPageStartAndRecommendType_whenGettingNewPosts_thenReturnNewPosts() thr .andExpect(jsonPath("$.postList[1].postId").value(2)) ; } + + @Test + @WithCustomMockUser + @DisplayName("본인의 게시글 상태를 바꾼다") + void givenWithLogin_whenTogglingPostStatus_thenTogglePostStatus() throws Exception { + //given + + //when & then + mockMvc.perform(patch("/posts/1/status") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + ; + } + + @Test + @WithCustomMockUser + @DisplayName("다른 사람 게시글 상태를 바꾼다") + void givenNonMyPost_whenTogglingPostStatus_thenErrorResponse() throws Exception { + //given + + //when & then + mockMvc.perform(patch("/posts/2/status") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + ; + } + + @Test + @DisplayName("로그인 없이 게시글 상태를 바꾼면 에러를 반환한다.") + void givenWithNonLogin_whenTogglingPostStatus_thenReturnErrorResponse() throws Exception { + //given + + //when & then + mockMvc.perform(patch("/posts/1/status") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnauthorized()) + ; + } + + } diff --git a/src/test/java/cotato/bookitlist/post/service/PostServiceTest.java b/src/test/java/cotato/bookitlist/post/service/PostServiceTest.java index a8b85c4..db1eba2 100644 --- a/src/test/java/cotato/bookitlist/post/service/PostServiceTest.java +++ b/src/test/java/cotato/bookitlist/post/service/PostServiceTest.java @@ -1,5 +1,6 @@ package cotato.bookitlist.post.service; +import cotato.bookitlist.post.domain.PostStatus; import cotato.bookitlist.post.domain.entity.Post; import cotato.bookitlist.post.repository.PostRepository; import org.junit.jupiter.api.DisplayName; @@ -59,4 +60,20 @@ void givenPostId_whenDeletingPost_thenDeletePost() throws Exception { assertThat(post.getLikeCount()).isZero(); } + @Test + @DisplayName("본인의 게시글 상태를 바꾼다") + void givenWithLogin_whenTogglingPostStatus_thenTogglePostStatus() throws Exception { + //given + Long postId = 1L; + Long memberId = 1L; + Post post = createPost(postId, memberId); + given(postRepository.findByIdAndMemberId(postId, memberId)).willReturn(Optional.of(post)); + + //when + sut.togglePostStatus(postId, memberId); + + //then + then(postRepository).should().findByIdAndMemberId(postId, memberId); + assertThat(post.getStatus()).isEqualTo(PostStatus.PRIVATE); + } } diff --git a/src/test/java/cotato/bookitlist/review/controller/ReviewControllerTest.java b/src/test/java/cotato/bookitlist/review/controller/ReviewControllerTest.java index 5b5047f..938f81e 100644 --- a/src/test/java/cotato/bookitlist/review/controller/ReviewControllerTest.java +++ b/src/test/java/cotato/bookitlist/review/controller/ReviewControllerTest.java @@ -537,4 +537,42 @@ void givenIsbn13NonReview_whenGettingBestReviewOfBook_thenReturnError() throws E .andExpect(jsonPath("$.message").value("한줄요약을 찾을 수 없습니다.")) ; } + + @Test + @WithCustomMockUser + @DisplayName("본인의 한줄요약 상태를 바꾼다") + void givenWithLogin_whenTogglingReviewStatus_thenToggleReviewStatus() throws Exception { + //given + + //when & then + mockMvc.perform(patch("/reviews/1/status") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + ; + } + + @Test + @WithCustomMockUser + @DisplayName("다른 사람 한줄요약 상태를 바꾼다") + void givenNonMyReview_whenTogglingReviewStatus_thenErrorResponse() throws Exception { + //given + + //when & then + mockMvc.perform(patch("/reviews/2/status") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + ; + } + + @Test + @DisplayName("로그인 없이 한줄요약 상태를 바꾼면 에러를 반환한다.") + void givenWithNonLogin_whenTogglingReviewStatus_thenReturnErrorResponse() throws Exception { + //given + + //when & then + mockMvc.perform(patch("/reviews/1/status") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnauthorized()) + ; + } } diff --git a/src/test/java/cotato/bookitlist/review/service/ReviewServiceTest.java b/src/test/java/cotato/bookitlist/review/service/ReviewServiceTest.java index ffa47c8..bb9ed87 100644 --- a/src/test/java/cotato/bookitlist/review/service/ReviewServiceTest.java +++ b/src/test/java/cotato/bookitlist/review/service/ReviewServiceTest.java @@ -1,5 +1,6 @@ package cotato.bookitlist.review.service; +import cotato.bookitlist.review.domain.ReviewStatus; import cotato.bookitlist.review.domain.entity.Review; import cotato.bookitlist.review.repository.ReviewRepository; import org.junit.jupiter.api.DisplayName; @@ -9,6 +10,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Optional; + import static cotato.bookitlist.fixture.ReviewFixture.createReview; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -39,4 +42,20 @@ void givenReviewId_whenIncreasingViewCount_thenIncreaseViewCount() { assertThat(review.getViewCount()).isEqualTo(1); } + @Test + @DisplayName("본인의 한줄요약 상태를 바꾼다") + void givenWithLogin_whenTogglingReviewStatus_thenToggleReviewStatus() throws Exception { + //given + Long reviewId = 1L; + Long memberId = 1L; + Review review = createReview(reviewId, memberId); + given(reviewRepository.findByIdAndMemberId(reviewId, memberId)).willReturn(Optional.of(review)); + + //when + sut.toggleReviewStatus(reviewId, memberId); + + //then + then(reviewRepository).should().findByIdAndMemberId(reviewId, memberId); + assertThat(review.getStatus()).isEqualTo(ReviewStatus.PRIVATE); + } }