From 51ac4596452035e40e8b83d14283045a553c78a2 Mon Sep 17 00:00:00 2001 From: JunsuPark Date: Sun, 11 Aug 2024 01:39:03 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=9C=A0=EC=A0=80=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/example/config/SecurityConfig.java | 1 + app/api/user-api/build.gradle | 3 + .../example/controller/UserController.java | 14 ++++ .../dto/response/UserProfileApiResponse.java | 25 ++++++ .../java/org/example/service/UserService.java | 27 ++++-- .../dto/request/LoginServiceRequest.java | 4 +- .../response/UserProfileServiceResponse.java | 19 +++++ .../org/example/vo/SocialLoginApiType.java | 8 ++ .../fixture/dto/UserRequestDtoFixture.java | 15 ++++ .../org/example/service/UserServiceTest.java | 82 +++++++++++++++++++ .../src/main/resources/schema.sql | 2 +- .../response/UserProfileDomainResponse.java | 10 +++ .../main/java/org/example/entity/User.java | 7 +- .../user/UserQuerydslRepository.java | 5 +- .../user/UserQuerydslRepositoryImpl.java | 28 ++++++- .../java/org/example/usecase/UserUseCase.java | 17 ++-- .../java/org/example/vo/RandomNickname.java | 55 +++++++++++++ .../src/test/java/org/example/QueryTest.java | 16 ++++ .../example/ShowDomainTestConfiguration.java | 11 +++ .../repository/UserRepositoryTest.java | 42 ++++++++++ .../application-user-domain-test.yml | 10 +++ .../example/fixture/SocialLoginFixture.java | 16 ++++ .../java/org/example/fixture/UserFixture.java | 14 ++++ 23 files changed, 410 insertions(+), 21 deletions(-) create mode 100644 app/api/user-api/src/main/java/org/example/controller/dto/response/UserProfileApiResponse.java create mode 100644 app/api/user-api/src/main/java/org/example/service/dto/response/UserProfileServiceResponse.java create mode 100644 app/api/user-api/src/test/java/org/example/fixture/dto/UserRequestDtoFixture.java create mode 100644 app/api/user-api/src/test/java/org/example/service/UserServiceTest.java create mode 100644 app/domain/user-domain/src/main/java/org/example/dto/response/UserProfileDomainResponse.java create mode 100644 app/domain/user-domain/src/main/java/org/example/vo/RandomNickname.java create mode 100644 app/domain/user-domain/src/test/java/org/example/QueryTest.java create mode 100644 app/domain/user-domain/src/test/java/org/example/ShowDomainTestConfiguration.java create mode 100644 app/domain/user-domain/src/test/java/org/example/repository/UserRepositoryTest.java create mode 100644 app/domain/user-domain/src/test/resources/application-user-domain-test.yml create mode 100644 app/domain/user-domain/src/testFixtures/java/org/example/fixture/SocialLoginFixture.java create mode 100644 app/domain/user-domain/src/testFixtures/java/org/example/fixture/UserFixture.java diff --git a/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java b/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java index 8d56d0de..b19dbc3c 100644 --- a/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java +++ b/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java @@ -87,6 +87,7 @@ private RequestMatcher getMatcherForUserAndAdmin() { antMatcher(HttpMethod.GET, "/api/v1/shows/interests"), antMatcher(HttpMethod.POST, "/api/v1/users/logout"), antMatcher(HttpMethod.POST, "/api/v1/users/withdrawal"), + antMatcher(HttpMethod.GET, "/api/v1/users/profile"), antMatcher(HttpMethod.POST, "/api/v1/shows/**/interest"), antMatcher(HttpMethod.POST, "/api/v1/shows/**/alert"), antMatcher(HttpMethod.POST, "/api/v1/genres/subscribe"), diff --git a/app/api/user-api/build.gradle b/app/api/user-api/build.gradle index e1d2e32e..a1a7f233 100644 --- a/app/api/user-api/build.gradle +++ b/app/api/user-api/build.gradle @@ -2,4 +2,7 @@ dependencies { implementation project(":app:domain:user-domain") implementation project(":app:api:common-api") + + //testFixtures + testImplementation(testFixtures(project(":app:domain:user-domain"))) } \ No newline at end of file diff --git a/app/api/user-api/src/main/java/org/example/controller/UserController.java b/app/api/user-api/src/main/java/org/example/controller/UserController.java index fd79236d..0561d28c 100644 --- a/app/api/user-api/src/main/java/org/example/controller/UserController.java +++ b/app/api/user-api/src/main/java/org/example/controller/UserController.java @@ -8,11 +8,13 @@ import org.example.controller.dto.request.LogoutApiRequest; import org.example.controller.dto.request.WithdrawalApiRequest; import org.example.controller.dto.response.LoginApiResponse; +import org.example.controller.dto.response.UserProfileApiResponse; import org.example.security.dto.AuthenticatedUser; import org.example.security.dto.TokenParam; import org.example.service.UserService; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +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; @@ -58,4 +60,16 @@ public ResponseEntity withdraw( userService.withdraw(request.toServiceRequest(user.userId())); return ResponseEntity.noContent().build(); } + + @GetMapping("/profile") + @Operation(summary = "회원 정보") + public ResponseEntity profile( + @AuthenticationPrincipal AuthenticatedUser user + ) { + var profile = userService.findUserProfile(user.userId()); + + return ResponseEntity.ok( + UserProfileApiResponse.from(profile) + ); + } } diff --git a/app/api/user-api/src/main/java/org/example/controller/dto/response/UserProfileApiResponse.java b/app/api/user-api/src/main/java/org/example/controller/dto/response/UserProfileApiResponse.java new file mode 100644 index 00000000..a7cfa4e0 --- /dev/null +++ b/app/api/user-api/src/main/java/org/example/controller/dto/response/UserProfileApiResponse.java @@ -0,0 +1,25 @@ +package org.example.controller.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import org.example.service.dto.response.UserProfileServiceResponse; +import org.example.vo.SocialLoginApiType; + +@Builder +public record UserProfileApiResponse( + + @Schema(description = "닉네임") + String nickname, + + @Schema(description = "소셜 로그인 타입") + SocialLoginApiType type +) { + + public static UserProfileApiResponse from(UserProfileServiceResponse profile) { + return UserProfileApiResponse.builder() + .nickname(profile.nickname()) + .type(profile.type()) + .build(); + } + +} diff --git a/app/api/user-api/src/main/java/org/example/service/UserService.java b/app/api/user-api/src/main/java/org/example/service/UserService.java index 29ec7aa5..d971a92a 100644 --- a/app/api/user-api/src/main/java/org/example/service/UserService.java +++ b/app/api/user-api/src/main/java/org/example/service/UserService.java @@ -2,9 +2,13 @@ import java.util.Date; import java.util.NoSuchElementException; +import java.util.UUID; import lombok.RequiredArgsConstructor; +import org.example.dto.response.UserProfileDomainResponse; import org.example.entity.SocialLogin; import org.example.entity.User; +import org.example.error.UserError; +import org.example.exception.BusinessException; import org.example.security.dto.TokenParam; import org.example.security.dto.UserParam; import org.example.security.token.JWTGenerator; @@ -12,6 +16,7 @@ import org.example.service.dto.request.LoginServiceRequest; import org.example.service.dto.request.LogoutServiceRequest; import org.example.service.dto.request.WithdrawalServiceRequest; +import org.example.service.dto.response.UserProfileServiceResponse; import org.example.usecase.UserUseCase; import org.springframework.stereotype.Service; @@ -38,11 +43,27 @@ public void logout(LogoutServiceRequest request) { } public void withdraw(WithdrawalServiceRequest request) { + try { + userUseCase.deleteUser(request.userId()); + } catch (NoSuchElementException e) { + throw new BusinessException(UserError.NOT_FOUND_USER); + } + tokenProcessor.makeAccessTokenBlacklistAndDeleteRefreshToken( request.accessToken(), request.userId() ); - userUseCase.deleteUser(request.userId()); + } + + public UserProfileServiceResponse findUserProfile(UUID userId) { + UserProfileDomainResponse profile; + try { + profile = userUseCase.findUserProfile(userId); + } catch (NoSuchElementException e) { + throw new BusinessException(UserError.NOT_FOUND_USER); + } + + return UserProfileServiceResponse.from(profile); } private User getUser(LoginServiceRequest request) { @@ -63,8 +84,4 @@ private User createUser(LoginServiceRequest loginServiceRequest) { return userUseCase.createNewUser(user, socialLogin); } - - public String findNickname(final User user) { - return userUseCase.findNickName(user); - } } diff --git a/app/api/user-api/src/main/java/org/example/service/dto/request/LoginServiceRequest.java b/app/api/user-api/src/main/java/org/example/service/dto/request/LoginServiceRequest.java index 09b1e70a..d3cfeeb0 100644 --- a/app/api/user-api/src/main/java/org/example/service/dto/request/LoginServiceRequest.java +++ b/app/api/user-api/src/main/java/org/example/service/dto/request/LoginServiceRequest.java @@ -1,9 +1,9 @@ package org.example.service.dto.request; -import java.util.UUID; import lombok.Builder; import org.example.dto.request.LoginDomainRequest; import org.example.entity.User; +import org.example.vo.RandomNickname; import org.example.vo.SocialLoginApiType; @Builder @@ -15,7 +15,7 @@ public record LoginServiceRequest( public User createUser() { return User.builder() - .nickname(UUID.randomUUID().toString()) + .nickname(RandomNickname.makeRandomNickName()) .fcmToken(fcmToken) .build(); } diff --git a/app/api/user-api/src/main/java/org/example/service/dto/response/UserProfileServiceResponse.java b/app/api/user-api/src/main/java/org/example/service/dto/response/UserProfileServiceResponse.java new file mode 100644 index 00000000..a371c6fd --- /dev/null +++ b/app/api/user-api/src/main/java/org/example/service/dto/response/UserProfileServiceResponse.java @@ -0,0 +1,19 @@ +package org.example.service.dto.response; + +import lombok.Builder; +import org.example.dto.response.UserProfileDomainResponse; +import org.example.vo.SocialLoginApiType; + +@Builder +public record UserProfileServiceResponse( + String nickname, + SocialLoginApiType type +) { + + public static UserProfileServiceResponse from(UserProfileDomainResponse profile) { + return UserProfileServiceResponse.builder() + .nickname(profile.nickname()) + .type(SocialLoginApiType.from(profile.type())) + .build(); + } +} diff --git a/app/api/user-api/src/main/java/org/example/vo/SocialLoginApiType.java b/app/api/user-api/src/main/java/org/example/vo/SocialLoginApiType.java index 25622bdc..428058cb 100644 --- a/app/api/user-api/src/main/java/org/example/vo/SocialLoginApiType.java +++ b/app/api/user-api/src/main/java/org/example/vo/SocialLoginApiType.java @@ -10,4 +10,12 @@ public SocialLoginType toDomainType() { case APPLE -> SocialLoginType.APPLE; }; } + + public static SocialLoginApiType from(SocialLoginType type) { + return switch (type) { + case GOOGLE -> SocialLoginApiType.GOOGLE; + case KAKAO -> SocialLoginApiType.KAKAO; + case APPLE -> SocialLoginApiType.APPLE; + }; + } } diff --git a/app/api/user-api/src/test/java/org/example/fixture/dto/UserRequestDtoFixture.java b/app/api/user-api/src/test/java/org/example/fixture/dto/UserRequestDtoFixture.java new file mode 100644 index 00000000..def788c4 --- /dev/null +++ b/app/api/user-api/src/test/java/org/example/fixture/dto/UserRequestDtoFixture.java @@ -0,0 +1,15 @@ +package org.example.fixture.dto; + +import org.example.service.dto.request.LoginServiceRequest; +import org.example.vo.SocialLoginApiType; + +public class UserRequestDtoFixture { + + public static LoginServiceRequest loginServiceRequest(SocialLoginApiType type) { + return LoginServiceRequest.builder() + .socialLoginType(type) + .fcmToken("testFcmToken") + .identifier("testIdentifier") + .build(); + } +} diff --git a/app/api/user-api/src/test/java/org/example/service/UserServiceTest.java b/app/api/user-api/src/test/java/org/example/service/UserServiceTest.java new file mode 100644 index 00000000..44b60d8b --- /dev/null +++ b/app/api/user-api/src/test/java/org/example/service/UserServiceTest.java @@ -0,0 +1,82 @@ +package org.example.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Date; +import java.util.NoSuchElementException; +import org.example.entity.SocialLogin; +import org.example.entity.User; +import org.example.fixture.UserFixture; +import org.example.fixture.dto.UserRequestDtoFixture; +import org.example.security.dto.UserParam; +import org.example.security.token.JWTGenerator; +import org.example.security.token.TokenProcessor; +import org.example.usecase.UserUseCase; +import org.example.vo.SocialLoginApiType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; + +public class UserServiceTest { + + private final UserUseCase userUseCase = mock(UserUseCase.class); + private final JWTGenerator jwtGenerator = mock(JWTGenerator.class); + private final TokenProcessor tokenProcessor = mock(TokenProcessor.class); + + private final UserService userService = new UserService( + userUseCase, + jwtGenerator, + tokenProcessor + ); + + @ParameterizedTest + @EnumSource( + value = SocialLoginApiType.class, + names = {"KAKAO", "GOOGLE", "APPLE"}, + mode = Mode.INCLUDE + ) + @DisplayName("로그인 시 사용자가 존재하지 않으면 새로운 사용자를 생성하고 JWT를 반환한다.") + void getJwtAndCreatesNewUserIfNotExistWhenLogin(SocialLoginApiType type) { + // given + var request = UserRequestDtoFixture.loginServiceRequest(type); + given( + userUseCase.findUser(request.toDomainRequest()) + ).willThrow( + NoSuchElementException.class + ); + + var user = UserFixture.randomNicknameUser(); + given( + userUseCase.createNewUser(any(User.class), any(SocialLogin.class)) + ).willReturn(user); + + // when + userService.login(request); + + // then + verify(jwtGenerator, times(1)).generate(any(UserParam.class), any(Date.class)); + } + + @Test + @DisplayName("로그인 시 사용자가 존재하면 기존의 사용자 정보로 JWT를 반환하다.") + void getJwtAndUserIfExistWhenLogin() { + // given + var request = UserRequestDtoFixture.loginServiceRequest(SocialLoginApiType.KAKAO); + var user = UserFixture.randomNicknameUser(); + given( + userUseCase.findUser(request.toDomainRequest()) + ).willReturn(user); + + // when + userService.login(request); + + // then + verify(jwtGenerator, times(1)).generate(any(UserParam.class), any(Date.class)); + } +} diff --git a/app/domain/common-domain/src/main/resources/schema.sql b/app/domain/common-domain/src/main/resources/schema.sql index 51f2b459..185dbc64 100644 --- a/app/domain/common-domain/src/main/resources/schema.sql +++ b/app/domain/common-domain/src/main/resources/schema.sql @@ -215,7 +215,7 @@ create table users birth date not null, fcm_token varchar(255) not null, gender varchar(255) not null check (gender in ('MAN', 'WOMAN', 'NOT_CHOSEN')), - nickname varchar(255) not null, + nickname varchar(255) not null unique, role varchar(255) not null check (role in ('GUEST', 'USER', 'ADMIN')), primary key (id) ); diff --git a/app/domain/user-domain/src/main/java/org/example/dto/response/UserProfileDomainResponse.java b/app/domain/user-domain/src/main/java/org/example/dto/response/UserProfileDomainResponse.java new file mode 100644 index 00000000..c3f3f1ee --- /dev/null +++ b/app/domain/user-domain/src/main/java/org/example/dto/response/UserProfileDomainResponse.java @@ -0,0 +1,10 @@ +package org.example.dto.response; + +import org.example.vo.SocialLoginType; + +public record UserProfileDomainResponse( + String nickname, + SocialLoginType type +) { + +} diff --git a/app/domain/user-domain/src/main/java/org/example/entity/User.java b/app/domain/user-domain/src/main/java/org/example/entity/User.java index 35fa68c9..be3bc2b8 100644 --- a/app/domain/user-domain/src/main/java/org/example/entity/User.java +++ b/app/domain/user-domain/src/main/java/org/example/entity/User.java @@ -10,6 +10,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.example.vo.RandomNickname; import org.example.vo.UserGender; import org.example.vo.UserRole; @@ -19,7 +20,7 @@ @Table(name = "users") public class User extends BaseEntity { - @Column(name = "nickname", nullable = false) + @Column(name = "nickname", nullable = false, unique = true) private String nickname; @Column(name = "fcm_token", nullable = false) @@ -57,4 +58,8 @@ public void dirtyCheckFcmToken(String fcmToken) { public boolean isWithdrew() { return this.getIsDeleted(); } + + public void changeNickname() { + this.nickname = RandomNickname.makeRandomNickName(); + } } \ No newline at end of file diff --git a/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepository.java b/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepository.java index 84bef0e6..0ebd8e49 100644 --- a/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepository.java +++ b/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepository.java @@ -2,8 +2,11 @@ import java.util.Optional; import java.util.UUID; +import org.example.dto.response.UserProfileDomainResponse; public interface UserQuerydslRepository { - Optional findNicknameById(final UUID id); + Optional findUserProfileById(UUID userId); + + boolean existsByNickname(String nickname); } diff --git a/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepositoryImpl.java b/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepositoryImpl.java index 5d024a38..a84c7c03 100644 --- a/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepositoryImpl.java +++ b/app/domain/user-domain/src/main/java/org/example/repository/user/UserQuerydslRepositoryImpl.java @@ -1,11 +1,14 @@ package org.example.repository.user; +import static org.example.entity.QSocialLogin.socialLogin; import static org.example.entity.QUser.user; +import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.Optional; import java.util.UUID; import lombok.RequiredArgsConstructor; +import org.example.dto.response.UserProfileDomainResponse; import org.springframework.stereotype.Repository; @Repository @@ -14,12 +17,29 @@ public class UserQuerydslRepositoryImpl implements UserQuerydslRepository { private final JPAQueryFactory jpaQueryFactory; - public Optional findNicknameById(UUID id) { + @Override + public Optional findUserProfileById(UUID userId) { return Optional.ofNullable(jpaQueryFactory - .select(user.nickname) + .select( + Projections.constructor( + UserProfileDomainResponse.class, + user.nickname, + socialLogin.socialLoginType + ) + ) .from(user) - .where(user.id.eq(id)) - .fetchOne() + .join(socialLogin).on(socialLogin.userId.eq(userId), socialLogin.isDeleted.isFalse()) + .where(user.id.eq(userId), user.isDeleted.isFalse()) + .fetchFirst() ); } + + @Override + public boolean existsByNickname(String nickname) { + return jpaQueryFactory + .selectOne() + .from(user) + .where(user.nickname.eq(nickname), user.isDeleted.isFalse()) + .fetchFirst() != null; + } } diff --git a/app/domain/user-domain/src/main/java/org/example/usecase/UserUseCase.java b/app/domain/user-domain/src/main/java/org/example/usecase/UserUseCase.java index 2e5fe2c5..06e78c2c 100644 --- a/app/domain/user-domain/src/main/java/org/example/usecase/UserUseCase.java +++ b/app/domain/user-domain/src/main/java/org/example/usecase/UserUseCase.java @@ -4,6 +4,7 @@ import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.request.LoginDomainRequest; +import org.example.dto.response.UserProfileDomainResponse; import org.example.entity.SocialLogin; import org.example.entity.User; import org.example.error.UserError; @@ -22,6 +23,10 @@ public class UserUseCase { @Transactional public User createNewUser(User user, SocialLogin socialLogin) { + while (userRepository.existsByNickname(user.getNickname())) { + user.changeNickname(); + } + socialLoginRepository.save(socialLogin); return userRepository.save(user); } @@ -45,16 +50,14 @@ public User findUser(LoginDomainRequest request) { return user; } - public String findNickName(final User user) { - return userRepository.findNicknameById(user.getId()).orElseThrow(); - } - @Transactional public void deleteUser(UUID userId) { - User user = userRepository.findById(userId).orElseThrow(() -> - new BusinessException(UserError.NOT_FOUND_USER) - ); + User user = userRepository.findById(userId).orElseThrow(NoSuchElementException::new); user.softDelete(); } + + public UserProfileDomainResponse findUserProfile(UUID userId) { + return userRepository.findUserProfileById(userId).orElseThrow(NoSuchElementException::new); + } } diff --git a/app/domain/user-domain/src/main/java/org/example/vo/RandomNickname.java b/app/domain/user-domain/src/main/java/org/example/vo/RandomNickname.java new file mode 100644 index 00000000..a4e19f3d --- /dev/null +++ b/app/domain/user-domain/src/main/java/org/example/vo/RandomNickname.java @@ -0,0 +1,55 @@ +package org.example.vo; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum RandomNickname { + NICK( + List.of( + "기분좋은", "신바람나는", "상쾌한", "짜릿한", "자유로운", + "당당한", "배부른", "수줍은", "멋있는", "잘생긴", "이쁜", "신비로운", + "따뜻한", "신나는", "씩씩한", "순수한", "담백한", "활기찬", + "단호한", "은은한", "우아한", "반짝이는", "행복한", + "선명한", "애틋한", "깔끔한", "유쾌한", "신선한", "산뜻한", + "쾌활한", "훈훈한", "몽환적인", "쿨한", "흥분된", "완벽한", + "존경받는", "포근한", "느긋한", "달콤한", "차분한", "활달한", + "고요한", "화사한", "청량한", "기분전환된", "평온한", + "깔끔한", "로맨틱한", "모험적인", "영감이되는", "잔잔한", + "산뜻한", "편안한" + ) + ), + + NAME( + List.of( + "사자", "코끼리", "호랑이", "곰", "여우", "늑대", "너구리", + "침팬치", "고릴라", "참새", "고슴도치", "강아지", "고양이", + "거북이", "토끼", "앵무새", "하이에나", "돼지", "하마", "원숭이", + "물소", "얼룩말", "치타", "악어", "기린", "수달", "염소", + "다람쥐", "판다", "파이어폭스", "오소리", "밍크", "레서판다", + "기러기", "펭귄", "오리", "카멜레온", "비둘기", "앵무새", + "햄스터", "코뿔소", "푸들", "꿀벌", "돌고래", "별", + "달팽이", "바다거북", "코알라", "타조", "하늘다람쥐", + "기니피그", "조개" + ) + ); + + private final List value; + + public static String makeRandomNickName() { + return NICK.value.get(randomNumber()) + " " + + NAME.value.get(randomNumber()) + + getUniqueNumber() + "님"; + } + + private static int randomNumber() { + return ThreadLocalRandom.current().nextInt(51); + } + + private static String getUniqueNumber() { + return String.valueOf(ThreadLocalRandom.current().nextInt(1000)); + } +} diff --git a/app/domain/user-domain/src/test/java/org/example/QueryTest.java b/app/domain/user-domain/src/test/java/org/example/QueryTest.java new file mode 100644 index 00000000..ca753266 --- /dev/null +++ b/app/domain/user-domain/src/test/java/org/example/QueryTest.java @@ -0,0 +1,16 @@ +package org.example; + +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ActiveProfiles; +import org.testcontainers.junit.jupiter.Testcontainers; + + +@DataJpaTest +@Testcontainers +@ActiveProfiles("user-domain-test") +@AutoConfigureTestDatabase(replace = Replace.NONE) +public class QueryTest { + +} diff --git a/app/domain/user-domain/src/test/java/org/example/ShowDomainTestConfiguration.java b/app/domain/user-domain/src/test/java/org/example/ShowDomainTestConfiguration.java new file mode 100644 index 00000000..82b4554b --- /dev/null +++ b/app/domain/user-domain/src/test/java/org/example/ShowDomainTestConfiguration.java @@ -0,0 +1,11 @@ +package org.example; + +import org.example.config.UserDomainConfig; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; + +@SpringBootApplication +@Import(value = {UserDomainConfig.class}) +public class ShowDomainTestConfiguration { + +} diff --git a/app/domain/user-domain/src/test/java/org/example/repository/UserRepositoryTest.java b/app/domain/user-domain/src/test/java/org/example/repository/UserRepositoryTest.java new file mode 100644 index 00000000..6fd70e55 --- /dev/null +++ b/app/domain/user-domain/src/test/java/org/example/repository/UserRepositoryTest.java @@ -0,0 +1,42 @@ +package org.example.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.example.QueryTest; +import org.example.entity.SocialLogin; +import org.example.entity.User; +import org.example.fixture.SocialLoginFixture; +import org.example.fixture.UserFixture; +import org.example.repository.user.SocialLoginRepository; +import org.example.repository.user.UserRepository; +import org.example.vo.SocialLoginType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class UserRepositoryTest extends QueryTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private SocialLoginRepository socialLoginRepository; + + @Test + @DisplayName("사용자의 프로필을 가져올 수 있다.") + void findUserProfile() { + //given + User user = UserFixture.randomNicknameUser(); + userRepository.save(user); + + SocialLogin socialLogin = SocialLoginFixture.socialLogin(SocialLoginType.KAKAO, + user.getId()); + socialLoginRepository.save(socialLogin); + + //when + var result = userRepository.findUserProfileById(user.getId()).orElseThrow(); + + //then + assertThat(result).isNotNull(); + } +} diff --git a/app/domain/user-domain/src/test/resources/application-user-domain-test.yml b/app/domain/user-domain/src/test/resources/application-user-domain-test.yml new file mode 100644 index 00000000..afa911a3 --- /dev/null +++ b/app/domain/user-domain/src/test/resources/application-user-domain-test.yml @@ -0,0 +1,10 @@ +spring: + jpa: + hibernate: + ddl-auto: create + show-sql: true + datasource: + driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver + url: jdbc:tc:postgresql:13:///user_test_container + username: root + password: root \ No newline at end of file diff --git a/app/domain/user-domain/src/testFixtures/java/org/example/fixture/SocialLoginFixture.java b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/SocialLoginFixture.java new file mode 100644 index 00000000..fff3af78 --- /dev/null +++ b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/SocialLoginFixture.java @@ -0,0 +1,16 @@ +package org.example.fixture; + +import java.util.UUID; +import org.example.entity.SocialLogin; +import org.example.vo.SocialLoginType; + +public class SocialLoginFixture { + + public static SocialLogin socialLogin(SocialLoginType type, UUID userId) { + return SocialLogin.builder() + .socialLoginType(type) + .identifier("testIdentifier") + .userId(userId) + .build(); + } +} diff --git a/app/domain/user-domain/src/testFixtures/java/org/example/fixture/UserFixture.java b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/UserFixture.java new file mode 100644 index 00000000..7e87a132 --- /dev/null +++ b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/UserFixture.java @@ -0,0 +1,14 @@ +package org.example.fixture; + +import org.example.entity.User; +import org.example.vo.RandomNickname; + +public class UserFixture { + + public static User randomNicknameUser() { + return User.builder() + .nickname(RandomNickname.makeRandomNickName()) + .fcmToken("testFcmToken") + .build(); + } +}