Skip to content

Commit 074e711

Browse files
authored
Merge pull request #178 from SEMOSAN/refactor/#173-owned-by-method
[Refactor] 소유권 검증을 엔티티 메서드로 통일
2 parents 6861fda + 3014855 commit 074e711

9 files changed

Lines changed: 150 additions & 4 deletions

File tree

src/main/java/com/semosan/api/domain/community/comment/entity/Comment.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,8 @@ public void softDelete() {
7272
public boolean isReply() {
7373
return this.parent != null;
7474
}
75+
76+
public boolean isOwnedBy(Long userId) {
77+
return author != null && userId != null && userId.equals(author.getId());
78+
}
7579
}

src/main/java/com/semosan/api/domain/community/comment/service/CommentService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public List<CommentResponse> getReplies(Long parentId, Long viewerId) {
8787
@Transactional
8888
public void delete(Long commentId, Long requesterId) {
8989
Comment comment = findActiveCommentOrThrow(commentId);
90-
if (!comment.getAuthor().getId().equals(requesterId)) {
90+
if (!comment.isOwnedBy(requesterId)) {
9191
throw new GeneralException(ErrorStatus.COMMENT_FORBIDDEN);
9292
}
9393
comment.softDelete();

src/main/java/com/semosan/api/domain/community/post/entity/Post.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ public void increaseViewCount() {
4444
public void softDelete() {
4545
this.deleted = true;
4646
}
47+
48+
public boolean isOwnedBy(Long userId) {
49+
return author != null && userId != null && userId.equals(author.getId());
50+
}
4751
}

src/main/java/com/semosan/api/domain/community/post/service/FreePostReportService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public FreePostReport report(Long reporterId, Long postId, FreePostReportReason
4242
User reporter = findReporterOrThrow(reporterId);
4343
FreePost post = findPostOrThrow(postId);
4444

45-
if (post.getAuthor().getId().equals(reporterId)) {
45+
if (post.isOwnedBy(reporterId)) {
4646
throw new GeneralException(ErrorStatus.FREE_POST_REPORT_SELF_NOT_ALLOWED);
4747
}
4848
if (freePostReportRepository.existsByReporter_IdAndPost_Id(reporterId, postId)) {

src/main/java/com/semosan/api/domain/community/post/service/FreePostService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public FreePostDetailResponse getDetail(Long viewerId, Long postId) {
9393
@Transactional
9494
public void delete(Long postId, Long requesterId) {
9595
FreePost post = findActivePostOrThrow(postId);
96-
if (!post.getAuthor().getId().equals(requesterId)) {
96+
if (!post.isOwnedBy(requesterId)) {
9797
throw new GeneralException(ErrorStatus.POST_FORBIDDEN);
9898
}
9999
post.softDelete();

src/main/java/com/semosan/api/domain/community/post/service/RecordPostService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public RecordPost getDetail(Long postId) {
5858
@Transactional
5959
public void delete(Long postId, Long requesterId) {
6060
RecordPost post = findActivePostOrThrow(postId);
61-
if (!post.getAuthor().getId().equals(requesterId)) {
61+
if (!post.isOwnedBy(requesterId)) {
6262
throw new GeneralException(ErrorStatus.POST_FORBIDDEN);
6363
}
6464
post.softDelete();

src/test/java/com/semosan/api/domain/community/comment/service/CommentServiceTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.semosan.api.domain.community.comment.service;
22

3+
import com.semosan.api.common.exception.GeneralException;
4+
import com.semosan.api.common.status.ErrorStatus;
35
import com.semosan.api.domain.community.comment.entity.Comment;
46
import com.semosan.api.domain.community.comment.repository.CommentRepository;
57
import com.semosan.api.domain.community.notification.service.CommunityNotificationService;
@@ -20,6 +22,7 @@
2022
import java.util.Optional;
2123

2224
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2326
import static org.mockito.ArgumentMatchers.any;
2427
import static org.mockito.Mockito.verify;
2528
import static org.mockito.Mockito.when;
@@ -91,6 +94,36 @@ void replySavesReplyAndDelegatesNotification() throws Exception {
9194
.sendReplyNotification(post, replyAuthor, parent, mentionedUser, result);
9295
}
9396

97+
@Test
98+
void deleteSoftDeletesWhenRequesterOwnsComment() throws Exception {
99+
User postAuthor = user(1L, "post-author");
100+
User commentAuthor = user(2L, "comment-author");
101+
FreePost post = freePost(10L, postAuthor, "제목", "본문");
102+
Comment comment = comment(100L, post, commentAuthor, "댓글");
103+
104+
when(commentRepository.findByIdAndDeletedFalse(100L)).thenReturn(Optional.of(comment));
105+
106+
commentService.delete(100L, 2L);
107+
108+
assertThat(comment.isDeleted()).isTrue();
109+
}
110+
111+
@Test
112+
void deleteThrowsWhenRequesterDoesNotOwnComment() throws Exception {
113+
User postAuthor = user(1L, "post-author");
114+
User commentAuthor = user(2L, "comment-author");
115+
FreePost post = freePost(10L, postAuthor, "제목", "본문");
116+
Comment comment = comment(100L, post, commentAuthor, "댓글");
117+
118+
when(commentRepository.findByIdAndDeletedFalse(100L)).thenReturn(Optional.of(comment));
119+
120+
assertThatThrownBy(() -> commentService.delete(100L, 3L))
121+
.isInstanceOf(GeneralException.class)
122+
.extracting("errorStatus")
123+
.isEqualTo(ErrorStatus.COMMENT_FORBIDDEN);
124+
assertThat(comment.isDeleted()).isFalse();
125+
}
126+
94127
private User user(Long id, String nickname) {
95128
User user = User.createTestUser(nickname, DeviceType.IOS);
96129
ReflectionTestUtils.setField(user, "id", id);

src/test/java/com/semosan/api/domain/community/post/service/FreePostServiceTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,32 @@ void getDetailThrowsWhenViewerBlockedAuthor() throws Exception {
112112
verify(postImageRepository, never()).findByPostOrderBySortOrderAsc(post);
113113
}
114114

115+
@Test
116+
void deleteSoftDeletesWhenRequesterOwnsPost() throws Exception {
117+
User author = user(2L, "author");
118+
FreePost post = freePost(10L, author, "제목", "본문");
119+
120+
when(freePostRepository.findById(10L)).thenReturn(Optional.of(post));
121+
122+
freePostService.delete(10L, 2L);
123+
124+
assertThat(post.isDeleted()).isTrue();
125+
}
126+
127+
@Test
128+
void deleteThrowsWhenRequesterDoesNotOwnPost() throws Exception {
129+
User author = user(2L, "author");
130+
FreePost post = freePost(10L, author, "제목", "본문");
131+
132+
when(freePostRepository.findById(10L)).thenReturn(Optional.of(post));
133+
134+
assertThatThrownBy(() -> freePostService.delete(10L, 3L))
135+
.isInstanceOf(GeneralException.class)
136+
.extracting("errorStatus")
137+
.isEqualTo(ErrorStatus.POST_FORBIDDEN);
138+
assertThat(post.isDeleted()).isFalse();
139+
}
140+
115141
private User user(Long id, String oauthId) {
116142
User user = User.createTestUser(oauthId, DeviceType.IOS);
117143
ReflectionTestUtils.setField(user, "id", id);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.semosan.api.domain.community.post.service;
2+
3+
import com.semosan.api.common.exception.GeneralException;
4+
import com.semosan.api.common.status.ErrorStatus;
5+
import com.semosan.api.domain.community.post.entity.RecordPost;
6+
import com.semosan.api.domain.community.post.repository.RecordPostRepository;
7+
import com.semosan.api.domain.hiking.repository.HikingMemberRepository;
8+
import com.semosan.api.domain.hiking.repository.HikingRecordRepository;
9+
import com.semosan.api.domain.user.entity.User;
10+
import com.semosan.api.domain.user.enums.user.DeviceType;
11+
import com.semosan.api.domain.user.service.UserReader;
12+
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.extension.ExtendWith;
14+
import org.mockito.InjectMocks;
15+
import org.mockito.Mock;
16+
import org.mockito.junit.jupiter.MockitoExtension;
17+
import org.springframework.test.util.ReflectionTestUtils;
18+
19+
import java.util.Optional;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
23+
import static org.mockito.Mockito.when;
24+
25+
@ExtendWith(MockitoExtension.class)
26+
class RecordPostServiceTest {
27+
28+
@Mock
29+
private RecordPostRepository recordPostRepository;
30+
31+
@Mock
32+
private HikingRecordRepository hikingRecordRepository;
33+
34+
@Mock
35+
private HikingMemberRepository hikingMemberRepository;
36+
37+
@Mock
38+
private UserReader userReader;
39+
40+
@InjectMocks
41+
private RecordPostService recordPostService;
42+
43+
@Test
44+
void deleteSoftDeletesWhenRequesterOwnsPost() {
45+
RecordPost post = recordPost(10L, user(2L, "author"));
46+
47+
when(recordPostRepository.findById(10L)).thenReturn(Optional.of(post));
48+
49+
recordPostService.delete(10L, 2L);
50+
51+
assertThat(post.isDeleted()).isTrue();
52+
}
53+
54+
@Test
55+
void deleteThrowsWhenRequesterDoesNotOwnPost() {
56+
RecordPost post = recordPost(10L, user(2L, "author"));
57+
58+
when(recordPostRepository.findById(10L)).thenReturn(Optional.of(post));
59+
60+
assertThatThrownBy(() -> recordPostService.delete(10L, 3L))
61+
.isInstanceOf(GeneralException.class)
62+
.extracting("errorStatus")
63+
.isEqualTo(ErrorStatus.POST_FORBIDDEN);
64+
assertThat(post.isDeleted()).isFalse();
65+
}
66+
67+
private User user(Long id, String nickname) {
68+
User user = User.createTestUser(nickname, DeviceType.IOS);
69+
ReflectionTestUtils.setField(user, "id", id);
70+
ReflectionTestUtils.setField(user, "nickname", nickname);
71+
return user;
72+
}
73+
74+
private RecordPost recordPost(Long id, User author) {
75+
RecordPost post = RecordPost.create(author, "본문", null);
76+
ReflectionTestUtils.setField(post, "id", id);
77+
return post;
78+
}
79+
}

0 commit comments

Comments
 (0)