diff --git a/backend/src/main/java/reviewme/global/GlobalExceptionHandler.java b/backend/src/main/java/reviewme/global/GlobalExceptionHandler.java index 608bdd3e4..e6fba936a 100644 --- a/backend/src/main/java/reviewme/global/GlobalExceptionHandler.java +++ b/backend/src/main/java/reviewme/global/GlobalExceptionHandler.java @@ -24,6 +24,7 @@ import reviewme.global.exception.DataInconsistencyException; import reviewme.global.exception.FieldErrorResponse; import reviewme.global.exception.NotFoundException; +import reviewme.global.exception.UnauthorizedException; import reviewme.global.exception.UnexpectedRequestException; @Slf4j @@ -45,6 +46,11 @@ public ProblemDetail handleUnexpectedRequestException(UnexpectedRequestException return ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getErrorMessage()); } + @ExceptionHandler(UnauthorizedException.class) + public ProblemDetail handleUnauthorizedException(UnauthorizedException ex) { + return ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, ex.getErrorMessage()); + } + @ExceptionHandler(DataInconsistencyException.class) public ProblemDetail handleDataConsistencyException(DataInconsistencyException ex) { return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, ex.getErrorMessage()); diff --git a/backend/src/main/java/reviewme/global/exception/UnauthorizedException.java b/backend/src/main/java/reviewme/global/exception/UnauthorizedException.java new file mode 100644 index 000000000..150fc998b --- /dev/null +++ b/backend/src/main/java/reviewme/global/exception/UnauthorizedException.java @@ -0,0 +1,8 @@ +package reviewme.global.exception; + +public abstract class UnauthorizedException extends ReviewMeException { + + protected UnauthorizedException(String errorMessage) { + super(errorMessage); + } +} diff --git a/backend/src/main/java/reviewme/review/repository/ReviewRepository.java b/backend/src/main/java/reviewme/review/repository/ReviewRepository.java index 6f1686f87..1ab6a1cf3 100644 --- a/backend/src/main/java/reviewme/review/repository/ReviewRepository.java +++ b/backend/src/main/java/reviewme/review/repository/ReviewRepository.java @@ -14,14 +14,4 @@ public interface ReviewRepository extends JpaRepository { List findReceivedReviewsByGroupId(long reviewGroupId); Optional findByIdAndReviewGroupId(long reviewId, long reviewGroupId); - - @Query(value = """ - SELECT r.* FROM review r - INNER JOIN review_group rg - ON rg.id = r.review_group_id - WHERE r.id = :reviewId - AND rg.review_request_code = :reviewRequestCode - AND rg.group_access_code = :groupAccessCode - """, nativeQuery = true) - Optional findByIdAndCodes(long reviewId, String reviewRequestCode, String groupAccessCode); } diff --git a/backend/src/main/java/reviewme/review/service/ReviewDetailLookupService.java b/backend/src/main/java/reviewme/review/service/ReviewDetailLookupService.java index 53d6b70cf..b12f1508d 100644 --- a/backend/src/main/java/reviewme/review/service/ReviewDetailLookupService.java +++ b/backend/src/main/java/reviewme/review/service/ReviewDetailLookupService.java @@ -15,14 +15,15 @@ import reviewme.review.domain.Review; import reviewme.review.domain.TextAnswer; import reviewme.review.domain.TextAnswers; +import reviewme.review.domain.exception.ReviewGroupNotFoundByReviewRequestCodeException; import reviewme.review.repository.ReviewRepository; import reviewme.review.service.dto.response.detail.OptionGroupAnswerResponse; import reviewme.review.service.dto.response.detail.OptionItemAnswerResponse; import reviewme.review.service.dto.response.detail.QuestionAnswerResponse; import reviewme.review.service.dto.response.detail.SectionAnswerResponse; import reviewme.review.service.dto.response.detail.TemplateAnswerResponse; -import reviewme.review.service.exception.ReviewGroupNotFoundByReviewException; -import reviewme.review.service.exception.ReviewNotFoundByIdAndCodesException; +import reviewme.review.service.exception.ReviewGroupUnauthorizedException; +import reviewme.review.service.exception.ReviewNotFoundByIdAndGroupException; import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.reviewgroup.repository.ReviewGroupRepository; import reviewme.template.domain.Section; @@ -42,11 +43,13 @@ public class ReviewDetailLookupService { private final OptionGroupRepository optionGroupRepository; public TemplateAnswerResponse getReviewDetail(long reviewId, String reviewRequestCode, String groupAccessCode) { - Review review = reviewRepository.findByIdAndCodes(reviewId, reviewRequestCode, groupAccessCode) - .orElseThrow(() -> new ReviewNotFoundByIdAndCodesException(reviewId, reviewRequestCode, groupAccessCode)); - - ReviewGroup reviewGroup = reviewGroupRepository.findById(review.getReviewGroupId()) - .orElseThrow(() -> new ReviewGroupNotFoundByReviewException(review.getId(), review.getReviewGroupId())); + ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(reviewRequestCode) + .orElseThrow(() -> new ReviewGroupNotFoundByReviewRequestCodeException(reviewRequestCode)); + if (!reviewGroup.matchesGroupAccessCode(groupAccessCode)) { + throw new ReviewGroupUnauthorizedException(reviewGroup.getId()); + } + Review review = reviewRepository.findByIdAndReviewGroupId(reviewId, reviewGroup.getId()) + .orElseThrow(() -> new ReviewNotFoundByIdAndGroupException(reviewId, reviewGroup.getId())); long templateId = review.getTemplateId(); diff --git a/backend/src/main/java/reviewme/review/service/ReviewPreviewGenerator.java b/backend/src/main/java/reviewme/review/service/ReviewPreviewGenerator.java index e45c52fd1..d0d49781d 100644 --- a/backend/src/main/java/reviewme/review/service/ReviewPreviewGenerator.java +++ b/backend/src/main/java/reviewme/review/service/ReviewPreviewGenerator.java @@ -7,7 +7,7 @@ public class ReviewPreviewGenerator { private static final int PREVIEW_LENGTH = 150; - public String generatePreview2(List reviewTextAnswers) { + public String generatePreview(List reviewTextAnswers) { if (reviewTextAnswers.isEmpty()) { return ""; } diff --git a/backend/src/main/java/reviewme/review/service/ReviewService.java b/backend/src/main/java/reviewme/review/service/ReviewService.java index 45a705afc..1ac3d0b3b 100644 --- a/backend/src/main/java/reviewme/review/service/ReviewService.java +++ b/backend/src/main/java/reviewme/review/service/ReviewService.java @@ -8,11 +8,12 @@ import reviewme.question.domain.OptionType; import reviewme.question.repository.OptionItemRepository; import reviewme.review.domain.Review; +import reviewme.review.domain.exception.ReviewGroupNotFoundByReviewRequestCodeException; import reviewme.review.repository.ReviewRepository; import reviewme.review.service.dto.response.list.ReceivedReviewCategoryResponse; import reviewme.review.service.dto.response.list.ReceivedReviewResponse; import reviewme.review.service.dto.response.list.ReceivedReviewsResponse; -import reviewme.review.service.exception.ReviewGroupNotFoundByCodesException; +import reviewme.review.service.exception.ReviewGroupUnauthorizedException; import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.reviewgroup.repository.ReviewGroupRepository; @@ -28,9 +29,12 @@ public class ReviewService { @Transactional(readOnly = true) public ReceivedReviewsResponse findReceivedReviews(String reviewRequestCode, String groupAccessCode) { - ReviewGroup reviewGroup = reviewGroupRepository - .findByReviewRequestCodeAndGroupAccessCode_Code(reviewRequestCode, groupAccessCode) - .orElseThrow(() -> new ReviewGroupNotFoundByCodesException(reviewRequestCode, groupAccessCode)); + ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(reviewRequestCode) + .orElseThrow(() -> new ReviewGroupNotFoundByReviewRequestCodeException(reviewRequestCode)); + + if (!reviewGroup.matchesGroupAccessCode(groupAccessCode)) { + throw new ReviewGroupUnauthorizedException(reviewGroup.getId()); + } List reviewResponses = reviewRepository.findReceivedReviewsByGroupId(reviewGroup.getId()) @@ -52,7 +56,7 @@ private ReceivedReviewResponse createReceivedReviewResponse(Review review) { return new ReceivedReviewResponse( review.getId(), review.getCreatedAt().toLocalDate(), - reviewPreviewGenerator.generatePreview2(review.getTextAnswers()), + reviewPreviewGenerator.generatePreview(review.getTextAnswers()), categoryResponses ); } diff --git a/backend/src/main/java/reviewme/review/service/exception/ReviewGroupNotFoundByReviewException.java b/backend/src/main/java/reviewme/review/service/exception/ReviewGroupNotFoundByReviewException.java deleted file mode 100644 index 92cb867d2..000000000 --- a/backend/src/main/java/reviewme/review/service/exception/ReviewGroupNotFoundByReviewException.java +++ /dev/null @@ -1,13 +0,0 @@ -package reviewme.review.service.exception; - -import lombok.extern.slf4j.Slf4j; -import reviewme.global.exception.DataInconsistencyException; - -@Slf4j -public class ReviewGroupNotFoundByReviewException extends DataInconsistencyException { - - public ReviewGroupNotFoundByReviewException(long reviewId, long reviewGroupId) { - super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); - log.error("ReviewGroup not found from review - reviewId: {}, reviewGroupId: {}", reviewId, reviewGroupId); - } -} diff --git a/backend/src/main/java/reviewme/review/service/exception/ReviewGroupUnauthorizedException.java b/backend/src/main/java/reviewme/review/service/exception/ReviewGroupUnauthorizedException.java new file mode 100644 index 000000000..e18bd7e34 --- /dev/null +++ b/backend/src/main/java/reviewme/review/service/exception/ReviewGroupUnauthorizedException.java @@ -0,0 +1,13 @@ +package reviewme.review.service.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.UnauthorizedException; + +@Slf4j +public class ReviewGroupUnauthorizedException extends UnauthorizedException { + + public ReviewGroupUnauthorizedException(long reviewGroupId) { + super("리뷰를 확인할 권한이 없어요."); + log.info("Group access code mismatch on review group: {}", reviewGroupId); + } +} diff --git a/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundByIdAndCodesException.java b/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundByIdAndCodesException.java deleted file mode 100644 index b7c18b2cb..000000000 --- a/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundByIdAndCodesException.java +++ /dev/null @@ -1,14 +0,0 @@ -package reviewme.review.service.exception; - -import lombok.extern.slf4j.Slf4j; -import reviewme.global.exception.NotFoundException; - -@Slf4j -public class ReviewNotFoundByIdAndCodesException extends NotFoundException { - - public ReviewNotFoundByIdAndCodesException(long reviewId, String reviewRequestCode, String groupAccessCode) { - super("리뷰를 찾을 수 없어요"); - log.info("Review not found - reviewId: {}, reviewRequestCode: {}, groupAccessCode: {}", - reviewId, reviewRequestCode, groupAccessCode); - } -} diff --git a/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundByIdAndGroupException.java b/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundByIdAndGroupException.java new file mode 100644 index 000000000..11cbc93e5 --- /dev/null +++ b/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundByIdAndGroupException.java @@ -0,0 +1,13 @@ +package reviewme.review.service.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.NotFoundException; + +@Slf4j +public class ReviewNotFoundByIdAndGroupException extends NotFoundException { + + public ReviewNotFoundByIdAndGroupException(long reviewId, long reviewGroupId) { + super("리뷰를 찾을 수 없어요"); + log.info("Review not found from group - reviewGroupId: {}, reviewId: {}", reviewGroupId, reviewId); + } +} diff --git a/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundException.java b/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundException.java new file mode 100644 index 000000000..ed4d79c00 --- /dev/null +++ b/backend/src/main/java/reviewme/review/service/exception/ReviewNotFoundException.java @@ -0,0 +1,13 @@ +package reviewme.review.service.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.NotFoundException; + +@Slf4j +public class ReviewNotFoundException extends NotFoundException { + + public ReviewNotFoundException(String reviewRequestCode, long reviewId) { + super("리뷰가 존재하지 않아요."); + log.info("Review not found: reviewRequestCode: {}, reviewId: {}", reviewRequestCode, reviewId); + } +} diff --git a/backend/src/main/java/reviewme/reviewgroup/domain/GroupAccessCode.java b/backend/src/main/java/reviewme/reviewgroup/domain/GroupAccessCode.java index c9e2d250c..25764e63d 100644 --- a/backend/src/main/java/reviewme/reviewgroup/domain/GroupAccessCode.java +++ b/backend/src/main/java/reviewme/reviewgroup/domain/GroupAccessCode.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import reviewme.reviewgroup.domain.exception.InvalidGroupAccessCodeFormatException; +import reviewme.util.Encoder; @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -21,7 +22,7 @@ public class GroupAccessCode { public GroupAccessCode(String code) { validateGroupAccessCode(code); - this.code = code; + this.code = Encoder.encode(code); } private void validateGroupAccessCode(String groupAccessCode) { @@ -30,5 +31,8 @@ private void validateGroupAccessCode(String groupAccessCode) { throw new InvalidGroupAccessCodeFormatException(groupAccessCode); } } -} + public boolean matches(String groupAccessCode) { + return code.equals(Encoder.encode(groupAccessCode)); + } +} diff --git a/backend/src/main/java/reviewme/reviewgroup/domain/ReviewGroup.java b/backend/src/main/java/reviewme/reviewgroup/domain/ReviewGroup.java index 2e9b68d1c..9da094186 100644 --- a/backend/src/main/java/reviewme/reviewgroup/domain/ReviewGroup.java +++ b/backend/src/main/java/reviewme/reviewgroup/domain/ReviewGroup.java @@ -66,6 +66,10 @@ private void validateProjectNameLength(String projectName) { } } + public boolean matchesGroupAccessCode(String code) { + return groupAccessCode.matches(code); + } + public String getGroupAccessCode() { return groupAccessCode.getCode(); } diff --git a/backend/src/main/java/reviewme/reviewgroup/repository/ReviewGroupRepository.java b/backend/src/main/java/reviewme/reviewgroup/repository/ReviewGroupRepository.java index 38ddbdfaa..37cce51d8 100644 --- a/backend/src/main/java/reviewme/reviewgroup/repository/ReviewGroupRepository.java +++ b/backend/src/main/java/reviewme/reviewgroup/repository/ReviewGroupRepository.java @@ -10,9 +10,5 @@ public interface ReviewGroupRepository extends JpaRepository Optional findByReviewRequestCode(String reviewRequestCode); - Optional findByReviewRequestCodeAndGroupAccessCode_Code(String reviewRequestCode, String groupAccessCode); - boolean existsByReviewRequestCode(String reviewRequestCode); - - boolean existsByReviewRequestCodeAndGroupAccessCode_Code(String reviewRequestCode, String groupAccessCode); } diff --git a/backend/src/main/java/reviewme/reviewgroup/service/ReviewGroupService.java b/backend/src/main/java/reviewme/reviewgroup/service/ReviewGroupService.java index 22a8d5fad..abc0b1cdf 100644 --- a/backend/src/main/java/reviewme/reviewgroup/service/ReviewGroupService.java +++ b/backend/src/main/java/reviewme/reviewgroup/service/ReviewGroupService.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import reviewme.review.domain.exception.ReviewGroupNotFoundByReviewRequestCodeException; import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.reviewgroup.repository.ReviewGroupRepository; import reviewme.reviewgroup.service.dto.CheckValidAccessRequest; @@ -26,7 +27,6 @@ public ReviewGroupCreationResponse createReviewGroup(ReviewGroupCreationRequest reviewRequestCode = randomCodeGenerator.generate(REVIEW_REQUEST_CODE_LENGTH); } while (reviewGroupRepository.existsByReviewRequestCode(reviewRequestCode)); - ReviewGroup reviewGroup = reviewGroupRepository.save( new ReviewGroup(request.revieweeName(), request.projectName(), reviewRequestCode, request.groupAccessCode()) ); @@ -35,9 +35,10 @@ public ReviewGroupCreationResponse createReviewGroup(ReviewGroupCreationRequest @Transactional(readOnly = true) public CheckValidAccessResponse checkGroupAccessCode(CheckValidAccessRequest request) { - boolean hasAccess = reviewGroupRepository.existsByReviewRequestCodeAndGroupAccessCode_Code( - request.reviewRequestCode(), request.groupAccessCode() - ); + ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(request.reviewRequestCode()) + .orElseThrow(() -> new ReviewGroupNotFoundByReviewRequestCodeException(request.reviewRequestCode())); + + boolean hasAccess = reviewGroup.matchesGroupAccessCode(request.groupAccessCode()); return new CheckValidAccessResponse(hasAccess); } } diff --git a/backend/src/main/java/reviewme/util/Encoder.java b/backend/src/main/java/reviewme/util/Encoder.java new file mode 100644 index 000000000..4b096d196 --- /dev/null +++ b/backend/src/main/java/reviewme/util/Encoder.java @@ -0,0 +1,32 @@ +package reviewme.util; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Encoder { + + private static final String SHA_256 = "SHA-256"; + + private Encoder() { + } + + public static String encode(String code) { + try { + MessageDigest messageDigest = MessageDigest.getInstance(SHA_256); + byte[] digest = messageDigest.digest(code.getBytes(UTF_8)); + return formatHexadecimal(digest); + } catch (NoSuchAlgorithmException e) { + throw new EncoderAlgorithmInitializationException(SHA_256); + } + } + + private static String formatHexadecimal(byte[] bytes) { + StringBuilder builder = new StringBuilder(); + for (byte b : bytes) { + builder.append("%02x".formatted(b)); + } + return builder.toString(); + } +} diff --git a/backend/src/main/java/reviewme/util/EncoderAlgorithmInitializationException.java b/backend/src/main/java/reviewme/util/EncoderAlgorithmInitializationException.java new file mode 100644 index 000000000..2155c6618 --- /dev/null +++ b/backend/src/main/java/reviewme/util/EncoderAlgorithmInitializationException.java @@ -0,0 +1,13 @@ +package reviewme.util; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.ReviewMeException; + +@Slf4j +public class EncoderAlgorithmInitializationException extends ReviewMeException { + + public EncoderAlgorithmInitializationException(String algorithm) { + super("서버 내부에 문제가 발생했습니다. 잠시 후 다시 시도해주세요."); + log.error("Failed to initialize encoder: Algorithm not found: {}", algorithm, this); + } +} diff --git a/backend/src/test/java/reviewme/review/service/ReviewDetailLookupServiceTest.java b/backend/src/test/java/reviewme/review/service/ReviewDetailLookupServiceTest.java index a367745ea..c46411b21 100644 --- a/backend/src/test/java/reviewme/review/service/ReviewDetailLookupServiceTest.java +++ b/backend/src/test/java/reviewme/review/service/ReviewDetailLookupServiceTest.java @@ -19,11 +19,13 @@ import reviewme.review.domain.CheckboxAnswer; import reviewme.review.domain.Review; import reviewme.review.domain.TextAnswer; +import reviewme.review.domain.exception.ReviewGroupNotFoundByReviewRequestCodeException; import reviewme.review.repository.ReviewRepository; import reviewme.review.service.dto.response.detail.QuestionAnswerResponse; import reviewme.review.service.dto.response.detail.SectionAnswerResponse; import reviewme.review.service.dto.response.detail.TemplateAnswerResponse; -import reviewme.review.service.exception.ReviewNotFoundByIdAndCodesException; +import reviewme.review.service.exception.ReviewGroupUnauthorizedException; +import reviewme.review.service.exception.ReviewNotFoundByIdAndGroupException; import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.reviewgroup.repository.ReviewGroupRepository; import reviewme.support.ServiceTest; @@ -61,7 +63,7 @@ class ReviewDetailLookupServiceTest { private TemplateRepository templateRepository; @Test - void 잘못된_리뷰_확인_코드로_리뷰를_조회할_경우_예외를_발생한다() { + void 잘못된_리뷰_요청_코드로_리뷰를_조회할_경우_예외를_발생한다() { // given String reviewRequestCode = "reviewRequestCode"; String groupAccessCode = "groupAccessCode"; @@ -73,7 +75,7 @@ class ReviewDetailLookupServiceTest { // when, then assertThatThrownBy(() -> reviewDetailLookupService.getReviewDetail( review.getId(), "wrong" + reviewRequestCode, groupAccessCode - )).isInstanceOf(ReviewNotFoundByIdAndCodesException.class); + )).isInstanceOf(ReviewGroupNotFoundByReviewRequestCodeException.class); } @Test @@ -89,30 +91,34 @@ class ReviewDetailLookupServiceTest { // when, then assertThatThrownBy(() -> reviewDetailLookupService.getReviewDetail( review.getId(), reviewRequestCode, "wrong" + groupAccessCode - )).isInstanceOf(ReviewNotFoundByIdAndCodesException.class); + )).isInstanceOf(ReviewGroupUnauthorizedException.class); } @Test void 리뷰_그룹에_해당하지_않는_리뷰를_조회할_경우_예외를_발생한다() { // given + String reviewRequestCode1 = "reviewRequestCode1"; + String groupAccessCode1 = "groupAccessCode1"; ReviewGroup reviewGroup1 = reviewGroupRepository.save( - new ReviewGroup("테드", "리뷰미 프로젝트", "reviewRequestCode1", "groupAccessCode1")); + new ReviewGroup("테드", "리뷰미 프로젝트", reviewRequestCode1, groupAccessCode1)); ReviewGroup reviewGroup2 = reviewGroupRepository.save( - new ReviewGroup("테드", "리뷰미 프로젝트", "reviewRequestCode2", "groupAccessCode2")); + new ReviewGroup("테드", "리뷰미 프로젝트", "ABCD", "1234")); Review review1 = reviewRepository.save(new Review(0, reviewGroup1.getId(), List.of(), List.of())); - Review review = reviewRepository.save(new Review(0, reviewGroup2.getId(), List.of(), List.of())); + Review review2 = reviewRepository.save(new Review(0, reviewGroup2.getId(), List.of(), List.of())); // when, then assertThatThrownBy(() -> reviewDetailLookupService.getReviewDetail( - review.getId(), reviewGroup1.getReviewRequestCode(), reviewGroup1.getGroupAccessCode())) - .isInstanceOf(ReviewNotFoundByIdAndCodesException.class); + review2.getId(), reviewRequestCode1, groupAccessCode1)) + .isInstanceOf(ReviewNotFoundByIdAndGroupException.class); } @Test void 사용자가_작성한_리뷰를_확인한다() { // given - ReviewGroup reviewGroup = reviewGroupRepository.save(new ReviewGroup("aru", "reviewme", "ABCD", "0000")); + String reviewRequestCode = "ABCD"; + String groupAccessCode = "0000"; + ReviewGroup reviewGroup = reviewGroupRepository.save(new ReviewGroup("aru", "reviewme", reviewRequestCode, groupAccessCode)); Question question1 = questionRepository.save(new Question(true, QuestionType.TEXT, "질문", null, 1)); Question question2 = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "질문", null, 1)); Question question3 = questionRepository.save(new Question(true, QuestionType.TEXT, "체크 1 조건", "가이드라인", 1)); @@ -143,7 +149,7 @@ class ReviewDetailLookupServiceTest { // when TemplateAnswerResponse reviewDetail = reviewDetailLookupService.getReviewDetail( - review.getId(), reviewGroup.getReviewRequestCode(), reviewGroup.getGroupAccessCode() + review.getId(), reviewRequestCode, groupAccessCode ); // then @@ -153,7 +159,9 @@ class ReviewDetailLookupServiceTest { @Test void 답변이_있는_리뷰만_보여준다() { // given - ReviewGroup reviewGroup = reviewGroupRepository.save(new ReviewGroup("aru", "reviewme", "ABCD", "0000")); + String reviewRequestCode = "ABCD"; + String groupAccessCode = "0000"; + ReviewGroup reviewGroup = reviewGroupRepository.save(new ReviewGroup("aru", "reviewme", reviewRequestCode, groupAccessCode)); Question question1 = questionRepository.save(new Question(true, QuestionType.TEXT, "질문", null, 1)); Question question2 = questionRepository.save(new Question(false, QuestionType.CHECKBOX, "질문", null, 1)); Question question3 = questionRepository.save(new Question(true, QuestionType.TEXT, "체크 1 조건", "가이드라인", 1)); @@ -188,7 +196,7 @@ class ReviewDetailLookupServiceTest { // when TemplateAnswerResponse reviewDetail = reviewDetailLookupService.getReviewDetail( - review.getId(), reviewGroup.getReviewRequestCode(), reviewGroup.getGroupAccessCode() + review.getId(), reviewRequestCode, groupAccessCode ); // then diff --git a/backend/src/test/java/reviewme/review/service/ReviewPreviewGeneratorTest.java b/backend/src/test/java/reviewme/review/service/ReviewPreviewGeneratorTest.java index 9ec649852..f63c47b5f 100644 --- a/backend/src/test/java/reviewme/review/service/ReviewPreviewGeneratorTest.java +++ b/backend/src/test/java/reviewme/review/service/ReviewPreviewGeneratorTest.java @@ -18,7 +18,7 @@ class ReviewPreviewGeneratorTest { TextAnswer textAnswer = new TextAnswer(1, answer); // when - String actual = reviewPreviewGenerator.generatePreview2(List.of(textAnswer)); + String actual = reviewPreviewGenerator.generatePreview(List.of(textAnswer)); // then assertThat(actual).hasSize(150); @@ -33,7 +33,7 @@ class ReviewPreviewGeneratorTest { TextAnswer textAnswer = new TextAnswer(1, answer); // when - String actual = reviewPreviewGenerator.generatePreview2(List.of(textAnswer)); + String actual = reviewPreviewGenerator.generatePreview(List.of(textAnswer)); // then assertThat(actual).hasSize(length); diff --git a/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java b/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java index efc1ccb38..f5acf1634 100644 --- a/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java +++ b/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java @@ -16,10 +16,11 @@ import reviewme.question.repository.QuestionRepository; import reviewme.review.domain.CheckboxAnswer; import reviewme.review.domain.Review; +import reviewme.review.domain.exception.ReviewGroupNotFoundByReviewRequestCodeException; import reviewme.review.repository.CheckboxAnswerRepository; import reviewme.review.repository.ReviewRepository; import reviewme.review.service.dto.response.list.ReceivedReviewsResponse; -import reviewme.review.service.exception.ReviewGroupNotFoundByCodesException; +import reviewme.review.service.exception.ReviewGroupUnauthorizedException; import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.reviewgroup.repository.ReviewGroupRepository; import reviewme.support.ServiceTest; @@ -58,9 +59,21 @@ class ReviewServiceTest { ReviewRepository reviewRepository; @Test - void 리뷰_요청_코드와_확인_코드에_해당하는_그룹이_없는_경우_예외가_발생한다() { + void 리뷰_요청_코드가_존재하지_않는_경우_예외가_발생한다() { assertThatThrownBy(() -> reviewService.findReceivedReviews("abc", "groupAccessCode")) - .isInstanceOf(ReviewGroupNotFoundByCodesException.class); + .isInstanceOf(ReviewGroupNotFoundByReviewRequestCodeException.class); + } + + @Test + void 그룹_액세스_코드가_일치하지_않는_경우_예외가_발생한다() { + // given + String reviewRequestCode = "code"; + String groupAccessCode = "1234"; + reviewGroupRepository.save(new ReviewGroup("커비", "리뷰미", reviewRequestCode, groupAccessCode)); + + // when, then + assertThatThrownBy(() -> reviewService.findReceivedReviews(reviewRequestCode, "5678")) + .isInstanceOf(ReviewGroupUnauthorizedException.class); } @Test diff --git a/backend/src/test/java/reviewme/reviewgroup/domain/GroupAccessCodeTest.java b/backend/src/test/java/reviewme/reviewgroup/domain/GroupAccessCodeTest.java new file mode 100644 index 000000000..fc5b8ec51 --- /dev/null +++ b/backend/src/test/java/reviewme/reviewgroup/domain/GroupAccessCodeTest.java @@ -0,0 +1,35 @@ +package reviewme.reviewgroup.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import reviewme.reviewgroup.domain.exception.InvalidGroupAccessCodeFormatException; + +class GroupAccessCodeTest { + + @Test + void 코드_일치_여부를_판단한다() { + // given + String code = "hello"; + GroupAccessCode groupAccessCode = new GroupAccessCode(code); + + // when, then + assertThat(groupAccessCode.matches("hello")).isTrue(); + } + + @ParameterizedTest + @ValueSource(strings = {"AZaz", "a0Z9", "aZ09", "ABCD123a", "1234"}) + void 정규식에_일치하면_성공적으로_생성된다(String code) { + assertDoesNotThrow(() -> new GroupAccessCode(code)); + } + + @ParameterizedTest + @ValueSource(strings = {"", "123", "123456789012345678901", "aaaa-"}) + void 정규식에_일치하지_않으면_예외가_발생한다(String code) { + assertThrows(InvalidGroupAccessCodeFormatException.class, () -> new GroupAccessCode(code)); + } +} diff --git a/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java b/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java index 3110235d8..d7f693de8 100644 --- a/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java +++ b/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java @@ -54,16 +54,12 @@ class ReviewGroupServiceTest { @Test void 리뷰_요청_코드와_리뷰_확인_코드가_일치하는지_확인한다() { // given - ReviewGroup reviewGroup = reviewGroupRepository.save( - new ReviewGroup("reviewee", "project", "reviewRequestCode", "groupAccessCode") - ); + String reviewRequestCode = "reviewRequestCode"; + String groupAccessCode = "groupAccessCode"; + reviewGroupRepository.save(new ReviewGroup("reviewee", "project", reviewRequestCode, groupAccessCode)); - CheckValidAccessRequest request = new CheckValidAccessRequest( - reviewGroup.getReviewRequestCode(), reviewGroup.getGroupAccessCode() - ); - CheckValidAccessRequest wrongRequest = new CheckValidAccessRequest( - reviewGroup.getReviewRequestCode(), "wrong" + reviewGroup.getGroupAccessCode() - ); + CheckValidAccessRequest request = new CheckValidAccessRequest(reviewRequestCode, groupAccessCode); + CheckValidAccessRequest wrongRequest = new CheckValidAccessRequest(reviewRequestCode, groupAccessCode + "!"); // when CheckValidAccessResponse expected1 = reviewGroupService.checkGroupAccessCode(request);