From 89c2bceddb207d0406d1507f469bd35bd20c55fc Mon Sep 17 00:00:00 2001 From: Yeongseo Na Date: Fri, 14 Feb 2025 11:18:17 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20=EB=82=B4=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EC=99=80=20=EB=8C=80=ED=95=99=20=EA=B4=80=EB=A0=A8=20api=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 대학 uri 복수형으로 수정 * refactor: 개발 환경 테스트 데이터 추가 * refactor: 사용자 정보 uri 수정 * chore: 의미 없는 throws 제거 * refactor: 위시 학교 추가, 삭제 api 분리 * chore: 사용하지 않은 의존성 제거 --- .../auth/service/EmailSignInService.java | 2 +- .../custom/exception/ErrorCode.java | 2 + .../controller/SiteUserController.java | 36 +---- .../siteuser/service/SiteUserService.java | 87 +++++------- .../controller/UniversityController.java | 16 ++- .../service/UniversityLikeService.java | 28 +++- src/main/resources/data.sql | 5 + .../solidconnection/e2e/MyPageTest.java | 2 +- .../solidconnection/e2e/MyPageUpdateTest.java | 133 ------------------ .../e2e/UniversityDetailTest.java | 2 +- .../e2e/UniversityLikeTest.java | 29 +--- .../e2e/UniversityRecommendTest.java | 10 +- .../e2e/UniversitySearchTest.java | 14 +- .../siteuser/service/SiteUserServiceTest.java | 61 +++----- .../service/UniversityLikeServiceTest.java | 91 ++++++++---- 15 files changed, 188 insertions(+), 330 deletions(-) delete mode 100644 src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java diff --git a/src/main/java/com/example/solidconnection/auth/service/EmailSignInService.java b/src/main/java/com/example/solidconnection/auth/service/EmailSignInService.java index bbbb4f85c..3e26309a5 100644 --- a/src/main/java/com/example/solidconnection/auth/service/EmailSignInService.java +++ b/src/main/java/com/example/solidconnection/auth/service/EmailSignInService.java @@ -35,7 +35,7 @@ public SignInResponse signIn(EmailSignInRequest signInRequest) { throw new CustomException(USER_NOT_FOUND, "이메일과 비밀번호를 확인해주세요."); } - private void validatePassword(String rawPassword, String encodedPassword) throws CustomException { + private void validatePassword(String rawPassword, String encodedPassword) { if (!passwordEncoder.matches(rawPassword, encodedPassword)) { throw new CustomException(USER_NOT_FOUND, "이메일과 비밀번호를 확인해주세요."); } 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 8c74ea55b..dff7b13f0 100644 --- a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java @@ -84,6 +84,8 @@ public enum ErrorCode { CAN_NOT_UPDATE_DEPRECATED_COMMENT(HttpStatus.BAD_REQUEST.value(), "이미 삭제된 댓글을 수정할 수 없습니다."), INVALID_POST_LIKE(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 게시글 좋아요입니다."), DUPLICATE_POST_LIKE(HttpStatus.BAD_REQUEST.value(), "이미 좋아요한 게시글입니다."), + ALREADY_LIKED_UNIVERSITY(HttpStatus.BAD_REQUEST.value(), "이미 좋아요한 대학입니다."), + NOT_LIKED_UNIVERSITY(HttpStatus.BAD_REQUEST.value(), "좋아요하지 않은 대학입니다."), // score INVALID_GPA_SCORE(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 학점입니다."), 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 11c154243..2f43337ed 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java @@ -3,24 +3,18 @@ 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.MyPageUpdateResponse; -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.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.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RequiredArgsConstructor -@RequestMapping("/my-page") +@RequestMapping("/my") @RestController class SiteUserController { @@ -34,29 +28,13 @@ public ResponseEntity getMyPageInfo( return ResponseEntity.ok(myPageResponse); } - @GetMapping("/update") - public ResponseEntity getMyPageInfoToUpdate( - @AuthorizedUser SiteUser siteUser - ) { - MyPageUpdateResponse myPageUpdateDto = siteUserService.getMyPageInfoToUpdate(siteUser); - return ResponseEntity.ok(myPageUpdateDto); - } - - @PatchMapping("/update/profileImage") - public ResponseEntity updateProfileImage( - @AuthorizedUser SiteUser siteUser, - @RequestParam(value = "file", required = false) MultipartFile imageFile - ) { - ProfileImageUpdateResponse profileImageUpdateResponse = siteUserService.updateProfileImage(siteUser, imageFile); - return ResponseEntity.ok().body(profileImageUpdateResponse); - } - - @PatchMapping("/update/nickname") - public ResponseEntity updateNickname( + @PatchMapping + public ResponseEntity updateMyPageInfo( @AuthorizedUser SiteUser siteUser, - @Valid @RequestBody NicknameUpdateRequest nicknameUpdateRequest + @RequestParam("file") MultipartFile imageFile, + @RequestParam("nickname") String nickname ) { - NicknameUpdateResponse nicknameUpdateResponse = siteUserService.updateNickname(siteUser, nicknameUpdateRequest); - return ResponseEntity.ok().body(nicknameUpdateResponse); + siteUserService.updateMyPageInfo(siteUser, imageFile, nickname); + return ResponseEntity.ok().build(); } } 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 c181c2809..30297283a 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -5,10 +5,6 @@ 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.MyPageUpdateResponse; -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.type.ImgType; @@ -48,68 +44,27 @@ public MyPageResponse getMyPageInfo(SiteUser siteUser) { } /* - * 내 정보를 수정하기 위한 마이페이지 정보를 조회한다. (닉네임, 프로필 사진) - * */ - @Transactional(readOnly = true) - public MyPageUpdateResponse getMyPageInfoToUpdate(SiteUser siteUser) { - return MyPageUpdateResponse.from(siteUser); - } - - /* - * 관심 대학교 목록을 조회한다. - * */ - @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(); - } - - /* - * 프로필 이미지를 수정한다. + * 마이페이지 정보를 수정한다. * */ @Transactional - public ProfileImageUpdateResponse updateProfileImage(SiteUser siteUser, MultipartFile imageFile) { - validateProfileImage(imageFile); + public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { + validateNicknameUnique(nickname); + validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); + validateProfileImageNotEmpty(imageFile); - // 프로필 이미지를 처음 수정하는 경우에는 deleteExProfile 수행하지 않음 if (!isDefaultProfileImage(siteUser.getProfileImageUrl())) { s3Service.deleteExProfile(siteUser); } - 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); - } - } - private boolean isDefaultProfileImage(String profileImageUrl) { - String prefix = "profile/"; - return profileImageUrl == null || !profileImageUrl.startsWith(prefix); - } + UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(imageFile, ImgType.PROFILE); + String profileImageUrl = uploadedFile.fileUrl(); - /* - * 닉네임을 수정한다. - * */ - @Transactional - public NicknameUpdateResponse updateNickname(SiteUser siteUser, NicknameUpdateRequest nicknameUpdateRequest) { - validateNicknameDuplicated(nicknameUpdateRequest.nickname()); - validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); - - siteUser.setNickname(nicknameUpdateRequest.nickname()); + siteUser.setProfileImageUrl(profileImageUrl); + siteUser.setNickname(nickname); siteUser.setNicknameModifiedAt(LocalDateTime.now()); siteUserRepository.save(siteUser); - - return NicknameUpdateResponse.from(siteUser); } - private void validateNicknameDuplicated(String nickname) { + private void validateNicknameUnique(String nickname) { if (siteUserRepository.existsByNickname(nickname)) { throw new CustomException(NICKNAME_ALREADY_EXISTED); } @@ -125,4 +80,26 @@ private void validateNicknameNotChangedRecently(LocalDateTime 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/university/controller/UniversityController.java b/src/main/java/com/example/solidconnection/university/controller/UniversityController.java index 505bfe072..66da87095 100644 --- a/src/main/java/com/example/solidconnection/university/controller/UniversityController.java +++ b/src/main/java/com/example/solidconnection/university/controller/UniversityController.java @@ -14,6 +14,7 @@ import com.example.solidconnection.university.service.UniversityRecommendService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -24,7 +25,7 @@ import java.util.List; @RequiredArgsConstructor -@RequestMapping("/university") +@RequestMapping("/universities") @RestController public class UniversityController { @@ -33,7 +34,7 @@ public class UniversityController { private final UniversityRecommendService universityRecommendService; private final SiteUserService siteUserService; - @GetMapping("/recommends") + @GetMapping("/recommend") public ResponseEntity getUniversityRecommends( @AuthorizedUser SiteUser siteUser ) { @@ -70,7 +71,16 @@ public ResponseEntity addWishUniversity( return ResponseEntity.ok(likeResultResponse); } - @GetMapping("/detail/{universityInfoForApplyId}") + @DeleteMapping("/{universityInfoForApplyId}/like") + public ResponseEntity cancelWishUniversity( + @AuthorizedUser SiteUser siteUser, + @PathVariable Long universityInfoForApplyId + ) { + LikeResultResponse likeResultResponse = universityLikeService.cancelLikeUniversity(siteUser, universityInfoForApplyId); + return ResponseEntity.ok(likeResultResponse); + } + + @GetMapping("/{universityInfoForApplyId}") public ResponseEntity getUniversityDetails( @PathVariable Long universityInfoForApplyId ) { diff --git a/src/main/java/com/example/solidconnection/university/service/UniversityLikeService.java b/src/main/java/com/example/solidconnection/university/service/UniversityLikeService.java index d926bc516..85971663b 100644 --- a/src/main/java/com/example/solidconnection/university/service/UniversityLikeService.java +++ b/src/main/java/com/example/solidconnection/university/service/UniversityLikeService.java @@ -1,5 +1,6 @@ package com.example.solidconnection.university.service; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.repository.LikedUniversityRepository; import com.example.solidconnection.university.domain.LikedUniversity; @@ -14,6 +15,9 @@ import java.util.Optional; +import static com.example.solidconnection.custom.exception.ErrorCode.ALREADY_LIKED_UNIVERSITY; +import static com.example.solidconnection.custom.exception.ErrorCode.NOT_LIKED_UNIVERSITY; + @RequiredArgsConstructor @Service public class UniversityLikeService { @@ -29,16 +33,14 @@ public class UniversityLikeService { /* * 대학교를 '좋아요' 한다. - * - 이미 좋아요가 눌러져있다면, 좋아요를 취소한다. * */ @Transactional public LikeResultResponse likeUniversity(SiteUser siteUser, Long universityInfoForApplyId) { UniversityInfoForApply universityInfoForApply = universityInfoForApplyRepository.getUniversityInfoForApplyById(universityInfoForApplyId); - Optional alreadyLikedUniversity = likedUniversityRepository.findBySiteUserAndUniversityInfoForApply(siteUser, universityInfoForApply); - if (alreadyLikedUniversity.isPresent()) { - likedUniversityRepository.delete(alreadyLikedUniversity.get()); - return new LikeResultResponse(LIKE_CANCELED_MESSAGE); + Optional optionalLikedUniversity = likedUniversityRepository.findBySiteUserAndUniversityInfoForApply(siteUser, universityInfoForApply); + if (optionalLikedUniversity.isPresent()) { + throw new CustomException(ALREADY_LIKED_UNIVERSITY); } LikedUniversity likedUniversity = LikedUniversity.builder() @@ -49,6 +51,22 @@ public LikeResultResponse likeUniversity(SiteUser siteUser, Long universityInfoF return new LikeResultResponse(LIKE_SUCCESS_MESSAGE); } + /* + * 대학교 '좋아요'를 취소한다. + * */ + @Transactional + public LikeResultResponse cancelLikeUniversity(SiteUser siteUser, long universityInfoForApplyId) throws CustomException { + UniversityInfoForApply universityInfoForApply = universityInfoForApplyRepository.getUniversityInfoForApplyById(universityInfoForApplyId); + + Optional optionalLikedUniversity = likedUniversityRepository.findBySiteUserAndUniversityInfoForApply(siteUser, universityInfoForApply); + if (optionalLikedUniversity.isEmpty()) { + throw new CustomException(NOT_LIKED_UNIVERSITY); + } + + likedUniversityRepository.delete(optionalLikedUniversity.get()); + return new LikeResultResponse(LIKE_CANCELED_MESSAGE); + } + /* * '좋아요'한 대학교인지 확인한다. * */ diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 477fc03f5..5ae08770d 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -41,6 +41,11 @@ VALUES ('BN', '브루나이', 'ASIA'), ('MY', '말레이시아', 'ASIA'), ('RU', '러시아', 'EUROPE'); +INSERT INTO site_user (birth, email, nickname, profile_image_url, gender, preparation_stage, role, password, auth_type) +VALUES ('1999-01-01', 'test@test.email', 'yonso','https://github.com/nayonsoso.png', + 'FEMALE', 'CONSIDERING', 'MENTEE', + '$2a$10$psmwlxPfqWnIlq9JrlQJkuXr1XtjRNsyVOgcTWYZub5jFfn0TML76', 'EMAIL'); -- 12341234 + INSERT INTO university(id, country_code, region_code, english_name, format_name, korean_name, accommodation_url, english_course_url, homepage_url, details_for_local, logo_image_url, background_image_url) diff --git a/src/test/java/com/example/solidconnection/e2e/MyPageTest.java b/src/test/java/com/example/solidconnection/e2e/MyPageTest.java index 7a0ae07f4..dd2ce1e3a 100644 --- a/src/test/java/com/example/solidconnection/e2e/MyPageTest.java +++ b/src/test/java/com/example/solidconnection/e2e/MyPageTest.java @@ -44,7 +44,7 @@ public void setUpUserAndToken() { MyPageResponse myPageResponse = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/my-page") + .get("/my") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(MyPageResponse.class); diff --git a/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java b/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java deleted file mode 100644 index b16f3b822..000000000 --- a/src/test/java/com/example/solidconnection/e2e/MyPageUpdateTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.example.solidconnection.e2e; - -import com.example.solidconnection.auth.service.AuthTokenProvider; -import com.example.solidconnection.custom.response.ErrorResponse; -import com.example.solidconnection.siteuser.domain.SiteUser; -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; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; - -import java.time.LocalDateTime; - -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.e2e.DynamicFixture.createSiteUserByEmail; -import static com.example.solidconnection.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -@DisplayName("마이페이지 수정 테스트") -class MyPageUpdateTest extends BaseEndToEndTest { - - @Autowired - private SiteUserRepository siteUserRepository; - - @Autowired - private AuthTokenProvider authTokenProvider; - - private String accessToken; - - private SiteUser siteUser; - - @BeforeEach - public void setUpUserAndToken() { - // setUp - 회원 정보 저장 - siteUser = createSiteUserByEmail("email"); - siteUserRepository.save(siteUser); - - // setUp - 엑세스 토큰 생성과 리프레시 토큰 생성 및 저장 - accessToken = authTokenProvider.generateAccessToken(siteUser); - authTokenProvider.generateAndSaveRefreshToken(siteUser); - } - - @Test - void 수정을_위해_수정_전_정보를_조회한다() { - // request - 요청 - MyPageUpdateResponse myPageUpdateResponse = RestAssured.given() - .header("Authorization", "Bearer " + accessToken) - .log().all() - .get("/my-page/update") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract().as(MyPageUpdateResponse.class); - - assertAll("불러온 마이 페이지 정보가 DB의 정보와 일치한다.", - () -> assertThat(myPageUpdateResponse.nickname()).isEqualTo(siteUser.getNickname()), - () -> assertThat(myPageUpdateResponse.profileImageUrl()).isEqualTo(siteUser.getProfileImageUrl())); - } - - @Test - void 닉네임을_수정한다() { - // request - body 생성 및 요청 - NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); - NicknameUpdateResponse nicknameUpdateResponse = RestAssured.given() - .header("Authorization", "Bearer " + accessToken) - .log().all() - .body(nicknameUpdateRequest) - .contentType("application/json") - .patch("/my-page/update/nickname") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract().as(NicknameUpdateResponse.class); - - SiteUser updatedSiteUser = siteUserRepository.findById(siteUser.getId()).get(); - assertAll("마이 페이지 정보가 수정된다.", - () -> assertThat(nicknameUpdateResponse.nickname()).isEqualTo(updatedSiteUser.getNickname())); - } - - @Test - void 닉네임을_수정할_때_닉네임이_중복된다면_예외_응답을_반환한다() { - // setUp - 같은 닉네임을 갖는 다른 회원 정보 저장 - SiteUser existUser = createSiteUserByEmail("existUser"); - String duplicateNickname = "duplicateNickname"; - existUser.setNickname(duplicateNickname); - siteUserRepository.save(existUser); - - // request - body 생성 및 요청 - NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("duplicateNickname"); - ErrorResponse response = RestAssured.given() - .header("Authorization", "Bearer " + accessToken) - .log().all() - .body(nicknameUpdateRequest) - .contentType("application/json") - .patch("/my-page/update/nickname") - .then().log().all() - .statusCode(HttpStatus.CONFLICT.value()) - .extract().as(ErrorResponse.class); - - assertThat(response.message()) - .isEqualTo(NICKNAME_ALREADY_EXISTED.getMessage()); - } - - @Test - void 닉네임을_수정할_때_닉네임_변경_가능_기한이_지나지않았다면_예외_응답을_반환한다() { - // setUp - 회원 정보 저장 (닉네임 변경 가능 시간이 되기 1분 전) - LocalDateTime nicknameModifiedAt = LocalDateTime.now() - .minusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES) - .plusMinutes(1); - siteUser.setNicknameModifiedAt(nicknameModifiedAt); - siteUserRepository.save(siteUser); - - // request - body 생성 및 요청 - NicknameUpdateRequest nicknameUpdateRequest = new NicknameUpdateRequest("newNickname"); - ErrorResponse response = RestAssured.given() - .header("Authorization", "Bearer " + accessToken) - .log().all() - .body(nicknameUpdateRequest) - .contentType("application/json") - .patch("/my-page/update/nickname") - .then().log().all() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract().as(ErrorResponse.class); - - assertThat(response.message()) - .contains(CAN_NOT_CHANGE_NICKNAME_YET.getMessage()); - } -} diff --git a/src/test/java/com/example/solidconnection/e2e/UniversityDetailTest.java b/src/test/java/com/example/solidconnection/e2e/UniversityDetailTest.java index 01b2b5730..c7a364ebf 100644 --- a/src/test/java/com/example/solidconnection/e2e/UniversityDetailTest.java +++ b/src/test/java/com/example/solidconnection/e2e/UniversityDetailTest.java @@ -45,7 +45,7 @@ public void setUpUserAndToken() { UniversityDetailResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/university/detail/" + 메이지대학_지원_정보.getId()) + .get("/universities/" + 메이지대학_지원_정보.getId()) .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(UniversityDetailResponse.class); diff --git a/src/test/java/com/example/solidconnection/e2e/UniversityLikeTest.java b/src/test/java/com/example/solidconnection/e2e/UniversityLikeTest.java index 3b5733d82..693d6d91b 100644 --- a/src/test/java/com/example/solidconnection/e2e/UniversityLikeTest.java +++ b/src/test/java/com/example/solidconnection/e2e/UniversityLikeTest.java @@ -24,7 +24,6 @@ import static com.example.solidconnection.e2e.DynamicFixture.createLikedUniversity; import static com.example.solidconnection.e2e.DynamicFixture.createSiteUserByEmail; import static com.example.solidconnection.e2e.DynamicFixture.createUniversityForApply; -import static com.example.solidconnection.university.service.UniversityLikeService.LIKE_CANCELED_MESSAGE; import static com.example.solidconnection.university.service.UniversityLikeService.LIKE_SUCCESS_MESSAGE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @@ -73,7 +72,7 @@ public void setUpUserAndToken() { List wishUniversities = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/university/like") + .get("/universities/like") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -92,7 +91,7 @@ public void setUpUserAndToken() { LikeResultResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .post("/university/" + 괌대학_A_지원_정보.getId() + "/like") + .post("/universities/" + 괌대학_A_지원_정보.getId() + "/like") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(LikeResultResponse.class); @@ -106,28 +105,6 @@ public void setUpUserAndToken() { ); } - @Test - void 이미_좋아요한_대학교에_좋아요를_누른다() { - // setUp - 대학교 좋아요 저장 - likedUniversityRepository.save(createLikedUniversity(siteUser, 괌대학_A_지원_정보)); - - // request - 요청 - LikeResultResponse response = RestAssured.given() - .header("Authorization", "Bearer " + accessToken) - .log().all() - .post("/university/" + 괌대학_A_지원_정보.getId() + "/like") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract().as(LikeResultResponse.class); - - Optional likedUniversity - = likedUniversityRepository.findAllBySiteUser_Id(siteUser.getId()).stream().findFirst(); - assertAll("좋아요 누른 대학교를 삭제하고, 좋아요 취소 응답을 반환한다.", - () -> assertThat(likedUniversity).isEmpty(), - () -> assertThat(response.result()).isEqualTo(LIKE_CANCELED_MESSAGE) - ); - } - @Test void 대학의_좋아요_여부를_조회한다() { // setUp - 대학교 좋아요 저장 @@ -136,7 +113,7 @@ public void setUpUserAndToken() { // request - 요청 IsLikeResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .get("/university/" + 괌대학_A_지원_정보.getId() + "/like") + .get("/universities/" + 괌대학_A_지원_정보.getId() + "/like") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(IsLikeResponse.class); diff --git a/src/test/java/com/example/solidconnection/e2e/UniversityRecommendTest.java b/src/test/java/com/example/solidconnection/e2e/UniversityRecommendTest.java index 8e1e8184f..9939d1a54 100644 --- a/src/test/java/com/example/solidconnection/e2e/UniversityRecommendTest.java +++ b/src/test/java/com/example/solidconnection/e2e/UniversityRecommendTest.java @@ -66,7 +66,7 @@ void setUp() { UniversityRecommendsResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/university/recommends") + .get("/universities/recommend") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(UniversityRecommendsResponse.class); @@ -94,7 +94,7 @@ void setUp() { UniversityRecommendsResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/university/recommends") + .get("/universities/recommend") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(UniversityRecommendsResponse.class); @@ -121,7 +121,7 @@ void setUp() { UniversityRecommendsResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/university/recommends") + .get("/universities/recommend") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(UniversityRecommendsResponse.class); @@ -148,7 +148,7 @@ void setUp() { UniversityRecommendsResponse response = RestAssured.given() .header("Authorization", "Bearer " + accessToken) .log().all() - .get("/university/recommends") + .get("/universities/recommend") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(UniversityRecommendsResponse.class); @@ -171,7 +171,7 @@ void setUp() { // request - 요청 UniversityRecommendsResponse response = RestAssured.given() .log().all() - .get("/university/recommends") + .get("/universities/recommend") .then().log().all() .statusCode(HttpStatus.OK.value()) .extract().as(UniversityRecommendsResponse.class); diff --git a/src/test/java/com/example/solidconnection/e2e/UniversitySearchTest.java b/src/test/java/com/example/solidconnection/e2e/UniversitySearchTest.java index 3b508d014..5d4ff71fd 100644 --- a/src/test/java/com/example/solidconnection/e2e/UniversitySearchTest.java +++ b/src/test/java/com/example/solidconnection/e2e/UniversitySearchTest.java @@ -43,7 +43,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search") + .when().get("/universities/search") .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -67,7 +67,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search?region=" + 영미권.getCode()) + .when().get("/universities/search?region=" + 영미권.getCode()) .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -85,7 +85,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search?keyword=라") + .when().get("/universities/search?keyword=라") .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -102,7 +102,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search?keyword=라&keyword=일본") + .when().get("/universities/search?keyword=라&keyword=일본") .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -120,7 +120,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search?testType=TOEFL_IBT") + .when().get("/universities/search?testType=TOEFL_IBT") .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -139,7 +139,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search?testType=TOEFL_IBT&testScore=70") + .when().get("/universities/search?testType=TOEFL_IBT&testScore=70") .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); @@ -155,7 +155,7 @@ public void setUpUserAndToken() { // request - 요청 List response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) - .when().get("/university/search?region=EUROPE&testType=TOEFL_IBT&testScore=70") + .when().get("/universities/search?region=EUROPE&testType=TOEFL_IBT&testScore=70") .then().log().all() .statusCode(200) .extract().jsonPath().getList(".", UniversityInfoForApplyPreviewResponse.class); 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 9fc6410d8..cb256bc0f 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java @@ -5,10 +5,7 @@ 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.MyPageUpdateResponse; 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.support.integration.BaseIntegrationTest; @@ -19,6 +16,7 @@ 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; @@ -36,11 +34,11 @@ 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.given; -import static org.mockito.BDDMockito.then; -import static org.mockito.BDDMockito.never; 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 { @@ -78,21 +76,6 @@ class SiteUserServiceTest extends BaseIntegrationTest { ); } - @Test - void 내_정보를_수정하기_위한_마이페이지_정보를_조회한다() { - // given - SiteUser testUser = createSiteUser(); - - // when - MyPageUpdateResponse response = siteUserService.getMyPageInfoToUpdate(testUser); - - // then - Assertions.assertAll( - () -> assertThat(response.nickname()).isEqualTo(testUser.getNickname()), - () -> assertThat(response.profileImageUrl()).isEqualTo(testUser.getProfileImageUrl()) - ); - } - @Test void 관심_대학교_목록을_조회한다() { // given @@ -126,13 +109,10 @@ class 프로필_이미지_수정_테스트 { .willReturn(new UploadedFileUrlResponse(expectedUrl)); // when - ProfileImageUpdateResponse response = siteUserService.updateProfileImage( - testUser, - imageFile - ); + siteUserService.updateMyPageInfo(testUser, imageFile, "newNickname"); // then - assertThat(response.profileImageUrl()).isEqualTo(expectedUrl); + assertThat(testUser.getProfileImageUrl()).isEqualTo(expectedUrl); } @Test @@ -144,7 +124,7 @@ class 프로필_이미지_수정_테스트 { .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); // when - siteUserService.updateProfileImage(testUser, imageFile); + siteUserService.updateMyPageInfo(testUser, imageFile, "newNickname"); // then then(s3Service).should(never()).deleteExProfile(any()); @@ -159,7 +139,7 @@ class 프로필_이미지_수정_테스트 { .willReturn(new UploadedFileUrlResponse("newProfileImageUrl")); // when - siteUserService.updateProfileImage(testUser, imageFile); + siteUserService.updateMyPageInfo(testUser, imageFile, "newNickname"); // then then(s3Service).should().deleteExProfile(testUser); @@ -172,7 +152,7 @@ class 프로필_이미지_수정_테스트 { MockMultipartFile emptyFile = createEmptyImageFile(); // when & then - assertThatCode(() -> siteUserService.updateProfileImage(testUser, emptyFile)) + assertThatCode(() -> siteUserService.updateMyPageInfo(testUser, emptyFile, "newNickname")) .isInstanceOf(CustomException.class) .hasMessage(PROFILE_IMAGE_NEEDED.getMessage()); } @@ -181,23 +161,26 @@ class 프로필_이미지_수정_테스트 { @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"; - NicknameUpdateRequest request = new NicknameUpdateRequest(newNickname); // when - NicknameUpdateResponse response = siteUserService.updateNickname( - testUser, - request - ); + siteUserService.updateMyPageInfo(testUser, imageFile, newNickname); // then SiteUser updatedUser = siteUserRepository.findById(testUser.getId()).get(); assertThat(updatedUser.getNicknameModifiedAt()).isNotNull(); - assertThat(response.nickname()).isEqualTo(newNickname); + assertThat(updatedUser.getNickname()).isEqualTo(newNickname); } @Test @@ -205,10 +188,10 @@ class 닉네임_수정_테스트 { // given createDuplicatedSiteUser(); SiteUser testUser = createSiteUser(); - NicknameUpdateRequest request = new NicknameUpdateRequest("duplicatedNickname"); + MockMultipartFile imageFile = createValidImageFile(); // when & then - assertThatCode(() -> siteUserService.updateNickname(testUser, request)) + assertThatCode(() -> siteUserService.updateMyPageInfo(testUser, imageFile, "duplicatedNickname")) .isInstanceOf(CustomException.class) .hasMessage(NICKNAME_ALREADY_EXISTED.getMessage()); } @@ -217,6 +200,7 @@ class 닉네임_수정_테스트 { 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); @@ -224,8 +208,7 @@ class 닉네임_수정_테스트 { NicknameUpdateRequest request = new NicknameUpdateRequest("newNickname"); // when & then - assertThatCode(() -> - siteUserService.updateNickname(testUser, request)) + assertThatCode(() -> siteUserService.updateMyPageInfo(testUser, imageFile, "nickname12")) .isInstanceOf(CustomException.class) .hasMessage(createExpectedErrorMessage(modifiedAt)); } diff --git a/src/test/java/com/example/solidconnection/university/service/UniversityLikeServiceTest.java b/src/test/java/com/example/solidconnection/university/service/UniversityLikeServiceTest.java index 51958ed5d..ec9b704a6 100644 --- a/src/test/java/com/example/solidconnection/university/service/UniversityLikeServiceTest.java +++ b/src/test/java/com/example/solidconnection/university/service/UniversityLikeServiceTest.java @@ -13,14 +13,18 @@ import com.example.solidconnection.university.dto.IsLikeResponse; import com.example.solidconnection.university.dto.LikeResultResponse; 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 static com.example.solidconnection.custom.exception.ErrorCode.ALREADY_LIKED_UNIVERSITY; +import static com.example.solidconnection.custom.exception.ErrorCode.NOT_LIKED_UNIVERSITY; import static com.example.solidconnection.custom.exception.ErrorCode.UNIVERSITY_INFO_FOR_APPLY_NOT_FOUND; import static com.example.solidconnection.university.service.UniversityLikeService.LIKE_CANCELED_MESSAGE; import static com.example.solidconnection.university.service.UniversityLikeService.LIKE_SUCCESS_MESSAGE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertAll; @DisplayName("대학교 좋아요 서비스 테스트") class UniversityLikeServiceTest extends BaseIntegrationTest { @@ -34,33 +38,70 @@ class UniversityLikeServiceTest extends BaseIntegrationTest { @Autowired private SiteUserRepository siteUserRepository; - @Test - void 대학_좋아요를_등록한다() { - // given - SiteUser testUser = createSiteUser(); - - // when - LikeResultResponse response = universityLikeService.likeUniversity(testUser, 괌대학_A_지원_정보.getId()); - - // then - assertThat(response.result()).isEqualTo(LIKE_SUCCESS_MESSAGE); - assertThat(likedUniversityRepository.findBySiteUserAndUniversityInfoForApply( - testUser, 괌대학_A_지원_정보)).isPresent(); + @Nested + class 대학_좋아요를_등록한다 { + + @Test + void 성공적으로_좋아요를_등록한다() { + // given + SiteUser testUser = createSiteUser(); + + // when + LikeResultResponse response = universityLikeService.likeUniversity(testUser, 괌대학_A_지원_정보.getId()); + + // then + assertAll( + () -> assertThat(response.result()).isEqualTo(LIKE_SUCCESS_MESSAGE), + () -> assertThat(likedUniversityRepository.findBySiteUserAndUniversityInfoForApply( + testUser, 괌대학_A_지원_정보 + )).isPresent() + ); + } + + @Test + void 이미_좋아요한_대학이면_예외_응답을_반환한다() { + // given + SiteUser testUser = createSiteUser(); + saveLikedUniversity(testUser, 괌대학_A_지원_정보); + + // when & then + assertThatCode(() -> universityLikeService.likeUniversity(testUser, 괌대학_A_지원_정보.getId())) + .isInstanceOf(CustomException.class) + .hasMessage(ALREADY_LIKED_UNIVERSITY.getMessage()); + } } - @Test - void 대학_좋아요를_취소한다() { - // given - SiteUser testUser = createSiteUser(); - saveLikedUniversity(testUser, 괌대학_A_지원_정보); - - // when - LikeResultResponse response = universityLikeService.likeUniversity(testUser, 괌대학_A_지원_정보.getId()); - - // then - assertThat(response.result()).isEqualTo(LIKE_CANCELED_MESSAGE); - assertThat(likedUniversityRepository.findBySiteUserAndUniversityInfoForApply( - testUser, 괌대학_A_지원_정보)).isEmpty(); + @Nested + class 대학_좋아요를_취소한다 { + + @Test + void 성공적으로_좋아요를_취소한다() { + // given + SiteUser testUser = createSiteUser(); + saveLikedUniversity(testUser, 괌대학_A_지원_정보); + + // when + LikeResultResponse response = universityLikeService.cancelLikeUniversity(testUser, 괌대학_A_지원_정보.getId()); + + // then + assertAll( + () -> assertThat(response.result()).isEqualTo(LIKE_CANCELED_MESSAGE), + () -> assertThat(likedUniversityRepository.findBySiteUserAndUniversityInfoForApply( + testUser, 괌대학_A_지원_정보 + )).isEmpty() + ); + } + + @Test + void 좋아요하지_않은_대학이면_예외_응답을_반환한다() { + // given + SiteUser testUser = createSiteUser(); + + // when & then + assertThatCode(() -> universityLikeService.cancelLikeUniversity(testUser, 괌대학_A_지원_정보.getId())) + .isInstanceOf(CustomException.class) + .hasMessage(NOT_LIKED_UNIVERSITY.getMessage()); + } } @Test From ea76630448a49571fa7376a8ec13bad5fcd180d9 Mon Sep 17 00:00:00 2001 From: Yeongseo Na Date: Fri, 14 Feb 2025 13:58:30 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EC=9D=B8=EC=A6=9D=EA=B3=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EB=90=98=EC=96=B4=20=EB=A6=AC=EC=A1=B8?= =?UTF-8?q?=EB=B2=84=EC=99=80=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=9D=B8=EC=9E=90=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=9C=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0=20(#201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 사용자 인증이 선택적으로 필요한 곳을 명시하도록 * fix: 만료된 토큰 객체가 아니라 토큰 자체를 사용하도록 * test: 인증된 사용자 리졸버 테스트 보강 * chore: 작업하며 발견한 개선이 필요한 부분 표시 --- .../auth/controller/AuthController.java | 21 ++++-- .../custom/resolver/AuthorizedUser.java | 1 + .../resolver/AuthorizedUserResolver.java | 20 ++++-- .../custom/resolver/ExpiredToken.java | 1 + .../custom/resolver/ExpiredTokenResolver.java | 1 + .../ExpiredTokenAuthentication.java | 1 + .../ExpiredTokenAuthenticationProvider.java | 1 + .../controller/UniversityController.java | 2 +- .../resolver/AuthorizedUserResolverTest.java | 66 +++++++++++++++---- 9 files changed, 92 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/example/solidconnection/auth/controller/AuthController.java b/src/main/java/com/example/solidconnection/auth/controller/AuthController.java index 80520942f..9c84e8d22 100644 --- a/src/main/java/com/example/solidconnection/auth/controller/AuthController.java +++ b/src/main/java/com/example/solidconnection/auth/controller/AuthController.java @@ -16,14 +16,15 @@ import com.example.solidconnection.auth.service.oauth.AppleOAuthService; import com.example.solidconnection.auth.service.oauth.KakaoOAuthService; import com.example.solidconnection.auth.service.oauth.OAuthSignUpService; +import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.custom.exception.ErrorCode; import com.example.solidconnection.custom.resolver.AuthorizedUser; -import com.example.solidconnection.custom.resolver.ExpiredToken; -import com.example.solidconnection.custom.security.authentication.ExpiredTokenAuthentication; import com.example.solidconnection.siteuser.domain.AuthType; import com.example.solidconnection.siteuser.domain.SiteUser; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -93,9 +94,13 @@ public ResponseEntity signUp( @PostMapping("/sign-out") public ResponseEntity signOut( - @ExpiredToken ExpiredTokenAuthentication expiredToken + Authentication authentication ) { - authService.signOut(expiredToken.getToken()); + String token = authentication.getCredentials().toString(); + if (token == null) { + throw new CustomException(ErrorCode.AUTHENTICATION_FAILED, "토큰이 없습니다."); + } + authService.signOut(token); return ResponseEntity.ok().build(); } @@ -109,9 +114,13 @@ public ResponseEntity quit( @PostMapping("/reissue") public ResponseEntity reissueToken( - @ExpiredToken ExpiredTokenAuthentication expiredToken + Authentication authentication ) { - ReissueResponse reissueResponse = authService.reissue(expiredToken.getSubject()); + String token = authentication.getCredentials().toString(); + if (token == null) { + throw new CustomException(ErrorCode.AUTHENTICATION_FAILED, "토큰이 없습니다."); + } + ReissueResponse reissueResponse = authService.reissue(token); return ResponseEntity.ok(reissueResponse); } } diff --git a/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUser.java b/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUser.java index b14d80994..fa1db7f74 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUser.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUser.java @@ -8,4 +8,5 @@ @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface AuthorizedUser { + boolean required() default true; } diff --git a/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolver.java index 93707b007..f4ba9fe7f 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolver.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolver.java @@ -1,9 +1,11 @@ package com.example.solidconnection.custom.resolver; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.custom.security.userdetails.SiteUserDetails; import com.example.solidconnection.siteuser.domain.SiteUser; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -11,6 +13,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; +import static com.example.solidconnection.custom.exception.ErrorCode.AUTHENTICATION_FAILED; + @Component @RequiredArgsConstructor public class AuthorizedUserResolver implements HandlerMethodArgumentResolver { @@ -25,11 +29,19 @@ public boolean supportsParameter(MethodParameter parameter) { public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + WebDataBinderFactory binderFactory) { + SiteUser siteUser = extractSiteUserFromAuthentication(); + if (parameter.getParameterAnnotation(AuthorizedUser.class).required() && siteUser == null) { + throw new CustomException(AUTHENTICATION_FAILED, "로그인 상태가 아닙니다."); + } + + return siteUser; + } + + private SiteUser extractSiteUserFromAuthentication() { try { - SiteUserDetails principal = (SiteUserDetails) SecurityContextHolder.getContext() - .getAuthentication() - .getPrincipal(); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + SiteUserDetails principal = (SiteUserDetails) authentication.getPrincipal(); return principal.getSiteUser(); } catch (Exception e) { return null; diff --git a/src/main/java/com/example/solidconnection/custom/resolver/ExpiredToken.java b/src/main/java/com/example/solidconnection/custom/resolver/ExpiredToken.java index 61abff98c..5de4ad95a 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/ExpiredToken.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/ExpiredToken.java @@ -5,6 +5,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +// todo: 사용되지 않음, 다른 PR에서 삭제하고 더 효율적인 구조를 고민해봐야 함 @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface ExpiredToken { diff --git a/src/main/java/com/example/solidconnection/custom/resolver/ExpiredTokenResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/ExpiredTokenResolver.java index 691136438..7547a1d61 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/ExpiredTokenResolver.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/ExpiredTokenResolver.java @@ -10,6 +10,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; +// todo: 사용되지 않음, 다른 PR에서 삭제하고 더 효율적인 구조를 고민해봐야 함 @Component @RequiredArgsConstructor public class ExpiredTokenResolver implements HandlerMethodArgumentResolver { diff --git a/src/main/java/com/example/solidconnection/custom/security/authentication/ExpiredTokenAuthentication.java b/src/main/java/com/example/solidconnection/custom/security/authentication/ExpiredTokenAuthentication.java index 811ea6a1b..061484674 100644 --- a/src/main/java/com/example/solidconnection/custom/security/authentication/ExpiredTokenAuthentication.java +++ b/src/main/java/com/example/solidconnection/custom/security/authentication/ExpiredTokenAuthentication.java @@ -1,5 +1,6 @@ package com.example.solidconnection.custom.security.authentication; +// todo: 사용되지 않음, 다른 PR에서 삭제하고 더 효율적인 구조를 고민해봐야 함 public class ExpiredTokenAuthentication extends JwtAuthentication { public ExpiredTokenAuthentication(String token) { diff --git a/src/main/java/com/example/solidconnection/custom/security/provider/ExpiredTokenAuthenticationProvider.java b/src/main/java/com/example/solidconnection/custom/security/provider/ExpiredTokenAuthenticationProvider.java index d7461a0e6..01b065a19 100644 --- a/src/main/java/com/example/solidconnection/custom/security/provider/ExpiredTokenAuthenticationProvider.java +++ b/src/main/java/com/example/solidconnection/custom/security/provider/ExpiredTokenAuthenticationProvider.java @@ -12,6 +12,7 @@ import static com.example.solidconnection.util.JwtUtils.parseSubjectIgnoringExpiration; +// todo: 사용되지 않음, 다른 PR에서 삭제하고 더 효율적인 구조를 고민해봐야 함 @Component @RequiredArgsConstructor public class ExpiredTokenAuthenticationProvider implements AuthenticationProvider { 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 66da87095..83d90f600 100644 --- a/src/main/java/com/example/solidconnection/university/controller/UniversityController.java +++ b/src/main/java/com/example/solidconnection/university/controller/UniversityController.java @@ -36,7 +36,7 @@ public class UniversityController { @GetMapping("/recommend") public ResponseEntity getUniversityRecommends( - @AuthorizedUser SiteUser siteUser + @AuthorizedUser(required = false) SiteUser siteUser ) { if (siteUser == null) { return ResponseEntity.ok(universityRecommendService.getGeneralRecommends()); diff --git a/src/test/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolverTest.java index 763fdf101..779474c27 100644 --- a/src/test/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolverTest.java +++ b/src/test/java/com/example/solidconnection/custom/resolver/AuthorizedUserResolverTest.java @@ -1,6 +1,7 @@ package com.example.solidconnection.custom.resolver; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.custom.security.authentication.SiteUserAuthentication; import com.example.solidconnection.custom.security.userdetails.SiteUserDetails; import com.example.solidconnection.siteuser.domain.SiteUser; @@ -11,11 +12,18 @@ import com.example.solidconnection.type.Role; 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.core.MethodParameter; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import static com.example.solidconnection.custom.exception.ErrorCode.AUTHENTICATION_FAILED; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; @TestContainerSpringBootTest @DisplayName("인증된 사용자 argument resolver 테스트") @@ -33,28 +41,58 @@ void setUp() { } @Test - void security_context_에_저장된_인증된_사용자를_반환한다() throws Exception { + void security_context_에_저장된_인증된_사용자를_반환한다() { // given - SiteUser siteUser = siteUserRepository.save(createSiteUser()); - SiteUserDetails userDetails = new SiteUserDetails(siteUser); - SiteUserAuthentication authentication = new SiteUserAuthentication("token", userDetails); + SiteUser siteUser = createAndSaveSiteUser(); + Authentication authentication = createAuthenticationWithUser(siteUser); SecurityContextHolder.getContext().setAuthentication(authentication); + MethodParameter parameter = mock(MethodParameter.class); + AuthorizedUser authorizedUser = mock(AuthorizedUser.class); + given(parameter.getParameterAnnotation(AuthorizedUser.class)).willReturn(authorizedUser); + given(authorizedUser.required()).willReturn(false); + // when - SiteUser resolveSiteUser = (SiteUser) authorizedUserResolver.resolveArgument(null, null, null, null); + SiteUser resolveSiteUser = (SiteUser) authorizedUserResolver.resolveArgument(parameter, null, null, null); // then assertThat(resolveSiteUser).isEqualTo(siteUser); } - @Test - void security_context_에_저장된_사용자가_없으면_null_을_반환한다() throws Exception { - // when, then - assertThat(authorizedUserResolver.resolveArgument(null, null, null, null)).isNull(); + @Nested + class security_context_에_저장된_사용자가_없는_경우 { + + @Test + void required_가_true_이면_예외_응답을_반환한다() { + // given + MethodParameter parameter = mock(MethodParameter.class); + AuthorizedUser authorizedUser = mock(AuthorizedUser.class); + given(parameter.getParameterAnnotation(AuthorizedUser.class)).willReturn(authorizedUser); + given(authorizedUser.required()).willReturn(true); + + // when, then + assertThatCode(() -> authorizedUserResolver.resolveArgument(parameter, null, null, null)) + .isInstanceOf(CustomException.class) + .hasMessageContaining(AUTHENTICATION_FAILED.getMessage()); + } + + @Test + void required_가_false_이면_null_을_반환한다() { + // given + MethodParameter parameter = mock(MethodParameter.class); + AuthorizedUser authorizedUser = mock(AuthorizedUser.class); + given(parameter.getParameterAnnotation(AuthorizedUser.class)).willReturn(authorizedUser); + given(authorizedUser.required()).willReturn(false); + + // when, then + assertThat( + authorizedUserResolver.resolveArgument(parameter, null, null, null) + ).isNull(); + } } - private SiteUser createSiteUser() { - return new SiteUser( + private SiteUser createAndSaveSiteUser() { + SiteUser siteUser = new SiteUser( "test@example.com", "nickname", "profileImageUrl", @@ -63,5 +101,11 @@ private SiteUser createSiteUser() { Role.MENTEE, Gender.MALE ); + return siteUserRepository.save(siteUser); + } + + private SiteUserAuthentication createAuthenticationWithUser(SiteUser siteUser) { + SiteUserDetails userDetails = new SiteUserDetails(siteUser); + return new SiteUserAuthentication("token", userDetails); } } From ba5193b4428249e86c63f32fda124a851bef0556 Mon Sep 17 00:00:00 2001 From: Yeongseo Na Date: Fri, 14 Feb 2025 16:20:37 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0,=20=EC=A7=80=EC=9B=90,=20=EC=84=B1=EC=A0=81=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20api=20=EC=88=98=EC=A0=95=20(#196)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 지원서 uri 복수형으로 변경 * refactor: 게시판 uri 변경 - communities -> boards * refactor: 게시글 uri 변경 - communities/{code}/posts -> posts * fix: 영속성 컨텍스트 때문에 동작하지 않는 부분 해결 * refactor: 댓글 uri 변경 * fix: 영속성 컨텍스트 때문에 동작하지 않는 부분 해결 * refactor: 댓글 uri 변경 * fix: 영속성 컨텍스트로 인한 문제 해결 * chore: 잘못 추가한 라인 삭제 --- .../controller/ApplicationController.java | 4 +- .../board/controller/BoardController.java | 19 ++++++- .../comment/controller/CommentController.java | 17 +++---- .../comment/dto/CommentCreateRequest.java | 4 ++ .../comment/service/CommentService.java | 25 ++++++--- .../post/controller/PostController.java | 45 +++++----------- .../community/post/dto/PostCreateRequest.java | 3 ++ .../post/service/PostCommandService.java | 36 ++++++------- .../post/service/PostLikeService.java | 31 +++++------ .../post/service/PostQueryService.java | 4 +- .../score/controller/ScoreController.java | 24 +++++---- .../score/dto/GpaScoreRequest.java | 15 +----- .../score/dto/LanguageTestScoreRequest.java | 15 +----- .../score/service/ScoreService.java | 51 +++++++++++++++---- .../comment/service/CommentServiceTest.java | 22 ++------ .../post/service/PostCommandServiceTest.java | 23 +++------ .../post/service/PostLikeServiceTest.java | 8 +-- .../post/service/PostQueryServiceTest.java | 1 - .../PostLikeCountConcurrencyTest.java | 4 +- .../e2e/ApplicantsQueryTest.java | 16 +++--- .../score/service/ScoreServiceTest.java | 40 ++++++++++++--- 21 files changed, 211 insertions(+), 196 deletions(-) diff --git a/src/main/java/com/example/solidconnection/application/controller/ApplicationController.java b/src/main/java/com/example/solidconnection/application/controller/ApplicationController.java index 6d8c45fbf..892597e90 100644 --- a/src/main/java/com/example/solidconnection/application/controller/ApplicationController.java +++ b/src/main/java/com/example/solidconnection/application/controller/ApplicationController.java @@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor -@RequestMapping("/application") +@RequestMapping("/applications") @RestController public class ApplicationController { @@ -27,7 +27,7 @@ public class ApplicationController { private final ApplicationQueryService applicationQueryService; // 지원서 제출하기 api - @PostMapping() + @PostMapping public ResponseEntity apply( @AuthorizedUser SiteUser siteUser, @Valid @RequestBody ApplyRequest applyRequest diff --git a/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java b/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java index 9329535a1..40410bdbe 100644 --- a/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java +++ b/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java @@ -1,10 +1,14 @@ package com.example.solidconnection.community.board.controller; +import com.example.solidconnection.community.post.dto.PostListResponse; +import com.example.solidconnection.community.post.service.PostQueryService; import com.example.solidconnection.type.BoardCode; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; @@ -12,11 +16,13 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/communities") +@RequestMapping("/boards") public class BoardController { + private final PostQueryService postQueryService; + // todo: 회원별로 접근 가능한 게시판 목록 조회 기능 개발 - @GetMapping() + @GetMapping public ResponseEntity findAccessibleCodes() { List accessibleCodeList = new ArrayList<>(); for (BoardCode boardCode : BoardCode.values()) { @@ -24,4 +30,13 @@ public ResponseEntity findAccessibleCodes() { } return ResponseEntity.ok().body(accessibleCodeList); } + + @GetMapping("/{code}") + public ResponseEntity findPostsByCodeAndCategory( + @PathVariable(value = "code") String code, + @RequestParam(value = "category", defaultValue = "전체") String category) { + List postsByCodeAndPostCategory = postQueryService + .findPostsByCodeAndPostCategory(code, category); + return ResponseEntity.ok().body(postsByCodeAndPostCategory); + } } diff --git a/src/main/java/com/example/solidconnection/community/comment/controller/CommentController.java b/src/main/java/com/example/solidconnection/community/comment/controller/CommentController.java index e215fea72..d096f6cc9 100644 --- a/src/main/java/com/example/solidconnection/community/comment/controller/CommentController.java +++ b/src/main/java/com/example/solidconnection/community/comment/controller/CommentController.java @@ -21,39 +21,36 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/posts") +@RequestMapping("/comments") public class CommentController { private final CommentService commentService; - @PostMapping("/{post_id}/comments") + @PostMapping public ResponseEntity createComment( @AuthorizedUser SiteUser siteUser, - @PathVariable("post_id") Long postId, @Valid @RequestBody CommentCreateRequest commentCreateRequest ) { - CommentCreateResponse response = commentService.createComment(siteUser, postId, commentCreateRequest); + CommentCreateResponse response = commentService.createComment(siteUser, commentCreateRequest); return ResponseEntity.ok().body(response); } - @PatchMapping("/{post_id}/comments/{comment_id}") + @PatchMapping("/{comment_id}") public ResponseEntity updateComment( @AuthorizedUser SiteUser siteUser, - @PathVariable("post_id") Long postId, @PathVariable("comment_id") Long commentId, @Valid @RequestBody CommentUpdateRequest commentUpdateRequest ) { - CommentUpdateResponse response = commentService.updateComment(siteUser, postId, commentId, commentUpdateRequest); + CommentUpdateResponse response = commentService.updateComment(siteUser, commentId, commentUpdateRequest); return ResponseEntity.ok().body(response); } - @DeleteMapping("/{post_id}/comments/{comment_id}") + @DeleteMapping("/{comment_id}") public ResponseEntity deleteCommentById( @AuthorizedUser SiteUser siteUser, - @PathVariable("post_id") Long postId, @PathVariable("comment_id") Long commentId ) { - CommentDeleteResponse response = commentService.deleteCommentById(siteUser, postId, commentId); + CommentDeleteResponse response = commentService.deleteCommentById(siteUser, commentId); return ResponseEntity.ok().body(response); } } diff --git a/src/main/java/com/example/solidconnection/community/comment/dto/CommentCreateRequest.java b/src/main/java/com/example/solidconnection/community/comment/dto/CommentCreateRequest.java index 610f602c8..13c512a0c 100644 --- a/src/main/java/com/example/solidconnection/community/comment/dto/CommentCreateRequest.java +++ b/src/main/java/com/example/solidconnection/community/comment/dto/CommentCreateRequest.java @@ -4,9 +4,13 @@ import com.example.solidconnection.community.post.domain.Post; import com.example.solidconnection.siteuser.domain.SiteUser; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; public record CommentCreateRequest( + @NotNull(message = "게시글 ID를 설정해주세요.") + Long postId, + @NotBlank(message = "댓글 내용은 빈 값일 수 없습니다.") @Size(min = 1, max = 255, message = "댓글 내용은 최소 1자 이상, 최대 255자 이하여야 합니다.") String content, diff --git a/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java b/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java index 209dd6987..76138b356 100644 --- a/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java +++ b/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java @@ -8,10 +8,11 @@ import com.example.solidconnection.community.comment.dto.CommentUpdateResponse; import com.example.solidconnection.community.comment.dto.PostFindCommentResponse; import com.example.solidconnection.community.comment.repository.CommentRepository; -import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.community.post.domain.Post; import com.example.solidconnection.community.post.repository.PostRepository; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,6 +23,7 @@ import static com.example.solidconnection.custom.exception.ErrorCode.CAN_NOT_UPDATE_DEPRECATED_COMMENT; import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_COMMENT_LEVEL; import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_POST_ACCESS; +import static com.example.solidconnection.custom.exception.ErrorCode.USER_NOT_FOUND; @Service @RequiredArgsConstructor @@ -29,6 +31,7 @@ public class CommentService { private final CommentRepository commentRepository; private final PostRepository postRepository; + private final SiteUserRepository siteUserRepository; @Transactional(readOnly = true) public List findCommentsByPostId(SiteUser siteUser, Long postId) { @@ -43,15 +46,23 @@ private Boolean isOwner(Comment comment, SiteUser siteUser) { } @Transactional - public CommentCreateResponse createComment(SiteUser siteUser, Long postId, CommentCreateRequest commentCreateRequest) { - Post post = postRepository.getById(postId); + public CommentCreateResponse createComment(SiteUser siteUser, CommentCreateRequest commentCreateRequest) { + Post post = postRepository.getById(commentCreateRequest.postId()); Comment parentComment = null; if (commentCreateRequest.parentId() != null) { parentComment = commentRepository.getById(commentCreateRequest.parentId()); validateCommentDepth(parentComment); } - Comment createdComment = commentRepository.save(commentCreateRequest.toEntity(siteUser, post, parentComment)); + + /* + * todo: siteUser를 영속 상태로 만들 수 있도록 컨트롤러에서 siteUserId 를 넘겨줄 것인지, + * siteUser 에 postList 를 FetchType.EAGER 로 설정할 것인지, + * post 와 siteUser 사이의 양방향을 끊을 것인지 생각해봐야한다. + */ + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + Comment comment = commentCreateRequest.toEntity(siteUser1, post, parentComment); + Comment createdComment = commentRepository.save(comment); return CommentCreateResponse.from(createdComment); } @@ -64,8 +75,7 @@ private void validateCommentDepth(Comment parentComment) { } @Transactional - public CommentUpdateResponse updateComment(SiteUser siteUser, Long postId, Long commentId, CommentUpdateRequest commentUpdateRequest) { - Post post = postRepository.getById(postId); + public CommentUpdateResponse updateComment(SiteUser siteUser, Long commentId, CommentUpdateRequest commentUpdateRequest) { Comment comment = commentRepository.getById(commentId); validateDeprecated(comment); validateOwnership(comment, siteUser); @@ -82,8 +92,7 @@ private void validateDeprecated(Comment comment) { } @Transactional - public CommentDeleteResponse deleteCommentById(SiteUser siteUser, Long postId, Long commentId) { - Post post = postRepository.getById(postId); + public CommentDeleteResponse deleteCommentById(SiteUser siteUser, Long commentId) { Comment comment = commentRepository.getById(commentId); validateOwnership(comment, siteUser); diff --git a/src/main/java/com/example/solidconnection/community/post/controller/PostController.java b/src/main/java/com/example/solidconnection/community/post/controller/PostController.java index a2479f08b..ee422930a 100644 --- a/src/main/java/com/example/solidconnection/community/post/controller/PostController.java +++ b/src/main/java/com/example/solidconnection/community/post/controller/PostController.java @@ -1,7 +1,5 @@ package com.example.solidconnection.community.post.controller; -import com.example.solidconnection.community.post.dto.PostListResponse; -import com.example.solidconnection.custom.resolver.AuthorizedUser; import com.example.solidconnection.community.post.dto.PostCreateRequest; import com.example.solidconnection.community.post.dto.PostCreateResponse; import com.example.solidconnection.community.post.dto.PostDeleteResponse; @@ -13,6 +11,7 @@ import com.example.solidconnection.community.post.service.PostCommandService; import com.example.solidconnection.community.post.service.PostLikeService; import com.example.solidconnection.community.post.service.PostQueryService; +import com.example.solidconnection.custom.resolver.AuthorizedUser; import com.example.solidconnection.siteuser.domain.SiteUser; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -33,41 +32,29 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/communities") +@RequestMapping("/posts") public class PostController { private final PostQueryService postQueryService; private final PostCommandService postCommandService; private final PostLikeService postLikeService; - @GetMapping("/{code}") - public ResponseEntity findPostsByCodeAndCategory( - @PathVariable(value = "code") String code, - @RequestParam(value = "category", defaultValue = "전체") String category) { - - List postsByCodeAndPostCategory = postQueryService - .findPostsByCodeAndPostCategory(code, category); - return ResponseEntity.ok().body(postsByCodeAndPostCategory); - } - - @PostMapping(value = "/{code}/posts") + @PostMapping public ResponseEntity createPost( @AuthorizedUser SiteUser siteUser, - @PathVariable("code") String code, @Valid @RequestPart("postCreateRequest") PostCreateRequest postCreateRequest, @RequestParam(value = "file", required = false) List imageFile ) { if (imageFile == null) { imageFile = Collections.emptyList(); } - PostCreateResponse post = postCommandService.createPost(siteUser, code, postCreateRequest, imageFile); + PostCreateResponse post = postCommandService.createPost(siteUser, postCreateRequest, imageFile); return ResponseEntity.ok().body(post); } - @PatchMapping(value = "/{code}/posts/{post_id}") + @PatchMapping(value = "/{post_id}") public ResponseEntity updatePost( @AuthorizedUser SiteUser siteUser, - @PathVariable("code") String code, @PathVariable("post_id") Long postId, @Valid @RequestPart("postUpdateRequest") PostUpdateRequest postUpdateRequest, @RequestParam(value = "file", required = false) List imageFile @@ -76,48 +63,44 @@ public ResponseEntity updatePost( imageFile = Collections.emptyList(); } PostUpdateResponse postUpdateResponse = postCommandService.updatePost( - siteUser, code, postId, postUpdateRequest, imageFile + siteUser, postId, postUpdateRequest, imageFile ); return ResponseEntity.ok().body(postUpdateResponse); } - @GetMapping("/{code}/posts/{post_id}") + @GetMapping("/{post_id}") public ResponseEntity findPostById( @AuthorizedUser SiteUser siteUser, - @PathVariable("code") String code, @PathVariable("post_id") Long postId ) { - PostFindResponse postFindResponse = postQueryService.findPostById(siteUser, code, postId); + PostFindResponse postFindResponse = postQueryService.findPostById(siteUser, postId); return ResponseEntity.ok().body(postFindResponse); } - @DeleteMapping(value = "/{code}/posts/{post_id}") + @DeleteMapping(value = "/{post_id}") public ResponseEntity deletePostById( @AuthorizedUser SiteUser siteUser, - @PathVariable("code") String code, @PathVariable("post_id") Long postId ) { - PostDeleteResponse postDeleteResponse = postCommandService.deletePostById(siteUser, code, postId); + PostDeleteResponse postDeleteResponse = postCommandService.deletePostById(siteUser, postId); return ResponseEntity.ok().body(postDeleteResponse); } - @PostMapping(value = "/{code}/posts/{post_id}/like") + @PostMapping(value = "/{post_id}/like") public ResponseEntity likePost( @AuthorizedUser SiteUser siteUser, - @PathVariable("code") String code, @PathVariable("post_id") Long postId ) { - PostLikeResponse postLikeResponse = postLikeService.likePost(siteUser, code, postId); + PostLikeResponse postLikeResponse = postLikeService.likePost(siteUser, postId); return ResponseEntity.ok().body(postLikeResponse); } - @DeleteMapping(value = "/{code}/posts/{post_id}/like") + @DeleteMapping(value = "/{post_id}/like") public ResponseEntity dislikePost( @AuthorizedUser SiteUser siteUser, - @PathVariable("code") String code, @PathVariable("post_id") Long postId ) { - PostDislikeResponse postDislikeResponse = postLikeService.dislikePost(siteUser, code, postId); + PostDislikeResponse postDislikeResponse = postLikeService.dislikePost(siteUser, postId); return ResponseEntity.ok().body(postDislikeResponse); } } diff --git a/src/main/java/com/example/solidconnection/community/post/dto/PostCreateRequest.java b/src/main/java/com/example/solidconnection/community/post/dto/PostCreateRequest.java index db271a80f..5e6590b20 100644 --- a/src/main/java/com/example/solidconnection/community/post/dto/PostCreateRequest.java +++ b/src/main/java/com/example/solidconnection/community/post/dto/PostCreateRequest.java @@ -9,6 +9,9 @@ import jakarta.validation.constraints.Size; public record PostCreateRequest( + @NotNull(message = "게시글 카테고리를 설정해주세요.") + String boardCode, + @NotNull(message = "게시글 카테고리를 설정해주세요.") String postCategory, diff --git a/src/main/java/com/example/solidconnection/community/post/service/PostCommandService.java b/src/main/java/com/example/solidconnection/community/post/service/PostCommandService.java index 1e66b52a4..b95cbcf1b 100644 --- a/src/main/java/com/example/solidconnection/community/post/service/PostCommandService.java +++ b/src/main/java/com/example/solidconnection/community/post/service/PostCommandService.java @@ -2,20 +2,20 @@ import com.example.solidconnection.community.board.domain.Board; import com.example.solidconnection.community.board.repository.BoardRepository; -import com.example.solidconnection.custom.exception.CustomException; -import com.example.solidconnection.community.post.domain.PostImage; import com.example.solidconnection.community.post.domain.Post; +import com.example.solidconnection.community.post.domain.PostImage; import com.example.solidconnection.community.post.dto.PostCreateRequest; import com.example.solidconnection.community.post.dto.PostCreateResponse; import com.example.solidconnection.community.post.dto.PostDeleteResponse; import com.example.solidconnection.community.post.dto.PostUpdateRequest; import com.example.solidconnection.community.post.dto.PostUpdateResponse; import com.example.solidconnection.community.post.repository.PostRepository; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.s3.S3Service; import com.example.solidconnection.s3.UploadedFileUrlResponse; import com.example.solidconnection.service.RedisService; import com.example.solidconnection.siteuser.domain.SiteUser; -import com.example.solidconnection.type.BoardCode; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; import com.example.solidconnection.type.ImgType; import com.example.solidconnection.type.PostCategory; import com.example.solidconnection.util.RedisUtils; @@ -29,9 +29,9 @@ import static com.example.solidconnection.custom.exception.ErrorCode.CAN_NOT_DELETE_OR_UPDATE_QUESTION; import static com.example.solidconnection.custom.exception.ErrorCode.CAN_NOT_UPLOAD_MORE_THAN_FIVE_IMAGES; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_BOARD_CODE; import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_POST_ACCESS; import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_POST_CATEGORY; +import static com.example.solidconnection.custom.exception.ErrorCode.USER_NOT_FOUND; @Service @RequiredArgsConstructor @@ -42,18 +42,24 @@ public class PostCommandService { private final S3Service s3Service; private final RedisService redisService; private final RedisUtils redisUtils; + private final SiteUserRepository siteUserRepository; @Transactional - public PostCreateResponse createPost(SiteUser siteUser, String code, PostCreateRequest postCreateRequest, + public PostCreateResponse createPost(SiteUser siteUser, PostCreateRequest postCreateRequest, List imageFile) { // 유효성 검증 - String boardCode = validateCode(code); validatePostCategory(postCreateRequest.postCategory()); validateFileSize(imageFile); // 객체 생성 - Board board = boardRepository.getByCode(boardCode); - Post post = postCreateRequest.toEntity(siteUser, board); + Board board = boardRepository.getByCode(postCreateRequest.boardCode()); + /* + * todo: siteUser를 영속 상태로 만들 수 있도록 컨트롤러에서 siteUserId 를 넘겨줄 것인지, + * siteUser 에 postList 를 FetchType.EAGER 로 설정할 것인지, + * post 와 siteUser 사이의 양방향을 끊을 것인지 생각해봐야한다. + */ + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + Post post = postCreateRequest.toEntity(siteUser1, board); // 이미지 처리 savePostImages(imageFile, post); Post createdPost = postRepository.save(post); @@ -62,10 +68,9 @@ public PostCreateResponse createPost(SiteUser siteUser, String code, PostCreateR } @Transactional - public PostUpdateResponse updatePost(SiteUser siteUser, String code, Long postId, PostUpdateRequest postUpdateRequest, + public PostUpdateResponse updatePost(SiteUser siteUser, Long postId, PostUpdateRequest postUpdateRequest, List imageFile) { // 유효성 검증 - String boardCode = validateCode(code); Post post = postRepository.getById(postId); validateOwnership(post, siteUser); validateQuestion(post); @@ -93,8 +98,7 @@ private void savePostImages(List imageFile, Post post) { } @Transactional - public PostDeleteResponse deletePostById(SiteUser siteUser, String code, Long postId) { - String boardCode = validateCode(code); + public PostDeleteResponse deletePostById(SiteUser siteUser, Long postId) { Post post = postRepository.getById(postId); validateOwnership(post, siteUser); validateQuestion(post); @@ -108,14 +112,6 @@ public PostDeleteResponse deletePostById(SiteUser siteUser, String code, Long po return new PostDeleteResponse(postId); } - private String validateCode(String code) { - try { - return String.valueOf(BoardCode.valueOf(code)); - } catch (IllegalArgumentException ex) { - throw new CustomException(INVALID_BOARD_CODE); - } - } - private void validateOwnership(Post post, SiteUser siteUser) { if (!post.getSiteUser().getId().equals(siteUser.getId())) { throw new CustomException(INVALID_POST_ACCESS); diff --git a/src/main/java/com/example/solidconnection/community/post/service/PostLikeService.java b/src/main/java/com/example/solidconnection/community/post/service/PostLikeService.java index 045c069cd..98d1a239f 100644 --- a/src/main/java/com/example/solidconnection/community/post/service/PostLikeService.java +++ b/src/main/java/com/example/solidconnection/community/post/service/PostLikeService.java @@ -1,21 +1,21 @@ package com.example.solidconnection.community.post.service; -import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.community.post.domain.Post; import com.example.solidconnection.community.post.domain.PostLike; import com.example.solidconnection.community.post.dto.PostDislikeResponse; import com.example.solidconnection.community.post.dto.PostLikeResponse; import com.example.solidconnection.community.post.repository.PostLikeRepository; import com.example.solidconnection.community.post.repository.PostRepository; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.siteuser.domain.SiteUser; -import com.example.solidconnection.type.BoardCode; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import static com.example.solidconnection.custom.exception.ErrorCode.DUPLICATE_POST_LIKE; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_BOARD_CODE; +import static com.example.solidconnection.custom.exception.ErrorCode.USER_NOT_FOUND; @Service @RequiredArgsConstructor @@ -23,15 +23,21 @@ public class PostLikeService { private final PostRepository postRepository; private final PostLikeRepository postLikeRepository; + private final SiteUserRepository siteUserRepository; @Transactional(isolation = Isolation.READ_COMMITTED) - public PostLikeResponse likePost(SiteUser siteUser, String code, Long postId) { - String boardCode = validateCode(code); + public PostLikeResponse likePost(SiteUser siteUser, Long postId) { Post post = postRepository.getById(postId); validateDuplicatePostLike(post, siteUser); - PostLike postLike = new PostLike(); - postLike.setPostAndSiteUser(post, siteUser); + + /* + * todo: siteUser를 영속 상태로 만들 수 있도록 컨트롤러에서 siteUserId 를 넘겨줄 것인지, + * siteUser 에 postList 를 FetchType.EAGER 로 설정할 것인지, + * post 와 siteUser 사이의 양방향을 끊을 것인지 생각해봐야한다. + */ + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + postLike.setPostAndSiteUser(post, siteUser1); postLikeRepository.save(postLike); postRepository.increaseLikeCount(post.getId()); @@ -39,8 +45,7 @@ public PostLikeResponse likePost(SiteUser siteUser, String code, Long postId) { } @Transactional(isolation = Isolation.READ_COMMITTED) - public PostDislikeResponse dislikePost(SiteUser siteUser, String code, Long postId) { - String boardCode = validateCode(code); + public PostDislikeResponse dislikePost(SiteUser siteUser, Long postId) { Post post = postRepository.getById(postId); PostLike postLike = postLikeRepository.getByPostAndSiteUser(post, siteUser); @@ -51,14 +56,6 @@ public PostDislikeResponse dislikePost(SiteUser siteUser, String code, Long post return PostDislikeResponse.from(postRepository.getById(postId)); // 실시간성을 위한 재조회 } - private String validateCode(String code) { - try { - return String.valueOf(BoardCode.valueOf(code)); - } catch (IllegalArgumentException ex) { - throw new CustomException(INVALID_BOARD_CODE); - } - } - private void validateDuplicatePostLike(Post post, SiteUser siteUser) { if (postLikeRepository.findPostLikeByPostAndSiteUser(post, siteUser).isPresent()) { throw new CustomException(DUPLICATE_POST_LIKE); diff --git a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java index 1d7f292ea..66cbb5faa 100644 --- a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java +++ b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java @@ -53,9 +53,7 @@ public List findPostsByCodeAndPostCategory(String code, String } @Transactional(readOnly = true) - public PostFindResponse findPostById(SiteUser siteUser, String code, Long postId) { - String boardCode = validateCode(code); - + public PostFindResponse findPostById(SiteUser siteUser, Long postId) { Post post = postRepository.getByIdUsingEntityGraph(postId); Boolean isOwner = getIsOwner(post, siteUser); Boolean isLiked = getIsLiked(post, siteUser); diff --git a/src/main/java/com/example/solidconnection/score/controller/ScoreController.java b/src/main/java/com/example/solidconnection/score/controller/ScoreController.java index 6c54ab5fe..4ea560657 100644 --- a/src/main/java/com/example/solidconnection/score/controller/ScoreController.java +++ b/src/main/java/com/example/solidconnection/score/controller/ScoreController.java @@ -12,39 +12,43 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @RestController -@RequestMapping("/score") +@RequestMapping("/scores") @RequiredArgsConstructor public class ScoreController { private final ScoreService scoreService; // 학점을 등록하는 api - @PostMapping("/gpa") + @PostMapping("/gpas") public ResponseEntity submitGpaScore( @AuthorizedUser SiteUser siteUser, - @Valid @RequestBody GpaScoreRequest gpaScoreRequest + @Valid @RequestPart("gpaScoreRequest") GpaScoreRequest gpaScoreRequest, + @RequestParam("file") MultipartFile file ) { - Long id = scoreService.submitGpaScore(siteUser, gpaScoreRequest); + Long id = scoreService.submitGpaScore(siteUser, gpaScoreRequest, file); return ResponseEntity.ok(id); } // 어학성적을 등록하는 api - @PostMapping("/languageTest") + @PostMapping("/language-tests") public ResponseEntity submitLanguageTestScore( @AuthorizedUser SiteUser siteUser, - @Valid @RequestBody LanguageTestScoreRequest languageTestScoreRequest + @Valid @RequestPart("languageTestScoreRequest") LanguageTestScoreRequest languageTestScoreRequest, + @RequestParam("file") MultipartFile file ) { - Long id = scoreService.submitLanguageTestScore(siteUser, languageTestScoreRequest); + Long id = scoreService.submitLanguageTestScore(siteUser, languageTestScoreRequest, file); return ResponseEntity.ok(id); } // 학점 상태를 확인하는 api - @GetMapping("/gpa") + @GetMapping("/gpas") public ResponseEntity getGpaScoreStatus( @AuthorizedUser SiteUser siteUser ) { @@ -53,7 +57,7 @@ public ResponseEntity getGpaScoreStatus( } // 어학 성적 상태를 확인하는 api - @GetMapping("/languageTest") + @GetMapping("/language-tests") public ResponseEntity getLanguageTestScoreStatus( @AuthorizedUser SiteUser siteUser ) { diff --git a/src/main/java/com/example/solidconnection/score/dto/GpaScoreRequest.java b/src/main/java/com/example/solidconnection/score/dto/GpaScoreRequest.java index 613ac5b54..beafbf2e3 100644 --- a/src/main/java/com/example/solidconnection/score/dto/GpaScoreRequest.java +++ b/src/main/java/com/example/solidconnection/score/dto/GpaScoreRequest.java @@ -1,7 +1,5 @@ package com.example.solidconnection.score.dto; -import com.example.solidconnection.application.domain.Gpa; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public record GpaScoreRequest( @@ -9,15 +7,6 @@ public record GpaScoreRequest( Double gpa, @NotNull(message = "학점 기준을 입력해주세요.") - Double gpaCriteria, - - @NotBlank(message = "대학 성적 증명서를 첨부해주세요.") - String gpaReportUrl) { - - public Gpa toGpa() { - return new Gpa( - this.gpa, - this.gpaCriteria, - this.gpaReportUrl); - } + Double gpaCriteria +) { } diff --git a/src/main/java/com/example/solidconnection/score/dto/LanguageTestScoreRequest.java b/src/main/java/com/example/solidconnection/score/dto/LanguageTestScoreRequest.java index 92522949e..de9329898 100644 --- a/src/main/java/com/example/solidconnection/score/dto/LanguageTestScoreRequest.java +++ b/src/main/java/com/example/solidconnection/score/dto/LanguageTestScoreRequest.java @@ -1,6 +1,5 @@ package com.example.solidconnection.score.dto; -import com.example.solidconnection.application.domain.LanguageTest; import com.example.solidconnection.type.LanguageTestType; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -10,16 +9,6 @@ public record LanguageTestScoreRequest( LanguageTestType languageTestType, @NotBlank(message = "어학 점수를 입력해주세요.") - String languageTestScore, - - @NotBlank(message = "어학 증명서를 첨부해주세요.") - String languageTestReportUrl) { - - public LanguageTest toLanguageTest() { - return new LanguageTest( - this.languageTestType, - this.languageTestScore, - this.languageTestReportUrl - ); - } + String languageTestScore +) { } diff --git a/src/main/java/com/example/solidconnection/score/service/ScoreService.java b/src/main/java/com/example/solidconnection/score/service/ScoreService.java index 45efb2aa1..66592d339 100644 --- a/src/main/java/com/example/solidconnection/score/service/ScoreService.java +++ b/src/main/java/com/example/solidconnection/score/service/ScoreService.java @@ -1,6 +1,10 @@ package com.example.solidconnection.score.service; +import com.example.solidconnection.application.domain.Gpa; import com.example.solidconnection.application.domain.LanguageTest; +import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.s3.S3Service; +import com.example.solidconnection.s3.UploadedFileUrlResponse; import com.example.solidconnection.score.domain.GpaScore; import com.example.solidconnection.score.domain.LanguageTestScore; import com.example.solidconnection.score.dto.GpaScoreRequest; @@ -12,45 +16,70 @@ import com.example.solidconnection.score.repository.GpaScoreRepository; import com.example.solidconnection.score.repository.LanguageTestScoreRepository; import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.type.ImgType; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import static com.example.solidconnection.custom.exception.ErrorCode.USER_NOT_FOUND; + @Service @RequiredArgsConstructor public class ScoreService { private final GpaScoreRepository gpaScoreRepository; + private final S3Service s3Service; private final LanguageTestScoreRepository languageTestScoreRepository; + private final SiteUserRepository siteUserRepository; @Transactional - public Long submitGpaScore(SiteUser siteUser, GpaScoreRequest gpaScoreRequest) { - GpaScore newGpaScore = new GpaScore(gpaScoreRequest.toGpa(), siteUser); - newGpaScore.setSiteUser(siteUser); + public Long submitGpaScore(SiteUser siteUser, GpaScoreRequest gpaScoreRequest, MultipartFile file) { + UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(file, ImgType.GPA); + Gpa gpa = new Gpa(gpaScoreRequest.gpa(), gpaScoreRequest.gpaCriteria(), uploadedFile.fileUrl()); + + /* + * todo: siteUser를 영속 상태로 만들 수 있도록 컨트롤러에서 siteUserId 를 넘겨줄 것인지, + * siteUser 에 gpaScoreList 를 FetchType.EAGER 로 설정할 것인지, + * gpa 와 siteUser 사이의 양방향을 끊을 것인지 생각해봐야한다. + */ + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + GpaScore newGpaScore = new GpaScore(gpa, siteUser1); + newGpaScore.setSiteUser(siteUser1); GpaScore savedNewGpaScore = gpaScoreRepository.save(newGpaScore); // 저장 후 반환된 객체 return savedNewGpaScore.getId(); // 저장된 GPA Score의 ID 반환 } @Transactional - public Long submitLanguageTestScore(SiteUser siteUser, LanguageTestScoreRequest languageTestScoreRequest) { - LanguageTest languageTest = languageTestScoreRequest.toLanguageTest(); + public Long submitLanguageTestScore(SiteUser siteUser, LanguageTestScoreRequest languageTestScoreRequest, MultipartFile file) { + UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(file, ImgType.LANGUAGE_TEST); + LanguageTest languageTest = new LanguageTest(languageTestScoreRequest.languageTestType(), + languageTestScoreRequest.languageTestScore(), uploadedFile.fileUrl()); - LanguageTestScore newScore = new LanguageTestScore( - languageTest, siteUser); - newScore.setSiteUser(siteUser); + /* + * todo: siteUser를 영속 상태로 만들 수 있도록 컨트롤러에서 siteUserId 를 넘겨줄 것인지, + * siteUser 에 languageTestScoreList 를 FetchType.EAGER 로 설정할 것인지, + * languageTest 와 siteUser 사이의 양방향을 끊을 것인지 생각해봐야한다. + */ + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + LanguageTestScore newScore = new LanguageTestScore(languageTest, siteUser1); + newScore.setSiteUser(siteUser1); LanguageTestScore savedNewScore = languageTestScoreRepository.save(newScore); // 새로 저장한 객체 return savedNewScore.getId(); // 저장된 객체의 ID 반환 } @Transactional(readOnly = true) public GpaScoreStatusResponse getGpaScoreStatus(SiteUser siteUser) { + // todo: ditto + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); List gpaScoreStatusList = - Optional.ofNullable(siteUser.getGpaScoreList()) + Optional.ofNullable(siteUser1.getGpaScoreList()) .map(scores -> scores.stream() .map(GpaScoreStatus::from) .collect(Collectors.toList())) @@ -60,8 +89,10 @@ public GpaScoreStatusResponse getGpaScoreStatus(SiteUser siteUser) { @Transactional(readOnly = true) public LanguageTestScoreStatusResponse getLanguageTestScoreStatus(SiteUser siteUser) { + // todo: ditto + SiteUser siteUser1 = siteUserRepository.findById(siteUser.getId()).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); List languageTestScoreStatusList = - Optional.ofNullable(siteUser.getLanguageTestScoreList()) + Optional.ofNullable(siteUser1.getLanguageTestScoreList()) .map(scores -> scores.stream() .map(LanguageTestScoreStatus::from) .collect(Collectors.toList())) diff --git a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java index fca6cd41e..ee74bb90b 100644 --- a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java @@ -9,9 +9,9 @@ import com.example.solidconnection.community.comment.dto.CommentUpdateResponse; import com.example.solidconnection.community.comment.dto.PostFindCommentResponse; import com.example.solidconnection.community.comment.repository.CommentRepository; -import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.community.post.domain.Post; import com.example.solidconnection.community.post.repository.PostRepository; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.support.integration.BaseIntegrationTest; import com.example.solidconnection.type.PostCategory; @@ -110,12 +110,11 @@ class 댓글_생성_테스트 { void 댓글을_성공적으로_생성한다() { // given Post testPost = createPost(자유게시판, 테스트유저_1); - CommentCreateRequest request = new CommentCreateRequest("테스트 댓글", null); + CommentCreateRequest request = new CommentCreateRequest(testPost.getId(), "테스트 댓글", null); // when CommentCreateResponse response = commentService.createComment( 테스트유저_1, - testPost.getId(), request ); @@ -135,12 +134,11 @@ class 댓글_생성_테스트 { // given Post testPost = createPost(자유게시판, 테스트유저_1); Comment parentComment = createComment(testPost, 테스트유저_1, "부모 댓글"); - CommentCreateRequest request = new CommentCreateRequest("테스트 대댓글", parentComment.getId()); + CommentCreateRequest request = new CommentCreateRequest(testPost.getId(), "테스트 대댓글", parentComment.getId()); // when CommentCreateResponse response = commentService.createComment( 테스트유저_2, - testPost.getId(), request ); @@ -161,13 +159,12 @@ class 댓글_생성_테스트 { Post testPost = createPost(자유게시판, 테스트유저_1); Comment parentComment = createComment(testPost, 테스트유저_1, "부모 댓글"); Comment childComment = createChildComment(testPost, 테스트유저_2, parentComment, "자식 댓글"); - CommentCreateRequest request = new CommentCreateRequest("테스트 대대댓글", childComment.getId()); + CommentCreateRequest request = new CommentCreateRequest(testPost.getId(), "테스트 대대댓글", childComment.getId()); // when & then assertThatThrownBy(() -> commentService.createComment( 테스트유저_1, - testPost.getId(), request )) .isInstanceOf(CustomException.class) @@ -179,13 +176,12 @@ class 댓글_생성_테스트 { // given Post testPost = createPost(자유게시판, 테스트유저_1); long invalidCommentId = 9999L; - CommentCreateRequest request = new CommentCreateRequest("테스트 대댓글", invalidCommentId); + CommentCreateRequest request = new CommentCreateRequest(testPost.getId(), "테스트 대댓글", invalidCommentId); // when & then assertThatThrownBy(() -> commentService.createComment( 테스트유저_1, - testPost.getId(), request )) .isInstanceOf(CustomException.class) @@ -206,7 +202,6 @@ class 댓글_수정_테스트 { // when CommentUpdateResponse response = commentService.updateComment( 테스트유저_1, - testPost.getId(), comment.getId(), request ); @@ -233,7 +228,6 @@ class 댓글_수정_테스트 { assertThatThrownBy(() -> commentService.updateComment( 테스트유저_2, - testPost.getId(), comment.getId(), request )) @@ -252,7 +246,6 @@ class 댓글_수정_테스트 { assertThatThrownBy(() -> commentService.updateComment( 테스트유저_1, - testPost.getId(), comment.getId(), request )) @@ -276,7 +269,6 @@ class 댓글_삭제_테스트 { // when CommentDeleteResponse response = commentService.deleteCommentById( 테스트유저_1, - testPost.getId(), comment.getId() ); @@ -301,7 +293,6 @@ class 댓글_삭제_테스트 { // when CommentDeleteResponse response = commentService.deleteCommentById( 테스트유저_1, - testPost.getId(), parentComment.getId() ); @@ -331,7 +322,6 @@ class 댓글_삭제_테스트 { // when CommentDeleteResponse response = commentService.deleteCommentById( 테스트유저_2, - testPost.getId(), childComment1.getId() ); @@ -362,7 +352,6 @@ class 댓글_삭제_테스트 { // when CommentDeleteResponse response = commentService.deleteCommentById( 테스트유저_2, - testPost.getId(), childComment.getId() ); @@ -384,7 +373,6 @@ class 댓글_삭제_테스트 { assertThatThrownBy(() -> commentService.deleteCommentById( 테스트유저_2, - testPost.getId(), comment.getId() )) .isInstanceOf(CustomException.class) diff --git a/src/test/java/com/example/solidconnection/community/post/service/PostCommandServiceTest.java b/src/test/java/com/example/solidconnection/community/post/service/PostCommandServiceTest.java index a8052a89c..328a1dc41 100644 --- a/src/test/java/com/example/solidconnection/community/post/service/PostCommandServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/post/service/PostCommandServiceTest.java @@ -1,16 +1,16 @@ package com.example.solidconnection.community.post.service; import com.example.solidconnection.community.board.domain.Board; -import com.example.solidconnection.custom.exception.CustomException; -import com.example.solidconnection.community.post.domain.PostImage; import com.example.solidconnection.community.post.domain.Post; +import com.example.solidconnection.community.post.domain.PostImage; import com.example.solidconnection.community.post.dto.PostCreateRequest; import com.example.solidconnection.community.post.dto.PostCreateResponse; import com.example.solidconnection.community.post.dto.PostDeleteResponse; import com.example.solidconnection.community.post.dto.PostUpdateRequest; import com.example.solidconnection.community.post.dto.PostUpdateResponse; -import com.example.solidconnection.community.post.repository.PostRepository; import com.example.solidconnection.community.post.repository.PostImageRepository; +import com.example.solidconnection.community.post.repository.PostRepository; +import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.s3.S3Service; import com.example.solidconnection.s3.UploadedFileUrlResponse; import com.example.solidconnection.service.RedisService; @@ -37,9 +37,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.any; import static org.mockito.BDDMockito.eq; +import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @DisplayName("게시글 생성/수정/삭제 서비스 테스트") @@ -79,7 +79,6 @@ class 게시글_생성_테스트 { // when PostCreateResponse response = postCommandService.createPost( 테스트유저_1, - 자유게시판.getCode(), request, imageFiles ); @@ -108,7 +107,7 @@ class 게시글_생성_테스트 { // when & then assertThatThrownBy(() -> - postCommandService.createPost(테스트유저_1, 자유게시판.getCode(), request, imageFiles)) + postCommandService.createPost(테스트유저_1, request, imageFiles)) .isInstanceOf(CustomException.class) .hasMessage(INVALID_POST_CATEGORY.getMessage()); } @@ -121,7 +120,7 @@ class 게시글_생성_테스트 { // when & then assertThatThrownBy(() -> - postCommandService.createPost(테스트유저_1, 자유게시판.getCode(), request, imageFiles)) + postCommandService.createPost(테스트유저_1, request, imageFiles)) .isInstanceOf(CustomException.class) .hasMessage(INVALID_POST_CATEGORY.getMessage()); } @@ -134,7 +133,7 @@ class 게시글_생성_테스트 { // when & then assertThatThrownBy(() -> - postCommandService.createPost(테스트유저_1, 자유게시판.getCode(), request, imageFiles)) + postCommandService.createPost(테스트유저_1, request, imageFiles)) .isInstanceOf(CustomException.class) .hasMessage(CAN_NOT_UPLOAD_MORE_THAN_FIVE_IMAGES.getMessage()); } @@ -159,7 +158,6 @@ class 게시글_수정_테스트 { // when PostUpdateResponse response = postCommandService.updatePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId(), request, imageFiles @@ -190,7 +188,6 @@ class 게시글_수정_테스트 { assertThatThrownBy(() -> postCommandService.updatePost( 테스트유저_2, - 자유게시판.getCode(), testPost.getId(), request, imageFiles @@ -210,7 +207,6 @@ class 게시글_수정_테스트 { assertThatThrownBy(() -> postCommandService.updatePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId(), request, imageFiles @@ -230,7 +226,6 @@ class 게시글_수정_테스트 { assertThatThrownBy(() -> postCommandService.updatePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId(), request, imageFiles @@ -254,7 +249,6 @@ class 게시글_삭제_테스트 { // when PostDeleteResponse response = postCommandService.deletePostById( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() ); @@ -276,7 +270,6 @@ class 게시글_삭제_테스트 { assertThatThrownBy(() -> postCommandService.deletePostById( 테스트유저_2, - 자유게시판.getCode(), testPost.getId() )) .isInstanceOf(CustomException.class) @@ -292,7 +285,6 @@ class 게시글_삭제_테스트 { assertThatThrownBy(() -> postCommandService.deletePostById( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() )) .isInstanceOf(CustomException.class) @@ -302,6 +294,7 @@ class 게시글_삭제_테스트 { private PostCreateRequest createPostCreateRequest(String category) { return new PostCreateRequest( + 자유게시판.getCode(), category, "테스트 제목", "테스트 내용", diff --git a/src/test/java/com/example/solidconnection/community/post/service/PostLikeServiceTest.java b/src/test/java/com/example/solidconnection/community/post/service/PostLikeServiceTest.java index 1b1e1d2fd..23fa6bf50 100644 --- a/src/test/java/com/example/solidconnection/community/post/service/PostLikeServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/post/service/PostLikeServiceTest.java @@ -45,7 +45,6 @@ class 게시글_좋아요_테스트 { // when PostLikeResponse response = postLikeService.likePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() ); @@ -63,13 +62,12 @@ class 게시글_좋아요_테스트 { void 이미_좋아요한_게시글을_다시_좋아요하면_예외_응답을_반환한다() { // given Post testPost = createPost(자유게시판, 테스트유저_1); - postLikeService.likePost(테스트유저_1, 자유게시판.getCode(), testPost.getId()); + postLikeService.likePost(테스트유저_1, testPost.getId()); // when & then assertThatThrownBy(() -> postLikeService.likePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() )) .isInstanceOf(CustomException.class) @@ -84,13 +82,12 @@ class 게시글_좋아요_취소_테스트 { void 게시글_좋아요를_성공적으로_취소한다() { // given Post testPost = createPost(자유게시판, 테스트유저_1); - PostLikeResponse beforeResponse = postLikeService.likePost(테스트유저_1, 자유게시판.getCode(), testPost.getId()); + PostLikeResponse beforeResponse = postLikeService.likePost(테스트유저_1, testPost.getId()); long beforeLikeCount = beforeResponse.likeCount(); // when PostDislikeResponse response = postLikeService.dislikePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() ); @@ -113,7 +110,6 @@ class 게시글_좋아요_취소_테스트 { assertThatThrownBy(() -> postLikeService.dislikePost( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() )) .isInstanceOf(CustomException.class) diff --git a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java index 33246e981..fc7926698 100644 --- a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java @@ -112,7 +112,6 @@ class PostQueryServiceTest extends BaseIntegrationTest { // when PostFindResponse response = postQueryService.findPostById( 테스트유저_1, - 자유게시판.getCode(), testPost.getId() ); diff --git a/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java b/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java index 3903f31ff..52b9f24f0 100644 --- a/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java +++ b/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java @@ -103,8 +103,8 @@ private Post createPost(Board board, SiteUser siteUser) { SiteUser tmpSiteUser = siteUserRepository.save(createSiteUserByEmail(email)); executorService.submit(() -> { try { - postLikeService.likePost(tmpSiteUser, board.getCode(), post.getId()); - postLikeService.dislikePost(tmpSiteUser, board.getCode(), post.getId()); + postLikeService.likePost(tmpSiteUser, post.getId()); + postLikeService.dislikePost(tmpSiteUser, post.getId()); } finally { doneSignal.countDown(); } diff --git a/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java b/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java index fa2cf0b0b..868eac179 100644 --- a/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java +++ b/src/test/java/com/example/solidconnection/e2e/ApplicantsQueryTest.java @@ -105,7 +105,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) .when().log().all() - .get("/application") + .get("/applications") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -151,7 +151,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) .when().log().all() - .get("/application?region=" + 영미권.getCode()) + .get("/applications?region=" + 영미권.getCode()) .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -178,7 +178,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) .when().log().all() - .get("/application?keyword=라") + .get("/applications?keyword=라") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -204,7 +204,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) .when().log().all() - .get("/application?keyword=일본") + .get("/applications?keyword=일본") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -224,7 +224,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) .when().log().all() - .get("/application") + .get("/applications") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -253,7 +253,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + accessToken) .when().log().all() - .get("/application/competitors") + .get("/applications/competitors") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -295,7 +295,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + user6AccessToken) .when().log().all() - .get("/application/competitors") + .get("/applications/competitors") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); @@ -316,7 +316,7 @@ public void setUpUserAndToken() { ApplicationsResponse response = RestAssured.given().log().all() .header("Authorization", "Bearer " + adminAccessToken) .when().log().all() - .get("/application/competitors") + .get("/applications/competitors") .then().log().all() .statusCode(200) .extract().as(ApplicationsResponse.class); diff --git a/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java b/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java index 038aa91b6..0617e1c25 100644 --- a/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java +++ b/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java @@ -2,6 +2,8 @@ import com.example.solidconnection.application.domain.Gpa; import com.example.solidconnection.application.domain.LanguageTest; +import com.example.solidconnection.s3.S3Service; +import com.example.solidconnection.s3.UploadedFileUrlResponse; import com.example.solidconnection.score.domain.GpaScore; import com.example.solidconnection.score.domain.LanguageTestScore; import com.example.solidconnection.score.dto.GpaScoreRequest; @@ -16,6 +18,7 @@ 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.LanguageTestType; import com.example.solidconnection.type.PreparationStatus; import com.example.solidconnection.type.Role; @@ -23,11 +26,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.BDDMockito.given; @DisplayName("점수 서비스 테스트") class ScoreServiceTest extends BaseIntegrationTest { @@ -44,6 +50,9 @@ class ScoreServiceTest extends BaseIntegrationTest { @Autowired private LanguageTestScoreRepository languageTestScoreRepository; + @MockBean + private S3Service s3Service; + @Test void GPA_점수_상태를_조회한다() { // given @@ -118,9 +127,12 @@ class ScoreServiceTest extends BaseIntegrationTest { // given SiteUser testUser = createSiteUser(); GpaScoreRequest request = createGpaScoreRequest(); + MockMultipartFile file = createFile(); + String fileUrl = "/gpa-report.pdf"; + given(s3Service.uploadFile(file, ImgType.GPA)).willReturn(new UploadedFileUrlResponse(fileUrl)); // when - long scoreId = scoreService.submitGpaScore(testUser, request); + long scoreId = scoreService.submitGpaScore(testUser, request, file); GpaScore savedScore = gpaScoreRepository.findById(scoreId).orElseThrow(); // then @@ -128,7 +140,8 @@ class ScoreServiceTest extends BaseIntegrationTest { () -> assertThat(savedScore.getId()).isEqualTo(scoreId), () -> assertThat(savedScore.getGpa().getGpa()).isEqualTo(request.gpa()), () -> assertThat(savedScore.getGpa().getGpaCriteria()).isEqualTo(request.gpaCriteria()), - () -> assertThat(savedScore.getVerifyStatus()).isEqualTo(VerifyStatus.PENDING) + () -> assertThat(savedScore.getVerifyStatus()).isEqualTo(VerifyStatus.PENDING), + () -> assertThat(savedScore.getGpa().getGpaReportUrl()).isEqualTo(fileUrl) ); } @@ -137,9 +150,12 @@ class ScoreServiceTest extends BaseIntegrationTest { // given SiteUser testUser = createSiteUser(); LanguageTestScoreRequest request = createLanguageTestScoreRequest(); + MockMultipartFile file = createFile(); + String fileUrl = "/gpa-report.pdf"; + given(s3Service.uploadFile(file, ImgType.LANGUAGE_TEST)).willReturn(new UploadedFileUrlResponse(fileUrl)); // when - long scoreId = scoreService.submitLanguageTestScore(testUser, request); + long scoreId = scoreService.submitLanguageTestScore(testUser, request, file); LanguageTestScore savedScore = languageTestScoreRepository.findById(scoreId).orElseThrow(); // then @@ -147,7 +163,8 @@ class ScoreServiceTest extends BaseIntegrationTest { () -> assertThat(savedScore.getId()).isEqualTo(scoreId), () -> assertThat(savedScore.getLanguageTest().getLanguageTestType()).isEqualTo(request.languageTestType()), () -> assertThat(savedScore.getLanguageTest().getLanguageTestScore()).isEqualTo(request.languageTestScore()), - () -> assertThat(savedScore.getVerifyStatus()).isEqualTo(VerifyStatus.PENDING) + () -> assertThat(savedScore.getVerifyStatus()).isEqualTo(VerifyStatus.PENDING), + () -> assertThat(savedScore.getLanguageTest().getLanguageTestReportUrl()).isEqualTo(fileUrl) ); } @@ -185,16 +202,23 @@ private LanguageTestScore createLanguageTestScore(SiteUser siteUser, LanguageTes private GpaScoreRequest createGpaScoreRequest() { return new GpaScoreRequest( 3.5, - 4.5, - "/gpa-report.pdf" + 4.5 ); } private LanguageTestScoreRequest createLanguageTestScoreRequest() { return new LanguageTestScoreRequest( LanguageTestType.TOEFL_IBT, - "100", - "/gpa-report.pdf" + "100" + ); + } + + private MockMultipartFile createFile() { + return new MockMultipartFile( + "image", + "test.jpg", + "image/jpeg", + "test image content".getBytes() ); } } From 74050187550ba1e5a2d130db78d681b21992b683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=A9=EA=B7=9C=ED=98=81?= <126947828+Gyuhyeok99@users.noreply.github.com> Date: Sat, 15 Feb 2025 14:22:48 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20university=5Finfo=5Ffor=5Fapply=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20details=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EA=B8=B8=EC=9D=B4=20=EC=A6=9D=EA=B0=80=20(#212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: details 컬럼 길이 500 → 1000 확장 * feat: Flyway 마이그레이션 스크립트 추가 --- .../university/domain/UniversityInfoForApply.java | 2 +- .../resources/db/migration/V7__expand_details_column_length.sql | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/migration/V7__expand_details_column_length.sql diff --git a/src/main/java/com/example/solidconnection/university/domain/UniversityInfoForApply.java b/src/main/java/com/example/solidconnection/university/domain/UniversityInfoForApply.java index 6a1cdf4dd..e1a87fe83 100644 --- a/src/main/java/com/example/solidconnection/university/domain/UniversityInfoForApply.java +++ b/src/main/java/com/example/solidconnection/university/domain/UniversityInfoForApply.java @@ -73,7 +73,7 @@ public class UniversityInfoForApply { @Column(length = 1000) private String detailsForEnglishCourse; - @Column(length = 500) + @Column(length = 1000) private String details; @OneToMany(mappedBy = "universityInfoForApply", fetch = FetchType.EAGER) diff --git a/src/main/resources/db/migration/V7__expand_details_column_length.sql b/src/main/resources/db/migration/V7__expand_details_column_length.sql new file mode 100644 index 000000000..452cc12a7 --- /dev/null +++ b/src/main/resources/db/migration/V7__expand_details_column_length.sql @@ -0,0 +1,2 @@ +ALTER TABLE university_info_for_apply + modify details VARCHAR(1000) NULL; \ No newline at end of file From dc31e4d8cd83bd71558b9d1f61d0d9e975eef0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=A9=EA=B7=9C=ED=98=81?= <126947828+Gyuhyeok99@users.noreply.github.com> Date: Sat, 15 Feb 2025 16:03:59 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20authType?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/solidconnection/siteuser/dto/MyPageResponse.java | 3 +++ .../solidconnection/siteuser/service/SiteUserServiceTest.java | 1 + 2 files changed, 4 insertions(+) diff --git a/src/main/java/com/example/solidconnection/siteuser/dto/MyPageResponse.java b/src/main/java/com/example/solidconnection/siteuser/dto/MyPageResponse.java index 66a7dbef2..9af5e6b2d 100644 --- a/src/main/java/com/example/solidconnection/siteuser/dto/MyPageResponse.java +++ b/src/main/java/com/example/solidconnection/siteuser/dto/MyPageResponse.java @@ -1,5 +1,6 @@ package com.example.solidconnection.siteuser.dto; +import com.example.solidconnection.siteuser.domain.AuthType; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.type.Role; @@ -7,6 +8,7 @@ public record MyPageResponse( String nickname, String profileImageUrl, Role role, + AuthType authType, String birth, String email, int likedPostCount, @@ -18,6 +20,7 @@ public static MyPageResponse of(SiteUser siteUser, int likedUniversityCount) { siteUser.getNickname(), siteUser.getProfileImageUrl(), siteUser.getRole(), + siteUser.getAuthType(), siteUser.getBirth(), siteUser.getEmail(), 0, // TODO: 커뮤니티 기능 생기면 업데이트 필요 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 cb256bc0f..c6236aedf 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java @@ -69,6 +69,7 @@ class SiteUserServiceTest extends BaseIntegrationTest { () -> 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()),