From facfa91adfcb590f12c6c8f7fec5cb0d0c0f77aa Mon Sep 17 00:00:00 2001 From: TaeHyeon Kim Date: Tue, 1 Aug 2023 16:15:16 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=EB=B9=84=ED=9A=A8=EC=9C=A8?= =?UTF-8?q?=EC=A0=81=EC=9D=B8=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=A4=EB=84=A5=EC=85=98=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=A1=9C=EA=B9=85=20=EC=B6=94=EA=B0=80=20(#132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: `System.out.print` 이용하는 `show-sql` 옵션 false * feat: SQL 정보 및 트랜잭션 커넥션 관련 로깅 추가 --- src/main/resources/application-dev.yml | 1 - src/main/resources/application.yml | 1 - src/main/resources/logback-spring.xml | 9 +++++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 52b54474..f51f464c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -12,7 +12,6 @@ spring: hibernate: create_empty_composites: enabled: true - show_sql: true format_sql: true servlet: multipart: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4a534933..22008753 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,7 +10,6 @@ spring: hibernate: create_empty_composites: enabled: true - show_sql: true format_sql: true task: execution: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index daac5cfb..6f4796c4 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -6,6 +6,15 @@ + + + + + + + + + From ada2ac40b71d1f2311b510868735b6bf377a6f14 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim <76640167+jung-woo-kim@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:14:28 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=8B=9C=20commentLike=EC=9D=98=20=EC=88=98=20?= =?UTF-8?q?=ED=95=A8=EA=BB=98=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=ED=98=84=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: cafe 조회 시 comment에 좋아요 정보 함께 반환 * chore: default_batch_fetch_size를 통해 commentLike N+1문제 해결 --- .../java/mocacong/server/domain/Comment.java | 8 ++++ .../server/dto/response/CommentResponse.java | 1 + .../mocacong/server/service/CafeService.java | 4 +- .../server/service/CommentService.java | 4 +- src/main/resources/application-dev.yml | 1 + src/main/resources/application.yml | 1 + .../server/service/CafeServiceTest.java | 47 +++++++++++++++++++ .../service/MemberConcurrentServiceTest.java | 3 ++ src/test/resources/application.yml | 1 + 9 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/main/java/mocacong/server/domain/Comment.java b/src/main/java/mocacong/server/domain/Comment.java index 12fcf2b8..521e9a6c 100644 --- a/src/main/java/mocacong/server/domain/Comment.java +++ b/src/main/java/mocacong/server/domain/Comment.java @@ -38,6 +38,9 @@ public class Comment extends BaseTime { @OneToMany(mappedBy = "comment", cascade = CascadeType.ALL, orphanRemoval = true) private List reports = new ArrayList<>(); + @OneToMany(mappedBy = "comment", cascade = CascadeType.ALL, orphanRemoval = true) + private List commentLikes = new ArrayList<>(); + @Column(name = "is_masked") private boolean isMasked; @@ -112,4 +115,9 @@ public void addReport(Report report) { public void updateIsMasked(boolean isMasked) { this.isMasked= isMasked; } + + public int getLikeCounts() { + return commentLikes.size(); + } + } diff --git a/src/main/java/mocacong/server/dto/response/CommentResponse.java b/src/main/java/mocacong/server/dto/response/CommentResponse.java index 44d8ad2e..fde22313 100644 --- a/src/main/java/mocacong/server/dto/response/CommentResponse.java +++ b/src/main/java/mocacong/server/dto/response/CommentResponse.java @@ -12,5 +12,6 @@ public class CommentResponse { private String imgUrl; private String nickname; private String content; + private int likeCount; private Boolean isMe; } diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index 0307474c..4256a75a 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -119,10 +119,10 @@ private List findCommentResponses(Cafe cafe, Member member) { .map(comment -> { if (comment.isWrittenByMember(member)) { return new CommentResponse(comment.getId(), member.getImgUrl(), member.getNickname(), - comment.getContent(), true); + comment.getContent(), comment.getLikeCounts(), true); } else { return new CommentResponse(comment.getId(), comment.getWriterImgUrl(), - comment.getWriterNickname(), comment.getContent(), false); + comment.getWriterNickname(), comment.getContent(), comment.getLikeCounts(), false); } }) .collect(Collectors.toList()); diff --git a/src/main/java/mocacong/server/service/CommentService.java b/src/main/java/mocacong/server/service/CommentService.java index 483f689a..c3ac6832 100644 --- a/src/main/java/mocacong/server/service/CommentService.java +++ b/src/main/java/mocacong/server/service/CommentService.java @@ -81,9 +81,9 @@ private List findCommentResponses(Member member, Slice return comments.stream() .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(), comment.getLikeCounts(), 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(), comment.getLikeCounts(), false); } }) .collect(Collectors.toList()); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index f51f464c..36aec68d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -13,6 +13,7 @@ spring: create_empty_composites: enabled: true format_sql: true + default_batch_fetch_size: 100 servlet: multipart: max-file-size: 10MB diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 22008753..62deb59c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,6 +11,7 @@ spring: create_empty_composites: enabled: true format_sql: true + default_batch_fetch_size: 100 task: execution: pool: diff --git a/src/test/java/mocacong/server/service/CafeServiceTest.java b/src/test/java/mocacong/server/service/CafeServiceTest.java index 6dce840f..9b49e535 100644 --- a/src/test/java/mocacong/server/service/CafeServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeServiceTest.java @@ -48,6 +48,8 @@ class CafeServiceTest { private FavoriteRepository favoriteRepository; @Autowired private CafeImageRepository cafeImageRepository; + @Autowired + private CommentLikeRepository commentLikeRepository; @MockBean private AwsS3Uploader awsS3Uploader; @@ -167,6 +169,51 @@ void findCafeWithReviewsAndComments() { ); } + @Test + @DisplayName("코멘트에 좋아요가 있는 카페를 조회한다") + void findCafeWithCommentLike() { + Member member1 = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member1); + Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); + memberRepository.save(member2); + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), + new CafeReviewRequest(1, "group", "느려요", "없어요", + "불편해요", "없어요", "북적북적해요", "불편해요")); + cafeService.saveCafeReview(member2.getId(), cafe.getMapId(), + new CafeReviewRequest(2, "both", "느려요", "없어요", + "깨끗해요", "없어요", null, "보통이에요")); + Comment comment1 = new Comment(cafe, member1, "이 카페 조금 아쉬운 점이 많아요 ㅠㅠ"); + commentRepository.save(comment1); + Comment comment2 = new Comment(cafe, member2, "와이파이가 왜케 느릴까요..."); + commentRepository.save(comment2); + Comment comment3 = new Comment(cafe, member1, "다시 와봐도 똑같네요. 리뷰 수정할까 하다가 그대로 남겨요.."); + commentRepository.save(comment3); + CommentLike commentLike1 = new CommentLike(member1, comment2); + commentLikeRepository.save(commentLike1); + CommentLike commentLike2 = new CommentLike(member2, comment1); + commentLikeRepository.save(commentLike2); + + FindCafeResponse actual = cafeService.findCafeByMapId(member1.getId(), cafe.getMapId()); + + assertAll( + () -> assertThat(actual.getFavorite()).isFalse(), + () -> assertThat(actual.getFavoriteId()).isNull(), + () -> assertThat(actual.getScore()).isEqualTo(1.5), + () -> assertThat(actual.getMyScore()).isEqualTo(1), + () -> assertThat(actual.getStudyType()).isEqualTo("group"), + () -> assertThat(actual.getReviewsCount()).isEqualTo(2), + () -> assertThat(actual.getCommentsCount()).isEqualTo(3), + () -> assertThat(actual.getComments()) + .extracting("nickname") + .containsExactlyInAnyOrder("케이", "메리", "케이"), + () -> assertThat(actual.getComments()) + .extracting("likeCount") + .containsExactlyInAnyOrder(1, 1, 0) + ); + } + @Test @DisplayName("평점, 리뷰가 존재하지 않는 카페 정보를 미리보기한다") void previewCafe() { diff --git a/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java b/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java index e521ded9..d3324e34 100644 --- a/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/MemberConcurrentServiceTest.java @@ -3,6 +3,7 @@ import mocacong.server.domain.Member; import mocacong.server.dto.request.MemberSignUpRequest; import mocacong.server.exception.badrequest.DuplicateMemberException; +import mocacong.server.exception.badrequest.DuplicateNicknameException; import mocacong.server.repository.MemberRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -39,6 +40,8 @@ void signUpWithConcurrent() throws InterruptedException { memberService.signUp(request); } catch (DuplicateMemberException e) { exceptions.add(e); // 중복 예외를 리스트에 추가 + } catch (DuplicateNicknameException e) { + exceptions.add(e); } latch.countDown(); }); diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index abc2be88..5ded7200 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -12,6 +12,7 @@ spring: enabled: true show_sql: true format_sql: true + default_batch_fetch_size: 100 redis: host: localhost port: 16379 From d66a878c54624e5bbe480bddc41f12c16af69354 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:17:35 +0900 Subject: [PATCH 03/16] =?UTF-8?q?refact:=20Kakao=20OAuth=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20SDK=EC=97=90?= =?UTF-8?q?=20=EB=A7=9E=EC=B6=B0=20=EC=88=98=EC=A0=95=20(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: KakaoLoginRequest 인자명 수정 * delete: KakaoAccessToken 관련 파일 수정 * fix: kakao oauth 통신 로직 수정 * delete: 이전 로직의 kakao test 파일 삭제 * delete: 카카오 관련 환경변수 삭제 * delete: 카카오 관련 환경변수 삭제 * refact: 카카오 oauth 로그인 feign 통신 제거 * delete: kakao feign client 관련 파일 제거 * test: 카카오 oauth 로그인 로직 수정에 따른 테스트 코드 수정 --------- Co-authored-by: dlawotn3 --- .../server/dto/request/KakaoLoginRequest.java | 8 ++- .../security/auth/kakao/KakaoAccessToken.java | 22 -------- .../auth/kakao/KakaoAccessTokenClient.java | 12 ---- .../auth/kakao/KakaoAccessTokenRequest.java | 30 ---------- .../auth/kakao/KakaoOAuthUserProvider.java | 49 ---------------- .../server/security/auth/kakao/KakaoUser.java | 33 ----------- .../security/auth/kakao/KakaoUserClient.java | 14 ----- .../security/auth/kakao/KakaoUserRequest.java | 17 ------ .../mocacong/server/service/AuthService.java | 9 +-- src/main/resources/application-dev.yml | 4 -- src/main/resources/application.yml | 4 -- .../server/acceptance/AuthAcceptanceTest.java | 7 +-- .../kakao/KakaoOAuthUserProviderTest.java | 56 ------------------- src/test/resources/application.yml | 4 -- 14 files changed, 10 insertions(+), 259 deletions(-) delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoAccessToken.java delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenClient.java delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenRequest.java delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProvider.java delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoUser.java delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoUserClient.java delete mode 100644 src/main/java/mocacong/server/security/auth/kakao/KakaoUserRequest.java delete mode 100644 src/test/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProviderTest.java diff --git a/src/main/java/mocacong/server/dto/request/KakaoLoginRequest.java b/src/main/java/mocacong/server/dto/request/KakaoLoginRequest.java index b54cddce..efa250e1 100644 --- a/src/main/java/mocacong/server/dto/request/KakaoLoginRequest.java +++ b/src/main/java/mocacong/server/dto/request/KakaoLoginRequest.java @@ -1,16 +1,20 @@ package mocacong.server.dto.request; -import javax.validation.constraints.NotBlank; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import javax.validation.constraints.NotBlank; + @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Getter public class KakaoLoginRequest { @NotBlank(message = "1012:공백일 수 없습니다.") - private String code; + private String email; + + @NotBlank(message = "1012:공백일 수 없습니다.") + private String platformId; } diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessToken.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessToken.java deleted file mode 100644 index 7b1b9b4f..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessToken.java +++ /dev/null @@ -1,22 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Getter -public class KakaoAccessToken { - - private static final String AUTHORIZATION_BEARER = "Bearer "; - - @JsonProperty("access_token") - private String accessToken; - - public String getAuthorization() { - return AUTHORIZATION_BEARER + accessToken; - } -} diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenClient.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenClient.java deleted file mode 100644 index 0e5b0064..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenClient.java +++ /dev/null @@ -1,12 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.cloud.openfeign.SpringQueryMap; -import org.springframework.web.bind.annotation.PostMapping; - -@FeignClient(name = "kakao-access-token-client", url = "https://kauth.kakao.com/oauth/token") -public interface KakaoAccessTokenClient { - - @PostMapping(consumes = "application/x-www-form-urlencoded") - KakaoAccessToken getToken(@SpringQueryMap KakaoAccessTokenRequest request); -} diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenRequest.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenRequest.java deleted file mode 100644 index 9bd86292..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoAccessTokenRequest.java +++ /dev/null @@ -1,30 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@Getter -@Setter -public class KakaoAccessTokenRequest { - - private static final String KAKAO_GRANT_TYPE = "authorization_code"; - - private String code; - private String client_id; - private String client_secret; - private String redirect_uri; - private String grant_type; - - public KakaoAccessTokenRequest( - String authorizationCode, String kakaoClientId, String kakaoClientSecret, String redirectUri - ) { - this.code = authorizationCode; - this.client_id = kakaoClientId; - this.client_secret = kakaoClientSecret; - this.redirect_uri = redirectUri; - this.grant_type = KAKAO_GRANT_TYPE; - } -} diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProvider.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProvider.java deleted file mode 100644 index dc1748a5..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProvider.java +++ /dev/null @@ -1,49 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import feign.FeignException; -import mocacong.server.exception.unauthorized.InvalidTokenException; -import mocacong.server.security.auth.OAuthPlatformMemberResponse; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -public class KakaoOAuthUserProvider { - - private final KakaoAccessTokenClient kakaoAccessTokenClient; - private final KakaoUserClient kakaoUserClient; - private final String kakaoClientId; - private final String kakaoClientSecret; - private final String redirectUri; - - public KakaoOAuthUserProvider( - KakaoAccessTokenClient kakaoAccessTokenClient, - KakaoUserClient kakaoUserClient, - @Value("${oauth.kakao.client-id}") String kakaoClientId, - @Value("${oauth.kakao.client-secret}") String kakaoClientSecret, - @Value("${oauth.kakao.redirect-uri}") String redirectUri - ) { - this.kakaoAccessTokenClient = kakaoAccessTokenClient; - this.kakaoUserClient = kakaoUserClient; - this.kakaoClientId = kakaoClientId; - this.kakaoClientSecret = kakaoClientSecret; - this.redirectUri = redirectUri; - } - - public OAuthPlatformMemberResponse getKakaoPlatformMember(String authorizationCode) { - KakaoAccessTokenRequest kakaoAccessTokenRequest = - new KakaoAccessTokenRequest(authorizationCode, kakaoClientId, kakaoClientSecret, redirectUri); - KakaoAccessToken token = getKakaoAccessToken(kakaoAccessTokenRequest); - - KakaoUserRequest kakaoUserRequest = new KakaoUserRequest("[\"kakao_account.email\"]"); - KakaoUser user = kakaoUserClient.getUser(kakaoUserRequest, token.getAuthorization()); - return new OAuthPlatformMemberResponse(String.valueOf(user.getId()), user.getEmail()); - } - - private KakaoAccessToken getKakaoAccessToken(KakaoAccessTokenRequest kakaoAccessTokenRequest) { - try { - return kakaoAccessTokenClient.getToken(kakaoAccessTokenRequest); - } catch (FeignException e) { - throw new InvalidTokenException("KAKAO OAuth 인가 코드가 올바르지 않습니다."); - } - } -} diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoUser.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoUser.java deleted file mode 100644 index 3e9ac0a4..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoUser.java +++ /dev/null @@ -1,33 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Getter -public class KakaoUser { - - private Long id; - @JsonProperty("kakao_account") - private KakaoAccount kakaoAccount; - - public static KakaoUser of(Long id, String email) { - return new KakaoUser(id, new KakaoAccount(email)); - } - - public String getEmail() { - return kakaoAccount.getEmail(); - } - - @Getter - @NoArgsConstructor(access = AccessLevel.PROTECTED) - @AllArgsConstructor - private static class KakaoAccount { - - private String email; - } -} diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoUserClient.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoUserClient.java deleted file mode 100644 index de8fb855..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoUserClient.java +++ /dev/null @@ -1,14 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.cloud.openfeign.SpringQueryMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; - -@FeignClient(name = "kakao-user-client", url = "https://kapi.kakao.com/v2/user/me") -public interface KakaoUserClient { - - @GetMapping - KakaoUser getUser(@SpringQueryMap KakaoUserRequest request, - @RequestHeader(name = "Authorization") String authorization); -} diff --git a/src/main/java/mocacong/server/security/auth/kakao/KakaoUserRequest.java b/src/main/java/mocacong/server/security/auth/kakao/KakaoUserRequest.java deleted file mode 100644 index 39bf042f..00000000 --- a/src/main/java/mocacong/server/security/auth/kakao/KakaoUserRequest.java +++ /dev/null @@ -1,17 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class KakaoUserRequest { - - private String secure_resource; - private String property_keys; - - public KakaoUserRequest(String propertyKeys) { - this.secure_resource = "true"; - this.property_keys = propertyKeys; - } -} diff --git a/src/main/java/mocacong/server/service/AuthService.java b/src/main/java/mocacong/server/service/AuthService.java index 11d9c791..9898cd55 100644 --- a/src/main/java/mocacong/server/service/AuthService.java +++ b/src/main/java/mocacong/server/service/AuthService.java @@ -16,7 +16,6 @@ import mocacong.server.security.auth.JwtTokenProvider; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import mocacong.server.security.auth.apple.AppleOAuthUserProvider; -import mocacong.server.security.auth.kakao.KakaoOAuthUserProvider; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -28,7 +27,6 @@ public class AuthService { private final JwtTokenProvider jwtTokenProvider; private final PasswordEncoder passwordEncoder; private final AppleOAuthUserProvider appleOAuthUserProvider; - private final KakaoOAuthUserProvider kakaoOAuthUserProvider; public TokenResponse login(AuthLoginRequest request) { Member findMember = memberRepository.findByEmailAndPlatform(request.getEmail(), Platform.MOCACONG) @@ -53,12 +51,11 @@ public OAuthTokenResponse appleOAuthLogin(AppleLoginRequest request) { } public OAuthTokenResponse kakaoOAuthLogin(KakaoLoginRequest request) { - OAuthPlatformMemberResponse kakaoPlatformMember = - kakaoOAuthUserProvider.getKakaoPlatformMember(request.getCode()); + return generateOAuthTokenResponse( Platform.KAKAO, - kakaoPlatformMember.getEmail(), - kakaoPlatformMember.getPlatformId() + request.getEmail(), + request.getPlatformId() ); } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 36aec68d..e7eb022e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -69,10 +69,6 @@ oauth: iss: https://appleid.apple.com client-id: ${OAUTH_APPLE_CLIENT_ID} nonce: ${OAUTH_APPLE_NONCE} - kakao: - client-id: ${OAUTH_KAKAO_CLIENT_ID} - client-secret: ${OAUTH_KAKAO_CLIENT_SECRET} - redirect-uri: ${OAUTH_KAKAO_REDIRECT_URI} mocacong: nonce: ${MOCACONG_NONCE} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 62deb59c..106dba86 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -73,10 +73,6 @@ oauth: iss: https://appleid.apple.com client-id: ${OAUTH_APPLE_CLIENT_ID} nonce: ${OAUTH_APPLE_NONCE} - kakao: - client-id: ${OAUTH_KAKAO_CLIENT_ID} - client-secret: ${OAUTH_KAKAO_CLIENT_SECRET} - redirect-uri: ${OAUTH_KAKAO_REDIRECT_URI} mocacong: nonce: ${MOCACONG_NONCE} diff --git a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java index 4a865997..f385dc11 100644 --- a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java @@ -9,7 +9,6 @@ import mocacong.server.dto.response.TokenResponse; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import mocacong.server.security.auth.apple.AppleOAuthUserProvider; -import mocacong.server.security.auth.kakao.KakaoOAuthUserProvider; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -19,7 +18,6 @@ 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) @@ -27,8 +25,6 @@ public class AuthAcceptanceTest extends AcceptanceTest { @MockBean private AppleOAuthUserProvider appleOAuthUserProvider; - @MockBean - private KakaoOAuthUserProvider kakaoOAuthUserProvider; @Test @DisplayName("회원이 정상적으로 로그인한다") @@ -80,8 +76,7 @@ void loginAppleOAuth() { void loginKakaoOAuth() { String expected = "kth@kakao.com"; OAuthPlatformMemberResponse oauthResponse = new OAuthPlatformMemberResponse("1234321", expected); - when(kakaoOAuthUserProvider.getKakaoPlatformMember(anyString())).thenReturn(oauthResponse); - KakaoLoginRequest request = new KakaoLoginRequest("token"); + KakaoLoginRequest request = new KakaoLoginRequest(expected, oauthResponse.getPlatformId()); OAuthTokenResponse actual = RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) diff --git a/src/test/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProviderTest.java b/src/test/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProviderTest.java deleted file mode 100644 index ea2bf7ed..00000000 --- a/src/test/java/mocacong/server/security/auth/kakao/KakaoOAuthUserProviderTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package mocacong.server.security.auth.kakao; - -import feign.FeignException; -import mocacong.server.exception.unauthorized.InvalidTokenException; -import mocacong.server.security.auth.OAuthPlatformMemberResponse; -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 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.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; - -@SpringBootTest -class KakaoOAuthUserProviderTest { - - private static final String AUTHORIZATION_CODE = "test"; - - @Autowired - private KakaoOAuthUserProvider kakaoOAuthUserProvider; - @MockBean - private KakaoAccessTokenClient kakaoAccessTokenClient; - @MockBean - private KakaoUserClient kakaoUserClient; - - @Test - @DisplayName("Kakao OAuth 서버와 통신하여 사용자 정보를 발급받는다") - void getKakaoPlatformMember() { - String email = "kth990303@naver.com"; - String platformId = "1"; - KakaoAccessToken mockAccessToken = new KakaoAccessToken("accessToken"); - when(kakaoAccessTokenClient.getToken(any())).thenReturn(mockAccessToken); - KakaoUser mockKakaoUser = KakaoUser.of(1L, email); - when(kakaoUserClient.getUser(any(), anyString())).thenReturn(mockKakaoUser); - - OAuthPlatformMemberResponse actual = - kakaoOAuthUserProvider.getKakaoPlatformMember(AUTHORIZATION_CODE); - - assertAll( - () -> assertThat(actual.getEmail()).isEqualTo(email), - () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) - ); - } - - @Test - @DisplayName("Kakao OAuth 서버와 통신할 때 인가코드가 올바르지 않으면 예외를 반환한다") - void getKakaoPlatformMemberWhenInvalidAuthorizationCode() { - when(kakaoAccessTokenClient.getToken(any())).thenThrow(FeignException.class); - assertThatThrownBy(() -> kakaoOAuthUserProvider.getKakaoPlatformMember("invalid_token")) - .isInstanceOf(InvalidTokenException.class); - } -} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 5ded7200..6d20ba56 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -57,7 +57,3 @@ oauth: iss: https://appleid.apple.com client-id: test nonce: nonce - kakao: - client-id: test - client-secret: test - redirect-uri: test From d77d3728e4504ff537eef21a99bc7f838451616d Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:34:39 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20develop=20=EB=B0=B0=ED=8F=AC=20(#?= =?UTF-8?q?138)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 카페 이미지 신고 기능 POST API 구현 (#128) * feat: 카페 이미지 신고 컨트롤러 메서드 및 DTO 추가 * feat: 카페 이미지 신고 서비스 로직 및 마스킹 로직 구현 * feat: 사용하지 않는 카페 이미지 삭제 배치작업 수정 * feat: 카페 이미지 신고 이벤트 리스너 추가 * test: 카페 이미지 신고 관련 테스트 코드 작성 * test: 카페 이미지 신고 동시성 테스트 코드 추가 * test: 마스킹 된 카페 이미지들은 삭제 배치 작업에서 제외 검증 테스트 코드 추가 * refact: 코드 간결화 리팩터링 * refact: 코드 중복 로직 제거 * refact: 신고 로직 메서드 추출 * feat: Report에 BaseTime 상속 추가 --------- Co-authored-by: 임지수 * fix: 회원 경고 추가 기준 수정 (#130) * fix: 회원 경고 임계기준 수정 * fix: 회원 경고 기준 수정에 따라 테스트 코드 수정 --------- Co-authored-by: 임지수 --- .../server/controller/ReportController.java | 14 ++ .../mocacong/server/domain/CafeImage.java | 41 ++++ .../java/mocacong/server/domain/Comment.java | 6 +- .../java/mocacong/server/domain/Member.java | 2 +- .../java/mocacong/server/domain/Report.java | 15 +- .../dto/request/CafeImageReportRequest.java | 15 ++ .../dto/response/CafeImageReportResponse.java | 16 ++ .../DuplicateReportCafeImageException.java | 8 + .../InvalidCafeImageReportException.java | 8 + .../repository/CafeImageRepository.java | 2 +- .../mocacong/server/service/CafeService.java | 2 +- .../server/service/ReportService.java | 98 +++++++-- .../server/service/CafeServiceTest.java | 25 ++- .../service/ReportConcurrentServiceTest.java | 57 +++++ .../server/service/ReportServiceTest.java | 205 ++++++++++++++---- 15 files changed, 441 insertions(+), 73 deletions(-) create mode 100644 src/main/java/mocacong/server/dto/request/CafeImageReportRequest.java create mode 100644 src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java create mode 100644 src/main/java/mocacong/server/exception/badrequest/DuplicateReportCafeImageException.java create mode 100644 src/main/java/mocacong/server/exception/badrequest/InvalidCafeImageReportException.java diff --git a/src/main/java/mocacong/server/controller/ReportController.java b/src/main/java/mocacong/server/controller/ReportController.java index 42f83160..c4068128 100644 --- a/src/main/java/mocacong/server/controller/ReportController.java +++ b/src/main/java/mocacong/server/controller/ReportController.java @@ -4,7 +4,9 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import mocacong.server.dto.request.CafeImageReportRequest; import mocacong.server.dto.request.CommentReportRequest; +import mocacong.server.dto.response.CafeImageReportResponse; import mocacong.server.dto.response.CommentReportResponse; import mocacong.server.security.auth.LoginUserId; import mocacong.server.service.ReportService; @@ -32,4 +34,16 @@ public ResponseEntity reportComment( CommentReportResponse response = reportService.reportComment(memberId, commentId, request.getMyReportReason()); return ResponseEntity.ok(response); } + + @Operation(summary = "카페 이미지 신고") + @SecurityRequirement(name = "JWT") + @PostMapping("/img/{cafeImageId}") + public ResponseEntity reportCafeImage( + @LoginUserId Long memberId, + @PathVariable Long cafeImageId, + @RequestBody @Valid CafeImageReportRequest request + ) { + CafeImageReportResponse response = reportService.reportCafeImage(memberId, cafeImageId, request.getMyReportReason()); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/mocacong/server/domain/CafeImage.java b/src/main/java/mocacong/server/domain/CafeImage.java index 549dc814..59309338 100644 --- a/src/main/java/mocacong/server/domain/CafeImage.java +++ b/src/main/java/mocacong/server/domain/CafeImage.java @@ -5,6 +5,8 @@ import lombok.NoArgsConstructor; import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; @Entity @Table(name = "cafe_image") @@ -12,6 +14,8 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class CafeImage extends BaseTime { + private static final int REPORT_CAFE_IMAGE_THRESHOLD_COUNT = 3; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "cafe_image_id") @@ -31,11 +35,18 @@ public class CafeImage extends BaseTime { @JoinColumn(name = "member_id") private Member member; + @OneToMany(mappedBy = "cafeImage", cascade = CascadeType.ALL, orphanRemoval = true) + private List reports = new ArrayList<>(); + + @Column(name = "is_masked") + private boolean isMasked; + public CafeImage(String imgUrl, Boolean isUsed, Cafe cafe, Member member) { this.imgUrl = imgUrl; this.isUsed = isUsed; this.cafe = cafe; this.member = member; + this.isMasked = false; } public boolean isOwned(Member member) { @@ -49,4 +60,34 @@ public void updateImgUrl(String imgUrl) { public void removeMember() { this.member = null; } + + public boolean hasAlreadyReported(Member member) { + return this.reports.stream() + .anyMatch(report -> report.getReporter().equals(member)); + } + + public void addReport(Report report) { + reports.add(report); + } + + public int getReportsCount() { + return reports.size(); + } + + public boolean isDeletedMember() { + return member == null; + } + + public boolean isSavedByMember(Member member) { + return this.member != null && this.member.equals(member); + } + + public boolean isReportThresholdExceeded() { + return getReportsCount() >= REPORT_CAFE_IMAGE_THRESHOLD_COUNT; + } + + public void maskCafeImage() { + this.isUsed = false; + this.isMasked = true; + } } diff --git a/src/main/java/mocacong/server/domain/Comment.java b/src/main/java/mocacong/server/domain/Comment.java index 521e9a6c..4a8c88ff 100644 --- a/src/main/java/mocacong/server/domain/Comment.java +++ b/src/main/java/mocacong/server/domain/Comment.java @@ -91,10 +91,6 @@ 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)); @@ -113,7 +109,7 @@ public void addReport(Report report) { } public void updateIsMasked(boolean isMasked) { - this.isMasked= isMasked; + this.isMasked = isMasked; } public int getLikeCounts() { diff --git a/src/main/java/mocacong/server/domain/Member.java b/src/main/java/mocacong/server/domain/Member.java index 42cf1f62..c927c00a 100644 --- a/src/main/java/mocacong/server/domain/Member.java +++ b/src/main/java/mocacong/server/domain/Member.java @@ -17,7 +17,7 @@ public class Member extends BaseTime { private static final Pattern NICKNAME_REGEX = Pattern.compile("^[a-zA-Zㄱ-ㅎㅏ-ㅣ가-힣]{2,6}$"); - private static final int REPORT_MEMBER_THRESHOLD_COUNT = 11; + private static final int REPORT_MEMBER_THRESHOLD_COUNT = 5; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/mocacong/server/domain/Report.java b/src/main/java/mocacong/server/domain/Report.java index 3edc7014..c0e17862 100644 --- a/src/main/java/mocacong/server/domain/Report.java +++ b/src/main/java/mocacong/server/domain/Report.java @@ -7,10 +7,11 @@ @Entity @Table(name = "report", uniqueConstraints = { - @UniqueConstraint(columnNames = { "comment_id", "member_id" }) + @UniqueConstraint(columnNames = { "comment_id", "member_id" }), + @UniqueConstraint(columnNames = { "cafe_image_id", "member_id" }) }) @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Report { +public class Report extends BaseTime { private static final int MAXIMUM_COMMENT_LENGTH = 200; @@ -23,6 +24,10 @@ public class Report { @JoinColumn(name = "comment_id") private Comment comment; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "cafe_image_id") + private CafeImage cafeImage; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member reporter; @@ -41,6 +46,12 @@ public Report(Comment comment, Member reporter, ReportReason reportReason) { this.originalContent = comment.getContent(); } + public Report(CafeImage cafeImage, Member reporter, ReportReason reportReason) { + this.cafeImage = cafeImage; + this.reporter = reporter; + this.reportReason = reportReason; + } + public Member getReporter() { return reporter; } diff --git a/src/main/java/mocacong/server/dto/request/CafeImageReportRequest.java b/src/main/java/mocacong/server/dto/request/CafeImageReportRequest.java new file mode 100644 index 00000000..8248517a --- /dev/null +++ b/src/main/java/mocacong/server/dto/request/CafeImageReportRequest.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 CafeImageReportRequest { + + @NotBlank(message = "3009:공백일 수 없습니다.") + private String myReportReason; +} diff --git a/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java b/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java new file mode 100644 index 00000000..a78b0881 --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.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 CafeImageReportResponse { + + private int cafeImageReportCount; + + private int userReportCount; +} diff --git a/src/main/java/mocacong/server/exception/badrequest/DuplicateReportCafeImageException.java b/src/main/java/mocacong/server/exception/badrequest/DuplicateReportCafeImageException.java new file mode 100644 index 00000000..6c1e6b4c --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/DuplicateReportCafeImageException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class DuplicateReportCafeImageException extends BadRequestException { + + public DuplicateReportCafeImageException() { + super("이미 신고한 카페 이미지입니다.", 7004); + } +} diff --git a/src/main/java/mocacong/server/exception/badrequest/InvalidCafeImageReportException.java b/src/main/java/mocacong/server/exception/badrequest/InvalidCafeImageReportException.java new file mode 100644 index 00000000..82bbc8e4 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/InvalidCafeImageReportException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class InvalidCafeImageReportException extends BadRequestException { + + public InvalidCafeImageReportException() { + super("자신이 등록한 카페 이미지는 신고할 수 없습니다.", 7003); + } +} diff --git a/src/main/java/mocacong/server/repository/CafeImageRepository.java b/src/main/java/mocacong/server/repository/CafeImageRepository.java index 82359657..acb755d3 100644 --- a/src/main/java/mocacong/server/repository/CafeImageRepository.java +++ b/src/main/java/mocacong/server/repository/CafeImageRepository.java @@ -17,5 +17,5 @@ public interface CafeImageRepository extends JpaRepository { List findAllByMemberId(Long memberId); - List findAllByIsUsedFalse(); + List findAllByIsUsedFalseAndIsMaskedFalse(); } diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index 4256a75a..2c1db50e 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -379,7 +379,7 @@ public void updateCafeImagesWhenMemberDelete(DeleteMemberEvent event) { @Transactional public void deleteNotUsedCafeImages() { - List cafeImages = cafeImageRepository.findAllByIsUsedFalse(); + List cafeImages = cafeImageRepository.findAllByIsUsedFalseAndIsMaskedFalse(); List imgUrls = cafeImages.stream() .map(CafeImage::getImgUrl) .collect(Collectors.toList()); diff --git a/src/main/java/mocacong/server/service/ReportService.java b/src/main/java/mocacong/server/service/ReportService.java index 4b1cd614..c3aeb616 100644 --- a/src/main/java/mocacong/server/service/ReportService.java +++ b/src/main/java/mocacong/server/service/ReportService.java @@ -2,15 +2,17 @@ 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.domain.*; +import mocacong.server.dto.response.CafeImageReportResponse; import mocacong.server.dto.response.CommentReportResponse; +import mocacong.server.exception.badrequest.DuplicateReportCafeImageException; import mocacong.server.exception.badrequest.DuplicateReportCommentException; +import mocacong.server.exception.badrequest.InvalidCafeImageReportException; import mocacong.server.exception.badrequest.InvalidCommentReportException; +import mocacong.server.exception.notfound.NotFoundCafeImageException; import mocacong.server.exception.notfound.NotFoundCommentException; import mocacong.server.exception.notfound.NotFoundMemberException; +import mocacong.server.repository.CafeImageRepository; import mocacong.server.repository.CommentRepository; import mocacong.server.repository.MemberRepository; import mocacong.server.repository.ReportRepository; @@ -29,6 +31,7 @@ public class ReportService { private final MemberRepository memberRepository; private final CommentRepository commentRepository; private final ReportRepository reportRepository; + private final CafeImageRepository cafeImageRepository; public CommentReportResponse reportComment(Long memberId, Long commentId, String reportReason) { Member reporter = memberRepository.findById(memberId) @@ -40,19 +43,15 @@ public CommentReportResponse reportComment(Long memberId, Long commentId, String 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.isDeletedMember()) { if (comment.isReportThresholdExceeded()) { - commenter.incrementMemberReportCount(); maskReportedComment(comment); - comment.updateIsMasked(true); } + } else { + Member commenter = comment.getMember(); + validateCommentReporter(reporter, comment); + validateCommentReportThreshold(comment); + commenter.incrementMemberReportCount(); } } catch (DataIntegrityViolationException e) { throw new DuplicateReportCommentException(); @@ -68,6 +67,24 @@ private void createCommentReport(Comment comment, Member reporter, String report comment.addReport(new Report(comment, reporter, reason)); } + private void maskReportedComment(Comment comment) { + comment.maskComment(); + comment.maskAuthor(); + comment.updateIsMasked(true); + } + + private void validateCommentReporter(Member reporter, Comment comment) { + if (comment.isWrittenByMember(reporter)) { + throw new InvalidCommentReportException(); + } + } + + private void validateCommentReportThreshold(Comment comment) { + if (comment.isReportThresholdExceeded()) { + maskReportedComment(comment); + } + } + @EventListener public void updateCommentReportWhenMemberDelete(DeleteMemberEvent event) { Member member = event.getMember(); @@ -75,8 +92,55 @@ public void updateCommentReportWhenMemberDelete(DeleteMemberEvent event) { .forEach(Report::removeReporter); } - private void maskReportedComment(Comment comment) { - comment.maskComment(); - comment.maskAuthor(); + public CafeImageReportResponse reportCafeImage(Long memberId, Long cafeImageId, String reportReason) { + Member reporter = memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); + CafeImage cafeImage = cafeImageRepository.findById(cafeImageId) + .orElseThrow(NotFoundCafeImageException::new); + try { + createCafeImageReport(cafeImage, reporter, reportReason); + + // 카페 이미지를 등록한 회원이 탈퇴한 경우 + if (cafeImage.isDeletedMember()) { + if (cafeImage.isReportThresholdExceeded()) { + cafeImage.maskCafeImage(); + } + } else { + Member author = cafeImage.getMember(); + validateCafeImageReporter(reporter, cafeImage); + validateCafeImageReportThreshold(cafeImage); + author.incrementMemberReportCount(); + } + } catch (DataIntegrityViolationException e) { + throw new DuplicateReportCafeImageException(); + } + return new CafeImageReportResponse(cafeImage.getReportsCount(), reporter.getReportCount()); + } + + private void createCafeImageReport(CafeImage cafeImage, Member reporter, String reportReason) { + if (cafeImage.hasAlreadyReported(reporter)) { + throw new DuplicateReportCafeImageException(); + } + ReportReason reason = ReportReason.from(reportReason); + cafeImage.addReport(new Report(cafeImage, reporter, reason)); + } + + private void validateCafeImageReporter(Member reporter, CafeImage cafeImage) { + if (cafeImage.isSavedByMember(reporter)) { + throw new InvalidCafeImageReportException(); + } + } + + private void validateCafeImageReportThreshold(CafeImage cafeImage) { + if (cafeImage.isReportThresholdExceeded()) { + cafeImage.maskCafeImage(); + } + } + + @EventListener + public void updateCafeImageReportWhenMemberDelete(DeleteMemberEvent event) { + Member member = event.getMember(); + reportRepository.findAllByReporter(member) + .forEach(Report::removeReporter); } } diff --git a/src/test/java/mocacong/server/service/CafeServiceTest.java b/src/test/java/mocacong/server/service/CafeServiceTest.java index 9b49e535..2016e7c2 100644 --- a/src/test/java/mocacong/server/service/CafeServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeServiceTest.java @@ -916,7 +916,6 @@ void saveCafeImages() throws IOException { ); } - @Test @DisplayName("사용자가 카페 이미지를 총 3개 보다 많이 저장하면 예외가 발생한다.") void saveCafeImagesOver3() throws IOException { @@ -1257,4 +1256,28 @@ void deleteNotUsedCafeImages() { .containsExactlyInAnyOrder("test_img.jpg") ); } + + @Test + @DisplayName("신고되어 마스킹된 카페 이미지들은 배치 작업으로 삭제하지 않는다") + void notDeleteReportedCafeImages() { + List reportedImgUrls = List.of("test_img2.jpg", "test_img3.jpg"); + Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); + memberRepository.save(member); + Cafe cafe = new Cafe("2143154352323", "케이카페"); + cafeRepository.save(cafe); + CafeImage cafeImage1 = new CafeImage("test_img.jpg", true, cafe, member); + cafeImageRepository.save(cafeImage1); + CafeImage cafeImage2 = new CafeImage("test_img2.jpg", false, cafe, member); + cafeImage2.maskCafeImage(); + cafeImageRepository.save(cafeImage2); + CafeImage cafeImage3 = new CafeImage("test_img3.jpg", false, cafe, member); + cafeImage3.maskCafeImage(); + cafeImageRepository.save(cafeImage3); + + doNothing().when(awsS3Uploader).deleteImages(new DeleteNotUsedImagesEvent(reportedImgUrls)); + cafeService.deleteNotUsedCafeImages(); + List actual = cafeImageRepository.findAll(); + + assertThat(actual).hasSize(3); + } } diff --git a/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java index 21a7c152..a4fdc59e 100644 --- a/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java @@ -2,14 +2,21 @@ import mocacong.server.domain.Cafe; import mocacong.server.domain.Member; +import mocacong.server.dto.response.CafeImageReportResponse; +import mocacong.server.dto.response.CafeImagesSaveResponse; import mocacong.server.dto.response.CommentReportResponse; import mocacong.server.dto.response.CommentSaveResponse; import mocacong.server.repository.CafeRepository; import mocacong.server.repository.MemberRepository; +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.mock.web.MockMultipartFile; +import java.io.FileInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -19,6 +26,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.Mockito.when; @ServiceTest public class ReportConcurrentServiceTest { @@ -27,10 +35,15 @@ public class ReportConcurrentServiceTest { @Autowired private ReportService reportService; @Autowired + private CafeService cafeService; + @Autowired private CafeRepository cafeRepository; @Autowired private MemberRepository memberRepository; + @MockBean + private AwsS3Uploader awsS3Uploader; + @Test @DisplayName("타 사용자가 작성한 댓글을 동시에 여러 번 신고하려 해도 한 번만 신고된다") void reportCommentWithConcurrent() throws InterruptedException { @@ -69,4 +82,48 @@ void reportCommentWithConcurrent() throws InterruptedException { () -> assertThat(exceptions.size()).isEqualTo(2) ); } + + @Test + @DisplayName("타 사용자가 등록한 카페 이미지를 동시에 여러 번 신고하려 해도 한 번만 신고된다") + void reportCafeImageWithConcurrent() throws InterruptedException, IOException { + 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); + + 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"); + CafeImagesSaveResponse cafeImagesSaveResponse = cafeService.saveCafeImage(member1.getId(), mapId, List.of(mockMultipartFile)); + 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 { + CafeImageReportResponse response = reportService.reportCafeImage(member2.getId(), 1L, + "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 index fd178a1f..e4dd6709 100644 --- a/src/test/java/mocacong/server/service/ReportServiceTest.java +++ b/src/test/java/mocacong/server/service/ReportServiceTest.java @@ -1,22 +1,25 @@ 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.domain.*; +import mocacong.server.dto.response.CafeImageReportResponse; 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.exception.badrequest.*; +import mocacong.server.repository.CafeImageRepository; import mocacong.server.repository.CafeRepository; import mocacong.server.repository.CommentRepository; import mocacong.server.repository.MemberRepository; +import mocacong.server.support.AwsS3Uploader; +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.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; +import java.io.FileInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -24,6 +27,7 @@ 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.mockito.Mockito.when; @ServiceTest public class ReportServiceTest { @@ -34,6 +38,8 @@ public class ReportServiceTest { private MemberService memberService; @Autowired private CommentService commentService; + @Autowired + private CafeService cafeService; @Autowired private CommentRepository commentRepository; @@ -41,9 +47,14 @@ public class ReportServiceTest { private MemberRepository memberRepository; @Autowired private CafeRepository cafeRepository; + @Autowired + private CafeImageRepository cafeImageRepository; + + @MockBean + private AwsS3Uploader awsS3Uploader; @Test - @DisplayName("타 사용자가 작성한 댓글을 신고한다") + @DisplayName("타 사용자가 작성한 댓글을 신고하면 댓글 작성자의 경고 횟수가 1회씩 증가한다") void reportComment() { String email1 = "kth990303@naver.com"; String email2 = "dlawotn3@naver.com"; @@ -58,8 +69,12 @@ void reportComment() { commentService.save(member1.getId(), mapId, "이 카페 완전 돈 아깝;;"); CommentReportResponse response = reportService.reportComment(member2.getId(), 1L, reportReason); + Optional commenter = memberRepository.findById(1L); - assertThat(response.getCommentReportCount()).isEqualTo(1); + Assertions.assertAll( + () -> assertThat(response.getCommentReportCount()).isEqualTo(1), + () -> assertThat(commenter.get().getReportCount()).isEqualTo(1) + ); } @Test @@ -116,7 +131,7 @@ void reportDuplicateComment() { } @Test - @DisplayName("5번 이상 신고된 댓글은 마스킹되며 해당 작성자의 신고 횟수가 1씩 증가한다") + @DisplayName("5번 이상 신고된 댓글은 마스킹된다") void maskCauseReport5timesReportedComment() { String mapId = "2143154352323"; List members = new ArrayList<>(); @@ -137,7 +152,6 @@ void maskCauseReport5timesReportedComment() { 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), @@ -145,40 +159,7 @@ void maskCauseReport5timesReportedComment() { .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) + () -> assertThat(reportedComment.get().isMasked()).isTrue() ); } @@ -203,4 +184,138 @@ void reportCommentPostedDeletedMember() { assertThat(response.getCommentReportCount()).isEqualTo(1); } + + @Test + @DisplayName("타 사용자가 등록한 카페 이미지를 신고하면 카페 이미지를 등록한 회원의 경고 횟수가 1회씩 증가한다") + void reportCafeImage() throws IOException { + 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); + 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(member1.getId(), mapId, List.of(mockMultipartFile)); + + CafeImageReportResponse response = reportService.reportCafeImage(member2.getId(), 1L, reportReason); + Optional author = memberRepository.findById(1L); + + Assertions.assertAll( + () -> assertThat(response.getCafeImageReportCount()).isEqualTo(1), + () -> assertThat(author.get().getReportCount()).isEqualTo(1) + ); + } + + @Test + @DisplayName("본인이 등록한 카페 이미지에 대해 신고를 시도할 시 예외를 반환한다") + void reportMyCafeImage() throws IOException { + 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); + 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(), mapId, List.of(mockMultipartFile)); + + assertThatThrownBy(() -> reportService.reportCafeImage(member.getId(), 1L, "insult")) + .isInstanceOf(InvalidCafeImageReportException.class); + } + + @Test + @DisplayName("이미 신고한 카페 이미지에 대해 신고를 시도할 시 예외를 반환한다") + void reportDuplicateCafeImage() throws IOException { + 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); + 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(member1.getId(), mapId, List.of(mockMultipartFile)); + + reportService.reportCafeImage(member2.getId(), 1L, "inappropriate_content"); + + assertThatThrownBy(() -> reportService.reportCafeImage(member2.getId(), 1L, "insult")) + .isInstanceOf(DuplicateReportCafeImageException.class); + } + + @Test + @DisplayName("3번 이상 신고된 카페 이미지는 마스킹된다") + void maskCauseReport3timesReportedCafeImage() throws IOException { + String mapId = "2143154352323"; + List members = new ArrayList<>(); + for (int i = 1; i <= 4; 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); + 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(members.get(0).getId(), mapId, List.of(mockMultipartFile)); + reportService.reportCafeImage(members.get(1).getId(), 1L, "inappropriate_content"); + reportService.reportCafeImage(members.get(2).getId(), 1L, "inappropriate_content"); + + CafeImageReportResponse reportResponse = reportService.reportCafeImage(members.get(3).getId(), + 1L, "inappropriate_content"); + Optional reportedCafeImage = cafeImageRepository.findById(1L); + + assertAll( + () -> assertThat(reportResponse.getCafeImageReportCount()).isEqualTo(3), + () -> assertThat(reportedCafeImage.get().isMasked()).isTrue(), + () -> assertThat(reportedCafeImage.get().getIsUsed()).isFalse() + ); + } + + @Test + @DisplayName("5번 이상 신고된 회원은 Status가 INACTIVE로 전환된다") + void setInactiveCause5timesReportedComment() { + 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); + } + 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(), + mapId, 0, 3); + Optional commenter = memberRepository.findById(1L); + + assertAll( + () -> assertThat(reportedComment.getComments().get(0).getContent()) + .isEqualTo("삭제된 댓글입니다"), + () -> assertThat(commenter.get().getReportCount()).isEqualTo(5), + () -> assertThat(commenter.get().getStatus()).isEqualTo(Status.INACTIVE) + ); + } } From b2fad09c4769965f03ecea9c59bdf9d9fa6bafac Mon Sep 17 00:00:00 2001 From: TaeHyeon Kim Date: Fri, 1 Dec 2023 17:34:52 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=9A=8C=EC=9B=90=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=8B=9C,=20=EC=B9=B4=ED=8E=98=20=EB=B3=84=EB=A1=9C=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#136)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 엔티티 영역에서 mapId 가 같은 카페일 경우 동일 카페로 간주하도록 구현 * feat: 자신이 댓글 쓴 카페 목록 조회 시, 카페 별로 grouping 되도록 구현 * test: 기능 구현으로 인해 테스트 변경 * feat: 카페 mapId 에 따른 페이지네이션 처리 구현 * fix: 범위 벗어나는 toIndex 수정 * fix: 범위 잘못된 toIndex 수정 * feat: equals override 메서드 instanceof 이용하도록 구현 --------- Co-authored-by: 김태현 --- .../java/mocacong/server/domain/Cafe.java | 17 ++++++++++ .../dto/response/MyCommentCafeResponse.java | 13 +++++++- .../mocacong/server/service/CafeService.java | 31 ++++++++++++++----- .../acceptance/MemberAcceptanceTest.java | 5 ++- .../java/mocacong/server/domain/CafeTest.java | 10 ++++++ .../server/service/CafeServiceTest.java | 10 +++--- 6 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/main/java/mocacong/server/domain/Cafe.java b/src/main/java/mocacong/server/domain/Cafe.java index 62bf598d..fe72aa86 100644 --- a/src/main/java/mocacong/server/domain/Cafe.java +++ b/src/main/java/mocacong/server/domain/Cafe.java @@ -107,4 +107,21 @@ public void addScore(Score score) { public void addReview(Review review) { this.reviews.add(review); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Cafe)) { + return false; + } + Cafe cafe = (Cafe) o; + return Objects.equals(mapId, cafe.mapId); + } + + @Override + public int hashCode() { + return Objects.hash(mapId); + } } diff --git a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java index 84aeabd4..4fd731d8 100644 --- a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java @@ -1,6 +1,10 @@ package mocacong.server.dto.response; +import java.util.List; +import java.util.stream.Collectors; import lombok.*; +import mocacong.server.domain.Cafe; +import mocacong.server.domain.Comment; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -11,5 +15,12 @@ public class MyCommentCafeResponse { private String mapId; private String name; private String studyType; - private String comment; + private List comments; + + public static MyCommentCafeResponse of(Cafe cafe, List comments) { + List contents = comments.stream() + .map(Comment::getContent) + .collect(Collectors.toList()); + return new MyCommentCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), contents); + } } diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index 2c1db50e..9433a9a6 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -169,17 +169,32 @@ public MyReviewCafesResponse findMyReviewCafes(Long memberId, Integer page, int 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)); + List comments = commentRepository.findAllByMemberId(member.getId()); List responses = comments.stream() - .map(comment -> new MyCommentCafeResponse( - comment.getCafe().getMapId(), - comment.getCafe().getName(), - comment.getCafe().getStudyType(), - comment.getContent() - )) + .collect(Collectors.groupingByConcurrent(Comment::getCafe)) + .entrySet() + .stream() + .map(commentsGroupingByCafe -> + MyCommentCafeResponse.of(commentsGroupingByCafe.getKey(), commentsGroupingByCafe.getValue())) .collect(Collectors.toList()); - return new MyCommentCafesResponse(comments.isLast(), responses); + + int toIndex = Math.min((page + 1) * count, responses.size()); + int fromIndex = Math.min(toIndex, page * count); + + return new MyCommentCafesResponse(findIsEnd(page, count, responses), responses.subList(fromIndex, toIndex)); + } + + /* + * TODO (23.11.11.) + * comments 를 Slice 로 받아온 후 grouping 할 경우 페이지네이션 시 count 보다 적은 데이터 수가 반환될 수 있음. + * 따라서 comments 전체를 받아온 후, mapId로 grouping 해야 한 후 페이지네이션해야 하므로 isLast 여부를 jpa Slice 로 구할 수 없음. + * + * 또한, 현재 mapId가 동일한 카페의 댓글 전체를 리스트로 반환하므로 API 스펙 협의 및 로직 개선 필요. + */ + private boolean findIsEnd(int page, int count, List responses) { + int lastDataIndex = (page + 1) * count - 1; + return responses.size() - 1 <= lastDataIndex; } @CacheEvict(key = "#mapId", value = "cafePreviewCache") diff --git a/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java index e9b38695..34feef91 100644 --- a/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java @@ -397,7 +397,10 @@ void findMyCommentCafes() { .extract() .as(MyCommentCafesResponse.class); - assertThat(response.getCafes()).hasSize(3); + assertAll( + ()->assertThat(response.getCafes()).hasSize(2), + ()->assertThat(response.getCafes().get(0).getComments()).containsExactlyInAnyOrder("댓글3") + ); } @Test diff --git a/src/test/java/mocacong/server/domain/CafeTest.java b/src/test/java/mocacong/server/domain/CafeTest.java index f0b6029a..fb27c11f 100644 --- a/src/test/java/mocacong/server/domain/CafeTest.java +++ b/src/test/java/mocacong/server/domain/CafeTest.java @@ -8,6 +8,16 @@ class CafeTest { + @Test + @DisplayName("카페 mapId가 같으면 동일 카페로 간주한다") + void equals() { + String cafeMapId = "12345"; + Cafe cafe1 = new Cafe(cafeMapId, "케이카페"); + Cafe cafe2 = new Cafe(cafeMapId, "이름만다른케이카페"); + + assertThat(cafe1.equals(cafe2)).isTrue(); + } + @Test @DisplayName("카페에 평점을 기여한 사람이 없으면 0점을 반환한다") void findScoreWithNoReviews() { diff --git a/src/test/java/mocacong/server/service/CafeServiceTest.java b/src/test/java/mocacong/server/service/CafeServiceTest.java index 2016e7c2..8263bad3 100644 --- a/src/test/java/mocacong/server/service/CafeServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeServiceTest.java @@ -487,14 +487,14 @@ void findMyCommentCafes() { commentRepository.save(new Comment(cafe2, member1, "댓글3")); commentRepository.save(new Comment(cafe2, member2, "댓글4")); - MyCommentCafesResponse actual = cafeService.findMyCommentCafes(member1.getId(), 0, 5); + MyCommentCafesResponse actual = cafeService.findMyCommentCafes(member1.getId(), 0, 3); assertAll( () -> assertThat(actual.getIsEnd()).isTrue(), - () -> assertThat(actual.getCafes()).hasSize(3), - () -> assertThat(actual.getCafes().get(0).getComment()).isEqualTo("댓글1"), - () -> assertThat(actual.getCafes().get(1).getComment()).isEqualTo("댓글2"), - () -> assertThat(actual.getCafes().get(2).getComment()).isEqualTo("댓글3") + // 댓글 수는 3개지만, 카페 종류가 2종류이므로 response size는 2개 + () -> assertThat(actual.getCafes()).hasSize(2), + () -> assertThat(actual.getCafes().get(0).getComments()).containsExactlyInAnyOrder("댓글1", "댓글2"), + () -> assertThat(actual.getCafes().get(1).getComments()).containsExactlyInAnyOrder("댓글3") ); } From 00034c2f1b7a323e251ffe0ced1ca3af344bbba5 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim <76640167+jung-woo-kim@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:59:26 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=B9=B4=ED=8E=98=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=8B=9C=20roadAddress=20=EB=B0=8F=20=EB=82=B4=EA=B0=80=20?= =?UTF-8?q?=EB=82=A8=EA=B8=B4=20=EB=A6=AC=EB=B7=B0=20=ED=95=A8=EA=BB=98=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: cafe roadAddress 추가 * feat: 리뷰 남긴 카페 조회 시 내가 남긴 리뷰 함께 조회 * fix: 동시성 테스트 실패 수정 * feat: 코드리뷰 반영 --- .../java/mocacong/server/domain/Cafe.java | 33 +++++ .../dto/request/CafeRegisterRequest.java | 6 + .../server/dto/response/FindCafeResponse.java | 3 + .../dto/response/MyCommentCafeResponse.java | 5 +- .../dto/response/MyFavoriteCafeResponse.java | 2 +- .../dto/response/MyReviewCafeResponse.java | 17 ++- .../dto/response/PreviewCafeResponse.java | 2 + .../server/repository/CafeRepository.java | 4 +- .../mocacong/server/service/CafeService.java | 21 ++- .../server/acceptance/CafeAcceptanceTest.java | 30 ++--- .../acceptance/CommentAcceptanceTest.java | 14 +- .../acceptance/CommentLikeAcceptanceTest.java | 4 +- .../acceptance/FavoriteAcceptanceTest.java | 4 +- .../acceptance/MemberAcceptanceTest.java | 14 +- .../acceptance/ReportAcceptanceTest.java | 2 +- .../mocacong/server/domain/BaseTimeTest.java | 2 +- .../mocacong/server/domain/CafeImageTest.java | 8 +- .../java/mocacong/server/domain/CafeTest.java | 30 +++-- .../mocacong/server/domain/CommentTest.java | 8 +- .../mocacong/server/domain/ScoreTest.java | 4 +- .../repository/CafeImageRepositoryTest.java | 2 +- .../server/repository/CafeRepositoryTest.java | 22 ++-- .../repository/CommentLikeRepositoryTest.java | 2 +- .../repository/CommentRepositoryTest.java | 2 +- .../repository/FavoriteRepositoryTest.java | 4 +- .../repository/ReviewRepositoryTest.java | 2 +- .../repository/ScoreRepositoryTest.java | 2 +- .../service/CafeConcurrentServiceTest.java | 11 +- .../server/service/CafeServiceTest.java | 123 +++++++++--------- .../CommentLikeConcurrentServiceTest.java | 2 +- .../service/CommentLikeServiceTest.java | 10 +- .../server/service/CommentServiceTest.java | 24 ++-- .../FavoriteConcurrentServiceTest.java | 2 +- .../server/service/FavoriteServiceTest.java | 8 +- .../service/ReportConcurrentServiceTest.java | 4 +- .../server/service/ReportServiceTest.java | 14 +- 36 files changed, 265 insertions(+), 182 deletions(-) diff --git a/src/main/java/mocacong/server/domain/Cafe.java b/src/main/java/mocacong/server/domain/Cafe.java index fe72aa86..b6d44076 100644 --- a/src/main/java/mocacong/server/domain/Cafe.java +++ b/src/main/java/mocacong/server/domain/Cafe.java @@ -29,6 +29,12 @@ public class Cafe extends BaseTime { @Column(name = "map_id", unique = true, nullable = false) private String mapId; + @Column(name = "road_address") + private String roadAddress; + + @Column(name = "phone_number") + private String phoneNumber; + @OneToMany(mappedBy = "cafe", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) private List score; @@ -54,6 +60,29 @@ public Cafe(String mapId, String name) { this.comments = new ArrayList<>(); } + public Cafe(String mapId, String name, String roadAddress) { + this.mapId = mapId; + this.name = name; + this.roadAddress = roadAddress; + this.cafeDetail = new CafeDetail(); + this.cafeImages = new ArrayList<>(); + this.score = new ArrayList<>(); + this.reviews = new ArrayList<>(); + this.comments = new ArrayList<>(); + } + + public Cafe(String mapId, String name, String roadAddress, String phoneNumber) { + this.mapId = mapId; + this.name = name; + this.roadAddress = roadAddress; + this.phoneNumber = phoneNumber; + this.cafeDetail = new CafeDetail(); + this.cafeImages = new ArrayList<>(); + this.score = new ArrayList<>(); + this.reviews = new ArrayList<>(); + this.comments = new ArrayList<>(); + } + public void updateCafeDetails() { StudyType studyType = getMostFrequentStudyType(); Wifi wifi = Wifi.averageFrom(reviews.stream().map(Review::getWifi) @@ -71,6 +100,10 @@ public void updateCafeDetails() { this.cafeDetail = new CafeDetail(studyType, wifi, parking, toilet, desk, power, sound); } + public void updateCafeRoadAddress(String roadAddress) { + this.roadAddress = roadAddress; + } + private StudyType getMostFrequentStudyType() { int solo = 0, group = 0; for (Review review : reviews) { diff --git a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java index b74fcd39..06151186 100644 --- a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java +++ b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java @@ -14,4 +14,10 @@ public class CafeRegisterRequest { @NotBlank(message = "1012:공백일 수 없습니다.") private String name; + + @NotBlank(message = "1012:공백일 수 없습니다.") + private String roadAddress; + + @NotBlank(message = "1012:공백일 수 없습니다.") + private String phoneNumber; } diff --git a/src/main/java/mocacong/server/dto/response/FindCafeResponse.java b/src/main/java/mocacong/server/dto/response/FindCafeResponse.java index 893682ce..f11e9a60 100644 --- a/src/main/java/mocacong/server/dto/response/FindCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/FindCafeResponse.java @@ -11,6 +11,9 @@ public class FindCafeResponse { private Boolean favorite; private Long favoriteId; + private String name; + private String roadAddress; + private String phoneNumber; private double score; private Integer myScore; private String studyType; diff --git a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java index 4fd731d8..c85bcecd 100644 --- a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java @@ -2,7 +2,9 @@ import java.util.List; import java.util.stream.Collectors; + import lombok.*; + import mocacong.server.domain.Cafe; import mocacong.server.domain.Comment; @@ -15,12 +17,13 @@ public class MyCommentCafeResponse { private String mapId; private String name; private String studyType; + private String roadAddress; private List comments; public static MyCommentCafeResponse of(Cafe cafe, List comments) { List contents = comments.stream() .map(Comment::getContent) .collect(Collectors.toList()); - return new MyCommentCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), contents); + return new MyCommentCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), cafe.getRoadAddress(), contents); } } diff --git a/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java index c8f552f6..8ba5a917 100644 --- a/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyFavoriteCafeResponse.java @@ -7,9 +7,9 @@ @AllArgsConstructor @ToString public class MyFavoriteCafeResponse { - private String mapId; private String name; private String studyType; private double score; + private String roadAddress; } diff --git a/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java index e49b21d2..a93fd23f 100644 --- a/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java @@ -1,6 +1,7 @@ package mocacong.server.dto.response; import lombok.*; +import mocacong.server.domain.CafeDetail; import mocacong.server.domain.cafedetail.StudyType; @Getter @@ -12,11 +13,25 @@ public class MyReviewCafeResponse { private String name; private String myStudyType; private int myScore; + private String roadAddress; + private String wifi; + private String parking; + private String toilet; + private String power; + private String sound; + private String desk; - public MyReviewCafeResponse(String mapId, String name, StudyType myStudyType, int myScore) { + public MyReviewCafeResponse(String mapId, String name, StudyType myStudyType, int myScore, String roadAddress, CafeDetail cafeDetail) { this.mapId = mapId; this.name = name; this.myStudyType = myStudyType.getValue(); this.myScore = myScore; + this.roadAddress = roadAddress; + this.wifi = cafeDetail.getWifiValue(); + this.parking = cafeDetail.getParkingValue(); + this.toilet = cafeDetail.getToiletValue(); + this.power = cafeDetail.getPowerValue(); + this.sound = cafeDetail.getSoundValue(); + this.desk = cafeDetail.getDeskValue(); } } diff --git a/src/main/java/mocacong/server/dto/response/PreviewCafeResponse.java b/src/main/java/mocacong/server/dto/response/PreviewCafeResponse.java index 1234c2b9..555b86f2 100644 --- a/src/main/java/mocacong/server/dto/response/PreviewCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/PreviewCafeResponse.java @@ -8,6 +8,8 @@ @ToString public class PreviewCafeResponse { + private String name; + private String roadAddress; private Boolean favorite; private double score; private String studyType; diff --git a/src/main/java/mocacong/server/repository/CafeRepository.java b/src/main/java/mocacong/server/repository/CafeRepository.java index f297f4e0..5b7ccbec 100644 --- a/src/main/java/mocacong/server/repository/CafeRepository.java +++ b/src/main/java/mocacong/server/repository/CafeRepository.java @@ -28,10 +28,10 @@ public interface CafeRepository extends JpaRepository { "where m.id = :id") Slice findByMyFavoriteCafes(Long id, Pageable pageRequest); - @Query("select new mocacong.server.dto.response.MyReviewCafeResponse(c.mapId,c.name,r.cafeDetail.studyType,s.score) from Review r " + + @Query("select new mocacong.server.dto.response.MyReviewCafeResponse(c.mapId,c.name,r.cafeDetail.studyType,s.score,c.roadAddress, r.cafeDetail) from Review r " + "join r.cafe c " + "join r.member m " + - "join c.score s "+ + "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/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index 9433a9a6..d25af000 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -31,6 +31,7 @@ import javax.persistence.EntityManager; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -53,13 +54,21 @@ public class CafeService { @Transactional public void save(CafeRegisterRequest request) { - Cafe cafe = new Cafe(request.getId(), request.getName()); + Cafe cafe = new Cafe(request.getId(), request.getName(), request.getRoadAddress(), request.getPhoneNumber()); + + + Optional cafeOptional = cafeRepository.findByMapId(request.getId()); try { - cafeRepository.save(cafe); + cafeRepository.findByMapId(request.getId()) + .ifPresentOrElse( + existedCafe -> existedCafe.updateCafeRoadAddress(request.getRoadAddress()), + () -> cafeRepository.save(cafe) + ); } catch (DataIntegrityViolationException e) { throw new DuplicateCafeException(); } + } @Transactional(readOnly = true) @@ -78,6 +87,9 @@ public FindCafeResponse findCafeByMapId(Long memberId, String mapId) { return new FindCafeResponse( favoriteId != null, favoriteId, + cafe.getName(), + cafe.getRoadAddress(), + cafe.getPhoneNumber(), cafe.findAverageScore(), scoreByLoginUser != null ? scoreByLoginUser.getScore() : null, cafeDetail.getStudyTypeValue(), @@ -105,6 +117,8 @@ public PreviewCafeResponse previewCafeByMapId(Long memberId, String mapId) { Long favoriteId = favoriteRepository.findFavoriteIdByCafeIdAndMemberId(cafe.getId(), member.getId()) .orElse(null); return new PreviewCafeResponse( + cafe.getName(), + cafe.getRoadAddress(), favoriteId != null, cafe.findAverageScore(), cafeDetail.getStudyTypeValue(), @@ -149,7 +163,7 @@ public MyFavoriteCafesResponse findMyFavoriteCafes(Long memberId, Integer page, List responses = myFavoriteCafes .getContent() .stream() - .map(cafe -> new MyFavoriteCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), cafe.findAverageScore())) + .map(cafe -> new MyFavoriteCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), cafe.findAverageScore(), cafe.getRoadAddress())) .collect(Collectors.toList()); return new MyFavoriteCafesResponse(myFavoriteCafes.isLast(), responses); } @@ -172,6 +186,7 @@ public MyCommentCafesResponse findMyCommentCafes(Long memberId, int page, int co List comments = commentRepository.findAllByMemberId(member.getId()); List responses = comments.stream() +// .map(MyCommentCafeResponse::from) .collect(Collectors.groupingByConcurrent(Comment::getCafe)) .entrySet() .stream() diff --git a/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java index 8811bd09..ca40e37d 100644 --- a/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/CafeAcceptanceTest.java @@ -21,7 +21,7 @@ public class CafeAcceptanceTest extends AcceptanceTest { @Test @DisplayName("카페를 정상적으로 등록한다") void cafeSave() { - CafeRegisterRequest request = new CafeRegisterRequest("1", "메리네 카페"); + CafeRegisterRequest request = new CafeRegisterRequest("1", "메리네 카페", "서울시 강남구", "010-1234-5678"); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -36,7 +36,7 @@ void cafeSave() { @DisplayName("특정 카페 정보를 조회한다") void findCafe() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -55,7 +55,7 @@ void findCafe() { @DisplayName("카페에 대한 리뷰를 작성한다") void saveCafeReview() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -83,7 +83,7 @@ void saveCafeReview() { @DisplayName("카페에 대해 내가 작성한 리뷰를 조회한다") void findMyCafeReview() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -103,7 +103,7 @@ void findMyCafeReview() { @DisplayName("카페에 대한 리뷰를 다시 작성할 수 없다") void saveCafeReviewManyTimes() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -126,7 +126,7 @@ void saveCafeReviewManyTimes() { @DisplayName("등록한 카페에 대한 리뷰를 수정한다") void updateCafeReview() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -157,7 +157,7 @@ void updateCafeReview() { @DisplayName("등록하지 않은 카페에 대한 리뷰는 수정할 수 없다") void updateCafeReviewNotFoundReview() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -178,7 +178,7 @@ void updateCafeReviewNotFoundReview() { @DisplayName("카페 정보를 미리보기한다") void previewCafe() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); @@ -206,9 +206,9 @@ void getCafesFilterStudyType() { String mapId1 = "12332312"; String mapId2 = "12355412"; String mapId3 = "18486512"; - 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페 본점")); - 카페_등록(new CafeRegisterRequest(mapId2, "메리네 카페 2호점")); - 카페_등록(new CafeRegisterRequest(mapId3, "메리네 카페 3호점")); + 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페 본점", "서울시 강남구", "010-1234-5678")); + 카페_등록(new CafeRegisterRequest(mapId2, "메리네 카페 2호점", "서울시 강남구", "010-1234-5678")); + 카페_등록(new CafeRegisterRequest(mapId3, "메리네 카페 3호점", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -242,9 +242,9 @@ void getCafesFilterFavorites() { String mapId1 = "12332312"; String mapId2 = "12355412"; String mapId3 = "18486512"; - 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페 본점")); - 카페_등록(new CafeRegisterRequest(mapId2, "메리네 카페 2호점")); - 카페_등록(new CafeRegisterRequest(mapId3, "메리네 카페 3호점")); + 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페 본점", "서울시 강남구", "010-1234-5678")); + 카페_등록(new CafeRegisterRequest(mapId2, "메리네 카페 2호점", "서울시 강남구", "010-1234-5678")); + 카페_등록(new CafeRegisterRequest(mapId3, "메리네 카페 3호점", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -270,7 +270,7 @@ void getCafesFilterFavorites() { @DisplayName("특정 카페 이미지들을 조회한다") void getCafeImages() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페 본점")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페 본점", "서울시 강남구", "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 6d1700b1..567744d5 100644 --- a/src/test/java/mocacong/server/acceptance/CommentAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/CommentAcceptanceTest.java @@ -24,7 +24,7 @@ public class CommentAcceptanceTest extends AcceptanceTest { void saveComment() { String expected = "공부하기 좋아요~🥰"; String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -51,7 +51,7 @@ void saveComment() { @DisplayName("카페 코멘트 목록을 조회한다") void findComments() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -71,7 +71,7 @@ void findComments() { @DisplayName("카페 코멘트 첫번째 목록을 조회한다") void findCommentsFirstPage() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "베어네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "베어네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); 회원_가입(signUpRequest); @@ -94,7 +94,7 @@ void findCommentsFirstPage() { @DisplayName("카페 코멘트 첫번째 목록을 제외한 페이지를 조회한다") void findCommentsNotFirstPage() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "베어네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "베어네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); 회원_가입(signUpRequest); @@ -117,7 +117,7 @@ void findCommentsNotFirstPage() { @DisplayName("카페 코멘트 목록 중 내가 작성한 코멘트만을 조회한다") void findOnlyMyComments() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -145,7 +145,7 @@ void findOnlyMyComments() { void updateComment() { String content = "공부하기 좋아요~🥰"; String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); @@ -179,7 +179,7 @@ void updateComment() { void deleteComment() { String content = "공부하기 좋아요~🥰"; String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "100", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); diff --git a/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java index 7a0a8023..c927d289 100644 --- a/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/CommentLikeAcceptanceTest.java @@ -17,7 +17,7 @@ public class CommentLikeAcceptanceTest extends AcceptanceTest{ void saveCommentLike() { String mapId = "12332312"; String comment = "코딩하고 싶어지는 카페에요."; - 카페_등록(new CafeRegisterRequest(mapId, "정우네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "정우네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); 회원_가입(signUpRequest1); @@ -43,7 +43,7 @@ void saveCommentLike() { void deleteCommentLike() { String mapId = "12332312"; String comment = "코딩하고 싶어지는 카페에요."; - 카페_등록(new CafeRegisterRequest(mapId, "정우네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "정우네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("rlawjddn103@naver.com", "a1b2c3d4", "베어"); 회원_가입(signUpRequest1); diff --git a/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java index 0b29a1ec..eb198f8b 100644 --- a/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/FavoriteAcceptanceTest.java @@ -17,7 +17,7 @@ public class FavoriteAcceptanceTest extends AcceptanceTest { @DisplayName("회원이 카페 즐겨찾기 등록을 진행한다") void saveFavoriteCafe() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -34,7 +34,7 @@ void saveFavoriteCafe() { @DisplayName("회원이 카페 즐겨찾기 삭제를 진행한다") void deleteFavoriteCafe() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "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/MemberAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java index 34feef91..b54f6034 100644 --- a/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java @@ -92,7 +92,7 @@ void deleteWhenSaveReviewsAndComments() { 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-1234")); 카페_리뷰_작성(token, mapId, new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", "깨끗해요", "충분해요", "조용해요", "편해요")); 카페_코멘트_작성(token, mapId, new CommentSaveRequest("좋아요~")); @@ -113,7 +113,7 @@ void deleteWhenSaveFavorites() { 회원_가입(memberSignUpRequest); String token = 로그인_토큰_발급(memberSignUpRequest.getEmail(), memberSignUpRequest.getPassword()); - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-1234")); 즐겨찾기_등록(token, mapId); RestAssured.given().log().all() @@ -313,7 +313,7 @@ void findMyInfo() { @DisplayName("마이페이지에서 즐겨찾기 등록한 카페 목록을 조회한다") void findMyFavoriteCafes() { String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-1234")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); String token = 로그인_토큰_발급(signUpRequest.getEmail(), signUpRequest.getPassword()); @@ -333,8 +333,8 @@ void findMyFavoriteCafes() { void findMyReviewCafes() { String mapId1 = "12332312"; String mapId2 = "12121212"; - 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페")); - 카페_등록(new CafeRegisterRequest(mapId2, "케이네 카페")); + 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페", "서울시 강남구", "010-1234-1234")); + 카페_등록(new CafeRegisterRequest(mapId2, "케이네 카페", "서울시 강남구", "010-1234-1234")); MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); 회원_가입(signUpRequest1); @@ -375,8 +375,8 @@ void findMyReviewCafes() { void findMyCommentCafes() { String mapId1 = "12332312"; String mapId2 = "12121212"; - 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페")); - 카페_등록(new CafeRegisterRequest(mapId2, "케이네 카페")); + 카페_등록(new CafeRegisterRequest(mapId1, "메리네 카페", "서울시 강남구", "010-1234-1234")); + 카페_등록(new CafeRegisterRequest(mapId2, "케이네 카페", "서울시 강남구", "010-1234-1234")); MemberSignUpRequest signUpRequest = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); 회원_가입(signUpRequest); MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("mery@naver.com", "a1b2c3d4", "메리"); diff --git a/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java index b9335631..d8a3e71f 100644 --- a/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/ReportAcceptanceTest.java @@ -24,7 +24,7 @@ public class ReportAcceptanceTest extends AcceptanceTest { void reportComment() { String reportReason = "insult"; String mapId = "12332312"; - 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페")); + 카페_등록(new CafeRegisterRequest(mapId, "메리네 카페", "서울시 강남구", "010-1234-5678")); MemberSignUpRequest signUpRequest1 = new MemberSignUpRequest("kth990303@naver.com", "a1b2c3d4", "케이"); MemberSignUpRequest signUpRequest2 = new MemberSignUpRequest("dlawotn3@naver.com", "a1b2c3d4", "메리"); diff --git a/src/test/java/mocacong/server/domain/BaseTimeTest.java b/src/test/java/mocacong/server/domain/BaseTimeTest.java index 5467de19..e80c6687 100644 --- a/src/test/java/mocacong/server/domain/BaseTimeTest.java +++ b/src/test/java/mocacong/server/domain/BaseTimeTest.java @@ -38,7 +38,7 @@ public void memberCreatedAtNow() { public void updateCafeAtNow() { Member member = new Member("kth990303@naver.com", "a1b2c3d4", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "100"); cafeRepository.save(cafe); CafeDetail cafeDetail = new CafeDetail(StudyType.SOLO, Wifi.FAST, Parking.COMFORTABLE, Toilet.CLEAN, Desk.UNCOMFORTABLE, Power.MANY, Sound.LOUD); Review addReview = new Review(member, cafe, cafeDetail); diff --git a/src/test/java/mocacong/server/domain/CafeImageTest.java b/src/test/java/mocacong/server/domain/CafeImageTest.java index b35476d0..2e46bbee 100644 --- a/src/test/java/mocacong/server/domain/CafeImageTest.java +++ b/src/test/java/mocacong/server/domain/CafeImageTest.java @@ -25,7 +25,7 @@ private static Stream provideCafeImagesMember() { @DisplayName("해당 카페 이미지의 작성자가 해당 회원이 맞는지 여부를 반환한다") @MethodSource("provideCafeImagesMember") void isOwned(Member input, boolean expected) { - Cafe cafe = new Cafe("123454321", "케이카페"); + Cafe cafe = new Cafe("123454321", "케이카페", "100"); CafeImage cafeImage = new CafeImage("test_url", true, cafe, member); assertThat(cafeImage.isOwned(input)).isEqualTo(expected); @@ -34,7 +34,7 @@ void isOwned(Member input, boolean expected) { @Test @DisplayName("해당 카페 이미지 작성자가 없을 경우, 해당 회원이 맞는지 여부 반환은 항상 false 반환한다") void isOwnedWhenMemberNull() { - Cafe cafe = new Cafe("123454321", "케이카페"); + Cafe cafe = new Cafe("123454321", "케이카페", "100"); CafeImage cafeImage = new CafeImage("test_url", true, cafe, null); assertThat(cafeImage.isOwned(member)).isFalse(); @@ -43,7 +43,7 @@ void isOwnedWhenMemberNull() { @Test @DisplayName("카페 이미지 작성자를 null 처리한다") void removeMember() { - Cafe cafe = new Cafe("123454321", "케이카페"); + Cafe cafe = new Cafe("123454321", "케이카페", "100"); CafeImage cafeImage = new CafeImage("test_url", true, cafe, member); cafeImage.removeMember(); @@ -55,7 +55,7 @@ void removeMember() { @DisplayName("특정 카페 이미지 url을 수정한다") void updateCafeImageUrl() { String expected = "test_update_url"; - Cafe cafe = new Cafe("123454321", "케이카페"); + Cafe cafe = new Cafe("123454321", "케이카페", "100"); CafeImage cafeImage = new CafeImage("test_url", true, cafe, member); cafeImage.updateImgUrl(expected); diff --git a/src/test/java/mocacong/server/domain/CafeTest.java b/src/test/java/mocacong/server/domain/CafeTest.java index fb27c11f..7dee8f96 100644 --- a/src/test/java/mocacong/server/domain/CafeTest.java +++ b/src/test/java/mocacong/server/domain/CafeTest.java @@ -21,7 +21,7 @@ void equals() { @Test @DisplayName("카페에 평점을 기여한 사람이 없으면 0점을 반환한다") void findScoreWithNoReviews() { - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); double actual = cafe.findAverageScore(); @@ -32,7 +32,7 @@ void findScoreWithNoReviews() { @DisplayName("카페의 평점을 올바르게 계산하여 반환한다") void findScore() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); Score score1 = new Score(5, member, cafe); Score score2 = new Score(2, member, cafe); @@ -46,7 +46,7 @@ void findScore() { void updateCafeDetails() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); CafeDetail cafeDetail1 = new CafeDetail(StudyType.SOLO, Wifi.FAST, Parking.COMFORTABLE, Toilet.CLEAN, Desk.COMFORTABLE, Power.MANY, Sound.LOUD); Review review1 = new Review(member, cafe, cafeDetail1); @@ -76,7 +76,7 @@ void updateCafeDetails() { @DisplayName("카페 세부정보 리뷰로 both가 작성될 경우 solo, group 포인트가 모두 1씩 증가한다") void updateCafeDetailsWhenStudyTypesAddBoth() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); // BOTH 리뷰 추가 -> SOLO, GROUP 모두 1포인트 CafeDetail cafeDetail1 = new CafeDetail(StudyType.BOTH, Wifi.NORMAL, Parking.COMFORTABLE, Toilet.NORMAL, Desk.UNCOMFORTABLE, Power.FEW, Sound.NOISY); @@ -111,7 +111,7 @@ void updateCafeDetailsWhenStudyTypesAddBoth() { void updateCafeDetailsWhenStudyTypesEqual() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); CafeDetail cafeDetail1 = new CafeDetail(StudyType.SOLO, Wifi.FAST, Parking.COMFORTABLE, Toilet.CLEAN, Desk.COMFORTABLE, Power.MANY, Sound.LOUD); Review review1 = new Review(member, cafe, cafeDetail1); @@ -131,7 +131,7 @@ void updateCafeDetailsWhenStudyTypesEqual() { void updateCafeDetailsWhenSomeTypesNoReviews() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); CafeDetail cafeDetail = new CafeDetail(StudyType.SOLO, Wifi.FAST, null, Toilet.CLEAN, null, Power.MANY, Sound.LOUD); Review review = new Review(member, cafe, cafeDetail); @@ -146,7 +146,7 @@ void updateCafeDetailsWhenSomeTypesNoReviews() { @Test @DisplayName("카페에 리뷰가 하나도 없을 경우 모든 세부정보 타입에 null을 반환한다") void updateCafeDetailsWhenNoReviews() { - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); cafe.updateCafeDetails(); @@ -165,7 +165,7 @@ void updateCafeDetailsWhenNoReviews() { @DisplayName("카페의 스터디 타입을 반환한다") void getStudyType() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); 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); @@ -177,8 +177,20 @@ void getStudyType() { @Test @DisplayName("리뷰가 없는 카페의 스터디 타입은 null 을 반환한다") void getStudyTypeWhenNotHasReviews() { - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); assertThat(cafe.getStudyType()).isNull(); } + + @Test + @DisplayName("카페의 도로명 주소를 업데이트한다") + void updateRoadAddress() { + Cafe cafe = new Cafe("1", "케이카페", "100"); + String expected = "서울시 강남구 테헤란로 123"; + + cafe.updateCafeRoadAddress(expected); + + assertThat(cafe.getRoadAddress()).isEqualTo(expected); + } + } diff --git a/src/test/java/mocacong/server/domain/CommentTest.java b/src/test/java/mocacong/server/domain/CommentTest.java index 9917515c..15929695 100644 --- a/src/test/java/mocacong/server/domain/CommentTest.java +++ b/src/test/java/mocacong/server/domain/CommentTest.java @@ -13,7 +13,7 @@ class CommentTest { @DisplayName("코멘트 길이가 200자를 초과하면 예외를 반환한다") void validateCommentLength() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); assertThatThrownBy(() -> new Comment(cafe, member, createLongComment(201))) .isInstanceOf(ExceedCommentLengthException.class); } @@ -30,7 +30,7 @@ private String createLongComment(int length) { @DisplayName("댓글 작성자 닉네임을 반환한다") void getWriterNickname() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); Comment comment = new Comment(cafe, member, "안녕하세요"); assertThat(comment.getWriterNickname()).isEqualTo(member.getNickname()); @@ -40,7 +40,7 @@ void getWriterNickname() { @DisplayName("댓글 작성자 프로필 이미지 url을 반환한다") void getWriterImgUrl() { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이", new MemberProfileImage("test_img.jpg")); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); Comment comment = new Comment(cafe, member, "안녕하세요"); assertThat(comment.getWriterImgUrl()).isEqualTo(member.getImgUrl()); @@ -51,7 +51,7 @@ void getWriterImgUrl() { void isWrittenByMember() { Member member1 = new Member("kth@naver.com", "a1b2c3d4", "케이"); Member member2 = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); Comment comment = new Comment(cafe, member1, "안녕하세요"); assertAll( diff --git a/src/test/java/mocacong/server/domain/ScoreTest.java b/src/test/java/mocacong/server/domain/ScoreTest.java index b2606243..02c17784 100644 --- a/src/test/java/mocacong/server/domain/ScoreTest.java +++ b/src/test/java/mocacong/server/domain/ScoreTest.java @@ -13,7 +13,7 @@ class ScoreTest { @DisplayName("평점이 1점 이상 5점 이하가 아니면 예외를 반환한다") void invalidRangeScore(int score) { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); assertThatThrownBy(() -> new Score(score, member, cafe)) .isInstanceOf(InvalidScoreException.class); @@ -24,7 +24,7 @@ void invalidRangeScore(int score) { @DisplayName("수정 시 평점이 1점 이상 5점 이하가 아니면 예외를 반환한다") void updateInvalidRangeScore(int score) { Member member = new Member("kth@naver.com", "a1b2c3d4", "케이"); - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); Score oldScore = new Score(3, member, cafe); assertThatThrownBy(() -> oldScore.updateScore(score)) .isInstanceOf(InvalidScoreException.class); diff --git a/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java b/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java index 948d494f..19352938 100644 --- a/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/CafeImageRepositoryTest.java @@ -27,7 +27,7 @@ public class CafeImageRepositoryTest { @DisplayName("내가 올린 카페 이미지부터 등록 순으로 조회한다") void findAllByCafeIdAndIsUsedOrderByCafeImageIdDesc() { Pageable pageable = PageRequest.of(0, 5); - Cafe cafe = cafeRepository.save(new Cafe("1", "케이카페")); + 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); diff --git a/src/test/java/mocacong/server/repository/CafeRepositoryTest.java b/src/test/java/mocacong/server/repository/CafeRepositoryTest.java index c706ba0b..e621e13e 100644 --- a/src/test/java/mocacong/server/repository/CafeRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/CafeRepositoryTest.java @@ -35,10 +35,10 @@ void findNearCafeMapIdsByMyFavoriteCafes() { String mapId2 = "2"; String mapId3 = "3"; String mapId4 = "4"; - Cafe savedCafe1 = cafeRepository.save(new Cafe(mapId1, "케이카페1")); - Cafe savedCafe2 = cafeRepository.save(new Cafe(mapId2, "케이카페2")); - Cafe savedCafe3 = cafeRepository.save(new Cafe(mapId3, "케이카페3")); - Cafe savedCafe4 = cafeRepository.save(new Cafe(mapId4, "케이카페4")); + Cafe savedCafe1 = cafeRepository.save(new Cafe(mapId1, "케이카페", "1001")); + Cafe savedCafe2 = cafeRepository.save(new Cafe(mapId2, "케이카페", "1002")); + Cafe savedCafe3 = cafeRepository.save(new Cafe(mapId3, "케이카페", "1003")); + Cafe savedCafe4 = cafeRepository.save(new Cafe(mapId4, "케이카페", "1004")); Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); favoriteRepository.save(new Favorite(member, savedCafe1)); favoriteRepository.save(new Favorite(member, savedCafe2)); @@ -58,10 +58,10 @@ void findNearCafeMapIdsByMyFavoriteCafes() { @Test @DisplayName("내가 즐겨찾기에 등록한 카페 목록을 조회한다") void findByMyFavoriteCafes() { - 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")); + Cafe savedCafe1 = cafeRepository.save(new Cafe("1", "케이카페1", "100")); + Cafe savedCafe2 = cafeRepository.save(new Cafe("2", "케이카페2", "100")); + Cafe savedCafe3 = cafeRepository.save(new Cafe("3", "케이카페3", "100")); + Cafe savedCafe4 = cafeRepository.save(new Cafe("4", "케이카페4", "100")); Member member = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); favoriteRepository.save(new Favorite(member, savedCafe1)); favoriteRepository.save(new Favorite(member, savedCafe2)); @@ -84,9 +84,9 @@ void findByMyFavoriteCafes() { 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 savedCafe1 = cafeRepository.save(new Cafe("1", "케이카페1", "100")); + Cafe savedCafe2 = cafeRepository.save(new Cafe("2", "케이카페2", "100")); + Cafe savedCafe3 = cafeRepository.save(new Cafe("3", "케이카페3", "100")); Score score1 = new Score(1, member, savedCafe1); Score score2 = new Score(1, member, savedCafe2); diff --git a/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java b/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java index 9717f678..55df03ad 100644 --- a/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/CommentLikeRepositoryTest.java @@ -25,7 +25,7 @@ class CommentLikeRepositoryTest { @Test @DisplayName("comment id, 멤버 id로 댓글 좋아요 id를 조회한다") void findByCommentIdAndMemberId() { - Cafe savedCafe = cafeRepository.save(new Cafe("1", "베어카페")); + 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)); diff --git a/src/test/java/mocacong/server/repository/CommentRepositoryTest.java b/src/test/java/mocacong/server/repository/CommentRepositoryTest.java index 82a88862..23831c12 100644 --- a/src/test/java/mocacong/server/repository/CommentRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/CommentRepositoryTest.java @@ -26,7 +26,7 @@ class CommentRepositoryTest { @DisplayName("코멘트 id, 멤버 id로 댓글 좋아요 id를 조회한다") void findByCommentIdAndMemberId() { Cafe savedCafe = cafeRepository - .save(new Cafe("1", "베어카페")); + .save(new Cafe("1", "베어카페", "서울시 강남구")); Member savedMember = memberRepository .save(new Member("rlawjddn103@naver.com", "abcd1234", "베어")); Comment savedComment = commentRepository diff --git a/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java b/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java index 1b46accc..d0bf1f1b 100644 --- a/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/FavoriteRepositoryTest.java @@ -24,7 +24,7 @@ class FavoriteRepositoryTest { @Test @DisplayName("카페 id, 멤버 id로 즐겨찾기 id를 조회한다") void findByCafeIdAndMemberId() { - Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); + Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페", "서울시 강남구")); Member savedMember = memberRepository.save(new Member("kth@naver.com", "abcd1234", "케이")); Favorite favorite = new Favorite(savedMember, savedCafe); favoriteRepository.save(favorite); @@ -41,7 +41,7 @@ void findByCafeIdAndMemberId() { @Test @DisplayName("멤버 id가 null인 즐겨찾기들을 모두 삭제한다") void deleteAllByMemberIdIsNull() { - Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); + Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페", "서울시 강남구")); Favorite favorite = new Favorite(null, savedCafe); favoriteRepository.save(favorite); diff --git a/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java b/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java index 70d3b5f0..9ba33a65 100644 --- a/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/ReviewRepositoryTest.java @@ -23,7 +23,7 @@ class ReviewRepositoryTest { @Test @DisplayName("카페 id, 멤버 id로 해당 멤버가 특정 카페에 작성한 리뷰의 id를 조회한다") void findIdByCafeIdAndMemberId() { - Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); + Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페", "서울시 강남구")); 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 738c325e..ee1c8158 100644 --- a/src/test/java/mocacong/server/repository/ScoreRepositoryTest.java +++ b/src/test/java/mocacong/server/repository/ScoreRepositoryTest.java @@ -21,7 +21,7 @@ class ScoreRepositoryTest { @Test @DisplayName("카페 id, 멤버 id로 해당 멤버가 특정 카페에 등록한 평점을 조회한다") void findScoreByCafeIdAndMemberId() { - Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페")); + Cafe savedCafe = cafeRepository.save(new Cafe("1", "케이카페", "서울시 강남구")); 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/service/CafeConcurrentServiceTest.java b/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java index 114a0aa0..b3e73f2e 100644 --- a/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeConcurrentServiceTest.java @@ -36,17 +36,15 @@ public class CafeConcurrentServiceTest { @Test @DisplayName("등록되지 않은 카페를 동시에 여러 번 등록하려 해도 한 번만 등록된다") void saveCafeWithConcurrent() throws InterruptedException { - CafeRegisterRequest request = new CafeRegisterRequest("20", "메리네 카페"); + CafeRegisterRequest request = new CafeRegisterRequest("20", "메리네 카페", "서울시 강남구", "010-1234-5678"); 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 { cafeService.save(request); - } catch (DuplicateCafeException e) { - exceptions.add(e); // 중복 예외를 리스트에 추가 + } catch (DuplicateCafeException ignored) { } latch.countDown(); }); @@ -55,7 +53,6 @@ void saveCafeWithConcurrent() throws InterruptedException { List actual = cafeRepository.findAll(); assertAll( - () -> assertThat(exceptions).hasSize(2), () -> assertThat(actual).hasSize(1) ); } @@ -65,7 +62,7 @@ void saveCafeWithConcurrent() throws InterruptedException { void saveScoreWithConcurrent() throws InterruptedException { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); ExecutorService executorService = Executors.newFixedThreadPool(3); CountDownLatch latch = new CountDownLatch(3); @@ -97,7 +94,7 @@ void saveScoreWithConcurrent() throws InterruptedException { void saveCafeReviewWithConcurrent() throws InterruptedException { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); ExecutorService executorService = Executors.newFixedThreadPool(3); CountDownLatch latch = new CountDownLatch(3); diff --git a/src/test/java/mocacong/server/service/CafeServiceTest.java b/src/test/java/mocacong/server/service/CafeServiceTest.java index 8263bad3..1e758101 100644 --- a/src/test/java/mocacong/server/service/CafeServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeServiceTest.java @@ -4,7 +4,6 @@ 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.ExceedCageImagesTotalCountsException; import mocacong.server.exception.notfound.NotFoundCafeImageException; import mocacong.server.exception.notfound.NotFoundReviewException; @@ -25,7 +24,6 @@ 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 static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; @@ -57,7 +55,7 @@ class CafeServiceTest { @Test @DisplayName("등록되지 않은 카페를 성공적으로 등록한다") void cafeSave() { - CafeRegisterRequest request = new CafeRegisterRequest("20", "메리네 카페"); + CafeRegisterRequest request = new CafeRegisterRequest("20", "메리네 카페", "100", "010-1234-5678"); cafeService.save(request); @@ -66,18 +64,17 @@ void cafeSave() { } @Test - @DisplayName("등록되어 있는 카페는 등록하지 않는다") + @DisplayName("등록되어 있는 카페를 등록하면 주소가 업데이트 된다") void cafeSaveDuplicate() { - CafeRegisterRequest request1 = new CafeRegisterRequest("20", "메리네 카페"); + CafeRegisterRequest request1 = new CafeRegisterRequest("20", "메리네 카페", "100", "010-1234-5678"); cafeService.save(request1); - CafeRegisterRequest request2 = new CafeRegisterRequest("20", "카페"); + CafeRegisterRequest request2 = new CafeRegisterRequest("20", "카페", "200", "010-1234-5678"); + cafeService.save(request2); List actual = cafeRepository.findAll(); - assertAll( - () -> assertThrows(DuplicateCafeException.class, () -> cafeService.save(request2)), - () -> assertThat(actual).hasSize(1) - ); + assertThat(actual).hasSize(1); + assertThat(actual.get(0).getRoadAddress()).isEqualTo("200"); } @Test @@ -85,7 +82,7 @@ void cafeSaveDuplicate() { void findCafe() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); FindCafeResponse actual = cafeService.findCafeByMapId(member.getId(), cafe.getMapId()); @@ -110,7 +107,7 @@ void findCafeWithScore() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Score score1 = new Score(4, member1, cafe); scoreRepository.save(score1); @@ -138,7 +135,7 @@ void findCafeWithReviewsAndComments() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", @@ -176,7 +173,7 @@ void findCafeWithCommentLike() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", @@ -219,7 +216,7 @@ void findCafeWithCommentLike() { void previewCafe() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); PreviewCafeResponse actual = cafeService.previewCafeByMapId(member.getId(), cafe.getMapId()); @@ -239,7 +236,7 @@ void previewCafeWithScore() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); scoreRepository.save(new Score(4, member1, cafe)); scoreRepository.save(new Score(5, member2, cafe)); @@ -262,7 +259,7 @@ void previewCafeWithScoreAndReview() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(3, "group", "느려요", "없어요", @@ -287,7 +284,7 @@ void previewCafeWithScoreAndReview() { void findCafeAndShowLimitComments() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Comment comment1 = new Comment(cafe, member, "댓글1"); commentRepository.save(comment1); @@ -313,7 +310,7 @@ void findCafeWithReviewsAndCommentsByDeleteMember() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", @@ -344,7 +341,7 @@ void findCafeWithAll() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", @@ -387,7 +384,7 @@ void findCafeAndShowLimitImages() throws IOException { memberRepository.save(member); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); String mapId = cafe.getMapId(); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + expected); @@ -417,7 +414,7 @@ void findMyFavoriteCafes() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(1, "group", "느려요", "없어요", @@ -443,8 +440,8 @@ void findMyReviewCafes() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe1 = new Cafe("2143154352323", "케이카페"); - Cafe cafe2 = new Cafe("2154122541112", "메리카페"); + Cafe cafe1 = new Cafe("2143154352323", "케이카페", "200"); + Cafe cafe2 = new Cafe("2154122541112", "메리카페", "100"); cafeRepository.save(cafe1); cafeRepository.save(cafe2); cafeService.saveCafeReview(member1.getId(), cafe1.getMapId(), @@ -463,10 +460,10 @@ void findMyReviewCafes() { () -> 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(0).getName()).isEqualTo("케이카페", "200"), () -> 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().get(1).getName()).isEqualTo("메리카페", "100"), () -> assertThat(actual.getCafes()).hasSize(2) ); } @@ -478,8 +475,8 @@ void findMyCommentCafes() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe1 = new Cafe("2143154352323", "케이카페"); - Cafe cafe2 = new Cafe("1212121212121", "메리카페"); + Cafe cafe1 = new Cafe("2143154352323", "케이카페", "200"); + Cafe cafe2 = new Cafe("1212121212121", "메리카페", "100"); cafeRepository.save(cafe1); cafeRepository.save(cafe2); commentRepository.save(new Comment(cafe1, member1, "댓글1")); @@ -505,7 +502,7 @@ void saveCafeReview() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -536,7 +533,7 @@ void saveCafeAndStudyTypesEquals() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), @@ -556,7 +553,7 @@ void findMyCafeReview() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -584,7 +581,7 @@ void findMyCafeReview() { void findNotRegisteredCafeReview() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); CafeMyReviewResponse actual = cafeService.findMyCafeReview(member.getId(), cafe.getMapId()); @@ -608,7 +605,7 @@ void cannotSaveManyReviews() { memberRepository.save(member1); Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member1.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -625,7 +622,7 @@ void cannotSaveManyReviews() { public void updateCafeReview() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -653,7 +650,7 @@ public void updateCafeReview() { public void updateCafeReviewWhenDetailsNull() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -681,7 +678,7 @@ public void updateCafeReviewWhenDetailsNull() { public void updateCafeReviewNotFoundReview() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); assertThatThrownBy(() -> cafeService.updateCafeReview(member.getId(), cafe.getMapId(), @@ -697,11 +694,11 @@ void getCafesFilterStudyType() { 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", "메리설빙"); + Cafe cafe1 = new Cafe("2143154352323", "케이카페", "200"); + Cafe cafe2 = new Cafe("2143154311111", "메리카페", "100"); + Cafe cafe3 = new Cafe("2111111125885", "메리카페 2호점", "300"); + Cafe cafe4 = new Cafe("1585656565441", "메리벅스", "400"); + Cafe cafe5 = new Cafe("1582212121441", "메리설빙", "500"); cafeRepository.save(cafe1); cafeRepository.save(cafe2); cafeRepository.save(cafe3); @@ -737,7 +734,7 @@ void getCafesFilterStudyType() { void getCafesFilterStudyTypeWhenNoMatch() { Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); cafeService.saveCafeReview(member.getId(), cafe.getMapId(), new CafeReviewRequest(4, "solo", "빵빵해요", "여유로워요", @@ -754,10 +751,10 @@ void getCafesFilterStudyTypeWhenNoMatch() { void getCafesFilterFavorites() { Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member); - Cafe cafe1 = new Cafe("2143154352323", "케이카페"); - Cafe cafe2 = new Cafe("2143154311111", "메리카페"); - Cafe cafe3 = new Cafe("2111111125885", "메리카페 2호점"); - Cafe cafe4 = new Cafe("1585656565441", "메리벅스"); + Cafe cafe1 = new Cafe("2143154352323", "케이카페", "200"); + Cafe cafe2 = new Cafe("2143154311111", "메리카페", "100"); + Cafe cafe3 = new Cafe("2111111125885", "메리카페 2호점", "300"); + Cafe cafe4 = new Cafe("1585656565441", "메리벅스", "400"); cafeRepository.save(cafe1); cafeRepository.save(cafe2); cafeRepository.save(cafe3); @@ -779,7 +776,7 @@ void getCafesFilterFavorites() { void getCafesFilterFavoritesWhenNoMatch() { Member member = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); CafeFilterFavoritesRequest requestBody = new CafeFilterFavoritesRequest(List.of(cafe.getMapId())); @@ -793,7 +790,7 @@ void getCafesFilterFavoritesWhenNoMatch() { @DisplayName("카페 이미지를 성공적으로 저장한다") void saveCafeImage() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -817,7 +814,7 @@ void saveCafeImage() throws IOException { @Test @DisplayName("카페 이미지를 저장한 후 Response를 반환한다.") void saveCafeImageWithResponse() throws IOException { - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -838,7 +835,7 @@ void saveCafeImageWithResponse() throws IOException { @Test @DisplayName("카페 이미지를 한 번에 3개까지 저장할 수 있다") void saveCafeImagesPerRequest() throws IOException { - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("kth990303@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -864,7 +861,7 @@ void saveCafeImagesPerRequest() throws IOException { @Test @DisplayName("카페 이미지를 한 번에 세 개를 초과하여 저장하면 예외를 반환한다") void saveCafeImagesManyPerRequest() throws IOException { - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("kth990303@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -893,7 +890,7 @@ void saveCafeImagesManyPerRequest() throws IOException { @DisplayName("사용자가 카페 이미지를 여러 번 저장한다") void saveCafeImages() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -919,7 +916,7 @@ void saveCafeImages() throws IOException { @Test @DisplayName("사용자가 카페 이미지를 총 3개 보다 많이 저장하면 예외가 발생한다.") void saveCafeImagesOver3() throws IOException { - Cafe cafe = new Cafe("2143154352323", "베어카페"); + Cafe cafe = new Cafe("2143154352323", "베어카페", "200"); cafeRepository.save(cafe); Member member = new Member("rlawjddn103@naver.com", "a1b2c3d4", "베어", null); memberRepository.save(member); @@ -956,7 +953,7 @@ void saveCafeImagesOver3() throws IOException { @DisplayName("사용자가 카페 이미지를 조회한다") void findCafeImages() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -982,7 +979,7 @@ void findCafeImages() throws IOException { @DisplayName("자신이 등록한 카페 이미지를 조회하는 경우 isMe가 True로 반환된다") void findCafeMyImagesReturnTrue() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -1008,7 +1005,7 @@ void findCafeMyImagesReturnTrue() throws IOException { @DisplayName("타인이 등록한 카페이미지를 조회할 때 isMe가 false로 반환된다") void findOtherCafeImagesReturnFalse() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member1); @@ -1038,7 +1035,7 @@ void findOtherCafeImagesReturnFalse() throws IOException { @DisplayName("자신이 등록한 이미지부터 등록 순으로 이미지를 조회한다") void findCafeImagesReturnOrderedImages() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member1); @@ -1074,7 +1071,7 @@ void findCafeImagesReturnOrderedImages() throws IOException { @DisplayName("타인이 나중에 이미지를 등록해도 자신이 등록한 이미지부터 등록 순으로 조회한다") void findCafeImagesReturnOrderedMyImages() throws IOException { String expected = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member1 = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member1); @@ -1111,7 +1108,7 @@ void findCafeImagesReturnOrderedMyImages() throws IOException { void updateCafeImage() throws IOException { String oldImage = "test_img.jpg"; String newImage = "test_img2.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -1146,7 +1143,7 @@ void updateCafeImage() throws IOException { @DisplayName("존재하지 않는 카페 이미지를 수정 시도할 시 예외를 반환한다") void updateCafeImageNotFoundImage() throws IOException { String newImage = "test_img.jpg"; - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); Member member = new Member("dlawotn3@naver.com", "a1b2c3d4", "메리", null); memberRepository.save(member); @@ -1171,7 +1168,7 @@ void updateCafeImageAndShowLimitImages() throws IOException { memberRepository.save(member); memberRepository.save(member2); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); String mapId = cafe.getMapId(); FileInputStream oldFileInputStream = new FileInputStream("src/test/resources/images/" + oldImage); @@ -1214,7 +1211,7 @@ void updateCafeImageAndShowLimitImages() throws IOException { void cafeImagesWhenMemberDelete() throws IOException { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/test_img.jpg"); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", "test_img.jpg", @@ -1237,7 +1234,7 @@ void deleteNotUsedCafeImages() { List notUsedImgUrls = List.of("test_img2.jpg", "test_img3.jpg"); Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "200"); cafeRepository.save(cafe); CafeImage cafeImage1 = new CafeImage("test_img.jpg", true, cafe, member); cafeImageRepository.save(cafeImage1); diff --git a/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java b/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java index 980dea14..1803d328 100644 --- a/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/CommentLikeConcurrentServiceTest.java @@ -43,7 +43,7 @@ void saveCommentLikeWithConcurrent() throws InterruptedException { memberRepository.save(member1); Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "100"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member2, commentContent); commentRepository.save(comment); diff --git a/src/test/java/mocacong/server/service/CommentLikeServiceTest.java b/src/test/java/mocacong/server/service/CommentLikeServiceTest.java index 27c76565..540eec98 100644 --- a/src/test/java/mocacong/server/service/CommentLikeServiceTest.java +++ b/src/test/java/mocacong/server/service/CommentLikeServiceTest.java @@ -42,7 +42,7 @@ void save() { memberRepository.save(member1); Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "서울시 강남구"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member2, commentContent); commentRepository.save(comment); @@ -65,7 +65,7 @@ void saveDuplicateCommentLike() { memberRepository.save(member1); Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "서울시 강남구"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member2, commentContent); commentRepository.save(comment); @@ -83,7 +83,7 @@ void saveInvalidCommentLike() { String commentContent = "코딩하고 싶어지는 카페에요."; Member member = new Member(email, "encodePassword", "베어"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "서울시 강남구"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member, commentContent); commentRepository.save(comment); @@ -100,7 +100,7 @@ void delete() { memberRepository.save(member1); Member member2 = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "서울시 강남구"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member2, commentContent); commentRepository.save(comment); @@ -120,7 +120,7 @@ void deleteNotExistCommentLike() { String commentContent = "코딩하고 싶어지는 카페에요."; Member member = new Member(email, "encodePassword", "베어"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "서울시 강남구"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member, commentContent); commentRepository.save(comment); diff --git a/src/test/java/mocacong/server/service/CommentServiceTest.java b/src/test/java/mocacong/server/service/CommentServiceTest.java index 13f4751f..55a6170f 100644 --- a/src/test/java/mocacong/server/service/CommentServiceTest.java +++ b/src/test/java/mocacong/server/service/CommentServiceTest.java @@ -49,7 +49,7 @@ void save() { String expected = "공부하기 좋아요~🥰"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); CommentSaveResponse savedComment = commentService.save(member.getId(), mapId, expected); @@ -78,7 +78,7 @@ void saveManyTimes() { String mapId = "2143154352323"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); commentService.save(member.getId(), mapId, "공부하기 좋아요~🥰"); @@ -93,7 +93,7 @@ void findComments() { String mapId = "2143154352323"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); commentRepository.save(new Comment(cafe, member, "댓글1")); commentRepository.save(new Comment(cafe, member, "댓글2")); @@ -118,7 +118,7 @@ void findCommentsWithCount() { String mapId = "2143154352323"; Member member = new Member(email, "encodePassword", "베어"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "100"); cafeRepository.save(cafe); commentRepository.save(new Comment(cafe, member, "댓글1")); commentRepository.save(new Comment(cafe, member, "댓글2")); @@ -148,7 +148,7 @@ void findOnlyMyComments() { Member member2 = new Member("mery@naver.com", "encodePassword", "메리"); memberRepository.save(member); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); commentRepository.save(new Comment(cafe, member, "댓글1")); commentRepository.save(new Comment(cafe, member2, "댓글2")); @@ -174,7 +174,7 @@ void updateComment() { String comment = "공부하기 좋아요~🥰"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); CommentSaveResponse savedComment = commentService.save(member.getId(), mapId, comment); String expected = "조용하고 좋네요"; @@ -194,7 +194,7 @@ void updateManyTimes() { String comment = "공부하기 좋아요~🥰"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); CommentSaveResponse savedComment = commentService.save(member.getId(), mapId, comment); String expected = "조용하고 좋네요"; @@ -214,7 +214,7 @@ void updateByNonWriter() { memberRepository.save(member1); Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); CommentSaveResponse savedComment = commentService.save(member1.getId(), mapId, "조용하고 좋네요"); @@ -229,7 +229,7 @@ void delete() { String mapId = "2143154352323"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); CommentSaveResponse response = commentService.save(member.getId(), mapId, "공부하기 좋아요~🥰"); @@ -246,7 +246,7 @@ void deleteNotExistsComment() { String mapId = "2143154352323"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); assertThatThrownBy(() -> commentService.delete(member.getId(), mapId, 9999L)) @@ -263,7 +263,7 @@ void deleteNotMyComment() { Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "100"); cafeRepository.save(cafe); CommentSaveResponse response = commentService.save(member1.getId(), mapId, "공부하기 좋아요~🥰"); @@ -279,7 +279,7 @@ void deleteExistCommentLike() { String commentContent = "코딩하고 싶어지는 카페에요."; Member member = new Member(email, "encodePassword", "베어"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "베어카페"); + Cafe cafe = new Cafe(mapId, "베어카페", "100"); cafeRepository.save(cafe); Comment savedComment = commentRepository.save(new Comment(cafe, member, commentContent)); commentLikeRepository.save(new CommentLike(member, savedComment)); diff --git a/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java b/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java index b86b1d5c..77c0cd46 100644 --- a/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/FavoriteConcurrentServiceTest.java @@ -37,7 +37,7 @@ public class FavoriteConcurrentServiceTest { void saveFavoriteWithConcurrent() throws InterruptedException { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); ExecutorService executorService = Executors.newFixedThreadPool(3); CountDownLatch latch = new CountDownLatch(3); diff --git a/src/test/java/mocacong/server/service/FavoriteServiceTest.java b/src/test/java/mocacong/server/service/FavoriteServiceTest.java index 8e633909..aa5b8414 100644 --- a/src/test/java/mocacong/server/service/FavoriteServiceTest.java +++ b/src/test/java/mocacong/server/service/FavoriteServiceTest.java @@ -35,7 +35,7 @@ class FavoriteServiceTest { void save() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); FavoriteSaveResponse actual = favoriteService.save(member.getId(), cafe.getMapId()); @@ -52,7 +52,7 @@ void save() { void saveDuplicate() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); favoriteService.save(member.getId(), cafe.getMapId()); @@ -65,7 +65,7 @@ void saveDuplicate() { void delete() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); Favorite favorite = new Favorite(member, cafe); favoriteRepository.save(favorite); @@ -80,7 +80,7 @@ void delete() { void deleteWithException() { Member member = new Member("kth990303@naver.com", "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe("2143154352323", "케이카페"); + Cafe cafe = new Cafe("2143154352323", "케이카페", "서울시 강남구"); cafeRepository.save(cafe); assertThrows(NotFoundFavoriteException.class, diff --git a/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java index a4fdc59e..23b0b528 100644 --- a/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java +++ b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java @@ -54,7 +54,7 @@ void reportCommentWithConcurrent() throws InterruptedException { Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); CommentSaveResponse saveResponse = commentService.save(member1.getId(), mapId, "아~ 소설보고 싶다"); ExecutorService executorService = Executors.newFixedThreadPool(3); @@ -93,7 +93,7 @@ void reportCafeImageWithConcurrent() throws InterruptedException, IOException { Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/" + "test_img.jpg"); diff --git a/src/test/java/mocacong/server/service/ReportServiceTest.java b/src/test/java/mocacong/server/service/ReportServiceTest.java index e4dd6709..1810603a 100644 --- a/src/test/java/mocacong/server/service/ReportServiceTest.java +++ b/src/test/java/mocacong/server/service/ReportServiceTest.java @@ -64,7 +64,7 @@ void reportComment() { Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); commentService.save(member1.getId(), mapId, "이 카페 완전 돈 아깝;;"); @@ -84,7 +84,7 @@ void reportMyComment() { String mapId = "2143154352323"; Member member = new Member(email, "encodePassword", "케이"); memberRepository.save(member); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); CommentSaveResponse saveResponse = commentService.save(member.getId(), mapId, "굳이 이런데 가야하나 ㅋ"); @@ -100,7 +100,7 @@ void reportByInvalidReportReason() { Member member2 = new Member("dlawotn3@naver.com", "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); commentService.save(member1.getId(), mapId, "이 카페 완전 돈 아깝;;"); @@ -119,7 +119,7 @@ void reportDuplicateComment() { Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); CommentSaveResponse saveResponse = commentService.save(member1.getId(), mapId, "아~ 소설보고 싶다"); @@ -141,7 +141,7 @@ void maskCauseReport5timesReportedComment() { members.add(member); memberRepository.save(member); } - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); CommentSaveResponse saveResponse = commentService.save(members.get(0).getId(), mapId, "아~ 소설보고 싶다"); @@ -173,7 +173,7 @@ void reportCommentPostedDeletedMember() { Member member2 = new Member(email2, "encodePassword", "메리"); memberRepository.save(member1); memberRepository.save(member2); - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); Comment comment = new Comment(cafe, member1, "이 카페 완전 돈 아깝;;"); commentRepository.save(comment); @@ -267,7 +267,7 @@ void maskCauseReport3timesReportedCafeImage() throws IOException { members.add(member); memberRepository.save(member); } - Cafe cafe = new Cafe(mapId, "케이카페"); + Cafe cafe = new Cafe(mapId, "케이카페", "서울시 강남구"); cafeRepository.save(cafe); FileInputStream fileInputStream = new FileInputStream("src/test/resources/images/test_img.jpg"); MockMultipartFile mockMultipartFile = new MockMultipartFile("test_img", "test_img.jpg", From d2216f24500e4874045ce379a4e10576f1d4e2f5 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:39:44 +0900 Subject: [PATCH 07/16] =?UTF-8?q?fix:=20CafeRegisterRequest=EC=97=90?= =?UTF-8?q?=EC=84=9C=EC=9D=98=20id=20=ED=95=84=EB=93=9C=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#139)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: change field in CafeRegisterRequest DTO * style: delete unused code in save method --- .../mocacong/server/dto/request/CafeRegisterRequest.java | 2 +- src/main/java/mocacong/server/service/CafeService.java | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java index 06151186..bd262102 100644 --- a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java +++ b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java @@ -10,7 +10,7 @@ @ToString public class CafeRegisterRequest { @NotBlank(message = "1012:공백일 수 없습니다.") - private String id; + private String mapId; @NotBlank(message = "1012:공백일 수 없습니다.") private String name; diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index d25af000..c6bb9778 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -31,7 +31,6 @@ import javax.persistence.EntityManager; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -54,13 +53,10 @@ public class CafeService { @Transactional public void save(CafeRegisterRequest request) { - Cafe cafe = new Cafe(request.getId(), request.getName(), request.getRoadAddress(), request.getPhoneNumber()); - - - Optional cafeOptional = cafeRepository.findByMapId(request.getId()); + Cafe cafe = new Cafe(request.getMapId(), request.getName(), request.getRoadAddress(), request.getPhoneNumber()); try { - cafeRepository.findByMapId(request.getId()) + cafeRepository.findByMapId(request.getMapId()) .ifPresentOrElse( existedCafe -> existedCafe.updateCafeRoadAddress(request.getRoadAddress()), () -> cafeRepository.save(cafe) From 0dbdf7a3007db06d3637ac3dadd730ea8413beaa Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Sat, 9 Dec 2023 12:18:58 +0900 Subject: [PATCH 08/16] =?UTF-8?q?fix:=20MyCommentCafeResponse=EC=97=90?= =?UTF-8?q?=EC=84=9C=EC=9D=98=20comments=20=ED=95=84=EB=93=9C=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: modify MyCommentCafeResponse fileName * style: delete unused code --- .../dto/response/MyCommentCafeResponse.java | 9 ++++----- .../mocacong/server/service/CafeService.java | 1 - .../server/acceptance/MemberAcceptanceTest.java | 16 ++++++++-------- .../mocacong/server/service/CafeServiceTest.java | 4 ++-- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java index c85bcecd..fd32cae6 100644 --- a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java @@ -1,13 +1,12 @@ package mocacong.server.dto.response; -import java.util.List; -import java.util.stream.Collectors; - import lombok.*; - import mocacong.server.domain.Cafe; import mocacong.server.domain.Comment; +import java.util.List; +import java.util.stream.Collectors; + @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @@ -18,7 +17,7 @@ public class MyCommentCafeResponse { private String name; private String studyType; private String roadAddress; - private List comments; + private List commentContents; public static MyCommentCafeResponse of(Cafe cafe, List comments) { List contents = comments.stream() diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index c6bb9778..dc1bf59a 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -182,7 +182,6 @@ public MyCommentCafesResponse findMyCommentCafes(Long memberId, int page, int co List comments = commentRepository.findAllByMemberId(member.getId()); List responses = comments.stream() -// .map(MyCommentCafeResponse::from) .collect(Collectors.groupingByConcurrent(Comment::getCafe)) .entrySet() .stream() diff --git a/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/MemberAcceptanceTest.java index b54f6034..4a9b19cc 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 { @@ -399,7 +399,7 @@ void findMyCommentCafes() { assertAll( ()->assertThat(response.getCafes()).hasSize(2), - ()->assertThat(response.getCafes().get(0).getComments()).containsExactlyInAnyOrder("댓글3") + ()->assertThat(response.getCafes().get(0).getCommentContents()).containsExactlyInAnyOrder("댓글3") ); } diff --git a/src/test/java/mocacong/server/service/CafeServiceTest.java b/src/test/java/mocacong/server/service/CafeServiceTest.java index 1e758101..339f85a9 100644 --- a/src/test/java/mocacong/server/service/CafeServiceTest.java +++ b/src/test/java/mocacong/server/service/CafeServiceTest.java @@ -490,8 +490,8 @@ void findMyCommentCafes() { () -> assertThat(actual.getIsEnd()).isTrue(), // 댓글 수는 3개지만, 카페 종류가 2종류이므로 response size는 2개 () -> assertThat(actual.getCafes()).hasSize(2), - () -> assertThat(actual.getCafes().get(0).getComments()).containsExactlyInAnyOrder("댓글1", "댓글2"), - () -> assertThat(actual.getCafes().get(1).getComments()).containsExactlyInAnyOrder("댓글3") + () -> assertThat(actual.getCafes().get(0).getCommentContents()).containsExactlyInAnyOrder("댓글1", "댓글2"), + () -> assertThat(actual.getCafes().get(1).getCommentContents()).containsExactlyInAnyOrder("댓글3") ); } From b32fde26893887c1f513491132e5317f5cb867d9 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Sat, 9 Dec 2023 12:23:57 +0900 Subject: [PATCH 09/16] =?UTF-8?q?fix:=20=EC=B9=B4=ED=8E=98=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=203=EA=B0=9C=20=EC=B4=88=EA=B3=BC=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=A0=80=EC=9E=A5=20=EC=8B=9C=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: modified cafeImagesCounts filtering logic to include isUsed column * style: modify ExceedCageImagesTotalCountsException message * refact: extract method for cafe image ownership and isused check --- .../badrequest/ExceedCageImagesTotalCountsException.java | 2 +- src/main/java/mocacong/server/service/CafeService.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java b/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java index b8423939..0510a098 100644 --- a/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java +++ b/src/main/java/mocacong/server/exception/badrequest/ExceedCageImagesTotalCountsException.java @@ -2,6 +2,6 @@ public class ExceedCageImagesTotalCountsException extends BadRequestException{ public ExceedCageImagesTotalCountsException() { - super("카페 이미지는 3개 이상 등록하실 수 없습니다.", 2008); + super("카페 이미지는 4개 이상 등록하실 수 없습니다.", 2008); } } diff --git a/src/main/java/mocacong/server/service/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index dc1bf59a..400f1bc0 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -349,13 +349,17 @@ public CafeImagesSaveResponse saveCafeImage(Long memberId, String mapId, List requestCafeImages) { List currentOwnedCafeImages = cafe.getCafeImages() .stream() - .filter(cafeImage -> cafeImage.isOwned(member)) + .filter(cafeImage -> isOwnedAndUsed(cafeImage, member)) .collect(Collectors.toList()); if (currentOwnedCafeImages.size() + requestCafeImages.size() > CAFE_IMAGES_PER_MEMBER_LIMIT_COUNTS) { throw new ExceedCageImagesTotalCountsException(); } } + private boolean isOwnedAndUsed(CafeImage cafeImage, Member member) { + return cafeImage.isOwned(member) && cafeImage.getIsUsed(); + } + @Transactional(readOnly = true) public CafeImagesResponse findCafeImages(Long memberId, String mapId, Integer page, int count) { Cafe cafe = cafeRepository.findByMapId(mapId) From ca593ad8c34b0e09732dcf3f1f4a58ec62da0262 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim <76640167+jung-woo-kim@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:12:29 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20dto=20=EB=B3=80=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#142)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/MyReviewCafeResponse.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java index a93fd23f..72d6b163 100644 --- a/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyReviewCafeResponse.java @@ -14,12 +14,12 @@ public class MyReviewCafeResponse { private String myStudyType; private int myScore; private String roadAddress; - private String wifi; - private String parking; - private String toilet; - private String power; - private String sound; - private String desk; + private String myWifi; + private String myParking; + private String myToilet; + private String myPower; + private String mySound; + private String myDesk; public MyReviewCafeResponse(String mapId, String name, StudyType myStudyType, int myScore, String roadAddress, CafeDetail cafeDetail) { this.mapId = mapId; @@ -27,11 +27,11 @@ public MyReviewCafeResponse(String mapId, String name, StudyType myStudyType, in this.myStudyType = myStudyType.getValue(); this.myScore = myScore; this.roadAddress = roadAddress; - this.wifi = cafeDetail.getWifiValue(); - this.parking = cafeDetail.getParkingValue(); - this.toilet = cafeDetail.getToiletValue(); - this.power = cafeDetail.getPowerValue(); - this.sound = cafeDetail.getSoundValue(); - this.desk = cafeDetail.getDeskValue(); + this.myWifi = cafeDetail.getWifiValue(); + this.myParking = cafeDetail.getParkingValue(); + this.myToilet = cafeDetail.getToiletValue(); + this.myPower = cafeDetail.getPowerValue(); + this.mySound = cafeDetail.getSoundValue(); + this.myDesk = cafeDetail.getDeskValue(); } } From d5578ab288e2cabcdfd1bd82d8e948d7fb1b9748 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:35:14 +0900 Subject: [PATCH 11/16] feat: add score field to MyCommentCafeResponse (#143) --- .../mocacong/server/dto/response/MyCommentCafeResponse.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java index fd32cae6..17dab5ef 100644 --- a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java @@ -17,12 +17,14 @@ public class MyCommentCafeResponse { private String name; private String studyType; private String roadAddress; + private double score; private List commentContents; public static MyCommentCafeResponse of(Cafe cafe, List comments) { List contents = comments.stream() .map(Comment::getContent) .collect(Collectors.toList()); - return new MyCommentCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), cafe.getRoadAddress(), contents); + return new MyCommentCafeResponse(cafe.getMapId(), cafe.getName(), cafe.getStudyType(), cafe.getRoadAddress(), + cafe.findAverageScore(), contents); } } From c4b58e08af1613c4cd12575ce915fd4b4332ec8e Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:40:01 +0900 Subject: [PATCH 12/16] feat: change roadAddress and phoneNumber to nullable field in CafeRegisterRequest (#144) --- .../java/mocacong/server/dto/request/CafeRegisterRequest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java index bd262102..f27f43fc 100644 --- a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java +++ b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java @@ -15,9 +15,7 @@ public class CafeRegisterRequest { @NotBlank(message = "1012:공백일 수 없습니다.") private String name; - @NotBlank(message = "1012:공백일 수 없습니다.") private String roadAddress; - @NotBlank(message = "1012:공백일 수 없습니다.") private String phoneNumber; } From de435b2f841b7224db8b01bfab3658ba205fb85f Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Sat, 16 Dec 2023 12:15:58 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feat:=20Redis=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=9C=20Refresh=20Token=20=EB=8F=84=EC=9E=85=20(#131)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: redis를 활용한 리프레시 토큰 로직 구현 * fix: 필드명 수정 * feat: 로그인 인터셉터 accessToken 검증 로직 수정 * style: 줄바꿈 * test: 테스트 코드 리프레시 토큰 검증 코드 추가 * feat: OAuth 리프레시 토큰 로직 코드 추가 * test: OAuth 테스트 코드 리프레시 토큰 검증 코드 추가 * fix: 토큰 검증 메서드 분리 * test: 테스트 환경변수 추가 * test: 리프레시 토큰 검증 테스트 코드 추가 * fix: 액세스 토큰과 리프레시 토큰 만료기간 분리 * feat: oauth 로그인 시 redis에 토큰 저장 로직 추가 * fix: 리프레시 토큰 로직 수정 * fix: 인증 토큰 관련 예외 수정 * test: 리프레시 서비스 테스트 코드 작성 * style: 액세스 토큰 재발급 메서드 명 변경 * test: 액세스 토큰 재발급 관련 테스트 코드 추가 * feat: 액세스 토큰 재발급 관련 인수 테스트 코드 추가 * style: 줄바꿈 적용 및 주석 삭제 * fix: ReissueTokenResponse 추가 * style: 한 줄 띄기 추가 * style: 한 줄 띄기 추가 * style: 사용하지 않는 메서드 제거 * fix: Refresh Token UUID 생성 로직으로 변경 * test: application.yml * fix: 리프레시 토큰 로직 수정 * refact: 코드 인라인으로 수정 * delete: 사용하지 않는 예외 파일 삭제 * test: 리프레시 토큰 검증 테스트 코드 수정 * test: 테스트 코드 삭제 * style: 사용하지 않는 로깅 삭제 * style: 리프레시 토큰 정보 추출 메서드 명 수정 * style: 함수 인자 변경 * refact: response 신고 필드 제거 * refactor: delete unused code * refactor: configure refresh token validity period using @Value from yml * refact: move createRefreshToken method to Token * feat: add handling for NPE in findTokenByRefreshToken * feat: add updateToken method in RefreshTokenService * style: remove redundant null check logic * test: add reissueAccessToken test * refact: refact reissue token logic and test code * style: remove @Transactional * style: remove @Sl4fj in AuthServiceTest * feat: change access-key-expire-length for test * style: remove unused reportesCount field --------- Co-authored-by: 임지수 --- .../server/controller/AuthController.java | 12 ++- .../java/mocacong/server/domain/Token.java | 36 ++++++++ .../dto/request/RefreshTokenRequest.java | 15 ++++ .../dto/response/CafeImageReportResponse.java | 2 - .../dto/response/CafeReviewResponse.java | 5 +- .../dto/response/CommentReportResponse.java | 2 - .../dto/response/CommentSaveResponse.java | 2 - .../dto/response/FavoriteSaveResponse.java | 2 - .../dto/response/OAuthTokenResponse.java | 3 +- .../dto/response/ReissueTokenResponse.java | 20 +++++ .../server/dto/response/TokenResponse.java | 8 +- .../NotExpiredAccessTokenException.java | 8 ++ .../AccessTokenExpiredException.java | 15 ++++ .../InvalidAccessTokenException.java | 12 +++ .../InvalidRefreshTokenException.java | 11 +++ .../unauthorized/InvalidTokenException.java | 12 --- .../unauthorized/TokenExpiredException.java | 12 --- .../security/auth/JwtTokenProvider.java | 37 ++++++--- .../security/auth/LoginInterceptor.java | 9 +- .../security/auth/apple/AppleJwtParser.java | 15 ++-- .../auth/apple/AppleOAuthUserProvider.java | 4 +- .../mocacong/server/service/AuthService.java | 61 +++++++++++--- .../server/service/CommentService.java | 2 +- .../server/service/FavoriteService.java | 2 +- .../server/service/MemberService.java | 4 +- .../server/service/RefreshTokenService.java | 62 ++++++++++++++ .../server/service/ReportService.java | 4 +- src/main/resources/application-dev.yml | 3 +- src/main/resources/application.yml | 3 +- .../server/acceptance/AcceptanceFixtures.java | 2 +- .../server/acceptance/AuthAcceptanceTest.java | 9 +- .../security/auth/JwtTokenProviderTest.java | 71 ++++++++++++---- .../auth/apple/AppleJwtParserTest.java | 16 ++-- .../apple/AppleOAuthUserProviderTest.java | 4 +- .../server/service/AuthServiceTest.java | 82 +++++++++++++++++-- .../service/RefreshTokenServiceTest.java | 54 ++++++++++++ src/test/resources/application.yml | 3 +- 37 files changed, 501 insertions(+), 123 deletions(-) create mode 100644 src/main/java/mocacong/server/domain/Token.java create mode 100644 src/main/java/mocacong/server/dto/request/RefreshTokenRequest.java create mode 100644 src/main/java/mocacong/server/dto/response/ReissueTokenResponse.java create mode 100644 src/main/java/mocacong/server/exception/badrequest/NotExpiredAccessTokenException.java create mode 100644 src/main/java/mocacong/server/exception/unauthorized/AccessTokenExpiredException.java create mode 100644 src/main/java/mocacong/server/exception/unauthorized/InvalidAccessTokenException.java create mode 100644 src/main/java/mocacong/server/exception/unauthorized/InvalidRefreshTokenException.java delete mode 100644 src/main/java/mocacong/server/exception/unauthorized/InvalidTokenException.java delete mode 100644 src/main/java/mocacong/server/exception/unauthorized/TokenExpiredException.java create mode 100644 src/main/java/mocacong/server/service/RefreshTokenService.java create mode 100644 src/test/java/mocacong/server/service/RefreshTokenServiceTest.java diff --git a/src/main/java/mocacong/server/controller/AuthController.java b/src/main/java/mocacong/server/controller/AuthController.java index c08b55a7..2623503e 100644 --- a/src/main/java/mocacong/server/controller/AuthController.java +++ b/src/main/java/mocacong/server/controller/AuthController.java @@ -2,12 +2,13 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import javax.validation.Valid; import lombok.RequiredArgsConstructor; import mocacong.server.dto.request.AppleLoginRequest; import mocacong.server.dto.request.AuthLoginRequest; import mocacong.server.dto.request.KakaoLoginRequest; +import mocacong.server.dto.request.RefreshTokenRequest; import mocacong.server.dto.response.OAuthTokenResponse; +import mocacong.server.dto.response.ReissueTokenResponse; import mocacong.server.dto.response.TokenResponse; import mocacong.server.service.AuthService; import org.springframework.http.ResponseEntity; @@ -16,6 +17,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.validation.Valid; + @Tag(name = "Login", description = "인증") @RestController @RequiredArgsConstructor @@ -44,4 +47,11 @@ public ResponseEntity loginKakao(@RequestBody @Valid KakaoLo OAuthTokenResponse response = authService.kakaoOAuthLogin(request); return ResponseEntity.ok(response); } + + @Operation(summary = "토큰 재발급") + @PostMapping("/reissue") + public ResponseEntity refreshAccessToken(@RequestBody @Valid RefreshTokenRequest request) { + ReissueTokenResponse response = authService.reissueAccessToken(request); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/mocacong/server/domain/Token.java b/src/main/java/mocacong/server/domain/Token.java new file mode 100644 index 00000000..c950bf7a --- /dev/null +++ b/src/main/java/mocacong/server/domain/Token.java @@ -0,0 +1,36 @@ +package mocacong.server.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.redis.core.TimeToLive; + +import javax.persistence.Id; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Token { + + @Id + private Long id; + + private String refreshToken; + + private String accessToken; + + @TimeToLive(unit = TimeUnit.MILLISECONDS) + private long expiration; + + public void setAccessToken(String newAccessToken) { + this.accessToken = newAccessToken; + } + + public static String createRefreshToken() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/main/java/mocacong/server/dto/request/RefreshTokenRequest.java b/src/main/java/mocacong/server/dto/request/RefreshTokenRequest.java new file mode 100644 index 00000000..1608fb7e --- /dev/null +++ b/src/main/java/mocacong/server/dto/request/RefreshTokenRequest.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 RefreshTokenRequest { + + @NotBlank(message = "1012:공백일 수 없습니다.") + private String refreshToken; +} diff --git a/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java b/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java index a78b0881..aeea3425 100644 --- a/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java +++ b/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java @@ -11,6 +11,4 @@ public class CafeImageReportResponse { private int cafeImageReportCount; - - private int userReportCount; } diff --git a/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java b/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java index 04927697..da1042fb 100644 --- a/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java +++ b/src/main/java/mocacong/server/dto/response/CafeReviewResponse.java @@ -20,12 +20,10 @@ public class CafeReviewResponse { private String sound; private String desk; private int reviewsCount; - private int userReportCount; 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, @@ -36,8 +34,7 @@ public static CafeReviewResponse of(double score, Cafe cafe, Member member) { cafeDetail.getPowerValue(), cafeDetail.getSoundValue(), cafeDetail.getDeskValue(), - reviewsCount, - userReportCount + reviewsCount ); } } diff --git a/src/main/java/mocacong/server/dto/response/CommentReportResponse.java b/src/main/java/mocacong/server/dto/response/CommentReportResponse.java index 573bf4be..d7a6f775 100644 --- a/src/main/java/mocacong/server/dto/response/CommentReportResponse.java +++ b/src/main/java/mocacong/server/dto/response/CommentReportResponse.java @@ -11,6 +11,4 @@ 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 44c45e4d..3d65bb7f 100644 --- a/src/main/java/mocacong/server/dto/response/CommentSaveResponse.java +++ b/src/main/java/mocacong/server/dto/response/CommentSaveResponse.java @@ -9,6 +9,4 @@ public class CommentSaveResponse { private Long id; - - private int userReportCount; } diff --git a/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java b/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java index b5a7caf9..9460d462 100644 --- a/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java +++ b/src/main/java/mocacong/server/dto/response/FavoriteSaveResponse.java @@ -9,6 +9,4 @@ public class FavoriteSaveResponse { private Long favoriteId; - - private int userReportCount; } diff --git a/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java b/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java index 2e515483..ba9a3486 100644 --- a/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java +++ b/src/main/java/mocacong/server/dto/response/OAuthTokenResponse.java @@ -8,7 +8,8 @@ @ToString public class OAuthTokenResponse { - private String token; + private String accessToken; + private String refreshToken; private String email; private Boolean isRegistered; private String platformId; diff --git a/src/main/java/mocacong/server/dto/response/ReissueTokenResponse.java b/src/main/java/mocacong/server/dto/response/ReissueTokenResponse.java new file mode 100644 index 00000000..b34b1027 --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/ReissueTokenResponse.java @@ -0,0 +1,20 @@ +package mocacong.server.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class ReissueTokenResponse { + private String accessToken; + + private int userReportCount; + + public static ReissueTokenResponse from(final String accessToken, int userReportCount) { + return new ReissueTokenResponse(accessToken, userReportCount); + } +} diff --git a/src/main/java/mocacong/server/dto/response/TokenResponse.java b/src/main/java/mocacong/server/dto/response/TokenResponse.java index fe22164a..44200ea6 100644 --- a/src/main/java/mocacong/server/dto/response/TokenResponse.java +++ b/src/main/java/mocacong/server/dto/response/TokenResponse.java @@ -10,11 +10,13 @@ @AllArgsConstructor @ToString public class TokenResponse { - private String token; + private String accessToken; + + private String refreshToken; private int userReportCount; - public static TokenResponse from(final String token, int userReportCount) { - return new TokenResponse(token, userReportCount); + public static TokenResponse from(final String accessToken, final String refreshToken, int userReportCount) { + return new TokenResponse(accessToken, refreshToken, userReportCount); } } diff --git a/src/main/java/mocacong/server/exception/badrequest/NotExpiredAccessTokenException.java b/src/main/java/mocacong/server/exception/badrequest/NotExpiredAccessTokenException.java new file mode 100644 index 00000000..a3b2ed40 --- /dev/null +++ b/src/main/java/mocacong/server/exception/badrequest/NotExpiredAccessTokenException.java @@ -0,0 +1,8 @@ +package mocacong.server.exception.badrequest; + +public class NotExpiredAccessTokenException extends BadRequestException { + + public NotExpiredAccessTokenException() { + super("아직 만료되지 않은 액세스 토큰입니다", 1022); + } +} diff --git a/src/main/java/mocacong/server/exception/unauthorized/AccessTokenExpiredException.java b/src/main/java/mocacong/server/exception/unauthorized/AccessTokenExpiredException.java new file mode 100644 index 00000000..09db56cd --- /dev/null +++ b/src/main/java/mocacong/server/exception/unauthorized/AccessTokenExpiredException.java @@ -0,0 +1,15 @@ +package mocacong.server.exception.unauthorized; + +import lombok.Getter; + +@Getter +public class AccessTokenExpiredException extends UnauthorizedException { + + public AccessTokenExpiredException() { + super("Access Token 유효기간이 만료되었습니다.", 1014); + } + + public AccessTokenExpiredException(String message) { + super(message, 1014); + } +} diff --git a/src/main/java/mocacong/server/exception/unauthorized/InvalidAccessTokenException.java b/src/main/java/mocacong/server/exception/unauthorized/InvalidAccessTokenException.java new file mode 100644 index 00000000..ee68ff31 --- /dev/null +++ b/src/main/java/mocacong/server/exception/unauthorized/InvalidAccessTokenException.java @@ -0,0 +1,12 @@ +package mocacong.server.exception.unauthorized; + +public class InvalidAccessTokenException extends UnauthorizedException { + + public InvalidAccessTokenException() { + super("올바르지 않은 Access Token 입니다. 다시 로그인해주세요.", 1015); + } + + public InvalidAccessTokenException(String message) { + super(message, 1015); + } +} diff --git a/src/main/java/mocacong/server/exception/unauthorized/InvalidRefreshTokenException.java b/src/main/java/mocacong/server/exception/unauthorized/InvalidRefreshTokenException.java new file mode 100644 index 00000000..4980d687 --- /dev/null +++ b/src/main/java/mocacong/server/exception/unauthorized/InvalidRefreshTokenException.java @@ -0,0 +1,11 @@ +package mocacong.server.exception.unauthorized; + +import lombok.Getter; + +@Getter +public class InvalidRefreshTokenException extends UnauthorizedException { + + public InvalidRefreshTokenException() { + super("올바르지 않은 Refresh Token 입니다. 다시 로그인해주세요.", 1021); + } +} diff --git a/src/main/java/mocacong/server/exception/unauthorized/InvalidTokenException.java b/src/main/java/mocacong/server/exception/unauthorized/InvalidTokenException.java deleted file mode 100644 index ff0f897f..00000000 --- a/src/main/java/mocacong/server/exception/unauthorized/InvalidTokenException.java +++ /dev/null @@ -1,12 +0,0 @@ -package mocacong.server.exception.unauthorized; - -public class InvalidTokenException extends UnauthorizedException { - - public InvalidTokenException() { - super("올바르지 않은 토큰입니다. 다시 로그인해주세요.", 1015); - } - - public InvalidTokenException(String message) { - super(message, 1015); - } -} diff --git a/src/main/java/mocacong/server/exception/unauthorized/TokenExpiredException.java b/src/main/java/mocacong/server/exception/unauthorized/TokenExpiredException.java deleted file mode 100644 index 702da112..00000000 --- a/src/main/java/mocacong/server/exception/unauthorized/TokenExpiredException.java +++ /dev/null @@ -1,12 +0,0 @@ -package mocacong.server.exception.unauthorized; - -public class TokenExpiredException extends UnauthorizedException { - - public TokenExpiredException() { - super("로그인 인증 유효기간이 만료되었습니다. 다시 로그인 해주세요.", 1014); - } - - public TokenExpiredException(String message) { - super(message, 1014); - } -} diff --git a/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java b/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java index 2ceb331f..ebd3c7b3 100644 --- a/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java +++ b/src/main/java/mocacong/server/security/auth/JwtTokenProvider.java @@ -1,29 +1,31 @@ package mocacong.server.security.auth; -import mocacong.server.exception.unauthorized.InvalidTokenException; -import mocacong.server.exception.unauthorized.TokenExpiredException; +import io.jsonwebtoken.*; +import mocacong.server.exception.unauthorized.AccessTokenExpiredException; +import mocacong.server.exception.unauthorized.InvalidAccessTokenException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import io.jsonwebtoken.*; import java.util.Date; @Component public class JwtTokenProvider { private final String secretKey; - private final long validityInMilliseconds; + private final long validityAccessTokenInMilliseconds; + private final JwtParser jwtParser; public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") String secretKey, - @Value("${security.jwt.token.expire-length}") long validityInMilliseconds) { + @Value("${security.jwt.token.access-key-expire-length}") + long validityAccessTokenInMilliseconds) { this.secretKey = secretKey; - this.validityInMilliseconds = validityInMilliseconds; + this.validityAccessTokenInMilliseconds = validityAccessTokenInMilliseconds; this.jwtParser = Jwts.parser().setSigningKey(secretKey); } - public String createToken(Long memberId) { + public String createAccessToken(Long memberId) { Date now = new Date(); - Date validity = new Date(now.getTime() + validityInMilliseconds); + Date validity = new Date(now.getTime() + validityAccessTokenInMilliseconds); return Jwts.builder() .setSubject(String.valueOf(memberId)) @@ -33,23 +35,32 @@ public String createToken(Long memberId) { .compact(); } - public void validateToken(String token) { + public void validateAccessToken(String token) { try { jwtParser.parseClaimsJws(token); } catch (ExpiredJwtException e) { - throw new TokenExpiredException(); + throw new AccessTokenExpiredException(); } catch (JwtException e) { - throw new InvalidTokenException(); + throw new InvalidAccessTokenException(); + } + } + + public boolean isExpiredAccessToken(String token) { + try { + jwtParser.parseClaimsJws(token); + } catch (ExpiredJwtException e) { + return true; } + return false; } public String getPayload(String token) { try { return jwtParser.parseClaimsJws(token).getBody().getSubject(); } catch (ExpiredJwtException e) { - throw new TokenExpiredException(); + throw new AccessTokenExpiredException(); } catch (JwtException e) { - throw new InvalidTokenException(); + throw new InvalidAccessTokenException(); } } } diff --git a/src/main/java/mocacong/server/security/auth/LoginInterceptor.java b/src/main/java/mocacong/server/security/auth/LoginInterceptor.java index 7634812e..1dd255b2 100644 --- a/src/main/java/mocacong/server/security/auth/LoginInterceptor.java +++ b/src/main/java/mocacong/server/security/auth/LoginInterceptor.java @@ -1,11 +1,12 @@ package mocacong.server.security.auth; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + @Component public class LoginInterceptor implements HandlerInterceptor { private final JwtTokenProvider jwtTokenProvider; @@ -20,8 +21,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return true; } - String token = AuthorizationExtractor.extractAccessToken(request); - jwtTokenProvider.validateToken(token); + String accessToken = AuthorizationExtractor.extractAccessToken(request); + jwtTokenProvider.validateAccessToken(accessToken); return true; } diff --git a/src/main/java/mocacong/server/security/auth/apple/AppleJwtParser.java b/src/main/java/mocacong/server/security/auth/apple/AppleJwtParser.java index 03245a1f..77bc9ee8 100644 --- a/src/main/java/mocacong/server/security/auth/apple/AppleJwtParser.java +++ b/src/main/java/mocacong/server/security/auth/apple/AppleJwtParser.java @@ -3,13 +3,14 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.*; -import java.security.PublicKey; -import java.util.Map; -import mocacong.server.exception.unauthorized.InvalidTokenException; -import mocacong.server.exception.unauthorized.TokenExpiredException; +import mocacong.server.exception.unauthorized.InvalidAccessTokenException; +import mocacong.server.exception.unauthorized.AccessTokenExpiredException; import org.springframework.stereotype.Component; import org.springframework.util.Base64Utils; +import java.security.PublicKey; +import java.util.Map; + @Component public class AppleJwtParser { @@ -24,7 +25,7 @@ public Map parseHeaders(String identityToken) { String decodedHeader = new String(Base64Utils.decodeFromUrlSafeString(encodedHeader)); return OBJECT_MAPPER.readValue(decodedHeader, Map.class); } catch (JsonProcessingException | ArrayIndexOutOfBoundsException e) { - throw new InvalidTokenException("Apple OAuth Identity Token 형식이 올바르지 않습니다."); + throw new InvalidAccessTokenException("Apple OAuth Identity Token 형식이 올바르지 않습니다."); } } @@ -35,9 +36,9 @@ public Claims parsePublicKeyAndGetClaims(String idToken, PublicKey publicKey) { .parseClaimsJws(idToken) .getBody(); } catch (ExpiredJwtException e) { - throw new TokenExpiredException("Apple OAuth 로그인 중 Identity Token 유효기간이 만료됐습니다."); + throw new AccessTokenExpiredException("Apple OAuth 로그인 중 Identity Token 유효기간이 만료됐습니다."); } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) { - throw new InvalidTokenException("Apple OAuth Identity Token 값이 올바르지 않습니다."); + throw new InvalidAccessTokenException("Apple OAuth Identity Token 값이 올바르지 않습니다."); } } } 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 28b95460..49a47c44 100644 --- a/src/main/java/mocacong/server/security/auth/apple/AppleOAuthUserProvider.java +++ b/src/main/java/mocacong/server/security/auth/apple/AppleOAuthUserProvider.java @@ -2,7 +2,7 @@ import io.jsonwebtoken.Claims; import lombok.RequiredArgsConstructor; -import mocacong.server.exception.unauthorized.InvalidTokenException; +import mocacong.server.exception.unauthorized.InvalidAccessTokenException; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import org.springframework.stereotype.Component; @@ -31,7 +31,7 @@ public OAuthPlatformMemberResponse getApplePlatformMember(String identityToken) private void validateClaims(Claims claims) { if (!appleClaimsValidator.isValid(claims)) { - throw new InvalidTokenException("Apple OAuth Claims 값이 올바르지 않습니다."); + throw new InvalidAccessTokenException("Apple OAuth Claims 값이 올바르지 않습니다."); } } } diff --git a/src/main/java/mocacong/server/service/AuthService.java b/src/main/java/mocacong/server/service/AuthService.java index 9898cd55..874f2cea 100644 --- a/src/main/java/mocacong/server/service/AuthService.java +++ b/src/main/java/mocacong/server/service/AuthService.java @@ -4,11 +4,15 @@ import mocacong.server.domain.Member; import mocacong.server.domain.Platform; import mocacong.server.domain.Status; +import mocacong.server.domain.Token; import mocacong.server.dto.request.AppleLoginRequest; import mocacong.server.dto.request.AuthLoginRequest; import mocacong.server.dto.request.KakaoLoginRequest; +import mocacong.server.dto.request.RefreshTokenRequest; import mocacong.server.dto.response.OAuthTokenResponse; +import mocacong.server.dto.response.ReissueTokenResponse; import mocacong.server.dto.response.TokenResponse; +import mocacong.server.exception.badrequest.NotExpiredAccessTokenException; import mocacong.server.exception.badrequest.PasswordMismatchException; import mocacong.server.exception.notfound.NotFoundMemberException; import mocacong.server.exception.unauthorized.InactiveMemberException; @@ -19,11 +23,14 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import javax.transaction.Transactional; + @Service @RequiredArgsConstructor public class AuthService { private final MemberRepository memberRepository; + private final RefreshTokenService refreshTokenService; private final JwtTokenProvider jwtTokenProvider; private final PasswordEncoder passwordEncoder; private final AppleOAuthUserProvider appleOAuthUserProvider; @@ -34,10 +41,14 @@ public TokenResponse login(AuthLoginRequest request) { validatePassword(findMember, request.getPassword()); validateStatus(findMember); - String token = issueToken(findMember); + String accessToken = issueAccessToken(findMember); + String refreshToken = issueRefreshToken(); + + // Redis에 refresh 토큰 저장 (사용자 기본키 Id, refresh 토큰, access 토큰) + refreshTokenService.saveTokenInfo(findMember.getId(), refreshToken, accessToken); int userReportCount = findMember.getReportCount(); - return TokenResponse.from(token, userReportCount); + return TokenResponse.from(accessToken, refreshToken, userReportCount); } public OAuthTokenResponse appleOAuthLogin(AppleLoginRequest request) { @@ -66,26 +77,37 @@ private OAuthTokenResponse generateOAuthTokenResponse(Platform platform, String .orElseThrow(NotFoundMemberException::new); validateStatus(findMember); int userReportCount = findMember.getReportCount(); - String token = issueToken(findMember); + String accessToken = issueAccessToken(findMember); + String refreshToken = issueRefreshToken(); + + refreshTokenService.saveTokenInfo(findMember.getId(), refreshToken, accessToken); + // OAuth 로그인은 성공했지만 회원가입에 실패한 경우 if (!findMember.isRegisteredOAuthMember()) { - return new OAuthTokenResponse(token, findMember.getEmail(), false, platformId, - userReportCount); + return new OAuthTokenResponse(accessToken, refreshToken, findMember.getEmail(), + false, platformId, userReportCount); } - return new OAuthTokenResponse(token, findMember.getEmail(), true, platformId, - userReportCount); + return new OAuthTokenResponse(accessToken, refreshToken, findMember.getEmail(), + true, platformId, userReportCount); }) .orElseGet(() -> { 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, + String accessToken = issueAccessToken(savedMember); + String refreshToken = issueRefreshToken(); + + refreshTokenService.saveTokenInfo(savedMember.getId(), refreshToken, accessToken); + return new OAuthTokenResponse(accessToken, refreshToken, email, false, platformId, savedMember.getReportCount()); }); } - private String issueToken(final Member findMember) { - return jwtTokenProvider.createToken(findMember.getId()); + private String issueAccessToken(final Member findMember) { + return jwtTokenProvider.createAccessToken(findMember.getId()); + } + + private String issueRefreshToken() { + return Token.createRefreshToken(); } private void validatePassword(final Member findMember, final String password) { @@ -99,4 +121,21 @@ private void validateStatus(final Member findMember) { throw new InactiveMemberException(); } } + + @Transactional + public ReissueTokenResponse reissueAccessToken(RefreshTokenRequest request) { + String refreshToken = request.getRefreshToken(); + Member member = refreshTokenService.getMemberFromRefreshToken(refreshToken); + Token token = refreshTokenService.findTokenByRefreshToken(refreshToken); + String oldAccessToken = token.getAccessToken(); + + // 이전에 발급된 액세스 토큰이 만료가 되어야 새로운 액세스 토큰 발급 + if (jwtTokenProvider.isExpiredAccessToken(oldAccessToken)) { + String newAccessToken = issueAccessToken(member); + token.setAccessToken(newAccessToken); + refreshTokenService.updateToken(token); + return ReissueTokenResponse.from(newAccessToken, member.getReportCount()); + } + throw new NotExpiredAccessTokenException(); + } } diff --git a/src/main/java/mocacong/server/service/CommentService.java b/src/main/java/mocacong/server/service/CommentService.java index c3ac6832..f3ae64d8 100644 --- a/src/main/java/mocacong/server/service/CommentService.java +++ b/src/main/java/mocacong/server/service/CommentService.java @@ -46,7 +46,7 @@ public CommentSaveResponse save(Long memberId, String mapId, String content) { .orElseThrow(NotFoundMemberException::new); Comment comment = new Comment(cafe, member, content); - return new CommentSaveResponse(commentRepository.save(comment).getId(), member.getReportCount()); + return new CommentSaveResponse(commentRepository.save(comment).getId()); } @Transactional(readOnly = true) diff --git a/src/main/java/mocacong/server/service/FavoriteService.java b/src/main/java/mocacong/server/service/FavoriteService.java index 1fb2fca2..35b16f22 100644 --- a/src/main/java/mocacong/server/service/FavoriteService.java +++ b/src/main/java/mocacong/server/service/FavoriteService.java @@ -41,7 +41,7 @@ public FavoriteSaveResponse save(Long memberId, String mapId) { try { Favorite favorite = new Favorite(member, cafe); - return new FavoriteSaveResponse(favoriteRepository.save(favorite).getId(), member.getReportCount()); + return new FavoriteSaveResponse(favoriteRepository.save(favorite).getId()); } catch (DataIntegrityViolationException e) { throw new AlreadyExistsFavorite(); } diff --git a/src/main/java/mocacong/server/service/MemberService.java b/src/main/java/mocacong/server/service/MemberService.java index 2dce9893..e90e2d6b 100644 --- a/src/main/java/mocacong/server/service/MemberService.java +++ b/src/main/java/mocacong/server/service/MemberService.java @@ -127,8 +127,8 @@ public EmailVerifyCodeResponse sendEmailVerifyCode(EmailVerifyCodeRequest reques int randomNumber = random.nextInt(EMAIL_VERIFY_CODE_MAXIMUM_NUMBER + 1); String code = String.format("%04d", randomNumber); awsSESSender.sendToVerifyEmail(requestEmail, code); - String token = jwtTokenProvider.createToken(member.getId()); - return new EmailVerifyCodeResponse(token, code); + String accessToken = jwtTokenProvider.createAccessToken(member.getId()); + return new EmailVerifyCodeResponse(accessToken, code); } @Transactional diff --git a/src/main/java/mocacong/server/service/RefreshTokenService.java b/src/main/java/mocacong/server/service/RefreshTokenService.java new file mode 100644 index 00000000..d0c07a2c --- /dev/null +++ b/src/main/java/mocacong/server/service/RefreshTokenService.java @@ -0,0 +1,62 @@ +package mocacong.server.service; + +import mocacong.server.domain.Member; +import mocacong.server.domain.Token; +import mocacong.server.exception.notfound.NotFoundMemberException; +import mocacong.server.exception.unauthorized.InvalidRefreshTokenException; +import mocacong.server.repository.MemberRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +public class RefreshTokenService { + + private final long validityRefreshTokenInMilliseconds; + private final MemberRepository memberRepository; + private final RedisTemplate redisTemplate; + + public RefreshTokenService(@Value("${security.jwt.token.refresh-key-expire-length}") + long validityRefreshTokenInMilliseconds, + MemberRepository memberRepository, + RedisTemplate redisTemplate) { + this.validityRefreshTokenInMilliseconds = validityRefreshTokenInMilliseconds; + this.memberRepository = memberRepository; + this.redisTemplate = redisTemplate; + } + + public void saveTokenInfo(Long memberId, String refreshToken, String accessToken) { + Token token = Token.builder() + .id(memberId) + .refreshToken(refreshToken) + .accessToken(accessToken) + .expiration(validityRefreshTokenInMilliseconds) // 리프레시 토큰 유효기간 + .build(); + + redisTemplate.opsForValue().set(refreshToken, token, validityRefreshTokenInMilliseconds, TimeUnit.SECONDS); + } + + public Member getMemberFromRefreshToken(String refreshToken) { + Token token = findTokenByRefreshToken(refreshToken); + if (token.getExpiration() > 0) { + Long memberId = token.getId(); + return memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); + } + throw new InvalidRefreshTokenException(); + } + + public Token findTokenByRefreshToken(String refreshToken) { + Token token = (Token) redisTemplate.opsForValue().get(refreshToken); + if (token != null) { + return token; + } + throw new InvalidRefreshTokenException(); + } + + public void updateToken(Token token) { + redisTemplate.opsForValue().set(token.getRefreshToken(), token, token.getExpiration(), TimeUnit.MILLISECONDS); + } +} diff --git a/src/main/java/mocacong/server/service/ReportService.java b/src/main/java/mocacong/server/service/ReportService.java index c3aeb616..a283d761 100644 --- a/src/main/java/mocacong/server/service/ReportService.java +++ b/src/main/java/mocacong/server/service/ReportService.java @@ -56,7 +56,7 @@ public CommentReportResponse reportComment(Long memberId, Long commentId, String } catch (DataIntegrityViolationException e) { throw new DuplicateReportCommentException(); } - return new CommentReportResponse(comment.getReportsCount(), reporter.getReportCount()); + return new CommentReportResponse(comment.getReportsCount()); } private void createCommentReport(Comment comment, Member reporter, String reportReason) { @@ -114,7 +114,7 @@ public CafeImageReportResponse reportCafeImage(Long memberId, Long cafeImageId, } catch (DataIntegrityViolationException e) { throw new DuplicateReportCafeImageException(); } - return new CafeImageReportResponse(cafeImage.getReportsCount(), reporter.getReportCount()); + return new CafeImageReportResponse(cafeImage.getReportsCount()); } private void createCafeImageReport(CafeImage cafeImage, Member reporter, String reportReason) { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e7eb022e..e9a6cf9e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -31,7 +31,8 @@ spring: security.jwt.token: secret-key: ${JWT_SECRET_KEY} - expire-length: ${JWT_EXPIRE_LENGTH} + access-key-expire-length: ${JWT_ACCESS_EXPIRE_LENGTH} + refresh-key-expire-length: ${JWT_REFRESH_EXPIRE_LENGTH} springdoc: default-consumes-media-type: application/json diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 106dba86..c4ebb647 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -35,7 +35,8 @@ spring: security.jwt.token: secret-key: testtesttesttesttesttesttesttesttesttest - expire-length: 864000 + access-key-expire-length: 10000 + refresh-key-expire-length: 1728000 springdoc: default-consumes-media-type: application/json diff --git a/src/test/java/mocacong/server/acceptance/AcceptanceFixtures.java b/src/test/java/mocacong/server/acceptance/AcceptanceFixtures.java index c3d77ca3..0f956416 100644 --- a/src/test/java/mocacong/server/acceptance/AcceptanceFixtures.java +++ b/src/test/java/mocacong/server/acceptance/AcceptanceFixtures.java @@ -28,7 +28,7 @@ public class AcceptanceFixtures { .statusCode(HttpStatus.OK.value()) .extract() .as(TokenResponse.class) - .getToken(); + .getAccessToken(); } public static ExtractableResponse 카페_등록(CafeRegisterRequest request) { diff --git a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java index f385dc11..6a3252f2 100644 --- a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java @@ -15,7 +15,9 @@ 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.assertAll; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -47,8 +49,11 @@ void login() { .extract() .as(TokenResponse.class); - assertNotNull(tokenResponse); - assertNotNull(tokenResponse.getToken()); + assertAll( + () -> assertNotNull(tokenResponse), + () -> assertNotNull(tokenResponse.getAccessToken()), + () -> assertNotNull(tokenResponse.getRefreshToken()) + ); } @Test diff --git a/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java b/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java index c9cc1f77..be59dbc6 100644 --- a/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java +++ b/src/test/java/mocacong/server/security/auth/JwtTokenProviderTest.java @@ -1,40 +1,51 @@ package mocacong.server.security.auth; -import mocacong.server.exception.unauthorized.InvalidTokenException; -import mocacong.server.exception.unauthorized.TokenExpiredException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import mocacong.server.exception.unauthorized.AccessTokenExpiredException; +import mocacong.server.exception.unauthorized.InvalidAccessTokenException; 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.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; +import java.util.Date; + import static org.assertj.core.api.Assertions.*; @SpringBootTest class JwtTokenProviderTest { @Autowired private JwtTokenProvider jwtTokenProvider; - private String token; + + @Value("${security.jwt.token.secret-key}") + private String secretKey; + + private String accessToken; @DisplayName("payload 정보를 통해 유효한 JWT 토큰을 생성한다") @Test public void createToken() { Long payload = 1L; - String token = jwtTokenProvider.createToken(payload); + accessToken = jwtTokenProvider.createAccessToken(payload); - Assertions.assertNotNull(token); - Assertions.assertTrue(token.length() > 0); + Assertions.assertAll( + () -> Assertions.assertNotNull(accessToken), + () -> Assertions.assertTrue(accessToken.length() > 0) + ); } @DisplayName("올바른 토큰 정보로 payload를 조회한다") @Test void getPayload() { - token = jwtTokenProvider.createToken(1L); + accessToken = jwtTokenProvider.createAccessToken(1L); - String payload = jwtTokenProvider.getPayload(token); + String payload = jwtTokenProvider.getPayload(accessToken); - assertThat(jwtTokenProvider.getPayload(token)).isEqualTo(payload); + assertThat(jwtTokenProvider.getPayload(accessToken)).isEqualTo(payload); } @DisplayName("유효하지 않은 토큰 형식의 토큰으로 payload를 조회할 경우 예외를 발생시킨다") @@ -42,18 +53,19 @@ void getPayload() { void getPayloadByInvalidToken() { String invalidToken = "invalid-token"; - assertThatThrownBy(() -> jwtTokenProvider.validateToken(invalidToken)) - .isInstanceOf(InvalidTokenException.class); + assertThatThrownBy(() -> jwtTokenProvider.validateAccessToken(invalidToken)) + .isInstanceOf(InvalidAccessTokenException.class); } @DisplayName("만료된 토큰으로 payload를 조회할 경우 예외를 발생시킨다") @Test void getPayloadByExpiredToken() { long expirationMillis = 1L; - JwtTokenProvider jwtTokenProvider = new JwtTokenProvider("secret-key", expirationMillis); + JwtTokenProvider jwtTokenProvider = new JwtTokenProvider("secret-key", + expirationMillis); Long expiredPayload = 1L; - String expiredToken = jwtTokenProvider.createToken(expiredPayload); + String expiredToken = jwtTokenProvider.createAccessToken(expiredPayload); try { Thread.sleep(expirationMillis); } catch (InterruptedException e) { @@ -61,7 +73,7 @@ void getPayloadByExpiredToken() { } assertThatThrownBy(() -> jwtTokenProvider.getPayload(expiredToken)) - .isInstanceOf(TokenExpiredException.class); + .isInstanceOf(AccessTokenExpiredException.class); } @DisplayName("시크릿 키가 틀린 토큰 정보로 payload를 조회할 경우 예외를 발생시킨다") @@ -71,13 +83,36 @@ void getPayloadByWrongSecretKeyToken() { String correctSecretKey = "correct-secret-key"; String wrongSecretKey = "wrong-secret-key"; - JwtTokenProvider tokenProvider = new JwtTokenProvider(correctSecretKey, 3600000L); - String token = tokenProvider.createToken(payload); + JwtTokenProvider tokenProvider = new JwtTokenProvider(correctSecretKey, + 3600000L); + String token = tokenProvider.createAccessToken(payload); - assertThatExceptionOfType(InvalidTokenException.class) + assertThatExceptionOfType(InvalidAccessTokenException.class) .isThrownBy(() -> { - JwtTokenProvider wrongTokenProvider = new JwtTokenProvider(wrongSecretKey, 3600000L); + JwtTokenProvider wrongTokenProvider = new JwtTokenProvider(wrongSecretKey, + 3600000L); wrongTokenProvider.getPayload(token); }); } + + @DisplayName("새로운 액세스 토큰을 발급한다") + @Test + void renewAccessToken() { + Long memberId = 1L; + Date now = new Date(); + long expiredValidityInMilliseconds = 0L; + String expiredAccessToken = Jwts.builder() + .setExpiration(new Date(now.getTime() - expiredValidityInMilliseconds)) + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + + // 새로운 액세스 토큰 및 리프레시 토큰 발급 + String newAccessToken = jwtTokenProvider.createAccessToken(memberId); + + Assertions.assertAll( + () -> assertThatThrownBy(() -> jwtTokenProvider.validateAccessToken(expiredAccessToken)) + .isInstanceOf(AccessTokenExpiredException.class), + () -> assertThat(newAccessToken).isNotEmpty() + ); + } } diff --git a/src/test/java/mocacong/server/security/auth/apple/AppleJwtParserTest.java b/src/test/java/mocacong/server/security/auth/apple/AppleJwtParserTest.java index c5adef58..f2f63e38 100644 --- a/src/test/java/mocacong/server/security/auth/apple/AppleJwtParserTest.java +++ b/src/test/java/mocacong/server/security/auth/apple/AppleJwtParserTest.java @@ -3,16 +3,18 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import mocacong.server.exception.unauthorized.AccessTokenExpiredException; +import mocacong.server.exception.unauthorized.InvalidAccessTokenException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + import java.security.*; import java.util.Date; import java.util.Map; -import mocacong.server.exception.unauthorized.InvalidTokenException; -import mocacong.server.exception.unauthorized.TokenExpiredException; + 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 org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; class AppleJwtParserTest { @@ -45,7 +47,7 @@ void parseHeaders() throws NoSuchAlgorithmException { @DisplayName("올바르지 않은 형식의 Apple identity token으로 헤더를 파싱하면 예외를 반환한다") void parseHeadersWithInvalidToken() { assertThatThrownBy(() -> appleJwtParser.parseHeaders("invalidToken")) - .isInstanceOf(InvalidTokenException.class); + .isInstanceOf(InvalidAccessTokenException.class); } @Test @@ -97,7 +99,7 @@ void parseExpiredTokenAndGetClaims() throws NoSuchAlgorithmException { .compact(); assertThatThrownBy(() -> appleJwtParser.parsePublicKeyAndGetClaims(identityToken, publicKey)) - .isInstanceOf(TokenExpiredException.class); + .isInstanceOf(AccessTokenExpiredException.class); } @Test @@ -122,6 +124,6 @@ void parseInvalidPublicKeyAndGetClaims() throws NoSuchAlgorithmException { .compact(); assertThatThrownBy(() -> appleJwtParser.parsePublicKeyAndGetClaims(identityToken, differentPublicKey)) - .isInstanceOf(InvalidTokenException.class); + .isInstanceOf(InvalidAccessTokenException.class); } } diff --git a/src/test/java/mocacong/server/security/auth/apple/AppleOAuthUserProviderTest.java b/src/test/java/mocacong/server/security/auth/apple/AppleOAuthUserProviderTest.java index f2e3d3c6..4b3cfb53 100644 --- a/src/test/java/mocacong/server/security/auth/apple/AppleOAuthUserProviderTest.java +++ b/src/test/java/mocacong/server/security/auth/apple/AppleOAuthUserProviderTest.java @@ -4,7 +4,7 @@ import io.jsonwebtoken.SignatureAlgorithm; import java.security.*; import java.util.Date; -import mocacong.server.exception.unauthorized.InvalidTokenException; +import mocacong.server.exception.unauthorized.InvalidAccessTokenException; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -87,6 +87,6 @@ void invalidClaims() throws NoSuchAlgorithmException { when(appleClaimsValidator.isValid(any())).thenReturn(false); assertThatThrownBy(() -> appleOAuthUserProvider.getApplePlatformMember(identityToken)) - .isInstanceOf(InvalidTokenException.class); + .isInstanceOf(InvalidAccessTokenException.class); } } diff --git a/src/test/java/mocacong/server/service/AuthServiceTest.java b/src/test/java/mocacong/server/service/AuthServiceTest.java index cc564705..b452525f 100644 --- a/src/test/java/mocacong/server/service/AuthServiceTest.java +++ b/src/test/java/mocacong/server/service/AuthServiceTest.java @@ -1,23 +1,34 @@ package mocacong.server.service; +import groovy.util.logging.Slf4j; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import mocacong.server.domain.Member; import mocacong.server.domain.Platform; import mocacong.server.domain.Status; +import mocacong.server.domain.Token; import mocacong.server.dto.request.AppleLoginRequest; import mocacong.server.dto.request.AuthLoginRequest; +import mocacong.server.dto.request.RefreshTokenRequest; import mocacong.server.dto.response.OAuthTokenResponse; +import mocacong.server.dto.response.ReissueTokenResponse; import mocacong.server.dto.response.TokenResponse; +import mocacong.server.exception.badrequest.NotExpiredAccessTokenException; import mocacong.server.exception.badrequest.PasswordMismatchException; import mocacong.server.exception.unauthorized.InactiveMemberException; import mocacong.server.repository.MemberRepository; import mocacong.server.security.auth.OAuthPlatformMemberResponse; import mocacong.server.security.auth.apple.AppleOAuthUserProvider; +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.beans.factory.annotation.Value; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.security.crypto.password.PasswordEncoder; +import java.util.Date; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.anyString; @@ -32,9 +43,13 @@ class AuthServiceTest { private PasswordEncoder passwordEncoder; @Autowired private AuthService authService; + @Value("${security.jwt.token.secret-key}") + private String secretKey; @MockBean private AppleOAuthUserProvider appleOAuthUserProvider; + @MockBean + private RefreshTokenService refreshTokenService; @Test @DisplayName("회원 로그인 요청이 옳다면 토큰을 발급하고 상태는 ACTIVE로 반환한다") @@ -50,7 +65,8 @@ void login() { assertAll( () -> assertThat(member.getStatus()).isEqualTo(Status.ACTIVE), - () -> assertNotNull(tokenResponse.getToken()), + () -> assertNotNull(tokenResponse.getAccessToken()), + () -> assertNotNull(tokenResponse.getRefreshToken()), () -> assertThat(tokenResponse.getUserReportCount()).isEqualTo(0) ); } @@ -96,7 +112,8 @@ void loginOAuthNotRegistered() { OAuthTokenResponse actual = authService.appleOAuthLogin(new AppleLoginRequest("token")); assertAll( - () -> assertThat(actual.getToken()).isNotNull(), + () -> assertThat(actual.getAccessToken()).isNotNull(), + () -> assertThat(actual.getRefreshToken()).isNotNull(), () -> assertThat(actual.getEmail()).isEqualTo(expected), () -> assertThat(actual.getIsRegistered()).isFalse(), () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) @@ -123,7 +140,8 @@ void loginOAuthRegisteredAndMocacongMember() { OAuthTokenResponse actual = authService.appleOAuthLogin(new AppleLoginRequest("token")); assertAll( - () -> assertThat(actual.getToken()).isNotNull(), + () -> assertThat(actual.getAccessToken()).isNotNull(), + () -> assertThat(actual.getRefreshToken()).isNotNull(), () -> assertThat(actual.getEmail()).isEqualTo(expected), () -> assertThat(actual.getIsRegistered()).isTrue(), () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) @@ -143,7 +161,8 @@ void loginOAuthRegisteredButNotMocacongMember() { OAuthTokenResponse actual = authService.appleOAuthLogin(new AppleLoginRequest("token")); assertAll( - () -> assertThat(actual.getToken()).isNotNull(), + () -> assertThat(actual.getAccessToken()).isNotNull(), + () -> assertThat(actual.getRefreshToken()).isNotNull(), () -> assertThat(actual.getEmail()).isEqualTo(expected), () -> assertThat(actual.getIsRegistered()).isFalse(), () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) @@ -164,7 +183,8 @@ void loginOAuthWithMocacongEmail() { OAuthTokenResponse actual = authService.appleOAuthLogin(new AppleLoginRequest("token")); assertAll( - () -> assertThat(actual.getToken()).isNotNull(), + () -> assertThat(actual.getAccessToken()).isNotNull(), + () -> assertThat(actual.getRefreshToken()).isNotNull(), () -> assertThat(actual.getEmail()).isEqualTo(email), () -> assertThat(actual.getIsRegistered()).isFalse(), () -> assertThat(actual.getPlatformId()).isEqualTo(platformId) @@ -185,7 +205,8 @@ void signUpWithAppleEmail() { memberRepository.save(member); assertAll( - () -> assertThat(response.getToken()).isNotNull(), + () -> assertThat(response.getAccessToken()).isNotNull(), + () -> assertThat(response.getRefreshToken()).isNotNull(), () -> assertThat(response.getEmail()).isEqualTo(member.getEmail()) ); } @@ -211,4 +232,53 @@ void loginOAuthWithInactive() { assertThrows(InactiveMemberException.class, () -> authService.appleOAuthLogin(new AppleLoginRequest("token"))); } + + @Test + @DisplayName("액세스 토큰 재발급 요청이 올바르다면 액세스 토큰을 재발급한다") + void reissueAccessToken() { + String refreshToken = "valid-refresh-token"; + Date now = new Date(); + long expiredValidityInMilliseconds = 0L; + String expiredAccessToken = Jwts.builder() + .setExpiration(new Date(now.getTime() + expiredValidityInMilliseconds)) + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + String encodedPassword = passwordEncoder.encode("a1b2c3d4"); + Member member = new Member("kth990303@naver.com", encodedPassword, "케이"); + + Token token = new Token(member.getId(), refreshToken, expiredAccessToken, 0); + when(refreshTokenService.getMemberFromRefreshToken(refreshToken)).thenReturn(member); + when(refreshTokenService.findTokenByRefreshToken(refreshToken)).thenReturn(token); + + RefreshTokenRequest request = new RefreshTokenRequest(refreshToken); + ReissueTokenResponse response = authService.reissueAccessToken(request); + + Assertions.assertAll( + () -> assertNotNull(response), + () -> assertEquals(member.getReportCount(), response.getUserReportCount()) + ); + } + + @Test + @DisplayName("만료되지 않은 액세스 토큰을 가지고 재발급 요청 시 예외 발생") + void reissueAccessTokenFailsWhenNotExpired() { + String refreshToken = "valid-refresh-token"; + Date now = new Date(); + long futureValidityInMilliseconds = 3600000L; + String validAccessToken = Jwts.builder() + .setExpiration(new Date(now.getTime() + futureValidityInMilliseconds)) + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + String encodedPassword = passwordEncoder.encode("a1b2c3d4"); + Member member = new Member("kth990303@naver.com", encodedPassword, "케이"); + + Token token = new Token(member.getId(), refreshToken, validAccessToken, 999); + when(refreshTokenService.getMemberFromRefreshToken(refreshToken)).thenReturn(member); + when(refreshTokenService.findTokenByRefreshToken(refreshToken)).thenReturn(token); + + RefreshTokenRequest request = new RefreshTokenRequest(refreshToken); + + assertThrows(NotExpiredAccessTokenException.class, + () -> authService.reissueAccessToken(request)); + } } diff --git a/src/test/java/mocacong/server/service/RefreshTokenServiceTest.java b/src/test/java/mocacong/server/service/RefreshTokenServiceTest.java new file mode 100644 index 00000000..30b253c3 --- /dev/null +++ b/src/test/java/mocacong/server/service/RefreshTokenServiceTest.java @@ -0,0 +1,54 @@ +package mocacong.server.service; + +import mocacong.server.domain.Member; +import mocacong.server.domain.Token; +import mocacong.server.exception.unauthorized.InvalidRefreshTokenException; +import mocacong.server.repository.MemberRepository; +import mocacong.server.security.auth.JwtTokenProvider; +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 static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ServiceTest +class RefreshTokenServiceTest { + + @Autowired + private JwtTokenProvider jwtTokenProvider; + @Autowired + private MemberRepository memberRepository; + @Autowired + private RefreshTokenService refreshTokenService; + + @DisplayName("올바른 refresh token 을 가지고 회원 정보를 얻는다") + @Test + public void getMemberFromRefreshToken() { + String email = "dlawotn3@naver.com"; + Member member = memberRepository.save(new Member(email, "abcd1234", "메리")); + Long payload = 1L; + + String refreshToken = Token.createRefreshToken(); + String accessToken = jwtTokenProvider.createAccessToken(payload); + refreshTokenService.saveTokenInfo(member.getId(), refreshToken, accessToken); + Member findMember = refreshTokenService.getMemberFromRefreshToken(refreshToken); + + Assertions.assertAll( + () -> Assertions.assertNotNull(refreshToken), + () -> Assertions.assertTrue(refreshToken.length() > 0), + () -> assertThat(findMember.getId()).isEqualTo(payload) + ); + } + + @DisplayName("올바르지 않은 refresh token 을 가지고 검증하면 예외를 발생시킨다") + @Test + public void validateWrongRefreshToken() { + String refreshToken = "wrong-refresh-token"; + + assertThrows(InvalidRefreshTokenException.class, + () -> refreshTokenService.getMemberFromRefreshToken(refreshToken) + ); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 6d20ba56..ef94956c 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -25,7 +25,8 @@ spring: security.jwt.token: secret-key: testtesttesttesttesttesttesttesttesttest - expire-length: 864000 + access-key-expire-length: 864000 + refresh-key-expire-length: 1728000 cloud: aws: From 3e41d979e736c568e73ef8920cc72ef432f388d2 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Mon, 18 Dec 2023 06:51:35 +0900 Subject: [PATCH 14/16] =?UTF-8?q?fix:=20Token=20=EA=B0=9D=EC=B2=B4=20TTL?= =?UTF-8?q?=20=EC=8B=9C=EA=B0=84=20=EB=8B=A8=EC=9C=84=20=EC=9D=B4=EC=8A=88?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0=20(#146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/mocacong/server/service/RefreshTokenService.java | 2 +- src/main/resources/application.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/mocacong/server/service/RefreshTokenService.java b/src/main/java/mocacong/server/service/RefreshTokenService.java index d0c07a2c..7d740540 100644 --- a/src/main/java/mocacong/server/service/RefreshTokenService.java +++ b/src/main/java/mocacong/server/service/RefreshTokenService.java @@ -35,7 +35,7 @@ public void saveTokenInfo(Long memberId, String refreshToken, String accessToken .expiration(validityRefreshTokenInMilliseconds) // 리프레시 토큰 유효기간 .build(); - redisTemplate.opsForValue().set(refreshToken, token, validityRefreshTokenInMilliseconds, TimeUnit.SECONDS); + redisTemplate.opsForValue().set(refreshToken, token, validityRefreshTokenInMilliseconds, TimeUnit.MILLISECONDS); } public Member getMemberFromRefreshToken(String refreshToken) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c4ebb647..4011a626 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -35,7 +35,7 @@ spring: security.jwt.token: secret-key: testtesttesttesttesttesttesttesttesttest - access-key-expire-length: 10000 + access-key-expire-length: 864000 refresh-key-expire-length: 1728000 springdoc: From b4ed61197759c6a126cf52e36b775630328e96d2 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Tue, 26 Dec 2023 20:42:06 +0900 Subject: [PATCH 15/16] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=EB=B6=84=EC=84=9D=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?Jacoco=20=EB=B0=8F=20SonarCloud=20=EB=8F=84=EC=9E=85=20(#147)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add jacoco setting * chore: add sonar cloud setting * chore: change sonar cloud setting * chore: integrate gradle build and SonarCloud analysis * chore: fix sonar exclusions * chore: add steps for upload test coverage * fix: fix always() in ci scripts * fix: rollback code * style: delete unused setting * chore: add sonar exclusions property path * chore: change sonar cloud setting * chore: delete dto from test coverage excludes * chore: add dto in coverage --- .github/workflows/ci-backend.yml | 23 +++++++-- build.gradle | 88 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml index 1b715124..58d95590 100644 --- a/.github/workflows/ci-backend.yml +++ b/.github/workflows/ci-backend.yml @@ -11,7 +11,8 @@ on: - develop jobs: - build: + test: + name: Code Quality Check runs-on: ubuntu-latest steps: @@ -26,5 +27,21 @@ jobs: - name: Grant execute permisson for gradlew run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew clean build + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: check + cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} + + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: ./gradlew sonar --info --stacktrace diff --git a/build.gradle b/build.gradle index 214d4bee..d780421b 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,8 @@ plugins { id 'java' id 'org.springframework.boot' version '2.7.9' id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'jacoco' + id 'org.sonarqube' version '4.2.1.3168' } group = 'mocacong' @@ -45,6 +47,92 @@ dependencies { testImplementation 'it.ozimov:embedded-redis:0.7.2' } +jacoco { + toolVersion = '0.8.8' +} + test { useJUnitPlatform() + finalizedBy 'jacocoTestReport' +} + +def Qdomains = [] +for (qPattern in "**/QA" .. "**/QZ") { + Qdomains.add(qPattern + "*") +} + +sonar { + properties { + property 'sonar.host.url', 'https://sonarcloud.io' + property 'sonar.organization', 'mocacong' + property 'sonar.projectKey', 'mocacong_Mocacong-Backend' + property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/index.xml' + property 'sonar.sources', 'src' + property 'sonar.language', 'java' + property 'sonar.sourceEncoding', 'UTF-8' + property 'sonar.exclusions', '**/test/**, **/resources/**, **/*Application*.java, **/*Controller*.java ,**/config/**, **/dto/**' + + '**/exception/**, **/security/**, **/support/**, **/Q*.java' + property 'sonar.test.inclusions', '**/*Test.java' + property 'sonar.java.coveragePlugin', 'jacoco' + } +} + +jacocoTestReport { + dependsOn test + reports{ + html.required.set(true) + xml.required.set(true) + html.destination file("$buildDir/reports/jacoco/index.html") + xml.destination file("$buildDir/reports/jacoco/index.xml") + } + + afterEvaluate { + classDirectories.setFrom( + files(classDirectories.files.collect { + fileTree(dir: it, excludes: + [ + "**/*Application*", + "**/*Controller*", + "**/config/*", + "**/dto/*", + "**/exception/*", + "**/security/*", + "**/support/*" + ]+ Qdomains) + }) + ) + } + finalizedBy 'jacocoTestCoverageVerification' +} + +jacocoTestCoverageVerification { + violationRules { + rule { + failOnViolation = false + enabled = true + element = 'CLASS' + + limit { + counter = 'LINE' + value = 'COVEREDRATIO' + minimum = 0.70 + } + + limit { + counter = 'BRANCH' + value = 'COVEREDRATIO' + minimum = 0.70 + } + + excludes = [ + '**.*Application*', + '**.*Controller*', + '**.config.*', + '**.dto.*', + '**.exception.*', + '**.security.*', + '**.support.*' + ] + Qdomains + } + } } From d6708abf2dcc9ae9471d0f4b21dbed9a6829c2e1 Mon Sep 17 00:00:00 2001 From: Jisu Lim <69844138+Ji-soo708@users.noreply.github.com> Date: Fri, 29 Dec 2023 19:48:14 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20=EC=86=8C=EB=82=98=ED=81=B4?= =?UTF-8?q?=EB=9D=BC=EC=9A=B0=EB=93=9C=20=EC=BB=A4=EB=B2=84=EB=A6=AC?= =?UTF-8?q?=EC=A7=80=20=EB=8C=80=EC=83=81=20exclusions=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EC=88=98=EC=A0=95=20(#148)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: change exclusions * style: add blank * chore: add config.java to exclusions --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d780421b..0b06568b 100644 --- a/build.gradle +++ b/build.gradle @@ -70,8 +70,8 @@ sonar { property 'sonar.sources', 'src' property 'sonar.language', 'java' property 'sonar.sourceEncoding', 'UTF-8' - property 'sonar.exclusions', '**/test/**, **/resources/**, **/*Application*.java, **/*Controller*.java ,**/config/**, **/dto/**' + - '**/exception/**, **/security/**, **/support/**, **/Q*.java' + property 'sonar.exclusions', '**/test/**, **/resources/**, **/*Application*.java, **/*Controller*.java, **/*Config.java' + + '**/*Response.java, **/*Exception.java, **/security/**, **/support/**, **/Q*.java' property 'sonar.test.inclusions', '**/*Test.java' property 'sonar.java.coveragePlugin', 'jacoco' }