From f960914fb97cdc9bc00e4ff41bcf5aa58dc8d92b Mon Sep 17 00:00:00 2001 From: dungbik Date: Tue, 22 Jul 2025 16:57:24 +0900 Subject: [PATCH 1/6] =?UTF-8?q?Feat:=20=EB=82=B4=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flipnote/auth/service/AuthService.java | 12 ++++++------ .../auth/service/TokenVersionService.java | 5 +++++ .../flipnote/user/controller/UserController.java | 11 +++++++++++ .../java/project/flipnote/user/entity/User.java | 4 ++++ .../user/model/ChangePasswordRequest.java | 13 +++++++++++++ .../flipnote/user/repository/UserRepository.java | 4 ++++ .../flipnote/user/service/UserService.java | 16 ++++++++++++++++ 7 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/main/java/project/flipnote/user/model/ChangePasswordRequest.java diff --git a/src/main/java/project/flipnote/auth/service/AuthService.java b/src/main/java/project/flipnote/auth/service/AuthService.java index f725d6cb..ee0a0db5 100644 --- a/src/main/java/project/flipnote/auth/service/AuthService.java +++ b/src/main/java/project/flipnote/auth/service/AuthService.java @@ -85,6 +85,12 @@ public TokenPair refreshToken(String refreshToken) { return jwtComponent.generateTokenPair(userAuth); } + public void validatePasswordMatch(String rawPassword, String encodedPassword) { + if (!passwordEncoder.matches(rawPassword, encodedPassword)) { + throw new BizException(AuthErrorCode.INVALID_CREDENTIALS); + } + } + private String generateVerificationCode(int length) { int origin = (int)Math.pow(10, length - 1); int bound = (int)Math.pow(10, length); @@ -96,12 +102,6 @@ private User findActiveUserByEmail(String email) { .orElseThrow(() -> new BizException(AuthErrorCode.INVALID_CREDENTIALS)); } - private void validatePasswordMatch(String rawPassword, String encodedPassword) { - if (!passwordEncoder.matches(rawPassword, encodedPassword)) { - throw new BizException(AuthErrorCode.INVALID_CREDENTIALS); - } - } - private void validateEmailIsAvailable(String email) { if (userRepository.existsByEmail(email)) { throw new BizException(AuthErrorCode.EXISTING_EMAIL); diff --git a/src/main/java/project/flipnote/auth/service/TokenVersionService.java b/src/main/java/project/flipnote/auth/service/TokenVersionService.java index c89aa613..d71d27b4 100644 --- a/src/main/java/project/flipnote/auth/service/TokenVersionService.java +++ b/src/main/java/project/flipnote/auth/service/TokenVersionService.java @@ -25,4 +25,9 @@ public Optional findTokenVersion(long userId) { return dbTokenVersion; }); } + + public void incrementTokenVersion(long userId) { + userRepository.incrementTokenVersion(userId); + tokenVersionRedisRepository.deleteTokenVersion(userId); + } } diff --git a/src/main/java/project/flipnote/user/controller/UserController.java b/src/main/java/project/flipnote/user/controller/UserController.java index d9333ab0..512dac73 100644 --- a/src/main/java/project/flipnote/user/controller/UserController.java +++ b/src/main/java/project/flipnote/user/controller/UserController.java @@ -5,6 +5,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -16,6 +17,7 @@ import lombok.RequiredArgsConstructor; import project.flipnote.common.security.dto.UserAuth; import project.flipnote.user.model.MyInfoResponse; +import project.flipnote.user.model.ChangePasswordRequest; import project.flipnote.user.model.UserInfoResponse; import project.flipnote.user.model.UserRegisterRequest; import project.flipnote.user.model.UserRegisterResponse; @@ -66,4 +68,13 @@ public ResponseEntity getUserInfo( UserInfoResponse res = userService.getUserInfo(userId); return ResponseEntity.ok(res); } + + @PatchMapping("/me/password") + public ResponseEntity updatePassword( + @AuthenticationPrincipal UserAuth userAuth, + @Valid @RequestBody ChangePasswordRequest req + ) { + userService.changePassword(userAuth.userId(), req); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/project/flipnote/user/entity/User.java b/src/main/java/project/flipnote/user/entity/User.java index 30ba719a..43c3cdcf 100644 --- a/src/main/java/project/flipnote/user/entity/User.java +++ b/src/main/java/project/flipnote/user/entity/User.java @@ -98,4 +98,8 @@ public void update(String nickname, String phone, boolean smsAgree, String profi this.smsAgree = smsAgree; this.profileImageUrl = profileImageUrl; } + + public void changePassword(String password) { + this.password = password; + } } diff --git a/src/main/java/project/flipnote/user/model/ChangePasswordRequest.java b/src/main/java/project/flipnote/user/model/ChangePasswordRequest.java new file mode 100644 index 00000000..18a38992 --- /dev/null +++ b/src/main/java/project/flipnote/user/model/ChangePasswordRequest.java @@ -0,0 +1,13 @@ +package project.flipnote.user.model; + +import project.flipnote.common.validation.annotation.ValidPassword; + +public record ChangePasswordRequest( + + @ValidPassword + String currentPassword, + + @ValidPassword + String newPassword +) { +} diff --git a/src/main/java/project/flipnote/user/repository/UserRepository.java b/src/main/java/project/flipnote/user/repository/UserRepository.java index fcdc109d..20ef9e06 100644 --- a/src/main/java/project/flipnote/user/repository/UserRepository.java +++ b/src/main/java/project/flipnote/user/repository/UserRepository.java @@ -3,6 +3,7 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -22,4 +23,7 @@ public interface UserRepository extends JpaRepository { @Query("SELECT u.tokenVersion FROM User u WHERE u.id = :userId") Optional findTokenVersionById(@Param("userId") Long userId); + @Modifying + @Query("UPDATE User u SET u.tokenVersion = u.tokenVersion + 1 WHERE u.id = :id") + void incrementTokenVersion(@Param("id") Long userId); } diff --git a/src/main/java/project/flipnote/user/service/UserService.java b/src/main/java/project/flipnote/user/service/UserService.java index 0422b52f..cef143df 100644 --- a/src/main/java/project/flipnote/user/service/UserService.java +++ b/src/main/java/project/flipnote/user/service/UserService.java @@ -9,11 +9,14 @@ import lombok.RequiredArgsConstructor; import project.flipnote.auth.repository.EmailVerificationRedisRepository; import project.flipnote.auth.repository.TokenVersionRedisRepository; +import project.flipnote.auth.service.AuthService; +import project.flipnote.auth.service.TokenVersionService; import project.flipnote.common.exception.BizException; import project.flipnote.user.entity.User; import project.flipnote.user.entity.UserStatus; import project.flipnote.user.exception.UserErrorCode; import project.flipnote.user.model.MyInfoResponse; +import project.flipnote.user.model.ChangePasswordRequest; import project.flipnote.user.model.UserInfoResponse; import project.flipnote.user.model.UserRegisterRequest; import project.flipnote.user.model.UserRegisterResponse; @@ -30,6 +33,8 @@ public class UserService { private final PasswordEncoder passwordEncoder; private final TokenVersionRedisRepository tokenVersionRedisRepository; private final EmailVerificationRedisRepository emailVerificationRedisRepository; + private final AuthService authService; + private final TokenVersionService tokenVersionService; @Transactional public UserRegisterResponse register(UserRegisterRequest req) { @@ -93,6 +98,17 @@ public UserInfoResponse getUserInfo(Long userId) { return UserInfoResponse.from(user); } + @Transactional + public void changePassword(Long userId, ChangePasswordRequest req) { + User user = findActiveUserById(userId); + + authService.validatePasswordMatch(req.currentPassword(), user.getPassword()); + + user.changePassword(passwordEncoder.encode(req.newPassword())); + + tokenVersionService.incrementTokenVersion(userId); + } + private User findActiveUserById(Long userId) { return userRepository.findByIdAndStatus(userId, UserStatus.ACTIVE) .orElseThrow(() -> new BizException(UserErrorCode.USER_NOT_FOUND)); From d877eb0315e9c91939910321dfec4c9b6d7b86b3 Mon Sep 17 00:00:00 2001 From: dungbik Date: Tue, 22 Jul 2025 16:57:40 +0900 Subject: [PATCH 2/6] =?UTF-8?q?Test:=20=EB=82=B4=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/service/UserServiceTest.java | 83 +++++++++++++++++-- 1 file changed, 75 insertions(+), 8 deletions(-) diff --git a/src/test/java/project/flipnote/user/service/UserServiceTest.java b/src/test/java/project/flipnote/user/service/UserServiceTest.java index 7a7107ca..4675d5ef 100644 --- a/src/test/java/project/flipnote/user/service/UserServiceTest.java +++ b/src/test/java/project/flipnote/user/service/UserServiceTest.java @@ -15,16 +15,20 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.crypto.password.PasswordEncoder; +import project.flipnote.auth.exception.AuthErrorCode; import project.flipnote.auth.repository.EmailVerificationRedisRepository; import project.flipnote.auth.repository.TokenVersionRedisRepository; +import project.flipnote.auth.service.AuthService; +import project.flipnote.auth.service.TokenVersionService; import project.flipnote.common.exception.BizException; import project.flipnote.fixture.UserFixture; import project.flipnote.user.entity.User; import project.flipnote.user.entity.UserStatus; import project.flipnote.user.exception.UserErrorCode; -import project.flipnote.user.model.UserRegisterRequest; -import project.flipnote.user.model.UserInfoResponse; import project.flipnote.user.model.MyInfoResponse; +import project.flipnote.user.model.ChangePasswordRequest; +import project.flipnote.user.model.UserInfoResponse; +import project.flipnote.user.model.UserRegisterRequest; import project.flipnote.user.model.UserRegisterResponse; import project.flipnote.user.model.UserUpdateRequest; import project.flipnote.user.model.UserUpdateResponse; @@ -49,6 +53,13 @@ class UserServiceTest { @Mock EmailVerificationRedisRepository emailVerificationRedisRepository; + // UserService의 새로운 의존성 Mock 객체 추가 + @Mock + AuthService authService; + + @Mock + TokenVersionService tokenVersionService; + @DisplayName("회원가입 테스트") @Nested class Register { @@ -67,7 +78,6 @@ void success() { given(passwordEncoder.encode(any(String.class))).willReturn("encodedPass"); given(userRepository.save(any(User.class))).willReturn(user); - UserRegisterResponse res = userService.register(req); assertThat(res.userId()).isEqualTo(user.getId()); @@ -154,10 +164,8 @@ void success() { userService.unregister(user.getId()); - assertThat(user.getStatus()).isEqualTo(UserStatus.INACTIVE); - assertThat(user.getDeletedAt()).isNotNull(); - - verify(user, times(1)).softDelete(); + // user.unregister()가 호출되었는지 spy 객체를 통해 검증 + verify(user, times(1)).unregister(); verify(tokenVersionRedisRepository, times(1)).deleteTokenVersion(anyLong()); } @@ -317,4 +325,63 @@ void fail_userNotFound() { assertThat(exception.getErrorCode()).isEqualTo(UserErrorCode.USER_NOT_FOUND); } } -} + + @DisplayName("비밀번호 변경 테스트") + @Nested + class ChangePassword { + + @DisplayName("성공") + @Test + void success() { + User user = spy(UserFixture.createActiveUser()); + ChangePasswordRequest req = new ChangePasswordRequest("currentPassword123!", "newPassword123!"); + String encodedNewPassword = "encodedNewPassword"; + + given(userRepository.findByIdAndStatus(user.getId(), UserStatus.ACTIVE)).willReturn(Optional.of(user)); + given(passwordEncoder.encode(req.newPassword())).willReturn(encodedNewPassword); + + userService.changePassword(user.getId(), req); + + verify(user, times(1)).changePassword(encodedNewPassword); + verify(tokenVersionService, times(1)).incrementTokenVersion(user.getId()); + } + + @DisplayName("존재하지 않는 회원의 비밀번호 변경 시 예외 발생") + @Test + void fail_userNotFound() { + ChangePasswordRequest req = new ChangePasswordRequest("currentPassword123!", "newPassword123!"); + Long nonExistentUserId = 99L; + + given(userRepository.findByIdAndStatus(nonExistentUserId, UserStatus.ACTIVE)).willReturn(Optional.empty()); + + BizException exception = assertThrows(BizException.class, + () -> userService.changePassword(nonExistentUserId, req)); + + assertThat(exception.getErrorCode()).isEqualTo(UserErrorCode.USER_NOT_FOUND); + + verify(passwordEncoder, never()).matches(anyString(), anyString()); + verify(passwordEncoder, never()).encode(anyString()); + verify(tokenVersionRedisRepository, never()).deleteTokenVersion(anyLong()); + } + + @DisplayName("현재 비밀번호가 일치하지 않을 경우 예외 발생") + @Test + void fail_incorrectCurrentPassword() { + User user = UserFixture.createActiveUser(); + ChangePasswordRequest req = new ChangePasswordRequest("wrongPassword", "newPassword123!"); + + given(userRepository.findByIdAndStatus(user.getId(), UserStatus.ACTIVE)).willReturn(Optional.of(user)); + doThrow(new BizException(AuthErrorCode.INVALID_CREDENTIALS)) + .when(authService) + .validatePasswordMatch(req.currentPassword(), user.getPassword()); + + BizException exception = assertThrows(BizException.class, + () -> userService.changePassword(user.getId(), req)); + + assertThat(exception.getErrorCode()).isEqualTo(AuthErrorCode.INVALID_CREDENTIALS); + verify(authService, times(1)).validatePasswordMatch(req.currentPassword(), user.getPassword()); + verify(passwordEncoder, never()).encode(anyString()); + verify(tokenVersionRedisRepository, never()).deleteTokenVersion(anyLong()); + } + } +} \ No newline at end of file From afe02574fb6b413bf186b7ce8fdd5bbc55a19dee Mon Sep 17 00:00:00 2001 From: dungbik Date: Tue, 22 Jul 2025 17:01:05 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Feat:=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=EC=8B=9C=20=EB=AA=A8=EB=93=A0=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=9D=B4=20=ED=92=80=EB=A6=AC=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/project/flipnote/user/service/UserService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/project/flipnote/user/service/UserService.java b/src/main/java/project/flipnote/user/service/UserService.java index cef143df..b1bd77b2 100644 --- a/src/main/java/project/flipnote/user/service/UserService.java +++ b/src/main/java/project/flipnote/user/service/UserService.java @@ -69,7 +69,8 @@ public void unregister(Long userId) { User user = findActiveUserById(userId); user.unregister(); - tokenVersionRedisRepository.deleteTokenVersion(userId); + + tokenVersionService.incrementTokenVersion(userId); } @Transactional From 069f2032a032541a47703dcc45bc37f91e7dc2cd Mon Sep 17 00:00:00 2001 From: dungbik Date: Tue, 22 Jul 2025 17:28:28 +0900 Subject: [PATCH 4/6] =?UTF-8?q?Refactor:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=97=AC=EB=B6=80=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20=EB=B3=84=EB=8F=84=EC=9D=98=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/EmailVerificationService.java | 21 +++++++++++++++++++ .../flipnote/user/service/UserService.java | 14 ++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 src/main/java/project/flipnote/auth/service/EmailVerificationService.java diff --git a/src/main/java/project/flipnote/auth/service/EmailVerificationService.java b/src/main/java/project/flipnote/auth/service/EmailVerificationService.java new file mode 100644 index 00000000..2813b878 --- /dev/null +++ b/src/main/java/project/flipnote/auth/service/EmailVerificationService.java @@ -0,0 +1,21 @@ +package project.flipnote.auth.service; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import project.flipnote.auth.repository.EmailVerificationRedisRepository; +import project.flipnote.common.exception.BizException; +import project.flipnote.user.exception.UserErrorCode; + +@RequiredArgsConstructor +@Service +public class EmailVerificationService { + + private final EmailVerificationRedisRepository emailVerificationRedisRepository; + + public void validateVerified(String email) { + if (!emailVerificationRedisRepository.isVerified(email)) { + throw new BizException(UserErrorCode.UNVERIFIED_EMAIL); + } + } +} diff --git a/src/main/java/project/flipnote/user/service/UserService.java b/src/main/java/project/flipnote/user/service/UserService.java index b1bd77b2..930de5ba 100644 --- a/src/main/java/project/flipnote/user/service/UserService.java +++ b/src/main/java/project/flipnote/user/service/UserService.java @@ -8,8 +8,8 @@ import lombok.RequiredArgsConstructor; import project.flipnote.auth.repository.EmailVerificationRedisRepository; -import project.flipnote.auth.repository.TokenVersionRedisRepository; import project.flipnote.auth.service.AuthService; +import project.flipnote.auth.service.EmailVerificationService; import project.flipnote.auth.service.TokenVersionService; import project.flipnote.common.exception.BizException; import project.flipnote.user.entity.User; @@ -31,10 +31,9 @@ public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; - private final TokenVersionRedisRepository tokenVersionRedisRepository; - private final EmailVerificationRedisRepository emailVerificationRedisRepository; private final AuthService authService; private final TokenVersionService tokenVersionService; + private final EmailVerificationService emailVerificationService; @Transactional public UserRegisterResponse register(UserRegisterRequest req) { @@ -43,10 +42,7 @@ public UserRegisterResponse register(UserRegisterRequest req) { validateEmailDuplicate(email); validatePhoneDuplicate(phone); - - if (!emailVerificationRedisRepository.isVerified(email)) { - throw new BizException(UserErrorCode.UNVERIFIED_EMAIL); - } + emailVerificationService.validateVerified(email); User user = User.builder() .email(email) @@ -59,8 +55,6 @@ public UserRegisterResponse register(UserRegisterRequest req) { .build(); User savedUser = userRepository.save(user); - emailVerificationRedisRepository.deleteVerified(email); - return UserRegisterResponse.from(savedUser.getId()); } @@ -69,7 +63,7 @@ public void unregister(Long userId) { User user = findActiveUserById(userId); user.unregister(); - + tokenVersionService.incrementTokenVersion(userId); } From c5bced95e0c592fca90fcab4b2c0f52bf5073f0b Mon Sep 17 00:00:00 2001 From: dungbik Date: Tue, 22 Jul 2025 17:29:36 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Refactor:=20UNVERIFIED=5FEMAIL=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=BD=94=EB=93=9C=20Auth=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/project/flipnote/auth/exception/AuthErrorCode.java | 3 ++- .../flipnote/auth/service/EmailVerificationService.java | 4 ++-- .../java/project/flipnote/user/exception/UserErrorCode.java | 4 +--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/project/flipnote/auth/exception/AuthErrorCode.java b/src/main/java/project/flipnote/auth/exception/AuthErrorCode.java index 5e28f714..b31ab2cc 100644 --- a/src/main/java/project/flipnote/auth/exception/AuthErrorCode.java +++ b/src/main/java/project/flipnote/auth/exception/AuthErrorCode.java @@ -15,7 +15,8 @@ public enum AuthErrorCode implements ErrorCode { NOT_ISSUED_VERIFICATION_CODE(HttpStatus.BAD_REQUEST, "AUTH_003", "발급된 인증번호가 없습니다. 인증번호를 먼저 요청해 주세요."), INVALID_VERIFICATION_CODE(HttpStatus.FORBIDDEN, "AUTH_004", "잘못된 인증번호입니다. 입력한 인증번호를 확인해 주세요."), EXISTING_EMAIL(HttpStatus.CONFLICT, "AUTH_005", "이미 가입된 이메일입니다. 다른 이메일을 사용해 주세요."), - INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "AUTH_006", "인증 정보가 유효하지 않습니다."); + INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "AUTH_006", "인증 정보가 유효하지 않습니다."), + UNVERIFIED_EMAIL(HttpStatus.FORBIDDEN, "AUTH_007", "인증되지 않은 이메일입니다. 이메일 인증을 완료해 주세요."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/project/flipnote/auth/service/EmailVerificationService.java b/src/main/java/project/flipnote/auth/service/EmailVerificationService.java index 2813b878..fb6fa4ab 100644 --- a/src/main/java/project/flipnote/auth/service/EmailVerificationService.java +++ b/src/main/java/project/flipnote/auth/service/EmailVerificationService.java @@ -3,9 +3,9 @@ import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; +import project.flipnote.auth.exception.AuthErrorCode; import project.flipnote.auth.repository.EmailVerificationRedisRepository; import project.flipnote.common.exception.BizException; -import project.flipnote.user.exception.UserErrorCode; @RequiredArgsConstructor @Service @@ -15,7 +15,7 @@ public class EmailVerificationService { public void validateVerified(String email) { if (!emailVerificationRedisRepository.isVerified(email)) { - throw new BizException(UserErrorCode.UNVERIFIED_EMAIL); + throw new BizException(AuthErrorCode.UNVERIFIED_EMAIL); } } } diff --git a/src/main/java/project/flipnote/user/exception/UserErrorCode.java b/src/main/java/project/flipnote/user/exception/UserErrorCode.java index 97341064..360076fe 100644 --- a/src/main/java/project/flipnote/user/exception/UserErrorCode.java +++ b/src/main/java/project/flipnote/user/exception/UserErrorCode.java @@ -12,9 +12,7 @@ public enum UserErrorCode implements ErrorCode { DUPLICATE_EMAIL(HttpStatus.CONFLICT, "USER_001", "이미 사용 중인 이메일입니다."), DUPLICATE_PHONE(HttpStatus.CONFLICT, "USER_002", "이미 사용 중인 휴대전화 번호입니다."), - USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_003", "회원이 존재하지 않습니다."), - UNVERIFIED_EMAIL(HttpStatus.FORBIDDEN, "USER_004", "인증되지 않은 이메일입니다. 이메일 인증을 완료해 주세요."); - + USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_003", "회원이 존재하지 않습니다."); private final HttpStatus httpStatus; private final String code; private final String message; From ef91f12bf321c56f5547932df04784d9393354f6 Mon Sep 17 00:00:00 2001 From: dungbik Date: Tue, 22 Jul 2025 17:34:54 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Test:=20=ED=9A=8C=EC=9B=90=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=ED=95=98=EB=8D=98=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flipnote/user/service/UserServiceTest.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/test/java/project/flipnote/user/service/UserServiceTest.java b/src/test/java/project/flipnote/user/service/UserServiceTest.java index 4675d5ef..1afe5071 100644 --- a/src/test/java/project/flipnote/user/service/UserServiceTest.java +++ b/src/test/java/project/flipnote/user/service/UserServiceTest.java @@ -19,6 +19,7 @@ import project.flipnote.auth.repository.EmailVerificationRedisRepository; import project.flipnote.auth.repository.TokenVersionRedisRepository; import project.flipnote.auth.service.AuthService; +import project.flipnote.auth.service.EmailVerificationService; import project.flipnote.auth.service.TokenVersionService; import project.flipnote.common.exception.BizException; import project.flipnote.fixture.UserFixture; @@ -53,13 +54,15 @@ class UserServiceTest { @Mock EmailVerificationRedisRepository emailVerificationRedisRepository; - // UserService의 새로운 의존성 Mock 객체 추가 @Mock AuthService authService; @Mock TokenVersionService tokenVersionService; + @Mock + EmailVerificationService emailVerificationService; + @DisplayName("회원가입 테스트") @Nested class Register { @@ -74,7 +77,6 @@ void success() { given(userRepository.existsByEmail(any(String.class))).willReturn(false); given(userRepository.existsByPhone(any(String.class))).willReturn(false); - given(emailVerificationRedisRepository.isVerified(anyString())).willReturn(true); given(passwordEncoder.encode(any(String.class))).willReturn("encodedPass"); given(userRepository.save(any(User.class))).willReturn(user); @@ -92,7 +94,6 @@ void success_ifPhoneIsNull() { ); given(userRepository.existsByEmail(any(String.class))).willReturn(false); - given(emailVerificationRedisRepository.isVerified(anyString())).willReturn(true); given(passwordEncoder.encode(any(String.class))).willReturn("encodedPass"); given(userRepository.save(any(User.class))).willReturn(user); @@ -142,10 +143,12 @@ void fail_unverifiedEmail() { given(userRepository.existsByEmail(anyString())).willReturn(false); given(userRepository.existsByPhone(anyString())).willReturn(false); - given(emailVerificationRedisRepository.isVerified(anyString())).willReturn(false); + doThrow(new BizException(AuthErrorCode.UNVERIFIED_EMAIL)) + .when(emailVerificationService) + .validateVerified(anyString()); BizException exception = assertThrows(BizException.class, () -> userService.register(req)); - assertThat(exception.getErrorCode()).isEqualTo(UserErrorCode.UNVERIFIED_EMAIL); + assertThat(exception.getErrorCode()).isEqualTo(AuthErrorCode.UNVERIFIED_EMAIL); verify(userRepository, never()).save(any(User.class)); } @@ -164,9 +167,8 @@ void success() { userService.unregister(user.getId()); - // user.unregister()가 호출되었는지 spy 객체를 통해 검증 verify(user, times(1)).unregister(); - verify(tokenVersionRedisRepository, times(1)).deleteTokenVersion(anyLong()); + verify(tokenVersionService, times(1)).incrementTokenVersion(user.getId()); } @DisplayName("회원 id가 존재하지 않는 경우 예외 발생")