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 e99d43f4a..16d612ded 100644 --- a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java @@ -5,7 +5,7 @@ import org.springframework.http.HttpStatus; import static com.example.solidconnection.application.service.ApplicationSubmissionService.APPLICATION_UPDATE_COUNT_LIMIT; -import static com.example.solidconnection.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; +import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; @Getter @AllArgsConstructor diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java new file mode 100644 index 000000000..94bc6e8a8 --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java @@ -0,0 +1,40 @@ +package com.example.solidconnection.siteuser.controller; + +import com.example.solidconnection.custom.resolver.AuthorizedUser; +import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.dto.MyPageResponse; +import com.example.solidconnection.siteuser.service.MyPageService; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RequiredArgsConstructor +@RequestMapping("/my") +@RestController +class MyPageController { + + private final MyPageService myPageService; + + @GetMapping + public ResponseEntity getMyPageInfo( + @AuthorizedUser SiteUser siteUser + ) { + MyPageResponse myPageResponse = myPageService.getMyPageInfo(siteUser); + return ResponseEntity.ok(myPageResponse); + } + + @PatchMapping + public ResponseEntity updateMyPageInfo( + @AuthorizedUser SiteUser siteUser, + @RequestParam("file") MultipartFile imageFile, + @RequestParam("nickname") String nickname + ) { + myPageService.updateMyPageInfo(siteUser, imageFile, nickname); + return ResponseEntity.ok().build(); + } +} 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 46f1555f1..64d926eca 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java @@ -1,40 +1,26 @@ package com.example.solidconnection.siteuser.controller; -import com.example.solidconnection.custom.resolver.AuthorizedUser; -import com.example.solidconnection.siteuser.domain.SiteUser; -import com.example.solidconnection.siteuser.dto.MyPageResponse; +import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; import com.example.solidconnection.siteuser.service.SiteUserService; 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.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; @RequiredArgsConstructor -@RequestMapping("/my") +@RequestMapping("/users") @RestController -class SiteUserController { +public class SiteUserController { private final SiteUserService siteUserService; - @GetMapping - public ResponseEntity getMyPageInfo( - @AuthorizedUser SiteUser siteUser + @GetMapping("/exists") + public ResponseEntity checkNicknameExists( + @RequestParam("nickname") String nickname ) { - MyPageResponse myPageResponse = siteUserService.getMyPageInfo(siteUser); - return ResponseEntity.ok(myPageResponse); - } - - @PatchMapping - public ResponseEntity updateMyPageInfo( - @AuthorizedUser SiteUser siteUser, - @RequestParam(value = "file", required = false) MultipartFile imageFile, - @RequestParam(value = "nickname", required = false) String nickname - ) { - siteUserService.updateMyPageInfo(siteUser, imageFile, nickname); - return ResponseEntity.ok().build(); + NicknameExistsResponse nicknameExistsResponse = siteUserService.checkNicknameExists(nickname); + return ResponseEntity.ok(nicknameExistsResponse); } } diff --git a/src/main/java/com/example/solidconnection/siteuser/dto/NicknameExistsResponse.java b/src/main/java/com/example/solidconnection/siteuser/dto/NicknameExistsResponse.java new file mode 100644 index 000000000..efb53df54 --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/dto/NicknameExistsResponse.java @@ -0,0 +1,9 @@ +package com.example.solidconnection.siteuser.dto; + +public record NicknameExistsResponse( + boolean exists +) { + public static NicknameExistsResponse from(boolean exists) { + return new NicknameExistsResponse(exists); + } +} diff --git a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java new file mode 100644 index 000000000..50a020427 --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java @@ -0,0 +1,105 @@ +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.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.PROFILE_IMAGE_NEEDED; + +@RequiredArgsConstructor +@Service +public class MyPageService { + + 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; + + /* + * 마이페이지 정보를 조회한다. + * */ + @Transactional(readOnly = true) + public MyPageResponse getMyPageInfo(SiteUser siteUser) { + int likedUniversityCount = likedUniversityRepository.countBySiteUser_Id(siteUser.getId()); + return MyPageResponse.of(siteUser, likedUniversityCount); + } + + /* + * 마이페이지 정보를 수정한다. + * */ + @Transactional + public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { + validateNicknameUnique(nickname); + validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); + validateProfileImageNotEmpty(imageFile); + + if (!isDefaultProfileImage(siteUser.getProfileImageUrl())) { + s3Service.deleteExProfile(siteUser); + } + UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(imageFile, ImgType.PROFILE); + String profileImageUrl = uploadedFile.fileUrl(); + + siteUser.setProfileImageUrl(profileImageUrl); + siteUser.setNickname(nickname); + siteUser.setNicknameModifiedAt(LocalDateTime.now()); + siteUserRepository.save(siteUser); + } + + private void validateNicknameUnique(String nickname) { + if (siteUserRepository.existsByNickname(nickname)) { + throw new CustomException(NICKNAME_ALREADY_EXISTED); + } + } + + private void validateNicknameNotChangedRecently(LocalDateTime lastModifiedAt) { + if (lastModifiedAt == null) { + return; + } + if (LocalDateTime.now().isBefore(lastModifiedAt.plusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES))) { + String formatLastModifiedAt + = String.format("(마지막 수정 시간 : %s)", NICKNAME_LAST_CHANGE_DATE_FORMAT.format(lastModifiedAt)); + throw new CustomException(CAN_NOT_CHANGE_NICKNAME_YET, formatLastModifiedAt); + } + } + + private void validateProfileImageNotEmpty(MultipartFile imageFile) { + if (imageFile == null || imageFile.isEmpty()) { + throw new CustomException(PROFILE_IMAGE_NEEDED); + } + } + + private boolean isDefaultProfileImage(String profileImageUrl) { + String prefix = "profile/"; + return profileImageUrl == null || !profileImageUrl.startsWith(prefix); + } + + /* + * 관심 대학교 목록을 조회한다. + * */ + @Transactional(readOnly = true) + public List getWishUniversity(SiteUser siteUser) { + List likedUniversities = likedUniversityRepository.findAllBySiteUser_Id(siteUser.getId()); + return likedUniversities.stream() + .map(likedUniversity -> UniversityInfoForApplyPreviewResponse.from(likedUniversity.getUniversityInfoForApply())) + .toList(); + } +} 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 c2ee4ded7..e67d71ab8 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -1,101 +1,18 @@ 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.repository.LikedUniversityRepository; +import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; 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.PROFILE_IMAGE_NEEDED; @RequiredArgsConstructor @Service public class SiteUserService { - 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; - - /* - * 마이페이지 정보를 조회한다. - * */ - @Transactional(readOnly = true) - public MyPageResponse getMyPageInfo(SiteUser siteUser) { - int likedUniversityCount = likedUniversityRepository.countBySiteUser_Id(siteUser.getId()); - return MyPageResponse.of(siteUser, likedUniversityCount); - } - - /* - * 마이페이지 정보를 수정한다. - * */ - @Transactional - public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { - if (nickname != null) { - validateNicknameUnique(nickname); - validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); - siteUser.setNickname(nickname); - siteUser.setNicknameModifiedAt(LocalDateTime.now()); - } - - if (imageFile != null && !imageFile.isEmpty()) { - UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(imageFile, ImgType.PROFILE); - if (!isDefaultProfileImage(siteUser.getProfileImageUrl())) { - s3Service.deleteExProfile(siteUser); - } - String profileImageUrl = uploadedFile.fileUrl(); - siteUser.setProfileImageUrl(profileImageUrl); - } - siteUserRepository.save(siteUser); - } - - private void validateNicknameUnique(String nickname) { - if (siteUserRepository.existsByNickname(nickname)) { - throw new CustomException(NICKNAME_ALREADY_EXISTED); - } - } - - private void validateNicknameNotChangedRecently(LocalDateTime lastModifiedAt) { - if (lastModifiedAt == null) { - return; - } - if (LocalDateTime.now().isBefore(lastModifiedAt.plusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES))) { - String formatLastModifiedAt - = String.format("(마지막 수정 시간 : %s)", NICKNAME_LAST_CHANGE_DATE_FORMAT.format(lastModifiedAt)); - throw new CustomException(CAN_NOT_CHANGE_NICKNAME_YET, formatLastModifiedAt); - } - } - - private boolean isDefaultProfileImage(String profileImageUrl) { - String prefix = "profile/"; - return profileImageUrl == null || !profileImageUrl.startsWith(prefix); - } - /* - * 관심 대학교 목록을 조회한다. - * */ - @Transactional(readOnly = true) - public List getWishUniversity(SiteUser siteUser) { - List likedUniversities = likedUniversityRepository.findAllBySiteUser_Id(siteUser.getId()); - return likedUniversities.stream() - .map(likedUniversity -> UniversityInfoForApplyPreviewResponse.from(likedUniversity.getUniversityInfoForApply())) - .toList(); + public NicknameExistsResponse checkNicknameExists(String nickname) { + boolean exists = siteUserRepository.existsByNickname(nickname); + return NicknameExistsResponse.from(exists); } } diff --git a/src/main/java/com/example/solidconnection/university/controller/UniversityController.java b/src/main/java/com/example/solidconnection/university/controller/UniversityController.java index 83d90f600..635693d4c 100644 --- a/src/main/java/com/example/solidconnection/university/controller/UniversityController.java +++ b/src/main/java/com/example/solidconnection/university/controller/UniversityController.java @@ -2,7 +2,7 @@ import com.example.solidconnection.custom.resolver.AuthorizedUser; import com.example.solidconnection.siteuser.domain.SiteUser; -import com.example.solidconnection.siteuser.service.SiteUserService; +import com.example.solidconnection.siteuser.service.MyPageService; import com.example.solidconnection.type.LanguageTestType; import com.example.solidconnection.university.dto.IsLikeResponse; import com.example.solidconnection.university.dto.LikeResultResponse; @@ -32,7 +32,7 @@ public class UniversityController { private final UniversityQueryService universityQueryService; private final UniversityLikeService universityLikeService; private final UniversityRecommendService universityRecommendService; - private final SiteUserService siteUserService; + private final MyPageService myPageService; @GetMapping("/recommend") public ResponseEntity getUniversityRecommends( @@ -49,7 +49,7 @@ public ResponseEntity getUniversityRecommends( public ResponseEntity> getMyWishUniversity( @AuthorizedUser SiteUser siteUser ) { - List wishUniversities = siteUserService.getWishUniversity(siteUser); + List wishUniversities = myPageService.getWishUniversity(siteUser); return ResponseEntity.ok(wishUniversities); } diff --git a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java new file mode 100644 index 000000000..6e897f04a --- /dev/null +++ b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java @@ -0,0 +1,292 @@ +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.NicknameUpdateRequest; +import com.example.solidconnection.siteuser.repository.LikedUniversityRepository; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.support.integration.BaseIntegrationTest; +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 com.example.solidconnection.university.domain.LikedUniversity; +import com.example.solidconnection.university.dto.UniversityInfoForApplyPreviewResponse; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; + +import java.time.LocalDateTime; +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.PROFILE_IMAGE_NEEDED; +import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; +import static com.example.solidconnection.siteuser.service.MyPageService.NICKNAME_LAST_CHANGE_DATE_FORMAT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.mockito.BDDMockito.any; +import static org.mockito.BDDMockito.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.never; +import static org.mockito.BDDMockito.then; + +@DisplayName("마이페이지 서비스 테스트") +class MyPageServiceTest extends BaseIntegrationTest { + + @Autowired + private MyPageService myPageService; + + @MockBean + private S3Service s3Service; + + @Autowired + private SiteUserRepository siteUserRepository; + + @Autowired + private LikedUniversityRepository likedUniversityRepository; + + @Test + void 마이페이지_정보를_조회한다() { + // given + SiteUser testUser = createSiteUser(); + int likedUniversityCount = createLikedUniversities(testUser); + + // when + MyPageResponse response = myPageService.getMyPageInfo(testUser); + + // then + Assertions.assertAll( + () -> assertThat(response.nickname()).isEqualTo(testUser.getNickname()), + () -> assertThat(response.profileImageUrl()).isEqualTo(testUser.getProfileImageUrl()), + () -> assertThat(response.role()).isEqualTo(testUser.getRole()), + () -> assertThat(response.birth()).isEqualTo(testUser.getBirth()), + () -> assertThat(response.email()).isEqualTo(testUser.getEmail()), + () -> assertThat(response.likedPostCount()).isEqualTo(testUser.getPostLikeList().size()), + () -> assertThat(response.likedUniversityCount()).isEqualTo(likedUniversityCount) + ); + } + + @Test + void 관심_대학교_목록을_조회한다() { + // given + SiteUser testUser = createSiteUser(); + int likedUniversityCount = createLikedUniversities(testUser); + + // when + List response = myPageService.getWishUniversity(testUser); + + // then + assertThat(response) + .hasSize(likedUniversityCount) + .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id") + .containsAll(List.of( + UniversityInfoForApplyPreviewResponse.from(괌대학_A_지원_정보), + UniversityInfoForApplyPreviewResponse.from(메이지대학_지원_정보), + UniversityInfoForApplyPreviewResponse.from(코펜하겐IT대학_지원_정보) + )); + } + + @Nested + class 프로필_이미지_수정_테스트 { + + @Test + void 새로운_이미지로_성공적으로_업데이트한다() { + // given + SiteUser testUser = createSiteUser(); + String expectedUrl = "newProfileImageUrl"; + MockMultipartFile imageFile = createValidImageFile(); + given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) + .willReturn(new UploadedFileUrlResponse(expectedUrl)); + + // when + myPageService.updateMyPageInfo(testUser, imageFile, "newNickname"); + + // then + assertThat(testUser.getProfileImageUrl()).isEqualTo(expectedUrl); + } + + @Test + void 프로필을_처음_수정하는_것이면_이전_이미지를_삭제하지_않는다() { + // given + SiteUser testUser = createSiteUser(); + MockMultipartFile imageFile = createValidImageFile(); + given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) + .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); + + // when + myPageService.updateMyPageInfo(testUser, imageFile, "newNickname"); + + // then + then(s3Service).should(never()).deleteExProfile(any()); + } + + @Test + void 프로필을_처음_수정하는_것이_아니라면_이전_이미지를_삭제한다() { + // given + SiteUser testUser = createSiteUserWithCustomProfile(); + MockMultipartFile imageFile = createValidImageFile(); + given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) + .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); + + // when + myPageService.updateMyPageInfo(testUser, imageFile, "newNickname"); + + // then + then(s3Service).should().deleteExProfile(testUser); + } + + @Test + void 빈_이미지_파일로_프로필을_수정하면_예외_응답을_반환한다() { + // given + SiteUser testUser = createSiteUser(); + MockMultipartFile emptyFile = createEmptyImageFile(); + + // when & then + assertThatCode(() -> myPageService.updateMyPageInfo(testUser, emptyFile, "newNickname")) + .isInstanceOf(CustomException.class) + .hasMessage(PROFILE_IMAGE_NEEDED.getMessage()); + } + } + + @Nested + class 닉네임_수정_테스트 { + + @BeforeEach + void setUp() { + given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) + .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); + } + + @Test + void 닉네임을_성공적으로_수정한다() { + // given + SiteUser testUser = createSiteUser(); + MockMultipartFile imageFile = createValidImageFile(); + String newNickname = "newNickname"; + + // when + myPageService.updateMyPageInfo(testUser, imageFile, newNickname); + + // then + SiteUser updatedUser = siteUserRepository.findById(testUser.getId()).get(); + assertThat(updatedUser.getNicknameModifiedAt()).isNotNull(); + assertThat(updatedUser.getNickname()).isEqualTo(newNickname); + } + + @Test + void 중복된_닉네임으로_변경하면_예외_응답을_반환한다() { + // given + createDuplicatedSiteUser(); + SiteUser testUser = createSiteUser(); + MockMultipartFile imageFile = createValidImageFile(); + + // when & then + assertThatCode(() -> myPageService.updateMyPageInfo(testUser, imageFile, "duplicatedNickname")) + .isInstanceOf(CustomException.class) + .hasMessage(NICKNAME_ALREADY_EXISTED.getMessage()); + } + + @Test + void 최소_대기기간이_지나지_않은_상태에서_변경하면_예외_응답을_반환한다() { + // given + SiteUser testUser = createSiteUser(); + MockMultipartFile imageFile = createValidImageFile(); + LocalDateTime modifiedAt = LocalDateTime.now().minusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES - 1); + testUser.setNicknameModifiedAt(modifiedAt); + siteUserRepository.save(testUser); + + NicknameUpdateRequest request = new NicknameUpdateRequest("newNickname"); + + // when & then + assertThatCode(() -> myPageService.updateMyPageInfo(testUser, imageFile, "nickname12")) + .isInstanceOf(CustomException.class) + .hasMessage(createExpectedErrorMessage(modifiedAt)); + } + } + + private SiteUser createSiteUser() { + SiteUser siteUser = new SiteUser( + "test@example.com", + "nickname", + "profileImageUrl", + "1999-01-01", + PreparationStatus.CONSIDERING, + Role.MENTEE, + Gender.MALE + ); + return siteUserRepository.save(siteUser); + } + + private SiteUser createSiteUserWithCustomProfile() { + SiteUser siteUser = new SiteUser( + "test@example.com", + "nickname", + "profile/profileImageUrl", + "1999-01-01", + PreparationStatus.CONSIDERING, + Role.MENTEE, + Gender.MALE + ); + return siteUserRepository.save(siteUser); + } + + private void createDuplicatedSiteUser() { + SiteUser siteUser = new SiteUser( + "duplicated@example.com", + "duplicatedNickname", + "profileImageUrl", + "1999-01-01", + PreparationStatus.CONSIDERING, + Role.MENTEE, + Gender.MALE + ); + siteUserRepository.save(siteUser); + } + + private int createLikedUniversities(SiteUser testUser) { + LikedUniversity likedUniversity1 = new LikedUniversity(null, 괌대학_A_지원_정보, testUser); + LikedUniversity likedUniversity2 = new LikedUniversity(null, 메이지대학_지원_정보, testUser); + LikedUniversity likedUniversity3 = new LikedUniversity(null, 코펜하겐IT대학_지원_정보, testUser); + + likedUniversityRepository.save(likedUniversity1); + likedUniversityRepository.save(likedUniversity2); + likedUniversityRepository.save(likedUniversity3); + return likedUniversityRepository.countBySiteUser_Id(testUser.getId()); + } + + private MockMultipartFile createValidImageFile() { + return new MockMultipartFile( + "image", + "test.jpg", + "image/jpeg", + "test image content".getBytes() + ); + } + + private MockMultipartFile createEmptyImageFile() { + return new MockMultipartFile( + "image", + "empty.jpg", + "image/jpeg", + new byte[0] + ); + } + + private String createExpectedErrorMessage(LocalDateTime modifiedAt) { + String formatLastModifiedAt = String.format( + "(마지막 수정 시간 : %s)", + NICKNAME_LAST_CHANGE_DATE_FORMAT.format(modifiedAt) + ); + return CAN_NOT_CHANGE_NICKNAME_YET.getMessage() + " : " + formatLastModifiedAt; + } +} diff --git a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java index 819f33156..7272b3498 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java @@ -1,43 +1,19 @@ 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.NicknameUpdateRequest; -import com.example.solidconnection.siteuser.repository.LikedUniversityRepository; +import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import com.example.solidconnection.support.integration.BaseIntegrationTest; 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 com.example.solidconnection.university.domain.LikedUniversity; -import com.example.solidconnection.university.dto.UniversityInfoForApplyPreviewResponse; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.mock.web.MockMultipartFile; -import java.time.LocalDateTime; -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.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; -import static com.example.solidconnection.siteuser.service.SiteUserService.NICKNAME_LAST_CHANGE_DATE_FORMAT; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; -import static org.mockito.BDDMockito.any; -import static org.mockito.BDDMockito.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.never; -import static org.mockito.BDDMockito.then; @DisplayName("유저 서비스 테스트") class SiteUserServiceTest extends BaseIntegrationTest { @@ -45,160 +21,35 @@ class SiteUserServiceTest extends BaseIntegrationTest { @Autowired private SiteUserService siteUserService; - @MockBean - private S3Service s3Service; - @Autowired private SiteUserRepository siteUserRepository; - @Autowired - private LikedUniversityRepository likedUniversityRepository; - - @Test - void 마이페이지_정보를_조회한다() { - // given - SiteUser testUser = createSiteUser(); - int likedUniversityCount = createLikedUniversities(testUser); - - // when - MyPageResponse response = siteUserService.getMyPageInfo(testUser); - - // then - Assertions.assertAll( - () -> assertThat(response.nickname()).isEqualTo(testUser.getNickname()), - () -> assertThat(response.profileImageUrl()).isEqualTo(testUser.getProfileImageUrl()), - () -> assertThat(response.role()).isEqualTo(testUser.getRole()), - () -> assertThat(response.authType()).isEqualTo(testUser.getAuthType()), - () -> assertThat(response.birth()).isEqualTo(testUser.getBirth()), - () -> assertThat(response.email()).isEqualTo(testUser.getEmail()), - () -> assertThat(response.likedPostCount()).isEqualTo(testUser.getPostLikeList().size()), - () -> assertThat(response.likedUniversityCount()).isEqualTo(likedUniversityCount) - ); - } - - @Test - void 관심_대학교_목록을_조회한다() { - // given - SiteUser testUser = createSiteUser(); - int likedUniversityCount = createLikedUniversities(testUser); + private SiteUser siteUser; - // when - List response = siteUserService.getWishUniversity(testUser); - - // then - assertThat(response) - .hasSize(likedUniversityCount) - .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id") - .containsAll(List.of( - UniversityInfoForApplyPreviewResponse.from(괌대학_A_지원_정보), - UniversityInfoForApplyPreviewResponse.from(메이지대학_지원_정보), - UniversityInfoForApplyPreviewResponse.from(코펜하겐IT대학_지원_정보) - )); + @BeforeEach + void setUp() { + siteUser = createSiteUser(); } @Nested - class 프로필_이미지_수정_테스트 { + class 닉네임_중복_검사 { @Test - void 새로운_이미지로_성공적으로_업데이트한다() { - // given - SiteUser testUser = createSiteUser(); - String expectedUrl = "newProfileImageUrl"; - MockMultipartFile imageFile = createValidImageFile(); - given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) - .willReturn(new UploadedFileUrlResponse(expectedUrl)); - + void 존재하는_닉네임이면_true를_반환한다() { // when - siteUserService.updateMyPageInfo(testUser, imageFile, "newNickname"); + NicknameExistsResponse response = siteUserService.checkNicknameExists(siteUser.getNickname()); // then - assertThat(testUser.getProfileImageUrl()).isEqualTo(expectedUrl); + assertThat(response.exists()).isTrue(); } @Test - void 프로필을_처음_수정하는_것이면_이전_이미지를_삭제하지_않는다() { - // given - SiteUser testUser = createSiteUser(); - MockMultipartFile imageFile = createValidImageFile(); - given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) - .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); - + void 존재하지_않는_닉네임이면_false를_반환한다() { // when - siteUserService.updateMyPageInfo(testUser, imageFile, "newNickname"); + NicknameExistsResponse response = siteUserService.checkNicknameExists("nonExistingNickname"); // then - then(s3Service).should(never()).deleteExProfile(any()); - } - - @Test - void 프로필을_처음_수정하는_것이_아니라면_이전_이미지를_삭제한다() { - // given - SiteUser testUser = createSiteUserWithCustomProfile(); - MockMultipartFile imageFile = createValidImageFile(); - given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) - .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); - - // when - siteUserService.updateMyPageInfo(testUser, imageFile, "newNickname"); - - // then - then(s3Service).should().deleteExProfile(testUser); - } - } - - @Nested - class 닉네임_수정_테스트 { - - @BeforeEach - void setUp() { - given(s3Service.uploadFile(any(), eq(ImgType.PROFILE))) - .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); - } - - @Test - void 닉네임을_성공적으로_수정한다() { - // given - SiteUser testUser = createSiteUser(); - MockMultipartFile imageFile = createValidImageFile(); - String newNickname = "newNickname"; - - // when - siteUserService.updateMyPageInfo(testUser, imageFile, newNickname); - - // then - SiteUser updatedUser = siteUserRepository.findById(testUser.getId()).get(); - assertThat(updatedUser.getNicknameModifiedAt()).isNotNull(); - assertThat(updatedUser.getNickname()).isEqualTo(newNickname); - } - - @Test - void 중복된_닉네임으로_변경하면_예외_응답을_반환한다() { - // given - createDuplicatedSiteUser(); - SiteUser testUser = createSiteUser(); - MockMultipartFile imageFile = createValidImageFile(); - - // when & then - assertThatCode(() -> siteUserService.updateMyPageInfo(testUser, imageFile, "duplicatedNickname")) - .isInstanceOf(CustomException.class) - .hasMessage(NICKNAME_ALREADY_EXISTED.getMessage()); - } - - @Test - void 최소_대기기간이_지나지_않은_상태에서_변경하면_예외_응답을_반환한다() { - // given - SiteUser testUser = createSiteUser(); - MockMultipartFile imageFile = createValidImageFile(); - LocalDateTime modifiedAt = LocalDateTime.now().minusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES - 1); - testUser.setNicknameModifiedAt(modifiedAt); - siteUserRepository.save(testUser); - - NicknameUpdateRequest request = new NicknameUpdateRequest("newNickname"); - - // when & then - assertThatCode(() -> siteUserService.updateMyPageInfo(testUser, imageFile, "nickname12")) - .isInstanceOf(CustomException.class) - .hasMessage(createExpectedErrorMessage(modifiedAt)); + assertThat(response.exists()).isFalse(); } } @@ -214,58 +65,4 @@ private SiteUser createSiteUser() { ); return siteUserRepository.save(siteUser); } - - private SiteUser createSiteUserWithCustomProfile() { - SiteUser siteUser = new SiteUser( - "test@example.com", - "nickname", - "profile/profileImageUrl", - "1999-01-01", - PreparationStatus.CONSIDERING, - Role.MENTEE, - Gender.MALE - ); - return siteUserRepository.save(siteUser); - } - - private void createDuplicatedSiteUser() { - SiteUser siteUser = new SiteUser( - "duplicated@example.com", - "duplicatedNickname", - "profileImageUrl", - "1999-01-01", - PreparationStatus.CONSIDERING, - Role.MENTEE, - Gender.MALE - ); - siteUserRepository.save(siteUser); - } - - private int createLikedUniversities(SiteUser testUser) { - LikedUniversity likedUniversity1 = new LikedUniversity(null, 괌대학_A_지원_정보, testUser); - LikedUniversity likedUniversity2 = new LikedUniversity(null, 메이지대학_지원_정보, testUser); - LikedUniversity likedUniversity3 = new LikedUniversity(null, 코펜하겐IT대학_지원_정보, testUser); - - likedUniversityRepository.save(likedUniversity1); - likedUniversityRepository.save(likedUniversity2); - likedUniversityRepository.save(likedUniversity3); - return likedUniversityRepository.countBySiteUser_Id(testUser.getId()); - } - - private MockMultipartFile createValidImageFile() { - return new MockMultipartFile( - "image", - "test.jpg", - "image/jpeg", - "test image content".getBytes() - ); - } - - private String createExpectedErrorMessage(LocalDateTime modifiedAt) { - String formatLastModifiedAt = String.format( - "(마지막 수정 시간 : %s)", - NICKNAME_LAST_CHANGE_DATE_FORMAT.format(modifiedAt) - ); - return CAN_NOT_CHANGE_NICKNAME_YET.getMessage() + " : " + formatLastModifiedAt; - } }