diff --git a/.gitignore b/.gitignore index c2065bc2..ac7510d5 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ + +### MAC DSStore +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 00000000..c9b06551 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +## 모각코하기 좋은 카페 지도 애플리케이션, 모카콩(MOCACONG) + +코딩하기 좋은 카페만 모아놓은 지도는 없을까? 고민했다면 모카콩! + +

+ image + image + image + image +

+ + +- - - + +### MVP 기능 +

+ image +

+ +- - - + +### 사용 기술스택 +- `Language`: Java 11, JUnit 5 +- `Framework`: Spring Boot 2.7.9 +- `Database`: H2, Amazon RDS for MySQL, Amazon Elasticache for Redis +- `ORM`: JPA (Spring Data JPA) +- `Deploy`: Github Actions, Docker CI/CD +- `Logging`: Logback, AWS Cloudwatch, AWS Lambda, Slack API +- `API Docs`: SpringDoc Swagger 3 +- `Performance Test`: nGrinder + +- - - + +### ERD +

+ image +

+ +- - - + +### 서비스 아키텍처 +

+ image +

diff --git a/src/main/java/mocacong/server/config/BatchConfig.java b/src/main/java/mocacong/server/config/BatchConfig.java index ef18e0c8..eb2e63a0 100644 --- a/src/main/java/mocacong/server/config/BatchConfig.java +++ b/src/main/java/mocacong/server/config/BatchConfig.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import mocacong.server.service.CafeService; import mocacong.server.service.MemberService; +import mocacong.server.service.ReportService; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; @@ -20,4 +21,9 @@ public void deleteNotUsedImages() { memberService.deleteNotUsedProfileImages(); cafeService.deleteNotUsedCafeImages(); } + + @Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") // 매일 자정에 실행 + public void activateInactivateMembers() { + memberService.setActiveAfter60days(); + } } diff --git a/src/main/java/mocacong/server/controller/CafeController.java b/src/main/java/mocacong/server/controller/CafeController.java index 7fa0e6bb..253bb071 100644 --- a/src/main/java/mocacong/server/controller/CafeController.java +++ b/src/main/java/mocacong/server/controller/CafeController.java @@ -3,18 +3,19 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; -import javax.validation.Valid; import lombok.RequiredArgsConstructor; import mocacong.server.dto.request.*; import mocacong.server.dto.response.*; -import mocacong.server.security.auth.LoginUserEmail; +import mocacong.server.security.auth.LoginUserId; import mocacong.server.service.CafeService; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import javax.validation.Valid; +import java.util.List; + @Tag(name = "Cafes", description = "카페") @RestController @RequiredArgsConstructor @@ -34,10 +35,10 @@ public ResponseEntity cafeRegister(@RequestBody @Valid CafeRegisterRequest @SecurityRequirement(name = "JWT") @GetMapping("/{mapId}") public ResponseEntity findCafeByMapId( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId ) { - FindCafeResponse response = cafeService.findCafeByMapId(email, mapId); + FindCafeResponse response = cafeService.findCafeByMapId(memberId, mapId); return ResponseEntity.ok(response); } @@ -45,10 +46,10 @@ public ResponseEntity findCafeByMapId( @SecurityRequirement(name = "JWT") @GetMapping("/{mapId}/preview") public ResponseEntity previewCafeByMapId( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId ) { - PreviewCafeResponse response = cafeService.previewCafeByMapId(email, mapId); + PreviewCafeResponse response = cafeService.previewCafeByMapId(memberId, mapId); return ResponseEntity.ok(response); } @@ -56,11 +57,11 @@ public ResponseEntity previewCafeByMapId( @SecurityRequirement(name = "JWT") @PostMapping("/{mapId}") public ResponseEntity saveCafeReview( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, - @RequestBody CafeReviewRequest request + @RequestBody @Valid CafeReviewRequest request ) { - CafeReviewResponse response = cafeService.saveCafeReview(email, mapId, request); + CafeReviewResponse response = cafeService.saveCafeReview(memberId, mapId, request); return ResponseEntity.ok(response); } @@ -68,10 +69,10 @@ public ResponseEntity saveCafeReview( @SecurityRequirement(name = "JWT") @GetMapping("/{mapId}/me") public ResponseEntity findMyCafeReview( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId ) { - CafeMyReviewResponse response = cafeService.findMyCafeReview(email, mapId); + CafeMyReviewResponse response = cafeService.findMyCafeReview(memberId, mapId); return ResponseEntity.ok(response); } @@ -79,11 +80,11 @@ public ResponseEntity findMyCafeReview( @SecurityRequirement(name = "JWT") @PutMapping("/{mapId}") public ResponseEntity updateCafeReview( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, - @RequestBody CafeReviewUpdateRequest request + @RequestBody @Valid CafeReviewUpdateRequest request ) { - CafeReviewUpdateResponse response = cafeService.updateCafeReview(email, mapId, request); + CafeReviewUpdateResponse response = cafeService.updateCafeReview(memberId, mapId, request); return ResponseEntity.ok(response); } @@ -91,7 +92,7 @@ public ResponseEntity updateCafeReview( @SecurityRequirement(name = "JWT") @PostMapping(value = "/studytypes") public ResponseEntity getCafesByStudyType( - @RequestParam(required = false) String studytype, + @RequestParam String studytype, @RequestBody CafeFilterStudyTypeRequest request ) { CafeFilterStudyTypeResponse responseBody = cafeService.filterCafesByStudyType(studytype, request); @@ -102,35 +103,35 @@ public ResponseEntity getCafesByStudyType( @SecurityRequirement(name = "JWT") @PostMapping("/favorites") public ResponseEntity getCafesByFavorites( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestBody CafeFilterFavoritesRequest request ) { - CafeFilterFavoritesResponse responseBody = cafeService.filterCafesByFavorites(email, request); + CafeFilterFavoritesResponse responseBody = cafeService.filterCafesByFavorites(memberId, request); return ResponseEntity.ok(responseBody); } @Operation(summary = "카페 이미지 업로드") @SecurityRequirement(name = "JWT") @PostMapping(value = "/{mapId}/img", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity saveCafeImage( - @LoginUserEmail String email, + public ResponseEntity saveCafeImage( + @LoginUserId Long memberId, @PathVariable String mapId, @RequestParam(value = "files") List multipartFiles ) { - cafeService.saveCafeImage(email, mapId, multipartFiles); - return ResponseEntity.ok().build(); + CafeImagesSaveResponse response = cafeService.saveCafeImage(memberId, mapId, multipartFiles); + return ResponseEntity.ok(response); } @Operation(summary = "카페 이미지 조회") @SecurityRequirement(name = "JWT") @GetMapping("/{mapId}/img") public ResponseEntity getCafeImages( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, @RequestParam("page") final Integer page, @RequestParam("count") final int count ) { - CafeImagesResponse response = cafeService.findCafeImages(email, mapId, page, count); + CafeImagesResponse response = cafeService.findCafeImages(memberId, mapId, page, count); return ResponseEntity.ok(response); } @@ -138,12 +139,12 @@ public ResponseEntity getCafeImages( @SecurityRequirement(name = "JWT") @PutMapping(value = "/{mapId}/img/{cafeImageId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity updateCafeImage( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, @PathVariable Long cafeImageId, @RequestParam(value = "file") MultipartFile multipartFile ) { - cafeService.updateCafeImage(email, mapId, cafeImageId, multipartFile); + cafeService.updateCafeImage(memberId, mapId, cafeImageId, multipartFile); return ResponseEntity.ok().build(); } } diff --git a/src/main/java/mocacong/server/controller/CommentController.java b/src/main/java/mocacong/server/controller/CommentController.java index bfd427d7..66c9f7ca 100644 --- a/src/main/java/mocacong/server/controller/CommentController.java +++ b/src/main/java/mocacong/server/controller/CommentController.java @@ -8,11 +8,13 @@ import mocacong.server.dto.request.CommentUpdateRequest; import mocacong.server.dto.response.CommentSaveResponse; import mocacong.server.dto.response.CommentsResponse; -import mocacong.server.security.auth.LoginUserEmail; +import mocacong.server.security.auth.LoginUserId; import mocacong.server.service.CommentService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + @Tag(name = "Comments", description = "댓글") @RestController @RequiredArgsConstructor @@ -25,11 +27,11 @@ public class CommentController { @SecurityRequirement(name = "JWT") @PostMapping public ResponseEntity saveComment( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, - @RequestBody CommentSaveRequest request + @RequestBody @Valid CommentSaveRequest request ) { - CommentSaveResponse response = commentService.save(email, mapId, request.getContent()); + CommentSaveResponse response = commentService.save(memberId, mapId, request.getContent()); return ResponseEntity.ok(response); } @@ -37,12 +39,12 @@ public ResponseEntity saveComment( @SecurityRequirement(name = "JWT") @GetMapping public ResponseEntity findComments( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, @RequestParam("page") final Integer page, @RequestParam("count") final int count ) { - CommentsResponse response = commentService.findAll(email, mapId, page, count); + CommentsResponse response = commentService.findAll(memberId, mapId, page, count); return ResponseEntity.ok(response); } @@ -50,12 +52,12 @@ public ResponseEntity findComments( @SecurityRequirement(name = "JWT") @GetMapping("/me") public ResponseEntity findCommentsByMe( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, @RequestParam("page") final Integer page, @RequestParam("count") final int count ) { - CommentsResponse response = commentService.findCafeCommentsOnlyMyComments(email, mapId, page, count); + CommentsResponse response = commentService.findCafeCommentsOnlyMyComments(memberId, mapId, page, count); return ResponseEntity.ok(response); } @@ -63,12 +65,12 @@ public ResponseEntity findCommentsByMe( @SecurityRequirement(name = "JWT") @PutMapping("/{commentId}") public ResponseEntity updateComment( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, @PathVariable Long commentId, - @RequestBody CommentUpdateRequest request + @RequestBody @Valid CommentUpdateRequest request ) { - commentService.update(email, mapId, request.getContent(), commentId); + commentService.update(memberId, mapId, request.getContent(), commentId); return ResponseEntity.ok().build(); } @@ -76,11 +78,11 @@ public ResponseEntity updateComment( @SecurityRequirement(name = "JWT") @DeleteMapping("/{commentId}") public ResponseEntity deleteComment( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId, @PathVariable Long commentId ) { - commentService.delete(email, mapId, commentId); + commentService.delete(memberId, mapId, commentId); return ResponseEntity.ok().build(); } } diff --git a/src/main/java/mocacong/server/controller/CommentLikeController.java b/src/main/java/mocacong/server/controller/CommentLikeController.java new file mode 100644 index 00000000..8bf13353 --- /dev/null +++ b/src/main/java/mocacong/server/controller/CommentLikeController.java @@ -0,0 +1,41 @@ +package mocacong.server.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import mocacong.server.dto.response.CommentLikeSaveResponse; +import mocacong.server.security.auth.LoginUserId; +import mocacong.server.service.CommentLikeService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "CommentsLike", description = "댓글 좋아요") +@RestController +@RequiredArgsConstructor +@RequestMapping("/comments/{commentId}/like") +public class CommentLikeController { + private final CommentLikeService commentLikeService; + + @Operation(summary = "댓글 좋아요 등록") + @SecurityRequirement(name = "JWT") + @PostMapping + public ResponseEntity saveCommentLike( + @LoginUserId Long memberId, + @PathVariable Long commentId + ) { + CommentLikeSaveResponse response = commentLikeService.save(memberId, commentId); + return ResponseEntity.ok(response); + } + + @Operation(summary = "댓글 좋아요 삭제") + @SecurityRequirement(name = "JWT") + @DeleteMapping + public ResponseEntity deleteCommentLike( + @LoginUserId Long memberId, + @PathVariable Long commentId + ) { + commentLikeService.delete(memberId, commentId); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/mocacong/server/controller/ControllerAdvice.java b/src/main/java/mocacong/server/controller/ControllerAdvice.java index 05c502db..6f5c3d1b 100644 --- a/src/main/java/mocacong/server/controller/ControllerAdvice.java +++ b/src/main/java/mocacong/server/controller/ControllerAdvice.java @@ -1,22 +1,24 @@ package mocacong.server.controller; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import mocacong.server.dto.response.ErrorResponse; import mocacong.server.exception.MocacongException; import mocacong.server.support.SlackAlarmGenerator; +import org.apache.tomcat.util.http.fileupload.FileUploadException; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.FieldError; import org.springframework.web.HttpMediaTypeException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.multipart.MultipartException; - -import javax.servlet.http.HttpServletRequest; -import java.util.Objects; +import org.springframework.web.multipart.support.MissingServletRequestPartException; @Slf4j @RequiredArgsConstructor @@ -43,28 +45,56 @@ public ResponseEntity handleInputFieldException(MethodArgumentNot public ResponseEntity handleJsonException(HttpMessageNotReadableException e) { log.warn("Json Exception ErrMessage={}\n", e.getMessage()); - return ResponseEntity.badRequest().body(new ErrorResponse(9000, "Json 형식이 올바르지 않습니다.")); + return ResponseEntity.badRequest() + .body(new ErrorResponse(9000, "Json 형식이 올바르지 않습니다.")); } @ExceptionHandler(HttpMediaTypeException.class) public ResponseEntity handleContentTypeException(HttpMediaTypeException e) { log.warn("ContentType Exception ErrMessage={}\n", e.getMessage()); - return ResponseEntity.badRequest().body(new ErrorResponse(9001, "ContentType 값이 올바르지 않습니다.")); + return ResponseEntity.badRequest() + .body(new ErrorResponse(9001, "ContentType 값이 올바르지 않습니다.")); } @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResponseEntity handleRequestMethodException(HttpRequestMethodNotSupportedException e) { log.warn("Http Method not supported Exception ErrMessage={}\n", e.getMessage()); - return ResponseEntity.badRequest().body(new ErrorResponse(9002, "해당 Http Method에 맞는 API가 존재하지 않습니다.")); + return ResponseEntity.badRequest() + .body(new ErrorResponse(9002, "해당 Http Method에 맞는 API가 존재하지 않습니다.")); } @ExceptionHandler(MultipartException.class) public ResponseEntity handleFileSizeLimitExceeded(MultipartException e) { log.error("File Size Limit Exception ErrMessage={}\n", e.getMessage()); - return ResponseEntity.badRequest().body(new ErrorResponse(9003, "이미지 용량이 10MB를 초과합니다.")); + return ResponseEntity.badRequest() + .body(new ErrorResponse(9003, "이미지 용량이 10MB를 초과합니다.")); + } + + @ExceptionHandler(MissingServletRequestParameterException.class) + public ResponseEntity handleMissingRequestParamException(MissingServletRequestParameterException e) { + log.warn("Request Param is Missing! ErrMessage={}\n", e.getMessage()); + + return ResponseEntity.badRequest() + .body(new ErrorResponse(9004, "요청 param 이름이 올바르지 않습니다.")); + } + + @ExceptionHandler(MissingServletRequestPartException.class) + public ResponseEntity handleMissingMultiPartParamException(MissingServletRequestPartException e) { + log.warn("MultipartFile Request Param is Missing! ErrMessage={}\n", e.getMessage()); + + return ResponseEntity.badRequest() + .body(new ErrorResponse(9005, "요청 MultipartFile param 이름이 올바르지 않습니다.")); + } + + @ExceptionHandler(FileUploadException.class) + public ResponseEntity handleMissingMultiPartParamException(FileUploadException e) { + log.warn("File Upload Exception! Please check request. ErrMessage={}\n", e.getMessage()); + + return ResponseEntity.badRequest() + .body(new ErrorResponse(9006, "요청 파일이 올바르지 않습니다. 파일 손상 여부나 요청 형식을 확인해주세요.")); } @ExceptionHandler(MocacongException.class) diff --git a/src/main/java/mocacong/server/controller/FavoriteController.java b/src/main/java/mocacong/server/controller/FavoriteController.java index 5f182f6b..d8de55ab 100644 --- a/src/main/java/mocacong/server/controller/FavoriteController.java +++ b/src/main/java/mocacong/server/controller/FavoriteController.java @@ -5,7 +5,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import mocacong.server.dto.response.FavoriteSaveResponse; -import mocacong.server.security.auth.LoginUserEmail; +import mocacong.server.security.auth.LoginUserId; import mocacong.server.service.FavoriteService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -22,10 +22,10 @@ public class FavoriteController { @SecurityRequirement(name = "JWT") @PostMapping public ResponseEntity saveFavoriteCafe( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId ) { - FavoriteSaveResponse response = favoriteService.save(email, mapId); + FavoriteSaveResponse response = favoriteService.save(memberId, mapId); return ResponseEntity.ok(response); } @@ -33,10 +33,10 @@ public ResponseEntity saveFavoriteCafe( @SecurityRequirement(name = "JWT") @DeleteMapping public ResponseEntity deleteFavoriteCafe( - @LoginUserEmail String email, + @LoginUserId Long memberId, @PathVariable String mapId ) { - favoriteService.delete(email, mapId); + favoriteService.delete(memberId, mapId); return ResponseEntity.ok().build(); } } diff --git a/src/main/java/mocacong/server/controller/MemberController.java b/src/main/java/mocacong/server/controller/MemberController.java index 0aba724d..bc5995b4 100644 --- a/src/main/java/mocacong/server/controller/MemberController.java +++ b/src/main/java/mocacong/server/controller/MemberController.java @@ -6,7 +6,7 @@ import lombok.RequiredArgsConstructor; import mocacong.server.dto.request.*; import mocacong.server.dto.response.*; -import mocacong.server.security.auth.LoginUserEmail; +import mocacong.server.security.auth.LoginUserId; import mocacong.server.service.CafeService; import mocacong.server.service.MemberService; import org.springframework.http.MediaType; @@ -63,20 +63,20 @@ public ResponseEntity checkDuplicateNickname(@Reque @Operation(summary = "마이페이지 조회") @SecurityRequirement(name = "JWT") @GetMapping("/mypage") - public ResponseEntity findMyInfo(@LoginUserEmail String email) { - MyPageResponse response = memberService.findMyInfo(email); + public ResponseEntity findMyInfo(@LoginUserId Long memberId) { + MyPageResponse response = memberService.findMyInfo(memberId); return ResponseEntity.ok(response); } @Operation(summary = "마이페이지 - 즐겨찾기 조회") @SecurityRequirement(name = "JWT") @GetMapping("/mypage/stars") - public ResponseEntity findMyInfo( - @LoginUserEmail String email, + public ResponseEntity findMyFavoriteCafes( + @LoginUserId Long memberId, @RequestParam("page") final Integer page, @RequestParam("count") final int count ) { - MyFavoriteCafesResponse response = cafeService.findMyFavoriteCafes(email, page, count); + MyFavoriteCafesResponse response = cafeService.findMyFavoriteCafes(memberId, page, count); return ResponseEntity.ok(response); } @@ -84,11 +84,11 @@ public ResponseEntity findMyInfo( @SecurityRequirement(name = "JWT") @GetMapping("/mypage/reviews") public ResponseEntity findMyReviewCafes( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestParam("page") final Integer page, @RequestParam("count") final int count ) { - MyReviewCafesResponse response = cafeService.findMyReviewCafes(email, page, count); + MyReviewCafesResponse response = cafeService.findMyReviewCafes(memberId, page, count); return ResponseEntity.ok(response); } @@ -96,11 +96,11 @@ public ResponseEntity findMyReviewCafes( @SecurityRequirement(name = "JWT") @GetMapping("/mypage/comments") public ResponseEntity findMyComments( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestParam("page") final Integer page, @RequestParam("count") final int count ) { - MyCommentCafesResponse response = cafeService.findMyCommentCafes(email, page, count); + MyCommentCafesResponse response = cafeService.findMyCommentCafes(memberId, page, count); return ResponseEntity.ok(response); } @@ -108,10 +108,10 @@ public ResponseEntity findMyComments( @SecurityRequirement(name = "JWT") @PutMapping(value = "/mypage/img", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity updateProfileImage( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestParam(value = "file", required = false) MultipartFile multipartFile ) { - memberService.updateProfileImage(email, multipartFile); + memberService.updateProfileImage(memberId, multipartFile); return ResponseEntity.ok().build(); } @@ -119,10 +119,10 @@ public ResponseEntity updateProfileImage( @SecurityRequirement(name = "JWT") @PutMapping(value = "/info") public ResponseEntity updateProfileInfo( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestBody @Valid MemberProfileUpdateRequest request ) { - memberService.updateProfileInfo(email, request); + memberService.updateProfileInfo(memberId, request); return ResponseEntity.ok().build(); } @@ -130,9 +130,9 @@ public ResponseEntity updateProfileInfo( @SecurityRequirement(name = "JWT") @GetMapping(value = "/info") public ResponseEntity getUpdateProfileInfo( - @LoginUserEmail String email + @LoginUserId Long memberId ) { - GetUpdateProfileInfoResponse response = memberService.getUpdateProfileInfo(email); + GetUpdateProfileInfoResponse response = memberService.getUpdateProfileInfo(memberId); return ResponseEntity.ok(response); } @@ -140,10 +140,10 @@ public ResponseEntity getUpdateProfileInfo( @SecurityRequirement(name = "JWT") @PostMapping("/info/password") public ResponseEntity passwordVerify( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestBody @Valid PasswordVerifyRequest request ) { - PasswordVerifyResponse response = memberService.verifyPassword(email, request); + PasswordVerifyResponse response = memberService.verifyPassword(memberId, request); return ResponseEntity.ok(response); } @@ -151,18 +151,18 @@ public ResponseEntity passwordVerify( @SecurityRequirement(name = "JWT") @PutMapping("/info/reset-password") public ResponseEntity findAndResetPassword( - @LoginUserEmail String email, + @LoginUserId Long memberId, @RequestBody @Valid ResetPasswordRequest request ) { - memberService.resetPassword(email, request); + memberService.resetPassword(memberId, request); return ResponseEntity.ok().build(); } @Operation(summary = "회원탈퇴") @SecurityRequirement(name = "JWT") @DeleteMapping - public ResponseEntity delete(@LoginUserEmail String email) { - memberService.delete(email); + public ResponseEntity delete(@LoginUserId Long memberId) { + memberService.delete(memberId); return ResponseEntity.ok().build(); } diff --git a/src/main/java/mocacong/server/controller/ReportController.java b/src/main/java/mocacong/server/controller/ReportController.java new file mode 100644 index 00000000..42f83160 --- /dev/null +++ b/src/main/java/mocacong/server/controller/ReportController.java @@ -0,0 +1,35 @@ +package mocacong.server.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import mocacong.server.dto.request.CommentReportRequest; +import mocacong.server.dto.response.CommentReportResponse; +import mocacong.server.security.auth.LoginUserId; +import mocacong.server.service.ReportService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@Tag(name = "Reports", description = "신고") +@RestController +@RequiredArgsConstructor +@RequestMapping("/reports") +public class ReportController { + + private final ReportService reportService; + + @Operation(summary = "카페 코멘트 신고") + @SecurityRequirement(name = "JWT") + @PostMapping("/comment/{commentId}") + public ResponseEntity reportComment( + @LoginUserId Long memberId, + @PathVariable Long commentId, + @RequestBody @Valid CommentReportRequest request + ) { + CommentReportResponse response = reportService.reportComment(memberId, commentId, request.getMyReportReason()); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/mocacong/server/domain/Cafe.java b/src/main/java/mocacong/server/domain/Cafe.java index 78fa09bb..62bf598d 100644 --- a/src/main/java/mocacong/server/domain/Cafe.java +++ b/src/main/java/mocacong/server/domain/Cafe.java @@ -35,7 +35,7 @@ public class Cafe extends BaseTime { @Embedded private CafeDetail cafeDetail; - @OneToMany(mappedBy = "cafe", fetch = FetchType.EAGER) + @OneToMany(mappedBy = "cafe", fetch = FetchType.LAZY) private List cafeImages; @OneToMany(mappedBy = "cafe", fetch = FetchType.LAZY) @@ -77,7 +77,7 @@ private StudyType getMostFrequentStudyType() { StudyType studyType = review.getStudyType(); if (studyType == StudyType.SOLO) solo++; else if (studyType == StudyType.GROUP) group++; - else { + else if (studyType == StudyType.BOTH) { solo++; group++; } @@ -89,6 +89,10 @@ private StudyType getMostFrequentStudyType() { else return StudyType.GROUP; } + public String getStudyType() { + return cafeDetail.getStudyTypeValue(); + } + public double findAverageScore() { return score.stream() .mapToDouble(Score::getScore) diff --git a/src/main/java/mocacong/server/domain/CafeImage.java b/src/main/java/mocacong/server/domain/CafeImage.java index 8b15da53..549dc814 100644 --- a/src/main/java/mocacong/server/domain/CafeImage.java +++ b/src/main/java/mocacong/server/domain/CafeImage.java @@ -39,10 +39,14 @@ public CafeImage(String imgUrl, Boolean isUsed, Cafe cafe, Member member) { } public boolean isOwned(Member member) { - return this.member.equals(member); + return this.member != null && this.member.equals(member); } - public void setIsUsed(Boolean isUsed) { - this.isUsed = isUsed; + public void updateImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public void removeMember() { + this.member = null; } } diff --git a/src/main/java/mocacong/server/domain/Comment.java b/src/main/java/mocacong/server/domain/Comment.java index c7a5d50b..12fcf2b8 100644 --- a/src/main/java/mocacong/server/domain/Comment.java +++ b/src/main/java/mocacong/server/domain/Comment.java @@ -1,11 +1,14 @@ package mocacong.server.domain; -import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import mocacong.server.exception.badrequest.ExceedCommentLengthException; +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + @Entity @Table(name = "comment") @Getter @@ -13,6 +16,9 @@ public class Comment extends BaseTime { private static final int MAXIMUM_COMMENT_LENGTH = 200; + private static final int REPORT_COMMENT_THRESHOLD_COUNT = 5; + private static final String MASK_COMMENT_CONTENT = "삭제된 댓글입니다"; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "comment_id") @@ -26,14 +32,21 @@ public class Comment extends BaseTime { @JoinColumn(name = "member_id") private Member member; - @Column(name = "content", nullable = false, length = 200) + @Column(name = "content", nullable = false, length = MAXIMUM_COMMENT_LENGTH) private String content; + @OneToMany(mappedBy = "comment", cascade = CascadeType.ALL, orphanRemoval = true) + private List reports = new ArrayList<>(); + + @Column(name = "is_masked") + private boolean isMasked; + public Comment(Cafe cafe, Member member, String content) { this.cafe = cafe; this.member = member; validateCommentLength(content); this.content = content; + this.isMasked = false; } private void validateCommentLength(String content) { @@ -62,4 +75,41 @@ public void updateComment(String content) { public void removeMember() { this.member = null; } + + public int getReportsCount() { + return reports.size(); + } + + public boolean isDeletedMember() { + return member == null; + } + + public boolean isReportThresholdExceeded() { + return getReportsCount() >= REPORT_COMMENT_THRESHOLD_COUNT; + } + + public boolean isDeletedCommenter() { + return isDeletedMember() && isReportThresholdExceeded(); + } + + public boolean hasAlreadyReported(Member member) { + return this.reports.stream() + .anyMatch(report -> report.getReporter().equals(member)); + } + + public void maskComment() { + this.content = MASK_COMMENT_CONTENT; + } + + public void maskAuthor() { + this.member = null; + } + + public void addReport(Report report) { + reports.add(report); + } + + public void updateIsMasked(boolean isMasked) { + this.isMasked= isMasked; + } } diff --git a/src/main/java/mocacong/server/domain/CommentLike.java b/src/main/java/mocacong/server/domain/CommentLike.java new file mode 100644 index 00000000..b7518400 --- /dev/null +++ b/src/main/java/mocacong/server/domain/CommentLike.java @@ -0,0 +1,42 @@ +package mocacong.server.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "comment_like", uniqueConstraints = { + @UniqueConstraint(columnNames = { "member_id", "comment_id" }) +}) +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CommentLike { + + @Id + @Column(name = "comment_like_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comment_id") + private Comment comment; + + public CommentLike(Member member, Comment comment) { + this.member = member; + this.comment = comment; + } + + public void removeMember() { + this.member = null; + } + + public void removeComment() { + this.comment = null; + } +} diff --git a/src/main/java/mocacong/server/domain/Member.java b/src/main/java/mocacong/server/domain/Member.java index 927eb8e9..42cf1f62 100644 --- a/src/main/java/mocacong/server/domain/Member.java +++ b/src/main/java/mocacong/server/domain/Member.java @@ -4,26 +4,27 @@ import lombok.Getter; import lombok.NoArgsConstructor; import mocacong.server.exception.badrequest.InvalidNicknameException; -import mocacong.server.exception.badrequest.InvalidPhoneException; import javax.persistence.*; import java.util.regex.Pattern; @Entity -@Table(name = "member") +@Table(name = "member", uniqueConstraints = { + @UniqueConstraint(columnNames = {"email", "platform"}) +}) @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member extends BaseTime { private static final Pattern NICKNAME_REGEX = Pattern.compile("^[a-zA-Zㄱ-ㅎㅏ-ㅣ가-힣]{2,6}$"); - private static final Pattern PHONE_REGEX = Pattern.compile("^01[\\d\\-]{8,12}$"); + private static final int REPORT_MEMBER_THRESHOLD_COUNT = 11; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "member_id") private Long id; - @Column(name = "email", unique = true, nullable = false) + @Column(name = "email", nullable = false) private String email; @Column(name = "password") @@ -32,9 +33,6 @@ public class Member extends BaseTime { @Column(name = "nickname", unique = true) private String nickname; - @Column(name = "phone") - private String phone; - @OneToOne @JoinColumn(name = "member_profile_image_id") private MemberProfileImage memberProfileImage; @@ -46,40 +44,57 @@ public class Member extends BaseTime { @Column(name = "platform_id") private String platformId; - public Member( - String email, String password, String nickname, String phone, MemberProfileImage memberProfileImage, - Platform platform, String platformId - ) { - validateMemberInfo(nickname, phone); + @Enumerated(EnumType.STRING) + @Column(name = "status") + private Status status; + + @Column(name = "report_count") + private int reportCount; + + public Member(String email, String password, String nickname, MemberProfileImage memberProfileImage, + Platform platform, String platformId) { + validateNickname(nickname); this.email = email; this.password = password; this.nickname = nickname; - this.phone = phone; this.memberProfileImage = memberProfileImage; this.platform = platform; this.platformId = platformId; + this.status = Status.ACTIVE; } - public Member(String email, String password, String nickname, String phone, MemberProfileImage memberProfileImage) { - this( - email, - password, - nickname, - phone, - memberProfileImage, - Platform.MOCACONG, - null - ); + public Member(String email, String password, String nickname, MemberProfileImage memberProfileImage, + Platform platform, String platformId, Status status) { + validateNickname(nickname); + this.email = email; + this.password = password; + this.nickname = nickname; + this.memberProfileImage = memberProfileImage; + this.platform = platform; + this.platformId = platformId; + this.status = status; + } + + public Member(String email, String password, String nickname, MemberProfileImage memberProfileImage) { + this(email, password, nickname, memberProfileImage, Platform.MOCACONG, null, Status.ACTIVE); } - public Member(String email, String password, String nickname, String phone) { - this(email, password, nickname, phone, null, Platform.MOCACONG, null); + public Member(String email, String password, String nickname) { + this(email, password, nickname, null, Platform.MOCACONG, null, Status.ACTIVE); } public Member(String email, Platform platform, String platformId) { this.email = email; this.platform = platform; this.platformId = platformId; + this.status = Status.ACTIVE; + } + + public Member(String email, Platform platform, String platformId, Status status) { + this.email = email; + this.platform = platform; + this.platformId = platformId; + this.status = status; } public void registerOAuthMember(String email, String nickname) { @@ -105,15 +120,9 @@ private void updateBeforeProfileImageNotUsedStatus() { } } - public void updateProfileInfo(String nickname, String phone) { - validateMemberInfo(nickname, phone); - this.nickname = nickname; - this.phone = phone; - } - - private void validateMemberInfo(String nickname, String phone) { + public void updateProfileInfo(String nickname) { validateNickname(nickname); - validatePhone(phone); + this.nickname = nickname; } private void validateNickname(String nickname) { @@ -122,12 +131,6 @@ private void validateNickname(String nickname) { } } - private void validatePhone(String phone) { - if (!PHONE_REGEX.matcher(phone).matches()) { - throw new InvalidPhoneException(); - } - } - public void updatePassword(String password) { this.password = password; } @@ -135,4 +138,22 @@ public void updatePassword(String password) { public boolean isRegisteredOAuthMember() { return nickname != null; } + + public void changeStatus(Status status) { + this.status = status; + if (status == Status.ACTIVE) { // 정지를 푸는 경우 + resetMemberReportCount(); + } + } + + public void resetMemberReportCount() { + this.reportCount = 0; + } + + public void incrementMemberReportCount() { + this.reportCount += 1; + if (this.reportCount >= REPORT_MEMBER_THRESHOLD_COUNT) { + changeStatus(Status.INACTIVE); + } + } } diff --git a/src/main/java/mocacong/server/domain/Report.java b/src/main/java/mocacong/server/domain/Report.java new file mode 100644 index 00000000..3edc7014 --- /dev/null +++ b/src/main/java/mocacong/server/domain/Report.java @@ -0,0 +1,51 @@ +package mocacong.server.domain; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "report", uniqueConstraints = { + @UniqueConstraint(columnNames = { "comment_id", "member_id" }) +}) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Report { + + private static final int MAXIMUM_COMMENT_LENGTH = 200; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "report_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comment_id") + private Comment comment; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member reporter; + + @Enumerated(EnumType.STRING) + @Column(name = "report_reason") + private ReportReason reportReason; + + @Column(name = "original_content", length = MAXIMUM_COMMENT_LENGTH) + private String originalContent; + + public Report(Comment comment, Member reporter, ReportReason reportReason) { + this.comment = comment; + this.reporter = reporter; + this.reportReason = reportReason; + this.originalContent = comment.getContent(); + } + + public Member getReporter() { + return reporter; + } + + public void removeReporter() { + this.reporter = null; + } +} diff --git a/src/main/java/mocacong/server/domain/ReportReason.java b/src/main/java/mocacong/server/domain/ReportReason.java new file mode 100644 index 00000000..55165e5a --- /dev/null +++ b/src/main/java/mocacong/server/domain/ReportReason.java @@ -0,0 +1,32 @@ +package mocacong.server.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import mocacong.server.exception.badrequest.InvalidReportReasonException; + +import java.util.Arrays; +import java.util.Objects; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +public enum ReportReason { + + FISHING_HARASSMENT_SPAM("fishing_harassment_spam"), + LEAKING_FRAUD("leaking_fraud"), + PORNOGRAPHY("pornography"), + INAPPROPRIATE_CONTENT("inappropriate_content"), + INSULT("insult"), + COMMERCIAL_AD("commercial_ad"), + POLITICAL_CONTENT("political_content"); + + private String value; + + public static ReportReason from(String value) { + return Arrays.stream(values()) + .filter(it -> Objects.equals(it.value, value)) + .findFirst() + .orElseThrow(InvalidReportReasonException::new); + } +} diff --git a/src/main/java/mocacong/server/domain/Score.java b/src/main/java/mocacong/server/domain/Score.java index 3f9b063e..652f89e7 100644 --- a/src/main/java/mocacong/server/domain/Score.java +++ b/src/main/java/mocacong/server/domain/Score.java @@ -8,7 +8,9 @@ import javax.persistence.*; @Entity -@Table(name = "score") +@Table(name = "score", uniqueConstraints = { + @UniqueConstraint(columnNames = { "member_id", "cafe_id" }) +}) @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Score extends BaseTime { diff --git a/src/main/java/mocacong/server/domain/Status.java b/src/main/java/mocacong/server/domain/Status.java new file mode 100644 index 00000000..72d03b26 --- /dev/null +++ b/src/main/java/mocacong/server/domain/Status.java @@ -0,0 +1,27 @@ +package mocacong.server.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import mocacong.server.exception.badrequest.InvalidStatusException; + +import java.util.Arrays; +import java.util.Objects; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +public enum Status { + + ACTIVE("active"), + INACTIVE("inactive"); + + private String value; + + public static Status from(String value) { + return Arrays.stream(values()) + .filter(it -> Objects.equals(it.value, value)) + .findFirst() + .orElseThrow(InvalidStatusException::new); + } +} diff --git a/src/main/java/mocacong/server/dto/request/CafeReviewRequest.java b/src/main/java/mocacong/server/dto/request/CafeReviewRequest.java index a538ad4b..dbb893ca 100644 --- a/src/main/java/mocacong/server/dto/request/CafeReviewRequest.java +++ b/src/main/java/mocacong/server/dto/request/CafeReviewRequest.java @@ -3,6 +3,7 @@ import lombok.*; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @@ -10,13 +11,23 @@ @ToString public class CafeReviewRequest { + @NotNull(message = "3009:공백일 수 없습니다.") + private Integer myScore; + @NotBlank(message = "3009:공백일 수 없습니다.") - private int myScore; private String myStudyType; + private String myWifi; + private String myParking; + private String myToilet; + + @NotBlank(message = "3009:공백일 수 없습니다.") private String myPower; + private String mySound; + + @NotBlank(message = "3009:공백일 수 없습니다.") private String myDesk; } diff --git a/src/main/java/mocacong/server/dto/request/CafeReviewUpdateRequest.java b/src/main/java/mocacong/server/dto/request/CafeReviewUpdateRequest.java index a7cd2a7d..5f9b0595 100644 --- a/src/main/java/mocacong/server/dto/request/CafeReviewUpdateRequest.java +++ b/src/main/java/mocacong/server/dto/request/CafeReviewUpdateRequest.java @@ -3,6 +3,7 @@ import lombok.*; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @@ -10,13 +11,23 @@ @ToString public class CafeReviewUpdateRequest { + @NotNull(message = "3009:공백일 수 없습니다.") + private Integer myScore; + @NotBlank(message = "3009:공백일 수 없습니다.") - private int myScore; private String myStudyType; + private String myWifi; + private String myParking; + private String myToilet; + + @NotBlank(message = "3009:공백일 수 없습니다.") private String myPower; + private String mySound; + + @NotBlank(message = "3009:공백일 수 없습니다.") private String myDesk; } diff --git a/src/main/java/mocacong/server/dto/request/CommentReportRequest.java b/src/main/java/mocacong/server/dto/request/CommentReportRequest.java new file mode 100644 index 00000000..922ce16c --- /dev/null +++ b/src/main/java/mocacong/server/dto/request/CommentReportRequest.java @@ -0,0 +1,15 @@ +package mocacong.server.dto.request; + +import lombok.*; + +import javax.validation.constraints.NotBlank; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +@ToString +public class CommentReportRequest { + + @NotBlank(message = "3009:공백일 수 없습니다.") + private String myReportReason; +} diff --git a/src/main/java/mocacong/server/dto/request/MemberProfileUpdateRequest.java b/src/main/java/mocacong/server/dto/request/MemberProfileUpdateRequest.java index 1611f88d..e21f036f 100644 --- a/src/main/java/mocacong/server/dto/request/MemberProfileUpdateRequest.java +++ b/src/main/java/mocacong/server/dto/request/MemberProfileUpdateRequest.java @@ -14,7 +14,4 @@ public class MemberProfileUpdateRequest { @NotBlank(message = "1012:공백일 수 없습니다.") private String nickname; - - @NotBlank(message = "1012:공백일 수 없습니다.") - private String phone; } diff --git a/src/main/java/mocacong/server/dto/request/MemberSignUpRequest.java b/src/main/java/mocacong/server/dto/request/MemberSignUpRequest.java index 43cb072e..c517fdc3 100644 --- a/src/main/java/mocacong/server/dto/request/MemberSignUpRequest.java +++ b/src/main/java/mocacong/server/dto/request/MemberSignUpRequest.java @@ -19,7 +19,4 @@ public class MemberSignUpRequest { @NotBlank(message = "1012:공백일 수 없습니다.") private String nickname; - - @NotBlank(message = "1012:공백일 수 없습니다.") - private String phone; } diff --git a/src/main/java/mocacong/server/dto/response/CafeImageSaveResponse.java b/src/main/java/mocacong/server/dto/response/CafeImageSaveResponse.java new file mode 100644 index 00000000..0846f071 --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/CafeImageSaveResponse.java @@ -0,0 +1,12 @@ +package mocacong.server.dto.response; + +import lombok.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@ToString +public class CafeImageSaveResponse { + + private Long id; +} diff --git a/src/main/java/mocacong/server/dto/response/CafeImagesSaveResponse.java b/src/main/java/mocacong/server/dto/response/CafeImagesSaveResponse.java new file mode 100644 index 00000000..1220193b --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/CafeImagesSaveResponse.java @@ -0,0 +1,15 @@ +package mocacong.server.dto.response; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class CafeImagesSaveResponse { + private List cafeImagesIds; +} diff --git a/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java b/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java index da8dea2b..04927697 100644 --- a/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java +++ b/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java @@ -3,6 +3,7 @@ import lombok.*; import mocacong.server.domain.Cafe; import mocacong.server.domain.CafeDetail; +import mocacong.server.domain.Member; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -19,10 +20,12 @@ public class CafeReviewResponse { private String sound; private String desk; private int reviewsCount; + private int userReportCount; - public static CafeReviewResponse of(double score, Cafe cafe) { + public static CafeReviewResponse of(double score, Cafe cafe, Member member) { CafeDetail cafeDetail = cafe.getCafeDetail(); int reviewsCount = cafe.getReviews().size(); + int userReportCount = member.getReportCount(); return new CafeReviewResponse( score, @@ -33,7 +36,8 @@ public static CafeReviewResponse of(double score, Cafe cafe) { cafeDetail.getPowerValue(), cafeDetail.getSoundValue(), cafeDetail.getDeskValue(), - reviewsCount + reviewsCount, + userReportCount ); } } diff --git a/src/main/java/mocacong/server/dto/response/CafeReviewUpdateResponse.java b/src/main/java/mocacong/server/dto/response/CafeReviewUpdateResponse.java index 592e3899..d7db24b6 100644 --- a/src/main/java/mocacong/server/dto/response/CafeReviewUpdateResponse.java +++ b/src/main/java/mocacong/server/dto/response/CafeReviewUpdateResponse.java @@ -27,12 +27,12 @@ public static CafeReviewUpdateResponse of(double score, Cafe cafe) { return new CafeReviewUpdateResponse( score, cafeDetail.getStudyTypeValue(), - cafeDetail.getWifi().getValue(), - cafeDetail.getParking().getValue(), - cafeDetail.getToilet().getValue(), - cafeDetail.getPower().getValue(), - cafeDetail.getSound().getValue(), - cafeDetail.getDesk().getValue(), + cafeDetail.getWifiValue(), + cafeDetail.getParkingValue(), + cafeDetail.getToiletValue(), + cafeDetail.getPowerValue(), + cafeDetail.getSoundValue(), + cafeDetail.getDeskValue(), reviewsCount ); } diff --git a/src/main/java/mocacong/server/dto/response/CommentLikeSaveResponse.java b/src/main/java/mocacong/server/dto/response/CommentLikeSaveResponse.java new file mode 100644 index 00000000..b4b38f19 --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/CommentLikeSaveResponse.java @@ -0,0 +1,11 @@ +package mocacong.server.dto.response; + +import lombok.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@ToString +public class CommentLikeSaveResponse { + private Long commentLikeId; +} diff --git a/src/main/java/mocacong/server/dto/response/CommentReportResponse.java b/src/main/java/mocacong/server/dto/response/CommentReportResponse.java new file mode 100644 index 00000000..573bf4be --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/CommentReportResponse.java @@ -0,0 +1,16 @@ +package mocacong.server.dto.response; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class CommentReportResponse { + + private int commentReportCount; + + private int userReportCount; +} diff --git a/src/main/java/mocacong/server/dto/response/CommentSaveResponse.java b/src/main/java/mocacong/server/dto/response/CommentSaveResponse.java index 3d65bb7f..44c45e4d 100644 --- a/src/main/java/mocacong/server/dto/response/CommentSaveResponse.java +++ b/src/main/java/mocacong/server/dto/response/CommentSaveResponse.java @@ -9,4 +9,6 @@ public class CommentSaveResponse { private Long id; + + private int userReportCount; } diff --git a/src/main/java/mocacong/server/dto/response/CommentsResponse.java b/src/main/java/mocacong/server/dto/response/CommentsResponse.java index 1f8daebd..aeb81845 100644 --- a/src/main/java/mocacong/server/dto/response/CommentsResponse.java +++ b/src/main/java/mocacong/server/dto/response/CommentsResponse.java @@ -1,6 +1,8 @@ package mocacong.server.dto.response; import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.*; @Getter @@ -10,5 +12,12 @@ public class CommentsResponse { private Boolean isEnd; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Long count; private List comments; + + public CommentsResponse(Boolean isEnd, List comments) { + this.isEnd = isEnd; + this.comments = comments; + } } diff --git a/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java b/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java index 9460d462..b5a7caf9 100644 --- a/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java +++ b/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java @@ -9,4 +9,6 @@ public class FavoriteSaveResponse { private Long favoriteId; + + private int userReportCount; } diff --git a/src/main/java/mocacong/server/dto/response/GetUpdateProfileInfoResponse.java b/src/main/java/mocacong/server/dto/response/GetUpdateProfileInfoResponse.java index 5f000062..e7a27db9 100644 --- a/src/main/java/mocacong/server/dto/response/GetUpdateProfileInfoResponse.java +++ b/src/main/java/mocacong/server/dto/response/GetUpdateProfileInfoResponse.java @@ -9,5 +9,4 @@ public class GetUpdateProfileInfoResponse { private String email; private String nickname; - private String phone; } diff --git a/src/main/java/mocacong/server/dto/response/MemberGetResponse.java b/src/main/java/mocacong/server/dto/response/MemberGetResponse.java index 1e4fb05c..078cac38 100644 --- a/src/main/java/mocacong/server/dto/response/MemberGetResponse.java +++ b/src/main/java/mocacong/server/dto/response/MemberGetResponse.java @@ -11,5 +11,4 @@ public class MemberGetResponse { private Long id; private String email; private String nickname; - private String phone; } diff --git a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java index 47135fea..84aeabd4 100644 --- a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java @@ -10,5 +10,6 @@ public class MyCommentCafeResponse { private String mapId; private String name; + private String studyType; private String comment; } diff --git a/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java index 6a6d1472..c8f552f6 100644 --- a/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java @@ -10,5 +10,6 @@ public class MyFavoriteCafeResponse { private String mapId; private String name; + private String studyType; private double score; } diff --git a/src/main/java/mocacong/server/dto/response/MyPageResponse.java b/src/main/java/mocacong/server/dto/response/MyPageResponse.java index 9903cecc..fb8431f3 100644 --- a/src/main/java/mocacong/server/dto/response/MyPageResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyPageResponse.java @@ -10,6 +10,5 @@ public class MyPageResponse { private String email; private String nickname; - private String phone; private String imgUrl; } diff --git a/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java index 18265ff4..e49b21d2 100644 --- a/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java @@ -1,14 +1,22 @@ package mocacong.server.dto.response; import lombok.*; +import mocacong.server.domain.cafedetail.StudyType; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor @ToString public class MyReviewCafeResponse { private String mapId; private String name; + private String myStudyType; private int myScore; + + public MyReviewCafeResponse(String mapId, String name, StudyType myStudyType, int myScore) { + this.mapId = mapId; + this.name = name; + this.myStudyType = myStudyType.getValue(); + this.myScore = myScore; + } } diff --git a/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java b/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java index e4629161..2e515483 100644 --- a/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java +++ b/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java @@ -12,4 +12,5 @@ public class OAuthTokenResponse { private String email; private Boolean isRegistered; private String platformId; + private int userReportCount; } diff --git a/src/main/java/mocacong/server/dto/response/TokenResponse.java b/src/main/java/mocacong/server/dto/response/TokenResponse.java index b67af11d..fe22164a 100644 --- a/src/main/java/mocacong/server/dto/response/TokenResponse.java +++ b/src/main/java/mocacong/server/dto/response/TokenResponse.java @@ -12,7 +12,9 @@ public class TokenResponse { private String token; - public static TokenResponse from(final String token) { - return new TokenResponse(token); + private int userReportCount; + + public static TokenResponse from(final String token, int userReportCount) { + return new TokenResponse(token, userReportCount); } } diff --git a/src/main/java/mocacong/server/exception/badrequest/AlreadyExistsCommentLike.java b/src/main/java/mocacong/server/exception/badrequest/AlreadyExistsCommentLike.java new file mode 100644 index 00000000..687d8402 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/AlreadyExistsCommentLike.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class AlreadyExistsCommentLike extends BadRequestException { + + public AlreadyExistsCommentLike() { + super("이미 좋아요한 댓글입니다.", 6001); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/BlankTokenException.java b/src/main/java/mocacong/server/exception/badrequest/BlankTokenException.java new file mode 100644 index 00000000..101b7f5a --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/BlankTokenException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class BlankTokenException extends BadRequestException { + + public BlankTokenException() { + super("토큰은 공백일 수 없습니다.", 1018); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/DuplicateReportCommentException.java b/src/main/java/mocacong/server/exception/badrequest/DuplicateReportCommentException.java new file mode 100644 index 00000000..d464e3af --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/DuplicateReportCommentException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class DuplicateReportCommentException extends BadRequestException { + + public DuplicateReportCommentException() { + super("이미 신고한 코멘트입니다.", 7002); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/ExceedCafeImagesCountsException.java b/src/main/java/mocacong/server/exception/badrequest/ExceedCafeImagesCountsException.java deleted file mode 100644 index 0282e973..00000000 --- a/src/main/java/mocacong/server/exception/badrequest/ExceedCafeImagesCountsException.java +++ /dev/null @@ -1,11 +0,0 @@ -package mocacong.server.exception.badrequest; - -import lombok.Getter; - -@Getter -public class ExceedCafeImagesCountsException extends BadRequestException { - - public ExceedCafeImagesCountsException() { - super("카페 이미지는 한 번에 최대 3개까지만 업로드 가능합니다.", 2008); - } -} diff --git a/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java b/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java new file mode 100644 index 00000000..b8423939 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java @@ -0,0 +1,7 @@ +package mocacong.server.exception.badrequest; + +public class ExceedCageImagesTotalCountsException extends BadRequestException{ + public ExceedCageImagesTotalCountsException() { + super("카페 이미지는 3개 이상 등록하실 수 없습니다.", 2008); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidCommentLikeException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidCommentLikeException.java new file mode 100644 index 00000000..2c6996ec --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/InvalidCommentLikeException.java @@ -0,0 +1,7 @@ +package mocacong.server.exception.badrequest; + +public class InvalidCommentLikeException extends BadRequestException { + public InvalidCommentLikeException() { + super("자신의 댓글에 좋아요할 수 없습니다.", 6002); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidCommentReportException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidCommentReportException.java new file mode 100644 index 00000000..9bba10f5 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/InvalidCommentReportException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class InvalidCommentReportException extends BadRequestException { + + public InvalidCommentReportException() { + super("자신이 작성한 코멘트는 신고할 수 없습니다.", 7001); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidFileEmptyException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidFileEmptyException.java new file mode 100644 index 00000000..7857c257 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/InvalidFileEmptyException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class InvalidFileEmptyException extends BadRequestException { + + public InvalidFileEmptyException() { + super("빈 파일은 업로드할 수 없습니다.", 9007); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidPhoneException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidPhoneException.java deleted file mode 100644 index 12f33059..00000000 --- a/src/main/java/mocacong/server/exception/badrequest/InvalidPhoneException.java +++ /dev/null @@ -1,8 +0,0 @@ -package mocacong.server.exception.badrequest; - -public class InvalidPhoneException extends BadRequestException { - - public InvalidPhoneException() { - super("전화번호 형식이 올바르지 않습니다.", 1011); - } -} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidReportReasonException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidReportReasonException.java new file mode 100644 index 00000000..115f699b --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/InvalidReportReasonException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class InvalidReportReasonException extends BadRequestException { + + public InvalidReportReasonException() { + super("신고 사유 정보가 올바르지 않습니다.", 7000); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidStatusException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidStatusException.java new file mode 100644 index 00000000..0c5e1d35 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/InvalidStatusException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class InvalidStatusException extends BadRequestException { + + public InvalidStatusException() { + super("회원 상태 정보가 올바르지 않습니다.", 1019); + } +} diff --git a/src/main/java/mocacong/server/exception/notfound/NotFoundCommentLikeException.java b/src/main/java/mocacong/server/exception/notfound/NotFoundCommentLikeException.java new file mode 100644 index 00000000..97afa8ae --- /dev/null +++ b/src/main/java/mocacong/server/exception/notfound/NotFoundCommentLikeException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.notfound; + +public class NotFoundCommentLikeException extends NotFoundException { + + public NotFoundCommentLikeException() { + super("존재하지 않는 댓글 좋아요입니다.", 6000); + } +} diff --git a/src/main/java/mocacong/server/exception/unauthorized/InactiveMemberException.java b/src/main/java/mocacong/server/exception/unauthorized/InactiveMemberException.java new file mode 100644 index 00000000..6911a946 --- /dev/null +++ b/src/main/java/mocacong/server/exception/unauthorized/InactiveMemberException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.unauthorized; + +public class InactiveMemberException extends UnauthorizedException { + + public InactiveMemberException() { + super("서비스에 접근할 수 없는 회원입니다.", 1020); + } +} diff --git a/src/main/java/mocacong/server/repository/CafeImageRepository.java b/src/main/java/mocacong/server/repository/CafeImageRepository.java index 4edbe334..82359657 100644 --- a/src/main/java/mocacong/server/repository/CafeImageRepository.java +++ b/src/main/java/mocacong/server/repository/CafeImageRepository.java @@ -1,15 +1,21 @@ package mocacong.server.repository; -import java.util.List; import mocacong.server.domain.CafeImage; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; public interface CafeImageRepository extends JpaRepository { - Slice findAllByCafeIdAndIsUsedTrue(Long cafeId, Pageable pageRequest); - List findAllByCafeIdAndIsUsedTrue(Long cafeId); + @Query("SELECT ci FROM CafeImage ci WHERE ci.cafe.id = :cafeId AND ci.isUsed = true " + + "ORDER BY CASE WHEN ci.member.id = :memberId THEN 0 ELSE 1 END, " + + "CASE WHEN ci.member.id = :memberId THEN ci.id END, ci.id") + Slice findAllByCafeIdAndIsUsedOrderByCafeImageId(Long cafeId, Long memberId, Pageable pageable); + + List findAllByMemberId(Long memberId); List findAllByIsUsedFalse(); } diff --git a/src/main/java/mocacong/server/repository/CafeRepository.java b/src/main/java/mocacong/server/repository/CafeRepository.java index 64f9950d..f297f4e0 100644 --- a/src/main/java/mocacong/server/repository/CafeRepository.java +++ b/src/main/java/mocacong/server/repository/CafeRepository.java @@ -2,6 +2,7 @@ import mocacong.server.domain.Cafe; import mocacong.server.domain.cafedetail.StudyType; +import mocacong.server.dto.response.MyReviewCafeResponse; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -18,18 +19,19 @@ public interface CafeRepository extends JpaRepository { @Query("select c.mapId from Favorite f " + "join f.cafe c join f.member m " + - "where c.mapId in :mapIds and m.email = :email") - List findNearCafeMapIdsByMyFavoriteCafes(String email, List mapIds); + "where c.mapId in :mapIds and m.id = :id") + List findNearCafeMapIdsByMyFavoriteCafes(Long id, List mapIds); @Query("select c from Favorite f " + "join f.cafe c " + "join f.member m " + - "where m.email = :email") - Slice findByMyFavoriteCafes(String email, Pageable pageRequest); + "where m.id = :id") + Slice findByMyFavoriteCafes(Long id, Pageable pageRequest); - @Query("select c from Review r " + + @Query("select new mocacong.server.dto.response.MyReviewCafeResponse(c.mapId,c.name,r.cafeDetail.studyType,s.score) from Review r " + "join r.cafe c " + "join r.member m " + - "where m.email = :email") - Slice findByMyReviewCafes(String email, Pageable pageRequest); + "join c.score s "+ + "where m.id = :id and s.member.id = :id") + Slice findMyReviewCafesById(Long id, Pageable pageRequest); } diff --git a/src/main/java/mocacong/server/repository/CommentLikeRepository.java b/src/main/java/mocacong/server/repository/CommentLikeRepository.java new file mode 100644 index 00000000..234025ef --- /dev/null +++ b/src/main/java/mocacong/server/repository/CommentLikeRepository.java @@ -0,0 +1,26 @@ +package mocacong.server.repository; + +import mocacong.server.domain.CommentLike; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; + +public interface CommentLikeRepository extends JpaRepository { + @Query("select cl.id " + + "from CommentLike cl " + + "join cl.comment c " + + "join cl.member m " + + "where c.id = :commentId and m.id = :memberId") + Optional findCommentLikeIdByCommentIdAndMemberId(Long memberId, Long commentId); + + List findAllByMemberId(Long memberId); + + List findAllByCommentId(Long commentId); + + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query("delete from CommentLike cl where cl.comment.id IS null") + void deleteAllByCommentIdIsNull(); +} diff --git a/src/main/java/mocacong/server/repository/CommentRepository.java b/src/main/java/mocacong/server/repository/CommentRepository.java index 3e033531..defc1f73 100644 --- a/src/main/java/mocacong/server/repository/CommentRepository.java +++ b/src/main/java/mocacong/server/repository/CommentRepository.java @@ -15,5 +15,7 @@ public interface CommentRepository extends JpaRepository { Slice findAllByCafeId(Long cafeId, Pageable pageRequest); + Long countAllByCafeId(Long cafeId); + Slice findAllByCafeIdAndMemberId(Long cafeId, Long memberId, Pageable pageRequest); } diff --git a/src/main/java/mocacong/server/repository/MemberRepository.java b/src/main/java/mocacong/server/repository/MemberRepository.java index de77b97b..994aebe0 100644 --- a/src/main/java/mocacong/server/repository/MemberRepository.java +++ b/src/main/java/mocacong/server/repository/MemberRepository.java @@ -1,18 +1,32 @@ package mocacong.server.repository; -import java.util.Optional; +import feign.Param; import mocacong.server.domain.Member; import mocacong.server.domain.Platform; +import mocacong.server.domain.Status; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -public interface MemberRepository extends JpaRepository { - Optional findByEmail(String email); +import java.util.Date; +import java.util.Optional; - Optional findByNickname(String nickname); +public interface MemberRepository extends JpaRepository { Optional findByPlatformAndPlatformId(Platform platform, String platformId); + Optional findByEmailAndPlatform(String email, Platform platform); + + Boolean existsByEmailAndPlatform(String email, Platform platform); + + Boolean existsByNickname(String nickname); + @Query("select m.id from Member m where m.platform = :platform and m.platformId = :platformId") Optional findIdByPlatformAndPlatformId(Platform platform, String platformId); + + @Modifying + @Query("UPDATE Member m SET m.status = :newStatus WHERE m.status = :oldStatus AND " + + "m.modifiedTime <= :thresholdDateTime") + void bulkUpdateStatus(@Param("newStatus") Status newStatus, @Param("oldStatus") Status oldStatus, + @Param("thresholdDateTime") Date thresholdDateTime); } diff --git a/src/main/java/mocacong/server/repository/ReportRepository.java b/src/main/java/mocacong/server/repository/ReportRepository.java new file mode 100644 index 00000000..31b76a1a --- /dev/null +++ b/src/main/java/mocacong/server/repository/ReportRepository.java @@ -0,0 +1,12 @@ +package mocacong.server.repository; + +import mocacong.server.domain.Member; +import mocacong.server.domain.Report; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ReportRepository extends JpaRepository { + + List findAllByReporter(Member reporter); +} diff --git a/src/main/java/mocacong/server/security/auth/AuthenticationPrincipalArgumentResolver.java b/src/main/java/mocacong/server/security/auth/AuthenticationPrincipalArgumentResolver.java index 651a12ea..60f2dc57 100644 --- a/src/main/java/mocacong/server/security/auth/AuthenticationPrincipalArgumentResolver.java +++ b/src/main/java/mocacong/server/security/auth/AuthenticationPrincipalArgumentResolver.java @@ -1,6 +1,5 @@ package mocacong.server.security.auth; -import javax.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -8,6 +7,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; +import javax.servlet.http.HttpServletRequest; + @Component public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver { @@ -19,7 +20,7 @@ public AuthenticationPrincipalArgumentResolver(final JwtTokenProvider jwtTokenPr @Override public boolean supportsParameter(MethodParameter parameter) { - return parameter.hasParameterAnnotation(LoginUserEmail.class); + return parameter.hasParameterAnnotation(LoginUserId.class); } @Override @@ -27,6 +28,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); String token = AuthorizationExtractor.extractAccessToken(request); - return jwtTokenProvider.getPayload(token); + String payload = jwtTokenProvider.getPayload(token); + return Long.parseLong(payload); } } diff --git a/src/main/java/mocacong/server/security/auth/AuthorizationExtractor.java b/src/main/java/mocacong/server/security/auth/AuthorizationExtractor.java index 36736827..4fd214b7 100644 --- a/src/main/java/mocacong/server/security/auth/AuthorizationExtractor.java +++ b/src/main/java/mocacong/server/security/auth/AuthorizationExtractor.java @@ -1,6 +1,7 @@ package mocacong.server.security.auth; import lombok.NoArgsConstructor; +import mocacong.server.exception.badrequest.BlankTokenException; import mocacong.server.exception.unauthorized.InvalidBearerException; import javax.servlet.http.HttpServletRequest; @@ -11,16 +12,24 @@ public class AuthorizationExtractor { private static final String AUTHENTICATION_TYPE = "Bearer"; private static final String AUTHORIZATION_HEADER_KEY = "Authorization"; - private static final int TOKEN_INDEX = 1; + private static final int START_TOKEN_INDEX = 6; public static String extractAccessToken(HttpServletRequest request) { Enumeration headers = request.getHeaders(AUTHORIZATION_HEADER_KEY); while (headers.hasMoreElements()) { String value = headers.nextElement(); if (value.toLowerCase().startsWith(AUTHENTICATION_TYPE.toLowerCase())) { - return value.split(" ")[TOKEN_INDEX]; + String extractToken = value.trim().substring(START_TOKEN_INDEX); + validateExtractToken(extractToken); + return extractToken; } } throw new InvalidBearerException(); } + + private static void validateExtractToken(String extractToken) { + if (extractToken.isBlank()) { + throw new BlankTokenException(); + } + } } diff --git a/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java b/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java index 483d6b8a..2ceb331f 100644 --- a/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java +++ b/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java @@ -21,12 +21,12 @@ public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") String secret this.jwtParser = Jwts.parser().setSigningKey(secretKey); } - public String createToken(String payload) { + public String createToken(Long memberId) { Date now = new Date(); Date validity = new Date(now.getTime() + validityInMilliseconds); return Jwts.builder() - .setSubject(payload) + .setSubject(String.valueOf(memberId)) .setIssuedAt(now) .setExpiration(validity) .signWith(SignatureAlgorithm.HS256, secretKey) diff --git a/src/main/java/mocacong/server/security/auth/LoginUserEmail.java b/src/main/java/mocacong/server/security/auth/LoginUserId.java similarity index 90% rename from src/main/java/mocacong/server/security/auth/LoginUserEmail.java rename to src/main/java/mocacong/server/security/auth/LoginUserId.java index f9a6977a..974e6210 100644 --- a/src/main/java/mocacong/server/security/auth/LoginUserEmail.java +++ b/src/main/java/mocacong/server/security/auth/LoginUserId.java @@ -9,6 +9,6 @@ @Hidden @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) -public @interface LoginUserEmail { +public @interface LoginUserId { } diff --git a/src/main/java/mocacong/server/security/auth/apple/AppleOAuthUserProvider.java b/src/main/java/mocacong/server/security/auth/apple/AppleOAuthUserProvider.java index 631c249b..28b95460 100644 --- a/src/main/java/mocacong/server/security/auth/apple/AppleOAuthUserProvider.java +++ b/src/main/java/mocacong/server/security/auth/apple/AppleOAuthUserProvider.java @@ -1,13 +1,14 @@ package mocacong.server.security.auth.apple; import io.jsonwebtoken.Claims; -import java.security.PublicKey; -import java.util.Map; import lombok.RequiredArgsConstructor; import mocacong.server.exception.unauthorized.InvalidTokenException; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import org.springframework.stereotype.Component; +import java.security.PublicKey; +import java.util.Map; + @Component @RequiredArgsConstructor public class AppleOAuthUserProvider { diff --git a/src/main/java/mocacong/server/service/AuthService.java b/src/main/java/mocacong/server/service/AuthService.java index e8df5418..11d9c791 100644 --- a/src/main/java/mocacong/server/service/AuthService.java +++ b/src/main/java/mocacong/server/service/AuthService.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import mocacong.server.domain.Member; import mocacong.server.domain.Platform; +import mocacong.server.domain.Status; import mocacong.server.dto.request.AppleLoginRequest; import mocacong.server.dto.request.AuthLoginRequest; import mocacong.server.dto.request.KakaoLoginRequest; @@ -10,6 +11,7 @@ import mocacong.server.dto.response.TokenResponse; import mocacong.server.exception.badrequest.PasswordMismatchException; import mocacong.server.exception.notfound.NotFoundMemberException; +import mocacong.server.exception.unauthorized.InactiveMemberException; import mocacong.server.repository.MemberRepository; import mocacong.server.security.auth.JwtTokenProvider; import mocacong.server.security.auth.OAuthPlatformMemberResponse; @@ -29,12 +31,15 @@ public class AuthService { private final KakaoOAuthUserProvider kakaoOAuthUserProvider; public TokenResponse login(AuthLoginRequest request) { - Member findMember = memberRepository.findByEmail(request.getEmail()) + Member findMember = memberRepository.findByEmailAndPlatform(request.getEmail(), Platform.MOCACONG) .orElseThrow(NotFoundMemberException::new); validatePassword(findMember, request.getPassword()); + validateStatus(findMember); String token = issueToken(findMember); - return TokenResponse.from(token); + int userReportCount = findMember.getReportCount(); + + return TokenResponse.from(token, userReportCount); } public OAuthTokenResponse appleOAuthLogin(AppleLoginRequest request) { @@ -62,25 +67,28 @@ private OAuthTokenResponse generateOAuthTokenResponse(Platform platform, String .map(memberId -> { Member findMember = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); + validateStatus(findMember); + int userReportCount = findMember.getReportCount(); String token = issueToken(findMember); // OAuth 로그인은 성공했지만 회원가입에 실패한 경우 if (!findMember.isRegisteredOAuthMember()) { - return new OAuthTokenResponse(token, findMember.getEmail(), false, platformId); + return new OAuthTokenResponse(token, findMember.getEmail(), false, platformId, + userReportCount); } - return new OAuthTokenResponse(token, findMember.getEmail(), true, platformId); + return new OAuthTokenResponse(token, findMember.getEmail(), true, platformId, + userReportCount); }) .orElseGet(() -> { - Member oauthMember = new Member(email, platform, platformId); + Member oauthMember = new Member(email, platform, platformId, Status.ACTIVE); Member savedMember = memberRepository.save(oauthMember); String token = issueToken(savedMember); - return new OAuthTokenResponse(token, email, false, platformId); + return new OAuthTokenResponse(token, email, false, platformId, + savedMember.getReportCount()); }); } private String issueToken(final Member findMember) { - String email = findMember.getEmail(); - - return jwtTokenProvider.createToken(email); + return jwtTokenProvider.createToken(findMember.getId()); } private void validatePassword(final Member findMember, final String password) { @@ -88,4 +96,10 @@ private void validatePassword(final Member findMember, final String password) { throw new PasswordMismatchException(); } } + + private void validateStatus(final Member findMember) { + if (findMember.getStatus() != Status.ACTIVE) { + throw new InactiveMemberException(); + } + } } diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index 72945c91..0307474c 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -1,9 +1,5 @@ package mocacong.server.service; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import javax.persistence.EntityManager; import lombok.RequiredArgsConstructor; import mocacong.server.domain.*; import mocacong.server.domain.cafedetail.*; @@ -11,14 +7,14 @@ import mocacong.server.dto.response.*; import mocacong.server.exception.badrequest.AlreadyExistsCafeReview; import mocacong.server.exception.badrequest.DuplicateCafeException; -import mocacong.server.exception.badrequest.ExceedCafeImagesCountsException; +import mocacong.server.exception.badrequest.ExceedCageImagesTotalCountsException; import mocacong.server.exception.notfound.NotFoundCafeException; import mocacong.server.exception.notfound.NotFoundCafeImageException; import mocacong.server.exception.notfound.NotFoundMemberException; import mocacong.server.exception.notfound.NotFoundReviewException; import mocacong.server.repository.*; +import mocacong.server.service.event.DeleteMemberEvent; import mocacong.server.service.event.DeleteNotUsedImagesEvent; -import mocacong.server.service.event.MemberEvent; import mocacong.server.support.AwsS3Uploader; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -32,13 +28,18 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import javax.persistence.EntityManager; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + @Service @RequiredArgsConstructor public class CafeService { - public static final int CAFE_IMAGES_PER_REQUEST_LIMIT_COUNTS = 3; + private static final int CAFE_IMAGES_PER_MEMBER_LIMIT_COUNTS = 3; private static final int CAFE_SHOW_PAGE_COMMENTS_LIMIT_COUNTS = 3; - private static final int CAFE_SHOW_PAGE_IMAGE_LIMIT_COUNTS = 5; private final CafeRepository cafeRepository; private final MemberRepository memberRepository; private final ScoreRepository scoreRepository; @@ -62,11 +63,11 @@ public void save(CafeRegisterRequest request) { } @Transactional(readOnly = true) - public FindCafeResponse findCafeByMapId(String email, String mapId) { + public FindCafeResponse findCafeByMapId(Long memberId, String mapId) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); CafeDetail cafeDetail = cafe.getCafeDetail(); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Score scoreByLoginUser = scoreRepository.findByCafeIdAndMemberId(cafe.getId(), member.getId()) .orElse(null); @@ -95,11 +96,11 @@ public FindCafeResponse findCafeByMapId(String email, String mapId) { @Cacheable(key = "#mapId", value = "cafePreviewCache", cacheManager = "cafeCacheManager") @Transactional(readOnly = true) - public PreviewCafeResponse previewCafeByMapId(String email, String mapId) { + public PreviewCafeResponse previewCafeByMapId(Long memberId, String mapId) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); CafeDetail cafeDetail = cafe.getCafeDetail(); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Long favoriteId = favoriteRepository.findFavoriteIdByCafeIdAndMemberId(cafe.getId(), member.getId()) .orElse(null); @@ -117,19 +118,24 @@ private List findCommentResponses(Cafe cafe, Member member) { .limit(CAFE_SHOW_PAGE_COMMENTS_LIMIT_COUNTS) .map(comment -> { if (comment.isWrittenByMember(member)) { - return new CommentResponse(comment.getId(), member.getImgUrl(), member.getNickname(), comment.getContent(), true); + return new CommentResponse(comment.getId(), member.getImgUrl(), member.getNickname(), + comment.getContent(), true); } else { - return new CommentResponse(comment.getId(), comment.getWriterImgUrl(), comment.getWriterNickname(), comment.getContent(), false); + return new CommentResponse(comment.getId(), comment.getWriterImgUrl(), + comment.getWriterNickname(), comment.getContent(), false); } }) .collect(Collectors.toList()); } private List findCafeImageResponses(Cafe cafe, Member member) { - List cafeImages = cafeImageRepository.findAllByCafeIdAndIsUsedTrue(cafe.getId()); + Pageable pageable = PageRequest.of(0, 5); + Slice cafeImages = cafeImageRepository. + findAllByCafeIdAndIsUsedOrderByCafeImageId(cafe.getId(), member.getId(), pageable); + return cafeImages + .getContent() .stream() - .limit(CAFE_SHOW_PAGE_IMAGE_LIMIT_COUNTS) .map(cafeImage -> { Boolean isMe = cafeImage.isOwned(member); return new CafeImageResponse(cafeImage.getId(), cafeImage.getImgUrl(), isMe); @@ -138,35 +144,30 @@ private List findCafeImageResponses(Cafe cafe, Member member) } @Transactional(readOnly = true) - public MyFavoriteCafesResponse findMyFavoriteCafes(String email, Integer page, int count) { - Slice myFavoriteCafes = cafeRepository.findByMyFavoriteCafes(email, PageRequest.of(page, count)); + public MyFavoriteCafesResponse findMyFavoriteCafes(Long memberId, Integer page, int count) { + Slice myFavoriteCafes = cafeRepository.findByMyFavoriteCafes(memberId, PageRequest.of(page, count)); List responses = myFavoriteCafes .getContent() .stream() - .map(cafe -> new MyFavoriteCafeResponse(cafe.getMapId(), cafe.getName(), cafe.findAverageScore())) + .map(cafe -> new MyFavoriteCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), cafe.findAverageScore())) .collect(Collectors.toList()); return new MyFavoriteCafesResponse(myFavoriteCafes.isLast(), responses); } @Transactional(readOnly = true) - public MyReviewCafesResponse findMyReviewCafes(String email, Integer page, int count) { - Member member = memberRepository.findByEmail(email) + public MyReviewCafesResponse findMyReviewCafes(Long memberId, Integer page, int count) { + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); - Slice myReviewCafes = cafeRepository.findByMyReviewCafes(email, PageRequest.of(page, count)); - List responses = myReviewCafes - .getContent() - .stream() - .map(cafe -> { - int score = scoreRepository.findScoreByCafeIdAndMemberId(cafe.getId(), member.getId()); - return new MyReviewCafeResponse(cafe.getMapId(), cafe.getName(), score); - }) - .collect(Collectors.toList()); + + Slice myReviewCafes = + cafeRepository.findMyReviewCafesById(member.getId(), PageRequest.of(page, count)); + List responses = myReviewCafes.getContent(); return new MyReviewCafesResponse(myReviewCafes.isLast(), responses); } @Transactional(readOnly = true) - public MyCommentCafesResponse findMyCommentCafes(String email, int page, int count) { - Member member = memberRepository.findByEmail(email) + public MyCommentCafesResponse findMyCommentCafes(Long memberId, int page, int count) { + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Slice comments = commentRepository.findByMemberId(member.getId(), PageRequest.of(page, count)); @@ -174,6 +175,7 @@ public MyCommentCafesResponse findMyCommentCafes(String email, int page, int cou .map(comment -> new MyCommentCafeResponse( comment.getCafe().getMapId(), comment.getCafe().getName(), + comment.getCafe().getStudyType(), comment.getContent() )) .collect(Collectors.toList()); @@ -182,10 +184,10 @@ public MyCommentCafesResponse findMyCommentCafes(String email, int page, int cou @CacheEvict(key = "#mapId", value = "cafePreviewCache") @Transactional - public CafeReviewResponse saveCafeReview(String email, String mapId, CafeReviewRequest request) { + public CafeReviewResponse saveCafeReview(Long memberId, String mapId, CafeReviewRequest request) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); checkAlreadySaveCafeReview(cafe, member); @@ -193,7 +195,7 @@ public CafeReviewResponse saveCafeReview(String email, String mapId, CafeReviewR em.flush(); cafe.updateCafeDetails(); - return CafeReviewResponse.of(cafe.findAverageScore(), cafe); + return CafeReviewResponse.of(cafe.findAverageScore(), cafe, member); } private void checkAlreadySaveCafeReview(Cafe cafe, Member member) { @@ -205,7 +207,6 @@ private void checkAlreadySaveCafeReview(Cafe cafe, Member member) { private void saveCafeDetails(CafeReviewRequest request, Cafe cafe, Member member) { Score score = new Score(request.getMyScore(), member, cafe); - scoreRepository.save(score); CafeDetail cafeDetail = new CafeDetail( StudyType.from(request.getMyStudyType()), Wifi.from(request.getMyWifi()), @@ -217,6 +218,7 @@ private void saveCafeDetails(CafeReviewRequest request, Cafe cafe, Member member ); Review review = new Review(member, cafe, cafeDetail); try { + scoreRepository.save(score); reviewRepository.save(review); } catch (DataIntegrityViolationException e) { throw new AlreadyExistsCafeReview(); @@ -224,10 +226,10 @@ private void saveCafeDetails(CafeReviewRequest request, Cafe cafe, Member member } @Transactional(readOnly = true) - public CafeMyReviewResponse findMyCafeReview(String email, String mapId) { + public CafeMyReviewResponse findMyCafeReview(Long memberId, String mapId) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Review review = reviewRepository.findByCafeIdAndMemberId(cafe.getId(), member.getId()) @@ -237,11 +239,12 @@ public CafeMyReviewResponse findMyCafeReview(String email, String mapId) { return CafeMyReviewResponse.of(score, review); } + @CacheEvict(key = "#mapId", value = "cafePreviewCache") @Transactional - public CafeReviewUpdateResponse updateCafeReview(String email, String mapId, CafeReviewUpdateRequest request) { + public CafeReviewUpdateResponse updateCafeReview(Long memberId, String mapId, CafeReviewUpdateRequest request) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); updateCafeReviewDetails(request, cafe, member); @@ -270,7 +273,7 @@ private void updateCafeReviewDetails(CafeReviewUpdateRequest request, Cafe cafe, } @EventListener - public void updateReviewWhenMemberDelete(MemberEvent event) { + public void updateReviewWhenMemberDelete(DeleteMemberEvent event) { Long memberId = event.getMember() .getId(); reviewRepository.findAllByMemberId(memberId) @@ -282,6 +285,7 @@ public void updateReviewWhenMemberDelete(MemberEvent event) { public CafeFilterStudyTypeResponse filterCafesByStudyType(String studyTypeValue, CafeFilterStudyTypeRequest request) { List cafes = cafeRepository.findByStudyTypeValue(StudyType.from(studyTypeValue)); + cafes.addAll(cafeRepository.findByStudyTypeValue(StudyType.BOTH)); Set filteredCafeMapIds = cafes.stream() .map(Cafe::getMapId) .collect(Collectors.toSet()); @@ -293,42 +297,49 @@ public CafeFilterStudyTypeResponse filterCafesByStudyType(String studyTypeValue, return new CafeFilterStudyTypeResponse(filteredIds); } - public CafeFilterFavoritesResponse filterCafesByFavorites(String email, CafeFilterFavoritesRequest request) { + public CafeFilterFavoritesResponse filterCafesByFavorites(Long memberId, CafeFilterFavoritesRequest request) { List mapIds = request.getMapIds(); - List filteredIds = cafeRepository.findNearCafeMapIdsByMyFavoriteCafes(email, mapIds); + List filteredIds = cafeRepository.findNearCafeMapIdsByMyFavoriteCafes(memberId, mapIds); return new CafeFilterFavoritesResponse(filteredIds); } @Transactional - public void saveCafeImage(String email, String mapId, List cafeImages) { - validateCafeImagesCounts(cafeImages); + public CafeImagesSaveResponse saveCafeImage(Long memberId, String mapId, List cafeImages) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); + validateOwnedCafeImagesCounts(cafe, member, cafeImages); + List cafeImageSaveResponses = new ArrayList<>(); for (MultipartFile cafeImage : cafeImages) { String imgUrl = awsS3Uploader.uploadImage(cafeImage); CafeImage uploadedCafeImage = new CafeImage(imgUrl, true, cafe, member); - cafeImageRepository.save(uploadedCafeImage); + cafeImageSaveResponses.add(new CafeImageSaveResponse(cafeImageRepository.save(uploadedCafeImage).getId())); } + return new CafeImagesSaveResponse(cafeImageSaveResponses); } - private void validateCafeImagesCounts(List cafeImages) { - if (cafeImages.size() > CAFE_IMAGES_PER_REQUEST_LIMIT_COUNTS) { - throw new ExceedCafeImagesCountsException(); + private void validateOwnedCafeImagesCounts(Cafe cafe, Member member, List requestCafeImages) { + List currentOwnedCafeImages = cafe.getCafeImages() + .stream() + .filter(cafeImage -> cafeImage.isOwned(member)) + .collect(Collectors.toList()); + if (currentOwnedCafeImages.size() + requestCafeImages.size() > CAFE_IMAGES_PER_MEMBER_LIMIT_COUNTS) { + throw new ExceedCageImagesTotalCountsException(); } } @Transactional(readOnly = true) - public CafeImagesResponse findCafeImages(String email, String mapId, Integer page, int count) { + public CafeImagesResponse findCafeImages(Long memberId, String mapId, Integer page, int count) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Pageable pageable = PageRequest.of(page, count); - Slice cafeImages = cafeImageRepository.findAllByCafeIdAndIsUsedTrue(cafe.getId(), pageable); + Slice cafeImages = cafeImageRepository. + findAllByCafeIdAndIsUsedOrderByCafeImageId(cafe.getId(), member.getId(), pageable); List responses = cafeImages .getContent() @@ -343,18 +354,27 @@ public CafeImagesResponse findCafeImages(String email, String mapId, Integer pag } @Transactional - public void updateCafeImage(String email, String mapId, Long cafeImageId, MultipartFile cafeImg) { + public void updateCafeImage(Long memberId, String mapId, Long cafeImageId, MultipartFile cafeImg) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); - CafeImage notUsedImage = cafeImageRepository.findById(cafeImageId) + CafeImage cafeImage = cafeImageRepository.findById(cafeImageId) .orElseThrow(NotFoundCafeImageException::new); - notUsedImage.setIsUsed(false); + String beforeImgUrl = cafeImage.getImgUrl(); String newImgUrl = awsS3Uploader.uploadImage(cafeImg); - CafeImage cafeImage = new CafeImage(newImgUrl, true, cafe, member); - cafeImageRepository.save(cafeImage); + cafeImage.updateImgUrl(newImgUrl); + + CafeImage notUseImage = new CafeImage(beforeImgUrl, false, cafe, member); + cafeImageRepository.save(notUseImage); + } + + @EventListener + public void updateCafeImagesWhenMemberDelete(DeleteMemberEvent event) { + Member member = event.getMember(); + cafeImageRepository.findAllByMemberId(member.getId()) + .forEach(CafeImage::removeMember); } @Transactional diff --git a/src/main/java/mocacong/server/service/CommentLikeService.java b/src/main/java/mocacong/server/service/CommentLikeService.java new file mode 100644 index 00000000..926af9eb --- /dev/null +++ b/src/main/java/mocacong/server/service/CommentLikeService.java @@ -0,0 +1,96 @@ +package mocacong.server.service; + +import lombok.RequiredArgsConstructor; +import mocacong.server.domain.Comment; +import mocacong.server.domain.CommentLike; +import mocacong.server.domain.Member; +import mocacong.server.dto.response.CommentLikeSaveResponse; +import mocacong.server.exception.badrequest.AlreadyExistsCommentLike; +import mocacong.server.exception.badrequest.InvalidCommentLikeException; +import mocacong.server.exception.notfound.NotFoundCommentException; +import mocacong.server.exception.notfound.NotFoundCommentLikeException; +import mocacong.server.exception.notfound.NotFoundMemberException; +import mocacong.server.repository.CommentLikeRepository; +import mocacong.server.repository.CommentRepository; +import mocacong.server.repository.MemberRepository; +import mocacong.server.service.event.DeleteCommentEvent; +import mocacong.server.service.event.DeleteMemberEvent; +import org.springframework.context.event.EventListener; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; + +@Service +@RequiredArgsConstructor +public class CommentLikeService { + + private final CommentLikeRepository commentLikeRepository; + private final MemberRepository memberRepository; + private final CommentRepository commentRepository; + + @Transactional + public CommentLikeSaveResponse save(Long memberId, Long commentId) { + Comment comment = commentRepository.findById(commentId) + .orElseThrow(NotFoundCommentException::new); + Member member = memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); + + validateDuplicateCommentLike(memberId, commentId); + validateIsNotOwnComment(comment, member); + + try { + CommentLike commentLike = new CommentLike(member, comment); + return new CommentLikeSaveResponse(commentLikeRepository.save(commentLike).getId()); + } catch (DataIntegrityViolationException e) { + throw new AlreadyExistsCommentLike(); + } + } + + private void validateIsNotOwnComment(Comment comment, Member member) { + if (comment.isWrittenByMember(member)) { + throw new InvalidCommentLikeException(); + } + } + + private void validateDuplicateCommentLike(Long memberId, Long commentId) { + commentLikeRepository.findCommentLikeIdByCommentIdAndMemberId(memberId, commentId).ifPresent(cl -> { + throw new AlreadyExistsCommentLike(); + }); + } + + @Transactional + public void delete(Long memberId, Long commentId) { + Comment comment = commentRepository.findById(commentId) + .orElseThrow(NotFoundCommentException::new); + Member member = memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); + + Long commentLikeId = commentLikeRepository + .findCommentLikeIdByCommentIdAndMemberId(member.getId(), comment.getId()) + .orElseThrow(NotFoundCommentLikeException::new); + + commentLikeRepository.deleteById(commentLikeId); + } + + @EventListener + public void deleteAllWhenMemberDeleted(DeleteMemberEvent event) { + Member member = event.getMember(); + commentLikeRepository.findAllByMemberId(member.getId()).forEach(CommentLike::removeMember); + } + + @EventListener + public void deleteAllWhenCommentDeleted(DeleteCommentEvent event) { + Comment comment = event.getComment(); + commentLikeRepository.findAllByCommentId(comment.getId()).forEach(CommentLike::removeComment); + } + + @Async + @Transactional(propagation = Propagation.REQUIRES_NEW) + @TransactionalEventListener + public void deleteCommentLikesWhenCommentDeleted(DeleteCommentEvent event) { + commentLikeRepository.deleteAllByCommentIdIsNull(); + } +} diff --git a/src/main/java/mocacong/server/service/CommentService.java b/src/main/java/mocacong/server/service/CommentService.java index 0e6b9d8d..483f689a 100644 --- a/src/main/java/mocacong/server/service/CommentService.java +++ b/src/main/java/mocacong/server/service/CommentService.java @@ -1,8 +1,7 @@ package mocacong.server.service; -import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import mocacong.server.domain.Cafe; import mocacong.server.domain.Comment; import mocacong.server.domain.Member; @@ -17,13 +16,19 @@ import mocacong.server.repository.CafeRepository; import mocacong.server.repository.CommentRepository; import mocacong.server.repository.MemberRepository; -import mocacong.server.service.event.MemberEvent; +import mocacong.server.service.event.DeleteCommentEvent; +import mocacong.server.service.event.DeleteMemberEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j @Service @RequiredArgsConstructor @Transactional @@ -32,33 +37,39 @@ public class CommentService { private final MemberRepository memberRepository; private final CafeRepository cafeRepository; private final CommentRepository commentRepository; + private final ApplicationEventPublisher applicationEventPublisher; - public CommentSaveResponse save(String email, String mapId, String content) { + public CommentSaveResponse save(Long memberId, String mapId, String content) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Comment comment = new Comment(cafe, member, content); - return new CommentSaveResponse(commentRepository.save(comment).getId()); + return new CommentSaveResponse(commentRepository.save(comment).getId(), member.getReportCount()); } @Transactional(readOnly = true) - public CommentsResponse findAll(String email, String mapId, Integer page, int count) { + public CommentsResponse findAll(Long memberId, String mapId, Integer page, int count) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Slice comments = commentRepository.findAllByCafeId(cafe.getId(), PageRequest.of(page, count)); List responses = findCommentResponses(member, comments); + + if (page == 0) { + Long totalCounts = commentRepository.countAllByCafeId(cafe.getId()); + return new CommentsResponse(comments.isLast(), totalCounts, responses); + } return new CommentsResponse(comments.isLast(), responses); } @Transactional(readOnly = true) - public CommentsResponse findCafeCommentsOnlyMyComments(String email, String mapId, Integer page, int count) { + public CommentsResponse findCafeCommentsOnlyMyComments(Long memberId, String mapId, Integer page, int count) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Slice comments = commentRepository.findAllByCafeIdAndMemberId(cafe.getId(), member.getId(), PageRequest.of(page, count)); @@ -79,10 +90,10 @@ private List findCommentResponses(Member member, Slice } @Transactional - public void update(String email, String mapId, String content, Long commentId) { + public void update(Long memberId, String mapId, String content, Long commentId) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Comment comment = cafe.getComments().stream() .filter(c -> c.getId().equals(commentId)) @@ -96,24 +107,26 @@ public void update(String email, String mapId, String content, Long commentId) { } @Transactional - public void delete(String email, String mapId, Long commentId) { + public void delete(Long memberId, String mapId, Long commentId) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); Comment comment = cafe.getComments().stream() .filter(c -> c.getId().equals(commentId)) .findFirst() .orElseThrow(NotFoundCommentException::new); + applicationEventPublisher.publishEvent(new DeleteCommentEvent(comment)); if (!comment.isWrittenByMember(member)) { throw new InvalidCommentDeleteException(); } + applicationEventPublisher.publishEvent(new DeleteCommentEvent(comment)); commentRepository.delete(comment); } @EventListener - public void updateCommentWhenMemberDelete(MemberEvent event) { + public void updateCommentWhenMemberDelete(DeleteMemberEvent event) { Member member = event.getMember(); commentRepository.findAllByMemberId(member.getId()) .forEach(Comment::removeMember); diff --git a/src/main/java/mocacong/server/service/FavoriteService.java b/src/main/java/mocacong/server/service/FavoriteService.java index f2bc3fed..1fb2fca2 100644 --- a/src/main/java/mocacong/server/service/FavoriteService.java +++ b/src/main/java/mocacong/server/service/FavoriteService.java @@ -12,7 +12,7 @@ import mocacong.server.repository.CafeRepository; import mocacong.server.repository.FavoriteRepository; import mocacong.server.repository.MemberRepository; -import mocacong.server.service.event.MemberEvent; +import mocacong.server.service.event.DeleteMemberEvent; import org.springframework.cache.annotation.CacheEvict; import org.springframework.context.event.EventListener; import org.springframework.dao.DataIntegrityViolationException; @@ -32,16 +32,16 @@ public class FavoriteService { @CacheEvict(key = "#mapId", value = "cafePreviewCache") @Transactional - public FavoriteSaveResponse save(String email, String mapId) { + public FavoriteSaveResponse save(Long memberId, String mapId) { Cafe cafe = cafeRepository.findByMapId(mapId) .orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); validateDuplicateFavorite(cafe.getId(), member.getId()); try { Favorite favorite = new Favorite(member, cafe); - return new FavoriteSaveResponse(favoriteRepository.save(favorite).getId()); + return new FavoriteSaveResponse(favoriteRepository.save(favorite).getId(), member.getReportCount()); } catch (DataIntegrityViolationException e) { throw new AlreadyExistsFavorite(); } @@ -55,9 +55,9 @@ private void validateDuplicateFavorite(Long cafeId, Long memberId) { @CacheEvict(key = "#mapId", value = "cafePreviewCache") @Transactional - public void delete(String email, String mapId) { + public void delete(Long memberId, String mapId) { Cafe cafe = cafeRepository.findByMapId(mapId).orElseThrow(NotFoundCafeException::new); - Member member = memberRepository.findByEmail(email).orElseThrow(NotFoundMemberException::new); + Member member = memberRepository.findById(memberId).orElseThrow(NotFoundMemberException::new); Long favoriteId = favoriteRepository.findFavoriteIdByCafeIdAndMemberId(cafe.getId(), member.getId()) .orElseThrow(NotFoundFavoriteException::new); @@ -65,7 +65,7 @@ public void delete(String email, String mapId) { } @EventListener - public void deleteAllWhenMemberDelete(MemberEvent event) { + public void deleteAllWhenMemberDelete(DeleteMemberEvent event) { Member member = event.getMember(); favoriteRepository.findAllByMemberId(member.getId()).forEach(Favorite::removeMember); } @@ -73,7 +73,7 @@ public void deleteAllWhenMemberDelete(MemberEvent event) { @Async @Transactional(propagation = Propagation.REQUIRES_NEW) @TransactionalEventListener - public void deleteFavoritesWhenMemberDeleted(MemberEvent event) { + public void deleteFavoritesWhenMemberDeleted(DeleteMemberEvent event) { favoriteRepository.deleteAllByMemberIdIsNull(); } } diff --git a/src/main/java/mocacong/server/service/MemberService.java b/src/main/java/mocacong/server/service/MemberService.java index 91b69771..2dce9893 100644 --- a/src/main/java/mocacong/server/service/MemberService.java +++ b/src/main/java/mocacong/server/service/MemberService.java @@ -4,6 +4,7 @@ import mocacong.server.domain.Member; import mocacong.server.domain.MemberProfileImage; import mocacong.server.domain.Platform; +import mocacong.server.domain.Status; import mocacong.server.dto.request.*; import mocacong.server.dto.response.*; import mocacong.server.exception.badrequest.*; @@ -11,8 +12,8 @@ import mocacong.server.repository.MemberProfileImageRepository; import mocacong.server.repository.MemberRepository; import mocacong.server.security.auth.JwtTokenProvider; +import mocacong.server.service.event.DeleteMemberEvent; import mocacong.server.service.event.DeleteNotUsedImagesEvent; -import mocacong.server.service.event.MemberEvent; import mocacong.server.support.AwsS3Uploader; import mocacong.server.support.AwsSESSender; import org.springframework.beans.factory.annotation.Value; @@ -23,6 +24,10 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; import java.util.List; import java.util.Optional; import java.util.Random; @@ -52,8 +57,8 @@ public MemberSignUpResponse signUp(MemberSignUpRequest request) { validateDuplicateMember(request); String encodedPassword = passwordEncoder.encode(request.getPassword()); - try{ - Member member = new Member(request.getEmail(), encodedPassword, request.getNickname(), request.getPhone()); + try { + Member member = new Member(request.getEmail(), encodedPassword, request.getNickname()); return new MemberSignUpResponse(memberRepository.save(member).getId()); } catch (DataIntegrityViolationException e) { throw new DuplicateMemberException(); @@ -61,21 +66,15 @@ public MemberSignUpResponse signUp(MemberSignUpRequest request) { } private void validateDuplicateMember(MemberSignUpRequest memberSignUpRequest) { - memberRepository.findByEmail(memberSignUpRequest.getEmail()) - .ifPresent(member -> { - throw new DuplicateMemberException(); - }); - memberRepository.findByNickname(memberSignUpRequest.getNickname()) - .ifPresent(member -> { - throw new DuplicateNicknameException(); - }); + if (memberRepository.existsByEmailAndPlatform(memberSignUpRequest.getEmail(), Platform.MOCACONG)) { + throw new DuplicateMemberException(); + } + validateDuplicateNickname(memberSignUpRequest.getNickname()); } private void validateDuplicateNickname(String nickname) { - memberRepository.findByNickname(nickname) - .ifPresent(member -> { - throw new DuplicateNicknameException(); - }); + if (memberRepository.existsByNickname(nickname)) + throw new DuplicateNicknameException(); } @Transactional @@ -89,10 +88,11 @@ public OAuthMemberSignUpResponse signUpByOAuthMember(OAuthMemberSignUpRequest re } @Transactional - public void delete(String email) { - Member findMember = memberRepository.findByEmail(email) + public void delete(Long memberId) { + Member findMember = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); - applicationEventPublisher.publishEvent(new MemberEvent(findMember)); + findMember.updateProfileImgUrl(null); + applicationEventPublisher.publishEvent(new DeleteMemberEvent(findMember)); memberRepository.delete(findMember); } @@ -100,8 +100,7 @@ public void delete(String email) { public MemberGetAllResponse getAllMembers() { List members = memberRepository.findAll(); List memberGetResponses = members.stream() - .map(member -> new MemberGetResponse(member.getId(), member.getEmail(), - member.getNickname(), member.getPhone())) + .map(member -> new MemberGetResponse(member.getId(), member.getEmail(), member.getNickname())) .collect(Collectors.toList()); return new MemberGetAllResponse(memberGetResponses); } @@ -109,7 +108,7 @@ public MemberGetAllResponse getAllMembers() { public IsDuplicateEmailResponse isDuplicateEmail(String email) { validateEmail(email); - Optional findMember = memberRepository.findByEmail(email); + Optional findMember = memberRepository.findByEmailAndPlatform(email, Platform.MOCACONG); return new IsDuplicateEmailResponse(findMember.isPresent()); } @@ -122,20 +121,20 @@ private void validateEmail(String email) { public EmailVerifyCodeResponse sendEmailVerifyCode(EmailVerifyCodeRequest request) { validateNonce(request.getNonce()); String requestEmail = request.getEmail(); - memberRepository.findByEmail(requestEmail) + Member member = memberRepository.findByEmailAndPlatform(requestEmail, Platform.MOCACONG) .orElseThrow(NotFoundMemberException::new); Random random = new Random(); int randomNumber = random.nextInt(EMAIL_VERIFY_CODE_MAXIMUM_NUMBER + 1); String code = String.format("%04d", randomNumber); awsSESSender.sendToVerifyEmail(requestEmail, code); - String token = jwtTokenProvider.createToken(requestEmail); + String token = jwtTokenProvider.createToken(member.getId()); return new EmailVerifyCodeResponse(token, code); } @Transactional - public void resetPassword(String email, ResetPasswordRequest request) { + public void resetPassword(Long memberId, ResetPasswordRequest request) { validateNonce(request.getNonce()); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); String updatePassword = request.getPassword(); validatePassword(updatePassword); @@ -158,8 +157,8 @@ private void validatePassword(String password) { public IsDuplicateNicknameResponse isDuplicateNickname(String nickname) { validateNickname(nickname); - Optional findMember = memberRepository.findByNickname(nickname); - return new IsDuplicateNicknameResponse(findMember.isPresent()); + Boolean isPresent = memberRepository.existsByNickname(nickname); + return new IsDuplicateNicknameResponse(isPresent); } private void validateNickname(String nickname) { @@ -168,15 +167,15 @@ private void validateNickname(String nickname) { } } - public MyPageResponse findMyInfo(String email) { - Member member = memberRepository.findByEmail(email) + public MyPageResponse findMyInfo(Long memberId) { + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); - return new MyPageResponse(member.getEmail(), member.getNickname(), member.getPhone(), member.getImgUrl()); + return new MyPageResponse(member.getEmail(), member.getNickname(), member.getImgUrl()); } @Transactional - public void updateProfileImage(String email, MultipartFile profileImg) { - Member member = memberRepository.findByEmail(email) + public void updateProfileImage(Long memberId, MultipartFile profileImg) { + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); if (profileImg == null) { member.updateProfileImgUrl(null); @@ -189,13 +188,12 @@ public void updateProfileImage(String email, MultipartFile profileImg) { } @Transactional - public void updateProfileInfo(String email, MemberProfileUpdateRequest request) { + public void updateProfileInfo(Long memberId, MemberProfileUpdateRequest request) { String updateNickname = request.getNickname(); - String updatePhone = request.getPhone(); - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); validateDuplicateNickname(updateNickname); - member.updateProfileInfo(updateNickname, updatePhone); + member.updateProfileInfo(updateNickname); } @Transactional @@ -212,8 +210,16 @@ public void deleteNotUsedProfileImages() { memberProfileImageRepository.deleteAllByIdInBatch(ids); } - public PasswordVerifyResponse verifyPassword(String email, PasswordVerifyRequest request) { - Member member = memberRepository.findByEmail(email) + @Transactional + public void setActiveAfter60days() { + LocalDate thresholdLocalDate = LocalDate.now().minusDays(60); + Instant instant = thresholdLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant(); + Date thresholdDate = Date.from(instant); + memberRepository.bulkUpdateStatus(Status.ACTIVE, Status.INACTIVE, thresholdDate); + } + + public PasswordVerifyResponse verifyPassword(Long memberId, PasswordVerifyRequest request) { + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); String storedPassword = member.getPassword(); String encodedPassword = passwordEncoder.encode(request.getPassword()); @@ -222,10 +228,10 @@ public PasswordVerifyResponse verifyPassword(String email, PasswordVerifyRequest return new PasswordVerifyResponse(isSuccess); } - public GetUpdateProfileInfoResponse getUpdateProfileInfo(String email) { - Member member = memberRepository.findByEmail(email) + public GetUpdateProfileInfoResponse getUpdateProfileInfo(Long memberId) { + Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); - return new GetUpdateProfileInfoResponse(member.getEmail(), member.getNickname(), member.getPhone()); + return new GetUpdateProfileInfoResponse(member.getEmail(), member.getNickname()); } } diff --git a/src/main/java/mocacong/server/service/ReportService.java b/src/main/java/mocacong/server/service/ReportService.java new file mode 100644 index 00000000..4b1cd614 --- /dev/null +++ b/src/main/java/mocacong/server/service/ReportService.java @@ -0,0 +1,82 @@ +package mocacong.server.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import mocacong.server.domain.Comment; +import mocacong.server.domain.Member; +import mocacong.server.domain.Report; +import mocacong.server.domain.ReportReason; +import mocacong.server.dto.response.CommentReportResponse; +import mocacong.server.exception.badrequest.DuplicateReportCommentException; +import mocacong.server.exception.badrequest.InvalidCommentReportException; +import mocacong.server.exception.notfound.NotFoundCommentException; +import mocacong.server.exception.notfound.NotFoundMemberException; +import mocacong.server.repository.CommentRepository; +import mocacong.server.repository.MemberRepository; +import mocacong.server.repository.ReportRepository; +import mocacong.server.service.event.DeleteMemberEvent; +import org.springframework.context.event.EventListener; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional +public class ReportService { + + private final MemberRepository memberRepository; + private final CommentRepository commentRepository; + private final ReportRepository reportRepository; + + public CommentReportResponse reportComment(Long memberId, Long commentId, String reportReason) { + Member reporter = memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); + Comment comment = commentRepository.findById(commentId) + .orElseThrow(NotFoundCommentException::new); + + try { + createCommentReport(comment, reporter, reportReason); + + // 코멘트를 작성한 회원이 탈퇴한 경우 + if (comment.isDeletedCommenter() && comment.isReportThresholdExceeded()) { + maskReportedComment(comment); + comment.updateIsMasked(true); + } else { + Member commenter = comment.getMember(); + if (comment.isWrittenByMember(reporter)) { + throw new InvalidCommentReportException(); + } + if (comment.isReportThresholdExceeded()) { + commenter.incrementMemberReportCount(); + maskReportedComment(comment); + comment.updateIsMasked(true); + } + } + } catch (DataIntegrityViolationException e) { + throw new DuplicateReportCommentException(); + } + return new CommentReportResponse(comment.getReportsCount(), reporter.getReportCount()); + } + + private void createCommentReport(Comment comment, Member reporter, String reportReason) { + if (comment.hasAlreadyReported(reporter)) { + throw new DuplicateReportCommentException(); + } + ReportReason reason = ReportReason.from(reportReason); + comment.addReport(new Report(comment, reporter, reason)); + } + + @EventListener + public void updateCommentReportWhenMemberDelete(DeleteMemberEvent event) { + Member member = event.getMember(); + reportRepository.findAllByReporter(member) + .forEach(Report::removeReporter); + } + + private void maskReportedComment(Comment comment) { + comment.maskComment(); + comment.maskAuthor(); + } +} diff --git a/src/main/java/mocacong/server/service/event/DeleteCommentEvent.java b/src/main/java/mocacong/server/service/event/DeleteCommentEvent.java new file mode 100644 index 00000000..a4af0178 --- /dev/null +++ b/src/main/java/mocacong/server/service/event/DeleteCommentEvent.java @@ -0,0 +1,12 @@ +package mocacong.server.service.event; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import mocacong.server.domain.Comment; + +@Getter +@RequiredArgsConstructor +public class DeleteCommentEvent { + + private final Comment comment; +} diff --git a/src/main/java/mocacong/server/service/event/MemberEvent.java b/src/main/java/mocacong/server/service/event/DeleteMemberEvent.java similarity index 86% rename from src/main/java/mocacong/server/service/event/MemberEvent.java rename to src/main/java/mocacong/server/service/event/DeleteMemberEvent.java index 43448ae4..a9b34553 100644 --- a/src/main/java/mocacong/server/service/event/MemberEvent.java +++ b/src/main/java/mocacong/server/service/event/DeleteMemberEvent.java @@ -6,7 +6,7 @@ @Getter @RequiredArgsConstructor -public class MemberEvent { +public class DeleteMemberEvent { private final Member member; } diff --git a/src/main/java/mocacong/server/support/AwsS3Uploader.java b/src/main/java/mocacong/server/support/AwsS3Uploader.java index 53891b08..bc5db32b 100644 --- a/src/main/java/mocacong/server/support/AwsS3Uploader.java +++ b/src/main/java/mocacong/server/support/AwsS3Uploader.java @@ -2,8 +2,14 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import mocacong.server.exception.badrequest.InvalidFileEmptyException; import mocacong.server.service.event.DeleteNotUsedImagesEvent; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.event.EventListener; @@ -11,12 +17,6 @@ import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - @Slf4j @Component @RequiredArgsConstructor @@ -30,6 +30,8 @@ public class AwsS3Uploader { private String bucket; public String uploadImage(MultipartFile multipartFile) { + checkInvalidUploadFile(multipartFile); + ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentType(multipartFile.getContentType()); objectMetadata.setContentLength(multipartFile.getSize()); @@ -46,6 +48,12 @@ public String uploadImage(MultipartFile multipartFile) { return amazonS3Client.getUrl(bucket, fileName).toString(); } + private void checkInvalidUploadFile(MultipartFile multipartFile) { + if (multipartFile.isEmpty() || multipartFile.getSize() == 0) { + throw new InvalidFileEmptyException(); + } + } + @Async @EventListener public void deleteImages(DeleteNotUsedImagesEvent event) { diff --git a/src/main/java/mocacong/server/support/logging/LoggingAspect.java b/src/main/java/mocacong/server/support/logging/LoggingAspect.java index 16b16795..a7a89306 100644 --- a/src/main/java/mocacong/server/support/logging/LoggingAspect.java +++ b/src/main/java/mocacong/server/support/logging/LoggingAspect.java @@ -3,10 +3,13 @@ import javax.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -30,7 +33,7 @@ private void allController() { } @Around("allComponents()") - public Object doLogTrace(ProceedingJoinPoint joinPoint) throws Throwable { + public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { final String message = joinPoint.getSignature().toShortString(); final Object[] args = joinPoint.getArgs(); try { @@ -45,7 +48,7 @@ public Object doLogTrace(ProceedingJoinPoint joinPoint) throws Throwable { } @Around("allController()") - public Object doLogRequest(ProceedingJoinPoint joinPoint) throws Throwable { + public Object request(ProceedingJoinPoint joinPoint) throws Throwable { loggingStatusManager.syncStatus(); String taskId = loggingStatusManager.getTaskId(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) @@ -61,4 +64,17 @@ public Object doLogRequest(ProceedingJoinPoint joinPoint) throws Throwable { loggingStatusManager.release(); } } + + @AfterReturning( + value = "execution(public * mocacong.server.controller..*Controller.*(..))", + returning = "result" + ) + public void response(JoinPoint joinPoint, Object result) { + String taskId = loggingStatusManager.getTaskId(); + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + String controllerMethodName = methodSignature.getMethod() + .getName(); + + log.info("[{}] method: {}, result: {}", taskId, controllerMethodName, result); + } } diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index b2aa0126..daac5cfb 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -10,11 +10,7 @@ - email\s*=\s*([^,)\s]+) - password\s*=\s*([^,)\s]+) - phone\s*=\s*([^,)\s]+) (\d+\.\d+\.\d+\.\d+) - (\w+@\w+\.\w+) ${LOG_PATTERN} @@ -32,13 +28,9 @@ ${LOG_PATH}/mocacong-${BY_DATE}.log - email\s*=\s*([^,)\s]+) password\s*=\s*([^,)\s]+) - phone\s*=\s*([^,)\s]+) - platformId\s*=\s*([^,)\s]+) nonce\s*=\s*([^,)\s]+) (\d+\.\d+\.\d+\.\d+) - (\w+@\w+\.\w+) ${LOG_PATTERN} @@ -65,13 +57,9 @@ - email\s*=\s*([^,)\s]+) password\s*=\s*([^,)\s]+) - phone\s*=\s*([^,)\s]+) - platformId\s*=\s*([^,)\s]+) nonce\s*=\s*([^,)\s]+) (\d+\.\d+\.\d+\.\d+) - (\w+@\w+\.\w+) ${LOG_PATTERN} diff --git a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java index 869282f1..4a865997 100644 --- a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java @@ -7,20 +7,20 @@ import mocacong.server.dto.request.MemberSignUpRequest; import mocacong.server.dto.response.OAuthTokenResponse; import mocacong.server.dto.response.TokenResponse; -import mocacong.server.security.auth.apple.AppleOAuthUserProvider; import mocacong.server.security.auth.OAuthPlatformMemberResponse; +import mocacong.server.security.auth.apple.AppleOAuthUserProvider; import mocacong.server.security.auth.kakao.KakaoOAuthUserProvider; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class AuthAcceptanceTest extends AcceptanceTest { @@ -33,7 +33,7 @@ public class AuthAcceptanceTest extends AcceptanceTest { @Test @DisplayName("회원이 정상적으로 로그인한다") void login() { - MemberSignUpRequest request = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(request) diff --git a/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java index f4a6dd0b..8811bd09 100644 --- a/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java @@ -1,23 +1,21 @@ package mocacong.server.acceptance; import io.restassured.RestAssured; +import java.util.List; +import static mocacong.server.acceptance.AcceptanceFixtures.*; import mocacong.server.dto.request.*; import mocacong.server.dto.response.CafeFilterFavoritesResponse; import mocacong.server.dto.response.CafeFilterStudyTypeResponse; import mocacong.server.dto.response.CafeReviewResponse; import mocacong.server.dto.response.CafeReviewUpdateResponse; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import java.util.List; - -import static mocacong.server.acceptance.AcceptanceFixtures.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.assertAll; - public class CafeAcceptanceTest extends AcceptanceTest { @Test @@ -40,7 +38,7 @@ void findCafe() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -59,7 +57,7 @@ void saveCafeReview() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CafeReviewRequest request = new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -87,7 +85,7 @@ void findMyCafeReview() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); 카페_리뷰_작성(token, mapId, new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -107,7 +105,7 @@ void saveCafeReviewManyTimes() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CafeReviewRequest request = new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -129,7 +127,7 @@ void saveCafeReviewManyTimes() { void updateCafeReview() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CafeReviewRequest request1 = new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -160,7 +158,7 @@ void updateCafeReview() { void updateCafeReviewNotFoundReview() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CafeReviewUpdateRequest request = new CafeReviewUpdateRequest(3, "solo", "빵빵해요", "여유로워요", @@ -182,8 +180,8 @@ void previewCafe() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); - MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1111-1111"); + MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); 회원_가입(signUpRequest1); 회원_가입(signUpRequest2); String token1 = 로그인_토큰_발급(signUpRequest1.getEmail(), signUpRequest1.getPassword()); @@ -211,7 +209,7 @@ void getCafesFilterStudyType() { 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페 본점")); 카페_등록(new CafeRegisterRequest(mapId2, "메리네 카페 2호점")); 카페_등록(new CafeRegisterRequest(mapId3, "메리네 카페 3호점")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CafeReviewRequest request1 = new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -247,7 +245,7 @@ void getCafesFilterFavorites() { 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페 본점")); 카페_등록(new CafeRegisterRequest(mapId2, "메리네 카페 2호점")); 카페_등록(new CafeRegisterRequest(mapId3, "메리네 카페 3호점")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -273,7 +271,7 @@ void getCafesFilterFavorites() { void getCafeImages() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페 본점")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); diff --git a/src/test/java/mocacong/server/acceptance/CommentAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/CommentAcceptanceTest.java index 5ec857d4..6d1700b1 100644 --- a/src/test/java/mocacong/server/acceptance/CommentAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/CommentAcceptanceTest.java @@ -3,14 +3,8 @@ import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import mocacong.server.dto.request.CafeRegisterRequest; -import mocacong.server.dto.request.CommentSaveRequest; -import mocacong.server.dto.request.CommentUpdateRequest; -import mocacong.server.dto.request.MemberSignUpRequest; -import mocacong.server.dto.response.CommentResponse; -import mocacong.server.dto.response.CommentSaveResponse; -import mocacong.server.dto.response.CommentsResponse; -import mocacong.server.dto.response.FindCafeResponse; +import mocacong.server.dto.request.*; +import mocacong.server.dto.response.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; @@ -32,7 +26,7 @@ void saveComment() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CommentSaveRequest request = new CommentSaveRequest(expected); @@ -59,7 +53,7 @@ void findComments() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); 카페_코멘트_작성(token, mapId, new CommentSaveRequest("댓글")); @@ -73,15 +67,61 @@ void findComments() { .extract(); } + @Test + @DisplayName("카페 코멘트 첫번째 목록을 조회한다") + void findCommentsFirstPage() { + String mapId = "12332312"; + 카페_등록(new CafeRegisterRequest(mapId, "베어네 카페")); + + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); + 회원_가입(signUpRequest); + String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); + 카페_코멘트_작성(token, mapId, new CommentSaveRequest("댓글")); + + CommentsResponse response = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(token) + .when().get("/cafes/" + mapId + "/comments?page=0&count=20") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract() + .as(CommentsResponse.class); + + assertThat(response.getCount()).isEqualTo(1L); + } + + @Test + @DisplayName("카페 코멘트 첫번째 목록을 제외한 페이지를 조회한다") + void findCommentsNotFirstPage() { + String mapId = "12332312"; + 카페_등록(new CafeRegisterRequest(mapId, "베어네 카페")); + + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); + 회원_가입(signUpRequest); + String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); + 카페_코멘트_작성(token, mapId, new CommentSaveRequest("댓글")); + + CommentsResponse response = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(token) + .when().get("/cafes/" + mapId + "/comments?page=1&count=20") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract() + .as(CommentsResponse.class); + + assertThat(response.getCount()).isEqualTo(null); + } + @Test @DisplayName("카페 코멘트 목록 중 내가 작성한 코멘트만을 조회한다") void findOnlyMyComments() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); - MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("mery@naver.com", "a1b2c3d4", "메리", "010-1234-5678"); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("mery@naver.com", "a1b2c3d4", "메리"); 회원_가입(signUpRequest2); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); String token2 = 로그인_토큰_발급(signUpRequest2.getEmail(), signUpRequest.getPassword()); @@ -107,7 +147,7 @@ void updateComment() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CommentSaveRequest saveRequest = new CommentSaveRequest(content); @@ -141,7 +181,7 @@ void deleteComment() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); CommentSaveRequest saveRequest = new CommentSaveRequest(content); diff --git a/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java new file mode 100644 index 00000000..7a0a8023 --- /dev/null +++ b/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java @@ -0,0 +1,73 @@ +package mocacong.server.acceptance; + +import io.restassured.RestAssured; +import mocacong.server.dto.request.CafeRegisterRequest; +import mocacong.server.dto.request.CommentSaveRequest; +import mocacong.server.dto.request.MemberSignUpRequest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +import static mocacong.server.acceptance.AcceptanceFixtures.*; + +public class CommentLikeAcceptanceTest extends AcceptanceTest{ + @Test + @DisplayName("회원이 댓글을 좋아요 한다.") + void saveCommentLike() { + String mapId = "12332312"; + String comment = "코딩하고 싶어지는 카페에요."; + 카페_등록(new CafeRegisterRequest(mapId, "정우네 카페")); + + MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); + 회원_가입(signUpRequest1); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); + 회원_가입(signUpRequest2); + + CommentSaveRequest commentRequest = new CommentSaveRequest(comment); + + String token1 = 로그인_토큰_발급(signUpRequest1.getEmail(), signUpRequest1.getPassword()); + String token2 = 로그인_토큰_발급(signUpRequest2.getEmail(), signUpRequest2.getPassword()); + long commentId = 카페_코멘트_작성(token1, mapId, commentRequest).body().jsonPath().getLong("id"); + + RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(token2) + .when().post("/comments/" + commentId + "/like") + .then().log().all() + .statusCode(HttpStatus.OK.value()); + } + + @Test + @DisplayName("회원이 댓글 좋아요를 취소한다.") + void deleteCommentLike() { + String mapId = "12332312"; + String comment = "코딩하고 싶어지는 카페에요."; + 카페_등록(new CafeRegisterRequest(mapId, "정우네 카페")); + + MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); + 회원_가입(signUpRequest1); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); + 회원_가입(signUpRequest2); + + CommentSaveRequest commentRequest = new CommentSaveRequest(comment); + + String token1 = 로그인_토큰_발급(signUpRequest1.getEmail(), signUpRequest1.getPassword()); + String token2 = 로그인_토큰_발급(signUpRequest2.getEmail(), signUpRequest2.getPassword()); + long commentId = 카페_코멘트_작성(token1, mapId, commentRequest).body().jsonPath().getLong("id"); + + RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(token2) + .when().post("/comments/" + commentId + "/like") + .then().log().all() + .statusCode(HttpStatus.OK.value()); + + RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(token2) + .when().delete("/comments/" + commentId + "/like") + .then().log().all() + .statusCode(HttpStatus.OK.value()); + } +} diff --git a/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java index 85af7296..0b29a1ec 100644 --- a/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java @@ -18,7 +18,7 @@ public class FavoriteAcceptanceTest extends AcceptanceTest { void saveFavoriteCafe() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -35,7 +35,7 @@ void saveFavoriteCafe() { void deleteFavoriteCafe() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); RestAssured.given().log().all() diff --git a/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java index 36dbf524..e9b38695 100644 --- a/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java @@ -1,26 +1,26 @@ package mocacong.server.acceptance; import io.restassured.RestAssured; +import static mocacong.server.acceptance.AcceptanceFixtures.*; import mocacong.server.domain.Platform; +import mocacong.server.domain.cafedetail.StudyType; import mocacong.server.dto.request.*; import mocacong.server.dto.response.*; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import mocacong.server.security.auth.apple.AppleOAuthUserProvider; import mocacong.server.support.AwsSESSender; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; - -import static mocacong.server.acceptance.AcceptanceFixtures.*; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; public class MemberAcceptanceTest extends AcceptanceTest { @@ -34,7 +34,7 @@ public class MemberAcceptanceTest extends AcceptanceTest { @Test @DisplayName("회원을 정상적으로 가입한다") void signUp() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -72,7 +72,7 @@ void signUpOauthMember() { @Test @DisplayName("회원이 정상적으로 탈퇴한다") void delete() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(request); String token = 로그인_토큰_발급(request.getEmail(), request.getPassword()); @@ -88,7 +88,7 @@ void delete() { @DisplayName("리뷰나 코멘트를 달은 회원이 정상적으로 탈퇴한다") void deleteWhenSaveReviewsAndComments() { String mapId = "1234"; - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); @@ -109,7 +109,7 @@ void deleteWhenSaveReviewsAndComments() { @DisplayName("즐겨찾기를 등록한 회원이 정상적으로 탈퇴한다") void deleteWhenSaveFavorites() { String mapId = "1234"; - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); @@ -127,7 +127,7 @@ void deleteWhenSaveFavorites() { @Test @DisplayName("회원은 올바르지 않은 형식의 필드로 가입할 수 없다") void signUpInvalidInputField() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "abcdefgh", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "abcdefgh", "케이"); ErrorResponse response = 회원_가입(request) .as(ErrorResponse.class); @@ -138,7 +138,7 @@ void signUpInvalidInputField() { @DisplayName("회원가입이 된 이메일로 이메일 인증을 요청할 수 있다") void verifyEmail() { String email = "kth990303@naver.com"; - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest(email, "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest(email, "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); doNothing().when(awsSESSender).sendToVerifyEmail(anyString(), anyString()); EmailVerifyCodeRequest request = new EmailVerifyCodeRequest(NONCE, email); @@ -169,7 +169,7 @@ void verifyEmailWhenNotRegisteredMember() { @DisplayName("비밀번호 찾기 요청으로 새로운 비밀번호로 변경한다") void findAndResetPassword() { String email = "kth990303@naver.com"; - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest(email, "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest(email, "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); ResetPasswordRequest request = new ResetPasswordRequest(NONCE, "password123"); @@ -186,7 +186,7 @@ void findAndResetPassword() { @Test @DisplayName("가입되어 있지 않은 이메일은 이메일 중복검사에서 걸리지 않는다") void isDuplicateWithNonExistingEmail() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -200,7 +200,7 @@ void isDuplicateWithNonExistingEmail() { @Test @DisplayName("이미 가입된 이메일은 이메일 중복검사에서 걸린다") void isDuplicateWithExistingEmail() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(request); @@ -216,7 +216,7 @@ void isDuplicateWithExistingEmail() { @Test @DisplayName("길이가 0인 이메일은 이메일 중복검사에서 예외를 던진다") void emailLengthIs0ReturnException() { - MemberSignUpRequest request = new MemberSignUpRequest("", "a1b2c3d4", "메리", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("", "a1b2c3d4", "메리"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -230,7 +230,7 @@ void emailLengthIs0ReturnException() { @Test @DisplayName("존재하지 않는 닉네임은 닉네임 중복검사에서 걸리지 않는다") void isDuplicateWithNonExistingNickname() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -244,7 +244,7 @@ void isDuplicateWithNonExistingNickname() { @Test @DisplayName("이미 존재하는 닉네임은 닉네임 중복검사에서 걸린다") void isDuplicateWithExistingNickname() { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -258,7 +258,7 @@ void isDuplicateWithExistingNickname() { @Test @DisplayName("길이가 0인 닉네임은 닉네임 중복검사에서 예외를 던진다") void nicknameLengthIs0ReturnException() { - MemberSignUpRequest request = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", ""); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -272,9 +272,9 @@ void nicknameLengthIs0ReturnException() { @Test @DisplayName("회원을 전체 조회한다") void getAllMembers() { - MemberSignUpRequest request1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(request1); - MemberSignUpRequest request2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678"); + MemberSignUpRequest request2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); 회원_가입(request2); RestAssured.given().log().all() @@ -289,7 +289,7 @@ void getAllMembers() { @Test @DisplayName("마이페이지로 내 정보를 조회한다") void findMyInfo() { - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -305,7 +305,6 @@ void findMyInfo() { assertAll( () -> assertThat(actual.getEmail()).isEqualTo("kth990303@naver.com"), () -> assertThat(actual.getNickname()).isEqualTo("케이"), - () -> assertThat(actual.getPhone()).isEqualTo("010-1234-5678"), () -> assertThat(actual.getImgUrl()).isNull() ); } @@ -315,7 +314,7 @@ void findMyInfo() { void findMyFavoriteCafes() { String mapId = "12332312"; 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); 즐겨찾기_등록(token, mapId); @@ -336,15 +335,15 @@ void findMyReviewCafes() { String mapId2 = "12121212"; 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페")); 카페_등록(new CafeRegisterRequest(mapId2, "케이네 카페")); - MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); - MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1111-1111"); + MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); 회원_가입(signUpRequest1); 회원_가입(signUpRequest2); String token1 = 로그인_토큰_발급(signUpRequest1.getEmail(), signUpRequest1.getPassword()); String token2 = 로그인_토큰_발급(signUpRequest2.getEmail(), signUpRequest2.getPassword()); CafeReviewRequest request1 = new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요"); - CafeReviewRequest request2 = new CafeReviewRequest(2, "solo", "빵빵해요", "여유로워요", + CafeReviewRequest request2 = new CafeReviewRequest(2, "group", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요"); CafeReviewRequest request3 = new CafeReviewRequest(1, "group", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요"); @@ -364,7 +363,9 @@ void findMyReviewCafes() { assertAll( () -> assertThat(actual.getCafes().get(0).getMyScore()).isEqualTo(4), () -> assertThat(actual.getCafes().get(0).getName()).isEqualTo("메리네 카페"), + () -> assertThat(actual.getCafes().get(0).getMyStudyType()).isEqualTo("solo"), () -> assertThat(actual.getCafes().get(1).getMyScore()).isEqualTo(2), + () -> assertThat(actual.getCafes().get(1).getMyStudyType()).isEqualTo("group"), () -> assertThat(actual.getCafes().get(1).getName()).isEqualTo("케이네 카페") ); } @@ -376,9 +377,9 @@ void findMyCommentCafes() { String mapId2 = "12121212"; 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페")); 카페_등록(new CafeRegisterRequest(mapId2, "케이네 카페")); - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); - MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("mery@naver.com", "a1b2c3d4", "메리", "010-1234-5678"); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("mery@naver.com", "a1b2c3d4", "메리"); 회원_가입(signUpRequest2); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); String token2 = 로그인_토큰_발급(signUpRequest2.getEmail(), signUpRequest.getPassword()); @@ -402,12 +403,11 @@ void findMyCommentCafes() { @Test @DisplayName("회원이 정상적으로 회원정보를 수정한다") void updateProfileInfo() { - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); String newNickname = "메리"; - String newPhone = "010-1234-5678"; - MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname, newPhone); + MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname); RestAssured.given().log().all() .auth().oauth2(token) @@ -427,7 +427,7 @@ void updateProfileInfo() { @Test @DisplayName("회원이 옳은 비밀번호로 비밀번호 인증한다") void verifyPasswordWithTrue() { - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); PasswordVerifyRequest request = new PasswordVerifyRequest("a1b2c3d4"); @@ -448,7 +448,7 @@ void verifyPasswordWithTrue() { @Test @DisplayName("회원이 틀린 비밀번호로 비밀번호 인증한다") void verifyPasswordWithFalse() { - MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest memberSignUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); PasswordVerifyRequest request = new PasswordVerifyRequest("wrongpwd123"); @@ -469,7 +469,7 @@ void verifyPasswordWithFalse() { @Test @DisplayName("프로필 수정 페이지에서 내 정보를 조회한다") void getUpdateProfileInfo() { - MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -484,8 +484,7 @@ void getUpdateProfileInfo() { assertAll( () -> assertThat(actual.getEmail()).isEqualTo("kth990303@naver.com"), - () -> assertThat(actual.getNickname()).isEqualTo("케이"), - () -> assertThat(actual.getPhone()).isEqualTo("010-1234-5678") + () -> assertThat(actual.getNickname()).isEqualTo("케이") ); } } diff --git a/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java new file mode 100644 index 00000000..b9335631 --- /dev/null +++ b/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java @@ -0,0 +1,52 @@ +package mocacong.server.acceptance; + +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import mocacong.server.dto.request.CafeRegisterRequest; +import mocacong.server.dto.request.CommentReportRequest; +import mocacong.server.dto.request.CommentSaveRequest; +import mocacong.server.dto.request.MemberSignUpRequest; +import mocacong.server.dto.response.CommentReportResponse; +import mocacong.server.dto.response.CommentSaveResponse; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +import static mocacong.server.acceptance.AcceptanceFixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +public class ReportAcceptanceTest extends AcceptanceTest { + + @Test + @DisplayName("타인이 작성한 코멘트를 신고한다") + void reportComment() { + String reportReason = "insult"; + String mapId = "12332312"; + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + + MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); + MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); + 회원_가입(signUpRequest1); + 회원_가입(signUpRequest2); + String token1 = 로그인_토큰_발급(signUpRequest1.getEmail(), signUpRequest1.getPassword()); + String token2 = 로그인_토큰_발급(signUpRequest2.getEmail(), signUpRequest2.getPassword()); + CommentSaveRequest saveRequest = new CommentSaveRequest("여길 왜 가냐"); + ExtractableResponse saveResponse = 카페_코멘트_작성(token1, mapId, saveRequest); + Long commentId = saveResponse.as(CommentSaveResponse.class).getId(); + CommentReportRequest reportRequest = new CommentReportRequest(reportReason); + + CommentReportResponse response = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(token2) + .body(reportRequest) + .when().post("/reports/comment/" + commentId) + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract() + .as(CommentReportResponse.class); + + assertThat(response.getCommentReportCount()).isEqualTo(1); + } +} diff --git a/src/test/java/mocacong/server/domain/BaseTimeTest.java b/src/test/java/mocacong/server/domain/BaseTimeTest.java index dcabb7be..5467de19 100644 --- a/src/test/java/mocacong/server/domain/BaseTimeTest.java +++ b/src/test/java/mocacong/server/domain/BaseTimeTest.java @@ -26,7 +26,7 @@ class BaseTimeTest { @Test @DisplayName("멤버를 저장하면 생성 시각이 자동으로 저장된다") public void memberCreatedAtNow() { - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678"); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리"); memberRepository.save(member); @@ -36,7 +36,7 @@ public void memberCreatedAtNow() { @Test @DisplayName("카페 객체를 수정하면 수정 시각이 자동으로 저장된다") public void updateCafeAtNow() { - Member member = new Member("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "a1b2c3d4", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); diff --git a/src/test/java/mocacong/server/domain/CafeImageTest.java b/src/test/java/mocacong/server/domain/CafeImageTest.java new file mode 100644 index 00000000..b35476d0 --- /dev/null +++ b/src/test/java/mocacong/server/domain/CafeImageTest.java @@ -0,0 +1,65 @@ +package mocacong.server.domain; + +import java.util.stream.Stream; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class CafeImageTest { + + private static final Member member = new Member("kth@apple.com", Platform.APPLE, "1234"); + + private static Stream provideCafeImagesMember() { + Member otherMember = new Member("kth@naver.com", Platform.KAKAO, "1234321"); + + return Stream.of( + Arguments.of(member, true), + Arguments.of(otherMember, false) + ); + } + + @ParameterizedTest + @DisplayName("해당 카페 이미지의 작성자가 해당 회원이 맞는지 여부를 반환한다") + @MethodSource("provideCafeImagesMember") + void isOwned(Member input, boolean expected) { + Cafe cafe = new Cafe("123454321", "케이카페"); + CafeImage cafeImage = new CafeImage("test_url", true, cafe, member); + + assertThat(cafeImage.isOwned(input)).isEqualTo(expected); + } + + @Test + @DisplayName("해당 카페 이미지 작성자가 없을 경우, 해당 회원이 맞는지 여부 반환은 항상 false 반환한다") + void isOwnedWhenMemberNull() { + Cafe cafe = new Cafe("123454321", "케이카페"); + CafeImage cafeImage = new CafeImage("test_url", true, cafe, null); + + assertThat(cafeImage.isOwned(member)).isFalse(); + } + + @Test + @DisplayName("카페 이미지 작성자를 null 처리한다") + void removeMember() { + Cafe cafe = new Cafe("123454321", "케이카페"); + CafeImage cafeImage = new CafeImage("test_url", true, cafe, member); + + cafeImage.removeMember(); + + assertThat(cafeImage.getMember()).isNull(); + } + + @Test + @DisplayName("특정 카페 이미지 url을 수정한다") + void updateCafeImageUrl() { + String expected = "test_update_url"; + Cafe cafe = new Cafe("123454321", "케이카페"); + CafeImage cafeImage = new CafeImage("test_url", true, cafe, member); + + cafeImage.updateImgUrl(expected); + + assertThat(cafeImage.getImgUrl()).isEqualTo(expected); + } +} diff --git a/src/test/java/mocacong/server/domain/CafeTest.java b/src/test/java/mocacong/server/domain/CafeTest.java index 1bff6632..f0b6029a 100644 --- a/src/test/java/mocacong/server/domain/CafeTest.java +++ b/src/test/java/mocacong/server/domain/CafeTest.java @@ -21,7 +21,7 @@ void findScoreWithNoReviews() { @Test @DisplayName("카페의 평점을 올바르게 계산하여 반환한다") void findScore() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); Score score1 = new Score(5, member, cafe); Score score2 = new Score(2, member, cafe); @@ -34,7 +34,7 @@ void findScore() { @Test @DisplayName("카페 세부정보 갱신이 올바르게 작동한다") void updateCafeDetails() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); @@ -65,7 +65,7 @@ void updateCafeDetails() { @Test @DisplayName("카페 세부정보 리뷰로 both가 작성될 경우 solo, group 포인트가 모두 1씩 증가한다") void updateCafeDetailsWhenStudyTypesAddBoth() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); // BOTH 리뷰 추가 -> SOLO, GROUP 모두 1포인트 @@ -99,7 +99,7 @@ void updateCafeDetailsWhenStudyTypesAddBoth() { @Test @DisplayName("카페 세부정보 중 study type은 같은 개수일 경우 solo나 group이 아닌 both를 반환한다") void updateCafeDetailsWhenStudyTypesEqual() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); @@ -119,7 +119,7 @@ void updateCafeDetailsWhenStudyTypesEqual() { @Test @DisplayName("카페에 일부 세부정보 리뷰가 하나도 없을 경우 해당 세부정보는 null을 반환한다") void updateCafeDetailsWhenSomeTypesNoReviews() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); CafeDetail cafeDetail = new CafeDetail(StudyType.SOLO, Wifi.FAST, null, Toilet.CLEAN, null, Power.MANY, Sound.LOUD); @@ -151,4 +151,24 @@ void updateCafeDetailsWhenNoReviews() { ); } + @Test + @DisplayName("카페의 스터디 타입을 반환한다") + void getStudyType() { + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); + Cafe cafe = new Cafe("1", "케이카페"); + CafeDetail cafeDetail = new CafeDetail(StudyType.SOLO, Wifi.FAST, null, Toilet.CLEAN, null, Power.MANY, Sound.LOUD); + Review review = new Review(member, cafe, cafeDetail); + cafe.addReview(review); + cafe.updateCafeDetails(); + + assertThat(cafe.getStudyType()).isEqualTo("solo"); + } + + @Test + @DisplayName("리뷰가 없는 카페의 스터디 타입은 null 을 반환한다") + void getStudyTypeWhenNotHasReviews() { + Cafe cafe = new Cafe("1", "케이카페"); + + assertThat(cafe.getStudyType()).isNull(); + } } diff --git a/src/test/java/mocacong/server/domain/CommentTest.java b/src/test/java/mocacong/server/domain/CommentTest.java index f48bd290..9917515c 100644 --- a/src/test/java/mocacong/server/domain/CommentTest.java +++ b/src/test/java/mocacong/server/domain/CommentTest.java @@ -12,7 +12,7 @@ class CommentTest { @Test @DisplayName("코멘트 길이가 200자를 초과하면 예외를 반환한다") void validateCommentLength() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); assertThatThrownBy(() -> new Comment(cafe, member, createLongComment(201))) .isInstanceOf(ExceedCommentLengthException.class); @@ -29,7 +29,7 @@ private String createLongComment(int length) { @Test @DisplayName("댓글 작성자 닉네임을 반환한다") void getWriterNickname() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); Comment comment = new Comment(cafe, member, "안녕하세요"); @@ -39,7 +39,7 @@ void getWriterNickname() { @Test @DisplayName("댓글 작성자 프로필 이미지 url을 반환한다") void getWriterImgUrl() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678", new MemberProfileImage("test_img.jpg")); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", new MemberProfileImage("test_img.jpg")); Cafe cafe = new Cafe("1", "케이카페"); Comment comment = new Comment(cafe, member, "안녕하세요"); @@ -49,8 +49,8 @@ void getWriterImgUrl() { @Test @DisplayName("코멘트가 해당 회원이 작성한 게 맞는지 여부를 반환한다") void isWrittenByMember() { - Member member1 = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); - Member member2 = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member1 = new Member("kth@naver.com", "a1b2c3d4", "케이"); + Member member2 = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); Comment comment = new Comment(cafe, member1, "안녕하세요"); diff --git a/src/test/java/mocacong/server/domain/MemberTest.java b/src/test/java/mocacong/server/domain/MemberTest.java index 22ef5fd9..6c609a49 100644 --- a/src/test/java/mocacong/server/domain/MemberTest.java +++ b/src/test/java/mocacong/server/domain/MemberTest.java @@ -1,7 +1,6 @@ package mocacong.server.domain; import mocacong.server.exception.badrequest.InvalidNicknameException; -import mocacong.server.exception.badrequest.InvalidPhoneException; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; @@ -17,7 +16,7 @@ class MemberTest { @DisplayName("모든 정보를 올바른 형식으로 입력하면 회원이 생성된다") void createMember() { assertDoesNotThrow( - () -> new Member("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678") + () -> new Member("kth990303@naver.com", "a1b2c3d4", "케이") ); } @@ -60,7 +59,7 @@ void isRegisterMember() { @ValueSource(strings = {"ㄱㅁㄴㄷㄹ", "ㅏㅣㅓㅜ", "가ㅏ누ㅟ"}) void createMemberNicknameOnlyOnset(String nickname) { assertDoesNotThrow( - () -> new Member("kth990303@naver.com", "a1b2c3d4", nickname, "010-1234-5678") + () -> new Member("kth990303@naver.com", "a1b2c3d4", nickname) ); } @@ -68,7 +67,7 @@ void createMemberNicknameOnlyOnset(String nickname) { @DisplayName("닉네임이 2~6자가 아니면 예외를 반환한다") @ValueSource(strings = {"일", "일이삼사오육칠"}) void nicknameLengthValidation(String nickname) { - assertThatThrownBy(() -> new Member("kth990303@naver.com", "a1b2c3d4", nickname, "010-1234-5678")) + assertThatThrownBy(() -> new Member("kth990303@naver.com", "a1b2c3d4", nickname)) .isInstanceOf(InvalidNicknameException.class); } @@ -76,23 +75,15 @@ void nicknameLengthValidation(String nickname) { @DisplayName("닉네임이 영어 또는 한글이 아니면 예외를 반환한다") @ValueSource(strings = {"123", "愛してるよ"}) void nicknameConfigureValidation(String nickname) { - assertThatThrownBy(() -> new Member("kth990303@naver.com", "a1b2c3d4", nickname, "010-1234-5678")) + assertThatThrownBy(() -> new Member("kth990303@naver.com", "a1b2c3d4", nickname)) .isInstanceOf(InvalidNicknameException.class); } - @ParameterizedTest - @DisplayName("전화번호가 01로 시작하지 않거나 하이픈 포함 10~14자가 아니면 예외를 반환한다") - @ValueSource(strings = {"030-1234-5678", "01-123-45", "010-12345-56789", "010123456", "012345678901234"}) - void phoneValidation(String phone) { - assertThatThrownBy(() -> new Member("kth990303@naver.com", "a1b2c3d4", "케이", phone)) - .isInstanceOf(InvalidPhoneException.class); - } - @Test @DisplayName("회원의 프로필 이미지가 존재하면 해당 이미지 url을 올바르게 반환한다") void getImgUrlWhenHasImage() { String expected = "test_img.jpg"; - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678", new MemberProfileImage(expected)); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", new MemberProfileImage(expected)); String actual = member.getImgUrl(); @@ -102,7 +93,7 @@ void getImgUrlWhenHasImage() { @Test @DisplayName("회원의 프로필 이미지가 존재하지 않으면 해당 이미지 url 반환은 null을 반환한다") void getImgUrlWhenHasNotImage() { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); String actual = member.getImgUrl(); @@ -114,7 +105,7 @@ void getImgUrlWhenHasNotImage() { void updateProfileImgUrl() { String expected = "test_img.jpg"; MemberProfileImage memberProfileImage = new MemberProfileImage("before.jpg"); - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678", + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", memberProfileImage, Platform.MOCACONG, "1234"); member.updateProfileImgUrl(new MemberProfileImage(expected)); diff --git a/src/test/java/mocacong/server/domain/ScoreTest.java b/src/test/java/mocacong/server/domain/ScoreTest.java index 7611fb55..b2606243 100644 --- a/src/test/java/mocacong/server/domain/ScoreTest.java +++ b/src/test/java/mocacong/server/domain/ScoreTest.java @@ -1,19 +1,18 @@ package mocacong.server.domain; import mocacong.server.exception.badrequest.InvalidScoreException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - class ScoreTest { @ParameterizedTest @ValueSource(ints = {0, 6}) @DisplayName("평점이 1점 이상 5점 이하가 아니면 예외를 반환한다") void invalidRangeScore(int score) { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); assertThatThrownBy(() -> new Score(score, member, cafe)) @@ -24,7 +23,7 @@ void invalidRangeScore(int score) { @ValueSource(ints = {0, 6}) @DisplayName("수정 시 평점이 1점 이상 5점 이하가 아니면 예외를 반환한다") void updateInvalidRangeScore(int score) { - Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); Cafe cafe = new Cafe("1", "케이카페"); Score oldScore = new Score(3, member, cafe); assertThatThrownBy(() -> oldScore.updateScore(score)) diff --git a/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java b/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java new file mode 100644 index 00000000..948d494f --- /dev/null +++ b/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java @@ -0,0 +1,59 @@ +package mocacong.server.repository; + +import java.util.List; +import mocacong.server.domain.Cafe; +import mocacong.server.domain.CafeImage; +import mocacong.server.domain.Member; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +@RepositoryTest +public class CafeImageRepositoryTest { + + @Autowired + private CafeImageRepository cafeImageRepository; + @Autowired + private CafeRepository cafeRepository; + @Autowired + private MemberRepository memberRepository; + + @Test + @DisplayName("내가 올린 카페 이미지부터 등록 순으로 조회한다") + void findAllByCafeIdAndIsUsedOrderByCafeImageIdDesc() { + Pageable pageable = PageRequest.of(0, 5); + Cafe cafe = cafeRepository.save(new Cafe("1", "케이카페")); + Member member1 = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); + Member member2 = memberRepository.save(new Member("dla@naver.com", "abcd1234", "메리")); + CafeImage cafeImage1 = new CafeImage("test_img.jpg", true, cafe, member1); + CafeImage cafeImage2 = new CafeImage("test_img2.jpg", true, cafe, member1); + CafeImage cafeImage3 = new CafeImage("test_img.jpg", true, cafe, member2); + CafeImage cafeImage4 = new CafeImage("test_img.jpg", true, cafe, member1); + CafeImage cafeImage5 = new CafeImage("test_img2.jpg", true, cafe, member1); + CafeImage cafeImage6 = new CafeImage("test_img.jpg", true, cafe, member2); + cafeImageRepository.save(cafeImage1); + cafeImageRepository.save(cafeImage2); + cafeImageRepository.save(cafeImage3); + cafeImageRepository.save(cafeImage4); + cafeImageRepository.save(cafeImage5); + cafeImageRepository.save(cafeImage6); + + Slice actual = cafeImageRepository.findAllByCafeIdAndIsUsedOrderByCafeImageId(cafe.getId(), + member1.getId(), pageable); // member1로 카페 이미지 조회 + List cafeImages = actual.getContent(); + + assertAll( + () -> assertThat(cafeImages).hasSize(5), + () -> assertThat(cafeImages.get(0)).isEqualTo(cafeImage1), + () -> assertThat(cafeImages.get(1)).isEqualTo(cafeImage2), + () -> assertThat(cafeImages.get(2)).isEqualTo(cafeImage4), + () -> assertThat(cafeImages.get(3)).isEqualTo(cafeImage5), + () -> assertThat(cafeImages.get(4)).isEqualTo(cafeImage3) + ); + } +} diff --git a/src/test/java/mocacong/server/repository/CafeRepositoryTest.java b/src/test/java/mocacong/server/repository/CafeRepositoryTest.java index a55debc7..c706ba0b 100644 --- a/src/test/java/mocacong/server/repository/CafeRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/CafeRepositoryTest.java @@ -2,6 +2,7 @@ import mocacong.server.domain.*; import mocacong.server.domain.cafedetail.*; +import mocacong.server.dto.response.MyReviewCafeResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -24,6 +25,8 @@ class CafeRepositoryTest { private FavoriteRepository favoriteRepository; @Autowired private ReviewRepository reviewRepository; + @Autowired + private ScoreRepository scoreRepository; @Test @DisplayName("내가 즐겨찾기에 등록한 카페 mapId 목록을 조회한다.") @@ -36,13 +39,13 @@ void findNearCafeMapIdsByMyFavoriteCafes() { Cafe savedCafe2 = cafeRepository.save(new Cafe(mapId2, "케이카페2")); Cafe savedCafe3 = cafeRepository.save(new Cafe(mapId3, "케이카페3")); Cafe savedCafe4 = cafeRepository.save(new Cafe(mapId4, "케이카페4")); - Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이", "010-1234-5678")); + Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); favoriteRepository.save(new Favorite(member, savedCafe1)); favoriteRepository.save(new Favorite(member, savedCafe2)); favoriteRepository.save(new Favorite(member, savedCafe3)); List mapIds = List.of(mapId1, mapId2, mapId3, mapId4); - List actual = cafeRepository.findNearCafeMapIdsByMyFavoriteCafes(member.getEmail(), mapIds); + List actual = cafeRepository.findNearCafeMapIdsByMyFavoriteCafes(member.getId(), mapIds); assertAll( () -> assertThat(actual).hasSize(3), @@ -59,12 +62,12 @@ void findByMyFavoriteCafes() { Cafe savedCafe2 = cafeRepository.save(new Cafe("2", "케이카페2")); Cafe savedCafe3 = cafeRepository.save(new Cafe("3", "케이카페3")); Cafe savedCafe4 = cafeRepository.save(new Cafe("4", "케이카페4")); - Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이", "010-1234-5678")); + Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); favoriteRepository.save(new Favorite(member, savedCafe1)); favoriteRepository.save(new Favorite(member, savedCafe2)); favoriteRepository.save(new Favorite(member, savedCafe3)); - Slice actual = cafeRepository.findByMyFavoriteCafes(member.getEmail(), PageRequest.of(1, 2)); + Slice actual = cafeRepository.findByMyFavoriteCafes(member.getId(), PageRequest.of(1, 2)); assertAll( () -> assertThat(actual).hasSize(1), @@ -78,27 +81,43 @@ void findByMyFavoriteCafes() { @Test @DisplayName("내가 리뷰를 등록한 카페 목록을 조회한다") - void findByMyReviewCafes() { + void findByMyReviewCafesById() { + Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); + Cafe savedCafe1 = cafeRepository.save(new Cafe("1", "케이카페1")); Cafe savedCafe2 = cafeRepository.save(new Cafe("2", "케이카페2")); Cafe savedCafe3 = cafeRepository.save(new Cafe("3", "케이카페3")); - Cafe savedCafe4 = cafeRepository.save(new Cafe("4", "케이카페4")); + + Score score1 = new Score(1, member, savedCafe1); + Score score2 = new Score(1, member, savedCafe2); + Score score3 = new Score(1, member, savedCafe3); + CafeDetail cafeDetail = new CafeDetail(StudyType.BOTH, Wifi.FAST, Parking.COMFORTABLE, Toilet.CLEAN, Desk.NORMAL, Power.NONE, Sound.LOUD); - Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이", "010-1234-5678")); + reviewRepository.save(new Review(member, savedCafe1, cafeDetail)); reviewRepository.save(new Review(member, savedCafe2, cafeDetail)); reviewRepository.save(new Review(member, savedCafe3, cafeDetail)); - Slice actual = cafeRepository.findByMyReviewCafes(member.getEmail(), PageRequest.of(1, 2)); + scoreRepository.save(score1); + scoreRepository.save(score2); + scoreRepository.save(score3); + + Slice actual = cafeRepository.findMyReviewCafesById(member.getId(), PageRequest.of(1, 2)); assertAll( () -> assertThat(actual).hasSize(1), () -> assertThat(actual.getNumber()).isEqualTo(1), () -> assertThat(actual.isLast()).isTrue(), + () -> assertThat(actual) + .extracting("myStudyType") + .containsExactly("both"), () -> assertThat(actual) .extracting("name") - .containsExactly("케이카페3") + .containsExactly("케이카페3"), + () -> assertThat(actual) + .extracting("myScore") + .containsExactly(1) ); } } diff --git a/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java b/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java new file mode 100644 index 00000000..9717f678 --- /dev/null +++ b/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java @@ -0,0 +1,53 @@ +package mocacong.server.repository; + +import mocacong.server.domain.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +@RepositoryTest +class CommentLikeRepositoryTest { + + @Autowired + private CommentLikeRepository commentLikeRepository; + @Autowired + private CommentRepository commentRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private CafeRepository cafeRepository; + + @Test + @DisplayName("comment id, 멤버 id로 댓글 좋아요 id를 조회한다") + void findByCommentIdAndMemberId() { + Cafe savedCafe = cafeRepository.save(new Cafe("1", "베어카페")); + Member savedMember = memberRepository.save(new Member("rlawjddn103@naver.com", "abcd1234", "베어")); + Comment savedComment = commentRepository.save(new Comment(savedCafe, savedMember, "코딩하기 좋은 카페네요.")); + CommentLike savedCommentLike = commentLikeRepository.save(new CommentLike(savedMember, savedComment)); + + Long actual = commentLikeRepository.findCommentLikeIdByCommentIdAndMemberId(savedMember.getId(), savedComment.getId()) + .orElse(null); + + assertAll( + () -> assertThat(actual).isNotNull(), + () -> assertThat(actual).isEqualTo(savedCommentLike.getId()) + ); + } + + @Test + @DisplayName("comment id가 null인 즐겨찾기들을 모두 삭제한다") + void deleteAllByCommentIdIsNull() { + Member savedMember = memberRepository.save(new Member("rlawjddn103@naver.com", "abcd1234", "베어")); + commentLikeRepository.save(new CommentLike(savedMember, null)); + + commentLikeRepository.deleteAllByCommentIdIsNull(); + + List actual = commentLikeRepository.findAll(); + assertThat(actual).isEmpty(); + } +} diff --git a/src/test/java/mocacong/server/repository/CommentRepositoryTest.java b/src/test/java/mocacong/server/repository/CommentRepositoryTest.java new file mode 100644 index 00000000..82a88862 --- /dev/null +++ b/src/test/java/mocacong/server/repository/CommentRepositoryTest.java @@ -0,0 +1,45 @@ +package mocacong.server.repository; + +import mocacong.server.domain.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +@RepositoryTest +class CommentRepositoryTest { + + @Autowired + private CommentLikeRepository commentLikeRepository; + @Autowired + private CommentRepository commentRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private CafeRepository cafeRepository; + + @Test + @DisplayName("코멘트 id, 멤버 id로 댓글 좋아요 id를 조회한다") + void findByCommentIdAndMemberId() { + Cafe savedCafe = cafeRepository + .save(new Cafe("1", "베어카페")); + Member savedMember = memberRepository + .save(new Member("rlawjddn103@naver.com", "abcd1234", "베어")); + Comment savedComment = commentRepository + .save(new Comment(savedCafe, savedMember, "코딩하고 싶어지네요.")); + CommentLike commentLike = new CommentLike(savedMember, savedComment); + commentLikeRepository.save(commentLike); + + Long actual = commentLikeRepository.findCommentLikeIdByCommentIdAndMemberId(savedMember.getId(), savedComment.getId()) + .orElse(null); + + assertAll( + () -> assertThat(actual).isNotNull(), + () -> assertThat(actual).isEqualTo(commentLike.getId()) + ); + } +} diff --git a/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java b/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java index e42d7b83..1b46accc 100644 --- a/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java @@ -25,7 +25,7 @@ class FavoriteRepositoryTest { @DisplayName("카페 id, 멤버 id로 즐겨찾기 id를 조회한다") void findByCafeIdAndMemberId() { Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); - Member savedMember = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이", "010-1234-5678")); + Member savedMember = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); Favorite favorite = new Favorite(savedMember, savedCafe); favoriteRepository.save(favorite); diff --git a/src/test/java/mocacong/server/repository/MemberRepositoryTest.java b/src/test/java/mocacong/server/repository/MemberRepositoryTest.java index fd15eac8..aca4f9c5 100644 --- a/src/test/java/mocacong/server/repository/MemberRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/MemberRepositoryTest.java @@ -2,13 +2,20 @@ import mocacong.server.domain.Member; import mocacong.server.domain.Platform; -import static org.assertj.core.api.Assertions.assertThat; - +import mocacong.server.domain.Status; 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.orm.jpa.DataJpaTest; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + @DataJpaTest class MemberRepositoryTest { @@ -22,7 +29,6 @@ void findIdByPlatformAndPlatformId() { "kth@apple.com", "a1b2c3d4", "케이", - "010-1234-1234", null, Platform.APPLE, "1234321" @@ -34,4 +40,30 @@ void findIdByPlatformAndPlatformId() { assertThat(actual).isEqualTo(savedMember.getId()); } + + @Test + @DisplayName("회원의 status가 변경된지 thresholdDateTime만큼 지났으면 newStatus로 일괄 변경한다") + void bulkUpdateStatus() { + Member member1 = new Member("dlawotn3@naver.com", "password1", "mery"); + Member member2 = new Member("dlawotn2@naver.com", "password2", "케이"); + memberRepository.save(member1); + memberRepository.save(member2); + + LocalDate thresholdDateTime = LocalDate.now(); + Instant instant = thresholdDateTime.atStartOfDay(ZoneId.systemDefault()).toInstant(); + Date thresholdDate = Date.from(instant); + Status oldStatus = Status.INACTIVE; + Status newStatus = Status.ACTIVE; + member1.incrementMemberReportCount(); // 상태를 ACTIVE -> INACTIVE + member2.incrementMemberReportCount(); // 상태를 ACTIVE -> INACTIVE + memberRepository.bulkUpdateStatus(newStatus, oldStatus, thresholdDate); + + Member updatedMember1 = memberRepository.findById(member1.getId()).orElse(null); + Member updatedMember2 = memberRepository.findById(member2.getId()).orElse(null); + + assertAll( + () -> assertThat(updatedMember1.getStatus()).isEqualTo(newStatus), + () -> assertThat(updatedMember2.getStatus()).isEqualTo(newStatus) + ); + } } diff --git a/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java b/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java index 932c1bfa..70d3b5f0 100644 --- a/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java @@ -6,7 +6,6 @@ import mocacong.server.domain.Review; import mocacong.server.domain.cafedetail.*; import static org.assertj.core.api.Assertions.assertThat; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +24,7 @@ class ReviewRepositoryTest { @DisplayName("카페 id, 멤버 id로 해당 멤버가 특정 카페에 작성한 리뷰의 id를 조회한다") void findIdByCafeIdAndMemberId() { Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); - Member savedMember = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이", "010-1234-5678")); + Member savedMember = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); CafeDetail cafeDetail = new CafeDetail(StudyType.SOLO, Wifi.FAST, Parking.COMFORTABLE, Toilet.CLEAN, Desk.COMFORTABLE, Power.MANY, Sound.LOUD); Review savedReview = reviewRepository.save(new Review(savedMember, savedCafe, cafeDetail)); diff --git a/src/test/java/mocacong/server/repository/ScoreRepositoryTest.java b/src/test/java/mocacong/server/repository/ScoreRepositoryTest.java index 4c580061..738c325e 100644 --- a/src/test/java/mocacong/server/repository/ScoreRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/ScoreRepositoryTest.java @@ -3,13 +3,11 @@ import mocacong.server.domain.Cafe; import mocacong.server.domain.Member; import mocacong.server.domain.Score; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.jupiter.api.Assertions.*; - @RepositoryTest class ScoreRepositoryTest { @@ -24,7 +22,7 @@ class ScoreRepositoryTest { @DisplayName("카페 id, 멤버 id로 해당 멤버가 특정 카페에 등록한 평점을 조회한다") void findScoreByCafeIdAndMemberId() { Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); - Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이", "010-1234-5678")); + Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); Score score = new Score(4, member, savedCafe); scoreRepository.save(score); diff --git a/src/test/java/mocacong/server/security/auth/AuthorizationExtractorTest.java b/src/test/java/mocacong/server/security/auth/AuthorizationExtractorTest.java new file mode 100644 index 00000000..6675f23a --- /dev/null +++ b/src/test/java/mocacong/server/security/auth/AuthorizationExtractorTest.java @@ -0,0 +1,22 @@ +package mocacong.server.security.auth; + +import mocacong.server.exception.badrequest.BlankTokenException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletRequest; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class AuthorizationExtractorTest { + + @DisplayName("공백 값으로 access token 추출을 시도하면 예외를 반환한다") + @Test + void extractAccessTokenByBlankToken() { + String token = "Bearer "; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", token); + + assertThatThrownBy(() -> AuthorizationExtractor.extractAccessToken(request)) + .isInstanceOf(BlankTokenException.class); + } +} diff --git a/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java b/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java index 8a1c92ad..c9cc1f77 100644 --- a/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java +++ b/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java @@ -2,13 +2,14 @@ import mocacong.server.exception.unauthorized.InvalidTokenException; import mocacong.server.exception.unauthorized.TokenExpiredException; -import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.Assertions; 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 static org.assertj.core.api.Assertions.*; + @SpringBootTest class JwtTokenProviderTest { @Autowired @@ -18,7 +19,7 @@ class JwtTokenProviderTest { @DisplayName("payload 정보를 통해 유효한 JWT 토큰을 생성한다") @Test public void createToken() { - String payload = "dlawotn3@naver.com"; + Long payload = 1L; String token = jwtTokenProvider.createToken(payload); @@ -29,7 +30,7 @@ public void createToken() { @DisplayName("올바른 토큰 정보로 payload를 조회한다") @Test void getPayload() { - token = jwtTokenProvider.createToken("dlawotn3@naver.com"); + token = jwtTokenProvider.createToken(1L); String payload = jwtTokenProvider.getPayload(token); @@ -50,7 +51,7 @@ void getPayloadByInvalidToken() { void getPayloadByExpiredToken() { long expirationMillis = 1L; JwtTokenProvider jwtTokenProvider = new JwtTokenProvider("secret-key", expirationMillis); - String expiredPayload = "expired-payload"; + Long expiredPayload = 1L; String expiredToken = jwtTokenProvider.createToken(expiredPayload); try { @@ -66,7 +67,7 @@ void getPayloadByExpiredToken() { @DisplayName("시크릿 키가 틀린 토큰 정보로 payload를 조회할 경우 예외를 발생시킨다") @Test void getPayloadByWrongSecretKeyToken() { - String payload = "dlawotn3@naver.com"; + Long payload = 1L; String correctSecretKey = "correct-secret-key"; String wrongSecretKey = "wrong-secret-key"; diff --git a/src/test/java/mocacong/server/service/AuthServiceTest.java b/src/test/java/mocacong/server/service/AuthServiceTest.java index c8111d64..cc564705 100644 --- a/src/test/java/mocacong/server/service/AuthServiceTest.java +++ b/src/test/java/mocacong/server/service/AuthServiceTest.java @@ -2,24 +2,27 @@ import mocacong.server.domain.Member; import mocacong.server.domain.Platform; +import mocacong.server.domain.Status; import mocacong.server.dto.request.AppleLoginRequest; import mocacong.server.dto.request.AuthLoginRequest; import mocacong.server.dto.response.OAuthTokenResponse; import mocacong.server.dto.response.TokenResponse; import mocacong.server.exception.badrequest.PasswordMismatchException; +import mocacong.server.exception.unauthorized.InactiveMemberException; import mocacong.server.repository.MemberRepository; -import mocacong.server.security.auth.apple.AppleOAuthUserProvider; import mocacong.server.security.auth.OAuthPlatformMemberResponse; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; +import mocacong.server.security.auth.apple.AppleOAuthUserProvider; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.when; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.security.crypto.password.PasswordEncoder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.when; + @ServiceTest class AuthServiceTest { @@ -34,18 +37,37 @@ class AuthServiceTest { private AppleOAuthUserProvider appleOAuthUserProvider; @Test - @DisplayName("회원 로그인 요청이 옳다면 토큰을 발급한다") + @DisplayName("회원 로그인 요청이 옳다면 토큰을 발급하고 상태는 ACTIVE로 반환한다") void login() { String email = "kth990303@naver.com"; String password = "a1b2c3d4"; String encodedPassword = passwordEncoder.encode("a1b2c3d4"); - Member member = new Member("kth990303@naver.com", encodedPassword, "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", encodedPassword, "케이"); memberRepository.save(member); AuthLoginRequest loginRequest = new AuthLoginRequest(email, password); TokenResponse tokenResponse = authService.login(loginRequest); - assertNotNull(tokenResponse.getToken()); + assertAll( + () -> assertThat(member.getStatus()).isEqualTo(Status.ACTIVE), + () -> assertNotNull(tokenResponse.getToken()), + () -> assertThat(tokenResponse.getUserReportCount()).isEqualTo(0) + ); + } + + @Test + @DisplayName("status가 INACTIVE인 회원이 자체 로그인 시도할 시 예외를 반환한다") + void loginWithInActive() { + String email = "kth990303@naver.com"; + String password = "a1b2c3d4"; + String encodedPassword = passwordEncoder.encode("a1b2c3d4"); + Member member = new Member("kth990303@naver.com", encodedPassword, "케이", null, + Platform.MOCACONG, "111111", Status.INACTIVE); + memberRepository.save(member); + AuthLoginRequest loginRequest = new AuthLoginRequest(email, password); + + assertThrows(InactiveMemberException.class, + () -> authService.login(loginRequest)); } @Test @@ -54,7 +76,7 @@ void loginWithException() { String email = "kth990303@naver.com"; String password = "a1b2c3d4"; String encodedPassword = passwordEncoder.encode(password); - Member member = new Member(email, encodedPassword, "케이", "010-1234-5678"); + Member member = new Member(email, encodedPassword, "케이"); memberRepository.save(member); AuthLoginRequest loginRequest = new AuthLoginRequest(email, "wrongPassword"); @@ -90,7 +112,6 @@ void loginOAuthRegisteredAndMocacongMember() { expected, passwordEncoder.encode("a1b2c3d4"), "케이", - "010-1234-1234", null, Platform.APPLE, platformId @@ -128,4 +149,66 @@ void loginOAuthRegisteredButNotMocacongMember() { () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) ); } + + @Test + @DisplayName("자체 회원가입을 진행한 이메일로 정상적으로 OAuth 로그인을 한다") + void loginOAuthWithMocacongEmail() { + String email = "kth@apple.com"; + String encodedPassword = passwordEncoder.encode("a1b2c3d4"); + Member member = new Member(email, encodedPassword, "케이"); + memberRepository.save(member); + String platformId = "1234321"; + when(appleOAuthUserProvider.getApplePlatformMember(anyString())) + .thenReturn(new OAuthPlatformMemberResponse(platformId, email)); + + OAuthTokenResponse actual = authService.appleOAuthLogin(new AppleLoginRequest("token")); + + assertAll( + () -> assertThat(actual.getToken()).isNotNull(), + () -> assertThat(actual.getEmail()).isEqualTo(email), + () -> assertThat(actual.getIsRegistered()).isFalse(), + () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) + ); + } + + @Test + @DisplayName("OAuth 로그인을 진행한 이메일로 자체 회원가입을 한다") + void signUpWithAppleEmail() { + String email = "kth@apple.com"; + String platformId = "1234321"; + when(appleOAuthUserProvider.getApplePlatformMember(anyString())) + .thenReturn(new OAuthPlatformMemberResponse(platformId, email)); + OAuthTokenResponse response = authService.appleOAuthLogin(new AppleLoginRequest("token")); + String encodedPassword = passwordEncoder.encode("a1b2c3d4"); + + Member member = new Member(email, encodedPassword, "케이"); + memberRepository.save(member); + + assertAll( + () -> assertThat(response.getToken()).isNotNull(), + () -> assertThat(response.getEmail()).isEqualTo(member.getEmail()) + ); + } + + @Test + @DisplayName("status가 INACTIVE인 회원이 OAuth 로그인 시도할 시 예외를 반환한다") + void loginOAuthWithInactive() { + String expected = "kth@apple.com"; + String platformId = "1234321"; + Member member = new Member( + expected, + passwordEncoder.encode("a1b2c3d4"), + "케이", + null, + Platform.APPLE, + platformId, + Status.INACTIVE + ); + memberRepository.save(member); + when(appleOAuthUserProvider.getApplePlatformMember(anyString())) + .thenReturn(new OAuthPlatformMemberResponse(platformId, expected)); + + assertThrows(InactiveMemberException.class, + () -> authService.appleOAuthLogin(new AppleLoginRequest("token"))); + } } diff --git a/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java b/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java index 6199fccb..114a0aa0 100644 --- a/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java @@ -1,5 +1,11 @@ package mocacong.server.service; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import mocacong.server.domain.Cafe; import mocacong.server.domain.Member; import mocacong.server.domain.Review; @@ -10,27 +16,17 @@ import mocacong.server.repository.CafeRepository; import mocacong.server.repository.MemberRepository; import mocacong.server.repository.ReviewRepository; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - @ServiceTest public class CafeConcurrentServiceTest { @Autowired private CafeService cafeService; @Autowired - private MemberService memberService; - @Autowired private CafeRepository cafeRepository; @Autowired private MemberRepository memberRepository; @@ -67,7 +63,7 @@ void saveCafeWithConcurrent() throws InterruptedException { @Test @DisplayName("회원이 한 카페에 동시에 여러 번 평점만 등록 시도해도 한 번만 등록된다") void saveScoreWithConcurrent() throws InterruptedException { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); @@ -80,7 +76,7 @@ void saveScoreWithConcurrent() throws InterruptedException { for (int i = 0; i < 3; i++) { executorService.execute(() -> { try { - cafeService.saveCafeReview(member.getEmail(), cafe.getMapId(), request); + cafeService.saveCafeReview(member.getId(), cafe.getMapId(), request); } catch (AlreadyExistsCafeReview e) { exceptions.add(e); // 중복 예외를 리스트에 추가 } @@ -99,7 +95,7 @@ void saveScoreWithConcurrent() throws InterruptedException { @Test @DisplayName("회원이 한 카페에 동시에 여러 번 리뷰 등록 시도해도 한 번만 등록된다") void saveCafeReviewWithConcurrent() throws InterruptedException { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); @@ -112,7 +108,7 @@ void saveCafeReviewWithConcurrent() throws InterruptedException { for (int i = 0; i < 3; i++) { executorService.execute(() -> { try { - cafeService.saveCafeReview(member.getEmail(), cafe.getMapId(), request); + cafeService.saveCafeReview(member.getId(), cafe.getMapId(), request); } catch (AlreadyExistsCafeReview e) { exceptions.add(e); // 중복 예외를 리스트에 추가 } diff --git a/src/test/java/mocacong/server/service/CafeServiceTest.java b/src/test/java/mocacong/server/service/CafeServiceTest.java index d0d65bf3..6dce840f 100644 --- a/src/test/java/mocacong/server/service/CafeServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeServiceTest.java @@ -1,31 +1,33 @@ package mocacong.server.service; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.List; import mocacong.server.domain.*; import mocacong.server.dto.request.*; import mocacong.server.dto.response.*; import mocacong.server.exception.badrequest.AlreadyExistsCafeReview; import mocacong.server.exception.badrequest.DuplicateCafeException; -import mocacong.server.exception.badrequest.ExceedCafeImagesCountsException; -import mocacong.server.exception.notfound.NotFoundCafeException; +import mocacong.server.exception.badrequest.ExceedCageImagesTotalCountsException; import mocacong.server.exception.notfound.NotFoundCafeImageException; import mocacong.server.exception.notfound.NotFoundReviewException; import mocacong.server.repository.*; import mocacong.server.service.event.DeleteNotUsedImagesEvent; import mocacong.server.support.AwsS3Uploader; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageRequest; +import org.springframework.mock.web.MockMultipartFile; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.List; + 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.assertThrows; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.mock.web.MockMultipartFile; @ServiceTest class CafeServiceTest { @@ -46,8 +48,6 @@ class CafeServiceTest { private FavoriteRepository favoriteRepository; @Autowired private CafeImageRepository cafeImageRepository; - @Autowired - private ReviewRepository reviewRepository; @MockBean private AwsS3Uploader awsS3Uploader; @@ -81,12 +81,12 @@ void cafeSaveDuplicate() { @Test @DisplayName("평점, 리뷰 및 코멘트가 존재하지 않는 카페를 조회한다") void findCafe() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - FindCafeResponse actual = cafeService.findCafeByMapId(member.getEmail(), cafe.getMapId()); + FindCafeResponse actual = cafeService.findCafeByMapId(member.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isFalse(), @@ -104,9 +104,9 @@ void findCafe() { @Test @DisplayName("평점이 존재하고 리뷰, 코멘트가 존재하지 않는 카페를 조회한다") void findCafeWithScore() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); @@ -115,7 +115,7 @@ void findCafeWithScore() { Score score2 = new Score(5, member2, cafe); scoreRepository.save(score2); - FindCafeResponse actual = cafeService.findCafeByMapId(member1.getEmail(), cafe.getMapId()); + FindCafeResponse actual = cafeService.findCafeByMapId(member1.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isFalse(), @@ -132,16 +132,16 @@ void findCafeWithScore() { @Test @DisplayName("평점, 리뷰, 코멘트가 모두 존재하는 카페를 조회한다") void findCafeWithReviewsAndComments() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "both", "느려요", "없어요", "깨끗해요", "없어요", null, "보통이에요")); Comment comment1 = new Comment(cafe, member1, "이 카페 조금 아쉬운 점이 많아요 ㅠㅠ"); @@ -151,7 +151,7 @@ void findCafeWithReviewsAndComments() { Comment comment3 = new Comment(cafe, member1, "다시 와봐도 똑같네요. 리뷰 수정할까 하다가 그대로 남겨요.."); commentRepository.save(comment3); - FindCafeResponse actual = cafeService.findCafeByMapId(member1.getEmail(), cafe.getMapId()); + FindCafeResponse actual = cafeService.findCafeByMapId(member1.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isFalse(), @@ -170,12 +170,12 @@ void findCafeWithReviewsAndComments() { @Test @DisplayName("평점, 리뷰가 존재하지 않는 카페 정보를 미리보기한다") void previewCafe() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - PreviewCafeResponse actual = cafeService.previewCafeByMapId(member.getEmail(), cafe.getMapId()); + PreviewCafeResponse actual = cafeService.previewCafeByMapId(member.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isFalse(), @@ -188,9 +188,9 @@ void previewCafe() { @Test @DisplayName("평점이 존재하고 리뷰가 존재하지 않는 카페 정보를 미리보기한다") void previewCafeWithScore() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); @@ -198,7 +198,7 @@ void previewCafeWithScore() { scoreRepository.save(new Score(5, member2, cafe)); favoriteRepository.save(new Favorite(member1, cafe)); - PreviewCafeResponse actual = cafeService.previewCafeByMapId(member1.getEmail(), cafe.getMapId()); + PreviewCafeResponse actual = cafeService.previewCafeByMapId(member1.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isTrue(), @@ -211,21 +211,21 @@ void previewCafeWithScore() { @Test @DisplayName("평점과 리뷰가 모두 존재하는 카페 정보를 미리보기한다") void previewCafeWithScoreAndReview() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(3, "group", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "both", "느려요", "없어요", "깨끗해요", "없어요", null, "보통이에요")); favoriteRepository.save(new Favorite(member1, cafe)); - PreviewCafeResponse actual = cafeService.previewCafeByMapId(member1.getEmail(), cafe.getMapId()); + PreviewCafeResponse actual = cafeService.previewCafeByMapId(member1.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isTrue(), @@ -238,7 +238,7 @@ void previewCafeWithScoreAndReview() { @Test @DisplayName("카페를 조회할 때 댓글은 3개까지만 보여준다") void findCafeAndShowLimitComments() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); @@ -251,7 +251,7 @@ void findCafeAndShowLimitComments() { Comment comment4 = new Comment(cafe, member, "댓글4"); commentRepository.save(comment4); - FindCafeResponse actual = cafeService.findCafeByMapId(member.getEmail(), cafe.getMapId()); + FindCafeResponse actual = cafeService.findCafeByMapId(member.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getCommentsCount()).isEqualTo(4), @@ -262,20 +262,21 @@ void findCafeAndShowLimitComments() { @Test @DisplayName("탈퇴한 회원이 작성한 리뷰와 코멘트가 존재하는 카페를 조회한다") void findCafeWithReviewsAndCommentsByDeleteMember() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, + "group", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); Comment comment = new Comment(cafe, member1, "이 카페 조금 아쉬운 점이 많아요 ㅠㅠ"); commentRepository.save(comment); - memberService.delete(member1.getEmail()); + memberService.delete(member1.getId()); memberRepository.delete(member1); - FindCafeResponse actual = cafeService.findCafeByMapId(member2.getEmail(), cafe.getMapId()); + FindCafeResponse actual = cafeService.findCafeByMapId(member2.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isFalse(), @@ -292,16 +293,16 @@ void findCafeWithReviewsAndCommentsByDeleteMember() { @Test @DisplayName("평점, 리뷰, 코멘트가 존재하고 즐겨찾기가 등록된 카페를 조회한다") void findCafeWithAll() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "group", "느려요", "없어요", "깨끗해요", "없어요", null, "보통이에요")); Comment comment1 = new Comment(cafe, member1, "이 카페 조금 아쉬운 점이 많아요 ㅠㅠ"); @@ -313,7 +314,7 @@ void findCafeWithAll() { Favorite favorite = new Favorite(member1, cafe); favoriteRepository.save(favorite); - FindCafeResponse actual = cafeService.findCafeByMapId(member1.getEmail(), cafe.getMapId()); + FindCafeResponse actual = cafeService.findCafeByMapId(member1.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getFavorite()).isTrue(), @@ -333,23 +334,28 @@ void findCafeWithAll() { @DisplayName("카페를 조회할 때 이미지는 5개까지만 보여준다") void findCafeAndShowLimitImages() throws IOException { String expected = "test_img.jpg"; - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); + Member member2 = new Member("rlawjddn103@naver.com", "encodePassword", "베어"); + memberRepository.save(member); + memberRepository.save(member2); + Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); - MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); + MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", + fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); - FindCafeResponse actual = cafeService.findCafeByMapId(member.getEmail(), mapId); - CafeImagesResponse actual2 = cafeService.findCafeImages(member.getEmail(), mapId, 0, 10); + FindCafeResponse actual = cafeService.findCafeByMapId(member.getId(), mapId); + CafeImagesResponse actual2 = cafeService.findCafeImages(member.getId(), mapId, 0, 10); assertAll( () -> assertThat(actual.getCafeImages()).hasSize(5), @@ -360,22 +366,22 @@ void findCafeAndShowLimitImages() throws IOException { @Test @DisplayName("회원이 즐겨찾기한 카페 목록들을 보여준다") void findMyFavoriteCafes() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "group", "느려요", "없어요", "깨끗해요", "없어요", null, "보통이에요")); Favorite favorite = new Favorite(member1, cafe); favoriteRepository.save(favorite); - MyFavoriteCafesResponse actual = cafeService.findMyFavoriteCafes(member1.getEmail(), 0, 3); + MyFavoriteCafesResponse actual = cafeService.findMyFavoriteCafes(member1.getId(), 0, 3); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), @@ -386,31 +392,33 @@ void findMyFavoriteCafes() { @Test @DisplayName("회원이 리뷰를 남긴 카페 목록들을 보여준다") void findMyReviewCafes() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe1 = new Cafe("2143154352323", "케이카페"); Cafe cafe2 = new Cafe("2154122541112", "메리카페"); cafeRepository.save(cafe1); cafeRepository.save(cafe2); - cafeService.saveCafeReview(member1.getEmail(), cafe1.getMapId(), - new CafeReviewRequest(1, "group", "느려요", "없어요", + cafeService.saveCafeReview(member1.getId(), cafe1.getMapId(), + new CafeReviewRequest(1, "solo", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); - cafeService.saveCafeReview(member1.getEmail(), cafe2.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe2.getMapId(), new CafeReviewRequest(5, "group", "느려요", "없어요", "불편해요", "없어요", "북적북적해요", "불편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe1.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe1.getMapId(), new CafeReviewRequest(2, "group", "느려요", "없어요", "깨끗해요", "없어요", null, "보통이에요")); - MyReviewCafesResponse actual = cafeService.findMyReviewCafes(member1.getEmail(), 0, 3); + MyReviewCafesResponse actual = cafeService.findMyReviewCafes(member1.getId(), 0, 3); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), () -> assertThat(actual.getCafes().get(0).getMyScore()).isEqualTo(1), + () -> assertThat(actual.getCafes().get(0).getMyStudyType()).isEqualTo("solo"), () -> assertThat(actual.getCafes().get(0).getName()).isEqualTo("케이카페"), () -> assertThat(actual.getCafes().get(1).getMyScore()).isEqualTo(5), + () -> assertThat(actual.getCafes().get(1).getMyStudyType()).isEqualTo("group"), () -> assertThat(actual.getCafes().get(1).getName()).isEqualTo("메리카페"), () -> assertThat(actual.getCafes()).hasSize(2) ); @@ -419,9 +427,9 @@ void findMyReviewCafes() { @Test @DisplayName("회원이 댓글을 작성한 카페 목록들을 보여준다") void findMyCommentCafes() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe1 = new Cafe("2143154352323", "케이카페"); Cafe cafe2 = new Cafe("1212121212121", "메리카페"); @@ -432,7 +440,7 @@ void findMyCommentCafes() { commentRepository.save(new Comment(cafe2, member1, "댓글3")); commentRepository.save(new Comment(cafe2, member2, "댓글4")); - MyCommentCafesResponse actual = cafeService.findMyCommentCafes(member1.getEmail(), 0, 5); + MyCommentCafesResponse actual = cafeService.findMyCommentCafes(member1.getId(), 0, 5); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), @@ -446,17 +454,17 @@ void findMyCommentCafes() { @Test @DisplayName("카페에 대한 리뷰를 작성하면 해당 카페 평점과 세부정보가 갱신된다") void saveCafeReview() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - CafeReviewResponse actual = cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + CafeReviewResponse actual = cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "solo", "빵빵해요", "협소해요", "깨끗해요", "충분해요", "적당해요", "편해요")); @@ -477,17 +485,17 @@ void saveCafeReview() { @Test @DisplayName("카페 리뷰 작성 후 studyType의 타입 개수가 동일하면 both를 반환한다") void saveCafeAndStudyTypesEquals() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - CafeReviewResponse actual = cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + CafeReviewResponse actual = cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "group", "빵빵해요", "협소해요", "깨끗해요", "충분해요", "적당해요", "편해요")); @@ -497,20 +505,20 @@ void saveCafeAndStudyTypesEquals() { @Test @DisplayName("특정 카페에 내가 작성한 리뷰를 볼 수 있다") void findMyCafeReview() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), new CafeReviewRequest(2, "group", "적당해요", "협소해요", "불편해요", "없어요", "적당해요", "불편해요")); - CafeMyReviewResponse actual = cafeService.findMyCafeReview(member1.getEmail(), cafe.getMapId()); + CafeMyReviewResponse actual = cafeService.findMyCafeReview(member1.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getMyScore()).isEqualTo(4), @@ -527,12 +535,12 @@ void findMyCafeReview() { @Test @DisplayName("작성하지 않은 카페에 대한 리뷰를 조회하는 경우 null을 반환한다") void findNotRegisteredCafeReview() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - CafeMyReviewResponse actual = cafeService.findMyCafeReview(member.getEmail(), cafe.getMapId()); + CafeMyReviewResponse actual = cafeService.findMyCafeReview(member.getId(), cafe.getMapId()); assertAll( () -> assertThat(actual.getMyScore()).isNull(), @@ -549,17 +557,17 @@ void findNotRegisteredCafeReview() { @Test @DisplayName("이미 리뷰를 작성했으면 수정만 가능하고 새로 작성은 불가능하다") void cannotSaveManyReviews() { - Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - assertThatThrownBy(() -> cafeService.saveCafeReview(member1.getEmail(), cafe.getMapId(), + assertThatThrownBy(() -> cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(2, "group", "빵빵해요", "협소해요", "깨끗해요", "충분해요", "적당해요", "편해요"))) .isInstanceOf(AlreadyExistsCafeReview.class); @@ -568,15 +576,15 @@ void cannotSaveManyReviews() { @Test @DisplayName("등록한 카페 리뷰를 성공적으로 수정한다") public void updateCafeReview() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - CafeReviewUpdateResponse actual = cafeService.updateCafeReview(member.getEmail(), cafe.getMapId(), + CafeReviewUpdateResponse actual = cafeService.updateCafeReview(member.getId(), cafe.getMapId(), new CafeReviewUpdateRequest(5, "group", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "불편해요")); @@ -593,65 +601,98 @@ public void updateCafeReview() { ); } + @Test + @DisplayName("등록한 카페 리뷰를 수정할 때 세부정보를 모두 null 값으로 성공적으로 수정한다") + public void updateCafeReviewWhenDetailsNull() { + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member); + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + cafeService.saveCafeReview(member.getId(), cafe.getMapId(), + new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", + "깨끗해요", "충분해요", "조용해요", "편해요")); + + CafeReviewUpdateResponse actual = cafeService.updateCafeReview(member.getId(), cafe.getMapId(), + new CafeReviewUpdateRequest(5, "group", null, null, + null, null, null, null)); + + assertAll( + () -> assertThat(actual.getScore()).isEqualTo(5.0), + () -> assertThat(actual.getStudyType()).isEqualTo("group"), + () -> assertThat(actual.getWifi()).isNull(), + () -> assertThat(actual.getParking()).isNull(), + () -> assertThat(actual.getToilet()).isNull(), + () -> assertThat(actual.getPower()).isNull(), + () -> assertThat(actual.getSound()).isNull(), + () -> assertThat(actual.getDesk()).isNull(), + () -> assertThat(actual.getReviewsCount()).isEqualTo(1) + ); + } + @Test @DisplayName("카페 리뷰를 등록한 적이 없다면 리뷰 수정은 불가능하다") public void updateCafeReviewNotFoundReview() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - assertThatThrownBy(() -> cafeService.updateCafeReview(member.getEmail(), cafe.getMapId(), new CafeReviewUpdateRequest(5, - "solo", "빵빵해요", "여유로워요", - "깨끗해요", "충분해요", "조용해요", "불편해요"))) + assertThatThrownBy(() -> cafeService.updateCafeReview(member.getId(), cafe.getMapId(), + new CafeReviewUpdateRequest(5, "solo", "빵빵해요", "여유로워요", + "깨끗해요", "충분해요", "조용해요", "불편해요"))) .isInstanceOf(NotFoundReviewException.class); } @Test @DisplayName("studyTypeValue가 주어진 경우 해당 카페 목록을 필터링한다") void getCafesFilterStudyType() { - Member member1 = new Member("dlawotn3@naver.com", "encodePassword", "메리", "010-1234-5678"); - Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member("dlawotn3@naver.com", "encodePassword", "메리"); + Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member1); memberRepository.save(member2); Cafe cafe1 = new Cafe("2143154352323", "케이카페"); Cafe cafe2 = new Cafe("2143154311111", "메리카페"); Cafe cafe3 = new Cafe("2111111125885", "메리카페 2호점"); Cafe cafe4 = new Cafe("1585656565441", "메리벅스"); + Cafe cafe5 = new Cafe("1582212121441", "메리설빙"); cafeRepository.save(cafe1); cafeRepository.save(cafe2); cafeRepository.save(cafe3); cafeRepository.save(cafe4); - cafeService.saveCafeReview(member1.getEmail(), cafe1.getMapId(), + cafeRepository.save(cafe5); + cafeService.saveCafeReview(member1.getId(), cafe1.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - cafeService.saveCafeReview(member1.getEmail(), cafe2.getMapId(), + cafeService.saveCafeReview(member1.getId(), cafe2.getMapId(), new CafeReviewRequest(2, "group", "빵빵해요", "여유로워요", "깨끗해요", "없어요", "조용해요", "편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe3.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe3.getMapId(), new CafeReviewRequest(5, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); - cafeService.saveCafeReview(member2.getEmail(), cafe4.getMapId(), + cafeService.saveCafeReview(member2.getId(), cafe4.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); + cafeService.saveCafeReview(member2.getId(), cafe5.getMapId(), + new CafeReviewRequest(4, "both", "빵빵해요", "여유로워요", + "깨끗해요", "충분해요", "조용해요", "편해요")); CafeFilterStudyTypeRequest requestBody = new CafeFilterStudyTypeRequest( - List.of(cafe1.getMapId(), cafe2.getMapId(), cafe3.getMapId(), cafe4.getMapId()) + List.of(cafe1.getMapId(), cafe2.getMapId(), cafe3.getMapId(), cafe4.getMapId(), cafe5.getMapId()) ); CafeFilterStudyTypeResponse filteredCafes = cafeService.filterCafesByStudyType("solo", requestBody); assertThat(filteredCafes.getMapIds()) - .containsExactlyInAnyOrder(cafe1.getMapId(), cafe3.getMapId(), cafe4.getMapId()); + .containsExactlyInAnyOrder(cafe1.getMapId(), cafe3.getMapId(), cafe4.getMapId(), cafe5.getMapId()); } @Test @DisplayName("studyTypeValue에 해당하는 카페가 없는 경우 빈 리스트를 반환한다") void getCafesFilterStudyTypeWhenNoMatch() { - Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리", "010-1234-5678"); + Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - cafeService.saveCafeReview(member.getEmail(), cafe.getMapId(), + cafeService.saveCafeReview(member.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); CafeFilterStudyTypeRequest requestBody = new CafeFilterStudyTypeRequest(List.of(cafe.getMapId())); @@ -664,7 +705,7 @@ void getCafesFilterStudyTypeWhenNoMatch() { @Test @DisplayName("즐겨찾기가 등록된 카페가 있는 경우 해당 카페 목록을 필터링한다") void getCafesFilterFavorites() { - Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리", "010-1234-5678"); + Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member); Cafe cafe1 = new Cafe("2143154352323", "케이카페"); Cafe cafe2 = new Cafe("2143154311111", "메리카페"); @@ -680,7 +721,7 @@ void getCafesFilterFavorites() { List.of(cafe1.getMapId(), cafe2.getMapId(), cafe3.getMapId(), cafe4.getMapId()) ); - CafeFilterFavoritesResponse filteredCafes = cafeService.filterCafesByFavorites(member.getEmail(), requestBody); + CafeFilterFavoritesResponse filteredCafes = cafeService.filterCafesByFavorites(member.getId(), requestBody); assertThat(filteredCafes.getMapIds()) .containsExactlyInAnyOrder(cafe1.getMapId(), cafe2.getMapId()); @@ -689,14 +730,14 @@ void getCafesFilterFavorites() { @Test @DisplayName("즐겨찾기가 등록된 카페가 없는 경우 빈 리스트를 반환한다") void getCafesFilterFavoritesWhenNoMatch() { - Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리", "010-1234-5678"); + Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); CafeFilterFavoritesRequest requestBody = new CafeFilterFavoritesRequest(List.of(cafe.getMapId())); - CafeFilterFavoritesResponse filteredCafes = cafeService.filterCafesByFavorites(member.getEmail(), requestBody); + CafeFilterFavoritesResponse filteredCafes = cafeService.filterCafesByFavorites(member.getId(), requestBody); assertThat(filteredCafes.getMapIds()).isEmpty(); } @@ -707,42 +748,70 @@ void saveCafeImage() throws IOException { String expected = "test_img.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); - MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); + MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", + fileInputStream); + + when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + + List actual = cafeImageRepository + .findAllByCafeIdAndIsUsedOrderByCafeImageId(cafe.getId(), member.getId(), PageRequest.of(0, 5)) + .getContent(); + assertAll( + () -> assertThat(actual).hasSize(1), + () -> assertThat(actual.get(0).getImgUrl()).isEqualTo(expected) + ); + } + + @Test + @DisplayName("카페 이미지를 저장한 후 Response를 반환한다.") + void saveCafeImageWithResponse() throws IOException { + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); + memberRepository.save(member); + String mapId = cafe.getMapId(); + FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + "test_img.jpg"); + MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", "test_img.jpg", "jpg", + fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); + CafeImagesSaveResponse cafeImagesSaveResponse = cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); - Cafe actual = cafeRepository.findByMapId(mapId).orElseThrow(NotFoundCafeException::new); assertAll( - () -> assertThat(actual.getCafeImages()).hasSize(1), - () -> assertThat(actual.getCafeImages().get(0).getImgUrl()).isEqualTo(expected) + () -> assertThat(cafeImagesSaveResponse.getCafeImagesIds()).hasSize(1), + () -> assertThat(cafeImagesSaveResponse.getCafeImagesIds().get(0).getId()).isEqualTo(1L) ); } @Test - @DisplayName("카페 이미지를 한 번에 여러 개 저장할 수 있다") + @DisplayName("카페 이미지를 한 번에 3개까지 저장할 수 있다") void saveCafeImagesPerRequest() throws IOException { Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("kth990303@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("kth990303@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); MockMultipartFile mockMultipartFile1 = new MockMultipartFile("test_img", "test_img.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img.jpg")); MockMultipartFile mockMultipartFile2 = new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img2.jpg")); - + MockMultipartFile mockMultipartFile3 = + new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img2.jpg")); when(awsS3Uploader.uploadImage(mockMultipartFile1)).thenReturn("test_img.jpg"); when(awsS3Uploader.uploadImage(mockMultipartFile2)).thenReturn("test_img2.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile1, mockMultipartFile2)); + when(awsS3Uploader.uploadImage(mockMultipartFile3)).thenReturn("test_img2.jpg"); + + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile1, mockMultipartFile2, mockMultipartFile3)); - Cafe actual = cafeRepository.findByMapId(mapId) - .orElseThrow(); - assertThat(actual.getCafeImages()).hasSize(2); + List actual = cafeImageRepository + .findAllByCafeIdAndIsUsedOrderByCafeImageId(cafe.getId(), member.getId(), PageRequest.of(0, 5)) + .getContent(); + assertThat(actual).hasSize(3); } @Test @@ -750,23 +819,27 @@ void saveCafeImagesPerRequest() throws IOException { void saveCafeImagesManyPerRequest() throws IOException { Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("kth990303@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("kth990303@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); MockMultipartFile mockMultipartFile1 = - new MockMultipartFile("test_img", "test_img.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img.jpg")); + new MockMultipartFile("test_img", "test_img.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img.jpg")); MockMultipartFile mockMultipartFile2 = - new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img2.jpg")); + new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img2.jpg")); MockMultipartFile mockMultipartFile3 = - new MockMultipartFile("test_img", "test_img.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img.jpg")); + new MockMultipartFile("test_img", "test_img.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img.jpg")); MockMultipartFile mockMultipartFile4 = - new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", new FileInputStream("src/test/resources/images/test_img2.jpg")); + new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img2.jpg")); assertThatThrownBy(() -> cafeService.saveCafeImage( - member.getEmail(), + member.getId(), mapId, List.of(mockMultipartFile1, mockMultipartFile2, mockMultipartFile3, mockMultipartFile4) - )).isInstanceOf(ExceedCafeImagesCountsException.class); + )).isInstanceOf(ExceedCageImagesTotalCountsException.class); } @Test @@ -775,41 +848,81 @@ void saveCafeImages() throws IOException { String expected = "test_img.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + + List actual = cafeImageRepository + .findAllByCafeIdAndIsUsedOrderByCafeImageId(cafe.getId(), member.getId(), PageRequest.of(0, 5)) + .getContent(); - Cafe actual = cafeRepository.findByMapId(mapId).orElseThrow(NotFoundCafeException::new); assertAll( - () -> assertThat(actual.getCafeImages()).hasSize(2), - () -> assertThat(actual.getCafeImages().get(0).getImgUrl()).isEqualTo(expected), - () -> assertThat(actual.getCafeImages().get(1).getImgUrl()).isEqualTo(expected) + () -> assertThat(actual).hasSize(2), + () -> assertThat(actual.get(0).getImgUrl()).isEqualTo(expected), + () -> assertThat(actual.get(1).getImgUrl()).isEqualTo(expected) + ); + } + + + @Test + @DisplayName("사용자가 카페 이미지를 총 3개 보다 많이 저장하면 예외가 발생한다.") + void saveCafeImagesOver3() throws IOException { + Cafe cafe = new Cafe("2143154352323", "베어카페"); + cafeRepository.save(cafe); + Member member = new Member("rlawjddn103@naver.com", "a1b2c3d4", "베어", null); + memberRepository.save(member); + String mapId = cafe.getMapId(); + MockMultipartFile mockMultipartFile1 = + new MockMultipartFile("test_img", "test_img.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img.jpg")); + MockMultipartFile mockMultipartFile2 = + new MockMultipartFile("test_img2", "test_img2.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img2.jpg")); + MockMultipartFile mockMultipartFile3 = + new MockMultipartFile("test_img", "test_img.jpg", "jpg", + new FileInputStream("src/test/resources/images/test_img.jpg")); + + when(awsS3Uploader.uploadImage(mockMultipartFile1)).thenReturn("test_img.jpg"); + when(awsS3Uploader.uploadImage(mockMultipartFile2)).thenReturn("test_img2.jpg"); + when(awsS3Uploader.uploadImage(mockMultipartFile3)).thenReturn("test_img.jpg"); + + cafeService.saveCafeImage( + member.getId(), + mapId, + List.of(mockMultipartFile1, mockMultipartFile2, mockMultipartFile3) ); + + assertThatThrownBy(() -> cafeService.saveCafeImage( + member.getId(), + mapId, + List.of(mockMultipartFile1) + )).isInstanceOf(ExceedCageImagesTotalCountsException.class); } + @Test @DisplayName("사용자가 카페 이미지를 조회한다") void findCafeImages() throws IOException { String expected = "test_img.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); - CafeImagesResponse actual = cafeService.findCafeImages("dlawotn3@naver.com", mapId, 0, 3); + CafeImagesResponse actual = cafeService.findCafeImages(member.getId(), mapId, 0, 3); List cafeImages = actual.getCafeImages(); @@ -825,15 +938,15 @@ void findCafeMyImagesReturnTrue() throws IOException { String expected = "test_img.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(mockMultipartFile)); - CafeImagesResponse actual = cafeService.findCafeImages(member.getEmail(), mapId, 0, 10); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(mockMultipartFile)); + CafeImagesResponse actual = cafeService.findCafeImages(member.getId(), mapId, 0, 10); List cafeImages = actual.getCafeImages(); @@ -851,31 +964,102 @@ void findOtherCafeImagesReturnFalse() throws IOException { String expected = "test_img.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member1); - Member member2 = new Member("kth990303@naver.com", "a1b2c3d4", "다른사람", "010-1111-2222", null); + Member member2 = new Member("kth990303@naver.com", "a1b2c3d4", "다른사람", null); memberRepository.save(member2); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member1.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member1.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member1.getEmail(), mapId, List.of(mockMultipartFile)); - cafeService.saveCafeImage(member1.getEmail(), mapId, List.of(mockMultipartFile)); - CafeImagesResponse actual = cafeService.findCafeImages(member2.getEmail(), mapId, 0, 10); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + CafeImagesResponse actual = cafeService.findCafeImages(member2.getId(), mapId, 0, 10); List cafeImages = actual.getCafeImages(); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), - () -> assertThat(cafeImages).hasSize(4), + () -> assertThat(cafeImages).hasSize(3), () -> assertThat(cafeImages.get(0).getIsMe()).isFalse(), () -> assertThat(cafeImages.get(1).getIsMe()).isFalse() ); } + @Test + @DisplayName("자신이 등록한 이미지부터 등록 순으로 이미지를 조회한다") + void findCafeImagesReturnOrderedImages() throws IOException { + String expected = "test_img.jpg"; + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); + memberRepository.save(member1); + Member member2 = new Member("kth990303@naver.com", "a1b2c3d4", "다른사람", null); + memberRepository.save(member2); + String mapId = cafe.getMapId(); + FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); + MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", + fileInputStream); + when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); + CafeImagesResponse actual = cafeService.findCafeImages(member2.getId(), mapId, 0, 10); + + List cafeImages = actual.getCafeImages(); + + assertAll( + () -> assertThat(actual.getIsEnd()).isTrue(), + () -> assertThat(cafeImages).hasSize(4), + // 등록 순으로 정렬되었는지 검증 + () -> assertThat(cafeImages.get(0).getId()).isEqualTo(3), + () -> assertThat(cafeImages.get(1).getId()).isEqualTo(4), + () -> assertThat(cafeImages.get(2).getId()).isEqualTo(1), + () -> assertThat(cafeImages.get(3).getId()).isEqualTo(2), + () -> assertThat(cafeImages.get(0).getIsMe()).isTrue(), + () -> assertThat(cafeImages.get(2).getIsMe()).isFalse() + ); + } + + @Test + @DisplayName("타인이 나중에 이미지를 등록해도 자신이 등록한 이미지부터 등록 순으로 조회한다") + void findCafeImagesReturnOrderedMyImages() throws IOException { + String expected = "test_img.jpg"; + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); + memberRepository.save(member1); + Member member2 = new Member("kth990303@naver.com", "a1b2c3d4", "다른사람", null); + memberRepository.save(member2); + String mapId = cafe.getMapId(); + FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); + MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", + fileInputStream); + when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(mockMultipartFile)); + cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + CafeImagesResponse actual = cafeService.findCafeImages(member2.getId(), mapId, 0, 10); + + List cafeImages = actual.getCafeImages(); + + assertAll( + () -> assertThat(actual.getIsEnd()).isTrue(), + () -> assertThat(cafeImages).hasSize(4), + // 자신이 올린 사진부터 등록 순으로 정렬되었는지 검증 + () -> assertThat(cafeImages.get(0).getId()).isEqualTo(1), + () -> assertThat(cafeImages.get(1).getId()).isEqualTo(3), + () -> assertThat(cafeImages.get(2).getId()).isEqualTo(2), + () -> assertThat(cafeImages.get(3).getId()).isEqualTo(4), + () -> assertThat(cafeImages.get(0).getIsMe()).isTrue(), + () -> assertThat(cafeImages.get(2).getIsMe()).isFalse() + ); + } + @Test @DisplayName("카페 이미지를 성공적으로 수정한다") void updateCafeImage() throws IOException { @@ -883,25 +1067,30 @@ void updateCafeImage() throws IOException { String newImage = "test_img2.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); FileInputStream oldFileInputStream = new FileInputStream("src/test/resources/images/" + oldImage); FileInputStream newFileInputStream = new FileInputStream("src/test/resources/images/" + newImage); - MockMultipartFile oldMockMultipartFile = new MockMultipartFile("test_img", oldImage, "jpg", oldFileInputStream); - MockMultipartFile newMockMultipartFile = new MockMultipartFile("test_img2", newImage, "jpg", newFileInputStream); + MockMultipartFile oldMockMultipartFile = + new MockMultipartFile("test_img", oldImage, "jpg", oldFileInputStream); + MockMultipartFile newMockMultipartFile = + new MockMultipartFile("test_img2", newImage, "jpg", newFileInputStream); when(awsS3Uploader.uploadImage(oldMockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - CafeImagesResponse oldFindImage = cafeService.findCafeImages(member.getEmail(), mapId, 0, 10); + cafeService.saveCafeImage(member.getId(), mapId, List.of(oldMockMultipartFile)); + CafeImagesResponse oldFindImage = cafeService.findCafeImages(member.getId(), mapId, 0, 10); when(awsS3Uploader.uploadImage(newMockMultipartFile)).thenReturn("test_img2.jpg"); - cafeService.updateCafeImage(member.getEmail(), mapId, oldFindImage.getCafeImages().get(0).getId(), newMockMultipartFile); - CafeImagesResponse actual = cafeService.findCafeImages(member.getEmail(), mapId, 0, 10); - List cafeImages = actual.getCafeImages(); + cafeService.updateCafeImage(member.getId(), mapId, oldFindImage.getCafeImages().get(0).getId(), + newMockMultipartFile); + CafeImagesResponse actual = cafeService.findCafeImages(member.getId(), mapId, 0, 10); + List cafeImages = actual.getCafeImages(); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), - () -> assertThat(actual.getCafeImages().get(0).getId()).isNotEqualTo(oldFindImage.getCafeImages().get(0).getId()), + // 수정 이미지의 url 자체만 변경되고 id는 동일할 것 + () -> assertThat(actual.getCafeImages().get(0).getId()) + .isEqualTo(oldFindImage.getCafeImages().get(0).getId()), () -> assertThat(cafeImages.get(0).getImageUrl()).endsWith("test_img2.jpg"), () -> assertThat(cafeImages).hasSize(1) ); @@ -913,62 +1102,94 @@ void updateCafeImageNotFoundImage() throws IOException { String newImage = "test_img.jpg"; Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678", null); + Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); String mapId = cafe.getMapId(); FileInputStream newFileInputStream = new FileInputStream("src/test/resources/images/" + newImage); - MockMultipartFile newMockMultipartFile = new MockMultipartFile("test_img2", newImage, "jpg", newFileInputStream); + MockMultipartFile newMockMultipartFile = new MockMultipartFile("test_img2", newImage, "jpg", + newFileInputStream); when(awsS3Uploader.uploadImage(newMockMultipartFile)).thenReturn("test_img2.jpg"); - assertThatThrownBy(() -> cafeService.updateCafeImage(member.getEmail(), mapId, 9999L, + assertThatThrownBy(() -> cafeService.updateCafeImage(member.getId(), mapId, 9999L, newMockMultipartFile)).isInstanceOf(NotFoundCafeImageException.class); } @Test - @DisplayName("카페 이미지를 수정한 후 조회할 때 isTrue인 이미지를 5개까지만 보여준다") + @DisplayName("카페 이미지를 수정한 후 조회할 때 isTrue인 이미지를 우선하여 먼저 등록된 이미지들부터 5개까지 보여준다.") void updateCafeImageAndShowLimitImages() throws IOException { String oldImage = "test_img.jpg"; String newImage = "test_img2.jpg"; - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); + Member member2 = new Member("rlawjddn103@naver.com", "encodePassword", "베어"); + memberRepository.save(member); + memberRepository.save(member2); + Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); String mapId = cafe.getMapId(); FileInputStream oldFileInputStream = new FileInputStream("src/test/resources/images/" + oldImage); FileInputStream newFileInputStream = new FileInputStream("src/test/resources/images/" + newImage); - MockMultipartFile oldMockMultipartFile = new MockMultipartFile("test_img", oldImage, "jpg", oldFileInputStream); - MockMultipartFile newMockMultipartFile = new MockMultipartFile("test_img2", newImage, "jpg", newFileInputStream); + MockMultipartFile oldMockMultipartFile = + new MockMultipartFile("test_img", oldImage, "jpg", oldFileInputStream); + MockMultipartFile newMockMultipartFile = + new MockMultipartFile("test_img2", newImage, "jpg", newFileInputStream); when(awsS3Uploader.uploadImage(oldMockMultipartFile)).thenReturn("test_img.jpg"); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - CafeImagesResponse oldFindImage = cafeService.findCafeImages(member.getEmail(), mapId, 0, 10); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(oldMockMultipartFile)); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(oldMockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(oldMockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(oldMockMultipartFile)); + cafeService.saveCafeImage(member.getId(), mapId, List.of(oldMockMultipartFile)); + + CafeImagesResponse oldFindImage = cafeService.findCafeImages(member.getId(), mapId, 0, 10); when(awsS3Uploader.uploadImage(newMockMultipartFile)).thenReturn("test_img2.jpg"); - cafeService.updateCafeImage(member.getEmail(), mapId, oldFindImage.getCafeImages().get(0).getId(), newMockMultipartFile); - cafeService.saveCafeImage(member.getEmail(), mapId, List.of(oldMockMultipartFile)); - FindCafeResponse actual = cafeService.findCafeByMapId(member.getEmail(), mapId); - CafeImagesResponse given = cafeService.findCafeImages(member.getEmail(), mapId, 0, 10); + cafeService.updateCafeImage( + member.getId(), mapId, oldFindImage.getCafeImages().get(0).getId(), newMockMultipartFile + ); + cafeService.saveCafeImage(member2.getId(), mapId, List.of(oldMockMultipartFile)); + FindCafeResponse actual = cafeService.findCafeByMapId(member.getId(), mapId); + CafeImagesResponse given = cafeService.findCafeImages(member.getId(), mapId, 0, 10); assertAll( () -> assertThat(actual.getCafeImages()).hasSize(5), () -> assertThat(actual.getCafeImages().get(0).getIsMe()).isEqualTo(true), () -> assertThat(actual.getCafeImages().get(1).getIsMe()).isEqualTo(true), () -> assertThat(actual.getCafeImages().get(2).getIsMe()).isEqualTo(true), - () -> assertThat(actual.getCafeImages().get(3).getIsMe()).isEqualTo(true), - () -> assertThat(actual.getCafeImages().get(4).getIsMe()).isEqualTo(true), - () -> assertThat(actual.getCafeImages().get(4).getImageUrl()).endsWith("test_img2.jpg"), + () -> assertThat(actual.getCafeImages().get(3).getIsMe()).isEqualTo(false), + () -> assertThat(actual.getCafeImages().get(4).getIsMe()).isEqualTo(false), + () -> assertThat(actual.getCafeImages().get(0).getImageUrl()).endsWith("test_img2.jpg"), () -> assertThat(given.getCafeImages()).hasSize(6) ); } + @Test + @DisplayName("탈퇴한 회원의 카페 이미지는 삭제되지 않고 이미지 작성자를 null 처리한다") + void cafeImagesWhenMemberDelete() throws IOException { + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member); + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/test_img.jpg"); + MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", "test_img.jpg", + "jpg", fileInputStream); + when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); + cafeService.saveCafeImage(member.getId(), cafe.getMapId(), List.of(mockMultipartFile)); + + memberService.delete(member.getId()); + List actual = cafeImageRepository.findAll(); + + assertAll( + () -> assertThat(actual).hasSize(1), + () -> assertThat(actual.get(0).getMember()).isNull() + ); + } + @Test @DisplayName("사용하지 않는 카페 이미지들을 삭제한다") void deleteNotUsedCafeImages() { List notUsedImgUrls = List.of("test_img2.jpg", "test_img3.jpg"); - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); diff --git a/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java b/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java new file mode 100644 index 00000000..980dea14 --- /dev/null +++ b/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java @@ -0,0 +1,73 @@ +package mocacong.server.service; + +import mocacong.server.domain.*; +import mocacong.server.exception.badrequest.AlreadyExistsCommentLike; +import mocacong.server.exception.badrequest.AlreadyExistsFavorite; +import mocacong.server.repository.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +@ServiceTest +public class CommentLikeConcurrentServiceTest { + + @Autowired + private CommentLikeService commentLikeService; + + @Autowired + private CommentLikeRepository commentLikeRepository; + + @Autowired + private CommentRepository commentRepository; + @Autowired + private CafeRepository cafeRepository; + @Autowired + private MemberRepository memberRepository; + + @Test + @DisplayName("회원이 한 댓글을 동시에 여러 번 좋아요 등록 시도해도 한 번만 등록된다") + void saveCommentLikeWithConcurrent() throws InterruptedException { + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member1 = new Member("rlawjddn103@naver.com", "encodePassword", "베어"); + memberRepository.save(member1); + Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member2, commentContent); + commentRepository.save(comment); + + ExecutorService executorService = Executors.newFixedThreadPool(3); + CountDownLatch latch = new CountDownLatch(3); + List exceptions = Collections.synchronizedList(new ArrayList<>()); + + for (int i = 0; i < 3; i++) { + executorService.execute(() -> { + try { + commentLikeService.save(member1.getId(), comment.getId()); + } catch (AlreadyExistsCommentLike e) { + exceptions.add(e); // 중복 예외를 리스트에 추가 + } + latch.countDown(); + }); + } + latch.await(); + + List commentLikes = commentLikeRepository.findAll(); + assertAll( + () -> assertThat(exceptions).hasSize(2), + () -> assertThat(commentLikes).hasSize(1) + ); + } +} diff --git a/src/test/java/mocacong/server/service/CommentLikeServiceTest.java b/src/test/java/mocacong/server/service/CommentLikeServiceTest.java new file mode 100644 index 00000000..27c76565 --- /dev/null +++ b/src/test/java/mocacong/server/service/CommentLikeServiceTest.java @@ -0,0 +1,135 @@ +package mocacong.server.service; + +import mocacong.server.domain.*; +import mocacong.server.dto.response.CommentLikeSaveResponse; +import mocacong.server.exception.badrequest.AlreadyExistsCommentLike; +import mocacong.server.exception.badrequest.InvalidCommentLikeException; +import mocacong.server.exception.notfound.NotFoundCommentException; +import mocacong.server.exception.notfound.NotFoundCommentLikeException; +import mocacong.server.repository.CafeRepository; +import mocacong.server.repository.CommentLikeRepository; +import mocacong.server.repository.CommentRepository; +import mocacong.server.repository.MemberRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@ServiceTest +class CommentLikeServiceTest { + + @Autowired + private CommentLikeService commentLikeService; + + @Autowired + private CommentLikeRepository commentLikeRepository; + + @Autowired + private CommentRepository commentRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private CafeRepository cafeRepository; + + @Test + @DisplayName("특정 댓글에 좋아요를 할 수 있다.") + void save() { + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member1 = new Member("rlawjddn103@naver.com", "encodePassword", "베어"); + memberRepository.save(member1); + Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member2, commentContent); + commentRepository.save(comment); + + CommentLikeSaveResponse savedCommentLike = commentLikeService.save(member1.getId(), comment.getId()); + + CommentLike actual = commentLikeRepository.findById(savedCommentLike.getCommentLikeId()).orElseThrow(NotFoundCommentLikeException::new); + + assertEquals(savedCommentLike.getCommentLikeId(), actual.getId()); + assertEquals(comment.getId(), actual.getComment().getId()); + assertEquals(member1.getId(), actual.getMember().getId()); + } + + @Test + @DisplayName("이미 좋아요한 댓글에 좋아요를 또 하면 예외를 반환한다.") + void saveDuplicateCommentLike() { + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member1 = new Member("rlawjddn103@naver.com", "encodePassword", "베어"); + memberRepository.save(member1); + Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member2, commentContent); + commentRepository.save(comment); + + commentLikeService.save(member1.getId(), comment.getId()); + + assertThrows(AlreadyExistsCommentLike.class,() -> commentLikeService.save(member1.getId(), comment.getId())); + } + + @Test + @DisplayName("자신의 댓글에 좋아요를 하면 예외를 반환한다.") + void saveInvalidCommentLike() { + String email = "rlawjddn103@naver.com"; + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member = new Member(email, "encodePassword", "베어"); + memberRepository.save(member); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member, commentContent); + commentRepository.save(comment); + + assertThrows(InvalidCommentLikeException.class,() -> commentLikeService.save(member.getId(), comment.getId())); + } + + @Test + @DisplayName("회원이 댓글 좋아요를 삭제한다.") + void delete() { + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member1 = new Member("rlawjddn103@naver.com", "encodePassword", "베어"); + memberRepository.save(member1); + Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member2, commentContent); + commentRepository.save(comment); + CommentLike commentLike = new CommentLike(member1, comment); + commentLikeRepository.save(commentLike); + + commentLikeService.delete(member1.getId(), comment.getId()); + + assertThat(commentLikeRepository.findById(commentLike.getId())).isEmpty(); + } + + @Test + @DisplayName("회원이 존재하지 않는 댓글 좋아요를 삭제할 시 오류를 반환한다.") + void deleteNotExistCommentLike() { + String email = "rlawjddn103@naver.com"; + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member = new Member(email, "encodePassword", "베어"); + memberRepository.save(member); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member, commentContent); + commentRepository.save(comment); + CommentLike commentLike = new CommentLike(member, comment); + commentLikeRepository.save(commentLike); + + commentLikeService.delete(member.getId(), comment.getId()); + + assertThrows(NotFoundCommentLikeException.class,() -> commentLikeService.delete(member.getId(), comment.getId())); + } +} + diff --git a/src/test/java/mocacong/server/service/CommentServiceTest.java b/src/test/java/mocacong/server/service/CommentServiceTest.java index fcf80ca8..13f4751f 100644 --- a/src/test/java/mocacong/server/service/CommentServiceTest.java +++ b/src/test/java/mocacong/server/service/CommentServiceTest.java @@ -1,7 +1,9 @@ package mocacong.server.service; +import groovy.util.logging.Slf4j; import mocacong.server.domain.Cafe; import mocacong.server.domain.Comment; +import mocacong.server.domain.CommentLike; import mocacong.server.domain.Member; import mocacong.server.dto.response.CommentSaveResponse; import mocacong.server.dto.response.CommentsResponse; @@ -9,18 +11,21 @@ import mocacong.server.exception.badrequest.InvalidCommentUpdateException; import mocacong.server.exception.notfound.NotFoundCafeException; import mocacong.server.exception.notfound.NotFoundCommentException; +import mocacong.server.exception.notfound.NotFoundCommentLikeException; import mocacong.server.repository.CafeRepository; +import mocacong.server.repository.CommentLikeRepository; import mocacong.server.repository.CommentRepository; import mocacong.server.repository.MemberRepository; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -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; +@Slf4j @ServiceTest class CommentServiceTest { @@ -33,6 +38,8 @@ class CommentServiceTest { private MemberRepository memberRepository; @Autowired private CafeRepository cafeRepository; + @Autowired + private CommentLikeRepository commentLikeRepository; @Test @DisplayName("특정 카페에 댓글을 작성할 수 있다") @@ -40,12 +47,12 @@ void save() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; String expected = "공부하기 좋아요~🥰"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - CommentSaveResponse savedComment = commentService.save(email, mapId, expected); + CommentSaveResponse savedComment = commentService.save(member.getId(), mapId, expected); Comment actual = commentRepository.findById(savedComment.getId()) .orElseThrow(NotFoundCommentException::new); @@ -57,10 +64,10 @@ void save() { void saveNotExistsCafe() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - assertThatThrownBy(() -> commentService.save(email, mapId, "공부하기 좋아요~🥰")) + assertThatThrownBy(() -> commentService.save(member.getId(), mapId, "공부하기 좋아요~🥰")) .isInstanceOf(NotFoundCafeException.class); } @@ -69,14 +76,14 @@ void saveNotExistsCafe() { void saveManyTimes() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - commentService.save(email, mapId, "공부하기 좋아요~🥰"); + commentService.save(member.getId(), mapId, "공부하기 좋아요~🥰"); - assertDoesNotThrow(() -> commentService.save(email, mapId, "공부하기 좋아요~🥰")); + assertDoesNotThrow(() -> commentService.save(member.getId(), mapId, "공부하기 좋아요~🥰")); } @Test @@ -84,7 +91,7 @@ void saveManyTimes() { void findComments() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); @@ -93,7 +100,7 @@ void findComments() { commentRepository.save(new Comment(cafe, member, "댓글3")); commentRepository.save(new Comment(cafe, member, "댓글4")); - CommentsResponse actual = commentService.findAll(email, mapId, 0, 3); + CommentsResponse actual = commentService.findAll(member.getId(), mapId, 0, 3); assertAll( () -> assertThat(actual.getIsEnd()).isFalse(), @@ -104,13 +111,41 @@ void findComments() { ); } + @Test + @DisplayName("특정 카페에 달린 댓글 목록의 첫 페이지를 조회할 시에만 총 댓글 개수를 함께 반환한다.") + void findCommentsWithCount() { + String email = "rlawjddn103@naver.com"; + String mapId = "2143154352323"; + Member member = new Member(email, "encodePassword", "베어"); + memberRepository.save(member); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + commentRepository.save(new Comment(cafe, member, "댓글1")); + commentRepository.save(new Comment(cafe, member, "댓글2")); + commentRepository.save(new Comment(cafe, member, "댓글3")); + commentRepository.save(new Comment(cafe, member, "댓글4")); + + CommentsResponse actualPageOne = commentService.findAll(member.getId(), mapId, 0, 3); + CommentsResponse actualPageTwo = commentService.findAll(member.getId(), mapId, 1, 3); + + assertAll( + () -> assertThat(actualPageOne.getIsEnd()).isFalse(), + () -> assertThat(actualPageOne.getComments()).hasSize(3), + () -> assertThat(actualPageOne.getComments()) + .extracting("content") + .containsExactly("댓글1", "댓글2", "댓글3"), + () -> assertThat(actualPageOne.getCount()).isEqualTo(4), + () -> assertThat(actualPageTwo.getCount()).isEqualTo(null) + ); + } + @Test @DisplayName("특정 카페에 달린 댓글 목록 중 내가 작성한 댓글만을 조회한다") void findOnlyMyComments() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); - Member member2 = new Member("mery@naver.com", "encodePassword", "메리", "010-1234-5679"); + Member member = new Member(email, "encodePassword", "케이"); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member); memberRepository.save(member2); Cafe cafe = new Cafe(mapId, "케이카페"); @@ -120,7 +155,7 @@ void findOnlyMyComments() { commentRepository.save(new Comment(cafe, member, "댓글3")); commentRepository.save(new Comment(cafe, member2, "댓글4")); - CommentsResponse actual = commentService.findCafeCommentsOnlyMyComments(email, mapId, 0, 3); + CommentsResponse actual = commentService.findCafeCommentsOnlyMyComments(member.getId(), mapId, 0, 3); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), @@ -137,14 +172,14 @@ void updateComment() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; String comment = "공부하기 좋아요~🥰"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - CommentSaveResponse savedComment = commentService.save(email, mapId, comment); + CommentSaveResponse savedComment = commentService.save(member.getId(), mapId, comment); String expected = "조용하고 좋네요"; - commentService.update(email, mapId, expected, savedComment.getId()); + commentService.update(member.getId(), mapId, expected, savedComment.getId()); Comment updatedComment = commentRepository.findById(savedComment.getId()) .orElseThrow(NotFoundCommentException::new); @@ -157,16 +192,16 @@ void updateManyTimes() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; String comment = "공부하기 좋아요~🥰"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - CommentSaveResponse savedComment = commentService.save(email, mapId, comment); + CommentSaveResponse savedComment = commentService.save(member.getId(), mapId, comment); String expected = "조용하고 좋네요"; - commentService.update(email, mapId, expected, savedComment.getId()); + commentService.update(member.getId(), mapId, expected, savedComment.getId()); - assertDoesNotThrow(() -> commentService.update(email, mapId, expected, savedComment.getId())); + assertDoesNotThrow(() -> commentService.update(member.getId(), mapId, expected, savedComment.getId())); } @Test @@ -175,15 +210,15 @@ void updateByNonWriter() { String email1 = "kth990303@naver.com"; String email2 = "mery@naver.com"; String mapId = "2143154352323"; - Member member1 = new Member(email1, "encodePassword", "케이", "010-1234-5678"); + Member member1 = new Member(email1, "encodePassword", "케이"); memberRepository.save(member1); - Member member2 = new Member(email2, "encodePassword", "메리", "010-1234-5678"); + Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member2); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - CommentSaveResponse savedComment = commentService.save(email1, mapId, "조용하고 좋네요"); + CommentSaveResponse savedComment = commentService.save(member1.getId(), mapId, "조용하고 좋네요"); - assertThatThrownBy(() -> commentService.update(email2, mapId, "몰래 이 코멘트를 바꿔", savedComment.getId())) + assertThatThrownBy(() -> commentService.update(member2.getId(), mapId, "몰래 이 코멘트를 바꿔", savedComment.getId())) .isInstanceOf(InvalidCommentUpdateException.class); } @@ -192,14 +227,14 @@ void updateByNonWriter() { void delete() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - CommentSaveResponse response = commentService.save(email, mapId, "공부하기 좋아요~🥰"); + CommentSaveResponse response = commentService.save(member.getId(), mapId, "공부하기 좋아요~🥰"); - commentService.delete(email, mapId, response.getId()); - CommentsResponse actual = commentService.findAll(email, mapId, 0, 3); + commentService.delete(member.getId(), mapId, response.getId()); + CommentsResponse actual = commentService.findAll(member.getId(), mapId, 0, 3); assertThat(actual.getComments()).hasSize(0); } @@ -209,12 +244,12 @@ void delete() { void deleteNotExistsComment() { String email = "kth990303@naver.com"; String mapId = "2143154352323"; - Member member = new Member(email, "encodePassword", "케이", "010-1234-5678"); + Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - assertThatThrownBy(() -> commentService.delete(email, mapId, 9999L)) + assertThatThrownBy(() -> commentService.delete(member.getId(), mapId, 9999L)) .isInstanceOf(NotFoundCommentException.class); } @@ -224,15 +259,35 @@ void deleteNotMyComment() { String email1 = "kth990303@naver.com"; String email2 = "dlawotn3@naver.com"; String mapId = "2143154352323"; - Member member1 = new Member(email1, "encodePassword", "케이", "010-1234-5678"); - Member member2 = new Member(email2, "encodePassword", "메리", "010-1234-5678"); + Member member1 = new Member(email1, "encodePassword", "케이"); + Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); Cafe cafe = new Cafe(mapId, "케이카페"); cafeRepository.save(cafe); - CommentSaveResponse response = commentService.save(email1, mapId, "공부하기 좋아요~🥰"); + CommentSaveResponse response = commentService.save(member1.getId(), mapId, "공부하기 좋아요~🥰"); - assertThatThrownBy(() -> commentService.delete(email2, mapId, response.getId())) + assertThatThrownBy(() -> commentService.delete(member2.getId(), mapId, response.getId())) .isInstanceOf(InvalidCommentDeleteException.class); } + + @Test + @DisplayName("댓글 좋아요가 있는 댓글을 삭제할 수 있다.") + void deleteExistCommentLike() { + String email = "rlawjddn103@naver.com"; + String mapId = "2143154352323"; + String commentContent = "코딩하고 싶어지는 카페에요."; + Member member = new Member(email, "encodePassword", "베어"); + memberRepository.save(member); + Cafe cafe = new Cafe(mapId, "베어카페"); + cafeRepository.save(cafe); + Comment savedComment = commentRepository.save(new Comment(cafe, member, commentContent)); + commentLikeRepository.save(new CommentLike(member, savedComment)); + + commentService.delete(member.getId(), mapId, savedComment.getId()); + + CommentsResponse actual = commentService.findAll(member.getId(), mapId, 0, 3); + + assertThat(actual.getComments()).hasSize(0); + } } diff --git a/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java b/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java index d5117e1d..b86b1d5c 100644 --- a/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java @@ -35,7 +35,7 @@ public class FavoriteConcurrentServiceTest { @Test @DisplayName("회원이 한 카페를 동시에 여러 번 즐겨찾기 등록 시도해도 한 번만 등록된다") void saveFavoriteWithConcurrent() throws InterruptedException { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); @@ -46,7 +46,7 @@ void saveFavoriteWithConcurrent() throws InterruptedException { for (int i = 0; i < 3; i++) { executorService.execute(() -> { try { - favoriteService.save(member.getEmail(), cafe.getMapId()); + favoriteService.save(member.getId(), cafe.getMapId()); } catch (AlreadyExistsFavorite e) { exceptions.add(e); // 중복 예외를 리스트에 추가 } diff --git a/src/test/java/mocacong/server/service/FavoriteServiceTest.java b/src/test/java/mocacong/server/service/FavoriteServiceTest.java index 2ff6d872..8e633909 100644 --- a/src/test/java/mocacong/server/service/FavoriteServiceTest.java +++ b/src/test/java/mocacong/server/service/FavoriteServiceTest.java @@ -1,5 +1,6 @@ package mocacong.server.service; +import java.util.List; import mocacong.server.domain.Cafe; import mocacong.server.domain.Favorite; import mocacong.server.domain.Member; @@ -9,16 +10,13 @@ import mocacong.server.repository.CafeRepository; import mocacong.server.repository.FavoriteRepository; import mocacong.server.repository.MemberRepository; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - 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.assertThrows; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; @ServiceTest class FavoriteServiceTest { @@ -35,12 +33,12 @@ class FavoriteServiceTest { @Test @DisplayName("회원이 카페를 즐겨찾기 등록한다") void save() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - FavoriteSaveResponse actual = favoriteService.save(member.getEmail(), cafe.getMapId()); + FavoriteSaveResponse actual = favoriteService.save(member.getId(), cafe.getMapId()); List favorites = favoriteRepository.findAll(); assertAll( @@ -52,27 +50,27 @@ void save() { @Test @DisplayName("이미 즐겨찾기 등록한 카페에 즐겨찾기를 등록하면 예외를 반환한다") void saveDuplicate() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); - favoriteService.save(member.getEmail(), cafe.getMapId()); + favoriteService.save(member.getId(), cafe.getMapId()); - assertThatThrownBy(() -> favoriteService.save(member.getEmail(), cafe.getMapId())) + assertThatThrownBy(() -> favoriteService.save(member.getId(), cafe.getMapId())) .isInstanceOf(AlreadyExistsFavorite.class); } @Test @DisplayName("회원이 카페를 즐겨찾기 삭제한다") void delete() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); Favorite favorite = new Favorite(member, cafe); favoriteRepository.save(favorite); - favoriteService.delete(member.getEmail(), cafe.getMapId()); + favoriteService.delete(member.getId(), cafe.getMapId()); assertThat(favoriteRepository.findById(favorite.getId())).isEmpty(); } @@ -80,12 +78,12 @@ void delete() { @Test @DisplayName("회원이 즐겨찾기를 안한 카페 즐겨찾기 삭제할 경우 예외를 던진다") void deleteWithException() { - Member member = new Member("kth990303@naver.com", "encodePassword", "케이", "010-1234-5678"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); Cafe cafe = new Cafe("2143154352323", "케이카페"); cafeRepository.save(cafe); assertThrows(NotFoundFavoriteException.class, - () -> favoriteService.delete(member.getEmail(), cafe.getMapId())); + () -> favoriteService.delete(member.getId(), cafe.getMapId())); } } diff --git a/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java b/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java index fb6f3602..e521ded9 100644 --- a/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java @@ -28,7 +28,7 @@ public class MemberConcurrentServiceTest { @Test @DisplayName("회원이 동시에 여러 번 가입 시도해도 한 번만 가입된다") void signUpWithConcurrent() throws InterruptedException { - MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); ExecutorService executorService = Executors.newFixedThreadPool(3); CountDownLatch latch = new CountDownLatch(3); List exceptions = Collections.synchronizedList(new ArrayList<>()); diff --git a/src/test/java/mocacong/server/service/MemberServiceTest.java b/src/test/java/mocacong/server/service/MemberServiceTest.java index 5efc535d..56b2e101 100644 --- a/src/test/java/mocacong/server/service/MemberServiceTest.java +++ b/src/test/java/mocacong/server/service/MemberServiceTest.java @@ -55,7 +55,7 @@ class MemberServiceTest { @DisplayName("회원을 정상적으로 가입한다") void signUp() { String expected = "kth990303@naver.com"; - MemberSignUpRequest request = new MemberSignUpRequest(expected, "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest(expected, "a1b2c3d4", "케이"); memberService.signUp(request); @@ -68,8 +68,8 @@ void signUp() { @DisplayName("이미 가입된 이메일이 존재하면 회원 가입 시에 예외를 반환한다") void signUpByDuplicateEmailMember() { String email = "kth990303@naver.com"; - memberRepository.save(new Member(email, "1234", "케이", "010-1234-5678")); - MemberSignUpRequest request = new MemberSignUpRequest(email, "a1b2c3d4", "케이", "010-1234-5678"); + memberRepository.save(new Member(email, "1234", "케이")); + MemberSignUpRequest request = new MemberSignUpRequest(email, "a1b2c3d4", "케이"); assertThatThrownBy(() -> memberService.signUp(request)) .isInstanceOf(DuplicateMemberException.class); @@ -79,8 +79,8 @@ void signUpByDuplicateEmailMember() { @DisplayName("이미 가입된 닉네임이 존재하면 회원 가입 시에 예외를 반환한다") void signUpByDuplicateNicknameMember() { String nickname = "케이"; - memberRepository.save(new Member("kth2@naver.com", "1234", nickname, "010-1234-5678")); - MemberSignUpRequest request = new MemberSignUpRequest("kth@naver.com", "a1b2c3d4", nickname, "010-1234-5678"); + memberRepository.save(new Member("kth2@naver.com", "1234", nickname)); + MemberSignUpRequest request = new MemberSignUpRequest("kth@naver.com", "a1b2c3d4", nickname); assertThatThrownBy(() -> memberService.signUp(request)) .isInstanceOf(DuplicateNicknameException.class); @@ -96,7 +96,7 @@ void signUpByOAuthMember() { memberService.signUpByOAuthMember(request); - Member actual = memberRepository.findByEmail(savedMember.getEmail()) + Member actual = memberRepository.findById(savedMember.getId()) .orElseThrow(); assertThat(actual.getNickname()).isEqualTo("케이"); } @@ -126,7 +126,7 @@ void encryptPassword() { @DisplayName("비밀번호가 8~20자가 아니면 예외를 반환한다") @ValueSource(strings = {"abcdef7", "abcdefgabcdefgabcde21"}) void passwordLengthValidation(String password) { - assertThatThrownBy(() -> memberService.signUp(new MemberSignUpRequest("kth990303@naver.com", password, "케이", "010-1234-5678"))) + assertThatThrownBy(() -> memberService.signUp(new MemberSignUpRequest("kth990303@naver.com", password, "케이"))) .isInstanceOf(InvalidPasswordException.class); } @@ -134,7 +134,7 @@ void passwordLengthValidation(String password) { @DisplayName("비밀번호가 소문자, 숫자를 모두 포함하지 않으면 예외를 반환한다") @ValueSource(strings = {"abcdefgh", "12345678"}) void passwordConfigureValidation(String password) { - assertThatThrownBy(() -> memberService.signUp(new MemberSignUpRequest("kth990303@naver.com", password, "케이", "010-1234-5678"))) + assertThatThrownBy(() -> memberService.signUp(new MemberSignUpRequest("kth990303@naver.com", password, "케이"))) .isInstanceOf(InvalidPasswordException.class); } @@ -181,7 +181,7 @@ void findAndResetPassword() { Member member = memberRepository.save(new Member(email, Platform.MOCACONG, "1234")); ResetPasswordRequest request = new ResetPasswordRequest(NONCE, updatePassword); - memberService.resetPassword(email, request); + memberService.resetPassword(member.getId(), request); Member actual = memberRepository.findById(member.getId()) .orElseThrow(); @@ -196,10 +196,10 @@ void findAndResetPassword() { @DisplayName("올바르지 않은 비밀번호로 비밀번호 찾기 요청을 받을 경우 예외를 반환한다") void findAndResetPasswordWhenInvalidPassword() { String email = "kth990303@naver.com"; - memberRepository.save(new Member(email, Platform.MOCACONG, "1234")); + Member member = memberRepository.save(new Member(email, Platform.MOCACONG, "1234")); ResetPasswordRequest request = new ResetPasswordRequest(NONCE, "123"); - assertThatThrownBy(() -> memberService.resetPassword(email, request)) + assertThatThrownBy(() -> memberService.resetPassword(member.getId(), request)) .isInstanceOf(InvalidPasswordException.class); } @@ -207,10 +207,10 @@ void findAndResetPasswordWhenInvalidPassword() { @DisplayName("nonce 값이 올바르지 않은, 유효한 비밀번호 찾기 요청이 아닌 경우 비밀번호 변경이 안되고 예외를 반환한다") void findAndResetPasswordWhenInvalidNonce() { String email = "kth990303@naver.com"; - memberRepository.save(new Member(email, Platform.MOCACONG, "1234")); + Member member = memberRepository.save(new Member(email, Platform.MOCACONG, "1234")); ResetPasswordRequest request = new ResetPasswordRequest("invalid_nonce", "password123"); - assertThatThrownBy(() -> memberService.resetPassword(email, request)) + assertThatThrownBy(() -> memberService.resetPassword(member.getId(), request)) .isInstanceOf(InvalidNonceException.class); } @@ -218,7 +218,7 @@ void findAndResetPasswordWhenInvalidNonce() { @DisplayName("이미 존재하는 이메일인 경우 True를 반환한다") void isDuplicateEmailReturnTrue() { String email = "dlawotn3@naver.com"; - MemberSignUpRequest request = new MemberSignUpRequest(email, "a1b2c3d4", "케이", "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest(email, "a1b2c3d4", "케이"); memberService.signUp(request); IsDuplicateEmailResponse response = memberService.isDuplicateEmail(email); @@ -240,7 +240,7 @@ void isDuplicateEmailReturnFalse() { @DisplayName("이미 존재하는 닉네임인 경우 True를 반환한다") void isDuplicateNicknameReturnTrue() { String nickname = "메리"; - MemberSignUpRequest request = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", nickname, "010-1234-5678"); + MemberSignUpRequest request = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", nickname); memberService.signUp(request); IsDuplicateNicknameResponse response = memberService.isDuplicateNickname(nickname); @@ -268,9 +268,9 @@ void nicknameLengthIs0ReturnException() { @Test @DisplayName("회원을 정상적으로 탈퇴한다") void delete() { - Member savedMember = memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "메리", "010-1234-5678")); + Member savedMember = memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "메리")); - memberService.delete(savedMember.getEmail()); + memberService.delete(savedMember.getId()); List actual = memberRepository.findAll(); assertThat(actual).hasSize(0); @@ -279,15 +279,15 @@ void delete() { @Test @DisplayName("존재하지 않는 회원을 탈퇴 시에 예외를 반환한다") void deleteByNotFoundMember() { - assertThatThrownBy(() -> memberService.delete("dlawotn3@naver.com")) + assertThatThrownBy(() -> memberService.delete(1L)) .isInstanceOf(NotFoundMemberException.class); } @Test @DisplayName("회원을 전체 조회한다") void getAllMembers() { - memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678")); - memberRepository.save(new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", "010-1234-5678")); + memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "케이")); + memberRepository.save(new Member("dlawotn3@naver.com", "a1b2c3d4", "메리")); MemberGetAllResponse actual = memberService.getAllMembers(); @@ -300,19 +300,16 @@ void findMyInfo() { String imgUrl = "test_img.jpg"; String email = "kth990303@naver.com"; String nickname = "케이"; - String phone = "010-1234-5678"; MemberProfileImage memberProfileImage = new MemberProfileImage(imgUrl); memberProfileImageRepository.save(memberProfileImage); - Member member = new Member(email, "a1b2c3d4", "케이", phone, memberProfileImage, - Platform.MOCACONG, "1234"); + Member member = new Member(email, "a1b2c3d4", "케이", memberProfileImage, Platform.MOCACONG, "1234"); memberRepository.save(member); - MyPageResponse actual = memberService.findMyInfo(email); + MyPageResponse actual = memberService.findMyInfo(member.getId()); assertAll( () -> assertThat(actual.getEmail()).isEqualTo(email), () -> assertThat(actual.getImgUrl()).isEqualTo(imgUrl), - () -> assertThat(actual.getPhone()).isEqualTo(phone), () -> assertThat(actual.getNickname()).isEqualTo(nickname) ); } @@ -321,14 +318,14 @@ void findMyInfo() { @DisplayName("회원의 프로필 이미지를 변경하면 s3 서버와 연동하여 이미지를 업로드한다") void updateProfileImg() throws IOException { String expected = "test_img.jpg"; - Member member = memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "메리", "010-1234-5678")); + Member member = memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "메리")); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", expected, "jpg", fileInputStream); when(awsS3Uploader.uploadImage(mockMultipartFile)).thenReturn("test_img.jpg"); - memberService.updateProfileImage(member.getEmail(), mockMultipartFile); + memberService.updateProfileImage(member.getId(), mockMultipartFile); - Member actual = memberRepository.findByEmail(member.getEmail()) + Member actual = memberRepository.findById(member.getId()) .orElseThrow(); assertAll( @@ -343,13 +340,12 @@ void updateProfileImgWithNull() { MemberProfileImage memberProfileImage = new MemberProfileImage("test.me.jpg"); memberProfileImageRepository.save(memberProfileImage); Member member = memberRepository.save( - new Member("kth990303@naver.com", "a1b2c3d4", "메리", "010-1234-5678", - memberProfileImage, Platform.MOCACONG, "1234") + new Member("kth990303@naver.com", "a1b2c3d4", "메리", memberProfileImage, Platform.MOCACONG, "1234") ); - memberService.updateProfileImage(member.getEmail(), null); + memberService.updateProfileImage(member.getId(), null); - Member actual = memberRepository.findByEmail(member.getEmail()) + Member actual = memberRepository.findById(member.getId()) .orElseThrow(); assertAll( @@ -365,18 +361,15 @@ void updateProfileInfo() { String password = "a1b2c3d4"; String originalNickname = "mery"; String newNickname = "케이"; - String originalPhone = "010-1111-1111"; - String newPhone = "010-1234-1234"; - Member member = new Member(email, passwordEncoder.encode(password), originalNickname, originalPhone); + Member member = new Member(email, passwordEncoder.encode(password), originalNickname); memberRepository.save(member); - memberService.updateProfileInfo(email, new MemberProfileUpdateRequest(newNickname, newPhone)); - Member updatedMember = memberRepository.findByEmail(member.getEmail()) + memberService.updateProfileInfo(member.getId(), new MemberProfileUpdateRequest(newNickname)); + Member updatedMember = memberRepository.findById(member.getId()) .orElseThrow(); assertAll( - () -> assertThat(updatedMember.getNickname()).isEqualTo(newNickname), - () -> assertThat(updatedMember.getPhone()).isEqualTo(newPhone) + () -> assertThat(updatedMember.getNickname()).isEqualTo(newNickname) ); } @@ -387,33 +380,14 @@ void updateBadNicknameWithValidateException() { String password = "jisu1234"; String originalNickname = "mery"; String newNickname = "케이123"; - String originalPhone = "010-1111-1111"; - String newPhone = "010-1234-1234"; - Member member = new Member(email, passwordEncoder.encode(password), originalNickname, originalPhone); + Member member = new Member(email, passwordEncoder.encode(password), originalNickname); memberRepository.save(member); - MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname, newPhone); - assertThatThrownBy(() -> memberService.updateProfileInfo(member.getEmail(), request)) + MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname); + assertThatThrownBy(() -> memberService.updateProfileInfo(member.getId(), request)) .isInstanceOf(InvalidNicknameException.class); } - @Test - @DisplayName("회원이 잘못된 전화번호 형식으로 회원정보 수정을 시도하면 예외를 반환한다") - void updateBadPhoneWithValidateException() { - String email = "mery@naver.com"; - String password = "jisu1234"; - String originalNickname = "mery"; - String newNickname = "케이"; - String originalPhone = "010-1111-1111"; - String newPhone = "010-0000"; - Member member = new Member(email, passwordEncoder.encode(password), originalNickname, originalPhone); - memberRepository.save(member); - - MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname, newPhone); - assertThatThrownBy(() -> memberService.updateProfileInfo(member.getEmail(), request)) - .isInstanceOf(InvalidPhoneException.class); - } - @Test @DisplayName("회원이 중복된 닉네임으로 회원정보 수정을 시도하면 예외를 반환한다") void updateDuplicateNicknameWithValidateException() { @@ -421,12 +395,11 @@ void updateDuplicateNicknameWithValidateException() { String password = "jisu0708"; String originalNickname = "mery"; String newNickname = "케이"; - String phone = "010-1111-1111"; - memberRepository.save(new Member(email, password, originalNickname, phone)); - memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "케이", "010-1234-5678")); + Member member = memberRepository.save(new Member(email, password, originalNickname)); + memberRepository.save(new Member("kth990303@naver.com", "a1b2c3d4", "케이")); - MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname, phone); - assertThatThrownBy(() -> memberService.updateProfileInfo(email, request)) + MemberProfileUpdateRequest request = new MemberProfileUpdateRequest(newNickname); + assertThatThrownBy(() -> memberService.updateProfileInfo(member.getId(), request)) .isInstanceOf(DuplicateNicknameException.class); } @@ -452,18 +425,33 @@ void deleteMemberProfileImages() { ); } + @Test + @DisplayName("프로필 이미지가 등록된 회원이 탈퇴하면 해당 프로필 이미지는 사용되지 않는 것으로 변경된다") + void deleteMemberAndProfileImage() { + MemberProfileImage memberProfileImage = new MemberProfileImage("test_img.jpg"); + memberProfileImageRepository.save(memberProfileImage); + Member member = memberRepository.save( + new Member("kth990303@naver.com", "a1b2c3d4", "케이", memberProfileImage) + ); + + memberService.delete(member.getId()); + + MemberProfileImage actual = memberProfileImageRepository.findById(memberProfileImage.getId()) + .orElseThrow(); + assertThat(actual.getIsUsed()).isFalse(); + } + @Test @DisplayName("회원이 옳은 비밀번호로 비밀번호 확인 인증을 성공한다") void verifyPasswordReturnTrue() { String email = "dlawotn3@naver.com"; String password = "jisu1234"; String nickname = "mery"; - String phone = "010-1111-1111"; - Member member = new Member(email, passwordEncoder.encode(password), nickname, phone); + Member member = new Member(email, passwordEncoder.encode(password), nickname); memberRepository.save(member); PasswordVerifyRequest request = new PasswordVerifyRequest("jisu1234"); - PasswordVerifyResponse actual = memberService.verifyPassword(email, request); + PasswordVerifyResponse actual = memberService.verifyPassword(member.getId(), request); assertThat(actual.getIsSuccess()).isTrue(); } @@ -474,12 +462,11 @@ void verifyPasswordReturnFalse() { String email = "dlawotn3@naver.com"; String password = "jisu1234"; String nickname = "mery"; - String phone = "010-1111-1111"; - Member member = new Member(email, passwordEncoder.encode(password), nickname, phone); + Member member = new Member(email, passwordEncoder.encode(password), nickname); memberRepository.save(member); PasswordVerifyRequest request = new PasswordVerifyRequest("wrongpwd123"); - PasswordVerifyResponse actual = memberService.verifyPassword(email, request); + PasswordVerifyResponse actual = memberService.verifyPassword(member.getId(), request); assertThat(actual.getIsSuccess()).isFalse(); } @@ -490,16 +477,14 @@ void getUpdateProfileInfo() { String email = "dlawotn3@naver.com"; String password = "jisu1234"; String nickname = "mery"; - String phone = "010-1111-1111"; - Member member = new Member(email, passwordEncoder.encode(password), nickname, phone); + Member member = new Member(email, passwordEncoder.encode(password), nickname); memberRepository.save(member); - GetUpdateProfileInfoResponse actual = memberService.getUpdateProfileInfo(member.getEmail()); + GetUpdateProfileInfoResponse actual = memberService.getUpdateProfileInfo(member.getId()); assertAll( () -> assertThat(actual.getEmail()).isEqualTo(email), - () -> assertThat(actual.getNickname()).isEqualTo(nickname), - () -> assertThat(actual.getPhone()).isEqualTo(phone) + () -> assertThat(actual.getNickname()).isEqualTo(nickname) ); } } diff --git a/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java new file mode 100644 index 00000000..21a7c152 --- /dev/null +++ b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java @@ -0,0 +1,72 @@ +package mocacong.server.service; + +import mocacong.server.domain.Cafe; +import mocacong.server.domain.Member; +import mocacong.server.dto.response.CommentReportResponse; +import mocacong.server.dto.response.CommentSaveResponse; +import mocacong.server.repository.CafeRepository; +import mocacong.server.repository.MemberRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +@ServiceTest +public class ReportConcurrentServiceTest { + @Autowired + private CommentService commentService; + @Autowired + private ReportService reportService; + @Autowired + private CafeRepository cafeRepository; + @Autowired + private MemberRepository memberRepository; + + @Test + @DisplayName("타 사용자가 작성한 댓글을 동시에 여러 번 신고하려 해도 한 번만 신고된다") + void reportCommentWithConcurrent() throws InterruptedException { + String email1 = "kth990303@naver.com"; + String email2 = "dlawotn3@naver.com"; + String mapId = "2143154352323"; + Member member1 = new Member(email1, "encodePassword", "케이"); + Member member2 = new Member(email2, "encodePassword", "메리"); + memberRepository.save(member1); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + CommentSaveResponse saveResponse = commentService.save(member1.getId(), mapId, "아~ 소설보고 싶다"); + ExecutorService executorService = Executors.newFixedThreadPool(3); + CountDownLatch latch = new CountDownLatch(3); + List responses = Collections.synchronizedList(new ArrayList<>()); + List exceptions = Collections.synchronizedList(new ArrayList<>()); + + for (int i = 0; i < 3; i++) { + executorService.execute(() -> { + try { + CommentReportResponse response = reportService.reportComment(member2.getId(), saveResponse.getId(), + "insult"); + responses.add(response); + } catch (Exception e) { + exceptions.add(e); // 중복 예외를 리스트에 추가 + + } + latch.countDown(); + }); + } + latch.await(); + + assertAll( + () -> assertThat(responses.size()).isEqualTo(1), + () -> assertThat(exceptions.size()).isEqualTo(2) + ); + } +} diff --git a/src/test/java/mocacong/server/service/ReportServiceTest.java b/src/test/java/mocacong/server/service/ReportServiceTest.java new file mode 100644 index 00000000..fd178a1f --- /dev/null +++ b/src/test/java/mocacong/server/service/ReportServiceTest.java @@ -0,0 +1,206 @@ +package mocacong.server.service; + +import mocacong.server.domain.Cafe; +import mocacong.server.domain.Comment; +import mocacong.server.domain.Member; +import mocacong.server.domain.Status; +import mocacong.server.dto.response.CommentReportResponse; +import mocacong.server.dto.response.CommentSaveResponse; +import mocacong.server.dto.response.CommentsResponse; +import mocacong.server.exception.badrequest.DuplicateReportCommentException; +import mocacong.server.exception.badrequest.InvalidCommentReportException; +import mocacong.server.exception.badrequest.InvalidReportReasonException; +import mocacong.server.repository.CafeRepository; +import mocacong.server.repository.CommentRepository; +import mocacong.server.repository.MemberRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +@ServiceTest +public class ReportServiceTest { + + @Autowired + private ReportService reportService; + @Autowired + private MemberService memberService; + @Autowired + private CommentService commentService; + + @Autowired + private CommentRepository commentRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private CafeRepository cafeRepository; + + @Test + @DisplayName("타 사용자가 작성한 댓글을 신고한다") + void reportComment() { + String email1 = "kth990303@naver.com"; + String email2 = "dlawotn3@naver.com"; + String mapId = "2143154352323"; + String reportReason = "insult"; + Member member1 = new Member(email1, "encodePassword", "케이"); + Member member2 = new Member(email2, "encodePassword", "메리"); + memberRepository.save(member1); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + commentService.save(member1.getId(), mapId, "이 카페 완전 돈 아깝;;"); + + CommentReportResponse response = reportService.reportComment(member2.getId(), 1L, reportReason); + + assertThat(response.getCommentReportCount()).isEqualTo(1); + } + + @Test + @DisplayName("본인이 작성한 댓글에 대해 신고를 시도할 시 예외를 반환한다") + void reportMyComment() { + String email = "kth990303@naver.com"; + String mapId = "2143154352323"; + Member member = new Member(email, "encodePassword", "케이"); + memberRepository.save(member); + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + CommentSaveResponse saveResponse = commentService.save(member.getId(), mapId, "굳이 이런데 가야하나 ㅋ"); + + assertThatThrownBy(() -> reportService.reportComment(member.getId(), saveResponse.getId(), "insult")) + .isInstanceOf(InvalidCommentReportException.class); + } + + @Test + @DisplayName("잘못된 신고 사유로 신고를 시도할 시 예외를 반환한다") + void reportByInvalidReportReason() { + String mapId = "2143154352323"; + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); + Member member2 = new Member("dlawotn3@naver.com", "encodePassword", "메리"); + memberRepository.save(member1); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + commentService.save(member1.getId(), mapId, "이 카페 완전 돈 아깝;;"); + + assertThatThrownBy(() -> reportService.reportComment(member2.getId(), 1L, + "invalidReportReason")) + .isInstanceOf(InvalidReportReasonException.class); + } + + @Test + @DisplayName("이미 신고한 댓글에 대해 신고를 시도할 시 예외를 반환한다") + void reportDuplicateComment() { + String email1 = "kth990303@naver.com"; + String email2 = "dlawotn3@naver.com"; + String mapId = "2143154352323"; + Member member1 = new Member(email1, "encodePassword", "케이"); + Member member2 = new Member(email2, "encodePassword", "메리"); + memberRepository.save(member1); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + CommentSaveResponse saveResponse = commentService.save(member1.getId(), mapId, "아~ 소설보고 싶다"); + + reportService.reportComment(member2.getId(), saveResponse.getId(), "inappropriate_content"); + + assertThatThrownBy(() -> reportService.reportComment(member2.getId(), saveResponse.getId(), + "inappropriate_content")) + .isInstanceOf(DuplicateReportCommentException.class); + } + + @Test + @DisplayName("5번 이상 신고된 댓글은 마스킹되며 해당 작성자의 신고 횟수가 1씩 증가한다") + void maskCauseReport5timesReportedComment() { + String mapId = "2143154352323"; + List members = new ArrayList<>(); + for (int i = 1; i <= 6; i++) { + Member member = new Member("dlawotn" + i + "@naver.com", "encodePassword", "메리" + + (char) ('A' + i)); + members.add(member); + memberRepository.save(member); + } + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + CommentSaveResponse saveResponse = commentService.save(members.get(0).getId(), mapId, "아~ 소설보고 싶다"); + + for (int i = 1; i <= 4; i++) { + reportService.reportComment(members.get(i).getId(), saveResponse.getId(), + "inappropriate_content"); + } + CommentReportResponse reportResponse = reportService.reportComment(members.get(5).getId(), saveResponse.getId(), + "inappropriate_content"); + Optional reportedComment = commentRepository.findById(1L); + Optional commenter = memberRepository.findById(1L); + + assertAll( + () -> assertThat(reportResponse.getCommentReportCount()).isEqualTo(5), + () -> assertThat(reportedComment.get().getContent()) + .isEqualTo("삭제된 댓글입니다"), + () -> assertThat(reportedComment.get().getWriterImgUrl()).isNull(), + () -> assertThat(reportedComment.get().getWriterNickname()).isNull(), + () -> assertThat(reportedComment.get().isMasked()).isTrue(), + () -> assertThat(commenter.get().getReportCount()).isEqualTo(1) + ); + } + + @Test + @DisplayName("11번 이상 신고된 회원은 Status가 INACTIVE로 전환된다") + void setInactiveCause11timesReportedComment() { + List members = new ArrayList<>(); + for (int i = 1; i <= 6; i++) { + Member member = new Member("dlawotn" + i + "@naver.com", "encodePassword", + "메리" + (char) ('A' + i)); + members.add(member); + memberRepository.save(member); + } + for (int i = 1; i <= 11; i++) { + String mapId = "abc" + (char) ('A' + i); + cafeRepository.save(new Cafe(mapId, "메리 카페")); + CommentSaveResponse saveResponse = commentService.save(members.get(0).getId(), mapId, + "아~ 소설보고 싶다"); + for (int j = 1; j <= 5; j ++) { + reportService.reportComment(members.get(j).getId(), saveResponse.getId(), + "inappropriate_content"); + } + } + CommentsResponse reportedComment = commentService.findAll(members.get(1).getId(), + "abc" + (char) ('A' + 1), 0, 3); + Optional commenter = memberRepository.findById(1L); + + assertAll( + () -> assertThat(reportedComment.getComments().get(0).getContent()) + .isEqualTo("삭제된 댓글입니다"), + () -> assertThat(commenter.get().getReportCount()).isEqualTo(11), + () -> assertThat(commenter.get().getStatus()).isEqualTo(Status.INACTIVE) + ); + } + + @Test + @DisplayName("탈퇴한 회원이 작성한 코멘트를 신고한다") + void reportCommentPostedDeletedMember() { + String email1 = "kth990303@naver.com"; + String email2 = "dlawotn3@naver.com"; + String mapId = "2143154352323"; + Member member1 = new Member(email1, "encodePassword", "케이"); + Member member2 = new Member(email2, "encodePassword", "메리"); + memberRepository.save(member1); + memberRepository.save(member2); + Cafe cafe = new Cafe(mapId, "케이카페"); + cafeRepository.save(cafe); + Comment comment = new Comment(cafe, member1, "이 카페 완전 돈 아깝;;"); + commentRepository.save(comment); + memberService.delete(member1.getId()); + + CommentReportResponse response = reportService.reportComment(member2.getId(), comment.getId(), + "inappropriate_content"); + + assertThat(response.getCommentReportCount()).isEqualTo(1); + } +} diff --git a/src/test/resources/images/test_img.jpg b/src/test/resources/images/test_img.jpg index e69de29b..b8fc28b7 100644 Binary files a/src/test/resources/images/test_img.jpg and b/src/test/resources/images/test_img.jpg differ diff --git a/src/test/resources/images/test_img2.jpg b/src/test/resources/images/test_img2.jpg index e69de29b..e9b6a9b4 100644 Binary files a/src/test/resources/images/test_img2.jpg and b/src/test/resources/images/test_img2.jpg differ