diff --git a/backend/src/main/java/com/votogether/domain/post/entity/comment/Content.java b/backend/src/main/java/com/votogether/domain/post/entity/comment/Content.java index e6e0fd3b8..c994cfe2b 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/comment/Content.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/comment/Content.java @@ -11,7 +11,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Embeddable -class Content { +public class Content { private static final int MAXIMUM_LENGTH = 500; diff --git a/backend/src/main/java/com/votogether/domain/report/controller/ReportController.java b/backend/src/main/java/com/votogether/domain/report/controller/ReportCommandCommandController.java similarity index 73% rename from backend/src/main/java/com/votogether/domain/report/controller/ReportController.java rename to backend/src/main/java/com/votogether/domain/report/controller/ReportCommandCommandController.java index fc94dfd68..941ce0740 100644 --- a/backend/src/main/java/com/votogether/domain/report/controller/ReportController.java +++ b/backend/src/main/java/com/votogether/domain/report/controller/ReportCommandCommandController.java @@ -2,7 +2,7 @@ import com.votogether.domain.member.entity.Member; import com.votogether.domain.report.dto.request.ReportRequest; -import com.votogether.domain.report.service.ReportService; +import com.votogether.domain.report.service.ReportCommandService; import com.votogether.global.jwt.Auth; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -13,13 +13,13 @@ @RequiredArgsConstructor @RestController -public class ReportController implements ReportControllerDocs { +public class ReportCommandCommandController implements ReportCommandControllerDocs { - private final ReportService reportService; + private final ReportCommandService reportCommandService; @PostMapping("/report") public ResponseEntity report(@Valid @RequestBody final ReportRequest request, @Auth final Member member) { - reportService.report(member, request); + reportCommandService.report(member, request); return ResponseEntity.ok().build(); } diff --git a/backend/src/main/java/com/votogether/domain/report/controller/ReportControllerDocs.java b/backend/src/main/java/com/votogether/domain/report/controller/ReportCommandControllerDocs.java similarity index 96% rename from backend/src/main/java/com/votogether/domain/report/controller/ReportControllerDocs.java rename to backend/src/main/java/com/votogether/domain/report/controller/ReportCommandControllerDocs.java index a07c32024..aab4566b5 100644 --- a/backend/src/main/java/com/votogether/domain/report/controller/ReportControllerDocs.java +++ b/backend/src/main/java/com/votogether/domain/report/controller/ReportCommandControllerDocs.java @@ -12,7 +12,7 @@ import org.springframework.http.ResponseEntity; @Tag(name = "신고", description = "신고 API") -public interface ReportControllerDocs { +public interface ReportCommandControllerDocs { @Operation(summary = "신고", description = "게시글, 댓글, 닉네임을 신고한다.") @ApiResponses({ diff --git a/backend/src/main/java/com/votogether/domain/report/repository/ReportRepository.java b/backend/src/main/java/com/votogether/domain/report/repository/ReportRepository.java index 9f56a5d0b..c575552d0 100644 --- a/backend/src/main/java/com/votogether/domain/report/repository/ReportRepository.java +++ b/backend/src/main/java/com/votogether/domain/report/repository/ReportRepository.java @@ -21,4 +21,6 @@ Optional findByMemberAndReportTypeAndTargetId( List findAllByReportTypeAndTargetId(final ReportType reportType, final Long targetId); + void deleteByReportTypeAndTargetId(final ReportType reportType, final Long targetId); + } diff --git a/backend/src/main/java/com/votogether/domain/report/service/ReportCommandService.java b/backend/src/main/java/com/votogether/domain/report/service/ReportCommandService.java new file mode 100644 index 000000000..c89e0d658 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/report/service/ReportCommandService.java @@ -0,0 +1,37 @@ +package com.votogether.domain.report.service; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.entity.vo.ReportType; +import com.votogether.domain.report.service.strategy.ReportCommentStrategy; +import com.votogether.domain.report.service.strategy.ReportNicknameStrategy; +import com.votogether.domain.report.service.strategy.ReportPostStrategy; +import com.votogether.domain.report.service.strategy.ReportStrategy; +import java.util.EnumMap; +import java.util.Map; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class ReportCommandService { + + private final Map reportActions; + + public ReportCommandService( + final ReportPostStrategy reportPostStrategy, + final ReportCommentStrategy reportCommentStrategy, + final ReportNicknameStrategy reportNicknameStrategy + ) { + this.reportActions = new EnumMap<>(ReportType.class); + this.reportActions.put(ReportType.POST, reportPostStrategy); + this.reportActions.put(ReportType.COMMENT, reportCommentStrategy); + this.reportActions.put(ReportType.NICKNAME, reportNicknameStrategy); + } + + public void report(final Member reporter, final ReportRequest request) { + final ReportStrategy reportStrategy = reportActions.get(request.type()); + reportStrategy.report(reporter, request); + } + +} diff --git a/backend/src/main/java/com/votogether/domain/report/service/ReportService.java b/backend/src/main/java/com/votogether/domain/report/service/ReportService.java deleted file mode 100644 index 5357b6c02..000000000 --- a/backend/src/main/java/com/votogether/domain/report/service/ReportService.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.votogether.domain.report.service; - -import com.votogether.domain.member.entity.Member; -import com.votogether.domain.member.exception.MemberExceptionType; -import com.votogether.domain.member.repository.MemberRepository; -import com.votogether.domain.post.entity.Post; -import com.votogether.domain.post.entity.comment.Comment; -import com.votogether.domain.post.exception.CommentExceptionType; -import com.votogether.domain.post.exception.PostExceptionType; -import com.votogether.domain.post.repository.CommentRepository; -import com.votogether.domain.post.repository.PostRepository; -import com.votogether.domain.report.dto.request.ReportRequest; -import com.votogether.domain.report.entity.Report; -import com.votogether.domain.report.entity.vo.ReportType; -import com.votogether.domain.report.exception.ReportExceptionType; -import com.votogether.domain.report.repository.ReportRepository; -import com.votogether.global.exception.BadRequestException; -import com.votogether.global.exception.NotFoundException; -import java.util.Objects; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@RequiredArgsConstructor -@Service -public class ReportService { - - private final ReportRepository reportRepository; - private final PostRepository postRepository; - private final CommentRepository commentRepository; - private final MemberRepository memberRepository; - - @Transactional - public void report(final Member reporter, final ReportRequest request) { - if (request.type() == ReportType.POST) { - reportPost(reporter, request); - } - if (request.type() == ReportType.COMMENT) { - reportComment(reporter, request); - } - if (request.type() == ReportType.NICKNAME) { - reportNickname(reporter, request); - } - } - - private void reportPost( - final Member reporter, - final ReportRequest request - ) { - final Post reportedPost = postRepository.findById(request.id()) - .orElseThrow(() -> new NotFoundException(PostExceptionType.POST_NOT_FOUND)); - validatePost(reporter, reportedPost, request); - - saveReport(reporter, request); - blindPost(request, reportedPost); - } - - private void validatePost( - final Member reporter, - final Post reportedPost, - final ReportRequest request - ) { - reportedPost.validateMine(reporter); - reportedPost.validateHidden(); - validateDuplicatedReport(reporter, request, ReportExceptionType.DUPLICATE_POST_REPORT); - } - - private void validateDuplicatedReport( - final Member reporter, - final ReportRequest request, - final ReportExceptionType exceptionType - ) { - reportRepository.findByMemberAndReportTypeAndTargetId(reporter, request.type(), request.id()) - .ifPresent(report -> { - throw new BadRequestException(exceptionType); - }); - } - - private void saveReport(final Member reporter, final ReportRequest request) { - final Report report = Report.builder() - .member(reporter) - .reportType(request.type()) - .targetId(request.id()) - .reason(request.reason()) - .build(); - reportRepository.save(report); - } - - private void blindPost( - final ReportRequest request, - final Post reportedPost - ) { - final int reportCount = reportRepository.countByReportTypeAndTargetId(request.type(), request.id()); - if (reportCount >= 5) { - reportedPost.blind(); - } - } - - private void reportComment( - final Member reporter, - final ReportRequest request - ) { - final Comment reportedComment = commentRepository.findById(request.id()) - .orElseThrow(() -> new NotFoundException(CommentExceptionType.COMMENT_NOT_FOUND)); - validateComment(reporter, request, reportedComment); - - saveReport(reporter, request); - blindComment(request, reportedComment); - } - - private void validateComment( - final Member reporter, - final ReportRequest request, - final Comment reportedComment - ) { - reportedComment.validateMine(reporter); - reportedComment.validateHidden(); - validateDuplicatedReport(reporter, request, ReportExceptionType.DUPLICATE_COMMENT_REPORT); - } - - private void blindComment( - final ReportRequest request, - final Comment reportedComment - ) { - final int reportCount = reportRepository.countByReportTypeAndTargetId(request.type(), request.id()); - if (reportCount >= 5) { - reportedComment.blind(); - } - } - - private void reportNickname( - final Member reporter, - final ReportRequest request - ) { - final Member reportedMember = memberRepository.findById(request.id()) - .orElseThrow(() -> new NotFoundException(MemberExceptionType.NONEXISTENT_MEMBER)); - validateNickname(reporter, request); - - saveReport(reporter, request); - changeNicknameByReport(reportedMember, request.type()); - } - - private void validateNickname( - final Member reporter, - final ReportRequest request - ) { - validateMyNickname(reporter, request); - validateDuplicatedReport(reporter, request, ReportExceptionType.DUPLICATE_NICKNAME_REPORT); - } - - private void validateMyNickname(final Member reporter, final ReportRequest request) { - if (Objects.equals(reporter.getId(), request.id())) { - throw new BadRequestException(ReportExceptionType.REPORT_MY_NICKNAME); - } - } - - private void changeNicknameByReport(final Member reportedMember, final ReportType reportType) { - final int reportCount = reportRepository.countByReportTypeAndTargetId(reportType, reportedMember.getId()); - if (reportCount >= 3) { - reportedMember.changeNicknameByReport(); - } - } - -} diff --git a/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportCommentStrategy.java b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportCommentStrategy.java new file mode 100644 index 000000000..5fa576365 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportCommentStrategy.java @@ -0,0 +1,55 @@ +package com.votogether.domain.report.service.strategy; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.post.entity.comment.Comment; +import com.votogether.domain.post.exception.CommentExceptionType; +import com.votogether.domain.post.repository.CommentRepository; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.exception.ReportExceptionType; +import com.votogether.domain.report.repository.ReportRepository; +import com.votogether.global.exception.NotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class ReportCommentStrategy implements ReportStrategy { + + private static final int NUMBER_OF_COMMENT_BLIND_BASED_REPORTS = 5; + + private final CommentRepository commentRepository; + private final ReportRepository reportRepository; + + @Override + public void report(final Member reporter, final ReportRequest request) { + final Comment reportedComment = commentRepository.findById(request.id()) + .orElseThrow(() -> new NotFoundException(CommentExceptionType.COMMENT_NOT_FOUND)); + validateComment(reporter, request, reportedComment); + + saveReport(reporter, request, reportRepository); + blindComment(request, reportedComment); + } + + private void validateComment( + final Member reporter, + final ReportRequest request, + final Comment reportedComment + ) { + reportedComment.validateMine(reporter); + reportedComment.validateHidden(); + validateDuplicatedReport( + reporter, + request, + ReportExceptionType.DUPLICATE_COMMENT_REPORT, + reportRepository + ); + } + + private void blindComment(final ReportRequest request, final Comment reportedComment) { + final int reportCount = reportRepository.countByReportTypeAndTargetId(request.type(), request.id()); + if (reportCount >= NUMBER_OF_COMMENT_BLIND_BASED_REPORTS) { + reportedComment.blind(); + } + } + +} diff --git a/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportNicknameStrategy.java b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportNicknameStrategy.java new file mode 100644 index 000000000..7d3361fa9 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportNicknameStrategy.java @@ -0,0 +1,59 @@ +package com.votogether.domain.report.service.strategy; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.member.exception.MemberExceptionType; +import com.votogether.domain.member.repository.MemberRepository; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.entity.vo.ReportType; +import com.votogether.domain.report.exception.ReportExceptionType; +import com.votogether.domain.report.repository.ReportRepository; +import com.votogether.global.exception.BadRequestException; +import com.votogether.global.exception.NotFoundException; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class ReportNicknameStrategy implements ReportStrategy { + + private static final int NUMBER_OF_NICKNAME_CHANGE_REPORTS = 3; + + private final MemberRepository memberRepository; + private final ReportRepository reportRepository; + + @Override + public void report(final Member reporter, final ReportRequest request) { + final Member reportedMember = memberRepository.findById(request.id()) + .orElseThrow(() -> new NotFoundException(MemberExceptionType.NONEXISTENT_MEMBER)); + validateNickname(reporter, request); + + saveReport(reporter, request, reportRepository); + changeNicknameByReport(reportedMember, request); + } + + private void validateNickname(final Member reporter, final ReportRequest request) { + validateMyNickname(reporter, request); + validateDuplicatedReport( + reporter, + request, + ReportExceptionType.DUPLICATE_NICKNAME_REPORT, + reportRepository + ); + } + + private void validateMyNickname(final Member reporter, final ReportRequest request) { + if (Objects.equals(reporter.getId(), request.id())) { + throw new BadRequestException(ReportExceptionType.REPORT_MY_NICKNAME); + } + } + + private void changeNicknameByReport(final Member reportedMember, final ReportRequest request) { + final int reportCount = reportRepository.countByReportTypeAndTargetId(request.type(), reportedMember.getId()); + if (reportCount >= NUMBER_OF_NICKNAME_CHANGE_REPORTS) { + reportedMember.changeNicknameByReport(); + reportRepository.deleteByReportTypeAndTargetId(ReportType.NICKNAME, request.id()); + } + } + +} diff --git a/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportPostStrategy.java b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportPostStrategy.java new file mode 100644 index 000000000..be85c7219 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportPostStrategy.java @@ -0,0 +1,55 @@ +package com.votogether.domain.report.service.strategy; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.exception.PostExceptionType; +import com.votogether.domain.post.repository.PostRepository; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.exception.ReportExceptionType; +import com.votogether.domain.report.repository.ReportRepository; +import com.votogether.global.exception.NotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class ReportPostStrategy implements ReportStrategy { + + private static final int NUMBER_OF_POST_BLIND_BASED_REPORTS = 5; + + private final PostRepository postRepository; + private final ReportRepository reportRepository; + + @Override + public void report(final Member reporter, final ReportRequest request) { + final Post reportedPost = postRepository.findById(request.id()) + .orElseThrow(() -> new NotFoundException(PostExceptionType.POST_NOT_FOUND)); + validatePost(reporter, reportedPost, request); + + saveReport(reporter, request, reportRepository); + blindPost(request, reportedPost); + } + + private void validatePost( + final Member reporter, + final Post reportedPost, + final ReportRequest request + ) { + reportedPost.validateMine(reporter); + reportedPost.validateHidden(); + validateDuplicatedReport( + reporter, + request, + ReportExceptionType.DUPLICATE_POST_REPORT, + reportRepository + ); + } + + private void blindPost(final ReportRequest request, final Post reportedPost) { + final int reportCount = reportRepository.countByReportTypeAndTargetId(request.type(), request.id()); + if (reportCount >= NUMBER_OF_POST_BLIND_BASED_REPORTS) { + reportedPost.blind(); + } + } + +} diff --git a/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportStrategy.java b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportStrategy.java new file mode 100644 index 000000000..9b5f7c464 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/report/service/strategy/ReportStrategy.java @@ -0,0 +1,41 @@ +package com.votogether.domain.report.service.strategy; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.entity.Report; +import com.votogether.domain.report.exception.ReportExceptionType; +import com.votogether.domain.report.repository.ReportRepository; +import com.votogether.global.exception.BadRequestException; + +@FunctionalInterface +public interface ReportStrategy { + + void report(final Member reporter, final ReportRequest request); + + default void validateDuplicatedReport( + final Member reporter, + final ReportRequest request, + final ReportExceptionType reportExceptionType, + final ReportRepository reportRepository + ) { + reportRepository.findByMemberAndReportTypeAndTargetId(reporter, request.type(), request.id()) + .ifPresent(report -> { + throw new BadRequestException(reportExceptionType); + }); + } + + default void saveReport( + final Member reporter, + final ReportRequest request, + final ReportRepository reportRepository + ) { + final Report report = Report.builder() + .member(reporter) + .reportType(request.type()) + .targetId(request.id()) + .reason(request.reason()) + .build(); + reportRepository.save(report); + } + +} diff --git a/backend/src/test/java/com/votogether/domain/member/service/MemberServiceTest.java b/backend/src/test/java/com/votogether/domain/member/service/MemberServiceTest.java index bba1e64e5..d3507d63a 100644 --- a/backend/src/test/java/com/votogether/domain/member/service/MemberServiceTest.java +++ b/backend/src/test/java/com/votogether/domain/member/service/MemberServiceTest.java @@ -62,15 +62,9 @@ class MemberServiceTest { @Autowired CommentRepository commentRepository; - @Autowired - MemberTestPersister memberTestPersister; - @Autowired PostTestPersister postTestPersister; - @Autowired - VoteTestPersister voteTestPersister; - @Autowired EntityManager em; diff --git a/backend/src/test/java/com/votogether/domain/post/repository/CommentRepositoryTest.java b/backend/src/test/java/com/votogether/domain/post/repository/CommentRepositoryTest.java index c50d89c9d..a09f72d60 100644 --- a/backend/src/test/java/com/votogether/domain/post/repository/CommentRepositoryTest.java +++ b/backend/src/test/java/com/votogether/domain/post/repository/CommentRepositoryTest.java @@ -9,6 +9,7 @@ import com.votogether.domain.post.entity.comment.Comment; import com.votogether.test.annotation.RepositoryTest; import com.votogether.test.fixtures.MemberFixtures; +import com.votogether.test.persister.PostTestPersister; import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -27,18 +28,21 @@ class CommentRepositoryTest { @Autowired PostRepository postRepository; + @Autowired + PostTestPersister postTestPersister; + @Test @DisplayName("게시글의 댓글 목록을 조회한다.") void findAllByPost() { // given Member member = memberRepository.save(MemberFixtures.MALE_20.get()); - Post post = postRepository.save( - Post.builder() - .writer(member) - .postBody(PostBody.builder().title("titleA").content("contentA").build()) - .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build() - ); + + final Post post = postTestPersister.builder() + .writer(member) + .postBody(PostBody.builder().title("titleA").content("contentA").build()) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + Comment commentA = commentRepository.save( Comment.builder() .member(member) diff --git a/backend/src/test/java/com/votogether/domain/report/controller/ReportControllerTest.java b/backend/src/test/java/com/votogether/domain/report/controller/ReportCommandControllerTest.java similarity index 87% rename from backend/src/test/java/com/votogether/domain/report/controller/ReportControllerTest.java rename to backend/src/test/java/com/votogether/domain/report/controller/ReportCommandControllerTest.java index 699f90f96..d66dd0d68 100644 --- a/backend/src/test/java/com/votogether/domain/report/controller/ReportControllerTest.java +++ b/backend/src/test/java/com/votogether/domain/report/controller/ReportCommandControllerTest.java @@ -11,7 +11,7 @@ import com.votogether.domain.member.service.MemberService; import com.votogether.domain.report.dto.request.ReportRequest; import com.votogether.domain.report.entity.vo.ReportType; -import com.votogether.domain.report.service.ReportService; +import com.votogether.domain.report.service.ReportCommandService; import com.votogether.global.jwt.TokenPayload; import com.votogether.global.jwt.TokenProcessor; import io.restassured.http.ContentType; @@ -29,11 +29,11 @@ import org.springframework.http.HttpStatus; import org.springframework.web.context.WebApplicationContext; -@WebMvcTest(ReportController.class) -class ReportControllerTest { +@WebMvcTest(ReportCommandCommandController.class) +class ReportCommandControllerTest { @MockBean - ReportService reportService; + ReportCommandService reportCommandService; @MockBean TokenProcessor tokenProcessor; @@ -43,7 +43,7 @@ class ReportControllerTest { @BeforeEach void setUp(final WebApplicationContext webApplicationContext) { - RestAssuredMockMvc.standaloneSetup(new ReportController(reportService)); + RestAssuredMockMvc.standaloneSetup(new ReportCommandCommandController(reportCommandService)); RestAssuredMockMvc.webAppContextSetup(webApplicationContext); } @@ -69,7 +69,7 @@ void reportPost() throws Exception { given(memberService.findById(anyLong())).willReturn(member); ReportRequest request = new ReportRequest(ReportType.POST, 1L, "불건전한 게시글"); - willDoNothing().given(reportService).report(member, request); + willDoNothing().given(reportCommandService).report(member, request); // when, then RestAssuredMockMvc @@ -79,7 +79,8 @@ void reportPost() throws Exception { .body(request) .when().post("/report") .then().log().all() - .statusCode(HttpStatus.OK.value()); + .assertThat() + .status(HttpStatus.OK); } @Test @@ -100,7 +101,7 @@ void reportComment() throws Exception { given(memberService.findById(anyLong())).willReturn(member); ReportRequest request = new ReportRequest(ReportType.COMMENT, 1L, "불건전한 댓글"); - willDoNothing().given(reportService).report(member, request); + willDoNothing().given(reportCommandService).report(member, request); // when, then RestAssuredMockMvc @@ -110,7 +111,8 @@ void reportComment() throws Exception { .body(request) .when().post("/report") .then().log().all() - .statusCode(HttpStatus.OK.value()); + .assertThat() + .status(HttpStatus.OK); } @Test @@ -131,7 +133,7 @@ void reportNickname() throws Exception { given(memberService.findById(anyLong())).willReturn(member); ReportRequest request = new ReportRequest(ReportType.NICKNAME, 1L, "불건전한 닉네임"); - willDoNothing().given(reportService).report(member, request); + willDoNothing().given(reportCommandService).report(member, request); // when, then RestAssuredMockMvc @@ -141,7 +143,8 @@ void reportNickname() throws Exception { .body(request) .when().post("/report") .then().log().all() - .statusCode(HttpStatus.OK.value()); + .assertThat() + .status(HttpStatus.OK); } } @@ -165,7 +168,7 @@ void report(Long id) throws Exception { given(memberService.findById(anyLong())).willReturn(member); ReportRequest request = new ReportRequest(ReportType.COMMENT, id, "불건전한 게시글"); - willDoNothing().given(reportService).report(member, request); + willDoNothing().given(reportCommandService).report(member, request); // when, then RestAssuredMockMvc @@ -175,7 +178,8 @@ void report(Long id) throws Exception { .body(request) .when().post("/report") .then().log().all() - .statusCode(HttpStatus.BAD_REQUEST.value()); + .assertThat() + .status(HttpStatus.BAD_REQUEST); } @ParameterizedTest @@ -197,7 +201,7 @@ void reportBadRequest(String reason) throws Exception { given(memberService.findById(anyLong())).willReturn(member); ReportRequest request = new ReportRequest(ReportType.POST, 1L, reason); - willDoNothing().given(reportService).report(member, request); + willDoNothing().given(reportCommandService).report(member, request); // when, then RestAssuredMockMvc @@ -207,7 +211,8 @@ void reportBadRequest(String reason) throws Exception { .body(request) .when().post("/report") .then().log().all() - .statusCode(HttpStatus.BAD_REQUEST.value()); + .assertThat() + .status(HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/test/java/com/votogether/domain/report/repository/ReportRepositoryTest.java b/backend/src/test/java/com/votogether/domain/report/repository/ReportRepositoryTest.java index 2c1422730..6e489bd15 100644 --- a/backend/src/test/java/com/votogether/domain/report/repository/ReportRepositoryTest.java +++ b/backend/src/test/java/com/votogether/domain/report/repository/ReportRepositoryTest.java @@ -12,7 +12,10 @@ import com.votogether.domain.report.entity.vo.ReportType; import com.votogether.test.annotation.RepositoryTest; import com.votogether.test.fixtures.MemberFixtures; +import com.votogether.test.persister.PostTestPersister; +import com.votogether.test.persister.ReportTestPersister; import java.time.LocalDateTime; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -29,34 +32,37 @@ class ReportRepositoryTest { @Autowired PostRepository postRepository; + @Autowired + PostTestPersister postTestPersister; + + @Autowired + ReportTestPersister reportTestPersister; + + @Test @DisplayName("회원, 신고타입, 대상ID를 통해서 신고 횟수를 반환한다.") void countByMemberAndReportTypeAndTargetId() { // given Member member = MemberFixtures.MALE_20.get(); ReportType reportType = ReportType.POST; - PostBody postBody = PostBody.builder() .title("title") .content("content") .build(); - Post post = Post.builder() + memberRepository.save(member); + Post post = postTestPersister.builder() .writer(member) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); - - memberRepository.save(member); - postRepository.save(post); + .save(); - Report report = Report.builder() + reportTestPersister.builder() .member(member) .reportType(reportType) .targetId(post.getId()) .reason("불건전한 게시글") - .build(); - reportRepository.save(report); + .save(); // when int reportCount = reportRepository.countByReportTypeAndTargetId(reportType, post.getId()); @@ -76,22 +82,19 @@ void findByMemberAndReportTypeAndTargetId() { .content("content") .build(); - Post post = Post.builder() + memberRepository.save(member); + Post post = postTestPersister.builder() .writer(member) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); + .save(); - memberRepository.save(member); - postRepository.save(post); - - Report report = Report.builder() - .targetId(post.getId()) - .reportType(ReportType.POST) + reportTestPersister.builder() .member(member) + .reportType(ReportType.POST) + .targetId(post.getId()) .reason("불건전한 게시글") - .build(); - reportRepository.save(report); + .save(); // when Report actualReport = reportRepository.findByMemberAndReportTypeAndTargetId( @@ -108,4 +111,37 @@ void findByMemberAndReportTypeAndTargetId() { ); } + @Test + @DisplayName("신고유형, 신고대상ID를 통해 관련된 신고정보를 모두 삭제한다.") + void deleteByReportTypeAndTargetId() { + // given + Member member = MemberFixtures.FEMALE_30.get(); + Member reporterA = MemberFixtures.MALE_30.get(); + Member reporterB = MemberFixtures.FEMALE_20.get(); + + memberRepository.save(member); + memberRepository.save(reporterA); + memberRepository.save(reporterB); + + reportTestPersister.builder() + .member(reporterA) + .reportType(ReportType.NICKNAME) + .targetId(member.getId()) + .reason("불건전한 게시글") + .save(); + + reportTestPersister.builder() + .member(reporterB) + .reportType(ReportType.NICKNAME) + .targetId(member.getId()) + .reason("불건전한 게시글") + .save(); + + // when + reportRepository.deleteByReportTypeAndTargetId(ReportType.NICKNAME, member.getId()); + + // then + assertThat(reportRepository.findAll()).isEmpty(); + } + } diff --git a/backend/src/test/java/com/votogether/domain/report/service/ReportServiceTest.java b/backend/src/test/java/com/votogether/domain/report/service/ReportCommandServiceTest.java similarity index 79% rename from backend/src/test/java/com/votogether/domain/report/service/ReportServiceTest.java rename to backend/src/test/java/com/votogether/domain/report/service/ReportCommandServiceTest.java index 74374aed9..19853b5ae 100644 --- a/backend/src/test/java/com/votogether/domain/report/service/ReportServiceTest.java +++ b/backend/src/test/java/com/votogether/domain/report/service/ReportCommandServiceTest.java @@ -11,6 +11,7 @@ import com.votogether.domain.post.entity.Post; import com.votogether.domain.post.entity.PostBody; import com.votogether.domain.post.entity.comment.Comment; +import com.votogether.domain.post.entity.comment.Content; import com.votogether.domain.post.entity.vo.PostClosingType; import com.votogether.domain.post.entity.vo.PostSortType; import com.votogether.domain.post.repository.CommentRepository; @@ -24,6 +25,8 @@ import com.votogether.global.exception.NotFoundException; import com.votogether.test.annotation.ServiceTest; import com.votogether.test.fixtures.MemberFixtures; +import com.votogether.test.persister.CommentTestPersister; +import com.votogether.test.persister.PostTestPersister; import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -32,10 +35,10 @@ import org.springframework.beans.factory.annotation.Autowired; @ServiceTest -class ReportServiceTest { +class ReportCommandServiceTest { @Autowired - ReportService reportService; + ReportCommandService reportCommandService; @Autowired MemberRepository memberRepository; @@ -55,6 +58,12 @@ class ReportServiceTest { @Autowired PostCommentService postCommentService; + @Autowired + PostTestPersister postTestPersister; + + @Autowired + CommentTestPersister commentTestPersister; + @Nested @DisplayName("게시글 신고기능은") class ReportPost { @@ -71,18 +80,16 @@ void reportPost() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); - - postRepository.save(post); + .save(); ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); // when, then - assertDoesNotThrow(() -> reportService.report(reporter, request)); + assertDoesNotThrow(() -> reportCommandService.report(reporter, request)); } @Test @@ -94,7 +101,7 @@ void reportNonExistPostThrowsException() { ReportRequest request = new ReportRequest(ReportType.POST, -1L, "불건전한 게시글"); // when, then - assertThatThrownBy(() -> reportService.report(writer, request)) + assertThatThrownBy(() -> reportCommandService.report(writer, request)) .isInstanceOf(NotFoundException.class) .hasMessage("해당 게시글이 존재하지 않습니다."); } @@ -110,18 +117,16 @@ void reportOwnPostThrowsException() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); - - postRepository.save(post); + .save(); ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); // when, then - assertThatThrownBy(() -> reportService.report(writer, request)) + assertThatThrownBy(() -> reportCommandService.report(writer, request)) .isInstanceOf(BadRequestException.class) .hasMessage("자신의 게시글은 신고할 수 없습니다."); } @@ -138,20 +143,18 @@ void reportHiddenPost() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); - post.blind(); - - postRepository.save(post); + .blind() + .save(); ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); // when, then - assertThatThrownBy(() -> reportService.report(reporter, request)) + assertThatThrownBy(() -> reportCommandService.report(reporter, request)) .isInstanceOf(BadRequestException.class) .hasMessage("이미 블라인드 처리된 글입니다."); } @@ -168,21 +171,19 @@ void reportDuplicated() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); - - postRepository.save(post); + .save(); ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); // when - reportService.report(reporter, request); + reportCommandService.report(reporter, request); // then - assertThatThrownBy(() -> reportService.report(reporter, request)) + assertThatThrownBy(() -> reportCommandService.report(reporter, request)) .isInstanceOf(BadRequestException.class) .hasMessage("하나의 글에 대해서 중복하여 신고할 수 없습니다."); } @@ -203,22 +204,20 @@ void reportAndBlind() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); - - postRepository.save(post); + .save(); ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); // when - reportService.report(reporter1, request); - reportService.report(reporter2, request); - reportService.report(reporter3, request); - reportService.report(reporter4, request); - reportService.report(reporter5, request); + reportCommandService.report(reporter1, request); + reportCommandService.report(reporter2, request); + reportCommandService.report(reporter3, request); + reportCommandService.report(reporter4, request); + reportCommandService.report(reporter5, request); // then final List responses = postService.getPostsGuest( @@ -252,25 +251,22 @@ void reportComment() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); + .save(); - Comment comment = Comment.builder() + Comment comment = commentTestPersister.builder() .post(post) .member(writer) - .content("으어어어어") - .build(); - - postRepository.save(post); - commentRepository.save(comment); + .content(new Content("으어어어어")) + .save(); ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 게시글"); // when, then - assertDoesNotThrow(() -> reportService.report(reporter, request)); + assertDoesNotThrow(() -> reportCommandService.report(reporter, request)); } @Test @@ -282,7 +278,7 @@ void reportNonExistCommentThrowsException() { ReportRequest request = new ReportRequest(ReportType.COMMENT, -1L, "불건전한 댓글"); // when, then - assertThatThrownBy(() -> reportService.report(writer, request)) + assertThatThrownBy(() -> reportCommandService.report(writer, request)) .isInstanceOf(NotFoundException.class) .hasMessage("해당 댓글이 존재하지 않습니다."); } @@ -298,25 +294,22 @@ void reportOwnCommentThrowsException() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); + .save(); - Comment comment = Comment.builder() + Comment comment = commentTestPersister.builder() .post(post) .member(writer) - .content("으어어어어") - .build(); - - postRepository.save(post); - commentRepository.save(comment); + .content(new Content("으어어어어")) + .save(); ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); // when, then - assertThatThrownBy(() -> reportService.report(writer, request)) + assertThatThrownBy(() -> reportCommandService.report(writer, request)) .isInstanceOf(BadRequestException.class) .hasMessage("자신의 댓글은 신고할 수 없습니다."); } @@ -333,26 +326,23 @@ void reportHiddenComment() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); + .save(); - Comment comment = Comment.builder() + Comment comment = commentTestPersister.builder() .post(post) .member(writer) - .content("으어어어어") - .build(); - - postRepository.save(post); - commentRepository.save(comment); + .content(new Content("으어어어어")) + .save(); ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); // when, then comment.blind(); - assertThatThrownBy(() -> reportService.report(reporter, request)) + assertThatThrownBy(() -> reportCommandService.report(reporter, request)) .isInstanceOf(BadRequestException.class) .hasMessage("이미 블라인드 처리된 댓글입니다."); } @@ -369,28 +359,25 @@ void reportDuplicated() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); + .save(); - Comment comment = Comment.builder() + Comment comment = commentTestPersister.builder() .post(post) .member(writer) - .content("으어어어어") - .build(); - - postRepository.save(post); - commentRepository.save(comment); + .content(new Content("으어어어어")) + .save(); ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); // when - reportService.report(reporter, request); + reportCommandService.report(reporter, request); // then - assertThatThrownBy(() -> reportService.report(reporter, request)) + assertThatThrownBy(() -> reportCommandService.report(reporter, request)) .isInstanceOf(BadRequestException.class) .hasMessage("하나의 댓글에 대해서 중복하여 신고할 수 없습니다."); } @@ -411,29 +398,26 @@ void reportAndBlind() { .content("content") .build(); - Post post = Post.builder() + Post post = postTestPersister.builder() .writer(writer) .postBody(postBody) .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) - .build(); + .save(); - Comment comment = Comment.builder() + Comment comment = commentTestPersister.builder() .post(post) .member(writer) - .content("으어어어어") - .build(); - - postRepository.save(post); - commentRepository.save(comment); + .content(new Content("으어어어어")) + .save(); ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); // when - reportService.report(reporter1, request); - reportService.report(reporter2, request); - reportService.report(reporter3, request); - reportService.report(reporter4, request); - reportService.report(reporter5, request); + reportCommandService.report(reporter1, request); + reportCommandService.report(reporter2, request); + reportCommandService.report(reporter3, request); + reportCommandService.report(reporter4, request); + reportCommandService.report(reporter5, request); // then assertAll( @@ -458,7 +442,7 @@ void reportNickname() { ReportRequest request = new ReportRequest(ReportType.NICKNAME, reported.getId(), "불건전한 닉네임"); // when, then - assertDoesNotThrow(() -> reportService.report(reporter, request)); + assertDoesNotThrow(() -> reportCommandService.report(reporter, request)); } @Test @@ -470,7 +454,7 @@ void reportOwnNicknameThrowsException() { ReportRequest request = new ReportRequest(ReportType.NICKNAME, reporter.getId(), "불건전한 닉네임"); // when, then - assertThatThrownBy(() -> reportService.report(reporter, request)) + assertThatThrownBy(() -> reportCommandService.report(reporter, request)) .isInstanceOf(BadRequestException.class) .hasMessage("자신의 닉네임은 신고할 수 없습니다."); } @@ -485,10 +469,10 @@ void reportDuplicated() { ReportRequest request = new ReportRequest(ReportType.NICKNAME, reported.getId(), "불건전한 닉네임"); // when - reportService.report(reporter, request); + reportCommandService.report(reporter, request); // then - assertThatThrownBy(() -> reportService.report(reporter, request)) + assertThatThrownBy(() -> reportCommandService.report(reporter, request)) .isInstanceOf(BadRequestException.class) .hasMessage("하나의 닉네임에 대해서 중복하여 신고할 수 없습니다."); } @@ -505,9 +489,9 @@ void reportAndBlind() { ReportRequest request = new ReportRequest(ReportType.NICKNAME, reported.getId(), "불건전한 닉네임"); // when - reportService.report(reporter1, request); - reportService.report(reporter2, request); - reportService.report(reporter3, request); + reportCommandService.report(reporter1, request); + reportCommandService.report(reporter2, request); + reportCommandService.report(reporter3, request); // then assertThat(reported.getNickname()).contains("Pause1"); diff --git a/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportCommentStrategyTest.java b/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportCommentStrategyTest.java new file mode 100644 index 000000000..bfd3deb04 --- /dev/null +++ b/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportCommentStrategyTest.java @@ -0,0 +1,234 @@ +package com.votogether.domain.report.service.strategy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.member.repository.MemberRepository; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.PostBody; +import com.votogether.domain.post.entity.comment.Comment; +import com.votogether.domain.post.entity.comment.Content; +import com.votogether.domain.post.service.PostCommentService; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.entity.vo.ReportType; +import com.votogether.global.exception.BadRequestException; +import com.votogether.global.exception.NotFoundException; +import com.votogether.test.annotation.ServiceTest; +import com.votogether.test.fixtures.MemberFixtures; +import com.votogether.test.persister.CommentTestPersister; +import com.votogether.test.persister.PostTestPersister; +import java.time.LocalDateTime; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@ServiceTest +@DisplayName("댓글 신고기능은") +class ReportCommentStrategyTest { + + @Autowired + ReportCommentStrategy reportCommentStrategy; + + @Autowired + MemberRepository memberRepository; + + @Autowired + PostTestPersister postTestPersister; + + @Autowired + PostCommentService postCommentService; + + @Autowired + CommentTestPersister commentTestPersister; + + @Test + @DisplayName("정상적으로 동작한다.") + void reportComment() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + Comment comment = commentTestPersister.builder() + .post(post) + .member(writer) + .content(new Content("으어어어어")) + .save(); + + ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 게시글"); + + // when, then + assertDoesNotThrow(() -> reportCommentStrategy.report(reporter, request)); + } + + @Test + @DisplayName("없는 댓글을 신고하는 경우 예외가 발생한다.") + void reportNonExistCommentThrowsException() { + // given + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + ReportRequest request = new ReportRequest(ReportType.COMMENT, -1L, "불건전한 댓글"); + + // when, then + assertThatThrownBy(() -> reportCommentStrategy.report(writer, request)) + .isInstanceOf(NotFoundException.class) + .hasMessage("해당 댓글이 존재하지 않습니다."); + } + + @Test + @DisplayName("자신의 댓글을 신고하는 경우 예외가 발생한다.") + void reportOwnCommentThrowsException() { + // given + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + Comment comment = commentTestPersister.builder() + .post(post) + .member(writer) + .content(new Content("으어어어어")) + .save(); + + ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); + + // when, then + assertThatThrownBy(() -> reportCommentStrategy.report(writer, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("자신의 댓글은 신고할 수 없습니다."); + } + + @Test + @DisplayName("블라인드 처리된 댓글을 신고하는 경우 예외가 발생한다.") + void reportHiddenComment() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + Comment comment = commentTestPersister.builder() + .post(post) + .member(writer) + .content(new Content("으어어어어")) + .save(); + + ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); + + // when, then + comment.blind(); + assertThatThrownBy(() -> reportCommentStrategy.report(reporter, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("이미 블라인드 처리된 댓글입니다."); + } + + @Test + @DisplayName("하나의 회원이 댓글을 중복하여 신고하면 예외를 던진다.") + void reportDuplicated() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_20.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_10.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + Comment comment = commentTestPersister.builder() + .post(post) + .member(writer) + .content(new Content("으어어어어")) + .save(); + + ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); + + // when + reportCommentStrategy.report(reporter, request); + + // then + assertThatThrownBy(() -> reportCommentStrategy.report(reporter, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("하나의 댓글에 대해서 중복하여 신고할 수 없습니다."); + } + + @Test + @DisplayName("댓글 신고가 5회가 되면 블라인드 처리가 된다.") + void reportAndBlind() { + // given + Member reporter1 = memberRepository.save(MemberFixtures.FEMALE_20.get()); + Member reporter2 = memberRepository.save(MemberFixtures.FEMALE_30.get()); + Member reporter3 = memberRepository.save(MemberFixtures.FEMALE_40.get()); + Member reporter4 = memberRepository.save(MemberFixtures.FEMALE_50.get()); + Member reporter5 = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_10.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + Comment comment = commentTestPersister.builder() + .post(post) + .member(writer) + .content(new Content("으어어어어")) + .save(); + + ReportRequest request = new ReportRequest(ReportType.COMMENT, comment.getId(), "불건전한 댓글"); + + // when + reportCommentStrategy.report(reporter1, request); + reportCommentStrategy.report(reporter2, request); + reportCommentStrategy.report(reporter3, request); + reportCommentStrategy.report(reporter4, request); + reportCommentStrategy.report(reporter5, request); + + // then + assertAll( + () -> assertThat(comment.isHidden()).isTrue(), + () -> assertThat(postCommentService.getComments(post.getId())).isEmpty() + ); + } + +} diff --git a/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportNicknameStrategyTest.java b/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportNicknameStrategyTest.java new file mode 100644 index 000000000..b8b47981f --- /dev/null +++ b/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportNicknameStrategyTest.java @@ -0,0 +1,100 @@ +package com.votogether.domain.report.service.strategy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.member.repository.MemberRepository; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.entity.vo.ReportType; +import com.votogether.domain.report.repository.ReportRepository; +import com.votogether.global.exception.BadRequestException; +import com.votogether.test.annotation.ServiceTest; +import com.votogether.test.fixtures.MemberFixtures; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@ServiceTest +@DisplayName("닉네임 신고기능은") +class ReportNicknameStrategyTest { + + @Autowired + ReportNicknameStrategy reportNicknameStrategy; + + @Autowired + ReportRepository reportRepository; + + @Autowired + MemberRepository memberRepository; + + @Test + @DisplayName("정상적으로 동작한다.") + void reportNickname() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member reported = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + ReportRequest request = new ReportRequest(ReportType.NICKNAME, reported.getId(), "불건전한 닉네임"); + + // when, then + assertDoesNotThrow(() -> reportNicknameStrategy.report(reporter, request)); + } + + @Test + @DisplayName("자신의 닉네임을 신고하는 경우 예외가 발생한다.") + void reportOwnNicknameThrowsException() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + ReportRequest request = new ReportRequest(ReportType.NICKNAME, reporter.getId(), "불건전한 닉네임"); + + // when, then + assertThatThrownBy(() -> reportNicknameStrategy.report(reporter, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("자신의 닉네임은 신고할 수 없습니다."); + } + + @Test + @DisplayName("하나의 회원이 다른 회원의 닉네임을 중복하여 신고하면 예외를 던진다.") + void reportDuplicated() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_20.get()); + Member reported = memberRepository.save(MemberFixtures.FEMALE_10.get()); + + ReportRequest request = new ReportRequest(ReportType.NICKNAME, reported.getId(), "불건전한 닉네임"); + + // when + reportNicknameStrategy.report(reporter, request); + + // then + assertThatThrownBy(() -> reportNicknameStrategy.report(reporter, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("하나의 닉네임에 대해서 중복하여 신고할 수 없습니다."); + } + + @Test + @DisplayName("닉네임 신고가 3회가 되면 닉네임이 자동변경처리가 된다.") + void reportAndBlind() { + // given + Member reporter1 = memberRepository.save(MemberFixtures.FEMALE_20.get()); + Member reporter2 = memberRepository.save(MemberFixtures.FEMALE_30.get()); + Member reporter3 = memberRepository.save(MemberFixtures.FEMALE_40.get()); + Member reported = memberRepository.save(MemberFixtures.FEMALE_10.get()); + + ReportRequest request = new ReportRequest(ReportType.NICKNAME, reported.getId(), "불건전한 닉네임"); + + // when + reportNicknameStrategy.report(reporter1, request); + reportNicknameStrategy.report(reporter2, request); + reportNicknameStrategy.report(reporter3, request); + + // then + assertAll( + () -> assertThat(reported.getNickname()).contains("Pause1"), + () -> assertThat(reportRepository.findAll()).isEmpty() + ); + } + +} diff --git a/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportPostStrategyTest.java b/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportPostStrategyTest.java new file mode 100644 index 000000000..fee18d357 --- /dev/null +++ b/backend/src/test/java/com/votogether/domain/report/service/strategy/ReportPostStrategyTest.java @@ -0,0 +1,209 @@ +package com.votogether.domain.report.service.strategy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.member.repository.MemberRepository; +import com.votogether.domain.post.dto.response.post.PostResponse; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.PostBody; +import com.votogether.domain.post.entity.vo.PostClosingType; +import com.votogether.domain.post.entity.vo.PostSortType; +import com.votogether.domain.post.service.PostService; +import com.votogether.domain.report.dto.request.ReportRequest; +import com.votogether.domain.report.entity.vo.ReportType; +import com.votogether.global.exception.BadRequestException; +import com.votogether.global.exception.NotFoundException; +import com.votogether.test.annotation.ServiceTest; +import com.votogether.test.fixtures.MemberFixtures; +import com.votogether.test.persister.PostTestPersister; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@ServiceTest +@DisplayName("게시글 신고 기능은") +class ReportPostStrategyTest { + + @Autowired + ReportPostStrategy reportPostStrategy; + + @Autowired + MemberRepository memberRepository; + + @Autowired + PostTestPersister postTestPersister; + + @Autowired + PostService postService; + + @Test + @DisplayName("정상적으로 동작한다.") + void reportPost() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); + + // when, then + assertDoesNotThrow(() -> reportPostStrategy.report(reporter, request)); + } + + @Test + @DisplayName("없는 투표글을 신고하는 경우 예외가 발생한다.") + void reportNonExistPostThrowsException() { + // given + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + ReportRequest request = new ReportRequest(ReportType.POST, -1L, "불건전한 게시글"); + + // when, then + assertThatThrownBy(() -> reportPostStrategy.report(writer, request)) + .isInstanceOf(NotFoundException.class) + .hasMessage("해당 게시글이 존재하지 않습니다."); + } + + @Test + @DisplayName("자신의 투표글을 신고하는 경우 예외가 발생한다.") + void reportOwnPostThrowsException() { + // given + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); + + // when, then + assertThatThrownBy(() -> reportPostStrategy.report(writer, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("자신의 게시글은 신고할 수 없습니다."); + } + + @Test + @DisplayName("블라인드 처리된 투표글을 신고하는 경우 예외가 발생한다.") + void reportHiddenPost() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_30.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .blind() + .save(); + + ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); + + // when, then + + assertThatThrownBy(() -> reportPostStrategy.report(reporter, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("이미 블라인드 처리된 글입니다."); + } + + @Test + @DisplayName("하나의 회원이 투표글을 중복하여 신고하면 예외를 던진다.") + void reportDuplicated() { + // given + Member reporter = memberRepository.save(MemberFixtures.FEMALE_20.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_10.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); + + // when + reportPostStrategy.report(reporter, request); + + // then + assertThatThrownBy(() -> reportPostStrategy.report(reporter, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("하나의 글에 대해서 중복하여 신고할 수 없습니다."); + } + + @Test + @DisplayName("투표글 신고가 5회가 되면 블라인드 처리가 된다.") + void reportAndBlind() { + // given + Member reporter1 = memberRepository.save(MemberFixtures.FEMALE_20.get()); + Member reporter2 = memberRepository.save(MemberFixtures.FEMALE_30.get()); + Member reporter3 = memberRepository.save(MemberFixtures.FEMALE_40.get()); + Member reporter4 = memberRepository.save(MemberFixtures.FEMALE_50.get()); + Member reporter5 = memberRepository.save(MemberFixtures.FEMALE_60.get()); + Member writer = memberRepository.save(MemberFixtures.FEMALE_10.get()); + + PostBody postBody = PostBody.builder() + .title("title") + .content("content") + .build(); + + Post post = postTestPersister.builder() + .writer(writer) + .postBody(postBody) + .deadline(LocalDateTime.of(2100, 7, 12, 0, 0)) + .save(); + + ReportRequest request = new ReportRequest(ReportType.POST, post.getId(), "불건전한 게시글"); + + // when + reportPostStrategy.report(reporter1, request); + reportPostStrategy.report(reporter2, request); + reportPostStrategy.report(reporter3, request); + reportPostStrategy.report(reporter4, request); + reportPostStrategy.report(reporter5, request); + + // then + final List responses = postService.getPostsGuest( + 0, + PostClosingType.ALL, + PostSortType.HOT, + null + ); + + assertAll( + () -> assertThat(post.isHidden()).isTrue(), + () -> assertThat(responses).isEmpty() + ); + } + +} diff --git a/backend/src/test/java/com/votogether/test/persister/CommentTestPersister.java b/backend/src/test/java/com/votogether/test/persister/CommentTestPersister.java new file mode 100644 index 000000000..e01c3dd12 --- /dev/null +++ b/backend/src/test/java/com/votogether/test/persister/CommentTestPersister.java @@ -0,0 +1,52 @@ +package com.votogether.test.persister; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.comment.Comment; +import com.votogether.domain.post.entity.comment.Content; +import com.votogether.domain.post.repository.CommentRepository; +import com.votogether.test.fixtures.MemberFixtures; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Persister +public class CommentTestPersister { + + private final CommentRepository commentRepository; + + public CommentBuilder builder() { + return new CommentTestPersister.CommentBuilder(); + } + + public final class CommentBuilder { + + private Post post; + private Member member; + private Content content; + + public CommentBuilder post(Post post) { + this.post = post; + return this; + } + + public CommentBuilder member(Member member) { + this.member = member; + return this; + } + + public CommentBuilder content(Content content) { + this.content = content; + return this; + } + + public Comment save() { + Comment comment = Comment.builder() + .post(post == null ? Post.builder().build() : post) + .member(member == null ? MemberFixtures.MALE_20.get() : member) + .content(content == null ? "content" : content.getValue()) + .build(); + return commentRepository.save(comment); + } + } + +} diff --git a/backend/src/test/java/com/votogether/test/persister/PostTestPersister.java b/backend/src/test/java/com/votogether/test/persister/PostTestPersister.java index 7f2e3cfb0..2826bd6e9 100644 --- a/backend/src/test/java/com/votogether/test/persister/PostTestPersister.java +++ b/backend/src/test/java/com/votogether/test/persister/PostTestPersister.java @@ -4,8 +4,10 @@ import com.votogether.domain.post.entity.Post; import com.votogether.domain.post.entity.PostBody; import com.votogether.domain.post.repository.PostRepository; +import com.votogether.domain.post.service.PostService; import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; @RequiredArgsConstructor @Persister @@ -23,6 +25,7 @@ public final class PostBuilder { private Member writer; private PostBody postBody; private LocalDateTime deadline; + private boolean isHidden; public PostBuilder writer(Member writer) { this.writer = writer; @@ -39,12 +42,21 @@ public PostBuilder deadline(LocalDateTime deadline) { return this; } + public PostBuilder blind() { + this.isHidden = true; + return this; + } + public Post save() { Post post = Post.builder() .writer(writer == null ? memberTestPersister.builder().save() : writer) .postBody(postBody == null ? generatePostBody() : postBody) .deadline(deadline == null ? LocalDateTime.of(2100, 12, 25, 0, 0) : deadline) .build(); + if (isHidden) { + post.blind(); + } + return postRepository.save(post); } @@ -54,7 +66,6 @@ private PostBody generatePostBody() { .content("content") .build(); } - } } diff --git a/backend/src/test/java/com/votogether/test/persister/ReportTestPersister.java b/backend/src/test/java/com/votogether/test/persister/ReportTestPersister.java new file mode 100644 index 000000000..a2b2c88a5 --- /dev/null +++ b/backend/src/test/java/com/votogether/test/persister/ReportTestPersister.java @@ -0,0 +1,59 @@ +package com.votogether.test.persister; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.report.entity.Report; +import com.votogether.domain.report.entity.vo.ReportType; +import com.votogether.domain.report.repository.ReportRepository; +import java.time.LocalDateTime; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Persister +public class ReportTestPersister { + + private final ReportRepository reportRepository; + private final MemberTestPersister memberTestPersister; + + public ReportBuilder builder() { + return new ReportTestPersister.ReportBuilder(); + } + + public final class ReportBuilder { + + private Member member; + private ReportType reportType; + private Long targetId; + private String reason; + + public ReportBuilder member(final Member member) { + this.member = member; + return this; + } + + public ReportBuilder reportType(final ReportType reportType) { + this.reportType = reportType; + return this; + } + + public ReportBuilder targetId(final Long targetId) { + this.targetId = targetId; + return this; + } + + public ReportBuilder reason(final String reason) { + this.reason = reason; + return this; + } + + public Report save() { + Report report = Report.builder() + .member(member == null ? memberTestPersister.builder().save() : member) + .reportType(reportType == null ? ReportType.POST : reportType) + .targetId(targetId == null ? 1L : targetId) + .reason(reason == null ? "reason" : reason) + .build(); + + return reportRepository.save(report); + } + } +}