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..0b06568b 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.java' + + '**/*Response.java, **/*Exception.java, **/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 + } + } } 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/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/Cafe.java b/src/main/java/mocacong/server/domain/Cafe.java index 62bf598d..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) { @@ -107,4 +140,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/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 12fcf2b8..4a8c88ff 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; @@ -88,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)); @@ -110,6 +109,11 @@ public void addReport(Report report) { } public void updateIsMasked(boolean isMasked) { - this.isMasked= isMasked; + this.isMasked = isMasked; } + + public int getLikeCounts() { + return commentLikes.size(); + } + } 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/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/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/request/CafeRegisterRequest.java b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java index b74fcd39..f27f43fc 100644 --- a/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java +++ b/src/main/java/mocacong/server/dto/request/CafeRegisterRequest.java @@ -10,8 +10,12 @@ @ToString public class CafeRegisterRequest { @NotBlank(message = "1012:공백일 수 없습니다.") - private String id; + private String mapId; @NotBlank(message = "1012:공백일 수 없습니다.") private String name; + + private String roadAddress; + + private String phoneNumber; } 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/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 new file mode 100644 index 00000000..aeea3425 --- /dev/null +++ b/src/main/java/mocacong/server/dto/response/CafeImageReportResponse.java @@ -0,0 +1,14 @@ +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; +} 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/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/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/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 84aeabd4..17dab5ef 100644 --- a/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java +++ b/src/main/java/mocacong/server/dto/response/MyCommentCafeResponse.java @@ -1,6 +1,11 @@ package mocacong.server.dto.response; 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) @@ -11,5 +16,15 @@ public class MyCommentCafeResponse { private String mapId; private String name; private String studyType; - private String comment; + 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(), + cafe.findAverageScore(), 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..72d6b163 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 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) { + 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.myWifi = cafeDetail.getWifiValue(); + this.myParking = cafeDetail.getParkingValue(); + this.myToilet = cafeDetail.getToiletValue(); + this.myPower = cafeDetail.getPowerValue(); + this.mySound = cafeDetail.getSoundValue(); + this.myDesk = cafeDetail.getDeskValue(); } } 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/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/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/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/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/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/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/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/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/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/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..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; @@ -16,19 +20,20 @@ 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; +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; - private final KakaoOAuthUserProvider kakaoOAuthUserProvider; public TokenResponse login(AuthLoginRequest request) { Member findMember = memberRepository.findByEmailAndPlatform(request.getEmail(), Platform.MOCACONG) @@ -36,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) { @@ -53,12 +62,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() ); } @@ -69,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) { @@ -102,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/CafeService.java b/src/main/java/mocacong/server/service/CafeService.java index 0307474c..400f1bc0 100644 --- a/src/main/java/mocacong/server/service/CafeService.java +++ b/src/main/java/mocacong/server/service/CafeService.java @@ -53,13 +53,18 @@ public class CafeService { @Transactional public void save(CafeRegisterRequest request) { - Cafe cafe = new Cafe(request.getId(), request.getName()); + Cafe cafe = new Cafe(request.getMapId(), request.getName(), request.getRoadAddress(), request.getPhoneNumber()); try { - cafeRepository.save(cafe); + cafeRepository.findByMapId(request.getMapId()) + .ifPresentOrElse( + existedCafe -> existedCafe.updateCafeRoadAddress(request.getRoadAddress()), + () -> cafeRepository.save(cafe) + ); } catch (DataIntegrityViolationException e) { throw new DuplicateCafeException(); } + } @Transactional(readOnly = true) @@ -78,6 +83,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 +113,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(), @@ -119,10 +129,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()); @@ -149,7 +159,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); } @@ -169,17 +179,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") @@ -324,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) @@ -379,7 +408,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/CommentService.java b/src/main/java/mocacong/server/service/CommentService.java index 483f689a..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) @@ -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/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..7d740540 --- /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.MILLISECONDS); + } + + 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 4b1cd614..a283d761 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,24 +43,20 @@ 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(); } - return new CommentReportResponse(comment.getReportsCount(), reporter.getReportCount()); + return new CommentReportResponse(comment.getReportsCount()); } private void createCommentReport(Comment comment, Member reporter, String reportReason) { @@ -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()); + } + + 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/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 52b54474..e9a6cf9e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -12,8 +12,8 @@ spring: hibernate: create_empty_composites: enabled: true - show_sql: true format_sql: true + default_batch_fetch_size: 100 servlet: multipart: max-file-size: 10MB @@ -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 @@ -69,10 +70,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 4a534933..4011a626 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,8 +10,8 @@ spring: hibernate: create_empty_composites: enabled: true - show_sql: true format_sql: true + default_batch_fetch_size: 100 task: execution: pool: @@ -35,7 +35,8 @@ spring: security.jwt.token: secret-key: testtesttesttesttesttesttesttesttesttest - expire-length: 864000 + access-key-expire-length: 864000 + refresh-key-expire-length: 1728000 springdoc: default-consumes-media-type: application/json @@ -73,10 +74,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/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 @@ + + + + + + + + + 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 4a865997..6a3252f2 100644 --- a/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/mocacong/server/acceptance/AuthAcceptanceTest.java @@ -9,17 +9,17 @@ 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; 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.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -27,8 +27,6 @@ public class AuthAcceptanceTest extends AcceptanceTest { @MockBean private AppleOAuthUserProvider appleOAuthUserProvider; - @MockBean - private KakaoOAuthUserProvider kakaoOAuthUserProvider; @Test @DisplayName("회원이 정상적으로 로그인한다") @@ -51,8 +49,11 @@ void login() { .extract() .as(TokenResponse.class); - assertNotNull(tokenResponse); - assertNotNull(tokenResponse.getToken()); + assertAll( + () -> assertNotNull(tokenResponse), + () -> assertNotNull(tokenResponse.getAccessToken()), + () -> assertNotNull(tokenResponse.getRefreshToken()) + ); } @Test @@ -80,8 +81,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/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 e9b38695..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 { @@ -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", "메리"); @@ -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).getCommentContents()).containsExactlyInAnyOrder("댓글3") + ); } @Test 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 f0b6029a..7dee8f96 100644 --- a/src/test/java/mocacong/server/domain/CafeTest.java +++ b/src/test/java/mocacong/server/domain/CafeTest.java @@ -8,10 +8,20 @@ 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() { - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); double actual = cafe.findAverageScore(); @@ -22,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); @@ -36,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); @@ -66,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); @@ -101,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); @@ -121,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); @@ -136,7 +146,7 @@ void updateCafeDetailsWhenSomeTypesNoReviews() { @Test @DisplayName("카페에 리뷰가 하나도 없을 경우 모든 세부정보 타입에 null을 반환한다") void updateCafeDetailsWhenNoReviews() { - Cafe cafe = new Cafe("1", "케이카페"); + Cafe cafe = new Cafe("1", "케이카페", "100"); cafe.updateCafeDetails(); @@ -155,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); @@ -167,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/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/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/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/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 6dce840f..339f85a9 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; @@ -48,6 +46,8 @@ class CafeServiceTest { private FavoriteRepository favoriteRepository; @Autowired private CafeImageRepository cafeImageRepository; + @Autowired + private CommentLikeRepository commentLikeRepository; @MockBean private AwsS3Uploader awsS3Uploader; @@ -55,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); @@ -64,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 @@ -83,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()); @@ -108,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); @@ -136,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", "느려요", "없어요", @@ -167,12 +166,57 @@ 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", "케이카페", "200"); + 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() { 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()); @@ -192,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)); @@ -215,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", "느려요", "없어요", @@ -240,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); @@ -266,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", "느려요", "없어요", @@ -297,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", "느려요", "없어요", @@ -340,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); @@ -370,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", "느려요", "없어요", @@ -396,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(), @@ -416,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) ); } @@ -431,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")); @@ -440,14 +484,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).getCommentContents()).containsExactlyInAnyOrder("댓글1", "댓글2"), + () -> assertThat(actual.getCafes().get(1).getCommentContents()).containsExactlyInAnyOrder("댓글3") ); } @@ -458,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", "빵빵해요", "여유로워요", @@ -489,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(), @@ -509,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", "빵빵해요", "여유로워요", @@ -537,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()); @@ -561,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", "빵빵해요", "여유로워요", @@ -578,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", "빵빵해요", "여유로워요", @@ -606,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", "빵빵해요", "여유로워요", @@ -634,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(), @@ -650,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); @@ -690,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", "빵빵해요", "여유로워요", @@ -707,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); @@ -732,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())); @@ -746,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); @@ -770,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); @@ -791,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); @@ -817,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); @@ -846,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); @@ -869,11 +913,10 @@ 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); @@ -910,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); @@ -936,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); @@ -962,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); @@ -992,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); @@ -1028,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); @@ -1065,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); @@ -1100,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); @@ -1125,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); @@ -1168,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", @@ -1191,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); @@ -1210,4 +1253,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/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/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/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/java/mocacong/server/service/ReportConcurrentServiceTest.java b/src/test/java/mocacong/server/service/ReportConcurrentServiceTest.java index 21a7c152..23b0b528 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 { @@ -41,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); @@ -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..1810603a 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"; @@ -53,13 +64,17 @@ 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, "이 카페 완전 돈 아깝;;"); 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 @@ -69,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, "굳이 이런데 가야하나 ㅋ"); @@ -85,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, "이 카페 완전 돈 아깝;;"); @@ -104,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, "아~ 소설보고 싶다"); @@ -116,7 +131,7 @@ void reportDuplicateComment() { } @Test - @DisplayName("5번 이상 신고된 댓글은 마스킹되며 해당 작성자의 신고 횟수가 1씩 증가한다") + @DisplayName("5번 이상 신고된 댓글은 마스킹된다") void maskCauseReport5timesReportedComment() { String mapId = "2143154352323"; List members = new ArrayList<>(); @@ -126,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, "아~ 소설보고 싶다"); @@ -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() ); } @@ -192,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); @@ -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) + ); + } } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index abc2be88..ef94956c 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 @@ -24,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: @@ -56,7 +58,3 @@ oauth: iss: https://appleid.apple.com client-id: test nonce: nonce - kakao: - client-id: test - client-secret: test - redirect-uri: test