diff --git a/src/main/java/com/example/solidconnection/application/domain/Application.java b/src/main/java/com/example/solidconnection/application/domain/Application.java index 44dcc52f1..085141f22 100644 --- a/src/main/java/com/example/solidconnection/application/domain/Application.java +++ b/src/main/java/com/example/solidconnection/application/domain/Application.java @@ -48,6 +48,9 @@ public class Application { @Column(columnDefinition = "int not null default 0") private Integer updateCount; + @Column(length = 50, nullable = false) + private String term; + @ManyToOne private UniversityInfoForApply firstChoiceUniversity; @@ -63,10 +66,14 @@ public class Application { public Application( SiteUser siteUser, Gpa gpa, - LanguageTest languageTest) { + LanguageTest languageTest, + String term) { this.siteUser = siteUser; this.gpa = gpa; this.languageTest = languageTest; + this.term = term; + this.updateCount = 0; + this.verifyStatus = PENDING; } public void updateGpaAndLanguageTest( diff --git a/src/main/java/com/example/solidconnection/application/dto/UniversityApplicantsResponse.java b/src/main/java/com/example/solidconnection/application/dto/UniversityApplicantsResponse.java index a78c98c20..84751786b 100644 --- a/src/main/java/com/example/solidconnection/application/dto/UniversityApplicantsResponse.java +++ b/src/main/java/com/example/solidconnection/application/dto/UniversityApplicantsResponse.java @@ -25,7 +25,7 @@ public record UniversityApplicantsResponse( public static UniversityApplicantsResponse of(UniversityInfoForApply universityInfoForApply, List applicant) { return new UniversityApplicantsResponse( - universityInfoForApply.getUniversity().getKoreanName(), + universityInfoForApply.getKoreanName(), universityInfoForApply.getStudentCapacity(), universityInfoForApply.getUniversity().getRegion().getKoreanName(), universityInfoForApply.getUniversity().getCountry().getKoreanName(), diff --git a/src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java b/src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java index 34b714438..8f30c196c 100644 --- a/src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java +++ b/src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java @@ -18,18 +18,18 @@ public interface ApplicationRepository extends JpaRepository boolean existsByNicknameForApply(String nicknameForApply); - Optional findBySiteUser_Email(String email); + Optional findTop1BySiteUser_EmailOrderByTermDesc(String email); - Optional findBySiteUser(SiteUser siteUser); + Optional findBySiteUserAndTerm(SiteUser siteUser, String term); - List findAllByFirstChoiceUniversityAndVerifyStatus(UniversityInfoForApply firstChoiceUniversity, VerifyStatus verifyStatus); + List findAllByFirstChoiceUniversityAndVerifyStatusAndTerm(UniversityInfoForApply firstChoiceUniversity, VerifyStatus verifyStatus, String term); - List findAllBySecondChoiceUniversityAndVerifyStatus(UniversityInfoForApply secondChoiceUniversity, VerifyStatus verifyStatus); + List findAllBySecondChoiceUniversityAndVerifyStatusAndTerm(UniversityInfoForApply secondChoiceUniversity, VerifyStatus verifyStatus, String term); - List findAllByThirdChoiceUniversityAndVerifyStatus(UniversityInfoForApply thirdChoiceUniversity, VerifyStatus verifyStatus); + List findAllByThirdChoiceUniversityAndVerifyStatusAndTerm(UniversityInfoForApply thirdChoiceUniversity, VerifyStatus verifyStatus, String term); - default Application getApplicationBySiteUser(SiteUser siteUser) { - return findBySiteUser(siteUser) + default Application getApplicationBySiteUserAndTerm(SiteUser siteUser, String term) { + return findBySiteUserAndTerm(siteUser, term) .orElseThrow(() -> new CustomException(APPLICATION_NOT_FOUND)); } } diff --git a/src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java b/src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java index 793cf28ca..2e481023d 100644 --- a/src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java +++ b/src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java @@ -52,41 +52,43 @@ public ApplicationsResponse getApplicants(String email, String regionCode, Strin List universities = universityFilterRepository.findByRegionCodeAndKeywords(regionCode, List.of(keyword)); - // 1지망, 2지망 지원자들을 조회한다. - List firstChoiceApplicants = getFirstChoiceApplicants(universities, siteUser); - List secondChoiceApplicants = getSecondChoiceApplicants(universities, siteUser); - List thirdChoiceApplicants = getThirdChoiceApplicants(universities, siteUser); + // 1지망, 2지망, 3지망 지원자들을 조회한다. + List firstChoiceApplicants = getFirstChoiceApplicants(universities, siteUser, term); + List secondChoiceApplicants = getSecondChoiceApplicants(universities, siteUser, term); + List thirdChoiceApplicants = getThirdChoiceApplicants(universities, siteUser, term); return new ApplicationsResponse(firstChoiceApplicants, secondChoiceApplicants, thirdChoiceApplicants); } + // 학기별로 상태가 관리된다. + // 금학기에 지원이력이 있는 사용자만 지원정보를 확인할 수 있도록 한다. private void validateSiteUserCanViewApplicants(SiteUser siteUser) { - VerifyStatus verifyStatus = applicationRepository.getApplicationBySiteUser(siteUser).getVerifyStatus(); + VerifyStatus verifyStatus = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term).getVerifyStatus(); if (verifyStatus != VerifyStatus.APPROVED) { throw new CustomException(APPLICATION_NOT_APPROVED); } } - private List getFirstChoiceApplicants(List universities, SiteUser siteUser) { + private List getFirstChoiceApplicants(List universities, SiteUser siteUser, String term) { return getApplicantsByChoice( universities, siteUser, - uia -> applicationRepository.findAllByFirstChoiceUniversityAndVerifyStatus(uia, VerifyStatus.APPROVED) + uia -> applicationRepository.findAllByFirstChoiceUniversityAndVerifyStatusAndTerm(uia, VerifyStatus.APPROVED, term) ); } - private List getSecondChoiceApplicants(List universities, SiteUser siteUser) { + private List getSecondChoiceApplicants(List universities, SiteUser siteUser, String term) { return getApplicantsByChoice( universities, siteUser, - uia -> applicationRepository.findAllBySecondChoiceUniversityAndVerifyStatus(uia, VerifyStatus.APPROVED) + uia -> applicationRepository.findAllBySecondChoiceUniversityAndVerifyStatusAndTerm(uia, VerifyStatus.APPROVED, term) ); } - private List getThirdChoiceApplicants(List universities, SiteUser siteUser) { + private List getThirdChoiceApplicants(List universities, SiteUser siteUser, String term) { return getApplicantsByChoice( universities, siteUser, - uia -> applicationRepository.findAllByThirdChoiceUniversityAndVerifyStatus(uia, VerifyStatus.APPROVED) + uia -> applicationRepository.findAllByThirdChoiceUniversityAndVerifyStatusAndTerm(uia, VerifyStatus.APPROVED, term) ); } diff --git a/src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java b/src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java index beec76a05..753bc89cc 100644 --- a/src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java +++ b/src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java @@ -16,13 +16,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; +import java.util.*; -import static com.example.solidconnection.custom.exception.ErrorCode.APPLY_UPDATE_LIMIT_EXCEED; -import static com.example.solidconnection.custom.exception.ErrorCode.CANT_APPLY_FOR_SAME_UNIVERSITY; -import static com.example.solidconnection.custom.exception.ErrorCode.SCORE_SHOULD_SUBMITTED_FIRST; +import static com.example.solidconnection.custom.exception.ErrorCode.*; @RequiredArgsConstructor @Service @@ -35,11 +31,12 @@ public class ApplicationSubmissionService { private final SiteUserRepository siteUserRepository; @Value("${university.term}") - public String term; + private String term; /* * 학점과 영어 성적을 제출한다. - * - 기존에 제출한 적이 있다면, 수정한다. + * - 금학기에 제출한 적이 있다면, 수정한다. + * - 성적을 제출한적이 한번도 없거나 제출한적이 있지만 금학기에 제출한 적이 없다면 새로 등록한다. * - 수정을 하고 나면, 성적 승인 상태(verifyStatus)를 PENDING 상태로 변경한다. * */ @Transactional @@ -48,15 +45,14 @@ public boolean submitScore(String email, ScoreRequest scoreRequest) { Gpa gpa = scoreRequest.toGpa(); LanguageTest languageTest = scoreRequest.toLanguageTest(); - applicationRepository.findBySiteUser_Email(email) + applicationRepository.findBySiteUserAndTerm(siteUser, term) .ifPresentOrElse( - // 수정 + // 금학기에 성적 제출 이력이 있는 경우 application -> application.updateGpaAndLanguageTest(gpa, languageTest), - - // 최초 등록 - () -> applicationRepository.save( - new Application(siteUser, gpa, languageTest) - ) + () -> { + // 성적 제출한적이 한번도 없는 경우 && 성적 제출한적이 있지만 금학기에 없는 경우 + applicationRepository.save(new Application(siteUser, gpa, languageTest, term)); + } ); return true; } @@ -73,9 +69,20 @@ public boolean submitScore(String email, ScoreRequest scoreRequest) { @Transactional public boolean submitUniversityChoice(String email, UniversityChoiceRequest universityChoiceRequest) { validateNoDuplicateUniversityChoices(universityChoiceRequest); - Application application = applicationRepository.findBySiteUser_Email(email) + + // 성적 제출한 적이 한번도 없는 경우 + Application existingApplication = applicationRepository.findTop1BySiteUser_EmailOrderByTermDesc(email) .orElseThrow(() -> new CustomException(SCORE_SHOULD_SUBMITTED_FIRST)); + Application application = Optional.of(existingApplication) + .filter(app -> !app.getTerm().equals(term)) + .map(app -> { + // 성적 제출한 적이 있지만 금학기에 없는 경우, 이전 성적으로 새 Application 객체를 등록 + SiteUser siteUser = siteUserRepository.getByEmail(email); + return applicationRepository.save(new Application(siteUser, app.getGpa(), app.getLanguageTest(), term)); + }) + .orElse(existingApplication); // 금학기에 이미 성적 제출한 경우 기존 객체 사용 + UniversityInfoForApply firstChoiceUniversity = universityInfoForApplyRepository .getUniversityInfoForApplyByIdAndTerm(universityChoiceRequest.firstChoiceUniversityId(), term); UniversityInfoForApply secondChoiceUniversity = universityInfoForApplyRepository diff --git a/src/main/java/com/example/solidconnection/application/service/VerifyStatusQueryService.java b/src/main/java/com/example/solidconnection/application/service/VerifyStatusQueryService.java index a5f7bf752..33bb340d9 100644 --- a/src/main/java/com/example/solidconnection/application/service/VerifyStatusQueryService.java +++ b/src/main/java/com/example/solidconnection/application/service/VerifyStatusQueryService.java @@ -7,6 +7,7 @@ import com.example.solidconnection.siteuser.repository.SiteUserRepository; import com.example.solidconnection.type.VerifyStatus; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -25,13 +26,17 @@ public class VerifyStatusQueryService { private final ApplicationRepository applicationRepository; private final SiteUserRepository siteUserRepository; + @Value("${university.term}") + private String term; + /* * 지원 상태를 조회한다. + * 학기별로 상태가 관리된다. * */ @Transactional(readOnly = true) public VerifyStatusResponse getVerifyStatus(String email) { SiteUser siteUser = siteUserRepository.getByEmail(email); - Optional application = applicationRepository.findBySiteUser_Email(siteUser.getEmail()); + Optional application = applicationRepository.findBySiteUserAndTerm(siteUser,term); // 아무것도 제출 안함 if (application.isEmpty()) { diff --git a/src/main/java/com/example/solidconnection/comment/dto/PostFindCommentResponse.java b/src/main/java/com/example/solidconnection/comment/dto/PostFindCommentResponse.java index 8524eb95a..2335b68ad 100644 --- a/src/main/java/com/example/solidconnection/comment/dto/PostFindCommentResponse.java +++ b/src/main/java/com/example/solidconnection/comment/dto/PostFindCommentResponse.java @@ -3,15 +3,15 @@ import com.example.solidconnection.comment.domain.Comment; import com.example.solidconnection.siteuser.dto.PostFindSiteUserResponse; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; public record PostFindCommentResponse( Long id, Long parentId, String content, Boolean isOwner, - LocalDateTime createdAt, - LocalDateTime updatedAt, + ZonedDateTime createdAt, + ZonedDateTime updatedAt, PostFindSiteUserResponse postFindSiteUserResponse ) { diff --git a/src/main/java/com/example/solidconnection/comment/service/CommentService.java b/src/main/java/com/example/solidconnection/comment/service/CommentService.java index 8003ab26b..8c0b0458f 100644 --- a/src/main/java/com/example/solidconnection/comment/service/CommentService.java +++ b/src/main/java/com/example/solidconnection/comment/service/CommentService.java @@ -13,11 +13,9 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; -import static com.example.solidconnection.custom.exception.ErrorCode.CAN_NOT_UPDATE_DEPRECATED_COMMENT; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_POST_ACCESS; +import static com.example.solidconnection.custom.exception.ErrorCode.*; @Service @RequiredArgsConstructor @@ -43,6 +41,13 @@ private void validateDeprecated(Comment comment) { } } + // 대대댓글부터 허용하지 않음 + private void validateCommentDepth(Comment parentComment) { + if (parentComment.getParentComment() != null) { + throw new CustomException(INVALID_COMMENT_LEVEL); + } + } + @Transactional(readOnly = true) public List findCommentsByPostId(String email, Long postId) { return commentRepository.findCommentTreeByPostId(postId) @@ -60,6 +65,7 @@ public CommentCreateResponse createComment(String email, Long postId, CommentCre Comment parentComment = null; if (commentCreateRequest.parentId() != null) { parentComment = commentRepository.getById(commentCreateRequest.parentId()); + validateCommentDepth(parentComment); } Comment createdComment = commentRepository.save(commentCreateRequest.toEntity(siteUser, post, parentComment)); @@ -87,15 +93,28 @@ public CommentDeleteResponse deleteCommentById(String email, Long postId, Long c Comment comment = commentRepository.getById(commentId); validateOwnership(comment, email); - if (comment.getCommentList().isEmpty()) { - // 하위 댓글이 없다면 삭제한다. + if (comment.getParentComment() != null) { + // 대댓글인 경우 + Comment parentComment = comment.getParentComment(); + // 대댓글을 삭제합니다. comment.resetPostAndSiteUserAndParentComment(); commentRepository.deleteById(commentId); + // 대댓글 삭제 이후, 부모댓글이 무의미하다면 이역시 삭제합니다. + if (parentComment.getCommentList().isEmpty() && parentComment.getContent() == null) { + parentComment.resetPostAndSiteUserAndParentComment(); + commentRepository.deleteById(parentComment.getId()); + } } else { - // 하위 댓글 있으면 value만 null로 수정한다. - comment.deprecateComment(); + // 댓글인 경우 + if (comment.getCommentList().isEmpty()) { + // 대댓글이 없는 경우 + comment.resetPostAndSiteUserAndParentComment(); + commentRepository.deleteById(commentId); + } else { + // 대댓글이 있는 경우 + comment.deprecateComment(); + } } - return new CommentDeleteResponse(commentId); } } diff --git a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java index c8bc38acc..f9e1e45b1 100644 --- a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java @@ -50,6 +50,7 @@ public enum ErrorCode { APPLY_UPDATE_LIMIT_EXCEED(HttpStatus.BAD_REQUEST.value(), "지원 정보 수정은 " + APPLICATION_UPDATE_COUNT_LIMIT + "회까지만 가능합니다."), CANT_APPLY_FOR_SAME_UNIVERSITY(HttpStatus.BAD_REQUEST.value(), "1, 2, 3지망에 동일한 대학교를 입력할 수 없습니다."), CAN_NOT_CHANGE_NICKNAME_YET(HttpStatus.BAD_REQUEST.value(), "마지막 닉네임 변경으로부터 " + MIN_DAYS_BETWEEN_NICKNAME_CHANGES + "일이 지나지 않았습니다."), + PROFILE_IMAGE_NEEDED(HttpStatus.BAD_REQUEST.value(), "프로필 이미지가 필요합니다."), // community INVALID_POST_CATEGORY(HttpStatus.BAD_REQUEST.value(),"잘못된 카테고리명입니다."), @@ -59,6 +60,7 @@ public enum ErrorCode { CAN_NOT_DELETE_OR_UPDATE_QUESTION(HttpStatus.BAD_REQUEST.value(), "질문글은 수정이나 삭제할 수 없습니다."), CAN_NOT_UPLOAD_MORE_THAN_FIVE_IMAGES(HttpStatus.BAD_REQUEST.value(), "5개 이상의 파일을 업로드할 수 없습니다."), INVALID_COMMENT_ID(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 댓글입니다."), + INVALID_COMMENT_LEVEL(HttpStatus.BAD_REQUEST.value(), "최대 대댓글까지만 작성할 수 있습니다."), INVALID_COMMENT_ACCESS(HttpStatus.BAD_REQUEST.value(), "자신의 댓글만 제어할 수 있습니다."), CAN_NOT_UPDATE_DEPRECATED_COMMENT(HttpStatus.BAD_REQUEST.value(),"이미 삭제된 댓글을 수정할 수 없습니다."), INVALID_POST_LIKE(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 게시글 좋아요입니다."), diff --git a/src/main/java/com/example/solidconnection/entity/common/BaseEntity.java b/src/main/java/com/example/solidconnection/entity/common/BaseEntity.java index 5f1283c64..27493f1be 100644 --- a/src/main/java/com/example/solidconnection/entity/common/BaseEntity.java +++ b/src/main/java/com/example/solidconnection/entity/common/BaseEntity.java @@ -2,14 +2,15 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreUpdate; import lombok.Getter; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; @MappedSuperclass @EntityListeners(AuditingEntityListener.class) @@ -18,9 +19,17 @@ @DynamicInsert public abstract class BaseEntity { - @CreatedDate - private LocalDateTime createdAt; + private ZonedDateTime createdAt; + private ZonedDateTime updatedAt; - @LastModifiedDate - private LocalDateTime updatedAt; + @PrePersist + public void onPrePersist() { + this.createdAt = ZonedDateTime.now(ZoneId.of("UTC")); + this.updatedAt = this.createdAt; + } + + @PreUpdate + public void onPreUpdate() { + this.updatedAt = ZonedDateTime.now(ZoneId.of("UTC")); + } } diff --git a/src/main/java/com/example/solidconnection/post/dto/BoardFindPostResponse.java b/src/main/java/com/example/solidconnection/post/dto/BoardFindPostResponse.java index 89c931925..8e6d3202a 100644 --- a/src/main/java/com/example/solidconnection/post/dto/BoardFindPostResponse.java +++ b/src/main/java/com/example/solidconnection/post/dto/BoardFindPostResponse.java @@ -3,7 +3,7 @@ import com.example.solidconnection.entity.PostImage; import com.example.solidconnection.post.domain.Post; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; import java.util.List; import java.util.stream.Collectors; @@ -13,8 +13,8 @@ public record BoardFindPostResponse( String content, Long likeCount, Integer commentCount, - LocalDateTime createdAt, - LocalDateTime updatedAt, + ZonedDateTime createdAt, + ZonedDateTime updatedAt, String postCategory, String url ) { diff --git a/src/main/java/com/example/solidconnection/post/dto/PostFindResponse.java b/src/main/java/com/example/solidconnection/post/dto/PostFindResponse.java index 7f5f703af..45e4e5dc7 100644 --- a/src/main/java/com/example/solidconnection/post/dto/PostFindResponse.java +++ b/src/main/java/com/example/solidconnection/post/dto/PostFindResponse.java @@ -6,7 +6,7 @@ import com.example.solidconnection.post.domain.Post; import com.example.solidconnection.siteuser.dto.PostFindSiteUserResponse; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; import java.util.List; public record PostFindResponse( @@ -20,8 +20,8 @@ public record PostFindResponse( String postCategory, Boolean isOwner, Boolean isLiked, - LocalDateTime createdAt, - LocalDateTime updatedAt, + ZonedDateTime createdAt, + ZonedDateTime updatedAt, PostFindBoardResponse postFindBoardResponse, PostFindSiteUserResponse postFindSiteUserResponse, List postFindCommentResponses, diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java index d7fe1a1fc..443404def 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java @@ -1,17 +1,12 @@ package com.example.solidconnection.siteuser.controller; -import com.example.solidconnection.siteuser.dto.MyPageResponse; -import com.example.solidconnection.siteuser.dto.MyPageUpdateRequest; -import com.example.solidconnection.siteuser.dto.MyPageUpdateResponse; +import com.example.solidconnection.siteuser.dto.*; import com.example.solidconnection.siteuser.service.SiteUserService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.security.Principal; @@ -36,12 +31,19 @@ public ResponseEntity getMyPageInfoToUpdate(Principal prin .ok(myPageUpdateDto); } - @PatchMapping("/update") - public ResponseEntity updateMyPageInfo( + @PatchMapping("/update/profileImage") + public ResponseEntity updateProfileImage( Principal principal, - @Valid @RequestBody MyPageUpdateRequest myPageUpdateDto) { - MyPageUpdateResponse myPageUpdateResponse = siteUserService.update(principal.getName(), myPageUpdateDto); - return ResponseEntity - .ok(myPageUpdateResponse); + @RequestParam(value = "file", required = false) MultipartFile imageFile) { + ProfileImageUpdateResponse profileImageUpdateResponse = siteUserService.updateProfileImage(principal.getName(), imageFile); + return ResponseEntity.ok().body(profileImageUpdateResponse); + } + + @PatchMapping("/update/nickname") + public ResponseEntity updateNickname( + Principal principal, + @Valid @RequestBody NicknameUpdateRequest nicknameUpdateRequest) { + NicknameUpdateResponse nicknameUpdateResponse = siteUserService.updateNickname(principal.getName(), nicknameUpdateRequest); + return ResponseEntity.ok().body(nicknameUpdateResponse); } } diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserControllerSwagger.java b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserControllerSwagger.java index e17b41c3e..6f479f67e 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserControllerSwagger.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserControllerSwagger.java @@ -52,27 +52,4 @@ public interface SiteUserControllerSwagger { } ) ResponseEntity getMyPageInfoToUpdate(Principal principal); - - @Operation( - summary = "마이 페이지 정보 수정", - requestBody = @RequestBody( - description = "업데이트할 정보", - required = true, - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = MyPageUpdateRequest.class) - ) - ), - responses = { - @ApiResponse( - responseCode = "200", - description = "마이 페이지 정보 수정 성공", - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = MyPageUpdateResponse.class) - ) - ) - } - ) - ResponseEntity updateMyPageInfo(Principal principal, @Valid @RequestBody MyPageUpdateRequest myPageUpdateDto); } diff --git a/src/main/java/com/example/solidconnection/siteuser/dto/NicknameUpdateRequest.java b/src/main/java/com/example/solidconnection/siteuser/dto/NicknameUpdateRequest.java new file mode 100644 index 000000000..4627a7451 --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/dto/NicknameUpdateRequest.java @@ -0,0 +1,11 @@ +package com.example.solidconnection.siteuser.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; + +public record NicknameUpdateRequest( + @NotBlank(message = "닉네임을 입력해주세요.") + @Schema(description = "변경할 닉네임", example = "NewNickname") + String nickname +) { +} diff --git a/src/main/java/com/example/solidconnection/siteuser/dto/NicknameUpdateResponse.java b/src/main/java/com/example/solidconnection/siteuser/dto/NicknameUpdateResponse.java new file mode 100644 index 000000000..5a967fa6e --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/dto/NicknameUpdateResponse.java @@ -0,0 +1,15 @@ +package com.example.solidconnection.siteuser.dto; + +import com.example.solidconnection.siteuser.domain.SiteUser; +import io.swagger.v3.oas.annotations.media.Schema; + +public record NicknameUpdateResponse( + @Schema(description = "업데이트된 사용자 닉네임", example = "UpdatedNickname") + String nickname +) { + public static NicknameUpdateResponse from(SiteUser siteUser) { + return new NicknameUpdateResponse( + siteUser.getNickname() + ); + } +} diff --git a/src/main/java/com/example/solidconnection/siteuser/dto/ProfileImageUpdateResponse.java b/src/main/java/com/example/solidconnection/siteuser/dto/ProfileImageUpdateResponse.java new file mode 100644 index 000000000..127578e2f --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/dto/ProfileImageUpdateResponse.java @@ -0,0 +1,15 @@ +package com.example.solidconnection.siteuser.dto; + +import com.example.solidconnection.siteuser.domain.SiteUser; +import io.swagger.v3.oas.annotations.media.Schema; + +public record ProfileImageUpdateResponse( + @Schema(description = "업데이트된 프로필 이미지 URL", example = "http://example.com/updated-profile.jpg") + String profileImageUrl +) { + public static ProfileImageUpdateResponse from(SiteUser siteUser) { + return new ProfileImageUpdateResponse( + siteUser.getProfileImageUrl() + ); + } +} diff --git a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java index e44af3a3e..71436ff87 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -1,34 +1,36 @@ package com.example.solidconnection.siteuser.service; import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.s3.S3Service; +import com.example.solidconnection.s3.UploadedFileUrlResponse; import com.example.solidconnection.siteuser.domain.SiteUser; -import com.example.solidconnection.siteuser.dto.MyPageResponse; -import com.example.solidconnection.siteuser.dto.MyPageUpdateRequest; -import com.example.solidconnection.siteuser.dto.MyPageUpdateResponse; +import com.example.solidconnection.siteuser.dto.*; import com.example.solidconnection.siteuser.repository.LikedUniversityRepository; import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.type.ImgType; import com.example.solidconnection.university.domain.LikedUniversity; import com.example.solidconnection.university.dto.UniversityInfoForApplyPreviewResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; -import static com.example.solidconnection.custom.exception.ErrorCode.CAN_NOT_CHANGE_NICKNAME_YET; -import static com.example.solidconnection.custom.exception.ErrorCode.NICKNAME_ALREADY_EXISTED; +import static com.example.solidconnection.custom.exception.ErrorCode.*; @RequiredArgsConstructor @Service public class SiteUserService { - public static final int MIN_DAYS_BETWEEN_NICKNAME_CHANGES = 30; + public static final int MIN_DAYS_BETWEEN_NICKNAME_CHANGES = 7; public static final DateTimeFormatter NICKNAME_LAST_CHANGE_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); private final SiteUserRepository siteUserRepository; private final LikedUniversityRepository likedUniversityRepository; + private final S3Service s3Service; /* * 마이페이지 정보를 조회한다. @@ -49,31 +51,11 @@ public MyPageUpdateResponse getMyPageInfoToUpdate(String email) { return MyPageUpdateResponse.from(siteUser); } - /* - * 마이페이지 정보를 수정한다. - * - 닉네임 중복을 검증한다. - * - '닉네임 변경 최소 기간'이 지나지 않았는데 변경하려 하는지 검증한다. - * */ - @Transactional - public MyPageUpdateResponse update(String email, MyPageUpdateRequest pageUpdateRequest) { - SiteUser siteUser = siteUserRepository.getByEmail(email); - - validateNicknameDuplicated(pageUpdateRequest.nickname()); - validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); - - siteUser.setNickname(pageUpdateRequest.nickname()); - siteUser.setProfileImageUrl(pageUpdateRequest.profileImageUrl()); - siteUser.setNicknameModifiedAt(LocalDateTime.now()); - siteUserRepository.save(siteUser); - return MyPageUpdateResponse.from(siteUser); - } - private void validateNicknameDuplicated(String nickname) { if (siteUserRepository.existsByNickname(nickname)) { throw new CustomException(NICKNAME_ALREADY_EXISTED); } } - private void validateNicknameNotChangedRecently(LocalDateTime lastModifiedAt) { if (lastModifiedAt == null) { return; @@ -96,4 +78,44 @@ public List getWishUniversity(String emai .map(likedUniversity -> UniversityInfoForApplyPreviewResponse.from(likedUniversity.getUniversityInfoForApply())) .toList(); } + + + /* + * 프로필 이미지를 수정한다. + * */ + @Transactional + public ProfileImageUpdateResponse updateProfileImage(String email, MultipartFile imageFile) { + SiteUser siteUser = siteUserRepository.getByEmail(email); + validateProfileImage(imageFile); + + s3Service.deleteExProfile(email); + UploadedFileUrlResponse uploadedFileUrlResponse = s3Service.uploadFile(imageFile, ImgType.PROFILE); + siteUser.setProfileImageUrl(uploadedFileUrlResponse.fileUrl()); + siteUserRepository.save(siteUser); + + return ProfileImageUpdateResponse.from(siteUser); + } + + private void validateProfileImage(MultipartFile imageFile) { + if (imageFile == null || imageFile.isEmpty()) { + throw new CustomException(PROFILE_IMAGE_NEEDED); + } + } + + /* + * 닉네임을 수정한다. + * */ + @Transactional + public NicknameUpdateResponse updateNickname(String email, NicknameUpdateRequest nicknameUpdateRequest) { + SiteUser siteUser = siteUserRepository.getByEmail(email); + + validateNicknameDuplicated(nicknameUpdateRequest.nickname()); + validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); + + siteUser.setNickname(nicknameUpdateRequest.nickname()); + siteUser.setNicknameModifiedAt(LocalDateTime.now()); + siteUserRepository.save(siteUser); + + return NicknameUpdateResponse.from(siteUser); + } } diff --git a/src/main/java/com/example/solidconnection/university/service/UniversityRecommendService.java b/src/main/java/com/example/solidconnection/university/service/UniversityRecommendService.java index bb704faea..88a7a222f 100644 --- a/src/main/java/com/example/solidconnection/university/service/UniversityRecommendService.java +++ b/src/main/java/com/example/solidconnection/university/service/UniversityRecommendService.java @@ -26,7 +26,7 @@ public class UniversityRecommendService { private final SiteUserRepository siteUserRepository; @Value("${university.term}") - public String term; + private String term; /* * 사용자 맞춤 추천 대학교를 불러온다. diff --git a/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java b/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java index d609aa121..cb5fbfe52 100644 --- a/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java +++ b/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java @@ -17,6 +17,7 @@ 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 java.util.List; @@ -42,6 +43,11 @@ class ApplicantsQueryTest extends UniversityDataSetUpEndToEndTest { private Application 사용자1_지원정보; private Application 사용자2_지원정보; private Application 사용자3_지원정보; + private Application 사용자4_이전학기_지원정보; + + @Value("${university.term}") + private String term; + private String beforeTerm = "1988-1"; @BeforeEach public void setUpUserAndToken() { @@ -58,23 +64,27 @@ public void setUpUserAndToken() { SiteUser 사용자1 = siteUserRepository.save(createSiteUserByEmail("email1")); SiteUser 사용자2 = siteUserRepository.save(createSiteUserByEmail("email2")); SiteUser 사용자3 = siteUserRepository.save(createSiteUserByEmail("email3")); + SiteUser 사용자4_이전학기_지원자 = siteUserRepository.save(createSiteUserByEmail("email4")); // setUp - 지원 정보 저장 Gpa gpa = createDummyGpa(); LanguageTest languageTest = createDummyLanguageTest(); - 나의_지원정보 = new Application(siteUser, gpa, languageTest); - 사용자1_지원정보 = new Application(사용자1, gpa, languageTest); - 사용자2_지원정보 = new Application(사용자2, gpa, languageTest); - 사용자3_지원정보 = new Application(사용자3, gpa, languageTest); + 나의_지원정보 = new Application(siteUser, gpa, languageTest, term); + 사용자1_지원정보 = new Application(사용자1, gpa, languageTest, term); + 사용자2_지원정보 = new Application(사용자2, gpa, languageTest, term); + 사용자3_지원정보 = new Application(사용자3, gpa, languageTest, term); + 사용자4_이전학기_지원정보 = new Application(사용자4_이전학기_지원자, gpa, languageTest, beforeTerm); 나의_지원정보.updateUniversityChoice(괌대학_B_지원_정보, 괌대학_A_지원_정보, 린츠_카톨릭대학_지원_정보, "0"); 사용자1_지원정보.updateUniversityChoice(괌대학_A_지원_정보, 괌대학_B_지원_정보, 그라츠공과대학_지원_정보, "1"); 사용자2_지원정보.updateUniversityChoice(메이지대학_지원_정보, 그라츠대학_지원_정보, 서던덴마크대학교_지원_정보, "2"); 사용자3_지원정보.updateUniversityChoice(네바다주립대학_라스베이거스_지원_정보, 그라츠공과대학_지원_정보, 메이지대학_지원_정보, "3"); + 사용자4_이전학기_지원정보.updateUniversityChoice(네바다주립대학_라스베이거스_지원_정보, 그라츠공과대학_지원_정보, 메이지대학_지원_정보, "4"); 나의_지원정보.setVerifyStatus(VerifyStatus.APPROVED); 사용자1_지원정보.setVerifyStatus(VerifyStatus.APPROVED); 사용자2_지원정보.setVerifyStatus(VerifyStatus.APPROVED); 사용자3_지원정보.setVerifyStatus(VerifyStatus.APPROVED); - applicationRepository.saveAll(List.of(나의_지원정보, 사용자1_지원정보, 사용자2_지원정보, 사용자3_지원정보)); + 사용자4_이전학기_지원정보.setVerifyStatus(VerifyStatus.APPROVED); + applicationRepository.saveAll(List.of(나의_지원정보, 사용자1_지원정보, 사용자2_지원정보, 사용자3_지원정보, 사용자4_이전학기_지원정보)); } @Test @@ -195,4 +205,33 @@ public void setUpUserAndToken() { assertThat(secondChoiceApplicants).containsExactlyInAnyOrder( UniversityApplicantsResponse.of(메이지대학_지원_정보, List.of())); } + + @Test + void 지원자를_조회할_때_이전학기_지원자는_조회되지_않는다() { + ApplicationsResponse response = RestAssured.given().log().all() + .header("Authorization", "Bearer " + accessToken) + .when().log().all() + .get("/application") + .then().log().all() + .statusCode(200) + .extract().as(ApplicationsResponse.class); + + List firstChoiceApplicants = response.firstChoice(); + List secondChoiceApplicants = response.secondChoice(); + List thirdChoiceApplicants = response.thirdChoice(); + + + assertThat(firstChoiceApplicants).doesNotContainAnyElementsOf(List.of( + UniversityApplicantsResponse.of(네바다주립대학_라스베이거스_지원_정보, + List.of(ApplicantResponse.of(사용자4_이전학기_지원정보, false))) + )); + assertThat(secondChoiceApplicants).doesNotContainAnyElementsOf(List.of( + UniversityApplicantsResponse.of(네바다주립대학_라스베이거스_지원_정보, + List.of(ApplicantResponse.of(사용자4_이전학기_지원정보, false))) + )); + assertThat(thirdChoiceApplicants).doesNotContainAnyElementsOf(List.of( + UniversityApplicantsResponse.of(네바다주립대학_라스베이거스_지원_정보, + List.of(ApplicantResponse.of(사용자4_이전학기_지원정보, false))) + )); + } } diff --git a/src/test/java/com/example/solidconnection/e2e/ApplicationSubmissionTest.java b/src/test/java/com/example/solidconnection/e2e/ApplicationSubmissionTest.java index 0cbe37f35..a12612071 100644 --- a/src/test/java/com/example/solidconnection/e2e/ApplicationSubmissionTest.java +++ b/src/test/java/com/example/solidconnection/e2e/ApplicationSubmissionTest.java @@ -66,7 +66,7 @@ public void setUpUserAndToken() { .then().log().all() .statusCode(HttpStatus.OK.value()); - Application application = applicationRepository.getApplicationBySiteUser(siteUser); + Application application = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term); assertAll("대학교 성적과 어학 성적을 저장한다.", () -> assertThat(application.getId()).isNotNull(), () -> assertThat(application.getSiteUser().getId()).isEqualTo(siteUser.getId()), @@ -84,7 +84,7 @@ public void setUpUserAndToken() { // setUp - 성적 정보 저장 ScoreRequest firstRequest = new ScoreRequest(LanguageTestType.TOEFL_IBT, "80", "languageTestReportUrl", 4.0, 4.5, "gpaReportUrl"); - applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest())); + applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest(),term)); // request - body 생성 및 요청 ScoreRequest secondRequest = new ScoreRequest(LanguageTestType.TOEFL_IBT, "90", @@ -98,7 +98,7 @@ public void setUpUserAndToken() { .then().log().all() .statusCode(HttpStatus.OK.value()); - Application updatedApplication = applicationRepository.getApplicationBySiteUser(siteUser); + Application updatedApplication = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term); assertAll("대학교 성적과 어학 성적을 수정한다. 이때 수정 횟수는 증가하지 않고, 성적 승인 상태는 PENDING 으로 바뀐다.", () -> assertThat(updatedApplication.getId()).isNotNull(), () -> assertThat(updatedApplication.getSiteUser().getId()).isEqualTo(siteUser.getId()), @@ -116,7 +116,7 @@ public void setUpUserAndToken() { // setUp - 성적 정보 저장 ScoreRequest firstRequest = new ScoreRequest(LanguageTestType.TOEFL_IBT, "80", "languageTestReportUrl", 4.0, 4.5, "gpaReportUrl"); - applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest())); + applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest(),term)); // request - body 생성 및 요청 UniversityChoiceRequest request = new UniversityChoiceRequest(그라츠대학_지원_정보.getId(), 코펜하겐IT대학_지원_정보.getId(), 메이지대학_지원_정보.getId()); @@ -129,7 +129,7 @@ public void setUpUserAndToken() { .then().log().all() .statusCode(HttpStatus.OK.value()); - Application application = applicationRepository.getApplicationBySiteUser(siteUser); + Application application = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term); assertAll("지망 대학교를 저장한다.", () -> assertThat(application.getId()).isNotNull(), () -> assertThat(application.getSiteUser().getId()).isEqualTo(siteUser.getId()), @@ -146,9 +146,9 @@ public void setUpUserAndToken() { // setUp - 성적 정보와 지망 대학 저장 ScoreRequest firstRequest = new ScoreRequest(LanguageTestType.TOEFL_IBT, "80", "languageTestReportUrl", 4.0, 4.5, "gpaReportUrl"); - applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest())) + applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest(),term)) .updateUniversityChoice(괌대학_A_지원_정보, 괌대학_B_지원_정보, 네바다주립대학_라스베이거스_지원_정보, "nickname"); - Application initialApplication = applicationRepository.getApplicationBySiteUser(siteUser); + Application initialApplication = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term); // request - body 생성 및 요청 UniversityChoiceRequest request = new UniversityChoiceRequest(그라츠대학_지원_정보.getId(), 코펜하겐IT대학_지원_정보.getId(), 메이지대학_지원_정보.getId()); @@ -161,7 +161,7 @@ public void setUpUserAndToken() { .then().log().all() .statusCode(HttpStatus.OK.value()); - Application updatedApplication = applicationRepository.getApplicationBySiteUser(siteUser); + Application updatedApplication = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term); assertAll("지망 대학교를 수정한다. 이때 수정 횟수는 증가하고, 성적 승인 상태는 바뀌지 않는다.", () -> assertThat(updatedApplication.getId()).isNotNull(), () -> assertThat(updatedApplication.getSiteUser().getId()).isEqualTo(siteUser.getId()), @@ -178,8 +178,8 @@ public void setUpUserAndToken() { // setUp - 성적 정보와 지망 대학 저장 ScoreRequest firstRequest = new ScoreRequest(LanguageTestType.TOEFL_IBT, "80", "languageTestReportUrl", 4.0, 4.5, "gpaReportUrl"); - applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest())); - Application initialApplication = applicationRepository.getApplicationBySiteUser(siteUser); + applicationRepository.save(new Application(siteUser, firstRequest.toGpa(), firstRequest.toLanguageTest(),term)); + Application initialApplication = applicationRepository.getApplicationBySiteUserAndTerm(siteUser,term); // setUp - 지망 대학을 한계까지 수정 for (int i = 0; i <= APPLICATION_UPDATE_COUNT_LIMIT; i++) { diff --git a/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java b/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java index fa515e4f2..cb058fe3a 100644 --- a/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java +++ b/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java @@ -4,8 +4,9 @@ import com.example.solidconnection.config.token.TokenType; import com.example.solidconnection.custom.response.ErrorResponse; import com.example.solidconnection.siteuser.domain.SiteUser; -import com.example.solidconnection.siteuser.dto.MyPageUpdateRequest; import com.example.solidconnection.siteuser.dto.MyPageUpdateResponse; +import com.example.solidconnection.siteuser.dto.NicknameUpdateRequest; +import com.example.solidconnection.siteuser.dto.NicknameUpdateResponse; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import io.restassured.RestAssured; import org.junit.jupiter.api.BeforeEach; @@ -68,27 +69,26 @@ public void setUpUserAndToken() { } @Test - void 마이_페이지_정보를_수정한다() { + void 닉네임을_수정한다() { // request - body 생성 및 요청 - MyPageUpdateRequest myPageUpdateRequest = new MyPageUpdateRequest("newNickname", "newProfileImageUrl"); - MyPageUpdateResponse myPageUpdateResponse = RestAssured.given() + NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); + NicknameUpdateResponse nicknameUpdateResponse = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .body(myPageUpdateRequest) + .body(nicknameUpdateRequest) .contentType("application/json") - .patch("/my-page/update") + .patch("/my-page/update/nickname") .then().log().all() .statusCode(HttpStatus.OK.value()) - .extract().as(MyPageUpdateResponse.class); + .extract().as(NicknameUpdateResponse.class); SiteUser savedSiteUser = siteUserRepository.getByEmail(email); assertAll("마이 페이지 정보가 수정된다.", - () -> assertThat(myPageUpdateResponse.nickname()).isEqualTo(savedSiteUser.getNickname()), - () -> assertThat(myPageUpdateResponse.profileImageUrl()).isEqualTo(savedSiteUser.getProfileImageUrl())); + () -> assertThat(nicknameUpdateResponse.nickname()).isEqualTo(savedSiteUser.getNickname())); } @Test - void 마이_페이지_정보를_수정할_때_닉네임이_중복된다면_예외_응답을_반환한다() { + void 닉네임을_수정할_때_닉네임이_중복된다면_예외_응답을_반환한다() { // setUp - 같은 닉네임을 갖는 다른 회원 정보 저장 SiteUser existUser = createSiteUserByEmail("existUser"); String duplicateNickname = "duplicateNickname"; @@ -96,13 +96,13 @@ public void setUpUserAndToken() { siteUserRepository.save(existUser); // request - body 생성 및 요청 - MyPageUpdateRequest myPageUpdateRequest = new MyPageUpdateRequest(duplicateNickname, "newProfileImageUrl"); + NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("duplicateNickname"); ErrorResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .body(myPageUpdateRequest) + .body(nicknameUpdateRequest) .contentType("application/json") - .patch("/my-page/update") + .patch("/my-page/update/nickname") .then().log().all() .statusCode(HttpStatus.CONFLICT.value()) .extract().as(ErrorResponse.class); @@ -112,7 +112,7 @@ public void setUpUserAndToken() { } @Test - void 마이_페이지_정보를_수정할_때_닉네임_변경_가능_기한이_지나지않았다면_예외_응답을_반환한다() { + void 닉네임을_수정할_때_닉네임_변경_가능_기한이_지나지않았다면_예외_응답을_반환한다() { // setUp - 회원 정보 저장 (닉네임 변경 가능 시간이 되기 1분 전) LocalDateTime nicknameModifiedAt = LocalDateTime.now() .minusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES) @@ -121,13 +121,13 @@ public void setUpUserAndToken() { siteUserRepository.save(siteUser); // request - body 생성 및 요청 - MyPageUpdateRequest myPageUpdateRequest = new MyPageUpdateRequest("newNickname", "newProfileImageUrl"); + NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); ErrorResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .body(myPageUpdateRequest) + .body(nicknameUpdateRequest) .contentType("application/json") - .patch("/my-page/update") + .patch("/my-page/update/nickname") .then().log().all() .statusCode(HttpStatus.BAD_REQUEST.value()) .extract().as(ErrorResponse.class); diff --git a/src/test/java/com/example/solidconnection/e2e/VerifyStatusQueryTest.java b/src/test/java/com/example/solidconnection/e2e/VerifyStatusQueryTest.java index b4447247a..856c4cdca 100644 --- a/src/test/java/com/example/solidconnection/e2e/VerifyStatusQueryTest.java +++ b/src/test/java/com/example/solidconnection/e2e/VerifyStatusQueryTest.java @@ -66,7 +66,7 @@ public void setUpUserAndToken() { @Test void 성적만_제출한_상태를_반환한다() { // setUp - 성적만 제출한 상태 - Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest()); + Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest(),term); applicationRepository.save(application); // request - 요청 @@ -86,7 +86,7 @@ public void setUpUserAndToken() { @Test void 성적과_대학을_모두_제출하고_승인을_기대라는_상태를_반환한다() { // setUp - 성적과 대학을 모두 제출한 상태 - Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest()); + Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest(),term); application.updateUniversityChoice(괌대학_B_지원_정보, 괌대학_A_지원_정보, 네바다주립대학_라스베이거스_지원_정보, "닉네임"); applicationRepository.save(application); @@ -107,7 +107,7 @@ public void setUpUserAndToken() { @Test void 성적과_대학을_모두_제출했지만_승인이_반려된_상태를_반환한다() { // setUp - 성적과 대학을 모두 제출했지만, 승인 거절 - Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest()); + Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest(),term); application.updateUniversityChoice(괌대학_B_지원_정보, 괌대학_A_지원_정보, 네바다주립대학_라스베이거스_지원_정보,"닉네임"); application.setVerifyStatus(VerifyStatus.REJECTED); applicationRepository.save(application); @@ -129,7 +129,7 @@ public void setUpUserAndToken() { @Test void 성적과_대학을_모두_제출했으며_승인이_된_상태를_반환한다() { // setUp - 성적과 대학을 모두 제출했으며, 승인이 된 상태 - Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest()); + Application application = new Application(siteUser, createDummyGpa(), createDummyLanguageTest(),term); application.updateUniversityChoice(괌대학_B_지원_정보, 괌대학_A_지원_정보, 네바다주립대학_라스베이거스_지원_정보, "닉네임"); application.setVerifyStatus(VerifyStatus.APPROVED); applicationRepository.save(application); diff --git a/src/test/java/com/example/solidconnection/unit/service/ApplicationServiceTest.java b/src/test/java/com/example/solidconnection/unit/service/ApplicationServiceTest.java new file mode 100644 index 000000000..d967c90bf --- /dev/null +++ b/src/test/java/com/example/solidconnection/unit/service/ApplicationServiceTest.java @@ -0,0 +1,174 @@ +package com.example.solidconnection.unit.service; + +import com.example.solidconnection.application.domain.Application; +import com.example.solidconnection.application.domain.Gpa; +import com.example.solidconnection.application.domain.LanguageTest; +import com.example.solidconnection.application.dto.ScoreRequest; +import com.example.solidconnection.application.dto.UniversityChoiceRequest; +import com.example.solidconnection.application.repository.ApplicationRepository; +import com.example.solidconnection.application.service.ApplicationSubmissionService; +import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.type.*; +import com.example.solidconnection.university.repository.UniversityInfoForApplyRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Optional; + +import static com.example.solidconnection.custom.exception.ErrorCode.SCORE_SHOULD_SUBMITTED_FIRST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("지원 서비스 테스트") +public class ApplicationServiceTest { + @InjectMocks + ApplicationSubmissionService applicationSubmissionService; + @Mock + ApplicationRepository applicationRepository; + @Mock + SiteUserRepository siteUserRepository; + @Mock + UniversityInfoForApplyRepository universityInfoForApplyRepository; + + private SiteUser siteUser; + private Application application; + private Application applicationBeforeTerm; + + private String term = "2024-1"; + private String beforeTerm = "1999-1"; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(applicationSubmissionService, "term", term); // 테스트시 @value값 주입위함 + siteUser = createSiteUser(); + application = createApplication(term); + applicationBeforeTerm = createApplication(beforeTerm); + } + + private SiteUser createSiteUser() { + return new SiteUser( + "test@example.com", + "nickname", + "profileImageUrl", + "1999-01-01", + PreparationStatus.CONSIDERING, + Role.MENTEE, + Gender.MALE + ); + } + + private Application createApplication(String term) { + return new Application( + siteUser, + new Gpa(4.0, 4.5, "url"), + new LanguageTest(LanguageTestType.TOEIC, "900", "url"), + term + ); + } + + @Test + void 성적을_제출한다_금학기_제출이력_없음() { + // Given + ScoreRequest scoreRequest = new ScoreRequest( + LanguageTestType.TOEIC, "990", "url", 4.5, 4.5, "url" + ); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(applicationRepository.findBySiteUserAndTerm(siteUser, term)).thenReturn(Optional.empty()); + + // When + applicationSubmissionService.submitScore(siteUser.getEmail(), scoreRequest); + + // Then + verify(siteUserRepository, times(1)).getByEmail(siteUser.getEmail()); + verify(applicationRepository, times(1)).findBySiteUserAndTerm(siteUser, term); + verify(applicationRepository, times(1)).save(any(Application.class)); + } + + @Test + void 성적을_제출한다_금학기_제출이력_있음() { + // Given + ScoreRequest scoreRequest = new ScoreRequest( + LanguageTestType.TOEIC, "990", "url", 4.5, 4.5, "url" + ); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(applicationRepository.findBySiteUserAndTerm(siteUser, term)).thenReturn(Optional.of(application)); + + // When + applicationSubmissionService.submitScore(siteUser.getEmail(), scoreRequest); + + // Then + assertEquals(application.getGpa().getGpa(), scoreRequest.gpa()); + assertEquals(application.getLanguageTest().getLanguageTestScore(), scoreRequest.languageTestScore()); + verify(siteUserRepository, times(1)).getByEmail(siteUser.getEmail()); + verify(applicationRepository, times(1)).findBySiteUserAndTerm(siteUser, term); + verify(applicationRepository, times(0)).save(any(Application.class)); + } + + // 예외테스트 + @Test + void 지망대학_제출할_때_성적_제출이력이_없다면_예외_응답을_반환한다() { + // given + UniversityChoiceRequest universityChoiceRequest = new UniversityChoiceRequest( + 1L, 2L, 3L + ); + when(applicationRepository.findTop1BySiteUser_EmailOrderByTermDesc(siteUser.getEmail())) + .thenReturn(Optional.empty()); + // when, then + CustomException exception = assertThrows(CustomException.class, () -> { + applicationSubmissionService.submitUniversityChoice(siteUser.getEmail(), universityChoiceRequest); + }); + assertThat(exception.getMessage()) + .isEqualTo(SCORE_SHOULD_SUBMITTED_FIRST.getMessage()); + assertThat(exception.getCode()) + .isEqualTo(SCORE_SHOULD_SUBMITTED_FIRST.getCode()); + } + + @Test + void 지망대학_제출한다_이전학기_성적_제출이력_있음() { + // Given + UniversityChoiceRequest universityChoiceRequest = new UniversityChoiceRequest( + 1L, 2L, 3L + ); + when(applicationRepository.findTop1BySiteUser_EmailOrderByTermDesc(siteUser.getEmail())) + .thenReturn(Optional.of(applicationBeforeTerm)); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + + // When + applicationSubmissionService.submitUniversityChoice(siteUser.getEmail(), universityChoiceRequest); + + // Then + verify(applicationRepository, times(1)).findTop1BySiteUser_EmailOrderByTermDesc(siteUser.getEmail()); + verify(siteUserRepository, times(1)).getByEmail(siteUser.getEmail()); + verify(applicationRepository, times(1)).save(any(Application.class)); + } + + @Test + void 지망대학_제출한다_금학기_성적_제출이력_있음() { + // Given + UniversityChoiceRequest universityChoiceRequest = new UniversityChoiceRequest( + 1L, 2L, 3L + ); + when(applicationRepository.findTop1BySiteUser_EmailOrderByTermDesc(siteUser.getEmail())) + .thenReturn(Optional.of(application)); + + // When + applicationSubmissionService.submitUniversityChoice(siteUser.getEmail(), universityChoiceRequest); + + // Then + verify(applicationRepository, times(1)).findTop1BySiteUser_EmailOrderByTermDesc(siteUser.getEmail()); + verify(siteUserRepository, times(0)).getByEmail(siteUser.getEmail()); + verify(applicationRepository, times(0)).save(any(Application.class)); + } +} diff --git a/src/test/java/com/example/solidconnection/unit/service/CommentServiceTest.java b/src/test/java/com/example/solidconnection/unit/service/CommentServiceTest.java index 8a90b275b..9ced8bcd8 100644 --- a/src/test/java/com/example/solidconnection/unit/service/CommentServiceTest.java +++ b/src/test/java/com/example/solidconnection/unit/service/CommentServiceTest.java @@ -44,9 +44,10 @@ class CommentServiceTest { private SiteUser siteUser; private Board board; private Post post; - private Comment parentComment_1; - private Comment parentComment_2; - private Comment p1s_childComment; + private Comment parentComment; + private Comment parentCommentWithNullContent; + private Comment childComment; + private Comment childCommentOfNullContentParent; @BeforeEach @@ -54,9 +55,10 @@ void setUp() { siteUser = createSiteUser(); board = createBoard(); post = createPost(board, siteUser); - parentComment_1 = createParentComment(); - parentComment_2 = createParentComment(); - p1s_childComment = createChildComment(); + parentComment = createParentComment("parent"); + parentCommentWithNullContent = createParentComment(null); + childComment = createChildComment(parentComment); + childCommentOfNullContentParent = createChildComment(parentCommentWithNullContent); } private SiteUser createSiteUser() { @@ -89,19 +91,19 @@ private Post createPost(Board board, SiteUser siteUser) { return post; } - private Comment createParentComment() { + private Comment createParentComment(String content) { Comment comment = new Comment( - "parent" + content ); comment.setPostAndSiteUser(post, siteUser); return comment; } - private Comment createChildComment() { + private Comment createChildComment(Comment parentComment) { Comment comment = new Comment( "child" ); - comment.setParentCommentAndPostAndSiteUser(parentComment_1, post, siteUser); + comment.setParentCommentAndPostAndSiteUser(parentComment, post, siteUser); return comment; } @@ -112,7 +114,7 @@ private Comment createChildComment() { @Test void 특정_게시글의_댓글들을_조회한다() { // Given - List commentList = List.of(parentComment_1, p1s_childComment, parentComment_2); + List commentList = List.of(parentComment, childComment, parentCommentWithNullContent); when(commentRepository.findCommentTreeByPostId(post.getId())).thenReturn(commentList); // When @@ -141,18 +143,18 @@ private Boolean isOwner(Comment comment, String email) { ); when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.save(any(Comment.class))).thenReturn(parentComment_1); + when(commentRepository.save(any(Comment.class))).thenReturn(parentComment); // When CommentCreateResponse commentCreateResponse = commentService.createComment( siteUser.getEmail(), post.getId(), commentCreateRequest); // Then - assertEquals(commentCreateResponse, CommentCreateResponse.from(parentComment_1)); + assertEquals(commentCreateResponse, CommentCreateResponse.from(parentComment)); verify(commentRepository, times(0)) .getById(any(Long.class)); verify(commentRepository, times(1)) - .save(commentCreateRequest.toEntity(siteUser, post, parentComment_1)); + .save(commentCreateRequest.toEntity(siteUser, post, parentComment)); } @Test @@ -164,19 +166,19 @@ private Boolean isOwner(Comment comment, String email) { ); when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(parentCommentId)).thenReturn(parentComment_1); - when(commentRepository.save(any(Comment.class))).thenReturn(p1s_childComment); + when(commentRepository.getById(parentCommentId)).thenReturn(parentComment); + when(commentRepository.save(any(Comment.class))).thenReturn(childComment); // When CommentCreateResponse commentCreateResponse = commentService.createComment( siteUser.getEmail(), post.getId(), commentCreateRequest); // Then - assertEquals(commentCreateResponse, CommentCreateResponse.from(p1s_childComment)); + assertEquals(commentCreateResponse, CommentCreateResponse.from(childComment)); verify(commentRepository, times(1)) .getById(parentCommentId); verify(commentRepository, times(1)) - .save(commentCreateRequest.toEntity(siteUser, post, parentComment_1)); + .save(commentCreateRequest.toEntity(siteUser, post, parentComment)); } @@ -225,6 +227,29 @@ private Boolean isOwner(Comment comment, String email) { .save(any(Comment.class)); } + @Test + void 댓글을_등록할_때_대대댓글_부터는_예외_응답을_반환한다() { + // Given + Long childCommentId = 1L; + CommentCreateRequest commentCreateRequest = new CommentCreateRequest( + "child's child", childCommentId + ); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(postRepository.getById(post.getId())).thenReturn(post); + when(commentRepository.getById(childCommentId)).thenReturn(childComment); + + // When & Then + CustomException exception = assertThrows(CustomException.class, () -> + commentService.createComment(siteUser.getEmail(), post.getId(), commentCreateRequest) + ); + assertThat(exception.getMessage()) + .isEqualTo(INVALID_COMMENT_LEVEL.getMessage()); + assertThat(exception.getCode()) + .isEqualTo(INVALID_COMMENT_LEVEL.getCode()); + verify(commentRepository, times(0)) + .save(any(Comment.class)); + } + /** * 댓글 수정 */ @@ -236,14 +261,14 @@ private Boolean isOwner(Comment comment, String email) { ); when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(any())).thenReturn(parentComment_1); + when(commentRepository.getById(any())).thenReturn(parentComment); // When CommentUpdateResponse commentUpdateResponse = commentService.updateComment( - siteUser.getEmail(), post.getId(), parentComment_1.getId(), commentUpdateRequest); + siteUser.getEmail(), post.getId(), parentComment.getId(), commentUpdateRequest); // Then - assertEquals(commentUpdateResponse.id(), parentComment_1.getId()); + assertEquals(commentUpdateResponse.id(), parentComment.getId()); } @Test @@ -258,7 +283,7 @@ private Boolean isOwner(Comment comment, String email) { // When & Then CustomException exception = assertThrows(CustomException.class, () -> - commentService.updateComment(siteUser.getEmail(), invalidPostId, parentComment_1.getId(), commentUpdateRequest) + commentService.updateComment(siteUser.getEmail(), invalidPostId, parentComment.getId(), commentUpdateRequest) ); assertThat(exception.getMessage()) .isEqualTo(INVALID_POST_ID.getMessage()); @@ -290,17 +315,17 @@ private Boolean isOwner(Comment comment, String email) { @Test void 댓글을_수정할_때_이미_삭제된_댓글이라면_예외_응답을_반환한다() { // Given - parentComment_1.deprecateComment(); + parentComment.deprecateComment(); CommentUpdateRequest commentUpdateRequest = new CommentUpdateRequest( "update" ); when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(any())).thenReturn(parentComment_1); + when(commentRepository.getById(any())).thenReturn(parentComment); // When & Then CustomException exception = assertThrows(CustomException.class, () -> - commentService.updateComment(siteUser.getEmail(), post.getId(), parentComment_1.getId(), commentUpdateRequest) + commentService.updateComment(siteUser.getEmail(), post.getId(), parentComment.getId(), commentUpdateRequest) ); assertThat(exception.getMessage()) .isEqualTo(CAN_NOT_UPDATE_DEPRECATED_COMMENT.getMessage()); @@ -317,11 +342,11 @@ private Boolean isOwner(Comment comment, String email) { ); when(siteUserRepository.getByEmail(invalidEmail)).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(any())).thenReturn(parentComment_1); + when(commentRepository.getById(any())).thenReturn(parentComment); // When & Then CustomException exception = assertThrows(CustomException.class, () -> - commentService.updateComment(invalidEmail, post.getId(), parentComment_1.getId(), commentUpdateRequest) + commentService.updateComment(invalidEmail, post.getId(), parentComment.getId(), commentUpdateRequest) ); assertThat(exception.getMessage()) .isEqualTo(INVALID_POST_ACCESS.getMessage()); @@ -339,14 +364,14 @@ private Boolean isOwner(Comment comment, String email) { Long parentCommentId = 1L; when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(any())).thenReturn(parentComment_1); + when(commentRepository.getById(any())).thenReturn(parentComment); // When CommentDeleteResponse commentDeleteResponse = commentService.deleteCommentById( siteUser.getEmail(), post.getId(), parentCommentId); // Then - assertEquals(parentComment_1.getContent(), null); + assertEquals(parentComment.getContent(), null); assertEquals(commentDeleteResponse.id(), parentCommentId); verify(commentRepository, times(0)).deleteById(parentCommentId); } @@ -357,7 +382,24 @@ private Boolean isOwner(Comment comment, String email) { Long childCommentId = 1L; when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(any())).thenReturn(p1s_childComment); + when(commentRepository.getById(any())).thenReturn(childComment); + + // When + CommentDeleteResponse commentDeleteResponse = commentService.deleteCommentById( + siteUser.getEmail(), post.getId(), childCommentId); + + // Then + assertEquals(commentDeleteResponse.id(), childCommentId); + verify(commentRepository, times(1)).deleteById(childCommentId); + } + + @Test + void 대댓글을_삭제한다_부모댓글_유효() { + // Given + Long childCommentId = 1L; + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(postRepository.getById(post.getId())).thenReturn(post); + when(commentRepository.getById(any())).thenReturn(childComment); // When CommentDeleteResponse commentDeleteResponse = commentService.deleteCommentById( @@ -368,6 +410,24 @@ private Boolean isOwner(Comment comment, String email) { verify(commentRepository, times(1)).deleteById(childCommentId); } + @Test + void 대댓글을_삭제한다_부모댓글_유효하지_않음() { + // Given + + Long childCommentId = 1L; + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(postRepository.getById(post.getId())).thenReturn(post); + when(commentRepository.getById(any())).thenReturn(childCommentOfNullContentParent); + + // When + CommentDeleteResponse commentDeleteResponse = commentService.deleteCommentById( + siteUser.getEmail(), post.getId(), childCommentId); + + // Then + assertEquals(commentDeleteResponse.id(), childCommentId); + verify(commentRepository, times(2)).deleteById(any()); + } + @Test void 댓글을_삭제할_때_유효한_게시글이_아니라면_예외_응답을_반환한다() { // Given @@ -377,7 +437,7 @@ private Boolean isOwner(Comment comment, String email) { // When & Then CustomException exception = assertThrows(CustomException.class, () -> - commentService.deleteCommentById(siteUser.getEmail(), invalidPostId, parentComment_1.getId()) + commentService.deleteCommentById(siteUser.getEmail(), invalidPostId, parentComment.getId()) ); assertThat(exception.getMessage()) .isEqualTo(INVALID_POST_ID.getMessage()); @@ -409,11 +469,11 @@ private Boolean isOwner(Comment comment, String email) { String invalidEmail = "invalidEmail@test.com"; when(siteUserRepository.getByEmail(invalidEmail)).thenReturn(siteUser); when(postRepository.getById(post.getId())).thenReturn(post); - when(commentRepository.getById(any())).thenReturn(parentComment_1); + when(commentRepository.getById(any())).thenReturn(parentComment); // When & Then CustomException exception = assertThrows(CustomException.class, () -> - commentService.deleteCommentById(invalidEmail, post.getId(), parentComment_1.getId()) + commentService.deleteCommentById(invalidEmail, post.getId(), parentComment.getId()) ); assertThat(exception.getMessage()) .isEqualTo(INVALID_POST_ACCESS.getMessage()); diff --git a/src/test/java/com/example/solidconnection/unit/service/SiteUserServiceTest.java b/src/test/java/com/example/solidconnection/unit/service/SiteUserServiceTest.java new file mode 100644 index 000000000..f6a330348 --- /dev/null +++ b/src/test/java/com/example/solidconnection/unit/service/SiteUserServiceTest.java @@ -0,0 +1,162 @@ +package com.example.solidconnection.unit.service; + +import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.s3.S3Service; +import com.example.solidconnection.s3.UploadedFileUrlResponse; +import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.dto.NicknameUpdateRequest; +import com.example.solidconnection.siteuser.dto.NicknameUpdateResponse; +import com.example.solidconnection.siteuser.dto.ProfileImageUpdateResponse; +import com.example.solidconnection.siteuser.repository.LikedUniversityRepository; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.siteuser.service.SiteUserService; +import com.example.solidconnection.type.Gender; +import com.example.solidconnection.type.ImgType; +import com.example.solidconnection.type.PreparationStatus; +import com.example.solidconnection.type.Role; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; + +import static com.example.solidconnection.custom.exception.ErrorCode.*; +import static com.example.solidconnection.siteuser.service.SiteUserService.NICKNAME_LAST_CHANGE_DATE_FORMAT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("유저 서비스 테스트") +public class SiteUserServiceTest { + @InjectMocks + SiteUserService siteUserService; + @Mock + SiteUserRepository siteUserRepository; + @Mock + LikedUniversityRepository likedUniversityRepository; + @Mock + S3Service s3Service; + + private SiteUser siteUser; + private MultipartFile imageFile; + private UploadedFileUrlResponse uploadedFileUrlResponse; + + @BeforeEach + void setUp() { + siteUser = createSiteUser(); + imageFile = createMockImageFile(); + uploadedFileUrlResponse = createUploadedFileUrlResponse(); + + } + + private SiteUser createSiteUser() { + return new SiteUser( + "test@example.com", + "nickname", + "http://example.com/profile.jpg", + "1999-01-01", + PreparationStatus.CONSIDERING, + Role.MENTEE, + Gender.MALE + ); + } + + private MultipartFile createMockImageFile() { + return new MockMultipartFile("file1", "test1.png", + "image/png", "test image content 1".getBytes()); + + } + + private UploadedFileUrlResponse createUploadedFileUrlResponse() { + return new UploadedFileUrlResponse("https://s3.example.com/test1.png"); + } + + @Test + void 프로필_이미지를_수정한다() { + // Given + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(s3Service.uploadFile(imageFile, ImgType.PROFILE)).thenReturn(uploadedFileUrlResponse); + + // When + ProfileImageUpdateResponse profileImageUpdateResponse = + siteUserService.updateProfileImage(siteUser.getEmail(), imageFile); + // Then + assertEquals(profileImageUpdateResponse, ProfileImageUpdateResponse.from(siteUser)); + verify(siteUserRepository, times(1)).getByEmail(siteUser.getEmail()); + verify(s3Service, times(1)).deleteExProfile(siteUser.getEmail()); + verify(s3Service, times(1)).uploadFile(imageFile, ImgType.PROFILE); + verify(siteUserRepository, times(1)).save(any(SiteUser.class)); + } + + @Test + void 프로필_이미지를_수정할_때_이미지가_없다면_예외_응답을_반환한다() { + // Given + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + + // When & Then + CustomException exception = assertThrows(CustomException.class, () -> + siteUserService.updateProfileImage(siteUser.getEmail(), null)); + assertThat(exception.getMessage()) + .isEqualTo(PROFILE_IMAGE_NEEDED.getMessage()); + assertThat(exception.getCode()) + .isEqualTo(PROFILE_IMAGE_NEEDED.getCode()); + } + + @Test + void 닉네임을_수정한다() { + // Given + NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + + // When + NicknameUpdateResponse nicknameUpdateResponse + = siteUserService.updateNickname(siteUser.getEmail(), nicknameUpdateRequest); + // Then + assertEquals( nicknameUpdateResponse, NicknameUpdateResponse.from(siteUser)); + verify(siteUserRepository, times(1)).getByEmail(siteUser.getEmail()); + verify(siteUserRepository, times(1)).save(any(SiteUser.class)); + } + + @Test + void 닉네임을_수정할_때_중복된_닉네임이라면_예외_응답을_반환한다() { + // Given + NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + when(siteUserRepository.existsByNickname(nicknameUpdateRequest.nickname())).thenReturn(true); + + // When & Then + CustomException exception = assertThrows(CustomException.class, () -> + siteUserService.updateNickname(siteUser.getEmail(), nicknameUpdateRequest)); + assertThat(exception.getMessage()) + .isEqualTo(NICKNAME_ALREADY_EXISTED.getMessage()); + assertThat(exception.getCode()) + .isEqualTo(NICKNAME_ALREADY_EXISTED.getCode()); + } + + @Test + void 닉네임을_수정할_때_변경_가능_기한이_지나지_않았다면_예외_응답을_반환한다() { + // Given + NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); + siteUser.setNicknameModifiedAt(LocalDateTime.now()); + when(siteUserRepository.getByEmail(siteUser.getEmail())).thenReturn(siteUser); + + // When & Then + CustomException exception = assertThrows(CustomException.class, () -> + siteUserService.updateNickname(siteUser.getEmail(), nicknameUpdateRequest)); + + String formatLastModifiedAt + = String.format("(마지막 수정 시간 : %s)", NICKNAME_LAST_CHANGE_DATE_FORMAT.format(siteUser.getNicknameModifiedAt())); + CustomException expectedException = new CustomException(CAN_NOT_CHANGE_NICKNAME_YET, formatLastModifiedAt); + assertThat(exception.getMessage()).isEqualTo(expectedException.getMessage()); + assertThat(exception.getCode()).isEqualTo(expectedException.getCode()); + } +}