diff --git a/build.gradle b/build.gradle index d9123ef4..5be3f183 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'idea' id 'java' id 'jacoco' - id 'org.springframework.boot' version '3.2.4' + id 'org.springframework.boot' version '3.2.5' id 'io.spring.dependency-management' version '1.1.4' id 'org.asciidoctor.jvm.convert' version '4.0.2' id 'com.epages.restdocs-api-spec' version '0.19.2' @@ -101,6 +101,7 @@ dependencies { implementation 'software.amazon.awssdk:kms' implementation 'com.amazonaws:aws-encryption-sdk-java:3.0.0' implementation 'software.amazon.cryptography:aws-cryptographic-material-providers:1.2.0' + implementation 'org.springframework.boot:spring-boot-starter-quartz' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.1' implementation 'com.slack.api:slack-app-backend:1.39.0' implementation 'io.netty:netty-resolver-dns-native-macos:4.1.108.Final:osx-aarch_64' diff --git a/src/docs/asciidoc/vote-api.adoc b/src/docs/asciidoc/vote-api.adoc index a1b20e6e..4159f8cf 100644 --- a/src/docs/asciidoc/vote-api.adoc +++ b/src/docs/asciidoc/vote-api.adoc @@ -1,8 +1,28 @@ [[Vote-API]] -== Vote API +== Vote Paper API + +=== 투표 게시물 스크롤 조회 API + +==== 성공 + +operation::get-vote-papers-success[snippets='http-request,http-response,query-parameters,response-fields'] === 투표 게시물 등록 API ==== 성공 -operation::register-vote-paper-success[snippets='http-request,http-response,request-fields'] \ No newline at end of file +operation::register-vote-paper-success[snippets='http-request,http-response,request-fields'] + +=== 투표 게시물 영상 제공일 등록 API + +==== 성공 + +operation::update-delivery-date-success[snippets='http-request,http-response,path-parameters,request-fields,response-fields'] + +== Vote API + +=== 투표 등록 API + +==== 성공 + +operation::register-vote-success[snippets='http-request,http-response,path-parameters,response-fields'] \ No newline at end of file diff --git a/src/main/java/com/tune_fun/v1/account/application/service/LoginService.java b/src/main/java/com/tune_fun/v1/account/application/service/LoginService.java index d60b50b8..2e59542a 100644 --- a/src/main/java/com/tune_fun/v1/account/application/service/LoginService.java +++ b/src/main/java/com/tune_fun/v1/account/application/service/LoginService.java @@ -11,6 +11,7 @@ import com.tune_fun.v1.account.domain.behavior.SaveJwtToken; import com.tune_fun.v1.account.domain.value.LoginResult; import com.tune_fun.v1.account.domain.value.RegisteredAccount; +import com.tune_fun.v1.common.constant.Constants; import com.tune_fun.v1.common.exception.CommonApplicationException; import com.tune_fun.v1.common.hexagon.UseCase; import lombok.RequiredArgsConstructor; @@ -54,7 +55,7 @@ public LoginResult login(final AccountCommands.Login command) { if (!passwordEncoder.matches(command.password(), registeredAccount.password())) throw new CommonApplicationException(ACCOUNT_NOT_FOUND); - String authorities = String.join(",", registeredAccount.roles()); + String authorities = String.join(Constants.COMMA, registeredAccount.roles()); SaveJwtToken saveJwtTokenBehavior = getSaveJwtToken(registeredAccount, authorities); diff --git a/src/main/java/com/tune_fun/v1/account/application/service/RegisterService.java b/src/main/java/com/tune_fun/v1/account/application/service/RegisterService.java index 534f7044..c32a75e7 100644 --- a/src/main/java/com/tune_fun/v1/account/application/service/RegisterService.java +++ b/src/main/java/com/tune_fun/v1/account/application/service/RegisterService.java @@ -10,6 +10,7 @@ import com.tune_fun.v1.account.domain.behavior.SaveJwtToken; import com.tune_fun.v1.account.domain.value.CurrentAccount; import com.tune_fun.v1.account.domain.value.RegisterResult; +import com.tune_fun.v1.common.constant.Constants; import com.tune_fun.v1.common.exception.CommonApplicationException; import com.tune_fun.v1.common.hexagon.UseCase; import com.tune_fun.v1.common.util.StringUtil; @@ -51,13 +52,13 @@ private static RegisterResult getRegisterResult(CurrentAccount savedAccount, Str @Override @Transactional public RegisterResult register(final String registerType, final AccountCommands.Register command) { - checkRegisterdAccount(command); + checkRegisteredAccount(command); String encodedPassword = passwordEncoder.encode(command.password()); SaveAccount saveAccount = getSaveAccount(registerType, command, encodedPassword); CurrentAccount savedAccount = saveAccountPort.saveAccount(saveAccount); - String authorities = String.join(",", savedAccount.roles()); + String authorities = String.join(Constants.COMMA, savedAccount.roles()); SaveJwtToken saveJwtToken = new SaveJwtToken(savedAccount.username(), authorities); @@ -68,7 +69,7 @@ public RegisterResult register(final String registerType, final AccountCommands. } @Transactional(readOnly = true) - public void checkRegisterdAccount(AccountCommands.Register command) { + public void checkRegisteredAccount(AccountCommands.Register command) { loadAccountPort.currentAccountInfo(command.username()).ifPresent(accountInfo -> { throw new CommonApplicationException(USER_POLICY_ACCOUNT_REGISTERED); }); diff --git a/src/main/java/com/tune_fun/v1/common/aspect/LoggingAspect.java b/src/main/java/com/tune_fun/v1/common/aspect/LoggingAspect.java index 9a75eaed..eaa519f6 100755 --- a/src/main/java/com/tune_fun/v1/common/aspect/LoggingAspect.java +++ b/src/main/java/com/tune_fun/v1/common/aspect/LoggingAspect.java @@ -1,5 +1,6 @@ package com.tune_fun.v1.common.aspect; +import com.tune_fun.v1.common.constant.Constants; import lombok.RequiredArgsConstructor; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; @@ -50,7 +51,7 @@ public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { Object proceed = joinPoint.proceed(); stopWatch.stop(); - String msg = joinPoint.getSignature().getDeclaringType() + "." + + String msg = joinPoint.getSignature().getDeclaringType() + Constants.DOT + joinPoint.getSignature().getName() + " : " + "running time = " + stopWatch.getTotalTimeMillis() + "ms"; diff --git a/src/main/java/com/tune_fun/v1/common/config/PermissionPolicyEvaluator.java b/src/main/java/com/tune_fun/v1/common/config/PermissionPolicyEvaluator.java index a8361caa..30b01810 100644 --- a/src/main/java/com/tune_fun/v1/common/config/PermissionPolicyEvaluator.java +++ b/src/main/java/com/tune_fun/v1/common/config/PermissionPolicyEvaluator.java @@ -2,6 +2,7 @@ import com.tune_fun.v1.common.exception.CommonApplicationException; import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort; +import com.tune_fun.v1.vote.application.port.output.LoadVotePort; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,8 +13,7 @@ import java.io.Serializable; -import static com.tune_fun.v1.common.response.MessageCode.VOTE_PAPER_NOT_FOUND; -import static com.tune_fun.v1.common.response.MessageCode.VOTE_POLICY_ONLY_AUTHOR_CAN_UPDATE_DELIVERY_DATE; +import static com.tune_fun.v1.common.response.MessageCode.*; @Slf4j @Component @@ -21,6 +21,7 @@ public class PermissionPolicyEvaluator implements PermissionEvaluator { private final LoadVotePaperPort loadVotePaperPort; + private final LoadVotePort loadVotePort; @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { @@ -33,6 +34,7 @@ public boolean hasPermission(Authentication authentication, Serializable targetI return switch (TargetType.valueOf(targetType)) { case VOTE_PAPER -> hasPermissionForVotePaper(principal, targetId); + case VOTE -> hasPermissionForVote(principal, targetId); }; } @@ -45,7 +47,15 @@ public boolean hasPermissionForVotePaper(User principal, Serializable targetId) throw new CommonApplicationException(VOTE_PAPER_NOT_FOUND); } + private boolean hasPermissionForVote(User principal, Serializable targetId) { + if (loadVotePort.loadVoteByVoterAndVotePaperId(principal.getUsername(), (Long) targetId).isPresent()) + throw new CommonApplicationException(VOTE_POLICY_ONE_VOTE_PER_USER); + + return true; + } + private enum TargetType { VOTE_PAPER, + VOTE } } diff --git a/src/main/java/com/tune_fun/v1/common/config/SecurityConfig.java b/src/main/java/com/tune_fun/v1/common/config/SecurityConfig.java index 8c02d2dd..0281fc1f 100644 --- a/src/main/java/com/tune_fun/v1/common/config/SecurityConfig.java +++ b/src/main/java/com/tune_fun/v1/common/config/SecurityConfig.java @@ -14,7 +14,6 @@ import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -85,12 +84,12 @@ public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospe http .sessionManagement(config -> config.sessionCreationPolicy(STATELESS)) .authorizeHttpRequests(registry -> registry - .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() - .requestMatchers(mvc(introspector, "/static/**", "/favicon.ico")).permitAll() - .requestMatchers(mvc(introspector, Uris.PERMIT_ALL_URIS)).permitAll() - .requestMatchers(mvc(introspector, Uris.ARTIST_URIS)).hasRole("ARTIST") - .requestMatchers(mvc(introspector, Uris.ADMIN_URIS)).hasRole("ADMIN") - .anyRequest().authenticated() + .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() + .requestMatchers(mvc(introspector, "/static/**", "/favicon.ico")).permitAll() + .requestMatchers(mvc(introspector, Uris.PERMIT_ALL_URIS)).permitAll() +// .requestMatchers(mvc(introspector, Uris.ARTIST_URIS)).hasRole("ARTIST") +// .requestMatchers(mvc(introspector, Uris.ADMIN_URIS)).hasRole("ADMIN") + .anyRequest().authenticated() ) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .oauth2Login(configure -> diff --git a/src/main/java/com/tune_fun/v1/common/config/Uris.java b/src/main/java/com/tune_fun/v1/common/config/Uris.java index d7af35a1..6010c288 100644 --- a/src/main/java/com/tune_fun/v1/common/config/Uris.java +++ b/src/main/java/com/tune_fun/v1/common/config/Uris.java @@ -52,9 +52,9 @@ private Uris() { public static final String RESEND_OTP = API_V1_ROOT + "/otp/resend"; public static final String VOTE_ROOT = API_V1_ROOT + "/votes"; + public static final String VOTE_PAPER_ROOT = VOTE_ROOT + "/paper"; - public static final String REGISTER_VOTE_PAPER = VOTE_ROOT + "/paper"; - public static final String SET_VOTE_PAPER_DELIVERY_DATE = VOTE_ROOT + "/paper/delivery-date"; + public static final String UPDATE_VOTE_PAPER_DELIVERY_DATE = VOTE_PAPER_ROOT + "/{votePaperId}/delivery-date"; public static final String REGISTER_VOTE = VOTE_ROOT + "/{votePaperId}" + "/register" + "/{voteChoiceId}"; @@ -143,7 +143,7 @@ private Uris() { }; public static final String[] ARTIST_URIS = { - REGISTER_VOTE_PAPER + UPDATE_VOTE_PAPER_DELIVERY_DATE }; public static final String[] ADMIN_URIS = { diff --git a/src/main/java/com/tune_fun/v1/common/constant/Constants.java b/src/main/java/com/tune_fun/v1/common/constant/Constants.java new file mode 100644 index 00000000..8d89baf1 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/common/constant/Constants.java @@ -0,0 +1,50 @@ +package com.tune_fun.v1.common.constant; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +public final class Constants { + + public static final Object NULL_OBJECT = null; + + public static final Number NULL_NUMBER = null; + + public static final String NULL_STRING = null; + + public static final String EMPTY_STRING = ""; + + public static final String SPACE = " "; + + public static final String COMMA = ","; + + public static final String COLON = ":"; + + public static final String SEMICOLON = ";"; + + public static final String DOT = "."; + + public static final LocalDateTime LOCAL_DATE_TIME_MIN = LocalDateTime.of( + LocalDate.of(1970, 1, 1), LocalTime.of(0, 0, 10) + ); + + public static final class NicknameFragment { + + public static final String[] PREFIX_NAMES = { + "행복한", "멋진", "멋있는", "귀여운", "예쁜", "잘생긴", "똑똑한", "졸린", "피곤한", "배고픈", "궁금한", "친절한", "평범한", + "활발한", "조용한", "빠른", "느린", "재빠른", "강한", "영리한", "우아한", "독특한", "재밌는", "발랄한", "따뜻한", "눈부신", + "명랑한", "빛나는", "깜찍한", "차분한", "이상한", "행운의" + }; + + public static final String[] ANIMAL_NAMES = { + "사자", "호랑이", "표범", "기린", "코끼리", "코뿔소", "하마", "펭귄", "독수리", "타조", "캥거루", "고래", "칠면조", "직박구리", + "청설모", "메추라기", "앵무새", "스라소니", "판다", "오소리", "오리", "거위", "백조", "두루미", "고슴도치", "두더지", "우파루파", + "너구리", "카멜레온", "이구아나", "노루", "제비", "까치", "고라니", "수달", "당나귀", "순록", "염소", "공작", "바다표범", "들소", + "참새", "물개", "바다사자", "얼룩말", "산양", "카피바라", "북극곰", "퓨마", "코요테", "라마", "딱따구리", "돌고래", "까마귀", + "낙타", "여우", "사슴", "늑대", "재규어", "알파카", "다람쥐", "담비", "사막여우", "북극여우", "꽃사슴", "해달", "강아지", + "고양이", "햄스터", "기니피그", "왈라비", "마못", "물범", "토끼", "미어캣", "북극곰", "코알라", "디프만" + }; + + } + +} diff --git a/src/main/java/com/tune_fun/v1/common/exception/GlobalExceptionHandler.java b/src/main/java/com/tune_fun/v1/common/exception/GlobalExceptionHandler.java index 4fa77c87..0e85dbb8 100644 --- a/src/main/java/com/tune_fun/v1/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/tune_fun/v1/common/exception/GlobalExceptionHandler.java @@ -19,6 +19,7 @@ import static com.google.common.base.CaseFormat.LOWER_CAMEL; import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE; +import static com.tune_fun.v1.common.constant.Constants.DOT; import static com.tune_fun.v1.common.response.MessageCode.ERROR; import static com.tune_fun.v1.common.response.MessageCode.EXCEPTION_ILLEGAL_ARGUMENT; import static java.util.stream.Collectors.toMap; @@ -31,7 +32,7 @@ public class GlobalExceptionHandler { private final ResponseMapper responseMapper; protected static String getPropertyName(String propertyPath) { - return propertyPath.substring(propertyPath.lastIndexOf('.') + 1); + return propertyPath.substring(propertyPath.lastIndexOf(DOT) + 1); } @ExceptionHandler(CommonApplicationException.class) diff --git a/src/main/java/com/tune_fun/v1/common/hexagon/UseCase.java b/src/main/java/com/tune_fun/v1/common/hexagon/UseCase.java index 4a088f88..614e2e27 100644 --- a/src/main/java/com/tune_fun/v1/common/hexagon/UseCase.java +++ b/src/main/java/com/tune_fun/v1/common/hexagon/UseCase.java @@ -1,14 +1,14 @@ package com.tune_fun.v1.common.hexagon; import org.springframework.core.annotation.AliasFor; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented -@Component +@Service public @interface UseCase { /** @@ -17,7 +17,7 @@ * * @return the suggested component name, if any (or empty String otherwise) */ - @AliasFor(annotation = Component.class) + @AliasFor(annotation = Service.class) String value() default ""; } diff --git a/src/main/java/com/tune_fun/v1/common/hexagon/WebAdapter.java b/src/main/java/com/tune_fun/v1/common/hexagon/WebAdapter.java index 5f0e186a..6baada9b 100644 --- a/src/main/java/com/tune_fun/v1/common/hexagon/WebAdapter.java +++ b/src/main/java/com/tune_fun/v1/common/hexagon/WebAdapter.java @@ -1,14 +1,14 @@ package com.tune_fun.v1.common.hexagon; import org.springframework.core.annotation.AliasFor; -import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RestController; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented -@Component +@RestController public @interface WebAdapter { /** @@ -17,7 +17,7 @@ * * @return the suggested component name, if any (or empty String otherwise) */ - @AliasFor(annotation = Component.class) + @AliasFor(annotation = RestController.class) String value() default ""; } diff --git a/src/main/java/com/tune_fun/v1/common/response/ValidationErrorData.java b/src/main/java/com/tune_fun/v1/common/response/ValidationErrorData.java index 8fcceb1d..b149968c 100644 --- a/src/main/java/com/tune_fun/v1/common/response/ValidationErrorData.java +++ b/src/main/java/com/tune_fun/v1/common/response/ValidationErrorData.java @@ -16,7 +16,7 @@ @AllArgsConstructor @Getter @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class ValidationErrorData implements BasePayload { +public final class ValidationErrorData implements BasePayload { @JsonIgnore private Map errors = new HashMap<>(); diff --git a/src/main/java/com/tune_fun/v1/common/util/StringUtil.java b/src/main/java/com/tune_fun/v1/common/util/StringUtil.java index 84f667b5..d22c60c9 100644 --- a/src/main/java/com/tune_fun/v1/common/util/StringUtil.java +++ b/src/main/java/com/tune_fun/v1/common/util/StringUtil.java @@ -1,10 +1,10 @@ package com.tune_fun.v1.common.util; +import com.tune_fun.v1.common.constant.Constants; import lombok.experimental.UtilityClass; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.rng.UniformRandomProvider; import org.apache.commons.rng.simple.RandomSource; -import org.apache.tomcat.util.codec.binary.Base64; import org.springframework.security.core.GrantedAuthority; import org.springframework.util.StringUtils; @@ -83,7 +83,7 @@ public boolean hasText(String text) { } public static String concatWithColon(String... strings) { - return join(":", strings); + return join(Constants.COLON, strings); } public static String removeBearerPrefix(String accessTokenFromRequest) { @@ -91,32 +91,17 @@ public static String removeBearerPrefix(String accessTokenFromRequest) { } public static String getFlattenAuthorities(Collection authorities) { - return authorities.stream().map(GrantedAuthority::getAuthority).collect(joining(",")); + return authorities.stream().map(GrantedAuthority::getAuthority).collect(joining(Constants.COMMA)); } public static String generateRandomNickname() throws NoSuchAlgorithmException { - int animalIndex = secureRandom().nextInt(ANIMAL_NAMES.length); - int prefixIndex = secureRandom().nextInt(PREFIX_NAMES.length); + int animalIndex = secureRandom().nextInt(Constants.NicknameFragment.ANIMAL_NAMES.length); + int prefixIndex = secureRandom().nextInt(Constants.NicknameFragment.PREFIX_NAMES.length); - String animalName = ANIMAL_NAMES[animalIndex]; - String prefix = PREFIX_NAMES[prefixIndex]; + String animalName = Constants.NicknameFragment.ANIMAL_NAMES[animalIndex]; + String prefix = Constants.NicknameFragment.PREFIX_NAMES[prefixIndex]; return prefix + animalName; } - private static final String[] PREFIX_NAMES = { - "행복한", "멋진", "멋있는", "귀여운", "예쁜", "잘생긴", "똑똑한", "졸린", "피곤한", "배고픈", "궁금한", "친절한", "평범한", - "활발한", "조용한", "빠른", "느린", "재빠른", "강한", "영리한", "우아한", "독특한", "재밌는", "발랄한", "따뜻한", "눈부신", - "명랑한", "빛나는", "깜찍한", "차분한", "이상한", "행운의" - }; - - private static final String[] ANIMAL_NAMES = { - "사자", "호랑이", "표범", "기린", "코끼리", "코뿔소", "하마", "펭귄", "독수리", "타조", "캥거루", "고래", "칠면조", "직박구리", - "청설모", "메추라기", "앵무새", "스라소니", "판다", "오소리", "오리", "거위", "백조", "두루미", "고슴도치", "두더지", "우파루파", - "너구리", "카멜레온", "이구아나", "노루", "제비", "까치", "고라니", "수달", "당나귀", "순록", "염소", "공작", "바다표범", "들소", - "참새", "물개", "바다사자", "얼룩말", "산양", "카피바라", "북극곰", "퓨마", "코요테", "라마", "딱따구리", "돌고래", "까마귀", - "낙타", "여우", "사슴", "늑대", "재규어", "알파카", "다람쥐", "담비", "사막여우", "북극여우", "꽃사슴", "해달", "강아지", - "고양이", "햄스터", "기니피그", "왈라비", "마못", "물범", "토끼", "미어캣", "북극곰", "코알라", "디프만" - }; - } diff --git a/src/main/java/com/tune_fun/v1/common/util/querydsl/FluentOperationFragment.java b/src/main/java/com/tune_fun/v1/common/util/querydsl/FluentOperationFragment.java index 016b1174..4b14006e 100644 --- a/src/main/java/com/tune_fun/v1/common/util/querydsl/FluentOperationFragment.java +++ b/src/main/java/com/tune_fun/v1/common/util/querydsl/FluentOperationFragment.java @@ -131,6 +131,22 @@ public > PredicateBuilder inEnum(EnumExpression column, Lis return operate(); } + public PredicateBuilder gtDateTime(DateTimeExpression column, LocalDateTime value) { + if (value != null) { + return operate(column.gt(value)); + } + + return operate(); + } + + public PredicateBuilder ltDateTime(DateTimeExpression column, LocalDateTime value) { + if (value != null) { + return operate(column.lt(value)); + } + + return operate(); + } + public PredicateBuilder betweenDateTime(DateTimeExpression column, LocalDateTime startDate, LocalDateTime endDate) { if (startDate != null && endDate != null) { return operate(column.between(startDate, endDate)); diff --git a/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java b/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java index 437ae4f6..d89aa604 100644 --- a/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java +++ b/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java @@ -1,5 +1,6 @@ package com.tune_fun.v1.external.aws.s3; +import com.tune_fun.v1.common.constant.Constants; import com.tune_fun.v1.common.util.StringUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -84,7 +85,7 @@ public String getKey(String rootPath, MultipartFile multipartFile) throws NoSuch "jpeg" : extractedExtension; log.info("extension is {}", extension); - return rootPath + "/" + generatedFileName + "." + extension; + return rootPath + "/" + generatedFileName + Constants.DOT + extension; } public String getUrl(String s3BucketName, String key) { diff --git a/src/main/java/com/tune_fun/v1/otp/application/service/VerifyOtpService.java b/src/main/java/com/tune_fun/v1/otp/application/service/VerifyOtpService.java index e5df5dee..8ad47c5b 100644 --- a/src/main/java/com/tune_fun/v1/otp/application/service/VerifyOtpService.java +++ b/src/main/java/com/tune_fun/v1/otp/application/service/VerifyOtpService.java @@ -6,6 +6,7 @@ import com.tune_fun.v1.account.application.port.output.jwt.CreateRefreshTokenPort; import com.tune_fun.v1.account.domain.behavior.SaveJwtToken; import com.tune_fun.v1.account.domain.value.CurrentAccount; +import com.tune_fun.v1.common.constant.Constants; import com.tune_fun.v1.common.exception.CommonApplicationException; import com.tune_fun.v1.common.hexagon.UseCase; import com.tune_fun.v1.otp.application.port.input.query.OtpQueries; @@ -39,7 +40,7 @@ private static VerifyOtp getVerifyOtpBehavior(final OtpQueries.Verify query) { @NotNull private static SaveJwtToken getSaveJwtTokenBehavior(CurrentAccount currentAccount) { - String authorities = String.join(",", currentAccount.roles()); + String authorities = String.join(Constants.COMMA, currentAccount.roles()); return new SaveJwtToken(currentAccount.username(), authorities); } diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/input/message/VoteMessageConsumer.java b/src/main/java/com/tune_fun/v1/vote/adapter/input/message/VoteMessageConsumer.java index 891cd368..623dc8a9 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/input/message/VoteMessageConsumer.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/input/message/VoteMessageConsumer.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.google.firebase.messaging.FirebaseMessagingException; -import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperRegisterFcmUseCase; -import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperUpdateDeliveryDateFcmUseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperRegisterNotificationUseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperUpdateDeliveryDateNotificationUseCase; import com.tune_fun.v1.vote.domain.event.VotePaperRegisterEvent; import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; import io.awspring.cloud.sqs.annotation.SqsListener; @@ -16,19 +16,19 @@ @RequiredArgsConstructor public class VoteMessageConsumer { - private final SendVotePaperRegisterFcmUseCase sendVotePaperRegisterFcmUseCase; - private final SendVotePaperUpdateDeliveryDateFcmUseCase sendVotePaperUpdateDeliveryDateFcmUseCase; + private final SendVotePaperRegisterNotificationUseCase sendVotePaperRegisterNotificationUseCase; + private final SendVotePaperUpdateDeliveryDateNotificationUseCase sendVotePaperUpdateDeliveryDateNotificationUseCase; @SqsListener(value = "${event.sqs.send-vote-paper-upload-notification.queue-name}") - public void consumeVotePaperUploadEvent(final VotePaperRegisterEvent votePaperRegisterEvent) throws JsonProcessingException, FirebaseMessagingException { - log.info("send-vote-paper-upload-notification-dev: {}", votePaperRegisterEvent.id()); - sendVotePaperRegisterFcmUseCase.send(votePaperRegisterEvent); + public void consumeVotePaperUploadEvent(final VotePaperRegisterEvent event) throws JsonProcessingException, FirebaseMessagingException { + log.info("CONSUME 'send-vote-paper-upload-notification-dev': {}", event.id()); + sendVotePaperRegisterNotificationUseCase.send(event); } @SqsListener(value = "${event.sqs.send-vote-paper-update-delivery-date-notification.queue-name}") - public void consumeVotePaperUpdateDeliveryDateEvent(final VotePaperUpdateDeliveryDateEvent votePaperUpdateDeliveryDateEvent) throws JsonProcessingException, FirebaseMessagingException { - log.info("send-vote-paper-update-delivery-date-notification-dev: {}", votePaperUpdateDeliveryDateEvent.id()); - sendVotePaperUpdateDeliveryDateFcmUseCase.send(votePaperUpdateDeliveryDateEvent); + public void consumeVotePaperUpdateDeliveryDateEvent(final VotePaperUpdateDeliveryDateEvent event) throws FirebaseMessagingException { + log.info("CONSUME 'send-vote-paper-update-delivery-date-notification-dev': {}", event.id()); + sendVotePaperUpdateDeliveryDateNotificationUseCase.send(event); } } diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/ScrollVotePaperResponse.java b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/ScrollVotePaperResponse.java new file mode 100644 index 00000000..3e455137 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/ScrollVotePaperResponse.java @@ -0,0 +1,9 @@ +package com.tune_fun.v1.vote.adapter.input.rest; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.tune_fun.v1.common.response.BasePayload; +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; +import org.springframework.data.domain.Window; + +public record ScrollVotePaperResponse(@JsonUnwrapped Window votePapers) implements BasePayload { +} diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VoteController.java b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VoteController.java index f5822e1e..b19050ea 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VoteController.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VoteController.java @@ -9,7 +9,6 @@ import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.userdetails.User; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -24,6 +23,7 @@ public class VoteController { private final RegisterVoteUseCase registerVoteUseCase; private final ResponseMapper responseMapper; + // @PreAuthorize("hasPermission(#votePaperId, 'VOTE', 'REGISTER_VOTE')") @PostMapping(value = Uris.REGISTER_VOTE) public ResponseEntity> registerVote(@PathVariable(name = "votePaperId") @NotNull(message = "{vote.paper.id.not_null}") final Long votePaperId, @PathVariable(name = "voteChoiceId") @NotNull(message = "{vote.choice.id.not_null}") final Long voteChoiceId, diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java index 7074eeb4..ebeb2440 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java @@ -10,16 +10,17 @@ import com.tune_fun.v1.common.response.ResponseMapper; import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands; import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVotePaperUseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.ScrollVotePaperUseCase; import com.tune_fun.v1.vote.application.port.input.usecase.UpdateVotePaperDeliveryDateUseCase; +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Window; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.userdetails.User; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @WebAdapter @@ -28,25 +29,39 @@ public class VotePaperController { private final ResponseMapper responseMapper; + private final ScrollVotePaperUseCase scrollVotePaperUseCase; private final RegisterVotePaperUseCase registerVotePaperUseCase; private final UpdateVotePaperDeliveryDateUseCase updateVotePaperDeliveryDateUseCase; + @GetMapping(value = Uris.VOTE_PAPER_ROOT) + public ResponseEntity> getVotePapers(@RequestParam(name = "last_id") Integer lastId, + @RequestParam(name = "sort_type", required = false, defaultValue = "RECENT") SortType sortType, + @CurrentUser User user) { + Window scrollableVotePapers = scrollVotePaperUseCase.scrollVotePaper(lastId, sortType.name()); + return responseMapper.ok(MessageCode.SUCCESS, new ScrollVotePaperResponse(scrollableVotePapers)); + } + // TODO : Follower 로직 구현 후 테스트 재진행 예정 @PreAuthorize("hasRole('ARTIST')") - @PostMapping(value = Uris.REGISTER_VOTE_PAPER) + @PostMapping(value = Uris.VOTE_PAPER_ROOT) public ResponseEntity> registerVotePaper(@Valid @RequestBody final VotePaperCommands.Register command, @CurrentUser final User user) throws JsonProcessingException { registerVotePaperUseCase.register(command, user); return responseMapper.ok(MessageCode.CREATED); } - // TODO : Vote 로직 구현 후 테스트 재진행 예정 - @PreAuthorize("hasRole('ARTIST') && hasPermission(#command.votePaperId(), 'VOTE_PAPER', 'SET_DELIEVERY_DATE')") - @PatchMapping(value = Uris.SET_VOTE_PAPER_DELIVERY_DATE) - public ResponseEntity> updateDeliveryDate(@Valid @RequestBody final VotePaperCommands.UpdateDeliveryDate command, - @CurrentUser final User user) throws JsonProcessingException { - updateVotePaperDeliveryDateUseCase.updateDeliveryDate(command); + // @PreAuthorize("hasRole('ARTIST') && hasPermission(#votePaperId, 'VOTE_PAPER', 'SET_DELIEVERY_DATE')") + @PreAuthorize("hasRole('ARTIST')") + @PatchMapping(value = Uris.UPDATE_VOTE_PAPER_DELIVERY_DATE) + public ResponseEntity> updateDeliveryDate(@PathVariable("votePaperId") @NotNull(message = "{vote.paper.id.not_null}") final Long votePaperId, + @Valid @RequestBody final VotePaperCommands.UpdateDeliveryDate command, + @CurrentUser final User user) { + updateVotePaperDeliveryDateUseCase.updateDeliveryDate(votePaperId, command, user); return responseMapper.ok(MessageCode.SUCCESS); } + public enum SortType { + RECENT, VOTE_COUNT + } + } diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/firebase/VoteFirebaseMessagingMapper.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/notification/VoteFirebaseMessagingMapper.java similarity index 70% rename from src/main/java/com/tune_fun/v1/vote/adapter/output/firebase/VoteFirebaseMessagingMapper.java rename to src/main/java/com/tune_fun/v1/vote/adapter/output/notification/VoteFirebaseMessagingMapper.java index f594d1c6..2c0013ca 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/firebase/VoteFirebaseMessagingMapper.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/notification/VoteFirebaseMessagingMapper.java @@ -1,9 +1,9 @@ -package com.tune_fun.v1.vote.adapter.output.firebase; +package com.tune_fun.v1.vote.adapter.output.notification; import com.tune_fun.v1.common.config.BaseMapperConfig; import com.tune_fun.v1.external.firebase.FirebaseMto; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateFcm; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterNotification; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateNotification; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -11,8 +11,8 @@ public abstract class VoteFirebaseMessagingMapper { @Mapping(target = "tokens", source = "fcmTokens") - public abstract FirebaseMto.ByTokens fromSendVotePaperRegisterFcmBehavior(final SendVotePaperRegisterFcm behavior); + public abstract FirebaseMto.ByTokens fromSendVotePaperRegisterNotificationBehavior(final SendVotePaperRegisterNotification behavior); @Mapping(target = "tokens", source = "fcmTokens") - public abstract FirebaseMto.ByTokens fromSendVotePaperUpdateDeliveryDateFcmBehavior(final SendVotePaperUpdateDeliveryDateFcm behavior); -} + public abstract FirebaseMto.ByTokens fromSendVotePaperUpdateDeliveryDateNotificationBehavior(final SendVotePaperUpdateDeliveryDateNotification behavior); +} \ No newline at end of file diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/firebase/VoteFcmAdapter.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/notification/VoteNotificationAdapter.java similarity index 59% rename from src/main/java/com/tune_fun/v1/vote/adapter/output/firebase/VoteFcmAdapter.java rename to src/main/java/com/tune_fun/v1/vote/adapter/output/notification/VoteNotificationAdapter.java index 3ef3e4cc..94810c71 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/firebase/VoteFcmAdapter.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/notification/VoteNotificationAdapter.java @@ -1,32 +1,32 @@ -package com.tune_fun.v1.vote.adapter.output.firebase; +package com.tune_fun.v1.vote.adapter.output.notification; import com.google.firebase.messaging.FirebaseMessagingException; import com.tune_fun.v1.external.firebase.FirebaseMessagingMediator; import com.tune_fun.v1.external.firebase.FirebaseMto; -import com.tune_fun.v1.vote.application.port.output.SendVoteFcmPort; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateFcm; +import com.tune_fun.v1.vote.application.port.output.SendVoteNotificationPort; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterNotification; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateNotification; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor -public class VoteFcmAdapter implements SendVoteFcmPort { +public class VoteNotificationAdapter implements SendVoteNotificationPort { private final FirebaseMessagingMediator firebaseMessagingMediator; private final VoteFirebaseMessagingMapper voteFirebaseMessagingMapper; @Override - public void notification(final SendVotePaperRegisterFcm behavior) throws FirebaseMessagingException { - FirebaseMto.ByTokens mto = voteFirebaseMessagingMapper.fromSendVotePaperRegisterFcmBehavior(behavior); + public void notification(final SendVotePaperRegisterNotification behavior) throws FirebaseMessagingException { + FirebaseMto.ByTokens mto = voteFirebaseMessagingMapper.fromSendVotePaperRegisterNotificationBehavior(behavior); firebaseMessagingMediator.sendMulticastMessageByTokens(mto); } @Override - public void notification(SendVotePaperUpdateDeliveryDateFcm sendVotePaperUpdateDeliveryDateFcmBehavior) throws FirebaseMessagingException { - FirebaseMto.ByTokens mto = voteFirebaseMessagingMapper.fromSendVotePaperUpdateDeliveryDateFcmBehavior(sendVotePaperUpdateDeliveryDateFcmBehavior); + public void notification(SendVotePaperUpdateDeliveryDateNotification behavior) throws FirebaseMessagingException { + FirebaseMto.ByTokens mto = voteFirebaseMessagingMapper.fromSendVotePaperUpdateDeliveryDateNotificationBehavior(behavior); firebaseMessagingMediator.sendMulticastMessageByTokens(mto); } } diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceMapper.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceMapper.java index 4c2ecb78..b3f7dd88 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceMapper.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceMapper.java @@ -1,6 +1,7 @@ package com.tune_fun.v1.vote.adapter.output.persistence; import com.tune_fun.v1.common.config.BaseMapperConfig; +import com.tune_fun.v1.common.constant.Constants; import com.tune_fun.v1.common.util.StringUtil; import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice; import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice; @@ -11,7 +12,7 @@ @Mapper( config = BaseMapperConfig.class, - imports = {StringUtil.class} + imports = {StringUtil.class, Constants.class} ) public abstract class VoteChoiceMapper { @@ -53,11 +54,11 @@ public abstract class VoteChoiceMapper { @Named("joinString") public String joinString(final Set strings) { - return String.join(",", strings); + return String.join(Constants.COMMA, strings); } @Named("splitString") public Set splitString(final String string) { - return Set.of(string.split(",")); + return Set.of(string.split(Constants.COMMA)); } } diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperCustomRepository.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperCustomRepository.java new file mode 100644 index 00000000..e4ce4631 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperCustomRepository.java @@ -0,0 +1,8 @@ +package com.tune_fun.v1.vote.adapter.output.persistence; + +import java.util.Optional; + +public interface VotePaperCustomRepository { + + Optional findOneAvailable(final Long id, final String username); +} diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperCustomRepositoryImpl.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperCustomRepositoryImpl.java new file mode 100644 index 00000000..2c1c40e2 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperCustomRepositoryImpl.java @@ -0,0 +1,37 @@ +package com.tune_fun.v1.vote.adapter.output.persistence; + +import com.querydsl.core.types.Predicate; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.tune_fun.v1.account.adapter.output.persistence.QAccountJpaEntity; +import com.tune_fun.v1.common.util.querydsl.PredicateBuilder; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; + +import static java.time.LocalDateTime.now; + +@RequiredArgsConstructor +public class VotePaperCustomRepositoryImpl implements VotePaperCustomRepository { + + private final JPAQueryFactory queryFactory; + + private static final QVotePaperJpaEntity VOTE_PAPER = QVotePaperJpaEntity.votePaperJpaEntity; + + private static final QAccountJpaEntity ACCOUNT = QAccountJpaEntity.accountJpaEntity; + + @Override + public Optional findOneAvailable(final Long id, final String username) { + + Predicate predicate = PredicateBuilder.builder() + .and().eqNumber(VOTE_PAPER.id, id) + .and().eqString(ACCOUNT.username, username) + .build(); + + return Optional.ofNullable( + queryFactory.selectFrom(VOTE_PAPER) + .join(VOTE_PAPER.author, ACCOUNT) + .where(VOTE_PAPER.voteEndAt.gt(now()), predicate) + .fetchOne() + ); + } +} diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java index d006e81b..be6c7c1c 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java @@ -5,12 +5,14 @@ import com.tune_fun.v1.common.util.StringUtil; import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; import org.mapstruct.Named; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; @Mapper( config = BaseMapperConfig.class, @@ -20,13 +22,29 @@ public abstract class VotePaperMapper { @Mapping(target = "option", source = "option", qualifiedByName = "toValue") @Mapping(target = "author", source = "author.nickname") + @Mapping(target = "authorUsername", source = "author.username") public abstract RegisteredVotePaper registeredVotePaper(final VotePaperJpaEntity votePaperJpaEntity); + @Mapping(target = "authorUsername", source = "author.username") + @Mapping(target = "authorNickname", source = "author.nickname") + @Mapping(target = "remainDays", source = ".", qualifiedByName = "remainDays") + @Mapping(target = "totalVoteCount", constant = "0") + @Mapping(target = "totalLikeCount", constant = "0") + public abstract ScrollableVotePaper scrollableVotePaper(final VotePaperJpaEntity votePaperJpaEntity); + @Named("toValue") public String toValue(final VotePaperOption option) { return option.getValue(); } + @Named("remainDays") + public Long remainDays(final VotePaperJpaEntity votePaperJpaEntity) { + return LocalDateTime.now().until(votePaperJpaEntity.getVoteEndAt(), ChronoUnit.DAYS); + } + + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "updatedAt", ignore = true) @Mapping(target = "uuid", expression = "java(StringUtil.uuid())") @Mapping(target = "author", source = "author") @Mapping(target = "option", source = "saveVotePaper.option", qualifiedByName = "votePaperOption") diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java index bbee7509..e309c87a 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java @@ -1,12 +1,15 @@ package com.tune_fun.v1.vote.adapter.output.persistence; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import java.time.LocalDateTime; import java.util.Optional; -public interface VotePaperRepository extends JpaRepository { +public interface VotePaperRepository extends JpaRepository, VotePaperCustomRepository { @EntityGraph(attributePaths = {"author"}) Optional findByVoteEndAtAfterAndAuthorUsername(LocalDateTime voteEndAt, String author); @@ -15,4 +18,7 @@ public interface VotePaperRepository extends JpaRepository findByVoteEndAtBeforeAndId(LocalDateTime voteEndAt, Long id); -} \ No newline at end of file + @EntityGraph(attributePaths = {"author"}) + Window findFirst10By(KeysetScrollPosition position, Sort sort); + +} diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapter.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapter.java index b4c09544..6c0171dc 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapter.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapter.java @@ -2,6 +2,7 @@ import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity; import com.tune_fun.v1.account.adapter.output.persistence.AccountPersistenceAdapter; +import com.tune_fun.v1.common.constant.Constants; import com.tune_fun.v1.common.hexagon.PersistenceAdapter; import com.tune_fun.v1.common.util.StringUtil; import com.tune_fun.v1.vote.application.port.output.*; @@ -10,16 +11,24 @@ import com.tune_fun.v1.vote.domain.value.RegisteredVote; import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import static java.time.LocalDateTime.now; import static java.util.stream.Collectors.toSet; +import static org.springframework.data.domain.ScrollPosition.forward; +import static org.springframework.data.domain.Sort.Order.desc; +import static org.springframework.data.domain.Sort.by; @Component @PersistenceAdapter @@ -66,12 +75,33 @@ public void saveVote(Long voteChoiceId, String username) { voteRepository.save(vote); } + /** + * TODO : VOTE_COUNT 정렬은 집계 로직 구현 후 추가 예정 + * + * @param lastId 해당 페이지의 마지막 인덱스 + * @param sortType 정렬 방식 + * @return {@link org.springframework.data.domain.Window} of {@link com.tune_fun.v1.vote.domain.value.ScrollableVotePaper} + * @see Keyset-scrolling queries add identifier columns twice when Sort already sorts by Id + * @see Spring Data JPA Scroll API
+ */ + @Override + public Window scrollVotePaper(final Integer lastId, final String sortType) { + KeysetScrollPosition position = forward(Map.of("id", lastId, "voteEndAt", Constants.LOCAL_DATE_TIME_MIN)); + Sort sort = by(desc("id"), desc("voteEndAt")); + return votePaperRepository.findFirst10By(position, sort).map(votePaperMapper::scrollableVotePaper); + } + @Override public Optional loadRegisteredVotePaper(final String username) { - return findAvailableVotePaperByAuthor(username) - .map(votePaperMapper::registeredVotePaper); + return findProgressingVotePaperByAuthor(username).map(votePaperMapper::registeredVotePaper); } + @Override + public Optional loadRegisteredVotePaper(final Long votePaperId) { + return findProgressingVotePaperById(votePaperId).map(votePaperMapper::registeredVotePaper); + } + + @Override public RegisteredVotePaper saveVotePaper(final SaveVotePaper saveVotePaper) { AccountJpaEntity account = accountPersistenceAdapter.loadAccountByUsername(saveVotePaper.author()) @@ -84,7 +114,7 @@ public RegisteredVotePaper saveVotePaper(final SaveVotePaper saveVotePaper) { @Override public RegisteredVotePaper updateDeliveryAt(final Long votePaperId, final LocalDateTime deliveryAt) { - VotePaperJpaEntity votePaper = findCompleteVotePaperById(votePaperId) + VotePaperJpaEntity votePaper = findOneAvailable(votePaperId, Constants.NULL_STRING) .orElseThrow(() -> new IllegalArgumentException("VotePaper not found")); VotePaperJpaEntity updatedVotePaper = votePaperMapper.updateDeliveryAt(deliveryAt, votePaper.toBuilder()).build(); @@ -100,7 +130,7 @@ public List loadRegisteredVoteChoice(Long votePaperId) { @Override public void saveVoteChoice(final Long votePaperId, final Set behavior) { - VotePaperJpaEntity votePaperJpaEntity = findProgressingVotePaperById(votePaperId) + VotePaperJpaEntity votePaperJpaEntity = findOneAvailable(votePaperId, Constants.NULL_STRING) .orElseThrow(() -> new IllegalArgumentException("VotePaper not found")); Set voteChoices = voteChoiceMapper.fromSaveVoteChoiceBehaviors(behavior); @@ -111,20 +141,23 @@ public void saveVoteChoice(final Long votePaperId, final Set beh voteChoiceRepository.saveAll(updatedVoteChoices); } - - public Optional findProgressingVotePaperById(final Long id) { - return votePaperRepository.findByVoteEndAtAfterAndId(now(), id); + public Optional findOneAvailable(final Long votePaperId, final String username) { + return votePaperRepository.findOneAvailable(votePaperId, username); } public Optional findCompleteVotePaperById(final Long id) { return votePaperRepository.findByVoteEndAtBeforeAndId(now(), id); } - public Optional findAvailableVotePaperByAuthor(final String username) { + public Optional findProgressingVotePaperByAuthor(final String username) { return votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(now(), username); } - public List findAllByVotePaperId(Long votePaperId) { + public Optional findProgressingVotePaperById(final Long id) { + return votePaperRepository.findByVoteEndAtAfterAndId(now(), id); + } + + public List findAllByVotePaperId(final Long votePaperId) { return voteChoiceRepository.findAllByVotePaperId(votePaperId); } } diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/input/command/VotePaperCommands.java b/src/main/java/com/tune_fun/v1/vote/application/port/input/command/VotePaperCommands.java index 4ae386cc..353aa375 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/port/input/command/VotePaperCommands.java +++ b/src/main/java/com/tune_fun/v1/vote/application/port/input/command/VotePaperCommands.java @@ -1,5 +1,6 @@ package com.tune_fun.v1.vote.application.port.input.command; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.tune_fun.v1.common.validation.Period; @@ -19,10 +20,12 @@ public record Register( @NotBlank(message = "{vote.paper.content.not_blank}") String content, @NotBlank(message = "{vote.paper.option.not_blank}") String option, + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @NotNull(message = "{vote.paper.vote_start_at.not_null}") @Future(message = "{vote.paper.vote_start_at.future}") LocalDateTime voteStartAt, + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @NotNull(message = "{vote.paper.vote_end_at.not_null}") @Future(message = "{vote.paper.vote_end_at.future}") LocalDateTime voteEndAt, @@ -59,9 +62,8 @@ public record Offer( @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public record UpdateDeliveryDate( - @NotNull(message = "{vote.paper.id.not_null}") - Long votePaperId, + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @NotNull(message = "{vote.paper.delivery_at.not_null}") @Future(message = "{vote.paper.delivery_at.future}") LocalDateTime deliveryAt diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/ScrollVotePaperUseCase.java b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/ScrollVotePaperUseCase.java new file mode 100644 index 00000000..0a682b24 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/ScrollVotePaperUseCase.java @@ -0,0 +1,11 @@ +package com.tune_fun.v1.vote.application.port.input.usecase; + +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; +import org.springframework.data.domain.Window; + +@FunctionalInterface +public interface ScrollVotePaperUseCase { + + Window scrollVotePaper(Integer lastId, String sortType); + +} diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperRegisterFcmUseCase.java b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperRegisterNotificationUseCase.java similarity index 86% rename from src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperRegisterFcmUseCase.java rename to src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperRegisterNotificationUseCase.java index a4d64044..fca64419 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperRegisterFcmUseCase.java +++ b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperRegisterNotificationUseCase.java @@ -4,6 +4,6 @@ import com.google.firebase.messaging.FirebaseMessagingException; import com.tune_fun.v1.vote.domain.event.VotePaperRegisterEvent; -public interface SendVotePaperRegisterFcmUseCase { +public interface SendVotePaperRegisterNotificationUseCase { void send(final VotePaperRegisterEvent votePaperRegisterEvent) throws JsonProcessingException, FirebaseMessagingException; } diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperUpdateDeliveryDateFcmUseCase.java b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperUpdateDeliveryDateNotificationUseCase.java similarity index 82% rename from src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperUpdateDeliveryDateFcmUseCase.java rename to src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperUpdateDeliveryDateNotificationUseCase.java index cb0b0b56..cd33409b 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperUpdateDeliveryDateFcmUseCase.java +++ b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/SendVotePaperUpdateDeliveryDateNotificationUseCase.java @@ -4,7 +4,7 @@ import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; @FunctionalInterface -public interface SendVotePaperUpdateDeliveryDateFcmUseCase { +public interface SendVotePaperUpdateDeliveryDateNotificationUseCase { void send(final VotePaperUpdateDeliveryDateEvent event) throws FirebaseMessagingException; diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/UpdateVotePaperDeliveryDateUseCase.java b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/UpdateVotePaperDeliveryDateUseCase.java index 4ec2a613..d29e4c40 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/UpdateVotePaperDeliveryDateUseCase.java +++ b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/UpdateVotePaperDeliveryDateUseCase.java @@ -1,10 +1,11 @@ package com.tune_fun.v1.vote.application.port.input.usecase; import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands; +import org.springframework.security.core.userdetails.User; @FunctionalInterface public interface UpdateVotePaperDeliveryDateUseCase { - void updateDeliveryDate(final VotePaperCommands.UpdateDeliveryDate command); + void updateDeliveryDate(final Long votePaperId, final VotePaperCommands.UpdateDeliveryDate command, final User user); } diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/output/LoadVotePaperPort.java b/src/main/java/com/tune_fun/v1/vote/application/port/output/LoadVotePaperPort.java index ccf36413..ffddc956 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/port/output/LoadVotePaperPort.java +++ b/src/main/java/com/tune_fun/v1/vote/application/port/output/LoadVotePaperPort.java @@ -1,10 +1,17 @@ package com.tune_fun.v1.vote.application.port.output; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; +import org.springframework.data.domain.Window; import java.util.Optional; public interface LoadVotePaperPort { + Window scrollVotePaper(final Integer lastId, final String sortType); + Optional loadRegisteredVotePaper(final String username); + + Optional loadRegisteredVotePaper(final Long votePaperId); + } diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/output/SendVoteFcmPort.java b/src/main/java/com/tune_fun/v1/vote/application/port/output/SendVoteFcmPort.java deleted file mode 100644 index 796256a3..00000000 --- a/src/main/java/com/tune_fun/v1/vote/application/port/output/SendVoteFcmPort.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.tune_fun.v1.vote.application.port.output; - -import com.google.firebase.messaging.FirebaseMessagingException; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateFcm; - -public interface SendVoteFcmPort { - void notification(final SendVotePaperRegisterFcm sendVotePaperRegisterFcmBehavior) throws FirebaseMessagingException; - - void notification(final SendVotePaperUpdateDeliveryDateFcm sendVotePaperUpdateDeliveryDateFcmBehavior) throws FirebaseMessagingException; -} diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/output/SendVoteNotificationPort.java b/src/main/java/com/tune_fun/v1/vote/application/port/output/SendVoteNotificationPort.java new file mode 100644 index 00000000..f58f8d8d --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/port/output/SendVoteNotificationPort.java @@ -0,0 +1,11 @@ +package com.tune_fun.v1.vote.application.port.output; + +import com.google.firebase.messaging.FirebaseMessagingException; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterNotification; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateNotification; + +public interface SendVoteNotificationPort { + void notification(final SendVotePaperRegisterNotification behavior) throws FirebaseMessagingException; + + void notification(final SendVotePaperUpdateDeliveryDateNotification behavior) throws FirebaseMessagingException; +} diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/ScrollVotePaperService.java b/src/main/java/com/tune_fun/v1/vote/application/service/ScrollVotePaperService.java new file mode 100644 index 00000000..f7cf5def --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/service/ScrollVotePaperService.java @@ -0,0 +1,22 @@ +package com.tune_fun.v1.vote.application.service; + +import com.tune_fun.v1.common.hexagon.UseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.ScrollVotePaperUseCase; +import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort; +import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Window; +import org.springframework.stereotype.Service; + +@Service +@UseCase +@RequiredArgsConstructor +public class ScrollVotePaperService implements ScrollVotePaperUseCase { + + private final LoadVotePaperPort loadVotePaperPort; + + @Override + public Window scrollVotePaper(Integer lastId, String sortType) { + return loadVotePaperPort.scrollVotePaper(lastId, sortType); + } +} diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperRegisterFcmService.java b/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperRegisterNotificationService.java similarity index 62% rename from src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperRegisterFcmService.java rename to src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperRegisterNotificationService.java index 7acfee8e..a8b9dd91 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperRegisterFcmService.java +++ b/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperRegisterNotificationService.java @@ -6,9 +6,9 @@ import com.tune_fun.v1.account.domain.value.NotificationApprovedDevice; import com.tune_fun.v1.common.hexagon.UseCase; import com.tune_fun.v1.common.util.ObjectUtil; -import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperRegisterFcmUseCase; -import com.tune_fun.v1.vote.application.port.output.SendVoteFcmPort; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm; +import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperRegisterNotificationUseCase; +import com.tune_fun.v1.vote.application.port.output.SendVoteNotificationPort; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterNotification; import com.tune_fun.v1.vote.domain.event.VotePaperRegisterEvent; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -26,10 +26,10 @@ @Service @UseCase @RequiredArgsConstructor -public class SendVotePaperRegisterFcmService implements SendVotePaperRegisterFcmUseCase { +public class SendVotePaperRegisterNotificationService implements SendVotePaperRegisterNotificationUseCase { private final LoadDevicePort loadDevicePort; - private final SendVoteFcmPort sendVoteFcmPort; + private final SendVoteNotificationPort sendVoteNotificationPort; private final VoteBehaviorMapper voteBehaviorMapper; @@ -44,26 +44,27 @@ public void send(VotePaperRegisterEvent votePaperRegisterEvent) throws JsonProce loadNotificationApprovedDevice(true, null, null, null); log.info("notificationApprovedDevices: \n{}", objectUtil.objectToPrettyJson(notificationApprovedDevices)); - sendVotePaperRegisterFcm(votePaperRegisterEvent, notificationApprovedDevices); + if (!notificationApprovedDevices.isEmpty()) + sendVotePaperRegisterNotification(votePaperRegisterEvent, notificationApprovedDevices); } - @Retryable(retryFor = FirebaseMessagingException.class, recover = "recoverSendVotePaperRegisterFcm", backoff = @Backoff(delay = 2000, multiplier = 1.5, maxDelay = 10000)) - private void sendVotePaperRegisterFcm(final VotePaperRegisterEvent votePaperRegisterEvent, final List notificationApprovedDevices) throws FirebaseMessagingException { + @Retryable(retryFor = FirebaseMessagingException.class, recover = "recoverSendVotePaperRegisterNotification", backoff = @Backoff(delay = 2000, multiplier = 1.5, maxDelay = 10000)) + private void sendVotePaperRegisterNotification(final VotePaperRegisterEvent event, final List devices) throws FirebaseMessagingException { if (RetrySynchronizationManager.getContext() != null) { RetryContext retryContext = RetrySynchronizationManager.getContext(); log.info("Retry count: {}", retryContext.getRetryCount()); - log.error("sendVotePaperRegisterFcm FAILED. \n{}\nRetry count: {}", retryContext.getLastThrowable(), retryContext.getRetryCount()); + log.error("sendVotePaperRegisterNotification FAILED. \n{}\nRetry count: {}", retryContext.getLastThrowable(), retryContext.getRetryCount()); } - SendVotePaperRegisterFcm sendVotePaperRegisterFcmBehavior = voteBehaviorMapper - .sendVotePaperRegisterFcm(votePaperRegisterEvent, notificationApprovedDevices); - sendVoteFcmPort.notification(sendVotePaperRegisterFcmBehavior); + SendVotePaperRegisterNotification sendVotePaperRegisterNotificationBehavior = voteBehaviorMapper + .sendVotePaperRegisterFcm(event, devices); + sendVoteNotificationPort.notification(sendVotePaperRegisterNotificationBehavior); } // TODO : Slack Notification? @Recover - private void recoverSendVotePaperRegisterFcm(FirebaseMessagingException e, VotePaperRegisterEvent votePaperRegisterEvent, List notificationApprovedDevices) { - log.error("Failed to send FCM notification for vote paper register event: {}", votePaperRegisterEvent.id(), e); + private void recoverSendVotePaperRegisterNotification(FirebaseMessagingException e, VotePaperRegisterEvent event, List notificationApprovedDevices) { + log.error("Failed to send FCM notification for vote paper register event: {}", event.id(), e); } } diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperUpdateDeliveryDateFcmService.java b/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperUpdateDeliveryDateFcmService.java deleted file mode 100644 index 38e21824..00000000 --- a/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperUpdateDeliveryDateFcmService.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.tune_fun.v1.vote.application.service; - -import com.google.firebase.messaging.FirebaseMessagingException; -import com.tune_fun.v1.account.application.port.output.device.LoadDevicePort; -import com.tune_fun.v1.account.domain.value.NotificationApprovedDevice; -import com.tune_fun.v1.common.hexagon.UseCase; -import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperUpdateDeliveryDateFcmUseCase; -import com.tune_fun.v1.vote.application.port.output.LoadVotePort; -import com.tune_fun.v1.vote.application.port.output.SendVoteFcmPort; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateFcm; -import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -@UseCase -@RequiredArgsConstructor -public class SendVotePaperUpdateDeliveryDateFcmService implements SendVotePaperUpdateDeliveryDateFcmUseCase { - - private final LoadVotePort loadVotePort; - private final LoadDevicePort loadDevicePort; - - private final SendVoteFcmPort sendVoteFcmPort; - - private final VoteBehaviorMapper voteBehaviorMapper; - - - @Override - public void send(final VotePaperUpdateDeliveryDateEvent event) throws FirebaseMessagingException { - List voterIds = loadVotePort.loadVoterIdsByVotePaperUuid(event.id()); - - List notificationApprovedDevices = - loadDevicePort.loadNotificationApprovedDevice(null, null, true, voterIds); - - SendVotePaperUpdateDeliveryDateFcm sendVotePaperUpdateDeliveryDateFcmBehavior = - voteBehaviorMapper.sendVotePaperUpdateDeliveryDateFcm(event, notificationApprovedDevices); - - sendVoteFcmPort.notification(sendVotePaperUpdateDeliveryDateFcmBehavior); - } - - -} diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperUpdateDeliveryDateNotificationService.java b/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperUpdateDeliveryDateNotificationService.java new file mode 100644 index 00000000..cda929d6 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/service/SendVotePaperUpdateDeliveryDateNotificationService.java @@ -0,0 +1,70 @@ +package com.tune_fun.v1.vote.application.service; + +import com.google.firebase.messaging.FirebaseMessagingException; +import com.tune_fun.v1.account.application.port.output.device.LoadDevicePort; +import com.tune_fun.v1.account.domain.value.NotificationApprovedDevice; +import com.tune_fun.v1.common.hexagon.UseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperUpdateDeliveryDateNotificationUseCase; +import com.tune_fun.v1.vote.application.port.output.LoadVotePort; +import com.tune_fun.v1.vote.application.port.output.SendVoteNotificationPort; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateNotification; +import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.retry.RetryContext; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Recover; +import org.springframework.retry.annotation.Retryable; +import org.springframework.retry.support.RetrySynchronizationManager; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +@UseCase +@RequiredArgsConstructor +public class SendVotePaperUpdateDeliveryDateNotificationService implements SendVotePaperUpdateDeliveryDateNotificationUseCase { + + private final LoadVotePort loadVotePort; + private final LoadDevicePort loadDevicePort; + + private final SendVoteNotificationPort sendVoteNotificationPort; + + private final VoteBehaviorMapper voteBehaviorMapper; + + + @Override + public void send(final VotePaperUpdateDeliveryDateEvent event) throws FirebaseMessagingException { + List voterIds = loadVotePort.loadVoterIdsByVotePaperUuid(event.id()); + + List notificationApprovedDevices = + loadDevicePort.loadNotificationApprovedDevice(null, null, true, voterIds); + + if (!notificationApprovedDevices.isEmpty()) + sendVotePaperUpdateDeliveryDateNotification(event, notificationApprovedDevices); + } + + @Retryable(retryFor = FirebaseMessagingException.class, recover = "recoverSendVotePaperUpdateDeliveryDateNotification", backoff = @Backoff(delay = 2000, multiplier = 1.5, maxDelay = 10000)) + private void sendVotePaperUpdateDeliveryDateNotification(final VotePaperUpdateDeliveryDateEvent event, final List devices) throws FirebaseMessagingException { + if (RetrySynchronizationManager.getContext() != null) { + RetryContext retryContext = RetrySynchronizationManager.getContext(); + log.info("Retry count: {}", retryContext.getRetryCount()); + + log.error("sendVotePaperUpdateDeliveryDateNotification FAILED. \n{}\nRetry count: {}", retryContext.getLastThrowable(), retryContext.getRetryCount()); + } + + + SendVotePaperUpdateDeliveryDateNotification behavior = voteBehaviorMapper + .sendVotePaperUpdateDeliveryDateNotification(event, devices); + sendVoteNotificationPort.notification(behavior); + } + + // TODO : Slack Notification? + @Recover + private void recoverSendVotePaperUpdateDeliveryDateNotification(final FirebaseMessagingException e, final VotePaperUpdateDeliveryDateEvent event, final List notificationApprovedDevices) { + log.error("sendVotePaperUpdateDeliveryDateNotification FAILED. \n{}", event.id(), e); + } + + +} diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/UpdateVotePaperDeliveryDateService.java b/src/main/java/com/tune_fun/v1/vote/application/service/UpdateVotePaperDeliveryDateService.java index 0986db27..7dd5a0dd 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/service/UpdateVotePaperDeliveryDateService.java +++ b/src/main/java/com/tune_fun/v1/vote/application/service/UpdateVotePaperDeliveryDateService.java @@ -1,33 +1,49 @@ package com.tune_fun.v1.vote.application.service; +import com.tune_fun.v1.common.exception.CommonApplicationException; import com.tune_fun.v1.common.hexagon.UseCase; import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands; import com.tune_fun.v1.vote.application.port.input.usecase.UpdateVotePaperDeliveryDateUseCase; +import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort; import com.tune_fun.v1.vote.application.port.output.ProduceVotePaperUpdateDeliveryDateEventPort; import com.tune_fun.v1.vote.application.port.output.UpdateDeliveryAtPort; import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static com.tune_fun.v1.common.response.MessageCode.VOTE_PAPER_NOT_FOUND; +import static com.tune_fun.v1.common.response.MessageCode.VOTE_POLICY_ONLY_AUTHOR_CAN_UPDATE_DELIVERY_DATE; +import static java.time.LocalDateTime.now; + @Service @UseCase @RequiredArgsConstructor public class UpdateVotePaperDeliveryDateService implements UpdateVotePaperDeliveryDateUseCase { + private final LoadVotePaperPort loadVotePaperPort; private final UpdateDeliveryAtPort updateDeliveryAtPort; private final ProduceVotePaperUpdateDeliveryDateEventPort produceVotePaperUploadEventPort; - @Transactional @Override - public void updateDeliveryDate(final VotePaperCommands.UpdateDeliveryDate command) { - RegisteredVotePaper registeredVotePaper = updateDeliveryAtPort.updateDeliveryAt(command.votePaperId(), command.deliveryAt()); + public void updateDeliveryDate(final Long votePaperId, final VotePaperCommands.UpdateDeliveryDate command, final User user) { + RegisteredVotePaper registeredVotePaper = loadVotePaperPort.loadRegisteredVotePaper(votePaperId) + .orElseThrow(() -> new CommonApplicationException(VOTE_PAPER_NOT_FOUND)); + + if (!registeredVotePaper.isAuthor(user.getUsername())) + throw new CommonApplicationException(VOTE_POLICY_ONLY_AUTHOR_CAN_UPDATE_DELIVERY_DATE); + + if (!registeredVotePaper.isValidDeliveryAt(now(), command.deliveryAt())) + throw new CommonApplicationException(VOTE_POLICY_ONLY_AUTHOR_CAN_UPDATE_DELIVERY_DATE); + + RegisteredVotePaper updatedVotePaper = updateDeliveryAtPort.updateDeliveryAt(votePaperId, command.deliveryAt()); - VotePaperUpdateDeliveryDateEvent event = new VotePaperUpdateDeliveryDateEvent(registeredVotePaper.uuid(), - registeredVotePaper.author(), registeredVotePaper.title(), registeredVotePaper.content()); + VotePaperUpdateDeliveryDateEvent event = new VotePaperUpdateDeliveryDateEvent(updatedVotePaper.uuid(), + updatedVotePaper.author(), updatedVotePaper.title(), updatedVotePaper.content()); produceVotePaperUploadEventPort.produceVotePaperUpdateDeliveryDateEvent(event); } diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/VoteBehaviorMapper.java b/src/main/java/com/tune_fun/v1/vote/application/service/VoteBehaviorMapper.java index d97c2285..fdc606f4 100644 --- a/src/main/java/com/tune_fun/v1/vote/application/service/VoteBehaviorMapper.java +++ b/src/main/java/com/tune_fun/v1/vote/application/service/VoteBehaviorMapper.java @@ -5,8 +5,8 @@ import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands; import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice; import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm; -import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateFcm; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterNotification; +import com.tune_fun.v1.vote.domain.behavior.SendVotePaperUpdateDeliveryDateNotification; import com.tune_fun.v1.vote.domain.event.VotePaperRegisterEvent; import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; import org.mapstruct.IterableMapping; @@ -39,12 +39,12 @@ public abstract class VoteBehaviorMapper { @Mapping(target = "title", source = "event", qualifiedByName = "votePaperRegisterFcmTitle") @Mapping(target = "body", source = "event.title") @Mapping(target = "fcmTokens", source = "devices", qualifiedByName = "fcmTokens") - public abstract SendVotePaperRegisterFcm sendVotePaperRegisterFcm(final VotePaperRegisterEvent event, final List devices); + public abstract SendVotePaperRegisterNotification sendVotePaperRegisterFcm(final VotePaperRegisterEvent event, final List devices); @Mapping(target = "title", source = "event", qualifiedByName = "votePaperUpdateDeliveryDateFcmTitle") @Mapping(target = "body", source = "event.title") @Mapping(target = "fcmTokens", source = "devices", qualifiedByName = "fcmTokens") - public abstract SendVotePaperUpdateDeliveryDateFcm sendVotePaperUpdateDeliveryDateFcm(final VotePaperUpdateDeliveryDateEvent event, final List devices); + public abstract SendVotePaperUpdateDeliveryDateNotification sendVotePaperUpdateDeliveryDateNotification(final VotePaperUpdateDeliveryDateEvent event, final List devices); @Named("votePaperRegisterFcmTitle") public String votePaperRegisterFcmTitle(final VotePaperRegisterEvent event) { diff --git a/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperRegisterFcm.java b/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperRegisterNotification.java similarity index 80% rename from src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperRegisterFcm.java rename to src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperRegisterNotification.java index 0cc2f8f7..97168268 100644 --- a/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperRegisterFcm.java +++ b/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperRegisterNotification.java @@ -3,7 +3,7 @@ import java.util.Map; import java.util.Set; -public record SendVotePaperRegisterFcm( +public record SendVotePaperRegisterNotification( String title, String body, Map data, diff --git a/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperUpdateDeliveryDateFcm.java b/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperUpdateDeliveryDateNotification.java similarity index 77% rename from src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperUpdateDeliveryDateFcm.java rename to src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperUpdateDeliveryDateNotification.java index e1c6c526..7634cc48 100644 --- a/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperUpdateDeliveryDateFcm.java +++ b/src/main/java/com/tune_fun/v1/vote/domain/behavior/SendVotePaperUpdateDeliveryDateNotification.java @@ -3,7 +3,7 @@ import java.util.Map; import java.util.Set; -public record SendVotePaperUpdateDeliveryDateFcm( +public record SendVotePaperUpdateDeliveryDateNotification( String title, String body, Map data, diff --git a/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVotePaper.java b/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVotePaper.java index 5848a776..0ecc5986 100644 --- a/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVotePaper.java +++ b/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVotePaper.java @@ -6,6 +6,7 @@ public record RegisteredVotePaper( Long id, String uuid, String author, + String authorUsername, String title, String content, String option, @@ -15,4 +16,13 @@ public record RegisteredVotePaper( LocalDateTime createdAt, LocalDateTime updatedAt ) { + + public boolean isAuthor(final String username) { + return authorUsername.equals(username); + } + + public boolean isValidDeliveryAt(final LocalDateTime now, final LocalDateTime deliveryAt) { + return deliveryAt.isAfter(now) && deliveryAt.isAfter(voteEndAt); + } + } diff --git a/src/main/java/com/tune_fun/v1/vote/domain/value/ScrollableVotePaper.java b/src/main/java/com/tune_fun/v1/vote/domain/value/ScrollableVotePaper.java new file mode 100644 index 00000000..e8319bce --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/domain/value/ScrollableVotePaper.java @@ -0,0 +1,18 @@ +package com.tune_fun.v1.vote.domain.value; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public record ScrollableVotePaper( + Long id, + String uuid, + String title, + String authorUsername, + String authorProfileImageUrl, + String authorNickname, + Long remainDays, + String totalVoteCount, + String totalLikeCount +) { +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c131d050..b834339e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -117,7 +117,9 @@ event: send-vote-paper-upload-notification: queue-name: send-vote-paper-upload-notification-dev send-vote-paper-update-delivery-date-notification: - queue-name: send-vote-paper-delivery-date-notification-dev + queue-name: send-vote-paper-update-delivery-date-notification-dev + test: + queue-name: test kafka: send-vote-paper-upload-notification: topic: send-vote-paper-upload-notification-dev diff --git a/src/main/resources/application-dev_standalone.yml b/src/main/resources/application-dev_standalone.yml index 15a7b6a2..3bb41d34 100644 --- a/src/main/resources/application-dev_standalone.yml +++ b/src/main/resources/application-dev_standalone.yml @@ -117,7 +117,7 @@ event: send-vote-paper-upload-notification: queue-name: send-vote-paper-upload-notification-dev send-vote-paper-update-delivery-date-notification: - queue-name: send-vote-paper-delivery-date-notification-dev + queue-name: send-vote-paper-update-delivery-date-notification-dev kafka: send-vote-paper-upload-notification: topic: send-vote-paper-upload-notification-dev diff --git a/src/main/resources/application-test_standalone.yml b/src/main/resources/application-test_standalone.yml index 9b4e1deb..a04282fa 100644 --- a/src/main/resources/application-test_standalone.yml +++ b/src/main/resources/application-test_standalone.yml @@ -102,7 +102,7 @@ event: send-vote-paper-upload-notification: queue-name: send-vote-paper-upload-notification-dev send-vote-paper-update-delivery-date-notification: - queue-name: send-vote-paper-delivery-date-notification-dev + queue-name: send-vote-paper-update-delivery-date-notification-dev test: queue-name: test kafka: diff --git a/src/test/java/com/tune_fun/v1/base/architecture/BoundedContextDependencyRuleTest.java b/src/test/java/com/tune_fun/v1/base/architecture/BoundedContextDependencyRuleTest.java index 5581e28f..112323a3 100644 --- a/src/test/java/com/tune_fun/v1/base/architecture/BoundedContextDependencyRuleTest.java +++ b/src/test/java/com/tune_fun/v1/base/architecture/BoundedContextDependencyRuleTest.java @@ -2,7 +2,7 @@ import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; -import org.junit.jupiter.api.Test; +import com.tune_fun.v1.common.constant.Constants; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; @@ -50,11 +50,11 @@ public static void checkNoDependencyFromTo( } private static String fullyBoundedContextPackage(String boundedContextPackage) { - return ROOT_PACKAGE + "." + boundedContextPackage; + return ROOT_PACKAGE + Constants.DOT + boundedContextPackage; } private static String fullyQualified(String rootPackage, String packageName) { - return rootPackage + '.' + packageName + ".."; + return rootPackage + Constants.DOT + packageName + ".."; } public abstract String getBoundedContextPackage(); diff --git a/src/test/java/com/tune_fun/v1/dummy/DummyService.java b/src/test/java/com/tune_fun/v1/dummy/DummyService.java index b9ebacf1..349e4b6c 100644 --- a/src/test/java/com/tune_fun/v1/dummy/DummyService.java +++ b/src/test/java/com/tune_fun/v1/dummy/DummyService.java @@ -25,12 +25,16 @@ import com.tune_fun.v1.vote.adapter.output.persistence.VotePersistenceAdapter; import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands; import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVotePaperUseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVoteUseCase; import com.tune_fun.v1.vote.application.service.VoteBehaviorMapper; import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice; import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; @@ -38,6 +42,9 @@ import org.springframework.transaction.annotation.Transactional; import java.security.NoSuchAlgorithmException; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; import java.util.Set; @@ -68,6 +75,9 @@ public class DummyService { @Autowired private RegisterVotePaperUseCase registerVotePaperUseCase; + @Autowired + private RegisterVoteUseCase registerVoteUseCase; + @Autowired private AccountPersistenceAdapter accountPersistenceAdapter; @@ -83,6 +93,9 @@ public class DummyService { @Autowired private VoteBehaviorMapper voteBehaviorMapper; + @Autowired + private JdbcTemplate jdbcTemplate; + private AccountJpaEntity defaultAccount = null; private DeviceJpaEntity defaultDevice = null; @@ -219,12 +232,47 @@ public void initVotePaper() { RegisteredVotePaper registeredVotePaper = saveVotePaper(command, user); saveVoteChoiceByRegisteredVotePaper(command, registeredVotePaper); - votePersistenceAdapter.findAvailableVotePaperByAuthor(defaultArtistUsername) + votePersistenceAdapter.findProgressingVotePaperByAuthor(defaultArtistUsername) .ifPresent(votePaper -> defaultVotePaper = votePaper); defaultVoteChoices = votePersistenceAdapter.findAllByVotePaperId(defaultVotePaper.getId()); } + public void initVotePaperBatch() { + Long authorId = defaultArtistAccount.getId(); + String sql = "INSERT INTO vote_paper (author_id, created_at, delivery_at, updated_at, vote_end_at, vote_start_at, content,\n" + + " option, title, uuid, video_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + for (int idx = 0; idx < 1000; idx++) { + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(@NotNull PreparedStatement ps, int i) throws SQLException { + ps.setLong(1, authorId); + ps.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now())); + ps.setTimestamp(3, Timestamp.valueOf(LocalDateTime.now().plusDays(10))); + ps.setTimestamp(4, Timestamp.valueOf(LocalDateTime.now())); + ps.setTimestamp(5, Timestamp.valueOf(LocalDateTime.now().plusDays(3))); + ps.setTimestamp(6, Timestamp.valueOf(LocalDateTime.now())); + ps.setString(7, "test"); + ps.setString(8, "DENY_ADD_CHOICES"); + ps.setString(9, "First Vote Paper"); + ps.setString(10, StringUtil.uuid()); + ps.setString(11, "test"); + } + + @Override + public int getBatchSize() { + return 100; + } + }); + } + } + + public void registerVote() { + User user = new User(defaultUsername, defaultPassword, defaultAccount.getAuthorities()); + registerVoteUseCase.register(defaultVotePaper.getId(), defaultVoteChoices.get(0).getId(), user); + } + public void expireOtp(OtpType otpType) { otpPersistenceAdapter.expire(otpType.getLabel(), defaultUsername); } diff --git a/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VoteControllerIT.java b/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VoteControllerIT.java index fc7cb6f6..a4377e8c 100644 --- a/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VoteControllerIT.java +++ b/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VoteControllerIT.java @@ -11,13 +11,21 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.restdocs.request.ParameterDescriptor; import java.util.Optional; +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static com.epages.restdocs.apispec.ResourceSnippetParameters.builder; +import static com.tune_fun.v1.base.doc.RestDocsConfig.constraint; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.http.HttpHeaders.AUTHORIZATION; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; @Slf4j class VoteControllerIT extends ControllerBaseTest { @@ -30,7 +38,7 @@ class VoteControllerIT extends ControllerBaseTest { @Test @Order(1) - @DisplayName("투표, 성공") + @DisplayName("투표 등록, 성공") void registerVoteSuccess() throws Exception { dummyService.initArtistAndLogin(); dummyService.initVotePaper(); @@ -41,12 +49,31 @@ void registerVoteSuccess() throws Exception { Long votePaperId = dummyService.getDefaultVotePaper().getId(); Long voteChoiceId = dummyService.getDefaultVoteChoices().getFirst().getId(); + ParameterDescriptor[] requestDescriptors = { + parameterWithName("votePaperId").description("투표 게시물 ID").attributes(constraint("NOT NULL")), + parameterWithName("voteChoiceId").description("투표 선택지 ID").attributes(constraint("NOT NULL")) + }; + mockMvc.perform( post(Uris.REGISTER_VOTE, votePaperId, voteChoiceId) .header(AUTHORIZATION, bearerToken(accessToken)) .contentType(APPLICATION_JSON_VALUE) ) - .andExpectAll(baseAssertion(MessageCode.SUCCESS)); + .andExpectAll(baseAssertion(MessageCode.SUCCESS)) + .andDo( + restDocs.document( + requestHeaders(authorizationHeader), + pathParameters(requestDescriptors), + responseFields(baseResponseFields), + resource( + builder(). + description("투표 등록"). + pathParameters(requestDescriptors). + responseFields(baseResponseFields) + .build() + ) + ) + ); Optional registeredVote = loadVotePort.loadVoteByVoterAndVotePaperId(dummyService.getDefaultUsername(), votePaperId); diff --git a/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java b/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java index 7aa4dbaf..91abdd78 100644 --- a/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java +++ b/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java @@ -11,9 +11,11 @@ import com.tune_fun.v1.vote.application.port.output.LoadVoteChoicePort; import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort; import com.tune_fun.v1.vote.domain.event.VotePaperRegisterEvent; +import com.tune_fun.v1.vote.domain.event.VotePaperUpdateDeliveryDateEvent; import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; import org.awaitility.core.ThrowingRunnable; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Order; @@ -22,7 +24,9 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.request.ParameterDescriptor; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.transaction.annotation.Transactional; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; @@ -35,19 +39,22 @@ import static com.epages.restdocs.apispec.ResourceDocumentation.resource; import static com.epages.restdocs.apispec.ResourceSnippetParameters.builder; import static com.tune_fun.v1.base.doc.RestDocsConfig.constraint; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.time.LocalDateTime.now; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.oneOf; +import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; import static org.springframework.http.HttpHeaders.AUTHORIZATION; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @Slf4j @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) @@ -71,13 +78,83 @@ class VotePaperControllerIT extends ControllerBaseTest { @SpyBean private VoteMessageConsumer voteMessageConsumer; + @Transactional @Test @Order(1) + @DisplayName("투표 게시물 스크롤 조회, 성공") + void getVotePapersSuccess() throws Exception { + dummyService.initArtistAndLogin(); + dummyService.initVotePaperBatch(); + + dummyService.initAndLogin(); + String accessToken = dummyService.getDefaultAccessToken(); + + + ParameterDescriptor[] requestDescriptors = { + parameterWithName("last_id").description("이전 페이지의 마지막 투표 게시물 ID").attributes(constraint("NOT NULL")), + parameterWithName("sort_type").description("정렬 방식").attributes(constraint("RECENT or VOTE_COUNT")) + }; + + FieldDescriptor[] responseDescriptors = ArrayUtils.addAll(baseResponseFields, + fieldWithPath("data.empty").description("데이터가 비어있는지 여부").attributes(constraint("NOT NULL")), + fieldWithPath("data.last").description("마지막 페이지인지 여부").attributes(constraint("NOT NULL")), + fieldWithPath("data.content").description("투표 게시물 목록").attributes(constraint("NOT EMPTY")), + fieldWithPath("data.content[].id").description("투표 게시물 ID").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].uuid").description("투표 게시물 UUID").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].title").description("투표 게시물 제목").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].author_username").description("투표 게시물 작성자 아이디").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].author_profile_image_url").description("투표 게시물 작성자 프로필 이미지 URL").attributes(constraint("NULL or NOT NULL")), + fieldWithPath("data.content[].author_nickname").description("투표 게시물 작성자 닉네임").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].remain_days").description("투표 게시물 남은 일수").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].total_vote_count").description("투표 게시물 총 투표 수").attributes(constraint("NOT NULL")), + fieldWithPath("data.content[].total_like_count").description("투표 게시물 총 좋아요 수").attributes(constraint("NOT NULL")) + ); + + mockMvc.perform( + get(Uris.VOTE_PAPER_ROOT) + .queryParam("last_id", "1000") + .queryParam("sort_type", "RECENT") + .header(AUTHORIZATION, bearerToken(accessToken)) + ) + .andExpectAll(baseAssertion(MessageCode.SUCCESS)) + .andExpectAll(jsonPath("$.data.empty", notNullValue()), jsonPath("$.data.empty", is(false))) + .andExpectAll(jsonPath("$.data.last", notNullValue()), jsonPath("$.data.last", is(false))) + .andExpect(jsonPath("$.data.content", hasSize(10))) + .andExpectAll(jsonPath("$.data.content[*].id").exists(), jsonPath("$.data.content[*].id", notNullValue())) + .andExpectAll(jsonPath("$.data.content[*].uuid").exists(), jsonPath("$.data.content[*].uuid", notNullValue())) + .andExpectAll(jsonPath("$.data.content[*].title").exists(), jsonPath("$.data.content[*].title", notNullValue())) + .andExpectAll(jsonPath("$.data.content[*].author_username").exists(), jsonPath("$.data.content[*].author_username", notNullValue())) + .andExpect(jsonPath("$.data.content[*].author_profile_image_url").exists()) + .andExpectAll(jsonPath("$.data.content[*].author_nickname").exists(), jsonPath("$.data.content[*].author_nickname", notNullValue())) + .andExpectAll(jsonPath("$.data.content[*].remain_days").exists(), jsonPath("$.data.content[*].remain_days", notNullValue())) + .andExpectAll(jsonPath("$.data.content[*].total_vote_count").exists(), jsonPath("$.data.content[*].total_vote_count", notNullValue())) + .andExpectAll(jsonPath("$.data.content[*].total_like_count").exists(), jsonPath("$.data.content[*].total_like_count", notNullValue())) + .andExpect(jsonPath("$.data.content[0].id", is(999))) + .andDo( + restDocs.document( + requestHeaders(authorizationHeader), + queryParameters(requestDescriptors), + responseFields(responseDescriptors), + resource( + builder(). + description("투표 게시물 스크롤 조회"). + queryParameters(requestDescriptors). + responseFields(responseDescriptors) + .build() + ) + ) + ); + + } + + @Transactional + @Test + @Order(2) @DisplayName("투표 게시물 등록, 성공") void registerVotePaperSuccess() throws Exception { - dummyService.initArtistAccount(); + dummyService.initAndLogin(); + dummyService.initArtistAndLogin(); - dummyService.login(dummyService.getDefaultArtistAccount()); String accessToken = dummyService.getDefaultArtistAccessToken(); Set offers = Set.of( @@ -110,7 +187,7 @@ void registerVotePaperSuccess() throws Exception { }; mockMvc.perform( - post(Uris.REGISTER_VOTE_PAPER) + post(Uris.VOTE_PAPER_ROOT) .header(AUTHORIZATION, bearerToken(accessToken)) .content(toJson(command)) .contentType(APPLICATION_JSON_VALUE) @@ -120,10 +197,12 @@ void registerVotePaperSuccess() throws Exception { restDocs.document( requestHeaders(authorizationHeader), requestFields(requestDescriptors), + responseFields(baseResponseFields), resource( builder(). description("투표 게시물 등록"). - requestFields(requestDescriptors) + requestFields(requestDescriptors). + responseFields(baseResponseFields) .build() ) ) @@ -134,7 +213,7 @@ void registerVotePaperSuccess() throws Exception { .thenApply(GetQueueUrlResponse::queueUrl) .thenCompose(queueUrl -> sqsAsyncClient.receiveMessage(getReceiveMessageRequest(queueUrl))); await().untilAsserted(receiveMessageAssertionRunnable); - verify(voteMessageConsumer, times(1)).consumeVotePaperUploadEvent(any(VotePaperRegisterEvent.class)); + verify(voteMessageConsumer).consumeVotePaperUploadEvent(any(VotePaperRegisterEvent.class)); // TODO : GitHub Actions 에서는 테스트 실패함. 원인 파악 필요 // verify(firebaseMessagingMediator).sendMulticastMessageByTokens(any()); @@ -174,4 +253,59 @@ private static ReceiveMessageRequest getReceiveMessageRequest(String queueUrl) { .build(); } + @Transactional + @Test + @Order(3) + @DisplayName("투표 게시물 영상 제공일 등록, 성공") + void updateDeliveryDateSuccess() throws Exception { + dummyService.initAndLogin(); + dummyService.initArtistAndLogin(); + + dummyService.initVotePaper(); + dummyService.registerVote(); + + ParameterDescriptor pathParameter = parameterWithName("votePaperId").description("투표 게시물 ID").attributes(constraint("NOT NULL")); + FieldDescriptor requestDescriptor = fieldWithPath("delivery_at").description("투표 게시물 영상 제공일").attributes(constraint("NOT NULL & FUTURE")); + + String accessToken = dummyService.getDefaultArtistAccessToken(); + + Long votePaperId = dummyService.getDefaultVotePaper().getId(); + VotePaperCommands.UpdateDeliveryDate command = new VotePaperCommands.UpdateDeliveryDate(now().plusDays(5)); + + doNothing().when(firebaseMessagingMediator).sendMulticastMessageByTokens(any()); + + mockMvc.perform( + patch(Uris.UPDATE_VOTE_PAPER_DELIVERY_DATE, votePaperId) + .header(AUTHORIZATION, bearerToken(accessToken)) + .content(toJson(command)) + .contentType(APPLICATION_JSON_VALUE) + .characterEncoding(UTF_8) + ) + .andExpectAll(baseAssertion(MessageCode.SUCCESS)) + .andDo( + restDocs.document( + requestHeaders(authorizationHeader), + pathParameters(pathParameter), + requestFields(requestDescriptor), + responseFields(baseResponseFields), + resource( + builder(). + description("투표 게시물 영상 제공일 등록"). + pathParameters(pathParameter). + requestFields(requestDescriptor). + responseFields(baseResponseFields) + .build() + ) + ) + ); + + ThrowingRunnable receiveMessageAssertionRunnable = () -> + sqsAsyncClient.getQueueUrl(r -> r.queueName("send-vote-paper-update-delivery-date-notification-dev")) + .thenApply(GetQueueUrlResponse::queueUrl) + .thenCompose(queueUrl -> sqsAsyncClient.receiveMessage(getReceiveMessageRequest(queueUrl))); + await().untilAsserted(receiveMessageAssertionRunnable); + verify(voteMessageConsumer).consumeVotePaperUpdateDeliveryDateEvent(any(VotePaperUpdateDeliveryDateEvent.class)); + + } + } diff --git a/src/test/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapterTest.java b/src/test/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapterTest.java index 73c856fa..e5b9ea68 100644 --- a/src/test/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapterTest.java +++ b/src/test/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePersistenceAdapterTest.java @@ -1,19 +1,19 @@ package com.tune_fun.v1.vote.adapter.output.persistence; import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity; +import com.tune_fun.v1.account.adapter.output.persistence.AccountMapperImpl; import com.tune_fun.v1.account.adapter.output.persistence.AccountPersistenceAdapter; +import com.tune_fun.v1.account.adapter.output.persistence.AccountRepository; +import com.tune_fun.v1.account.adapter.output.persistence.oauth2.OAuth2AccountRepository; +import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice; import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper; import com.tune_fun.v1.vote.domain.value.RegisteredVote; import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.aot.DisabledInAotMode; -import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.Sort; import java.time.LocalDate; import java.time.LocalDateTime; @@ -21,36 +21,10 @@ import java.util.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -@ContextConfiguration(classes = {VotePersistenceAdapter.class}) -@ExtendWith(SpringExtension.class) -@DisabledInAotMode class VotePersistenceAdapterTest { - - @MockBean - private AccountPersistenceAdapter accountPersistenceAdapter; - - @MockBean - private VoteChoiceMapper voteChoiceMapper; - - @MockBean - private VoteChoiceRepository voteChoiceRepository; - - @MockBean - private VotePaperMapper votePaperMapper; - - @MockBean - private VotePaperRepository votePaperRepository; - - @Autowired - private VotePersistenceAdapter votePersistenceAdapter; - - @MockBean - private VoteRepository voteRepository; - /** * Method under test: * {@link VotePersistenceAdapter#loadVoterIdsByVotePaperUuid(String)} @@ -58,11 +32,21 @@ class VotePersistenceAdapterTest { @Test void testLoadVoterIdsByVotePaperUuid() { // Arrange + VoteRepository voteRepository = mock(VoteRepository.class); ArrayList resultLongList = new ArrayList<>(); when(voteRepository.findVoterIdsByVotePaperUuid(Mockito.any())).thenReturn(resultLongList); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - List actualLoadVoterIdsByVotePaperUuidResult = votePersistenceAdapter + List actualLoadVoterIdsByVotePaperUuidResult = (new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())) .loadVoterIdsByVotePaperUuid("01234567-89AB-CDEF-FEDC-BA9876543210"); // Assert @@ -78,12 +62,23 @@ void testLoadVoterIdsByVotePaperUuid() { @Test void testLoadVoterIdsByVotePaperUuid2() { // Arrange + VoteRepository voteRepository = mock(VoteRepository.class); when(voteRepository.findVoterIdsByVotePaperUuid(Mockito.any())) .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert assertThrows(IllegalArgumentException.class, - () -> votePersistenceAdapter.loadVoterIdsByVotePaperUuid("01234567-89AB-CDEF-FEDC-BA9876543210")); + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())) + .loadVoterIdsByVotePaperUuid("01234567-89AB-CDEF-FEDC-BA9876543210")); verify(voteRepository).findVoterIdsByVotePaperUuid(eq("01234567-89AB-CDEF-FEDC-BA9876543210")); } @@ -94,6 +89,7 @@ void testLoadVoterIdsByVotePaperUuid2() { @Test void testLoadVoteByVoterAndVotePaperId() { // Arrange + VoteRepository voteRepository = mock(VoteRepository.class); RegisteredVote buildResult = RegisteredVote.builder() .artistName("Artist Name") .id(1L) @@ -105,10 +101,19 @@ void testLoadVoteByVoterAndVotePaperId() { Optional ofResult = Optional.of(buildResult); when(voteRepository.findByVoterUsernameAndVotePaperId(Mockito.any(), Mockito.any())) .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - Optional actualLoadVoteByVoterAndVotePaperIdResult = votePersistenceAdapter - .loadVoteByVoterAndVotePaperId("Voter", 1L); + Optional actualLoadVoteByVoterAndVotePaperIdResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadVoteByVoterAndVotePaperId("Voter", 1L); // Assert verify(voteRepository).findByVoterUsernameAndVotePaperId(eq("Voter"), eq(1L)); @@ -122,12 +127,23 @@ void testLoadVoteByVoterAndVotePaperId() { @Test void testLoadVoteByVoterAndVotePaperId2() { // Arrange + VoteRepository voteRepository = mock(VoteRepository.class); when(voteRepository.findByVoterUsernameAndVotePaperId(Mockito.any(), Mockito.any())) .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert assertThrows(IllegalArgumentException.class, - () -> votePersistenceAdapter.loadVoteByVoterAndVotePaperId("Voter", 1L)); + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).loadVoteByVoterAndVotePaperId("Voter", + 1L)); verify(voteRepository).findByVoterUsernameAndVotePaperId(eq("Voter"), eq(1L)); } @@ -137,17 +153,28 @@ void testLoadVoteByVoterAndVotePaperId2() { @Test void testSaveVote() { // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); Optional ofResult = Optional.of(new AccountJpaEntity()); - when(accountPersistenceAdapter.loadAccountByUsername(Mockito.any())).thenReturn(ofResult); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); when(voteRepository.save(Mockito.any())).thenReturn(new VoteJpaEntity()); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); Optional ofResult2 = Optional.of(new VoteChoiceJpaEntity()); when(voteChoiceRepository.findById(Mockito.any())).thenReturn(ofResult2); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - votePersistenceAdapter.saveVote(1L, "janedoe"); + (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, + votePaperMapper, new VoteChoiceMapperImpl())).saveVote(1L, "janedoe"); // Assert - verify(accountPersistenceAdapter).loadAccountByUsername(eq("janedoe")); + verify(accountRepository).findActive(eq("janedoe"), isNull(), isNull()); verify(voteChoiceRepository).findById(eq(1L)); verify(voteRepository).save(isA(VoteJpaEntity.class)); } @@ -158,15 +185,27 @@ void testSaveVote() { @Test void testSaveVote2() { // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); Optional ofResult = Optional.of(new AccountJpaEntity()); - when(accountPersistenceAdapter.loadAccountByUsername(Mockito.any())).thenReturn(ofResult); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); when(voteRepository.save(Mockito.any())).thenThrow(new IllegalArgumentException("foo")); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); Optional ofResult2 = Optional.of(new VoteChoiceJpaEntity()); when(voteChoiceRepository.findById(Mockito.any())).thenReturn(ofResult2); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVote(1L, "janedoe")); - verify(accountPersistenceAdapter).loadAccountByUsername(eq("janedoe")); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).saveVote(1L, "janedoe")); + verify(accountRepository).findActive(eq("janedoe"), isNull(), isNull()); verify(voteChoiceRepository).findById(eq(1L)); verify(voteRepository).save(isA(VoteJpaEntity.class)); } @@ -177,14 +216,26 @@ void testSaveVote2() { @Test void testSaveVote3() { // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); Optional emptyResult = Optional.empty(); - when(accountPersistenceAdapter.loadAccountByUsername(Mockito.any())).thenReturn(emptyResult); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(emptyResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); Optional ofResult = Optional.of(new VoteChoiceJpaEntity()); when(voteChoiceRepository.findById(Mockito.any())).thenReturn(ofResult); + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVote(1L, "janedoe")); - verify(accountPersistenceAdapter).loadAccountByUsername(eq("janedoe")); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).saveVote(1L, "janedoe")); + verify(accountRepository).findActive(eq("janedoe"), isNull(), isNull()); verify(voteChoiceRepository).findById(eq(1L)); } @@ -194,44 +245,323 @@ void testSaveVote3() { @Test void testSaveVote4() { // Arrange + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); Optional emptyResult = Optional.empty(); when(voteChoiceRepository.findById(Mockito.any())).thenReturn(emptyResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVote(1L, "janedoe")); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).saveVote(1L, "janedoe")); verify(voteChoiceRepository).findById(eq(1L)); } /** * Method under test: - * {@link VotePersistenceAdapter#loadRegisteredVotePaper(String)} + * {@link VotePersistenceAdapter#scrollVotePaper(Integer, String)} + */ + @Test + void testScrollVotePaper() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.findFirst10By(Mockito.any(), Mockito.any())) + .thenThrow(new IllegalArgumentException("id")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act and Assert + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).scrollVotePaper(1, "Sort Type")); + verify(votePaperRepository).findFirst10By(isA(KeysetScrollPosition.class), isA(Sort.class)); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(Long)} */ @Test void testLoadRegisteredVotePaper() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + Optional actualLoadRegisteredVotePaperResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVotePaper(1L); + + // Assert + verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + RegisteredVotePaper getResult = actualLoadRegisteredVotePaperResult.get(); + assertNull(getResult.id()); + assertNull(getResult.author()); + assertNull(getResult.content()); + assertNull(getResult.option()); + assertNull(getResult.title()); + assertNull(getResult.uuid()); + assertNull(getResult.createdAt()); + assertNull(getResult.deliveryAt()); + assertNull(getResult.updatedAt()); + assertNull(getResult.voteEndAt()); + assertNull(getResult.voteStartAt()); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(Long)} + */ + @Test + void testLoadRegisteredVotePaper2() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + AccountJpaEntity author = new AccountJpaEntity(); + LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + LocalDateTime voteEndAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + Optional ofResult = Optional + .of(new VotePaperJpaEntity(1L, "01234567-89AB-CDEF-FEDC-BA9876543210", "Dr", "Not all who wander are lost", + author, VotePaperOption.ALLOW_ADD_CHOICES, voteStartAt, voteEndAt, LocalDate.of(1970, 1, 1).atStartOfDay(), + "https://example.org/example")); + when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + Optional actualLoadRegisteredVotePaperResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVotePaper(1L); + + // Assert + verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + RegisteredVotePaper getResult = actualLoadRegisteredVotePaperResult.get(); + LocalTime expectedToLocalTimeResult = getResult.voteStartAt().toLocalTime(); + assertSame(expectedToLocalTimeResult, getResult.voteEndAt().toLocalTime()); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(Long)} + */ + @Test + void testLoadRegisteredVotePaper3() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional emptyResult = Optional.empty(); + when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + .thenReturn(emptyResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + Optional actualLoadRegisteredVotePaperResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVotePaper(1L); + + // Assert + verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + assertFalse(actualLoadRegisteredVotePaperResult.isPresent()); + assertSame(emptyResult, actualLoadRegisteredVotePaperResult); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(Long)} + */ + @Test + void testLoadRegisteredVotePaper4() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act and Assert + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).loadRegisteredVotePaper(1L)); + verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(String)} + */ + @Test + void testLoadRegisteredVotePaper5() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional ofResult = Optional.of(new VotePaperJpaEntity()); when(votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(Mockito.any(), Mockito.any())) .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + Optional actualLoadRegisteredVotePaperResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVotePaper("janedoe"); + + // Assert + verify(votePaperRepository).findByVoteEndAtAfterAndAuthorUsername(isA(LocalDateTime.class), eq("janedoe")); + RegisteredVotePaper getResult = actualLoadRegisteredVotePaperResult.get(); + assertNull(getResult.id()); + assertNull(getResult.author()); + assertNull(getResult.content()); + assertNull(getResult.option()); + assertNull(getResult.title()); + assertNull(getResult.uuid()); + assertNull(getResult.createdAt()); + assertNull(getResult.deliveryAt()); + assertNull(getResult.updatedAt()); + assertNull(getResult.voteEndAt()); + assertNull(getResult.voteStartAt()); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(String)} + */ + @Test + void testLoadRegisteredVotePaper6() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + AccountJpaEntity author = new AccountJpaEntity(); LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); LocalDateTime voteEndAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - LocalDateTime deliveryAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - LocalDateTime createdAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - when(votePaperMapper.registeredVotePaper(Mockito.any())).thenReturn(new RegisteredVotePaper(1L, - "01234567-89AB-CDEF-FEDC-BA9876543210", "JaneDoe", "Dr", "Not all who wander are lost", "Option", voteStartAt, - voteEndAt, deliveryAt, createdAt, LocalDate.of(1970, 1, 1).atStartOfDay())); + Optional ofResult = Optional + .of(new VotePaperJpaEntity(1L, "01234567-89AB-CDEF-FEDC-BA9876543210", "Dr", "Not all who wander are lost", + author, VotePaperOption.ALLOW_ADD_CHOICES, voteStartAt, voteEndAt, LocalDate.of(1970, 1, 1).atStartOfDay(), + "https://example.org/example")); + when(votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - Optional actualLoadRegisteredVotePaperResult = votePersistenceAdapter - .loadRegisteredVotePaper("janedoe"); + Optional actualLoadRegisteredVotePaperResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVotePaper("janedoe"); // Assert - verify(votePaperMapper).registeredVotePaper(isA(VotePaperJpaEntity.class)); verify(votePaperRepository).findByVoteEndAtAfterAndAuthorUsername(isA(LocalDateTime.class), eq("janedoe")); RegisteredVotePaper getResult = actualLoadRegisteredVotePaperResult.get(); LocalTime expectedToLocalTimeResult = getResult.voteStartAt().toLocalTime(); assertSame(expectedToLocalTimeResult, getResult.voteEndAt().toLocalTime()); } + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(String)} + */ + @Test + void testLoadRegisteredVotePaper7() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional emptyResult = Optional.empty(); + when(votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(Mockito.any(), Mockito.any())) + .thenReturn(emptyResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + Optional actualLoadRegisteredVotePaperResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVotePaper("janedoe"); + + // Assert + verify(votePaperRepository).findByVoteEndAtAfterAndAuthorUsername(isA(LocalDateTime.class), eq("janedoe")); + assertFalse(actualLoadRegisteredVotePaperResult.isPresent()); + assertSame(emptyResult, actualLoadRegisteredVotePaperResult); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVotePaper(String)} + */ + @Test + void testLoadRegisteredVotePaper8() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(Mockito.any(), Mockito.any())) + .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act and Assert + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).loadRegisteredVotePaper("janedoe")); + verify(votePaperRepository).findByVoteEndAtAfterAndAuthorUsername(isA(LocalDateTime.class), eq("janedoe")); + } + /** * Method under test: * {@link VotePersistenceAdapter#saveVotePaper(SaveVotePaper)} @@ -239,31 +569,71 @@ void testLoadRegisteredVotePaper() { @Test void testSaveVotePaper() { // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); + Optional emptyResult = Optional.empty(); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(emptyResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); + LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + + // Act and Assert + assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVotePaper(new SaveVotePaper("Dr", + "Not all who wander are lost", "JaneDoe", "Option", voteStartAt, LocalDate.of(1970, 1, 1).atStartOfDay()))); + verify(accountRepository).findActive(eq("JaneDoe"), isNull(), isNull()); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#saveVotePaper(SaveVotePaper)} + */ + @Test + void testSaveVotePaper2() { + // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); Optional ofResult = Optional.of(new AccountJpaEntity()); - when(accountPersistenceAdapter.loadAccountByUsername(Mockito.any())).thenReturn(ofResult); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); when(votePaperRepository.save(Mockito.any())).thenReturn(new VotePaperJpaEntity()); - when(votePaperMapper.fromSaveVotePaperBehavior(Mockito.any(), Mockito.any())) - .thenReturn(new VotePaperJpaEntity()); + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - LocalDateTime voteEndAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - LocalDateTime deliveryAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - LocalDateTime createdAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - when(votePaperMapper.registeredVotePaper(Mockito.any())).thenReturn(new RegisteredVotePaper(1L, - "01234567-89AB-CDEF-FEDC-BA9876543210", "JaneDoe", "Dr", "Not all who wander are lost", "Option", voteStartAt, - voteEndAt, deliveryAt, createdAt, LocalDate.of(1970, 1, 1).atStartOfDay())); - LocalDateTime voteStartAt2 = LocalDate.of(1970, 1, 1).atStartOfDay(); // Act - RegisteredVotePaper actualSaveVotePaperResult = votePersistenceAdapter.saveVotePaper(new SaveVotePaper("Dr", - "Not all who wander are lost", "JaneDoe", "Option", voteStartAt2, LocalDate.of(1970, 1, 1).atStartOfDay())); + RegisteredVotePaper actualSaveVotePaperResult = votePersistenceAdapter + .saveVotePaper(new SaveVotePaper("Dr", "Not all who wander are lost", "JaneDoe", "allow-add-choices", + voteStartAt, LocalDate.of(1970, 1, 1).atStartOfDay())); // Assert - verify(accountPersistenceAdapter).loadAccountByUsername(eq("JaneDoe")); - verify(votePaperMapper).fromSaveVotePaperBehavior(isA(SaveVotePaper.class), isA(AccountJpaEntity.class)); - verify(votePaperMapper).registeredVotePaper(isA(VotePaperJpaEntity.class)); + verify(accountRepository).findActive(eq("JaneDoe"), isNull(), isNull()); verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); - LocalTime expectedToLocalTimeResult = actualSaveVotePaperResult.voteStartAt().toLocalTime(); - assertSame(expectedToLocalTimeResult, actualSaveVotePaperResult.voteEndAt().toLocalTime()); + assertNull(actualSaveVotePaperResult.id()); + assertNull(actualSaveVotePaperResult.author()); + assertNull(actualSaveVotePaperResult.content()); + assertNull(actualSaveVotePaperResult.option()); + assertNull(actualSaveVotePaperResult.title()); + assertNull(actualSaveVotePaperResult.uuid()); + assertNull(actualSaveVotePaperResult.createdAt()); + assertNull(actualSaveVotePaperResult.deliveryAt()); + assertNull(actualSaveVotePaperResult.updatedAt()); + assertNull(actualSaveVotePaperResult.voteEndAt()); + assertNull(actualSaveVotePaperResult.voteStartAt()); } /** @@ -271,19 +641,34 @@ void testSaveVotePaper() { * {@link VotePersistenceAdapter#saveVotePaper(SaveVotePaper)} */ @Test - void testSaveVotePaper2() { + void testSaveVotePaper3() { // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); Optional ofResult = Optional.of(new AccountJpaEntity()); - when(accountPersistenceAdapter.loadAccountByUsername(Mockito.any())).thenReturn(ofResult); - when(votePaperMapper.fromSaveVotePaperBehavior(Mockito.any(), Mockito.any())) - .thenThrow(new IllegalArgumentException("foo")); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.save(Mockito.any())).thenReturn(null); + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); - // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVotePaper(new SaveVotePaper("Dr", - "Not all who wander are lost", "JaneDoe", "Option", voteStartAt, LocalDate.of(1970, 1, 1).atStartOfDay()))); - verify(accountPersistenceAdapter).loadAccountByUsername(eq("JaneDoe")); - verify(votePaperMapper).fromSaveVotePaperBehavior(isA(SaveVotePaper.class), isA(AccountJpaEntity.class)); + // Act + RegisteredVotePaper actualSaveVotePaperResult = votePersistenceAdapter + .saveVotePaper(new SaveVotePaper("Dr", "Not all who wander are lost", "JaneDoe", "allow-add-choices", + voteStartAt, LocalDate.of(1970, 1, 1).atStartOfDay())); + + // Assert + verify(accountRepository).findActive(eq("JaneDoe"), isNull(), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); + assertNull(actualSaveVotePaperResult); } /** @@ -291,16 +676,74 @@ void testSaveVotePaper2() { * {@link VotePersistenceAdapter#saveVotePaper(SaveVotePaper)} */ @Test - void testSaveVotePaper3() { + void testSaveVotePaper4() { // Arrange - Optional emptyResult = Optional.empty(); - when(accountPersistenceAdapter.loadAccountByUsername(Mockito.any())).thenReturn(emptyResult); + AccountRepository accountRepository = mock(AccountRepository.class); + Optional ofResult = Optional.of(new AccountJpaEntity()); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + AccountJpaEntity author = new AccountJpaEntity(); + LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + LocalDateTime voteEndAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + when(votePaperRepository.save(Mockito.any())) + .thenReturn(new VotePaperJpaEntity(1L, "01234567-89AB-CDEF-FEDC-BA9876543210", "Dr", + "Not all who wander are lost", author, VotePaperOption.ALLOW_ADD_CHOICES, voteStartAt, voteEndAt, + LocalDate.of(1970, 1, 1).atStartOfDay(), "https://example.org/example")); + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); + LocalDateTime voteStartAt2 = LocalDate.of(1970, 1, 1).atStartOfDay(); + + // Act + RegisteredVotePaper actualSaveVotePaperResult = votePersistenceAdapter + .saveVotePaper(new SaveVotePaper("Dr", "Not all who wander are lost", "JaneDoe", "allow-add-choices", + voteStartAt2, LocalDate.of(1970, 1, 1).atStartOfDay())); + + // Assert + verify(accountRepository).findActive(eq("JaneDoe"), isNull(), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); + LocalTime expectedToLocalTimeResult = actualSaveVotePaperResult.voteStartAt().toLocalTime(); + assertSame(expectedToLocalTimeResult, actualSaveVotePaperResult.voteEndAt().toLocalTime()); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#saveVotePaper(SaveVotePaper)} + */ + @Test + void testSaveVotePaper5() { + // Arrange + AccountRepository accountRepository = mock(AccountRepository.class); + Optional ofResult = Optional.of(new AccountJpaEntity()); + when(accountRepository.findActive(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.save(Mockito.any())) + .thenThrow(new IllegalArgumentException("allow-add-choices")); + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVotePaper(new SaveVotePaper("Dr", - "Not all who wander are lost", "JaneDoe", "Option", voteStartAt, LocalDate.of(1970, 1, 1).atStartOfDay()))); - verify(accountPersistenceAdapter).loadAccountByUsername(eq("JaneDoe")); + assertThrows(IllegalArgumentException.class, + () -> votePersistenceAdapter.saveVotePaper(new SaveVotePaper("Dr", "Not all who wander are lost", "JaneDoe", + "allow-add-choices", voteStartAt, LocalDate.of(1970, 1, 1).atStartOfDay()))); + verify(accountRepository).findActive(eq("JaneDoe"), isNull(), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); } /** @@ -310,19 +753,39 @@ void testSaveVotePaper3() { @Test void testUpdateDeliveryAt() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.save(Mockito.any())).thenReturn(new VotePaperJpaEntity()); Optional ofResult = Optional.of(new VotePaperJpaEntity()); - when(votePaperRepository.findByVoteEndAtBeforeAndId(Mockito.any(), Mockito.any())) - .thenReturn(ofResult); - Mockito.>when(votePaperMapper.updateDeliveryAt( - Mockito.any(), Mockito.any())) - .thenThrow(new IllegalArgumentException("foo")); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); - // Act and Assert - assertThrows(IllegalArgumentException.class, - () -> votePersistenceAdapter.updateDeliveryAt(1L, LocalDate.of(1970, 1, 1).atStartOfDay())); - verify(votePaperMapper).updateDeliveryAt(isA(LocalDateTime.class), - isA(VotePaperJpaEntity.VotePaperJpaEntityBuilder.class)); - verify(votePaperRepository).findByVoteEndAtBeforeAndId(isA(LocalDateTime.class), eq(1L)); + // Act + RegisteredVotePaper actualUpdateDeliveryAtResult = votePersistenceAdapter.updateDeliveryAt(1L, + LocalDate.of(1970, 1, 1).atStartOfDay()); + + // Assert + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); + assertNull(actualUpdateDeliveryAtResult.id()); + assertNull(actualUpdateDeliveryAtResult.author()); + assertNull(actualUpdateDeliveryAtResult.content()); + assertNull(actualUpdateDeliveryAtResult.option()); + assertNull(actualUpdateDeliveryAtResult.title()); + assertNull(actualUpdateDeliveryAtResult.uuid()); + assertNull(actualUpdateDeliveryAtResult.createdAt()); + assertNull(actualUpdateDeliveryAtResult.deliveryAt()); + assertNull(actualUpdateDeliveryAtResult.updatedAt()); + assertNull(actualUpdateDeliveryAtResult.voteEndAt()); + assertNull(actualUpdateDeliveryAtResult.voteStartAt()); } /** @@ -332,14 +795,26 @@ void testUpdateDeliveryAt() { @Test void testUpdateDeliveryAt2() { // Arrange - Optional emptyResult = Optional.empty(); - when(votePaperRepository.findByVoteEndAtBeforeAndId(Mockito.any(), Mockito.any())) - .thenReturn(emptyResult); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.save(Mockito.any())).thenThrow(new IllegalArgumentException("foo")); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); // Act and Assert assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.updateDeliveryAt(1L, LocalDate.of(1970, 1, 1).atStartOfDay())); - verify(votePaperRepository).findByVoteEndAtBeforeAndId(isA(LocalDateTime.class), eq(1L)); + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); } /** @@ -349,18 +824,95 @@ void testUpdateDeliveryAt2() { @Test void testUpdateDeliveryAt3() { // Arrange - VotePaperJpaEntity votePaperJpaEntity = mock(VotePaperJpaEntity.class); - Mockito.>when(votePaperJpaEntity.toBuilder()) - .thenThrow(new IllegalArgumentException("foo")); - Optional ofResult = Optional.of(votePaperJpaEntity); - when(votePaperRepository.findByVoteEndAtBeforeAndId(Mockito.any(), Mockito.any())) - .thenReturn(ofResult); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.save(Mockito.any())).thenReturn(null); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); + + // Act + RegisteredVotePaper actualUpdateDeliveryAtResult = votePersistenceAdapter.updateDeliveryAt(1L, + LocalDate.of(1970, 1, 1).atStartOfDay()); + + // Assert + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); + assertNull(actualUpdateDeliveryAtResult); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#updateDeliveryAt(Long, LocalDateTime)} + */ + @Test + void testUpdateDeliveryAt4() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + AccountJpaEntity author = new AccountJpaEntity(); + LocalDateTime voteStartAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + LocalDateTime voteEndAt = LocalDate.of(1970, 1, 1).atStartOfDay(); + when(votePaperRepository.save(Mockito.any())) + .thenReturn(new VotePaperJpaEntity(1L, "01234567-89AB-CDEF-FEDC-BA9876543210", "Dr", + "Not all who wander are lost", author, VotePaperOption.ALLOW_ADD_CHOICES, voteStartAt, voteEndAt, + LocalDate.of(1970, 1, 1).atStartOfDay(), "https://example.org/example")); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); + + // Act + RegisteredVotePaper actualUpdateDeliveryAtResult = votePersistenceAdapter.updateDeliveryAt(1L, + LocalDate.of(1970, 1, 1).atStartOfDay()); + + // Assert + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(votePaperRepository).save(isA(VotePaperJpaEntity.class)); + LocalTime expectedToLocalTimeResult = actualUpdateDeliveryAtResult.voteStartAt().toLocalTime(); + assertSame(expectedToLocalTimeResult, actualUpdateDeliveryAtResult.voteEndAt().toLocalTime()); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#updateDeliveryAt(Long, LocalDateTime)} + */ + @Test + void testUpdateDeliveryAt5() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional emptyResult = Optional.empty(); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(emptyResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); // Act and Assert assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.updateDeliveryAt(1L, LocalDate.of(1970, 1, 1).atStartOfDay())); - verify(votePaperJpaEntity).toBuilder(); - verify(votePaperRepository).findByVoteEndAtBeforeAndId(isA(LocalDateTime.class), eq(1L)); + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); } /** @@ -370,20 +922,25 @@ void testUpdateDeliveryAt3() { @Test void testLoadRegisteredVoteChoice() { // Arrange + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); when(voteChoiceRepository.findAllByVotePaperId(Mockito.any())).thenReturn(new ArrayList<>()); - ArrayList registeredVoteChoiceList = new ArrayList<>(); - when(voteChoiceMapper.registeredVoteChoices(Mockito.any())) - .thenReturn(registeredVoteChoiceList); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - List actualLoadRegisteredVoteChoiceResult = votePersistenceAdapter - .loadRegisteredVoteChoice(1L); + List actualLoadRegisteredVoteChoiceResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVoteChoice(1L); // Assert - verify(voteChoiceMapper).registeredVoteChoices(isA(List.class)); verify(voteChoiceRepository).findAllByVotePaperId(eq(1L)); assertTrue(actualLoadRegisteredVoteChoiceResult.isEmpty()); - assertSame(registeredVoteChoiceList, actualLoadRegisteredVoteChoiceResult); } /** @@ -393,14 +950,54 @@ void testLoadRegisteredVoteChoice() { @Test void testLoadRegisteredVoteChoice2() { // Arrange - when(voteChoiceRepository.findAllByVotePaperId(Mockito.any())).thenReturn(new ArrayList<>()); - when(voteChoiceMapper.registeredVoteChoices(Mockito.any())) - .thenThrow(new IllegalArgumentException("foo")); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + when(voteChoiceRepository.findAllByVotePaperId(Mockito.any())).thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.loadRegisteredVoteChoice(1L)); - verify(voteChoiceMapper).registeredVoteChoices(isA(List.class)); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).loadRegisteredVoteChoice(1L)); + verify(voteChoiceRepository).findAllByVotePaperId(eq(1L)); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#loadRegisteredVoteChoice(Long)} + */ + @Test + void testLoadRegisteredVoteChoice3() { + // Arrange + ArrayList voteChoiceJpaEntityList = new ArrayList<>(); + VotePaperJpaEntity votePaper = new VotePaperJpaEntity(); + voteChoiceJpaEntityList.add(new VoteChoiceJpaEntity(1L, "01234567-89AB-CDEF-FEDC-BA9876543210", votePaper, + new Offer(",", ",", ",", "2020-03-01", 1))); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + when(voteChoiceRepository.findAllByVotePaperId(Mockito.any())).thenReturn(voteChoiceJpaEntityList); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + List actualLoadRegisteredVoteChoiceResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).loadRegisteredVoteChoice(1L); + + // Assert verify(voteChoiceRepository).findAllByVotePaperId(eq(1L)); + assertEquals(1, actualLoadRegisteredVoteChoiceResult.size()); } /** @@ -409,18 +1006,26 @@ void testLoadRegisteredVoteChoice2() { @Test void testSaveVoteChoice() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional ofResult = Optional.of(new VotePaperJpaEntity()); - when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) - .thenReturn(ofResult); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); when(voteChoiceRepository.saveAll(Mockito.any())).thenReturn(new ArrayList<>()); - when(voteChoiceMapper.fromSaveVoteChoiceBehaviors(Mockito.any())).thenReturn(new HashSet<>()); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); // Act votePersistenceAdapter.saveVoteChoice(1L, new HashSet<>()); // Assert - verify(voteChoiceMapper).fromSaveVoteChoiceBehaviors(isA(Set.class)); - verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); verify(voteChoiceRepository).saveAll(isA(Iterable.class)); } @@ -430,16 +1035,26 @@ void testSaveVoteChoice() { @Test void testSaveVoteChoice2() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional ofResult = Optional.of(new VotePaperJpaEntity()); - when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) - .thenReturn(ofResult); - when(voteChoiceMapper.fromSaveVoteChoiceBehaviors(Mockito.any())) + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + when(voteChoiceRepository.saveAll(Mockito.any())) .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); // Act and Assert assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVoteChoice(1L, new HashSet<>())); - verify(voteChoiceMapper).fromSaveVoteChoiceBehaviors(isA(Set.class)); - verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(voteChoiceRepository).saveAll(isA(Iterable.class)); } /** @@ -448,48 +1063,143 @@ void testSaveVoteChoice2() { @Test void testSaveVoteChoice3() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional emptyResult = Optional.empty(); - when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) - .thenReturn(emptyResult); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(emptyResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); // Act and Assert assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.saveVoteChoice(1L, new HashSet<>())); - verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + } + + /** + * Method under test: {@link VotePersistenceAdapter#saveVoteChoice(Long, Set)} + */ + @Test + void testSaveVoteChoice4() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + when(voteChoiceRepository.saveAll(Mockito.any())).thenReturn(new ArrayList<>()); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); + + HashSet behavior = new HashSet<>(); + behavior.add(new SaveVoteChoice("Music", "Offer Artist Name", new HashSet<>(), 2, "2020-03-01")); + + // Act + votePersistenceAdapter.saveVoteChoice(1L, behavior); + + // Assert + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(voteChoiceRepository).saveAll(isA(Iterable.class)); + } + + /** + * Method under test: {@link VotePersistenceAdapter#saveVoteChoice(Long, Set)} + */ + @Test + void testSaveVoteChoice5() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + when(voteChoiceRepository.saveAll(Mockito.any())).thenReturn(new ArrayList<>()); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + VotePersistenceAdapter votePersistenceAdapter = new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl()); + + HashSet behavior = new HashSet<>(); + behavior.add(new SaveVoteChoice(",", ",", new HashSet<>(), 2, "2020-03-01")); + behavior.add(new SaveVoteChoice("Music", "Offer Artist Name", new HashSet<>(), 2, "2020-03-01")); + + // Act + votePersistenceAdapter.saveVoteChoice(1L, behavior); + + // Assert + verify(votePaperRepository).findOneAvailable(eq(1L), isNull()); + verify(voteChoiceRepository).saveAll(isA(Iterable.class)); } /** * Method under test: - * {@link VotePersistenceAdapter#findProgressingVotePaperById(Long)} + * {@link VotePersistenceAdapter#findOneAvailable(Long, String)} */ @Test - void testFindProgressingVotePaperById() { + void testFindOneAvailable() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional ofResult = Optional.of(new VotePaperJpaEntity()); - when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) - .thenReturn(ofResult); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())).thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - Optional actualFindProgressingVotePaperByIdResult = votePersistenceAdapter - .findProgressingVotePaperById(1L); + Optional actualFindOneAvailableResult = (new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())) + .findOneAvailable(1L, "janedoe"); // Assert - verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); - assertSame(ofResult, actualFindProgressingVotePaperByIdResult); + verify(votePaperRepository).findOneAvailable(eq(1L), eq("janedoe")); + assertSame(ofResult, actualFindOneAvailableResult); } /** * Method under test: - * {@link VotePersistenceAdapter#findProgressingVotePaperById(Long)} + * {@link VotePersistenceAdapter#findOneAvailable(Long, String)} */ @Test - void testFindProgressingVotePaperById2() { + void testFindOneAvailable2() { // Arrange - when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.findOneAvailable(Mockito.any(), Mockito.any())) .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.findProgressingVotePaperById(1L)); - verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).findOneAvailable(1L, "janedoe")); + verify(votePaperRepository).findOneAvailable(eq(1L), eq("janedoe")); } /** @@ -499,13 +1209,23 @@ void testFindProgressingVotePaperById2() { @Test void testFindCompleteVotePaperById() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional ofResult = Optional.of(new VotePaperJpaEntity()); when(votePaperRepository.findByVoteEndAtBeforeAndId(Mockito.any(), Mockito.any())) .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - Optional actualFindCompleteVotePaperByIdResult = votePersistenceAdapter - .findCompleteVotePaperById(1L); + Optional actualFindCompleteVotePaperByIdResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).findCompleteVotePaperById(1L); // Assert verify(votePaperRepository).findByVoteEndAtBeforeAndId(isA(LocalDateTime.class), eq(1L)); @@ -519,61 +1239,160 @@ void testFindCompleteVotePaperById() { @Test void testFindCompleteVotePaperById2() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); when(votePaperRepository.findByVoteEndAtBeforeAndId(Mockito.any(), Mockito.any())) .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.findCompleteVotePaperById(1L)); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).findCompleteVotePaperById(1L)); verify(votePaperRepository).findByVoteEndAtBeforeAndId(isA(LocalDateTime.class), eq(1L)); } /** * Method under test: - * {@link VotePersistenceAdapter#findAvailableVotePaperByAuthor(String)} + * {@link VotePersistenceAdapter#findProgressingVotePaperByAuthor(String)} */ @Test - void testFindAvailableVotePaperByAuthor() { + void testFindProgressingVotePaperByAuthor() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); Optional ofResult = Optional.of(new VotePaperJpaEntity()); when(votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(Mockito.any(), Mockito.any())) .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - Optional actualFindAvailableVotePaperByAuthorResult = votePersistenceAdapter - .findAvailableVotePaperByAuthor("janedoe"); + Optional actualFindProgressingVotePaperByAuthorResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).findProgressingVotePaperByAuthor("janedoe"); // Assert verify(votePaperRepository).findByVoteEndAtAfterAndAuthorUsername(isA(LocalDateTime.class), eq("janedoe")); - assertSame(ofResult, actualFindAvailableVotePaperByAuthorResult); + assertSame(ofResult, actualFindProgressingVotePaperByAuthorResult); } /** * Method under test: - * {@link VotePersistenceAdapter#findAvailableVotePaperByAuthor(String)} + * {@link VotePersistenceAdapter#findProgressingVotePaperByAuthor(String)} */ @Test - void testFindAvailableVotePaperByAuthor2() { + void testFindProgressingVotePaperByAuthor2() { // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); when(votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(Mockito.any(), Mockito.any())) .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert assertThrows(IllegalArgumentException.class, - () -> votePersistenceAdapter.findAvailableVotePaperByAuthor("janedoe")); + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())) + .findProgressingVotePaperByAuthor("janedoe")); verify(votePaperRepository).findByVoteEndAtAfterAndAuthorUsername(isA(LocalDateTime.class), eq("janedoe")); } + /** + * Method under test: + * {@link VotePersistenceAdapter#findProgressingVotePaperById(Long)} + */ + @Test + void testFindProgressingVotePaperById() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + Optional ofResult = Optional.of(new VotePaperJpaEntity()); + when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + .thenReturn(ofResult); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act + Optional actualFindProgressingVotePaperByIdResult = (new VotePersistenceAdapter( + accountPersistenceAdapter, voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, + new VoteChoiceMapperImpl())).findProgressingVotePaperById(1L); + + // Assert + verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + assertSame(ofResult, actualFindProgressingVotePaperByIdResult); + } + + /** + * Method under test: + * {@link VotePersistenceAdapter#findProgressingVotePaperById(Long)} + */ + @Test + void testFindProgressingVotePaperById2() { + // Arrange + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + when(votePaperRepository.findByVoteEndAtAfterAndId(Mockito.any(), Mockito.any())) + .thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); + + // Act and Assert + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).findProgressingVotePaperById(1L)); + verify(votePaperRepository).findByVoteEndAtAfterAndId(isA(LocalDateTime.class), eq(1L)); + } + /** * Method under test: {@link VotePersistenceAdapter#findAllByVotePaperId(Long)} */ @Test void testFindAllByVotePaperId() { // Arrange + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); ArrayList voteChoiceJpaEntityList = new ArrayList<>(); when(voteChoiceRepository.findAllByVotePaperId(Mockito.any())).thenReturn(voteChoiceJpaEntityList); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act - List actualFindAllByVotePaperIdResult = votePersistenceAdapter.findAllByVotePaperId(1L); + List actualFindAllByVotePaperIdResult = (new VotePersistenceAdapter(accountPersistenceAdapter, + voteRepository, votePaperRepository, voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())) + .findAllByVotePaperId(1L); // Assert verify(voteChoiceRepository).findAllByVotePaperId(eq(1L)); @@ -587,10 +1406,21 @@ void testFindAllByVotePaperId() { @Test void testFindAllByVotePaperId2() { // Arrange + VoteChoiceRepository voteChoiceRepository = mock(VoteChoiceRepository.class); when(voteChoiceRepository.findAllByVotePaperId(Mockito.any())).thenThrow(new IllegalArgumentException("foo")); + AccountRepository accountRepository = mock(AccountRepository.class); + OAuth2AccountRepository oauth2AccountRepository = mock(OAuth2AccountRepository.class); + AccountPersistenceAdapter accountPersistenceAdapter = new AccountPersistenceAdapter(accountRepository, + oauth2AccountRepository, new AccountMapperImpl()); + + VoteRepository voteRepository = mock(VoteRepository.class); + VotePaperRepository votePaperRepository = mock(VotePaperRepository.class); + VotePaperMapperImpl votePaperMapper = new VotePaperMapperImpl(); // Act and Assert - assertThrows(IllegalArgumentException.class, () -> votePersistenceAdapter.findAllByVotePaperId(1L)); + assertThrows(IllegalArgumentException.class, + () -> (new VotePersistenceAdapter(accountPersistenceAdapter, voteRepository, votePaperRepository, + voteChoiceRepository, votePaperMapper, new VoteChoiceMapperImpl())).findAllByVotePaperId(1L)); verify(voteChoiceRepository).findAllByVotePaperId(eq(1L)); } }